Пример #1
0
        public FileBasedScannedImage(Bitmap img, ScanBitDepth bitDepth, bool highQuality)
        {
            Bitmap baseImage;
            MemoryStream baseImageEncoded;
            ScannedImageHelper.GetSmallestBitmap(img, bitDepth, highQuality, out baseImage, out baseImageEncoded, out baseImageFileFormat);

            baseImageFileName = (_recoveryFileNumber++).ToString("D5", CultureInfo.InvariantCulture) + GetExtension(baseImageFileFormat);
            baseImageFilePath = Path.Combine(RecoveryFolder.FullName, baseImageFileName);

            if (baseImage != null)
            {
                // TODO: If I'm stuck using PNG anyway, then don't treat B&W specially
                baseImage.Save(baseImageFilePath, baseImageFileFormat);
                baseImage.Dispose();
            }
            else
            {
                Debug.Assert(baseImageEncoded != null);
                using (var fs = new FileStream(baseImageFilePath, FileMode.CreateNew))
                {
                    baseImageEncoded.Seek(0, SeekOrigin.Begin);
                    baseImageEncoded.CopyTo(fs);
                }
                baseImageEncoded.Dispose();
            }

            _recoveryIndexManager.Index.Images.Add(new RecoveryIndexImage
            {
                FileName = baseImageFileName,
                BitDepth = bitDepth,
                HighQuality = highQuality,
                TransformList = transformList
            });
            _recoveryIndexManager.Save();
        }
Пример #2
0
        public ScannedImage(Bitmap img, ScanBitDepth bitDepth, bool highQuality, int quality)
        {
            Bitmap baseImage;
            MemoryStream baseImageEncoded;
            ImageFormat baseImageFileFormat;
            ScannedImageHelper.GetSmallestBitmap(img, bitDepth, highQuality, quality, out baseImage, out baseImageEncoded, out baseImageFileFormat);

            transformList = new List<Transform>();
            recoveryImage = RecoveryImage.CreateNew(baseImageFileFormat, bitDepth, highQuality, transformList);

            if (baseImage != null)
            {
                baseImage.Save(recoveryImage.FilePath, recoveryImage.FileFormat);
                baseImage.Dispose();
            }
            else
            {
                Debug.Assert(baseImageEncoded != null);
                using (var fs = new FileStream(recoveryImage.FilePath, FileMode.CreateNew))
                {
                    baseImageEncoded.Seek(0, SeekOrigin.Begin);
                    baseImageEncoded.CopyTo(fs);
                }
                baseImageEncoded.Dispose();
            }

            recoveryImage.Save();
        }
Пример #3
0
 public static string SaveSmallestBitmap(Bitmap sourceImage, ScanBitDepth bitDepth, bool highQuality, int quality, out ImageFormat imageFormat)
 {
     // Store the image in as little space as possible
     if (sourceImage.PixelFormat == PixelFormat.Format1bppIndexed)
     {
         // Already encoded as 1-bit
         imageFormat = ImageFormat.Png;
         return(EncodePng(sourceImage));
     }
     else if (bitDepth == ScanBitDepth.BlackWhite)
     {
         // Convert to a 1-bit bitmap before saving to help compression
         // This is lossless and takes up minimal storage (best of both worlds), so highQuality is irrelevant
         using (var bitmap = BitmapHelper.CopyToBpp(sourceImage, 1))
         {
             imageFormat = ImageFormat.Png;
             return(EncodePng(bitmap));
         }
         // Note that if a black and white image comes from native WIA, bitDepth is unknown,
         // so the image will be png-encoded below instead of using a 1-bit bitmap
     }
     else if (highQuality)
     {
         // Store as PNG
         // Lossless, but some images (color/grayscale) take up lots of storage
         imageFormat = ImageFormat.Png;
         return(EncodePng(sourceImage));
     }
     else if (Equals(sourceImage.RawFormat, ImageFormat.Jpeg))
     {
         // Store as JPEG
         // Since the image was originally in JPEG format, PNG is unlikely to have size benefits
         imageFormat = ImageFormat.Jpeg;
         return(EncodeJpeg(sourceImage, quality));
     }
     else
     {
         // Store as PNG/JPEG depending on which is smaller
         var pngEncoded  = EncodePng(sourceImage);
         var jpegEncoded = EncodeJpeg(sourceImage, quality);
         if (new FileInfo(pngEncoded).Length <= new FileInfo(jpegEncoded).Length)
         {
             // Probably a black and white image (from native WIA, so bitDepth is unknown), which PNG compresses well vs. JPEG
             File.Delete(jpegEncoded);
             imageFormat = ImageFormat.Png;
             return(pngEncoded);
         }
         else
         {
             // Probably a color or grayscale image, which JPEG compresses well vs. PNG
             File.Delete(pngEncoded);
             imageFormat = ImageFormat.Jpeg;
             return(jpegEncoded);
         }
     }
 }
Пример #4
0
        public ScannedImage(Bitmap img, ScanBitDepth bitDepth, bool highQuality, int quality)
        {
            var tempFilePath = ScannedImageHelper.SaveSmallestBitmap(img, bitDepth, highQuality, quality, out var fileFormat);

            transformList = new List <Transform>();
            recoveryImage = RecoveryImage.CreateNew(fileFormat, bitDepth, highQuality, transformList);

            File.Move(tempFilePath, recoveryImage.FilePath);

            recoveryImage.Save();
        }
Пример #5
0
        public ScannedImage(Bitmap img, ScanBitDepth bitDepth, bool highQuality, int quality)
        {
            ImageFormat fileFormat;
            string tempFilePath = ScannedImageHelper.SaveSmallestBitmap(img, bitDepth, highQuality, quality, out fileFormat);

            transformList = new List<Transform>();
            recoveryImage = RecoveryImage.CreateNew(fileFormat, bitDepth, highQuality, transformList);

            File.Move(tempFilePath, recoveryImage.FilePath);

            recoveryImage.Save();
        }
Пример #6
0
 private RecoveryImage(ImageFormat fileFormat, ScanBitDepth bitDepth, bool highQuality, List <Transform> transformList)
 {
     FileFormat = fileFormat;
     FileName   = GetNextFileName() + GetExtension(FileFormat);
     FilePath   = Path.Combine(RecoveryFolder.FullName, FileName);
     IndexImage = new RecoveryIndexImage
     {
         FileName      = FileName,
         BitDepth      = bitDepth,
         HighQuality   = highQuality,
         TransformList = transformList
     };
 }
Пример #7
0
 private RecoveryImage(ImageFormat fileFormat, ScanBitDepth bitDepth, bool highQuality, List <Transform> transformList)
 {
     FileFormat = fileFormat;
     FileName   = (_recoveryFileNumber++).ToString("D5", CultureInfo.InvariantCulture) + GetExtension(FileFormat);
     FilePath   = Path.Combine(RecoveryFolder.FullName, FileName);
     IndexImage = new RecoveryIndexImage
     {
         FileName      = FileName,
         BitDepth      = bitDepth,
         HighQuality   = highQuality,
         TransformList = transformList
     };
 }
Пример #8
0
 private ScannedImage TransferImage(WiaBackgroundEventLoop eventLoop, int pageNumber, out bool cancel)
 {
     try
     {
         // TODO: Use the NoUI flag uniformly
         var transfer = ScanParams.NoUI ? new ConsoleWiaTransfer() : wiaTransfer;
         using (var stream = transfer.Transfer(pageNumber, eventLoop, WiaApi.Formats.BMP))
         {
             if (stream == null)
             {
                 cancel = true;
                 return(null);
             }
             cancel = false;
             using (Image output = Image.FromStream(stream))
             {
                 using (var result = ScannedImageHelper.PostProcessStep1(output, ScanProfile))
                 {
                     if (blankDetector.ExcludePage(result, ScanProfile))
                     {
                         return(null);
                     }
                     ScanBitDepth bitDepth = ScanProfile.UseNativeUI ? ScanBitDepth.C24Bit : ScanProfile.BitDepth;
                     var          image    = new ScannedImage(result, bitDepth, ScanProfile.MaxQuality, ScanProfile.Quality);
                     image.SetThumbnail(thumbnailRenderer.RenderThumbnail(result));
                     ScannedImageHelper.PostProcessStep2(image, ScanProfile, pageNumber);
                     return(image);
                 }
             }
         }
     }
     catch (NoPagesException)
     {
         if (ScanProfile.PaperSource != ScanSource.Glass && pageNumber == 1)
         {
             // No pages were in the feeder, so show the user an error
             throw new NoPagesException();
         }
         // At least one page was scanned but now the feeder is empty, so exit normally
         cancel = true;
         return(null);
     }
     catch (ScanDriverException)
     {
         throw;
     }
     catch (Exception e)
     {
         throw new ScanDriverUnknownException(e);
     }
 }
Пример #9
0
        private IScannedImage TransferImage(WiaBackgroundEventLoop eventLoop, int pageNumber)
        {
            try
            {
                using (var stream = wiaTransfer.Transfer(pageNumber, eventLoop, WiaApi.Formats.BMP))
                {
                    if (stream == null)
                    {
                        // User cancelled
                        return(null);
                    }
                    using (Image output = Image.FromStream(stream))
                    {
                        double scaleFactor = 1;
                        if (!ScanSettings.UseNativeUI)
                        {
                            scaleFactor = ScanSettings.AfterScanScale.ToIntScaleFactor();
                        }

                        using (var result = ImageScaleHelper.ScaleImage(output, scaleFactor))
                        {
                            ScanBitDepth bitDepth = ScanSettings.UseNativeUI ? ScanBitDepth.C24Bit : ScanSettings.BitDepth;
                            return(scannedImageFactory.Create(result, bitDepth, ScanSettings.MaxQuality));
                        }
                    }
                }
            }
            catch (COMException e)
            {
                if ((uint)e.ErrorCode == WiaApi.Errors.OUT_OF_PAPER)
                {
                    if (ScanSettings.PaperSource != ScanSource.Glass && pageNumber == 1)
                    {
                        throw new NoPagesException();
                    }
                    return(null);
                }
                else if ((uint)e.ErrorCode == WiaApi.Errors.OFFLINE)
                {
                    throw new DeviceOfflineException();
                }
                else
                {
                    throw new ScanDriverUnknownException(e);
                }
            }
        }
Пример #10
0
        private async Task <(ScannedImage, bool)> TransferImage(WiaBackgroundEventLoop eventLoop, int pageNumber)
        {
            return(await Task.Factory.StartNew(() =>
            {
                try
                {
                    ChaosMonkey.MaybeError(0, new COMException("Fail", -2147467259));
                    using (var stream = DoTransfer(pageNumber, eventLoop, WiaApi.Formats.BMP))
                    {
                        if (stream == null)
                        {
                            return (null, true);
                        }

                        using (Image output = Image.FromStream(stream))
                        {
                            using (var result = scannedImageHelper.PostProcessStep1(output, ScanProfile))
                            {
                                if (blankDetector.ExcludePage(result, ScanProfile))
                                {
                                    return (null, false);
                                }

                                ScanBitDepth bitDepth = ScanProfile.UseNativeUI ? ScanBitDepth.C24Bit : ScanProfile.BitDepth;
                                var image = new ScannedImage(result, bitDepth, ScanProfile.MaxQuality, ScanProfile.Quality);
                                scannedImageHelper.PostProcessStep2(image, result, ScanProfile, ScanParams, pageNumber);
                                string tempPath = scannedImageHelper.SaveForBackgroundOcr(result, ScanParams);
                                scannedImageHelper.RunBackgroundOcr(image, ScanParams, tempPath);
                                return (image, false);
                            }
                        }
                    }
                }
                catch (NoPagesException)
                {
                    if (ScanProfile.PaperSource != ScanSource.Glass && pageNumber == 1)
                    {
                        // No pages were in the feeder, so show the user an error
                        throw new NoPagesException();
                    }

                    // At least one page was scanned but now the feeder is empty, so exit normally
                    return (null, true);
                }
            }, TaskCreationOptions.LongRunning));
        }
Пример #11
0
        private void ProduceImage(ScannedImageSource.Concrete source, Image output, ref int pageNumber)
        {
            var results = new[] { scannedImageHelper.PostProcessStep1(output, ScanProfile) };

            if (ScanProfile.DivideScanIntoTwoPages)
            {
                var result = results[0];

                // Should probably detect portrait vs landscape and split appropriately but this is the
                // main use case.
                var halfHeight = result.Height / 2;
                var firstRect  = new Rectangle(0, 0, result.Width, halfHeight);
                var secondRect = new Rectangle(0, halfHeight, result.Width, halfHeight);

                var firstPage = result.Clone(secondRect, result.PixelFormat);
                firstPage.RotateFlip(RotateFlipType.Rotate90FlipNone);
                var secondPage = result.Clone(firstRect, result.PixelFormat);
                secondPage.RotateFlip(RotateFlipType.Rotate90FlipNone);

                results = new[] { firstPage, secondPage };

                result.Dispose();
            }

            foreach (var result in results)
            {
                if (blankDetector.ExcludePage(result, ScanProfile))
                {
                    continue;
                }

                ScanBitDepth bitDepth = ScanProfile.UseNativeUI ? ScanBitDepth.C24Bit : ScanProfile.BitDepth;
                var          image    = new ScannedImage(result, bitDepth, ScanProfile.MaxQuality, ScanProfile.Quality);
                scannedImageHelper.PostProcessStep2(image, result, ScanProfile, ScanParams, pageNumber);
                string tempPath = scannedImageHelper.SaveForBackgroundOcr(result, ScanParams);
                result.Dispose();
                scannedImageHelper.RunBackgroundOcr(image, ScanParams, tempPath);
                source.Put(image);

                pageNumber++;
                InitNextPageProgress(pageNumber);
            }
        }
Пример #12
0
        public static void GetSmallestBitmap(Bitmap sourceImage, ScanBitDepth bitDepth, bool highQuality, out Bitmap bitmap, out MemoryStream encodedBitmap, out ImageFormat imageFormat)
        {
            // Defaults for out arguments
            bitmap = null;
            encodedBitmap = null;
            imageFormat = ImageFormat.Png;

            // Store the image in as little space as possible
            if (bitDepth == ScanBitDepth.BlackWhite)
            {
                // Store as a 1-bit bitmap
                // This is lossless and takes up minimal storage (best of both worlds), so highQuality is irrelevant
                bitmap = (Bitmap)BitmapHelper.CopyToBpp(sourceImage, 1).Clone();
                // Note that if a black and white image comes from native WIA, bitDepth is unknown,
                // so the image will be png-encoded below instead of using a 1-bit bitmap
            }
            else if (highQuality)
            {
                // Store as PNG
                // Lossless, but some images (color/grayscale) take up lots of storage
                encodedBitmap = EncodeBitmap(sourceImage, ImageFormat.Png);
            }
            else
            {
                // Store as PNG/JPEG depending on which is smaller
                var pngEncoded = EncodeBitmap(sourceImage, ImageFormat.Png);
                var jpegEncoded = EncodeBitmap(sourceImage, ImageFormat.Jpeg);
                if (pngEncoded.Length <= jpegEncoded.Length)
                {
                    // Probably a black and white image (from native WIA, so bitDepth is unknown), which PNG compresses well vs. JPEG
                    encodedBitmap = pngEncoded;
                    jpegEncoded.Dispose();
                }
                else
                {
                    // Probably a color or grayscale image, which JPEG compresses well vs. PNG
                    encodedBitmap = jpegEncoded;
                    pngEncoded.Dispose();
                    imageFormat = ImageFormat.Jpeg;
                }
            }
        }
Пример #13
0
        public static void GetSmallestBitmap(Bitmap sourceImage, ScanBitDepth bitDepth, bool highQuality, out Bitmap bitmap, out MemoryStream encodedBitmap, out ImageFormat imageFormat)
        {
            // Defaults for out arguments
            bitmap        = null;
            encodedBitmap = null;
            imageFormat   = ImageFormat.Png;

            // Store the image in as little space as possible
            if (bitDepth == ScanBitDepth.BlackWhite)
            {
                // Store as a 1-bit bitmap
                // This is lossless and takes up minimal storage (best of both worlds), so highQuality is irrelevant
                bitmap = (Bitmap)BitmapHelper.CopyToBpp(sourceImage, 1).Clone();
                // Note that if a black and white image comes from native WIA, bitDepth is unknown,
                // so the image will be png-encoded below instead of using a 1-bit bitmap
            }
            else if (highQuality)
            {
                // Store as PNG
                // Lossless, but some images (color/grayscale) take up lots of storage
                encodedBitmap = EncodeBitmap(sourceImage, ImageFormat.Png);
            }
            else
            {
                // Store as PNG/JPEG depending on which is smaller
                var pngEncoded  = EncodeBitmap(sourceImage, ImageFormat.Png);
                var jpegEncoded = EncodeBitmap(sourceImage, ImageFormat.Jpeg);
                if (pngEncoded.Length <= jpegEncoded.Length)
                {
                    // Probably a black and white image (from native WIA, so bitDepth is unknown), which PNG compresses well vs. JPEG
                    encodedBitmap = pngEncoded;
                    jpegEncoded.Dispose();
                }
                else
                {
                    // Probably a color or grayscale image, which JPEG compresses well vs. PNG
                    encodedBitmap = jpegEncoded;
                    pngEncoded.Dispose();
                    imageFormat = ImageFormat.Jpeg;
                }
            }
        }
Пример #14
0
        private void ProduceImage(ScannedImageSource.Concrete source, Image output, ref int pageNumber)
        {
            using (var result = scannedImageHelper.PostProcessStep1(output, ScanProfile))
            {
                if (blankDetector.ExcludePage(result, ScanProfile))
                {
                    return;
                }

                ScanBitDepth bitDepth = ScanProfile.UseNativeUI ? ScanBitDepth.C24Bit : ScanProfile.BitDepth;
                var          image    = new ScannedImage(result, bitDepth, ScanProfile.MaxQuality, ScanProfile.Quality);
                scannedImageHelper.PostProcessStep2(image, result, ScanProfile, ScanParams, pageNumber);
                string tempPath = scannedImageHelper.SaveForBackgroundOcr(result, ScanParams);
                scannedImageHelper.RunBackgroundOcr(image, ScanParams, tempPath);
                source.Put(image);

                pageNumber++;
                InitNextPageProgress(pageNumber);
            }
        }
Пример #15
0
        public ScannedImage(String imgBase64, ScanBitDepth bitDepth, bool highQuality)
        {
            using (MemoryStream stream = new MemoryStream(Convert.FromBase64String(imgBase64)))
                using (Image image = Image.FromStream(stream))
                {
                    this.bitDepth = bitDepth;
                    Thumbnail     = ThumbnailHelper.GetThumbnail(new Bitmap(image));
                    ScannedImageHelper.GetSmallestBitmap(new Bitmap(image), bitDepth, highQuality, out baseImage, out baseImageEncoded, out baseImageFileFormat);
                }

            ////Convertir String a Imagen
            //// Convert Base64 String to byte[]
            //byte[] imageBytes = Convert.FromBase64String(imgBase64);
            //// Convert byte[] to Image
            //using (var ms = new MemoryStream(imageBytes, 0, imageBytes.Length))
            //{
            //    Image image = Image.FromStream(ms, true);
            //
            //
            //
            //}
        }
Пример #16
0
        public FileBasedScannedImage(Bitmap img, ScanBitDepth bitDepth, bool highQuality)
        {
            Bitmap       baseImage;
            MemoryStream baseImageEncoded;

            ScannedImageHelper.GetSmallestBitmap(img, bitDepth, highQuality, out baseImage, out baseImageEncoded, out baseImageFileFormat);

            baseImageFileName = (_recoveryFileNumber++).ToString("D5", CultureInfo.InvariantCulture) + GetExtension(baseImageFileFormat);
            baseImageFilePath = Path.Combine(RecoveryFolder.FullName, baseImageFileName);

            if (baseImage != null)
            {
                // TODO: If I'm stuck using PNG anyway, then don't treat B&W specially
                baseImage.Save(baseImageFilePath, baseImageFileFormat);
                baseImage.Dispose();
            }
            else
            {
                Debug.Assert(baseImageEncoded != null);
                using (var fs = new FileStream(baseImageFilePath, FileMode.CreateNew))
                {
                    baseImageEncoded.Seek(0, SeekOrigin.Begin);
                    baseImageEncoded.CopyTo(fs);
                }
                baseImageEncoded.Dispose();
            }

            _recoveryIndexManager.Index.Images.Add(new RecoveryIndexImage
            {
                FileName      = baseImageFileName,
                BitDepth      = bitDepth,
                HighQuality   = highQuality,
                TransformList = transformList
            });
            _recoveryIndexManager.Save();
        }
Пример #17
0
 public IScannedImage Create(Bitmap img, ScanBitDepth bitDepth, bool highQuality)
 {
     return(new FileBasedScannedImage(img, bitDepth, highQuality));
 }
Пример #18
0
 public static string SaveSmallestBitmap(Bitmap sourceImage, ScanBitDepth bitDepth, bool highQuality, int quality, out ImageFormat imageFormat)
 {
     // Store the image in as little space as possible
     if (bitDepth == ScanBitDepth.BlackWhite)
     {
         // Convert to a 1-bit bitmap before saving to help compression
         // This is lossless and takes up minimal storage (best of both worlds), so highQuality is irrelevant
         using (var bitmap = BitmapHelper.CopyToBpp(sourceImage, 1))
         {
             imageFormat = ImageFormat.Png;
             return EncodePng(bitmap);
         }
         // Note that if a black and white image comes from native WIA, bitDepth is unknown,
         // so the image will be png-encoded below instead of using a 1-bit bitmap
     }
     else if (highQuality)
     {
         // Store as PNG
         // Lossless, but some images (color/grayscale) take up lots of storage
         imageFormat = ImageFormat.Png;
         return EncodePng(sourceImage);
     }
     else if (Equals(sourceImage.RawFormat, ImageFormat.Jpeg))
     {
         // Store as JPEG
         // Since the image was originally in JPEG format, PNG is unlikely to have size benefits
         imageFormat = ImageFormat.Jpeg;
         return EncodeJpeg(sourceImage, quality);
     }
     else
     {
         // Store as PNG/JPEG depending on which is smaller
         var pngEncoded = EncodePng(sourceImage);
         var jpegEncoded = EncodeJpeg(sourceImage, quality);
         if (new FileInfo(pngEncoded).Length <= new FileInfo(jpegEncoded).Length)
         {
             // Probably a black and white image (from native WIA, so bitDepth is unknown), which PNG compresses well vs. JPEG
             File.Delete(jpegEncoded);
             imageFormat = ImageFormat.Png;
             return pngEncoded;
         }
         else
         {
             // Probably a color or grayscale image, which JPEG compresses well vs. PNG
             File.Delete(pngEncoded);
             imageFormat = ImageFormat.Jpeg;
             return jpegEncoded;
         }
     }
 }
Пример #19
0
 public static RecoveryImage CreateNew(ImageFormat fileFormat, ScanBitDepth bitDepth, bool highQuality, List<Transform> transformList)
 {
     return new RecoveryImage(fileFormat, bitDepth, highQuality, transformList);
 }
Пример #20
0
 private RecoveryImage(ImageFormat fileFormat, ScanBitDepth bitDepth, bool highQuality, List<Transform> transformList)
 {
     FileFormat = fileFormat;
     FileName = (_recoveryFileNumber++).ToString("D5", CultureInfo.InvariantCulture) + GetExtension(FileFormat);
     FilePath = Path.Combine(RecoveryFolder.FullName, FileName);
     IndexImage = new RecoveryIndexImage
     {
         FileName = FileName,
         BitDepth = bitDepth,
         HighQuality = highQuality,
         TransformList = transformList
     };
 }
Пример #21
0
 public static RecoveryImage CreateNew(ImageFormat fileFormat, ScanBitDepth bitDepth, bool highQuality, List <Transform> transformList)
 {
     return(new RecoveryImage(fileFormat, bitDepth, highQuality, transformList));
 }
Пример #22
0
 public IScannedImage Create(Bitmap img, ScanBitDepth bitDepth, bool highQuality, int quality)
 {
     return new FileBasedScannedImage(img, bitDepth, highQuality, quality);
 }
Пример #23
0
 public ScannedImage(Bitmap img, ScanBitDepth bitDepth, bool highQuality)
 {
     this.bitDepth = bitDepth;
     ScannedImageHelper.GetSmallestBitmap(img, bitDepth, highQuality, out baseImage, out baseImageEncoded, out baseImageFileFormat);
 }
Пример #24
0
 public ScannedImage(Bitmap img, ScanBitDepth bitDepth, bool highQuality)
 {
     this.bitDepth = bitDepth;
     Thumbnail     = ThumbnailHelper.GetThumbnail(img);
     ScannedImageHelper.GetSmallestBitmap(img, bitDepth, highQuality, out baseImage, out baseImageEncoded, out baseImageFileFormat);
 }
Пример #25
0
 public IScannedImage Create(Bitmap img, ScanBitDepth bitDepth, bool highQuality)
 {
     return new ScannedImage(img, bitDepth, highQuality);
 }