How to fix skewed image for OCR? - c#

I just started OCR project a few weeks ago. I stuck with image skewed problem I tried many different methods nothing seems to work so plz help:)
Skewed image given below
I want final image such as given below
I already tried to deskew image but I was unable to get final image.
public Image<Gray, byte> ImageDeskewOuter(Image<Gray, byte> img)
{
img = img.Resize(img.Height, img.Width, Inter.Linear);
Image<Gray, byte> tmp = new Image<Gray, byte>(img.Bitmap);
tmp = tmp.ThresholdToZero(new Gray(180));
int nZero = tmp.CountNonzero()[0] == 0 ? 1 : tmp.CountNonzero()[0];
if (tmp.Bytes.Length / nZero < 10)
img = tmp.Not();
else
img = img.ThresholdToZero(new Gray(80)).InRange(new Gray(0), new Gray(60)).Not();
tmp = new Image<Gray, byte>(img.Bitmap).Canny(50, 150);
List<Rectangle> rlist = new List<Rectangle>();
Rectangle min = new Rectangle();
Rectangle max = new Rectangle();
VectorOfVectorOfPoint contour = new VectorOfVectorOfPoint();
Mat hier = new Mat();
CvInvoke.FindContours(tmp, contour, hier, RetrType.External, ChainApproxMethod.ChainApproxSimple);
if (contour.Size > 0)
{
for (int i = 0; i < contour.Size; i++)
{
Rectangle rec = CvInvoke.BoundingRectangle(contour[i]);
if (rec.Width > 30 && rec.Width < 120 && rec.Height > 50 && rec.Height < 120)
{
rlist.Add(rec);
}
}
min = rlist.OrderBy(x => x.X).FirstOrDefault();
max = rlist.OrderByDescending(x => x.X).FirstOrDefault();
Rectangle roi = Rectangle.Union(min, max);
img.ROI = roi;
}
if (rlist.Count > 0)
{
double angle = LineAngle(min.X, min.Bottom, max.X, max.Bottom, min.X, min.Bottom, max.X, min.Bottom) + 3;
img = img.Rotate(angle, new Gray(255), false);
}
return img;
}
Final image using above function

Step 1 > Select desired contour and apply CvInvoke.MinAreaRect() and copy image area into new image
Step 2> Create a bitmap image whose width is sum of all cropped image width(if you want to put each image side by side)
Step 3> Using Graphics draw image on bitmap
public Image<Gray, byte> ImageDeskew(Image<Gray, byte> img)
{
img = img.Resize(img.Height, img.Width, Inter.Linear);
Image<Gray, byte> tmp = new Image<Gray, byte>(img.Bitmap);
tmp = new Image<Gray, byte>(AdjustContrast(tmp.Bitmap, 70));
img = tmp.Not().ThresholdAdaptive(new Gray(255), AdaptiveThresholdType.MeanC, ThresholdType.Binary, 45, new Gray(10));
tmp = new Image<Gray, byte>(img.Bitmap).Canny(50, 150);
List<Image<Gray, byte>> imglist = new List<Image<Gray, byte>>();
Rectangle min = new Rectangle();
Rectangle max = new Rectangle();
VectorOfVectorOfPoint contour = new VectorOfVectorOfPoint();
Mat hier = new Mat();
CvInvoke.FindContours(tmp, contour, hier, RetrType.External, ChainApproxMethod.ChainApproxSimple);
if (contour.Size > 0)
{
for (int i = 0; i < contour.Size; i++)
{
RotatedRect rRect = CvInvoke.MinAreaRect(contour[i]);
float area = rRect.Size.Width * rRect.Size.Height;
if (area > (img.Bytes.Length / 10))
{
rlist.Add(rec);
if (rRect.Angle > -45) imglist.Add(img.Copy(rRect));
else imglist.Add(img.Copy(rRect).Rotate(-90, new Gray(255), false));
}
}
}
if (imglist.Count > 0)
{
int xPx = imglist.Sum(x => x.Width);
Bitmap bitmap = new Bitmap(xPx, img.Height);
using (Graphics g = Graphics.FromImage(bitmap))
{
xPx = 0;
imglist.Reverse();
foreach (Image<Gray, byte> i in imglist)
{
g.DrawImage(i.Not().Bitmap, xPx, 0);
xPx += i.Width;
}
}
img = new Image<Gray, byte>(bitmap).Not();
}
return img;
}

Related

How can I crop the white space of an image using EmguCV and windows form

My project need to crop the image automatically to remove the white space around the drawing (Lattice).
Here is my code
grayImage = grayImage.ThresholdBinary(new Gray(threshold), new Gray(255));
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
CvInvoke.FindContours(grayImage, contours, null, RetrType.External, ChainApproxMethod.ChainApproxSimple);
for (int i = 0; i < contours.Size; i++)
{
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
if (rect.Width > minWidth && rect.Height > minHeight)
{
CvInvoke.DrawContours(image, contours, i, new MCvScalar(255, 0, 0), 2);
}
}
imageBox.Image = image;
The main issue is that FindContours finds white contours, and the image background is white.
We use ThresholdBinaryInv instead of ThresholdBinary.
ThresholdBinaryInv applies threshold and invert black and white after applying the threshold (the pattern is going to be white on black instead of black on white).
Code sample:
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using System.Drawing;
namespace Testings
{
public class Program
{
static void Main(string[] args)
{
int threshold = 254;
var image_file_name = #"auxetic_lattice_screen.png";
Mat image = new Mat(image_file_name, Emgu.CV.CvEnum.ImreadModes.Color); //Read input image as BGR
var grayImage = new Image<Gray, System.Byte>(image_file_name); //Read input image as Grayscale
//grayImage = grayImage.ThresholdBinary(new Gray(threshold), new Gray(255));
grayImage = grayImage.ThresholdBinaryInv(new Gray(threshold), new Gray(255)); // Use ThresholdBinaryInv - invert black and white so that the pattern be white
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
CvInvoke.FindContours(grayImage, contours, null, RetrType.External, ChainApproxMethod.ChainApproxSimple);
int minWidth = grayImage.Width / 2;
int minHeight = grayImage.Height / 2;
Mat croppedImage = null;
for (int i = 0; i < contours.Size; i++)
{
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
if (rect.Width > minWidth && rect.Height > minHeight)
{
croppedImage = new Mat(image.Clone(), rect); //Crop the rectangle
CvInvoke.DrawContours(image, contours, i, new MCvScalar(255, 0, 0), 2);
}
}
//Show images for testing
CvInvoke.Imshow("grayImage", grayImage);
CvInvoke.Imshow("image", image);
CvInvoke.WaitKey();
CvInvoke.Imwrite("output_image.png", image); //Save output for testing
CvInvoke.Imwrite("croppedImage.png", croppedImage); //Save output for testing
}
}
}
Result:

How to get rid of unnecessary lines with emgu cv

I'm trying to detect contour of an ellipse-like water droplet with Emgu CV. I wrote code for contour detection:
public List<int> GetDiameters()
{
string inputFile = #"path.jpg";
Image<Bgr, byte> imageInput = new Image<Bgr, byte>(inputFile);
Image<Gray, byte> grayImage = imageInput.Convert<Gray, byte>();
Image<Gray, byte> bluredImage = grayImage;
CvInvoke.MedianBlur(grayImage, bluredImage, 9);
Image<Gray, byte> edgedImage = bluredImage;
CvInvoke.Canny(bluredImage, edgedImage, 50, 5);
Image<Gray, byte> closedImage = edgedImage;
Mat kernel = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Ellipse, new System.Drawing.Size { Height = 100, Width = 250}, new System.Drawing.Point(-1, -1));
CvInvoke.MorphologyEx(edgedImage, closedImage, Emgu.CV.CvEnum.MorphOp.Close, kernel, new System.Drawing.Point(-1, -1), 0, Emgu.CV.CvEnum.BorderType.Replicate, new MCvScalar());
System.Drawing.Point(100, 250), 10000, Emgu.CV.CvEnum.BorderType.Default, new MCvScalar()
Image<Gray, byte> contoursImage = closedImage;
Image<Bgr, byte> imageOut = imageInput;
VectorOfVectorOfPoint rescontours1 = new VectorOfVectorOfPoint();
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
CvInvoke.FindContours(contoursImage, contours, null, Emgu.CV.CvEnum.RetrType.List,
Emgu.CV.CvEnum.ChainApproxMethod.LinkRuns);
MCvScalar color = new MCvScalar(0, 0, 255);
int count = contours.Size;
for (int i = 0; i < count; i++)
{
using (VectorOfPoint contour = contours[i])
using (VectorOfPoint approxContour = new VectorOfPoint())
{
CvInvoke.ApproxPolyDP(contour, approxContour,
0.01 * CvInvoke.ArcLength(contour, true), true);
var area = CvInvoke.ContourArea(contour);
if (area > 0 && approxContour.Size > 10)
{
rescontours1.Push(approxContour);
}
CvInvoke.DrawContours(imageOut, rescontours1, -1, color, 2);
}
}
}
}
result so far:
I think there is a problem with approximation. How to get rid of internal lines and close external contour?
I might need some more information to exactly pinpoint your issue, but it might be something to do with your median blur. I would see if you are blurring enough that EmguCV things the blur is enough that you can canny edge detection. Another method that you could use is Dilate. Try Dialating your Canny edge detection and see if you get any better results.
EDIT
Here is the code below
public List<int> GetDiameters()
{
//List to hold output diameters
List<int> diametors = new List<int>();
//File path to where the image is located
string inputFile = #"C:\Users\jones\Desktop\Image Folder\water.JPG";
//Read in the image and store it as a mat object
Mat img = CvInvoke.Imread(inputFile, Emgu.CV.CvEnum.ImreadModes.AnyColor);
//Mat object that will hold the output of the gaussian blur
Mat gaussianBlur = new Mat();
//Blur the image
CvInvoke.GaussianBlur(img, gaussianBlur, new System.Drawing.Size(21, 21), 20, 20, Emgu.CV.CvEnum.BorderType.Default);
//Mat object that will hold the output of the canny
Mat canny = new Mat();
//Canny the image
CvInvoke.Canny(gaussianBlur, canny, 40, 40);
//Mat object that will hold the output of the dilate
Mat dilate = new Mat();
//Dilate the canny image
CvInvoke.Dilate(canny, dilate, null, new System.Drawing.Point(-1, -1), 6, Emgu.CV.CvEnum.BorderType.Default, new MCvScalar(0, 0, 0));
//Vector that will hold all found contours
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
//Find the contours and draw them on the image
CvInvoke.FindContours(dilate, contours, null, Emgu.CV.CvEnum.RetrType.External, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxSimple);
CvInvoke.DrawContours(img, contours, -1, new MCvScalar(255, 0, 0), 5, Emgu.CV.CvEnum.LineType.FourConnected);
//Variables to hold relevent info on what is the biggest contour
int biggest = 0;
int index = 0;
//Find the biggest contour
for (int i = 0; i < contours.Size; i++)
{
if (contours.Size > biggest)
{
biggest = contours.Size;
index = i;
}
}
//Once all contours have been looped over, add the biggest contour's index to the list
diametors.Add(index);
//Return the list
return diametors;
}
The first thing you do is blur the image.
Then you canny the image.
Then you dilate the image, as to make the final output contours more uniform.
Then you just find contours.
I know the final contours are a little bigger than the water droplet, but this is the best that I could come up with. You can probably fiddle around with some of the settings and the code above to make the result a little cleaner.

CvInvoke.MinAreaRect(contour) returns the wrong angle

I have a contour of a number plate and I want to check if it's tilted or not. I used CvInvoke.MinAreaRect(contour) but it always returns the angle == -90 even when the plate is obviously tilted, you can see the contour I draw in the picture below.
Does anyone know what happened and solution for my problem?
Here is the code:
Image<Gray, byte> gray = new Image<Gray, byte>("2.PNG");
Image<Gray, byte> adaptive_threshold_img = gray.ThresholdAdaptive(new Gray(255), AdaptiveThresholdType.GaussianC, ThresholdType.BinaryInv, 11, new Gray(2));
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
Mat hier = new Mat();
CvInvoke.FindContours(adaptive_threshold_img, contours, hier, RetrType.Tree, ChainApproxMethod.ChainApproxSimple);
double max_area = 0;
VectorOfPoint max_contour = new VectorOfPoint();
for (int i = 0; i < contours.Size; i++)
{
double temp = CvInvoke.ContourArea(contours[i]);
if (temp > max_area)
{
max_area = temp;
max_contour = contours[i];
}
}
VectorOfVectorOfPoint contour_to_draw = new VectorOfVectorOfPoint(max_contour);
CvInvoke.DrawContours(gray, contour_to_draw, 0, new MCvScalar(255), 2);
CvInvoke.Imshow("plate", gray);
RotatedRect plate_feature = CvInvoke.MinAreaRect(max_contour);
CvInvoke.WaitKey();
CvInvoke.DestroyAllWindows();
Try CvInvoke.threshold() instead of gray.ThresholdAdaptive(). Set proper threshold and you'll get better contour than before.

How to detect rectangles in image with emgu cv?

I am trying to detect rectangles in Emgucv in c#, I was playing around with the following code that I got off the internet. I am new to this so I hope someone could help me out:
public class ShapeDectection
{
public Image<Bgr, Byte> img;
public PictureBox Picture;
public PictureBox Result;
public double dCannyThres;
private UMat uimage;
private UMat cannyEdges;
private List<Triangle2DF> triangleList;
private List<RotatedRect> boxList;
private Image<Bgr, Byte> triangleRectImage;
public ShapeDectection(PictureBox pic, string filepath, PictureBox results)
{
Picture = pic;
Result = results;
img = new Image<Bgr, Byte>(filepath);
triangleList = new List<Triangle2DF>();
boxList = new List<RotatedRect>();
uimage = new UMat();
cannyEdges = new UMat();
dCannyThres = 180.0;
fnFindTriangleRect();
MessageBox.Show("done");
}
private void fnFindTriangleRect()
{
CvInvoke.CvtColor(img, uimage, ColorConversion.Bgr2Gray);
UMat pyrDown = new UMat();
CvInvoke.PyrDown(uimage, pyrDown);
CvInvoke.PyrUp(pyrDown, uimage);
triangleRectImage = img.CopyBlank();
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
CvInvoke.FindContours(cannyEdges, contours, null, RetrType.List, ChainApproxMethod.ChainApproxSimple);
int count = contours.Size;
MessageBox.Show("count = " + count);
for (int i = 0; i < count; i++)
{
using (VectorOfPoint contour = contours[i])
using (VectorOfPoint approxContour = new VectorOfPoint())
{
CvInvoke.ApproxPolyDP(contour, approxContour, CvInvoke.ArcLength(contour, true) * 0.05, true);
if (CvInvoke.ContourArea(approxContour, false) > 250) //only consider contour with area > 250
{
MessageBox.Show("approxContour.Size = " + approxContour.Size);
if (approxContour.Size == 3) //The contour has 3 vertices, is a triangle
{
Point[] pts = approxContour.ToArray();
triangleList.Add(new Triangle2DF(pts[0], pts[1], pts[2]));
}
else if (approxContour.Size == 4) // The contour has 4 vertices
{
#region Determine if all the angles in the contours are within [80,100] degree
bool isRectangle = true;
Point[] pts = approxContour.ToArray();
LineSegment2D[] edges = PointCollection.PolyLine(pts, true);
for (int j = 0; j < edges.Length; j++)
{
double dAngle = Math.Abs(edges[(j + 1) % edges.Length].GetExteriorAngleDegree(edges[j]));
MessageBox.Show("" + dAngle);
if (dAngle < 80 || dAngle > 100)
{
isRectangle = false;
break;
}
}
#endregion
if (isRectangle) boxList.Add(CvInvoke.MinAreaRect(approxContour));
}
}
}
}
}
foreach (Triangle2DF triangle in triangleList)
{
triangleRectImage.Draw(triangle, new Bgr(Color.DarkBlue), 2);
}
foreach (RotatedRect box in boxList)
triangleRectImage.Draw(box, new Bgr(Color.Red), 2);
Result.Image = triangleRectImage.ToBitmap();
}
}
I am trying to detect the shapes in the following picture
however this is the result:
as you can see no shape was detected by the script as the number of contours was zero.how can I modify the script so that I can detect these shapes?
I can't seem to find "Canny edge detection"
UMat cannyEdges = new UMat();
CvInvoke.Canny(uimage, cannyEdges, cannyThreshold, cannyThresholdLinking);
in your code. Here is the link to EmguCV wiki page http://www.emgu.com/wiki/index.php/Shape_(Triangle,_Rectangle,_Circle,_Line)_Detection_in_CSharp
Check there for missing parts.

AForge GetObjectsRectangles() gives false output

For me the Aforge rectangle detection gives completely false coordinates.
Here's my code:
public List<System.Drawing.Rectangle> Detect(string path)
{
var image = GetImage(path);
var blobCounter = new BlobCounter();
blobCounter.FilterBlobs = true;
blobCounter.MinWidth = 50;
blobCounter.MinHeight = 50;
blobCounter.ProcessImage(image);
var rects = blobCounter.GetObjectsRectangles();
return rects.ToList();
}
public System.Drawing.Bitmap GetImage(string path)
{
BitmapSource bSource = new BitmapImage(new Uri(path));
var image = Helpers.BitmapConverter.GetBitmap(bSource);
return image;
}
and my test image is this:
I've also tried reverting the colors, but nothing seems to help.
I always get just: {X = 16 Y = 42 Width = 51 Height = 141} That is obviously wrong for that rectangle in the image. How do I use Aforge to detect rectangles?
By using the AForge BlobCounter class GetObjectsInformation method.
Using the image provided, a snippet of the code provided in a simple Windows Forms Application with a picture box and a button I was able to detect the rectangle by drawing a lime green line over the original image using the following code.
//after pictureBox1's image has been set to the provided image.
Bitmap image = new Bitmap(pictureBox1.Image);
BlobCounter blobCounter = new BlobCounter();
blobCounter.FilterBlobs = true;
blobCounter.MinWidth = 50;
blobCounter.MinHeight = 50;
blobCounter.ProcessImage(image);
Blob[] blobs = blobCounter.GetObjectsInformation();
Blob blob = blobs[0];
SimpleShapeChecker shapeChecker = new SimpleShapeChecker();
Graphics g = Graphics.FromImage(image);
Pen redPen = new Pen(Color.Red, 2);
Pen greenPen = new Pen(Color.Lime, 2);
List<IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints(blob);
List<IntPoint> corners;
if (shapeChecker.IsConvexPolygon(edgePoints, out corners))
{
PolygonSubType subType = shapeChecker.CheckPolygonSubType(corners);
Pen pen;
if (subType == PolygonSubType.Unknown)
{
pen = (corners.Count == 4) ? redPen : redPen;
}
else
{
pen = greenPen;
}
System.Drawing.Point[] array = new System.Drawing.Point[corners.Count];
for (int i = 0, n = corners.Count; i < n; i++)
{
array[i] = new System.Drawing.Point(corners[i].X + 1, corners[i].Y + 1);
}
g.DrawPolygon(pen, array);
}
redPen.Dispose();
greenPen.Dispose();
g.Dispose();
pictureBox1.Image = image;
pictureBox1.Width = image.Width;
pictureBox1.Height = image.Height;
Provided Image
Processed Image

Categories

Resources