# How to improve contour detection

**URL:** <https://forum.opencv.org/t/how-to-improve-contour-detection/8544>\
**Category:** Python\
**Created:** [April 26, 2022, 12:09am UTC](https://forum.opencv.org/t/how-to-improve-contour-detection/8544 "2022-04-26T00:09:17Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![willsm](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/willsm/32/4425_2.png) [@willsm](https://forum.opencv.org/u/willsm)\
**Post date:** [April 26, 2022, 12:09am UTC](https://forum.opencv.org/t/how-to-improve-contour-detection/8544/1 "2022-04-26T00:09:17Z")

</div>

Hello there!

Is it possible to improve the contour detection in images like these?

![image](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/e/e374b1399ee7d1448b2ed89aea59c858497c449a.jpeg)

I can’t detect the contour correctly on some rice when they are too close together.

What would be the best technique in this case?

Thank you!

---

<div class="post-metadata">

**Author:** ![sturkmen](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/sturkmen/32/13_2.png) [@sturkmen](https://forum.opencv.org/u/sturkmen)\
**Post date:** [April 26, 2022, 12:55pm UTC](https://forum.opencv.org/t/how-to-improve-contour-detection/8544/2 "2022-04-26T12:55:56Z")

</div>

please provide the original image and the code you used.

---

<div class="post-metadata">

**Author:** ![willsm](https://sea2.discourse-cdn.com/flex020/user_avatar/forum.opencv.org/willsm/32/4425_2.png) [@willsm](https://forum.opencv.org/u/willsm)\
**Post date:** [April 26, 2022, 8:39pm UTC](https://forum.opencv.org/t/how-to-improve-contour-detection/8544/3 "2022-04-26T20:39:27Z")

</div>

Sure!

Original image:

![9bea7868-c34b-11ec-885a-0242ac120006](https://us1.discourse-cdn.com/flex020/uploads/opencv/original/2X/e/e937bf08ef7c8748435218452ac0ee9003f2547f.jpeg)

My code:

```python
import cv2
import numpy as np
from scipy.spatial import distance

while True:

    image = cv2.imread('/path/original-image.png')
    
    image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    thresh, output_binthresh = cv2.threshold(image_gray, 48, 255, cv2.THRESH_BINARY)    
    cv2.imshow("Binary Threshold (fixed)", output_binthresh)

    thresh, output_otsuthresh = cv2.threshold(image_gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
    cv2.imshow("Binary Threshold (otsu)", output_otsuthresh)

    output_adapthresh = cv2.adaptiveThreshold (image_gray, 253, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 51, 0)
    cv2.imshow("Adaptive Thresholding", output_adapthresh)

    kernel = np.ones((3,3),np.uint8)
    output_erosion = cv2.erode(output_adapthresh, kernel)
    cv2.imshow("Morphological Erosion", output_erosion)

    contours, _ = cv2.findContours(output_erosion, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    output_contour = cv2.cvtColor(image_gray, cv2.COLOR_GRAY2BGR)
    cv2.drawContours(output_contour, contours, -1, (0, 0, 255), 2)
    cv2.imshow("Contours", output_contour)

    contours_sorted = sorted(contours, key=lambda ctr: cv2.boundingRect(ctr)[0])

    image_result = image.copy()

    pixelsPerMm = 5.509

    rices = []

    for c in contours_sorted:

        box = cv2.minAreaRect(c)
        box = cv2.boxPoints(box)
        box = np.array(box, dtype="int")
       
        (top_left, top_right, bottom_right, bottom_left) = box

        distance_a = distance.euclidean((top_left[0], top_left[1]), (top_right[0], top_right[1]))
        distance_b = distance.euclidean((top_left[0], top_left[1]), (bottom_left[0], bottom_left[1]))

        dimension_a = distance_a / pixelsPerMm
        dimension_b = distance_b / pixelsPerMm

        length = max(dimension_a, dimension_b)        
        width = min(dimension_a, dimension_b)        

        if length < 1.4 or length > 12.3:
            continue
        if width < 1.4 or width > 3.5:
            continue

        rice = [box.astype("int")]

        # Check class
        if length <= 1.6:
            rices.append({'class': 'class1', 'box': rice, 'color': (255, 0, 255), 'width': width, 'length': length, 'top_right': top_right})
        elif length <= 1.8:
            rices.append({'class': 'class2', 'box': rice, 'color': (255, 0, 0), 'width': width, 'length': length, 'top_right': top_right})
        elif length <= 3.8:
            rices.append({'class': 'class3', 'box': rice, 'color': (0, 255, 0), 'width': width, 'length': length, 'top_right': top_right})
        elif length <= 5.6:
            rices.append({'class': 'class4', 'box': rice, 'color': (51, 255, 255), 'width': width, 'length': length, 'top_right': top_right})
        elif length <= 12:
            rices.append({'class': 'class5', 'box': rice, 'color': (0, 0, 255), 'width': width, 'length': length, 'top_right': top_right})
        else:
            continue       
        
        
    for rice in rices:
        cv2.drawContours(image_result, rice['box'], -1, rice['color'], 1)
        cv2.putText(image_result, "l={:.1f}mm".format(rice['length']), (rice['top_right'][0]-10, rice['top_right'][1]-20), cv2.FONT_HERSHEY_SIMPLEX, 0.3, (255, 255, 255), 1)
        cv2.putText(image_result, "w={:.1f}mm".format(rice['width']), (rice['top_right'][0]-10, rice['top_right'][1]-10), cv2.FONT_HERSHEY_SIMPLEX, 0.3, (255, 255, 255), 1)

    cv2.imshow("Result", image_result)

    key = cv2.waitKey(1)

    if key%256 == 27:
        break

cv2.destroyAllWindows()

```
