OpenCV - Remove noise in image using Java - java

I am trying to create an OCR application. I need to locate the text using contours. However, my image has a lot of noise and I was wondering if there is a way to remove it.
My current code:
// Input image already converted to a matrix
Imgproc.cvtColor(matrixImage, matrixImage, Imgproc.COLOR_BGR2GRAY);
// Gaussian blur
Imgproc.GaussianBlur(matrixImage, matrixImage, new Size(7,7), 0);
Imgproc.threshold(matrixImage, matrixImage, 125, 255, Imgproc.THRESH_BINARY_INV);
// This is my current approach for removing noise. However, there is still
// a lot of random areas that can be removed.
// Remove specs from image
Mat morphingMatrix = Mat.ones(3,3, CV_8UC1);
Imgproc.morphologyEx(matrixImage, matrixImage, Imgproc.MORPH_OPEN, morphingMatrix);
// Image denoising
Photo.fastNlMeansDenoising(matrixImage, matrixImage);
My input image. I allow users to manually mark the corners so the transformed image below only applies the transformation to the middle white piece of paper.

I have a solution for your problem. However, it does not involve removal of noise in this case.
Step 1:
I have obtained my own transformed image from the original image uploaded by you:
I presume you know how to perform transformations on images as stated in your question. Nevertheless to learn more about them visit THIS. To learn about homographic proJections visit THIS SITE.
I obtained the gray scale of this image:
Step 2:
To this image I performed adaptive threshold using Gaussian filter:
Step 3:
This step involves a couple of morphological operations:
Firstly, to remove the unwanted spots in the image, I used morphological closing operation:
Secondly, I used morphological dilation operation (which you may not need unless you want to highlight your text):

Related

Downscale image for MNIST

I'm trying to solve MNIST classification problem on Android devices. I have a trained model already, now I want to be able to recognize a single digit on the photo.
After taking a photo I make some pre-processing, before passing image to the model.
Here's an example of original image:
After that I make it black-and-white only, so it starts looking like this:
Please, don't pay attention to the changes in dimensions - the were introduced by the way I make screenshots, in the app both images still have the same size.
After casting it to BW colors I extract the number's blob, downscale it to 20*20 (respecting the aspect ratio) and then add padding aroung to make it fit the MNIST 28*28 size. The final result is the following:
Notice, that I upscaled image to show up the problem. And the problem is the following: after downscaling a lot of useful information gets lost. Sometimes the whole edges of the number are gone. Is there any way to avoid it? Maybe I can somehow make white lines thicker before downscaling?
P.S. I use Catalano framework for images processing.
EDIT After applying the suggested filter from the answer here's what I get:
I'm not sure about the framework you've mentioned,
but one thing that can be of help here, is to use some morphological operations on the original image, before going for MNIST style normalization.
Namely, one can do an erosion as follows (I'm recording the approach in python, there should be analogues in the framework you use, as the operations are pretty standard).
import numpy as np
import cv2
xx = cv2.imread('6.jpg') # your original image of 6
kernel = np.ones((20,20), np.uint8)
erosion = cv2.erode(xx, kernel, iterations = 2)
cv2.imwrite('6A.jpg',erosion) # this will be used as a replacement for the original image
this will produce something that looks like this. Then, if you do the binarization of the new image (say threshold by gray intensity 150), and do the resize followed by padding, you should get something like this one, which is more robust.
Note also, that you need to centralize the image at the very last stage (against its center of mass) before feeding to any classifier.
The end result, in MNIST's standards is as follows ( physical dimensions 28x28).

Image preprocessing with OpenCV before doing character recognition (tesseract)

I'm trying to develop simple PC application for license plate recognition (Java + OpenCV + Tess4j). Images aren't really good (in further they will be good). I want to preprocess image for tesseract, and I'm stuck on detection of license plate (rectangle detection).
My steps:
1) Source Image
Mat img = new Mat();
img = Imgcodecs.imread("sample_photo.jpg");
Imgcodecs.imwrite("preprocess/True_Image.png", img);
2) Gray Scale
Mat imgGray = new Mat();
Imgproc.cvtColor(img, imgGray, Imgproc.COLOR_BGR2GRAY);
Imgcodecs.imwrite("preprocess/Gray.png", imgGray);
3) Gaussian Blur
Mat imgGaussianBlur = new Mat();
Imgproc.GaussianBlur(imgGray,imgGaussianBlur,new Size(3, 3),0);
Imgcodecs.imwrite("preprocess/gaussian_blur.png", imgGaussianBlur);
4) Adaptive Threshold
Mat imgAdaptiveThreshold = new Mat();
Imgproc.adaptiveThreshold(imgGaussianBlur, imgAdaptiveThreshold, 255, CV_ADAPTIVE_THRESH_MEAN_C ,CV_THRESH_BINARY, 99, 4);
Imgcodecs.imwrite("preprocess/adaptive_threshold.png", imgAdaptiveThreshold);
Here should be 5th step, which is detection of plate region (probably even without deskewing for now).
I croped needed region from image (after 4th step) with Paint, and got:
Then I did OCR (via tesseract, tess4j):
File imageFile = new File("preprocess/adaptive_threshold_AFTER_PAINT.png");
ITesseract instance = new Tesseract();
instance.setLanguage("eng");
instance.setTessVariable("tessedit_char_whitelist", "acekopxyABCEHKMOPTXY0123456789");
String result = instance.doOCR(imageFile);
System.out.println(result);
and got (good enough?) result - "Y841ox EH" (almost true)
How can I detect and crop plate region after 4th step? Have I to make some changes (improvements) in 1-4 steps? Would like to see some example implemented via Java + OpenCV (not JavaCV).
Thanks in advance.
EDIT (thanks to #Abdul Fatir's answer)
Well, I provide working (for me atleast) code sample (Netbeans+Java+OpenCV+Tess4j) for those who interested in this question. Code is not the best, but I made it just for studying.
http://pastebin.com/H46wuXWn (do not forget to put tessdata folder into your project folder)
Here's how I suggest you should do this task.
Convert to Grayscale.
Gaussian Blur with 3x3 or 5x5 filter.
Apply Sobel Filter to find vertical edges.
Sobel(gray, dst, -1, 1, 0)
Threshold the resultant image to get a binary image.
Apply a morphological close operation using suitable structuring element.
Find contours of the resulting image.
Find minAreaRect of each contour. Select rectangles based on aspect ratio and minimum and maximum area.
For each selected contour, find edge density. Set a threshold for edge density and choose the rectangles breaching that threshold as possible plate regions.
Few rectangles will remain after this. You can filter them based on orientation or any criteria you deem suitable.
Clip these detected rectangular portions from the image after adaptiveThreshold and apply OCR.
a) Result after Step 5
b) Result after Step 7. Green ones are all the minAreaRects and the Red ones are those which satisfy the following criteria: Aspect Ratio range (2,12) & Area range (300,10000)
c) Result after Step 9. Selected rectangle. Criteria: Edge Density > 0.5
EDIT
For edge-density, what I did in the above examples is the following.
Apply Canny Edge detector directly to input image. Let the cannyED image be Ic.
Multiply results of Sobel filter and Ic. Basically, take an AND of Sobel and Canny images.
Gaussian Blur the resultant image with a large filter. I used 21x21.
Threshold the resulting image using OTSU's method. You'll get a binary image
For each red rectangle, rotate the portion inside this rectangle (in the binary image) to make it upright. Loop through the pixels of the rectangle and count white pixels. (How to rotate?)
Edge Density = No. of White Pixels in the Rectangle/Total no. of Pixels in the rectangle
Choose a threshold for edge density.
NOTE: Instead of going through steps 1 to 3, you can also use the binary image from step 5 for calculating edge density.
Actually OpenCV has pre-trained model specially for Russian license plates: haarcascade_russian_plate_number
Also there is open source ANPR project for Russian license plates: plate_recognition. It is not use tesseract, but it has quite good pre-trained neural network.
You find all connected components (the white areas) and determine their outline.
If you filter them based on size (as part of the image), ratio (width-height) and white/black ratio to retrieve candidate-plates.
Undo the transformation of the rectangle
Remove the bolts
Pass in image to the OCR engine.

Edge detection using OpenCV (Canny)

I'm trying to detect rectangles using OpenCV. However, sometimes this is getting pretty difficult after running the Canny method, because two of the edges are usually being erased out. I've tried many different sets of thresholds and blurring it before applying Canny, but I haven't got major positive results yet. Currently, I'm not blurring the image, so this is pretty much what I'm doing:
Mat imgSource = Highgui.imread(filepath);
Imgproc.Canny(imgSource, imgSource, 300, 600, 5, true);
Example:
original http://imagizer.imageshack.us/a/img822/8776/27i9j.jpg
Canny http://imagizer.imageshack.us/a/img841/9868/wkc95.jpg
Then, I'm trying OpenCV's findContours method to detect the rectangle, it works 80% of the time, how can I improve it?
Try with different threshold value, in this case you will get better result when using lower threshold values, like 10,100.
blur(src,src,Size(3,3));
cvtColor(src,tmp,CV_BGR2GRAY);
Canny( src, thr, 10, 100, 3 );
Or in another way you will get the contour images by applying threshold
like,
threshold(tmp,thr,50,255,THRESH_BINARY_INV);
the problem here is the image compression JPEG file type probably.
try converting image to monochrome since you only have Black/white image
and edit the threshold value. this should eliminate the noise around the edges of the lines.
then canny can be applied with any values.

image enhancement of plots and other line diagrams

I am looking for library routines for the image enhancement of (scientific) plots and diagrams. Typical examples are shown in
http://www.jcheminf.com/content/pdf/1758-2946-4-11.pdf
and Figure 3 of http://en.wikipedia.org/wiki/Anti-aliasing
These have the features that:
They usually use a very small number of primitives (line, character, circle, rectangle)
They are usually monochrome (black/white) or have a very small number of block colours
The originals have no gradients or patterns.
I wish to reconstruct the primitives and am looking for an algorithm to restore clean lines in the image before the next stage of analysis (which may include line detection and OCR). The noise often comes from :
use of JPGs (the noise is often seen close to the original primitive)
antialiasing
I require Free/Open Source solutions and would ideally like existing Java libraries. If there are any which already do some of the job or reconstructing lines that would be a bonus! For characters recognition I would be happy to isolate each character at this stage and defer OCR, though pointers to that would also be appreciated.
UPDATE:
I am surprised that even with a bounty there have been no substantive replies to the question. I am therefore investigating it myself. I still invite answers but they should go beyond my own answer.
ANSWER TO OWN QUESTION
Since there there have been no answers after nearly a week here is what I now plan:
I found mention of the Canny edge-detection algorithm on another SO post and then found:
[http://www.tomgibara.com/computer-vision/canny-edge-detector][2]
from Tom Gibara.
This is very easy to use in default mode and the main program is:
public static void main(String[] args) throws Exception {
File file = new File("c.bmp");
//create the detector
CannyEdgeDetector detector = new CannyEdgeDetector();
//adjust its parameters as desired
detector.setLowThreshold(0.5f);
detector.setHighThreshold(1f);
//apply it to an image
BufferedImage img = ImageIO.read(file);
detector.setSourceImage(img);
detector.process();
BufferedImage edges = detector.getEdgesImage();
ImageIO.write(edges, "png", new File("c.png"));
}
Here ImageIO reads and writes bitmaps. The unprocessed image is read as a 24-bit BMP (ImageIO seems to fail with lower colour range). The defaults are Gibara's out-of-the-box.
The edge detection is very impressive and outlines all the lines and characters. This bitmap
is converted to the edges
So now I have two tasks:
fit straight lines to the outlines, which are essentially clean "tramlines". I expect this to be straightforward for clean diagrams. I'd be grateful for any mention of Java libraries to fit line primitives to outlines.
recognize the characters. Gibara has done an excellent job of separating them and so this is an exercise of recognising the individual glyphs. I can use the outlines to isolate the individual pixel maps for each glyph and then pass these to JavaOCR. Alternatively the outlines may be good enough to recognize the characters directly. I do NOT know what the font is, but most characters are in the 32-255 range and I believe I can build up heuristic maps.
See How do I properly load a BufferedImage in java? for loading bitmaps in Java
Java Library
OpenCV is the go-to library for computer vision tasks like this. There are Java bindings here: http://code.google.com/p/javacv/ . OpenCV covers everything from basic image processing filters to high-level object and motion detection algorithms.
Line Detection
For detecting straight lines, try the Hough Transform. The OpenCV Tutorials have a good explanation: http://opencv.itseez.com/doc/tutorials/imgproc/imgtrans/hough_lines/hough_lines.html#how-does-it-work
The classical Hough transform outputs infinite lines, but OpenCV also implements a variant called the Probabilistic Hough Transform that outputs line segments. It should give what you need. The original academic paper is here: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.34.9440&rep=rep1&type=pdf
Once you detect line segments, you might want to detect linked line segments and join them together. For your simple images, you will probably do just fine with a brute-force comparison of all segment endpoints. If you detect more than one endpoint within a small radius, say 2 pixels, join them together to make sure your lines are continuous. You can also measure the angle between joined line segments to detect polygons.
Circle Detection
There is another version of the Hough transform that can detect circles, explained here: http://opencv.itseez.com/doc/tutorials/imgproc/imgtrans/hough_circle/hough_circle.html#hough-circle
I wish to reconstruct the primitives and am looking for an algorithm
to restore clean lines in the image before the next stage of analysis
(which may include line detection and OCR).
Have you looked at jaitools? ( http://code.google.com/p/jaitools/ ).
They have API for vectorizing graphics which are quite fast and flexible; see API and docs here: http://jaitools.org/

Java image library - turn grid image into array

If I have an image of a table of boxes, with some coloured in, is there an image processing library that can help me turn this into an array?
Thanks
You can use a thresholding function to binarize the image into dark/light pixels so dark pixels are 0 and light ones are 1.
Then you would want to remove image artifacts using dilation and erosion functions to remove noise (all these are well defined on Wikipedia).
Finally if you know where the boxes are, you can just get the value in the center of each box to determine the array value, or possibly use an area near the center and take the prevailing value (i.e. more 0's is a filled in square, more 1's is and empty square).
If you are scanning these boxes and there is a lot of variation in the position of the boxes, you will have to perform some level of image registration using known points, or fiducials.
As far as what tools to use to do this, I'd recommend first trying this manually using a tool like ImageJ, which has a UI and can also be used programatically since it is written all in Java.
Other good libraries for this include OpenCV and the Java Advanced Imaging API.
Your results will definitely vary depending on the input images and how consistenly lit and positioned they are.
The best way to see how it will do for your data is to try applying these processing steps manually to see where your threshold value should be, how much dilating/eroding you need to get consistent results.

Categories

Resources