Search code examples
androidimageopencvimage-processingcontour

OpenCV contours not closing


Good day everyone!

I got an issue in regard of creating contours. I tried all configurations and unfortunately it is not possible for me to get one big contour of the object. First file is loaded into bitmap and in the end the bitmap is loaded to file. (Not shown here as it gives nothing).

Image before:

img1

Image after:

img2

The effect I would like to have is just the outer contour of the whole car or at least some approximation of it. The code I currently have (after looking everywhere) is this:

bitmap = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(filePath), 500, 500);
    Mat src = new Mat();
    Utils.bitmapToMat(bitmap, src);
    Mat gray = new Mat();
    Imgproc.cvtColor(src, gray, Imgproc.COLOR_RGBA2GRAY);
    Imgproc.Canny(gray, gray, 0, 1000);
    List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
    Mat hierarchy = new Mat();
    Imgproc.findContours(gray, contours, hierarchy, Imgproc.CV_RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
    for (int contourIdx = 0; contourIdx < contours.size(); contourIdx++) {
        Imgproc.drawContours(src, contours, contourIdx, new Scalar(0, 0, 255), -1);
    }
    Utils.matToBitmap(gray, bitmap);

Solution

  • Whenever you have a bunch of complex colors in an image, always convert it to another colorspace before trying segmenting your object of interest.

    In the example provided I converted the image to various color spaces like HSV, LAB and YCrCb (google them for more info). The hue channel in HSV image turned out to be for prominent for segmentation.

    Here is my result:

    enter image description here

    Pseudocode:

    1. Obtained a threshold image of the hue channel image
    2. Performed dilation on the inverted image of the hue channel.
    3. Found the contour having the greatest area coverage and marked it

    EDIT

    Here is the code:

    import cv2
    
    path = 'C:/Users/Desktop/'
    img = cv2.imread(path + 'car.jpg')
    img = cv2.resize(img, (0,0), fx=0.25, fy=0.25)  #-- image was too big hence I had to resize it
    
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)      #-- HSV conversion
    h, s, v = cv2.split(hsv)
    
    cv2.imshow('hsv Image', hsv)
    cv2.imshow('h Image', h)
    cv2.imshow('s Image', s)
    cv2.imshow('v Image', v)
    

    I also tried with LAB and YCrCb color space but the hue channel of HSV seemed to look better hence I stuck with it.

    ret, thresh = cv2.threshold(s, 87, 255, cv2.THRESH_BINARY)  #-- I had to check which threshold would be the optimum one to best separate the car
    cv2.imshow('hue_thresh', thresh)
    
    
    kernel1 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
    dilation = cv2.dilate(cv2.bitwise_not(thresh), kernel1, iterations = 4)   #-- performed dilation on the inverted image because the region of interest was not highlighted
    cv2.imshow('dilation', dilation)
    

    Now after having the possible region of interest I presumed the contour with the greatest area to contain the car.

    _, contours, hierarchy =    cv2.findContours(dilation, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    big_contour = 0    #-- contour with maximum area is stored here
    max_area = 0       #-- to store maximum area
    
    for cnt in contours:
        if (cv2.contourArea(cnt) > max_area):  #-- condition check to find contour with max area
            max_area = cv2.contourArea(cnt)
            big_contour = cnt
    
    x, y, w, h = cv2.boundingRect(big_cnt)     #-- store coordinates of contour with max area
    cv2.rectangle(img, (x,y), (x+w,y+h), (0, 255, 0), 2)
    cv2.imshow('rect_img', img)
    
    cv2.imwrite(path + 'rect_img.jpg', img)
    
    cv2.waitKey(0)
    cv2.destroyAllWindows()