Rayees Ck
Rayees Ck

Reputation: 33

Contour Identification using OpenCV

I have collection of objects in a image. Check the sample input image here.

I would like to find the contour of each object. I am following the below approach to identifying the contour using OpenCV2

gray = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
edged = cv2.Canny(gray, 50, 100)
dilate= cv2.dilate(edged, None, iterations=1)
erode= cv2.erode(dilate, None, iterations=1)
cnts = cv2.findContours(erode, cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE)

This is the contour output which I am getting for the above code : see output image

Is there any better approach for identifying the object in the image?

Upvotes: 3

Views: 5296

Answers (1)

ZdaR
ZdaR

Reputation: 22954

You have missed a simple step in your code snippet, cv2.findContours() works best on binary images, but you are simply passing the gray scale image to cv2.findContours. I have followed the following steps to segment out the apples from background:

Step 1: Segment out the background which majorly contains gray-scale pixels.

You can use HSV color domain here, where low value of saturation would get the background segmented as:

img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV_FULL)

# Filter out low saturation values, which means gray-scale pixels(majorly in background)
bgd_mask = cv2.inRange(img_hsv, np.array([0, 0, 0]), np.array([255, 30, 255]))

enter image description here

Step 2: For pitch black pixels, the saturation value was abrupt so we segmented the extreme black and white pixels:

# Get a mask for pitch black pixel values
black_pixels_mask = cv2.inRange(img_bgr, np.array([0, 0, 0]), np.array([70, 70, 70]))

# Get the mask for extreme white pixels.
white_pixels_mask = cv2.inRange(img_bgr, np.array([230, 230, 230]), np.array([255, 255, 255]))

Black pixels mask White pixels mask

Step 3: Merge these masks to get a final mask for cv2.findContours:

final_mask = cv2.max(bgd_mask, black_pixels_mask)
final_mask = cv2.min(final_mask, ~white_pixels_mask)
final_mask = ~final_mask

Merged mask

Step 4: Now to fill in the holes, we erode and dilate the image:

final_mask = cv2.erode(final_mask, np.ones((3, 3), dtype=np.uint8))
final_mask = cv2.dilate(final_mask, np.ones((5, 5), dtype=np.uint8))

enter image description here

Step 5: Use cv2.findContours() to get the contours and filter them on area to remove the smaller ones:

# Now you can finally find contours.
im, contours, hierarchy = cv2.findContours(final_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

final_contours = []
for contour in contours:
    area = cv2.contourArea(contour)
    if area > 2000:
        final_contours.append(contour)

Step 6: Show the final contours

Final output

Here is full code snippet:

import cv2
import numpy as np

img_bgr = cv2.imread("/home/anmol/Downloads/tWuTW.jpg")
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV_FULL)

# Filter out low saturation values, which means gray-scale pixels(majorly in background)
bgd_mask = cv2.inRange(img_hsv, np.array([0, 0, 0]), np.array([255, 30, 255]))

# Get a mask for pitch black pixel values
black_pixels_mask = cv2.inRange(img_bgr, np.array([0, 0, 0]), np.array([70, 70, 70]))

# Get the mask for extreme white pixels.
white_pixels_mask = cv2.inRange(img_bgr, np.array([230, 230, 230]), np.array([255, 255, 255]))

final_mask = cv2.max(bgd_mask, black_pixels_mask)
final_mask = cv2.min(final_mask, ~white_pixels_mask)
final_mask = ~final_mask

final_mask = cv2.erode(final_mask, np.ones((3, 3), dtype=np.uint8))
final_mask = cv2.dilate(final_mask, np.ones((5, 5), dtype=np.uint8))

# Now you can finally find contours.
im, contours, hierarchy = cv2.findContours(final_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

final_contours = []
for contour in contours:
    area = cv2.contourArea(contour)
    if area > 2000:
        final_contours.append(contour)


for i in xrange(len(final_contours)):
    img_bgr = cv2.drawContours(img_bgr, final_contours, i, np.array([50, 250, 50]), 4)


debug_img = img_bgr
debug_img = cv2.resize(debug_img, None, fx=0.3, fy=0.3)
cv2.imwrite("./out.png", debug_img)

Upvotes: 8

Related Questions