According to my research, Canny Edge Detector is very useful for detecting the edge of an image. After I put many effort on it, I found that OpenCV function can do that, which is
Imgproc.Canny(Mat image, Mat edges, double threshold1, double threshold2)
But for the low threshold and high threshold, I know that different image has different threshold, so can I know if there are any fast adaptive threshold method can automatically assign the low and high threshold according to different image?
This is relatively easy to do. Check out this older SO post on the subject.
A quick way is to compute the mean and standard deviation of the current image and apply +/- one standard deviation to the image.
The example in C++ would be something like:
Mat img = ...;
Scalar mu, sigma;
meanStdDev(img, mu, sigma);
Mat edges;
Canny(img, edges, mu.val[0] - sigma.val[0], mu.val[0] + sigma.val[0]);
Another method is to compute the median of the image and target a ratio above and below the median (e.g., 0.66*medianValue and 1.33*medianValue).
Hope that helps!
Opencv has an adaptive threshold function.
With OpenCV4Android it is like this:
Imgproc.adaptiveThreshold(src, dst, maxValue, adaptiveMethod, thresholdType, blockSize, C);
An example:
Imgproc.adaptiveThreshold(mInput, mInput, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY_INV, 15, 4);
As for how to choose the parameters, you have to read the docs for more details. Choosing the right threshold for each image is a whole different question.
Related
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.
The javadoc mentions signature of adaptive threshold function as
adaptiveThreshold(src, dst, maxValue, adaptiveMethod, thresholdType, blockSize, C)
I need to somehow put in values for blockSize and C(offset) automatically given a particular image. So I get a colored image which I convert to gray scale and then apply adaptive threshold as a pre-proccessing step for OCR.
Currently I hardcode the values for blocksize and C and see what gives me better result and then settle on that value. Is there a way to somehow find the best(or better) values for these parameters so that given a grayscale image my algorithm knows what would be good values for blocksize and 'C'.
PS: The adaptive threshold method that I am using is ADAPTIVE_THRESH_MEAN_C.
I have got a known problem with iris and pupil detection in images. I've already read some topics (such as:
What are the correct usage/parameter values for HoughCircles in OpenCV for Iris detection?
pupil Detection and cvHoughCircles?
Using HoughCircles to detect and measure pupil and iris
HoughCircles Parameters to recognise balls
about this issue, but still can't find solution of my problem.
I've got a eye image and what I want to do is detect iris and pupil. My problem is that, I can't select good parameter values.
This is my input sample picture:
And this is my output:
My code is posted below.
Mat src = Highgui.imread("in.jpg", Highgui.CV_LOAD_IMAGE_GRAYSCALE);
Mat des = new Mat(src.rows(), src.cols(), src.type());
Imgproc.GaussianBlur(src,src, new Size(3,3),0,0);
Imgproc.Canny(src, dst, 5, 10);
Mat circles = new Mat();
Imgproc.HoughCircles(source, circles, Imgproc.CV_HOUGH_GRADIENT, 1.0, 20.0, 70.0, 30.0, 3, 100);
//draw circles code here
I want to have a circled pupil and iris. Can somebody post correct values for my circle detection?
I also have few questions:
1) Is better to use Canny or Sobel filter?
2) Can I do this detection better, more flexible?
3) Can you simple explain me what exacly means HoughCircles parameters -
(from OpenCV javadoc)
* #param dp Inverse ratio of the accumulator resolution to the image
* resolution. For example, if <code>dp=1</code>, the accumulator has the same
* resolution as the input image. If <code>dp=2</code>, the accumulator has half
* as big width and height.
* #param param1 First method-specific parameter. In case of <code>CV_HOUGH_GRADIENT</code>,
* it is the higher threshold of the two passed to the "Canny" edge detector
* (the lower one is twice smaller).
* #param param2 Second method-specific parameter. In case of <code>CV_HOUGH_GRADIENT</code>,
* it is the accumulator threshold for the circle centers at the detection
* stage. The smaller it is, the more false circles may be detected. Circles,
Hough circles is not a very good method for iris/pupil detection. It is not very reliable and you'll always end up tuning too many parameters than you should.
After some basic thresholding or canny edge detection, feature detection methods like MSER work better in these cases. Here is a similar question with a solution.
Since you have a good resolution image, if you are looking to measure them or want something very accurate, I would suggest this blog. It has detailed explanation on the steps involved.
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.
I'm trying to tell if a given photo is blurry. I know that this is basically an impossible task, and that any metric will return undesirable results sometimes.
I'm wondering if there's a simple metric that at least tries to estimate blur that I can use though. Specifically, the task has a high tolerance for false positives. e.g. If I got something that eliminated 90% of blurry photos and 50% of non-blurry photos I would be very happy.
I'm trying to implement this in Java. I have an array of pixels (as ints). Please keep in mind I have a limited understanding of image processing techniques (fourier transforms, etc.), and I would love a very specific walkthrough of how to code a solution.
A very simple measure would be to apply a Sobel filter and investigate the overall energy of the filtered image. The more an image is blurred, the more edges vanish, the smaller the energy of the filtered image. Of course you'll run into problems with this approach when you try to determine a threshold for blurred vs. not blurred, but maybe this simple method will give you an idea.
Check wikipedia for the Sobel filter, and here is a code snippet to get out the edge ratio of an image. You can use these edge ratios to pair wise compare is images have more or less edges. Still, keep in mind that this is a simple approach and the answer of a.lasram is definitely correct.
float[] sobelX = {
-1, 0, 1,
-2, 0, 2,
-1, 0, 1,
};
BufferedImage image = ImageIO.read(new File("test.jpg"));
ColorConvertOp grayScaleOp = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
BufferedImage grayImage = grayScaleOp.filter(image, null);
BufferedImageOp op = new ConvolveOp( new Kernel(3, 3, sobelX) );
BufferedImage result = op.filter(grayImage, null);
WritableRaster r = result.getRaster();
int[] pixel = new int[r.getWidth()];
double countEdgePixels = 0;
for (int y = 0; y<r.getHeight();y++) {
// System.out.println("y = " + y);
r.getPixels(0, y, r.getWidth(),1, pixel);
for (int i = 0; i < pixel.length; i++) {
// create some stat out of the energy ...
if (pixel[i] > 128) {
countEdgePixels++;
}
}
}
System.out.printf("Edge pixel ratio = %4.4f\n", countEdgePixels/(double) (r.getWidth()*r.getHeight()));
ImageIO.write(result, "png", new File("out.png"));
As you've said you're not going to find a universal metric.
Also there are different types of blur: uniform, anisotropic, motion blur...
In general blurred images tend to exhibit low frequencies. A possible descriptor is the sum of magnitude of the k highest frequencies. Image with a low sum is likely to be blurred overall.
The magnitudes can be obtained in N*log(N) time using Fourier spectrum (high frequencies are far from the origin) or a Laplace pyramid (high frequencies correspond to the first scales).
Wavelet transform is another possible descriptor
A bit late reply but worth for the next person that will bump in this question.
I found in Google Scholars several papers that talk about averaging the sum of all edges in the picture compared to the width of all of them, as can be seen in this two articles: First and Second.
I have an better idea: what if we solve vice versa task? If we find sharpness image then we gain opposite to blur metric.
Some of articles are here:
A Perceptual Image Sharpness Metric Based on Local Edge Gradient Analysis
A No-Reference Objective Image Sharpness Metric Based on the Notion of Just Noticeable Blur (JNB)
Aberration correction by maximizing generalized sharpness metrics