Search code examples
pythonopencvimutils

7 Segment Detection using OpenCV with Webcam


I want to read some digital displays using a camera. I found a tutorial to detect and read the display here: https://www.pyimagesearch.com/2017/02/13/recognizing-digits-with-opencv-and-python/

Because I need the reading real time, I set the video input from the webcam. Here's my code:

from imutils.perspective import four_point_transform
from imutils import contours
import imutils
import cv2

# creating a dictionary for 7-segment detection
DIGITS_LOOKUP = {
    (1, 1, 1, 0, 1, 1, 1): 0,
    (0, 0, 1, 0, 0, 1, 0): 1,
    (1, 0, 1, 1, 1, 1, 0): 2,
    (1, 0, 1, 1, 0, 1, 1): 3,
    (0, 1, 1, 1, 0, 1, 0): 4,
    (1, 1, 0, 1, 0, 1, 1): 5,
    (1, 1, 0, 1, 1, 1, 1): 6,
    (1, 0, 1, 0, 0, 1, 0): 7,
    (1, 1, 1, 1, 1, 1, 1): 8,
    (1, 1, 1, 1, 0, 1, 1): 9
}

# capturing from webcam
cap = cv2.VideoCapture(0)
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))

# define codec and create VideoWriter object
out = cv2.VideoWriter('out_videos/cam_blur.avi', 
                      cv2.VideoWriter_fourcc('M','J','P','G'), 
                      30, 
                      (frame_width,frame_height))

# continuous capture from webcam
while(cap.isOpened()):
    ret, frame = cap.read()
    if ret == True:
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        blurred = cv2.GaussianBlur(gray, (7, 7), 0)
        edged = cv2.Canny(blurred, 50, 200, 200)
        cv2.imshow('Video', edged)
                
        # find contours in the edge map, then sort them by their size in descending order
        cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
            cv2.CHAIN_APPROX_SIMPLE)
        cnts = imutils.grab_contours(cnts)
        cnts = sorted(cnts, key=cv2.contourArea, reverse=True)
        displayCnt = None
        
        # loop over the contours
        for c in cnts:
            # approximate the contour
            peri = cv2.arcLength(c, True)
            approx = cv2.approxPolyDP(c, 0.02 * peri, True)
            
        # if the contour has four vertices, then we have found
        # the thermostat display
            if len(approx) == 4:
                 displayCnt = approx
                 break
        
        # extract the thermostat display, apply a perspective transform
        # to it
        warped = four_point_transform(gray, displayCnt.reshape(4, 2))
        output = four_point_transform(frame, displayCnt.reshape(4, 2))
        cv2.imshow('Birdeye', output)
        
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
       
cap.release()
cv2.destroyAllWindows()

At the first try, the program ran well although sometimes it detected random rectangle objects and sent this error when it crashed:

File "7segmentcam.py", line 63, in <module>
    warped = four_point_transform(gray, displayCnt.reshape(4, 2))

AttributeError: 'NoneType' object has no attribute 'reshape'

After I tweaked some parameters in lines 37 and 38, the program wasn't responding and crashed with the same error above. Is there any better option to run the program without errors when the camera doesn't detect any object?


Solution

  • This is happening because sometimes, your code will not find any contour with 4 vertices after approxPolyDP, thus, the value of the variable displayCnt remains None as set before the for loop and hence four_point_transform function is showing error as it does not take None as input.

    To avoid this error, before four_point_transform function, check the value of displayCnt by: if displayCnt is not None:, and if it is None, then do not run the function.

    Also, according to me, the if condition in the for loop is not correct, as it is fulfilled at very few cases and in Image Processing, you can work with such specific conditions when you are sure that it will be true at atleast 1 condition. So, in my opinion, change that if condition.