Note that there are some explanatory texts on larger screens.

plurals
  1. POUsing .Net to deskew an image
    primarykey
    data
    text
    <p>I have been searching high and low for a reliable way to deskew an image in .Net, and am not having much luck.</p> <p>At the minute I am using Aforge. This is a pain as I am working with WPF, so the images I am working with are BitmapImage objects, as opposed to Bitmap objects, meaning I need to start with a BitmapImage object, save this to a memory stream, create a new Bitmap object from the memory stream, go through the deskewing process, save the deskewed image to a new memory stream and then create a new BitmapImage object from said memory stream. Not only that, but the deskewing isn't great.</p> <p>I am trying to read OMR data of a piece of paper scanned into a scanner, and therefore I need to rely on a particular OMR box being at the same co-ordinates every time, so the deskewing needs to be reliable.</p> <p>So I am using Aforge at the minute, I can't find any other free/open source libraries for image deskewing in .Net, everything I have found is either properly expensive or in C/C++.</p> <p>My question is do other free/open source libraries exist that assist in image deskewing in .Net? If so what are they called, if not how should I approach this problem?</p> <p>Edit: For example, let's say I have the below page:</p> <p><img src="https://i.stack.imgur.com/DER5l.png" alt="Initial Image"></p> <p>Note: This is for illustrative purposes only, but the actual image does indeed have a black rectangle at each corner of the page, maybe this will help.</p> <p>When I print this out, and scan it back into my scanner, it looks like this:</p> <p><img src="https://i.stack.imgur.com/G6slz.png" alt="Scanned Image"></p> <p>I need to deskew this image so that my box is in the same place each time. In the real world, there are a lot of boxes, they are smaller and close together, so the accuracy is important.</p> <p>My current method for this is a massive ineffective pain-in-the-ass:</p> <pre><code>using AForge.Imaging; using AForge.Imaging.Filters; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Windows.Media.Imaging; public static BitmapImage DeskewBitmap(BitmapImage skewedBitmap) { //Using a memory stream to minimise disk IO var memoryStream = BitmapImageToMemoryStream(skewedBitmap); var bitmap = MemoryStreamToBitmap(memoryStream); var skewAngle = CalculateSkewAngle(bitmap); //Aforge needs a Bppp indexed image for the deskewing process var bitmapConvertedToBbppIndexed = ConvertBitmapToBbppIndexed(bitmap); var rotatedImage = DeskewBitmap(skewAngle, bitmapConvertedToBbppIndexed); //I need to convert the image back to a non indexed format to put it back into a BitmapImage object var imageConvertedToNonIndexed = ConvertImageToNonIndexed(rotatedImage); var imageAsMemoryStream = BitmapToMemoryStream(imageConvertedToNonIndexed); var memoryStreamAsBitmapImage = MemoryStreamToBitmapImage(imageAsMemoryStream); return memoryStreamAsBitmapImage; } private static Bitmap ConvertImageToNonIndexed(Bitmap rotatedImage) { var imageConvertedToNonIndexed = rotatedImage.Clone( new Rectangle(0, 0, rotatedImage.Width, rotatedImage.Height), PixelFormat.Format32bppArgb); return imageConvertedToNonIndexed; } private static Bitmap DeskewBitmap(double skewAngle, Bitmap bitmapConvertedToBbppIndexed) { var rotationFilter = new RotateBilinear(-skewAngle) { FillColor = Color.White }; var rotatedImage = rotationFilter.Apply(bitmapConvertedToBbppIndexed); return rotatedImage; } private static double CalculateSkewAngle(Bitmap bitmapConvertedToBbppIndexed) { var documentSkewChecker = new DocumentSkewChecker(); double skewAngle = documentSkewChecker.GetSkewAngle(bitmapConvertedToBbppIndexed); return skewAngle; } private static Bitmap ConvertBitmapToBbppIndexed(Bitmap bitmap) { var bitmapConvertedToBbppIndexed = bitmap.Clone( new Rectangle(0, 0, bitmap.Width, bitmap.Height), PixelFormat.Format8bppIndexed); return bitmapConvertedToBbppIndexed; } private static BitmapImage ResizeBitmap(BitmapImage originalBitmap, int desiredWidth, int desiredHeight) { var ms = BitmapImageToMemoryStream(originalBitmap); ms.Position = 0; var result = new BitmapImage(); result.BeginInit(); result.DecodePixelHeight = desiredHeight; result.DecodePixelWidth = desiredWidth; result.StreamSource = ms; result.CacheOption = BitmapCacheOption.OnLoad; result.EndInit(); result.Freeze(); return result; } private static MemoryStream BitmapImageToMemoryStream(BitmapImage image) { var ms = new MemoryStream(); var encoder = new JpegBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create(image)); encoder.Save(ms); return ms; } private static BitmapImage MemoryStreamToBitmapImage(MemoryStream ms) { ms.Position = 0; var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.StreamSource = ms; bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.EndInit(); bitmap.Freeze(); return bitmap; } private static Bitmap MemoryStreamToBitmap(MemoryStream ms) { return new Bitmap(ms); } private static MemoryStream BitmapToMemoryStream(Bitmap image) { var memoryStream = new MemoryStream(); image.Save(memoryStream, ImageFormat.Bmp); return memoryStream; } </code></pre> <p>In retrospect, a couple more questions:</p> <ol> <li>Am I using AForge correctly?</li> <li>Is AForge the best library to use for this task?</li> <li>How could my current approach to this be improved to get more accurate results?</li> </ol>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload