基于OpenCV的手指只数检测

提示:本项目是由微信公众号:小白学视觉,中Opencv视觉实战项目里为源文,等下也会分享链接。有兴趣的小伙伴们可以关注哦,里面还有很多的基础视觉项目,我个人觉得还是很有意思的。

文章目录

  • 前言
  • 一、手指检测流程
  • 二、使用步骤
    • 1.引入库
    • 2.检测皮肤肤色Mask
    • 3.轮廓线绘制
    • 4.凸包缺陷检测
    • 5.余弦定理手指个数计算
    • 6.完整代码
  • 总结


前言

例如:非常感谢微信公众号:小白学视觉,提供的开源的openCV视觉项目。链接:小白学视觉 有兴趣的小伙伴赶紧去关注吧。


一、手指检测流程

  1. 打开摄像头或一张图片(如果是图片,建议不要太大。图片和视频的背景尽量为白色
  2. 通过设置肤色上下限,检测手掌位置
  3. 对手掌进行轮廓检测(确定手掌的位置)
  4. 依据轮廓找出凸包位置(为后面的余弦定理检测手指个数做准备)
  5. 通过余弦公式计算角度,角度小于90度认为是手指。
  6. 在图像或视频上显示手指只数。

二、使用步骤

1.引入库

没有添加库的记得到控制台添加这两个库哦,安装非常简单,下面也给出了下载方式,不然会面临报错的风险。
通过pip下载方式:
• pip install opencv
• pip install numpy

import cv2 as cv
import numpy as np

2.检测皮肤肤色Mask

开源中的图像图片:

基于OpenCV的手指只数检测_第1张图片

导入图像

img_path = "图片地址"
# 读取图片
img = cv.imread(img_path)
# 显示图片
cv.imshow('palm image',img)
cv.waitKey(0)
# 将BGR(蓝色,绿色,红色)图像更改为HSV(色相,饱和度,值)
hsvim = cv.cvtColor(img, cv.COLOR_BGR2HSV)
# 设置HSV中的肤色范围下限
lower = np.array([0, 48, 80], dtype="uint8")
# 设置HSV中的肤色范围上限
upper = np.array([20, 255, 255], dtype="uint8")
# 在HSV色彩空间的上下像素值范围内检测皮肤
skinRegionHSV = cv.inRange(hsvim, lower, upper)
# 进行均值滤波
blurred = cv.blur(skinRegionHSV, (2, 2))
# 去噪
ret, thresh = cv.threshold(blurred, 0, 255, cv.THRESH_BINARY)
    

3.轮廓线绘制

# 寻找轮廓
contours, hierarchy = cv.findContours(mask_img, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
# 寻找最大轮廓   cv.contourArea()计算轮廓面积
contours = max(contours, key=lambda x: cv.contourArea(x))
# 寻找凸包返回点并绘制凸包的轮廓
hull = cv.convexHull(contours)

4.凸包缺陷检测

# 寻找凸包点地址的索引并绘制凸包的轮廓
hull = cv.convexHull(contours, returnPoints=False)
# 计算轮廓凸缺陷
defects = cv.convexityDefects(contours, hull)

5.余弦定理手指个数计算

原理啥的大家自行到原文中去看吧,再来一次链接:小白学视觉

# 手指标志数
cnt = 0
# 获取缺陷行数
for i in range(defects.shape[0]):
   # 获取该行所有列的值(起点、终点、最远点、到最远点的大概距离)
   s, e, f, d = defects[i][0]
   # 获取起点坐标
   start = tuple(contours[s][0])
   # 获取终点坐标
   end = tuple(contours[e][0])
   # 获取最远点坐标
   far = tuple(contours[f][0])
   # 计算a的边长
   a = np.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)
   # 计算b的边长
   b = np.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2)
   # 计算c的边长
   c = np.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)
   # 计算两根手指的夹角
   angle = np.arccos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c))
   # 如果夹角小于90,我们会将其视为手指
   if angle <= np.pi / 2:
       # 手指数加1
       cnt += 1
       # 在原图上绘制圆 颜色:蓝色
       cv.circle(img, far, 4, [0, 0, 255], -1)
# 检测到手指
if cnt > 0:
   # 自加
   cnt = cnt + 1

在这里其实对环境的要求是比较高的,而且再检测时,对一只手指头检测也并不是特别的灵敏,这还需要更为细致的调整。

6.完整代码

import cv2 as cv
import numpy as np


# 检测皮肤肤色Mask
def skinmask(img):
    # 将BGR(蓝色,绿色,红色)图像更改为HSV(色相,饱和度,值)
    hsvim = cv.cvtColor(img, cv.COLOR_BGR2HSV)
    # 设置HSV中的肤色范围下限
    lower = np.array([0, 48, 80], dtype="uint8")
    # 设置HSV中的肤色范围上限
    upper = np.array([20, 255, 255], dtype="uint8")
    # 在HSV色彩空间的上下像素值范围内检测皮肤
    skinRegionHSV = cv.inRange(hsvim, lower, upper)
    # 进行均值滤波
    blurred = cv.blur(skinRegionHSV, (2, 2))
    # 去噪
    ret, thresh = cv.threshold(blurred, 0, 255, cv.THRESH_BINARY)
    return thresh


# 轮廓线绘制
def getcnthull(mask_img):
    # 寻找轮廓
    contours, hierarchy = cv.findContours(mask_img, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
    # 寻找最大轮廓   cv.contourArea()计算轮廓面积
    contours = max(contours, key=lambda x: cv.contourArea(x))
    # 寻找凸包返回点并绘制凸包的轮廓
    hull = cv.convexHull(contours)
    return contours, hull


# 凸包缺陷检测 手掌与凸包检测轮廓线的任何偏离的地方都可以视为凸度缺陷
def getdefects(contours):
    # 寻找凸包点地址的索引并绘制凸包的轮廓
    hull = cv.convexHull(contours, returnPoints=False)
    # 计算轮廓凸缺陷
    defects = cv.convexityDefects(contours, hull)
    return defects


if __name__ == '__main__':
    xuan = input("本案例可以实现图像和实时视频的手指检测:\n"
                 "1.输入0进行视频检测:\n"
                 "2,输入绝对地址的图片,进行图片检测:")
    if xuan == "0":
        # vc = cv2.VideoCapture(0) 参数是0,表示打开笔记本的内置摄像头,
        # 参数是filename(视频文件路径)则打开视频
        cap = cv.VideoCapture(0)
    else:
        cap = cv.VideoCapture(xuan)
    while cap.isOpened():
        # 读取视频数据
        _, img = cap.read()
        try:
            # 调用肤色检测函数
            mask_img = skinmask(img)
            # 调用肤色绘制
            contours, hull = getcnthull(mask_img)
            # 轮廓绘制 颜色:红色 宽度:2
            cv.drawContours(img, [contours], -1, (255, 0, 0), 2)
            # 包点轮廓绘制 颜色:绿色 宽度:2
            cv.drawContours(img, [hull], -1, (0, 255, 0), 2)
            # 调用凸包缺陷函数
            defects = getdefects(contours)
            # 如果缺陷不为空
            if defects is not None:
                # 手指标志数
                cnt = 0
                # 获取缺陷行数
                for i in range(defects.shape[0]):
                    # 获取该行所有列的值(起点、终点、最远点、到最远点的大概距离)
                    s, e, f, d = defects[i][0]
                    # 获取起点坐标
                    start = tuple(contours[s][0])
                    # 获取终点坐标
                    end = tuple(contours[e][0])
                    # 获取最远点坐标
                    far = tuple(contours[f][0])
                    # 计算a的边长
                    a = np.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2)
                    # 计算b的边长
                    b = np.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2)
                    # 计算c的边长
                    c = np.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2)
                    # 计算两根手指的夹角
                    angle = np.arccos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c))
                    # 如果夹角小于90,我们会将其视为手指
                    if angle <= np.pi / 2:
                        # 手指数加1
                        cnt += 1
                        # 在原图上绘制圆 颜色:蓝色
                        cv.circle(img, far, 4, [0, 0, 255], -1)
                # 检测到手指
                if cnt > 0:
                    # 自加
                    cnt = cnt + 1
                # 在图像上显示检测到的手指个数
                cv.putText(img, str(cnt), (0, 50), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2, cv.LINE_AA)
            # 显示图像
            cv.imshow("img", img)
        except:
            pass
        # 按ESC键退出
        if cv.waitKey(1) == 27:
            break
    # 释放内存
    cap.release()
    cv.destroyAllWindows()

总结

这是一个非常有意思的OpenCV视觉项目,哪怕是一个想我这样的视觉小白也能看得懂。不过视觉是个很深的学问,如果大家对这个有兴趣,坚持下去肯定很有意思。我是一个喜欢发现各种开源项目的路人刘。

你可能感兴趣的:(开源项目,python,opencv)