Friday, October 28, 2022

[SOLVED] Opencv Why does the contour remain if the object has disappeared?

Issue

I found a pre-written object detection program on the internet. I would like to use this to make a plastic bottle cap sensor on an assembly line that will tell me the size. Only when you move the cap out of the conveyor belt the coordinates taken from the contour stay there. Any ideas?

import cv2
from object_detector import *
import numpy as np
import time

# Load Object Detector
detector = HomogeneousBgDetector()

# Load Cap
cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 800)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 600)

while True:
    _, img = cap.read()
    img = img[0:600, 60:700 ]

    contours = []
    contours = detector.detect_objects(img)

        # Draw objects boundaries
    for cnt in contours:
        # Get rect
        rect = cv2.minAreaRect(cnt)
        (x, y), (w, h), angle = rect

        # Get Width and Height of the Objects by applying the Ratio pixel to cm
        object_width = w
        object_height = h

        # Display rectangle
        box = cv2.boxPoints(rect)
        box = np.int0(box)

    cv2.circle(img, (int(x), int(y)), 5, (0, 0, 255), -1)
    cv2.polylines(img, [box], True, (255, 0, 0), 2)
    cv2.putText(img, "Width {} px".format(round(object_width, 1)), (int(x - 100), int(y - 20)), cv2.FONT_HERSHEY_PLAIN, 2, (100, 200, 0), 2)
    cv2.putText(img, "Height {} px".format(round(object_height, 1)), (int(x - 100), int(y + 15)), cv2.FONT_HERSHEY_PLAIN, 2, (100, 200, 0), 2)

    cv2.imshow("Image", img)
    key = cv2.waitKey(1)

    if key == 27:
        break

cap.release()
cv2.destroyAllWindows()

object_detector.py

import cv2


class HomogeneousBgDetector():
    def __init__(self):
        pass

    def detect_objects(self, frame):
        def difference_of_Gaussians(img, k1, s1, k2, s2):
            b1 = cv2.GaussianBlur(img,(k1, k1), s1)
            b2 = cv2.GaussianBlur(img,(k2, k2), s2)
            return b1 - b2      
        # Convert Image to grayscale
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        DoG_img = difference_of_Gaussians(gray, 7, 7, 17, 13)
        
        # Create a Mask with adaptive threshold
        #mask = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 19, 5)
        mask =  cv2.threshold(DoG_img ,130,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]

        # Find contours
        contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

        cv2.imshow("mask", mask)
        objects_contours = []

        for cnt in contours:
            area = cv2.contourArea(cnt)
            if area > 2000:
                
                objects_contours.append(cnt)

        return objects_contours

I tried to create the contour variable empty before, but failed:

....
_, img = cap.read()
img = img[0:600, 60:700 ]

contours = []# here
contours = detector.detect_objects(img)

    # Draw objects boundaries
for cnt in contours:
.....

the picture: You can view here


Solution

Eran and Christoph are right. If no contour is found, the last position remains in the global x and y variables. Perhaps you want to increase the indent of the four lines that draw circle, lines and text so that it belongs to the for-loop above?

....
    img = img[0:600, 60:700 ]

    contours = detector.detect_objects(img)

    # Draw objects boundaries
    for cnt in contours:
....
        # Display rectangle
        box = cv2.boxPoints(rect)
        box = np.int0(box)

        # keep indent here
        cv2.circle(img, (int(x), int(y)), 5, (0, 0, 255), -1)
        cv2.polylines(img, [box], True, (255, 0, 0), 2)
        cv2.putText(img, "Width {} px".format(round(object_width, 1)), (int(x - 100), int(y - 20)), cv2.FONT_HERSHEY_PLAIN, 2, (100, 200, 0), 2)
        cv2.putText(img, "Height {} px".format(round(object_height, 1)), (int(x - 100), int(y + 15)), cv2.FONT_HERSHEY_PLAIN, 2, (100, 200, 0), 2)
....


Answered By - Markus
Answer Checked By - Gilberto Lyons (WPSolving Admin)