前言

一次拿到SANSUNG手机解锁发现自带一个黑科技:虹膜解锁(虹膜就是眼睛黑黑的那部分);想起以前看过的未来科技大片里面的片段:搜捕罪犯通过黑匣子识别人们的眼球,只要摄像头扫描到过你的眼睛,你的身份即被确定;哇喔,很炫酷的样子。
日前大火的人脸识别技术因为疫情大家都带上了口罩,为了减少直接触碰的风险,指纹识别也在暗淡,虹膜识别正好互补。虽然识别对于戴眼镜尤其是墨镜不太友好,但各识别方法各有利弊吧。
查阅信息的时候发现虹膜包含的信息还对应身体健康知识,博大精深,又是一种很好的发展方向,nice。
在这里插入图片描述
话题扯远了,直接上思路和代码

正文

一、思路

  1. 环境
    这里还是采用最简单可上手的OpenCV,(别呛为什么不用YOLO等等标注识别,我像是有闲心搞学术的吗?不是)
    环境:python3.9+jupyter notebook+opencv4.5.3
  2. 下载识别
    opencv官网,下载识别用的配置文件https://github.com/opencv/opencv/tree/master/data/haarcascades
    这里需要eye眼部检测,意外发现还有eyeglasses戴眼镜的配置文件,强呀强呀!

Tips:怎么快速下载–浏览器打开想要的xml文件,把地址中的blob改为raw再保存网页即可,github、gitee通用;
打开:https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_eye.xml
修改后:https://github.com/opencv/opencv/raw/master/data/haarcascades/haarcascade_eye.xml
转换并下载:https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_eye.xml

  1. haarcascade介绍
    Haar分类器,如何训练及原理,可以查看一下这篇参考文章,比较详细。
训练集识别范围
haarcascade_lefteye_2splits.xml可用来检测睁开或闭着的眼睛
haarcascade_eye.xml仅可以检测睁开的眼睛
haarcascade_eye_tree_eyeglasses.xml仅在带被检测者戴眼镜时方可检测

二、眼睛定位

先识别人脸,在人脸范围内识别眼睛(这里用的opencv自带,可优化为其他方案dlib,yolo等)

import numpy as np
import cv2
from matplotlib import pyplot as plt

def findeyes(path):
    face_cascade = cv2.CascadeClassifier('/Users/wangyu/Desktop/haarcascade_frontalface_default.xml')
    eye_cascade = cv2.CascadeClassifier('/Users/wangyu/Desktop/haarcascade_eye.xml')
    img = cv2.imread(path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 灰度处理
    # 人脸识别
    face = face_cascade.detectMultiScale(gray, 2, 2)  # 参数:1、灰度图片, 2、缩放比例, 3、阈值
    print("这张图片中有%d张人脸" % len(face))
    for (x, y, w, h) in face:
        cv2.rectangle(img, (x, y), (x+w, y+h), (255, 255, 0), 2)  # 绘制人脸方框

        face_gray = gray[y:y+h, x:x+w]# 在人脸的基础上识别眼睛
        face_color = img[y:y+h, x:x+w]
        # 眼睛识别
        eyes = eye_cascade.detectMultiScale(face_gray)
        print("在这张脸上有%d个眼睛" % len(eyes))
        for (e_x, e_y, e_w, e_h) in eyes:
            cv2.rectangle(face_color, (e_x, e_y), (e_x+e_w, e_y+e_h), (0, 255, 0), 2)  # 绘制眼睛方框
            roi_color = face_color[e_y:e_y+e_h, e_x:e_x+e_w] #裁剪眼睛框图
            #getCircle(roi_color)
    plt.figure(figsize=(10,10))
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.show()
    # cv2.imshow('dst', img)
    # cv2.waitKey(0)
    
if __name__=="__main__":
    path = '/Users/wangyu/Desktop/lena.jpg'
    findeyes(path)

opencv识别结果:
在这里插入图片描述

三、虹膜识别

1.图像去干扰

识别到眼睛部分,截取眼睛部分图像,并做干扰处理

  1. 高斯模糊
    高斯矩阵的长与宽都是3,标准差取5;高斯矩阵的尺寸越大,标准差越大,处理过的图像模糊程度越大。
  2. 二值化
    把图像的像素转变为0或者255,只有这两个像素值。0白色 1黑色
  3. 腐蚀
    腐蚀操作 开运算:先腐蚀后膨胀,去除孤立的小点,毛刺
  4. 膨胀
    膨胀操作 闭运算:先膨胀后腐蚀,填平小孔,弥合小裂缝
    在这里插入图片描述

这里读取的图片为全脸图片,如果是纯眼部图,效果会更好!或许还有更优解,欢迎讨论。

2.检测虹膜

霍夫梯度法检测圆(Canny边缘检测的最大阈值,检测阶段圆心的累加器阈值,最小圆的半径,最大圆的半径)
原理参考
在这里插入图片描述
在这里插入图片描述

输入参数为(image,method,dp,min_dist,param1,param2,minRadius,maxRadius)

参数描述
image为需要进行霍夫变换的图像
method为检测方法,一般用CV_HOUGH_GRADIENT,即霍夫梯度法
dp为检测内侧圆心的累加器图像的分辨率与输入图像之比的倒数。若为1,即累加器和输入图像具有相同的分辨率;若为2,则累加器有输入图像一半的宽度和高度。
min_dist两个圆之间圆心的最小距离。防止重复画一个圆
param1默认值100,为传递给canny边缘检测算子的高阈值,低阈值为其一半
param2默认值100,表示在检测阶段圆心的累加器阈值,它越小,表示可以检测到更多不存在的圆,它越大,表示能检测出来的圆越完美
minRadius默认值0,圆半径的最小值
maxRadius默认值0,圆半径的最大值

在这里插入图片描述

完整代码:

import numpy as np
import cv2
from matplotlib import pyplot as plt

def findeyes(path):
    face_cascade = cv2.CascadeClassifier('/Users/wangyu/Desktop/haarcascade_frontalface_default.xml')
    eye_cascade = cv2.CascadeClassifier('/Users/wangyu/Desktop/haarcascade_eye.xml')
    img = cv2.imread(path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 灰度处理
    # 人脸识别
    face = face_cascade.detectMultiScale(gray, 2, 2)  # 参数:1、灰度图片, 2、缩放比例, 3、阈值
    print("这张图片中有%d张人脸" % len(face))
    for (x, y, w, h) in face:
        cv2.rectangle(img, (x, y), (x+w, y+h), (255, 255, 0), 2)  # 绘制人脸方框

        face_gray = gray[y:y+h, x:x+w]# 在人脸的基础上识别眼睛
        face_color = img[y:y+h, x:x+w]
        # 眼睛识别
        eyes = eye_cascade.detectMultiScale(face_gray)
        print("在这张脸上有%d个眼睛" % len(eyes))
        for (e_x, e_y, e_w, e_h) in eyes:
            cv2.rectangle(face_color, (e_x, e_y), (e_x+e_w, e_y+e_h), (0, 255, 0), 2)  # 绘制眼睛方框
            roi_color = face_color[e_y:e_y+e_h, e_x:e_x+e_w] #裁剪眼睛框图
            # 霍夫圆检测虹膜===
            getHoughCircle(roi_color)
    plt.figure(figsize=(10,10))
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.show()
    # cv2.imshow('dst', img)
    # cv2.waitKey(0)

    
def getHoughCircle(img):
    plt.figure(figsize=(15,15))
    plt.subplot(1,4,1) 
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))#BGR转RGB
    plt.xlabel(u'img')
    
    
    blur = cv2.GaussianBlur(img, (3, 3), 5) # 高斯模糊,给出高斯模糊矩阵和标准差
    gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)# 灰度化

    # 图像二值化,全局自适应阈值:对输入的单通道矩阵逐像素进行阈值分割
    ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_TRIANGLE)
    kernel = np.ones((3,3),np.uint8)# 设置卷积核3*3
    erosion = cv2.erode(binary,kernel)# 图像的腐蚀,默认迭代次数
    dst = cv2.dilate(erosion,kernel)# 图像的膨胀
    
    plt.subplot(1,4,2)
    plt.imshow(cv2.cvtColor(dst, cv2.COLOR_BGR2RGB))
    plt.xlabel(u'gray')
    
    # 霍夫梯度法检测圆(Canny边缘检测的最大阈值,检测阶段圆心的累加器阈值,最小圆的半径,最大圆的半径)
    circles = cv2.HoughCircles(dst,cv2.HOUGH_GRADIENT,1,50,param1=100,param2=10,
                               minRadius=0,maxRadius=200)
    img2 = img
    if circles is None:
        print("未检测到霍夫圆")
    else:
        for i in circles[0:]:# 遍历矩阵每一行的数据
            item = i[0]
            # print(item)
            # cv2.circle(img2,(int(i[0]),int(i[1])),i[2],(0,255,0),2)
            cv2.circle(img2, (int(item[0]), int(item[1])), int(item[2]), (0, 255, 255), 2)
            cv2.circle(img2,(int(item[0]),int(item[1])),2,(0,255,255),-1)

        #plt.imshow(img[:,:,::-1])
        #plt.show()


    # 轮廓提取
    binaryimg = cv2.Canny(dst, 50, 200) #canny检测轮廓
    h = cv2.findContours(dst,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) #寻找轮廓
    contours = h[0] #提取轮廓
    ret = np.ones(dst.shape, np.uint8) #创建黑色幕布
    cv2.drawContours(ret,contours,-1,(255,255,255),1) #绘制白色轮廓
    
    plt.subplot(1,4,3)
    plt.imshow(cv2.cvtColor(ret, cv2.COLOR_BGR2RGB))
    plt.xlabel(u'line')


    plt.subplot(1,4,4)
    plt.imshow(cv2.cvtColor(img2, cv2.COLOR_BGR2RGB))
    plt.xlabel(u'img2')
    plt.show()
    
if __name__=="__main__":
    path = '/Users/wangyu/Desktop/lena.jpg'
    findeyes(path)

在这里插入图片描述
戴眼镜:
在这里插入图片描述

总结

  1. lena女神这张图片识别效果意外还挺好的,预期应该没有这么好,为什么没有这么好:a.opencv自带的眼睛识别定位是8 years ago的训练样本了,鲁棒性不够好,会把部分图片的嘴巴也识别为眼睛;如果在yolo标注训练,嘴巴要作为负样本训练。b.图像去干扰:如果不是纯眼部图,干扰会很大。c.没有好的二值化区域,霍夫梯度法类圆检测效果也不好。

    a.类opencv误判,识别不到人脸或眼睛识别错误,不过派派的脸是真符合检测标准,检测正确的霍夫圆可以说是非常完美。
    在这里插入图片描述
    在这里插入图片描述
    b.c.类,当眼型很圆时,霍夫圆的检测效果不太理想,或许可以用其他方法内接圆来判断,momo的笑眼,嘻!
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  2. 其他很好的思路:参考思路1,这篇文章的思路也很好,不过对于眼部取样的要求还是很高,具体情况具体应用。

说了这么多,实现的效果普遍性不高,还有必要研究吗?有必要的,虹膜识别的应用性还是很广;对于戴眼镜的识别优化,虹膜信息提取等等。
在这里插入图片描述
图片均来源网络,仅学习使用

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐