25

I am working with OpenCV on the Android platform. With the tremendous help from this community and techies, I am able to successfully detect a sheet out of the image.

These are the step I used.

  1. Imgproc.cvtColor()
  2. Imgproc.Canny()
  3. Imgproc.GausianBlur()
  4. Imgproc.findContours()
  5. Imgproc.approxPolyDP()
  6. findLargestRectangle()
  7. Find the vertices of the rectangle
  8. Find the vertices of the rectangle top-left anticlockwise order using center of mass approach
  9. Find the height and width of the rectangle just to maintain the aspect ratio and do warpPerspective transformation.

After applying all these steps I can easily get the document or the largest rectangle from an image. But it highly depends on the difference in the intensities of the background and the document sheet. As the Canny edge detector works on the principle of intensity gradient, a difference in intensity is always assumed from the implementation side. That is why Canny took into the account the various threshold parameters.

  1. Lower threshold
  2. Higher threshold

So if the intensity gradient of a pixel is greater than the higher threshold, it will be added as an edge pixel in the output image. A pixel will be rejected completely if its intensity gradient value is lower than the lower threshold. And if a pixel has an intensity between the lower and higher threshold, it will only be added as an edge pixel if it is connected to any other pixel having the value larger than the higher threshold.

My main purpose is to use Canny edge detection for the document scanning. So how can I compute these thresholds dynamically so that it can work with the both cases of dark and light background?

I tried a lot by manually adjusting the parameters, but I couldn't find any relationship associated with the scenarios.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ankur Gautam
  • 1,412
  • 5
  • 15
  • 27

2 Answers2

27

You could calculate your thresholds using Otsu’s method.

The (Python) code would look like this:

high_thresh, thresh_im = cv2.threshold(im, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
lowThresh = 0.5*high_thresh
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mailerdaimon
  • 6,003
  • 3
  • 35
  • 46
  • 1
    Im needs to be a gray scale image ? The main problem i am facing is, though the background looks different than the sheet of paper but in the gray scale ,since both of them almost the same intensity ,they look same. – Ankur Gautam Jan 24 '14 at 08:12
  • And hence canny edge detector is not able to detect the edges of the sheet that sharply(3 of the four edges are detected sharp but in the other edge ,there is a gap but depending upon the real life images it should not be advised to predict the other edge based on three edges ?) I tried with dilation to fill the gap ,but it seems creating other problems(giving some extra edges) based on the Kernal used(A rectangle of 2*2 generally). I will give it a try to compute parameters of canny using your method . And by the time if you have any thought ,please share with me. – Ankur Gautam Jan 24 '14 at 08:12
  • 1
    Maybe you should look for a better Segmentation strategy. – Mailerdaimon Jan 24 '14 at 08:14
  • I don't want to do the segmentation more exactly.I want to scan the pages out of the image(remove the extra things and warp perspective things) ,that is more like the functionality used by the camScanner app. They can easily detect the edges easily even on the almost same background. As i am new to openCV,i want to know if there is any filter or something else that enlarge the intensity difference between the parts of the image(i mean create more intensity gradient but in the original format) – Ankur Gautam Jan 24 '14 at 08:18
  • not giving me the desired result or perhaps i should say the bad as compared to previous parameters.Value of Higher Threshold comes out to be around 121 – Ankur Gautam Jan 24 '14 at 09:48
  • @Mailerdaimon You would need to use Otsu's method on the gradient magnitude image, not the image itself, no? – David Doria Mar 20 '16 at 00:49
  • @DavidDoria If your image has distinct foreground and background you can use it directly on the image, – Mailerdaimon Mar 21 '16 at 07:00
  • @Mailerdaimon Though it may "work" in easy cases, this seems like conceptually very much the wrong thing to use. The gradient magnitude image that gets thresholded is float valued in the range 0 - [thousands], so using a function of the Otsu threshold computed on the original image will always produce Canny thresholds that are at the very very low end of the range (since they are [0-255]), which basically just separates "definitely not edge" from "at least a little edge". In cases where this works I bet you'd get the same result if you just hard code "100" or even "200" as the threshold. – David Doria Mar 21 '16 at 12:08
  • Isn't the magnitude image [-thousands, +thousands] depending on the phase of the edges? Using Otsu on that wouldn't be very helpfull either... could work if you use the absolute of the magnitude gradient. I used the method as presented here and it worked on more than just trivial cases for me, but I haven't proved it to be correct scientifically. If you have the time and passion I would be happy to see an Answer from you with a new approach and a scientificall explanation though! – Mailerdaimon Mar 21 '16 at 12:35
  • @AnkurGautam -Have you fixed your problem? Can you please help me to fix the same problem? – NullPointer Jun 05 '20 at 07:59
6

Use the following snippet which I obtained from this blog:

v = np.median(gray_image)

#---- Apply automatic Canny edge detection using the computed median----
lower = int(max(0, (1.0 - sigma) * v))
upper = int(min(255, (1.0 + sigma) * v))
edged = cv2.Canny(gray_image, lower, upper)
cv2.imshow('Edges',edged)

##So what am I doing here?

I am taking the median value of the gray scale image. The sigma value of 0.33 is chosen to set the lower and upper threshold. 0.33 value is generally used by statisticians for data science. So it is considered here as well.

ssoler
  • 4,884
  • 4
  • 32
  • 33
Jeru Luke
  • 20,118
  • 13
  • 80
  • 87