From ec05ec657c04c6b6a1eed0b91144763403d3dcc6 Mon Sep 17 00:00:00 2001 From: Neha Patil Date: Sat, 11 Oct 2025 23:59:57 -1100 Subject: [PATCH] Fix: OpenCV 4.x compatibility issue (updated cv2.findContours) --- recognize-image.py | 88 +++++++++++++++++++--------------------------- requirements.txt | 0 segment.py | 19 +++++----- 3 files changed, 46 insertions(+), 61 deletions(-) create mode 100644 requirements.txt diff --git a/recognize-image.py b/recognize-image.py index bd4dae0..9fb05c5 100644 --- a/recognize-image.py +++ b/recognize-image.py @@ -12,27 +12,25 @@ # To segment the region of hand in the image #--------------------------------------------- def segment(image, grayimage, threshold=75): - # threshold the image to get the foreground which is the hand + # threshold the image to get the foreground (hand) thresholded = cv2.threshold(grayimage, threshold, 255, cv2.THRESH_BINARY)[1] - # get the contours in the thresholded image - (_, cnts, _) = cv2.findContours(thresholded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + # get the contours in the thresholded image (OpenCV 4.x style) + cnts, _ = cv2.findContours(thresholded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) - # return None, if no contours detected + # return None if no contours detected if len(cnts) == 0: - return - else: - # based on contour area, get the maximum contour which is the hand + return None + else: + # based on contour area, get the maximum contour (the hand) segmented = max(cnts, key=cv2.contourArea) - return (thresholded, segmented) #-------------------------------------------------------------- # To count the number of fingers in the segmented hand region #-------------------------------------------------------------- def count(image, thresholded, segmented): - # find the convex hull of the segmented hand region - # which is the maximum contour with respect to area + # convex hull of the segmented hand region chull = cv2.convexHull(segmented) # find the most extreme points in the convex hull @@ -41,60 +39,52 @@ def count(image, thresholded, segmented): extreme_left = tuple(chull[chull[:, :, 0].argmin()][0]) extreme_right = tuple(chull[chull[:, :, 0].argmax()][0]) - # find the center of the palm + # center of the palm cX = int((extreme_left[0] + extreme_right[0]) / 2) cY = int((extreme_top[1] + extreme_bottom[1]) / 2) - # find the maximum euclidean distance between the center of the palm - # and the most extreme points of the convex hull - distances = pairwise.euclidean_distances([(cX, cY)], Y=[extreme_left, extreme_right, extreme_top, extreme_bottom])[0] + # maximum euclidean distance from center to extreme points + distances = pairwise.euclidean_distances( + [(cX, cY)], + Y=[extreme_left, extreme_right, extreme_top, extreme_bottom] + )[0] max_distance = distances[distances.argmax()] - # calculate the radius of the circle with 80% of the max euclidean distance obtained + # radius of circular ROI (80% of max distance) radius = int(0.8 * max_distance) - # find the circumference of the circle + # circumference of circular ROI circumference = (2 * np.pi * radius) - # initialize circular_roi with same shape as thresholded image + # circular ROI mask circular_roi = np.zeros(thresholded.shape[:2], dtype="uint8") - - # draw the circular ROI with radius and center point of convex hull calculated above cv2.circle(circular_roi, (cX, cY), radius, 255, 1) - # take bit-wise AND between thresholded hand using the circular ROI as the mask - # which gives the cuts obtained using mask on the thresholded hand image + # apply circular ROI on thresholded hand circular_roi = cv2.bitwise_and(thresholded, thresholded, mask=circular_roi) - # compute the contours in the circular ROI - (_, cnts, _) = cv2.findContours(circular_roi.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) - - count = 0 - # approach 1 - eliminating wrist - #cntsSorted = sorted(cnts, key=lambda x: cv2.contourArea(x)) - #print(len(cntsSorted[1:])) # gives the count of fingers + # find contours in circular ROI (OpenCV 4.x style) + cnts, hierarchy = cv2.findContours(circular_roi.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) - # approach 2 - eliminating wrist - # loop through the contours found - for i, c in enumerate(cnts): + count_fingers = 0 - # compute the bounding box of the contour + # loop through contours + for c in cnts: (x, y, w, h) = cv2.boundingRect(c) - # increment the count of fingers only if - - # 1. The contour region is not the wrist (bottom area) - # 2. The number of points along the contour does not exceed - # 25% of the circumference of the circular ROI + # count finger only if: + # 1. Not the wrist + # 2. Small enough relative to circumference if ((cY + (cY * 0.25)) > (y + h)) and ((circumference * 0.25) > c.shape[0]): - count += 1 + count_fingers += 1 - return count + return count_fingers #----------------- # MAIN FUNCTION #----------------- if __name__ == "__main__": - # get the current frame + # load a hand image frame = cv2.imread("resources/hand-sample.jpg") # resize the frame @@ -103,28 +93,24 @@ def count(image, thresholded, segmented): # clone the frame clone = frame.copy() - # get the height and width of the frame - (height, width) = frame.shape[:2] - - # convert the frame to grayscale and blur it + # convert to grayscale and blur gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray = cv2.GaussianBlur(gray, (7, 7), 0) # segment the hand region hand = segment(clone, gray) - # check whether hand region is segmented if hand is not None: - # if yes, unpack the thresholded image and segmented contour (thresholded, segmented) = hand - # count the number of fingers + # count fingers fingers = count(clone, thresholded, segmented) - cv2.putText(clone, "This is " + str(fingers), (70, 45), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2) - - # display the frame with segmented hand - cv2.imshow("Image", clone) + # display result + cv2.putText(clone, f"This is {fingers}", (70, 45), + cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2) + # show output + cv2.imshow("Image", clone) cv2.waitKey(0) - cv2.destroyAllWindows() \ No newline at end of file + cv2.destroyAllWindows() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/segment.py b/segment.py index ade4319..dfe7901 100644 --- a/segment.py +++ b/segment.py @@ -34,8 +34,10 @@ def segment(image, threshold=25): # threshold the diff image so that we get the foreground thresholded = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY)[1] - # get the contours in the thresholded image - (_, cnts, _) = cv2.findContours(thresholded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + # ✅ OpenCV 4.x style + cnts, _ = cv2.findContours( + thresholded.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE + ) # return None, if no contours detected if len(cnts) == 0: @@ -75,9 +77,6 @@ def segment(image, threshold=25): # clone the frame clone = frame.copy() - # get the height and width of the frame - (height, width) = frame.shape[:2] - # get the ROI roi = frame[top:bottom, right:left] @@ -95,15 +94,14 @@ def segment(image, threshold=25): # check whether hand region is segmented if hand is not None: - # if yes, unpack the thresholded image and - # segmented region + # if yes, unpack the thresholded image and segmented region (thresholded, segmented) = hand # draw the segmented region and display the frame cv2.drawContours(clone, [segmented + (right, top)], -1, (0, 0, 255)) - cv2.imshow("Thesholded", thresholded) + cv2.imshow("Thresholded", thresholded) - # draw the segmented hand + # draw the segmented hand ROI box cv2.rectangle(clone, (left, top), (right, bottom), (0,255,0), 2) # increment the number of frames @@ -121,4 +119,5 @@ def segment(image, threshold=25): # free up memory camera.release() -cv2.destroyAllWindows() \ No newline at end of file +cv2.destroyAllWindows() +