Today I started using Tesseract to parse portions of my screen for numbers. I have had a decent amount of success with larger text (which results in a higher resolution image). Now I am trying to use Tesseract in a more practical sense and the image quality is too low. I have tried increasing the resolution and redrawing with anti-aliasing, but I am not sure if I am even doing these things right. Do you have any suggestions as to how I might be able to get Tesseract to recognize the "12" in my tiny image?
Image:
static public void test()
{
string readIn;
TesseractEngine engine = new TesseractEngine(#".\tessdata","eng", EngineMode.Default);
engine.SetVariable("tessedit_char_whitelist", "0123456789"); //only read as numbers
Rectangle rect = new Rectangle(181, 107, 25, 25);
Bitmap bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bmp);
g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
g.InterpolationMode = InterpolationMode.High;
g.CompositingQuality = CompositingQuality.HighQuality;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.DrawImage(bmp, rect.Width, rect.Height); //Do some anti-aliasing hopefully?
bmp.SetResolution(300, 300) //Try increasing resolution??
bmp.Save(#".\tmp.jpg");
readIn = engine.Process(PixConverter.ToPix(bmp)).GetText();
Console.WriteLine("This is what was read: " + readIn); //Empty
}
I suggest using image processing methodes to improve the accuracy of tesseract-ocr. I use OpenCV libraries in c++ for this.
So let's take your image and rescale it by +500%:
You can see the image is getting a bit pixely. In this case you want to smooth the edges by using a Gaussian filter. I used a Gaussian filter with a kernal size of 3x3:
The last thing you need to do is segmentation of the digits by using a threshold:
Running tesseract on the segmented image using the digit whitelist will result in "12".
Hope this helped. :)
Related
What I'm trying to do:
Since in my bitmaps there are some unwanted white edges around the picture that result from anti-aliasing as pointed out from another user from stackoverflow.
I'm trying to convert an image that's inputted into a bitmap, convert bitmap into a Graphics object so that I can set the Smooth Mode to none, and then finally convert that Graphics object to a bitmap so that it can be copied by the user after setting it to the clipboard. I'm not sure if this is a good way of getting rid anti-aliasing in bitmaps but I'm definitely interested in improvements and suggestions.
The issue I'm facing:
The result of the image after is completely blank and does not contain any of the pixels that are previously found in the original bitmap. Here's the result:
This issue applies to all pictures no matter what their format is.
My code:
public PicGen(PictureBox pictureBox)
{
Clipboard.Clear();
Bitmap firstImage = new(pictureBox.Image, pictureBox.Width, pictureBox.Height);
RectangleF cloneRect = new RectangleF(0, 0, firstImage.Width, firstImage.Height);
System.Drawing.Imaging.PixelFormat format = firstImage.PixelFormat;
Bitmap cloneBitmap = firstImage.Clone(cloneRect, format);
Graphics AntiARemover = Graphics.FromImage(cloneBitmap);
AntiARemover.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
Bitmap finalImg = new(52, 52, AntiARemover);
Clipboard.SetImage(finalImg);
Color backColorBottom = firstImage.GetPixel(0, 0);
firstImage.ReplaceColor(backColorBottom, Color.FromArgb(54, 57, 63));
Bitmap finalImg = new(52, 52, AntiARemover);
From the documentation for this bitmap constructor:
The new Bitmap that this method creates takes its horizontal and vertical resolution from the DpiX and DpiY properties of g, respectively.
If you want create a new image with the content from another you need to call one of the DrawImage methods. You should also dispose your graphics object, and any temporary bitmaps you may use.
using var finalImg = new Bitmap(52,52);
using var graphics = Graphics.FromImage(finalImg);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
graphics.DrawImage(cloneBitmap)
However, edge artifacts typically occur when combining two images using an alpha channel, see Premultiplied alpha. In your example I can only see one input image, so I'm really not sure what it is you are actually trying to do. If you need to convert to premultiplied alpha you can use the following code to convert the color for each pixel
premultiplied.R = (byte)(straight.R * straight.A / 255);
premultiplied.G = (byte)(straight.G * straight.A / 255);
premultiplied.B = (byte)(straight.B * straight.A / 255);
premultiplied.A = straight.A;
I need to shrink multiple images that contain text. Because of the text they need to be shrunk in such a way as to retain the sharp edges of the text and not smoothed. My first attempt was the following:
RenderOptions.SetBitmapScalingMode(upgradeCard, BitmapScalingMode.HighQuality);
upgradeCard.Height(resizedHeight);
upgradeCard.Width(resizedWidth);
The result was too blurry, the text was hard to read. It was, however, really really fast. I then tried this:
public static class ImageResizer
{
public static Image Resize(Image image, Size size)
{
if (image == null || size.IsEmpty)
return null;
var resizedImage = new Bitmap(size.Width, size.Height, image.PixelFormat);
resizedImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(resizedImage))
{
var location = new Point(0, 0);
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.None;
graphics.DrawImage(image, new Rectangle(location, size),
new Rectangle(location, image.Size), GraphicsUnit.Pixel);
}
return resizedImage;
}
}
This worked really well, almost as good as Photoshop Bicubic Sharper. Unfortunately it was also very slow. Way too slow for what I need.
Is there any other way of doing this that produces the results of the second method but does so fairly quickly?
Without examples of your images it's hard to give reliable advice.
For example, how much contrast is in your images already? By what factor are you reducing them?
You could try nearest neighbour scaling, which can be very fast, and then try blurring the output slightly with a Gaussian filter, or similar. If that's too aliased, you could also try linear scaling with a soft blur.
I am trying to detectect the bounding box of sentences in an Image. I am using Emgu OpenCV in C# using HougLinesP method to extract lines, but I am obviously doing something wrong. I have looked at many examples and estimate the skew level with houghLines is pretty much what I am trying to do.
Using that sample image I do some pre-processing (Thresholding, canny, ect) and end up with http://snag.gy/sWCuO.jpg, but then when I do HoughLines and draw the lines on the original image, I get http://snag.gy/ESKmR.jpg .
Here is an extract of my code:
using (MemStorage stor = new MemStorage())
{
Image<Hsv, byte> imgHSV = new Image<Hsv, byte>(bitmap);
Image<Gray, Byte> gray = imgHSV.Convert<Gray, Byte>().PyrDown().PyrUp();
CvInvoke.cvCanny(gray, EdgeMap, 100, 400, 3);
IntPtr lines = CvInvoke.cvHoughLines2(EdgeMap, stor,
Emgu.CV.CvEnum.HOUGH_TYPE.CV_HOUGH_PROBABILISTIC, 1, Math.PI / 360, 10,
gray.Width / 4, 20);
Seq<LineSegment2D> segments = new Seq<LineSegment2D>(lines, stor);
ar = segments.ToArray();
}
Graphics g = Graphics.FromImage(OriginalImage);
foreach (LineSegment2D line in ar)
{
g.DrawLine(new Pen(Color.Blue),
new Point(line.P1.X, line.P1.Y),
new Point(line.P2.X, line.P2.Y));
}
g.Save();
Any help would be appreciated.
You can try two approaches:
1- Utilize the frequency domain. Example here
2- After pre-processing, extract the contours, collect all the points (or at least collect all points that are not black); find the minimum bounding rectangle with its angle. Example here
I'd like to take a bitmap with an ARGB 32 pixel format and clip it so that the contents within its inscribed ellipse remain, and anything outside the ellipse turns into ARGB(0,0,0,0).
I could do it programmatically using GetPixel and SetPixel and some trigonometry to figure out which pixel is out of bounds - but I suspect there's a better, more built-in way to do it.
Any ideas?
Thanks to Alessandro D'Andria for pointing out the region part - I've figured out the rest:
public Bitmap Rasterize()
{
Bitmap ringBmp = new Bitmap(width: _size.Width, height: _size.Height, format: PixelFormat.Format32bppArgb);
//Create an appropriate region from the inscribed ellipse
Drawing2D.GraphicsPath graphicsEllipsePath = new Drawing2D.GraphicsPath();
graphicsEllipsePath.AddEllipse(0, 0, _size.Width, _size.Height);
Region ellipseRegion = new Region(graphicsEllipsePath);
//Create a graphics object from our new bitmap
Graphics gfx = Graphics.FromImage(ringBmp);
//Draw a resized version of our image to our new bitmap while using the highest quality interpolation and within the defined ellipse region
gfx.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor;
gfx.SmoothingMode = Drawing2D.SmoothingMode.HighQuality;
gfx.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality;
gfx.PageUnit = GraphicsUnit.Pixel;
gfx.Clear(Color.Transparent);
gfx.Clip = ellipseRegion;
gfx.DrawImage(image: _image, rect: new Rectangle(0, 0, _size.Width, _size.Height));
//Dispose our graphics
gfx.Dispose();
//return the resultant bitmap
return ringBmp;
}
Apparently it is extremely important to set PixelOffsetMode to HighQuality, because otherwise the DrawImage method would crop parts of the resulting image.
I have an image of Go board with some stones. I need to detect these stone. I was trying AForge's IsCircle Function but image's background have to be turn to black first. But I need to detect black circle.
Is it possible? or is there any other solution with other library (OpenCV)?
You can convert black pixel to white pixel and white pixel to black pixel and then use Iscircle function to detect circle here is code to convert using Aforge.net
Bitmap orig = (Bitmap)pictureBox1.Image;
Bitmap clone = new Bitmap(orig.Width, orig.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(clone))
{
gr.DrawImage(orig, new Rectangle(0, 0, clone.Width, clone.Height));
}
FiltersSequence commonSeq = new FiltersSequence();
commonSeq.Add(Grayscale.CommonAlgorithms.BT709);
commonSeq.Add(new BradleyLocalThresholding());
commonSeq.Add(new DifferenceEdgeDetector());
Bitmap temp = commonSeq.Apply(clone);