pythonopencvimage-thresholding

Shape Detection with Adaptive Thresholding in Python/OpenCV


I have some problem with preprocessing of an image. I have this picture of a book on my desk:

img

I'm trying to get the shape of the book (a filled rectangle) in black&white using cv2 adaptive threshold

ts_img = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,11,2)

but what I get is just the edges of figure with a lot of noise, for example the edges of the title. Is there a way to get just the black filled rectangle that represents the book on white background? Thanks!


Solution

  • Since the book is blue, just search for the pixels where the blue channel is higher than r, you can make this also more robust by specifying factors and so on.

    Here is my approach:

    import cv2
    %matplotlib notebook
    import matplotlib.pyplot as plt
    import numpy as np
    imRGB = cv2.cvtColor(cv2.imread("book.jpg"), cv2.COLOR_BGR2RGB) # read and convert to RGB for easier plotting
    r,g,b = cv2.split(imRGB) # split into different channels
    mask = (b>r).astype(np.uint8) # where blue channel is higher
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # find contours
    largestContour = max(contours, key = cv2.contourArea) # get the largest contour by area
    imContoured = cv2.drawContours(imRGB.copy(), largestContour, -1, (255,0,0), 10) # draw contour
    cv2.imwrite("contoured.jpg", cv2.cvtColor(imContoured, cv2.COLOR_RGB2BGR)) # save image
    

    And the results:

    The results

    As fmw42 mentioned, the best answers are the simplest. cv2.adaptiveThreshold is not an easy function to use and in this case, the obvious approach should be to go with colour channels instead of dynamic thresholds