# organize imports
import cv2
import imutils
import numpy as np
from sklearn.metrics import pairwise
# global variables
bg = None
#-------------------------------------------------------------------------------
# Function - To find the running average over the background
#-------------------------------------------------------------------------------
def run_avg(image, accumWeight):
global bg
# initialize the background
if bg is None:
bg = image.copy().astype("float")
return
# compute weighted average, accumulate it and update the background
cv2.accumulateWeighted(image, bg, accumWeight)
#-------------------------------------------------------------------------------
# Function - To segment the region of hand in the image
#-------------------------------------------------------------------------------
def segment(image, threshold=25):
global bg
# find the absolute difference between background and current frame
diff = cv2.absdiff(bg.astype("uint8"), image)
# 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)
# return None, if no contours detected
if len(cnts) == 0:
return
else:
# based on contour area, get the maximum contour which is the hand
segmented = max(cnts, key=cv2.contourArea)
#生成mask
#contour_image_gray = np.zeros(image.shape, dtype=np.uint8)
#contour_image_gray = cv2.drawContours(contour_image_gray, cnts, segmented, (1, 1, 1), cv2.FILLED)
#obj_image = image * contour_image_gray
return (thresholded, segmented)
def calculateBoundImage(src_image):
'''
求取图像中的物体的边界矩形
:param src_image: 源图像
:return: 图像中的物体的边界矩形
'''
tmp_image = src_image.copy()
#if (len(tmp_image.shape) == 3):
#tmp_image = cv2.cvtColor(tmp_image, cv2.COLOR_BGR2GRAY)
# tmp_image = cv2.blur(tmp_image, (51, 51))
# 对图像进行二值化
# ret, thresh_image = cv2.threshold(tmp_image, 120, 255, cv2.THRESH_BINARY_INV)
thresh_image = cv2.adaptiveThreshold(tmp_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 71, 10)
thresh_image = cv2.morphologyEx(thresh_image, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_RECT, (25, 25)))
# 寻找最外的轮廓
ret_image, contours_ls, hierachy = cv2.findContours(thresh_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_KCOS)
pnt_cnt_ls = np.array([tmp_contour.shape[0] for tmp_contour in contours_ls])
# contour_iamge_gray = np.zeros(tmp_image.shape, dtype=np.uint8)
# contour_iamge_gray = cv2.drawContours(contour_iamge_gray, contours_ls, np.argmax(pnt_cnt_ls), 255, cv2.FILLED)
# contour_iamge_gray = cv2.morphologyEx(contour_iamge_gray, cv2.MORPH_CROSS, np.ones(10,dtype=np.uint8))
# ret, thresh_image = cv2.threshold(contour_iamge_gray, 122, 255, cv2.THRESH_BINARY)
# ret_image, contours_ls, hierachy = cv2.findContours(thresh_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_KCOS)
contour_image = src_image.copy()
contour_idx = np.argmax(pnt_cnt_ls)
contour_image = cv2.drawContours(contour_image, contours_ls, contour_idx, (0, 0, 255))
longest_contour = contours_ls[contour_idx]
contour_image_gray = np.zeros(src_image.shape, dtype=np.uint8)
contour_image_gray = cv2.drawContours(contour_image_gray, contours_ls, contour_idx, (1, 1, 1), cv2.FILLED)
obj_image = src_image * contour_image_gray
bound_box = cv2.boundingRect(longest_contour)
return bound_box
#-------------------------------------------------------------------------------
# Function - 计算分割出来的轮廓中手指的个数
#-------------------------------------------------------------------------------
def count(thresholded, segmented):
# find the convex hull of the segmented hand region
chull = cv2.convexHull(segmented)
# find the most extreme points in the convex hull
extreme_top = tuple(chull[chull[:, :, 1].argmin()][0])
extreme_bottom = tuple(chull[chull[:, :, 1].argmax()][0])
extreme_left = tuple(chull[chull[:, :, 0].argmin()][0])
extreme_right = tuple(chull[chull[:, :, 0].argmax()][0])
# find the center of the palm
cX = (extreme_left[0] + extreme_right[0]) / 2
cY = (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
distance = pairwise.euclidean_distances([(cX, cY)], Y=[extreme_left, extreme_right, extreme_top, extreme_bottom])[0]
maximum_distance = distance[distance.argmax()]
# calculate the radius of the circle with 80% of the max euclidean distance obtained
radius = int(0.8 * maximum_distance)
# find the circumference of the circle
circumference = (2 * np.pi * radius)
# take out the circular region of interest which has
# the palm and the fingers
circular_roi = np.zeros(thresholded.shape[:2], dtype="uint8")
# draw the circular ROI
cv2.circle(circular_roi, (int(cX), int(cY)), int(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
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)
# initalize the finger count
count = 0
# loop through the contours found
for c in cnts:
# compute the bounding box of the contour
(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
if ((cY + (cY * 0.25)) > (y + h)) and ((circumference * 0.25) > c.shape[0]):
count += 1
return count
#-------------------------------------------------------------------------------
# Main function
#-------------------------------------------------------------------------------
if __name__ == "__main__":
# initialize accumulated weight
accumWeight = 0.5
# get the reference to the webcam
camera = cv2.VideoCapture(0)
# region of interest (ROI) coordinates
top, right, bottom, left = 10, 380, 325, 630
# initialize num of frames
num_frames = 0
# calibration indicator
calibrated = False
# keep looping, until interrupted
while(True):
# get the current frame
(grabbed, frame) = camera.read()
# resize the frame
frame = imutils.resize(frame, width=640)
# flip the frame so that it is not the mirror view
frame = cv2.flip(frame, 1)
# 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]
# convert the roi to grayscale and blur it
gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)
# to get the background, keep looking till a threshold is reached
# so that our weighted average model gets calibrated
if num_frames < 30:
run_avg(gray, accumWeight)
if num_frames == 1:
print("[STATUS] please wait! calibrating...")
elif num_frames == 29:
print("[STATUS] calibration successfull...")
else:
# segment the hand region
hand = segment(gray)
# check whether hand region is segmented
if hand is not None:
# if yes, unpack the thresholded image and
# segmented region
(thresholded, segmented) = hand
bbox = calculateBoundImage(thresholded)
#print(bbox[0],bbox[1],bbox[2],bbox[3])
bound_thres = 4500
if bbox[2] > bound_thres or bbox[3] > bound_thres:
continue
#生成ROI图像
roi_color = frame[10 + bbox[1]:10 + bbox[1] + bbox[3], 380+bbox[0]:380 + bbox[0] + bbox[2]]
#cv2.imshow("roi_color", roi_color)
roi_mask = thresholded[bbox[1]:bbox[1] + bbox[3], bbox[0]:bbox[0] + bbox[2]]
#merged = cv2.merge([b, g, r])
#cv2.imshow("roi_mask", roi_mask)
dst = cv2.add(roi_color, np.zeros(np.shape(roi_color), dtype=np.uint8), mask=roi_mask)
cv2.imshow("dst", dst)
contour_image = cv2.rectangle(thresholded, (bbox[0], bbox[1]), (bbox[0] + bbox[2], bbox[1] + bbox[3]),
(255), thickness=2)
# draw the segmented region and display the frame
cv2.drawContours(clone, [segmented + (right, top)], -1, (0, 0, 255))
cv2.rectangle(clone, (380 + bbox[0], 10 + bbox[1]),(380 + bbox[0] + bbox[2], 10 + bbox[1] + bbox[3]), (255,0 , 0), 2)
# count the number of fingers
#fingers = count(thresholded, segmented)
#cv2.putText(clone, str(fingers), (70, 45), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255), 2)
# show the thresholded image
#cv2.imshow("Thesholded", thresholded)
# draw the segmented hand
cv2.rectangle(clone, (left, top), (right, bottom), (0,255,0), 2)
# increment the number of frames
num_frames += 1
# display the frame with segmented hand
cv2.imshow("Video Feed", clone)
# observe the keypress by the user
keypress = cv2.waitKey(1) & 0xFF
# if the user pressed "q", then stop looping
if keypress == ord("q"):
break
# free up memory
camera.release()
cv2.destroyAllWindows()