How to detect a bean defect using python3

regular bean sample

defect bean sample-1

I want to judge whether a bean is normal or defective. I tried to use Canny method (to find edge) etc ... but I failed. I just solve this using a form. (Cracked bean and unformed bean ...) Please give me some idea to solve this problem.

Sorry for my English, this is not my first language.

+3


source to share


1 answer


One of the most widely used methods to "fill in cracks" in an image is stretch-blur. In simple terms, you make your binary image "grow" at the edges, so the cracks are filled, then you change the process and make it “squash" at the edges, but as the cracks fill in, information about them remains in the image, so they remain filled. Perhaps you can use that and then look at the difference between your original image and the stretch-blur image: if there were no cracks there would be little difference, and if there were a lot of cracks, there would be a big difference.

For example. Let's convert our image to a binary black and white mask:

def get_th_binary_mask(img):
    gs = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ret, thresh = cv2.threshold(gs, 0, 255, cv2.THRESH_BINARY)
    mask = np.zeros(thresh.shape, np.uint8)
    mask[thresh == 255] = 0
    mask[thresh == 0] = 1
    return mask

      

Now we just add the elements of the matrix, which gives us the number of white pixels, stretch-blur, sum again, subtract the sums:

def get_de_difference(binary_image):
    s_before = np.sum(binary_image)
    kernel = np.ones((17, 17), np.uint8)
    d = cv2.dilate(binary_image, kernel, 1)
    d = cv2.erode(d, kernel, 1)
    s_after = np.sum(d)
    return abs(s_after - s_before)

      



For a "good" bean it gives 72 different pixels, for a "bad" one - 1158.

Can be further improved with a more complex threshold function, for example based on Otsu and Grab cut:

def get_gc_binary_mask(img):
    gs = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    ret, thresh = cv2.threshold(gs, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    mask = np.zeros(thresh.shape, np.uint8)
    mask[thresh == 255] = cv2.GC_PR_BGD
    mask[thresh == 0] = cv2.GC_FGD

    bgdModel = np.zeros((1,65),np.float64)
    fgdModel = np.zeros((1,65),np.float64)

    cv2.grabCut(img, mask, (0, 0, 1365, 767), bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_MASK)

    mask2 = np.where((mask==2)|(mask==0),0,1).astype('uint8')
    return mask2

      

Using it instead of the previous one gives only 1 pixel difference for the "good" bean (despite the unfortunate artifact - see below), while for the "bad" bean it gives 741. Or, if you can change the background of your pictures, just put a bright green / blue leaf before taking pictures and using color keyboard.

This is how it looks, from left to right: original images (column 1), baseline, stretch, blur, otsu / grabcut threshold, expand, blur. The difference between columns 2 and 4 and between columns 5 and 7 matters. enter image description here

+1


source







All Articles