0

I've got image of book

enter image description here:

And I need to contour it. I've tried this:

  1. make it gray
  2. blur
  3. threshold
  4. find contours using findContours()

But it doesn't work! I think it's all up to the blue part on the top, so I tried changing it's color to the color of book with cv.inRange(), but it didn't work, the contour was still unclear. Then I tried to change background color ( table color ) to green and try again. It worked nice, but only for that image, if I change background it don't work

Sometimes the contour also included "M" letter, maybe because it's near edge

Is there any way to do it?

gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
gray = cv.GaussianBlur(gray, (5, 5), 0)
#at this step I also tried using cv2.Canny(gray, 75, 200)
thresh = cv.adaptiveThreshold(blurred, 255,  cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY_INV, 21, 10)
#I also tried usual threshold and THRESH_BINARY, but it works worse

contours, _ = cv.findContours(thresh, cv.RETR_LIST, cv.CHAIN_APPROX_TC89_KCOS)

Boris Borais
  • 79
  • 1
  • 7
  • 1
    Could you supply the code you used to do the 4 steps? – Shmack Dec 25 '22 at 17:51
  • I also tried applying mathematical morphologies ( closing, then gradient ), but It doesn't work well either... And I've manipulated with tresh parameter in threshold() and with thresholding types... – Boris Borais Dec 25 '22 at 18:03
  • 1
    The color of your book and background are too similar, plus the color change. That makes it very hard using simple image processing. But AI/Deep Learning with training works. Your image has its background removed fine using http://remove.bg – fmw42 Dec 25 '22 at 18:06
  • @fmw42 I can remove background using cv.inRange() with corresponding color, but I want everything to be done automatically, without me – Boris Borais Dec 25 '22 at 18:07
  • Not easy to do with simple image processing for all situations. It might be better if your images were taken against a contrasting color. – fmw42 Dec 25 '22 at 18:09
  • I think I'll change book))) in case no one offers a solution or suggestion – Boris Borais Dec 25 '22 at 18:13
  • Have you tried [this](https://stackoverflow.com/questions/64345584/how-to-properly-use-cv2-findcontours-on-opencv-version-4-4-0)? – Shmack Dec 25 '22 at 19:01
  • Actually, I think this is better, but you're going to have to do some image pre processing to remove the background. – Shmack Dec 25 '22 at 19:38
  • For [background removal](https://www.freedomvc.com/index.php/2022/01/17/basic-background-remover-with-opencv/#:~:text=Background%20Remover%20with%20OpenCV%20%E2%80%93%20Method%201&text=Perform%20Gaussian%20Blur%20to%20remove,a%20mask%20of%20the%20foreground) – Shmack Dec 25 '22 at 20:18
  • @Shmack https://imgur.com/a/JeDktc2 unfortunately it doesn't work. wrote simple mask to remove it. your previous link is very useful , thank you. I think I'm on right way – Boris Borais Dec 25 '22 at 20:33
  • 1
    It has an error that the guy who answered needs to fix, but its not a hard fix - I left it in the comments of the post. – Shmack Dec 25 '22 at 21:05

1 Answers1

0

I've finally managed to do what I'd wanted Here are some tips in case you have the same problem:

  1. Don't go too far with blur. With contrast images it can make it worse

  2. Don't rely on built in functions. What worked for me was using morphological operations instead of using Canny() . https://ietresearch.onlinelibrary.wiley.com/doi/full/10.1049/joe.2018.9113 - article about improving Canny()

  3. Try changing kernel to find the perfect combination

  4. Play with thresholding types

  5. If thresholding doesn't work well, think about applying mask in order to clear background a busy cat

def kernel(n,m):
    kernel = np.ones((n,m), np.uint8)

    return kernel


img_path = sys.argv[1]
image = cv.imread(img_path)
img_init = image.copy()

hsv=cv.cvtColor(img_init,cv.COLOR_BGR2HSV)
brown_lo=np.array([3,0,0])
brown_hi=np.array([18,255,255])
mask=cv.inRange(hsv,brown_lo,brown_hi)
img_init[mask>0]=(0,0,0)


gray = cv.cvtColor(img_init, cv.COLOR_BGR2GRAY)

blurred = cv.GaussianBlur(gray, (3, 3), 0)

ret,thresh = cv.threshold(blurred,150,255,cv.THRESH_TOZERO_INV)


k = ''
n, m = 100, 100
img_closed = cv.erode(cv.dilate(thresh, kernel(n,m), iterations=1), kernel(n,m), iterations=1)

n,m = 5,5
img_grad = cv.dilate(img_closed, kernel(n,m), iterations=1) - cv.erode(img_closed, kernel(n,m), iterations=1)

contours, h = cv.findContours(img_grad, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)

c = max(contours, key = cv.contourArea)

image_semi = image.copy()
image_semi_2 = image.copy()
cv.drawContours(image_semi, contours, -1, (0, 255, 0), 2)

p = cv.arcLength(c , True)
ap = cv.approxPolyDP(c, 0.02*p, True)

image_c_max = cv.polylines(image, [ap], True, (0,255,0), thickness=5)

cv.imshow("Transformations", np.hstack([ gray, blurred, thresh, img_closed, img_grad ]))
cv.imshow("Result", np.hstack([ image_semi_2 , image_semi, image_c_max ]))
cv.waitKey(0)
cv.destroyAllWindows()
Boris Borais
  • 79
  • 1
  • 7
  • 2
    Yah, I remember seeing that - "... don't use canny..." or something to that effect; definitely using morphological operations. – Shmack Dec 26 '22 at 01:36
  • 1
    People overuse canny, just because they found it in some tutorial. They take it out of context. Usually the best method is thresholding and morphology, followed by contouring. – fmw42 Dec 26 '22 at 03:07