瞳孔检测眼动追踪python实现(基于dlib)

news2024/9/26 1:22:31

效果展示:
原图:(图片来自 b站up 借我300去洗牙)
在这里插入图片描述

dlib实现的特征点检测
在这里插入图片描述
瞳孔检测结果

完整代码:

# encoding:utf-8

import dlib
import numpy as np
import cv2

def rect_to_bb(rect): # 获得人脸矩形的坐标信息
    x = rect.left()
    y = rect.top()
    w = rect.right() - x
    h = rect.bottom() - y
    return (x, y, w, h)

def shape_to_np(shape, dtype="int"): # 将包含68个特征的的shape转换为numpy array格式
    coords = np.zeros((68, 2), dtype=dtype)
    for i in range(0, 68):
        coords[i] = (shape.part(i).x, shape.part(i).y)
    return coords


def resize(image, width=1200):  # 将待检测的image进行resize
    r = width * 1.0 / image.shape[1]
    dim = (width, int(image.shape[0] * r))
    resized = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
    return resized

def feature():
    image_file = r"F:\project_python\facenet\2.PNG"
    detector = dlib.get_frontal_face_detector()
    predictor = dlib.shape_predictor(r"F:\project_python\facenet\shape_predictor_68_face_landmarks.dat")
    image = cv2.imread(image_file)
    image = resize(image, width=1200)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    rects = detector(gray, 1)
    shapes = []
    for (i, rect) in enumerate(rects):
        shape = predictor(gray, rect)
        shape = shape_to_np(shape)
        shapes.append(shape)
        (x, y, w, h) = rect_to_bb(rect)
        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
        cv2.putText(image, "Face: {}".format(i + 1), (x - 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

    count1 = 0
    count2 = 0# encoding:utf-8

import dlib
import numpy as np
import cv2

def rect_to_bb(rect): # 获得人脸矩形的坐标信息
    x = rect.left()
    y = rect.top()
    w = rect.right() - x
    h = rect.bottom() - y
    return (x, y, w, h)

def shape_to_np(shape, dtype="int"): # 将包含68个特征的的shape转换为numpy array格式
    coords = np.zeros((68, 2), dtype=dtype)
    for i in range(0, 68):
        coords[i] = (shape.part(i).x, shape.part(i).y)
    return coords


def resize(image, width=1200):  # 将待检测的image进行resize
    r = width * 1.0 / image.shape[1]
    dim = (width, int(image.shape[0] * r))
    resized = cv2.resize(image, dim, interpolation=cv2.INTER_AREA)
    return resized

def feature():
    image_file = r"F:\project_python\facenet\2.PNG"
    detector = dlib.get_frontal_face_detector()
    predictor = dlib.shape_predictor(r"F:\project_python\facenet\shape_predictor_68_face_landmarks.dat")
    image = cv2.imread(image_file)
    image = resize(image, width=1200)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    rects = detector(gray, 1)
    shapes = []
    for (i, rect) in enumerate(rects):
        shape = predictor(gray, rect)
        shape = shape_to_np(shape)
        shapes.append(shape)
        (x, y, w, h) = rect_to_bb(rect)
        cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
        cv2.putText(image, "Face: {}".format(i + 1), (x - 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)

    count1 = 0
    count2 = 0
    image1=image.copy()
    
    eyes=[]
    eyes2=[]
    
    for shape in shapes:
        left_eye=[]
        right_eye=[]
        left_eye2=[]
        right_eye2=[]
        for (x, y) in shape:
            
            cv2.circle(image1, (x, y), 2, (0, 0, 255), -1)
            cv2.putText(image1, str(count1)+"-"+str(count2), (x-3, y-3), cv2.FONT_HERSHEY_COMPLEX, 0.4, (100, 200, 200), 1)

            if count2>=36 and count2<=41:
                left_eye.append([x,y])
                left_eye2.append((x,y))
            elif count2>=42 and count2<=47:
                right_eye.append([x,y])
                right_eye2.append((x,y))
            
            count2+=1
        count1+=1    
        eyes.append([left_eye,right_eye])
        eyes2.append([left_eye2,right_eye2])
    cv2.imshow("Output", image1)
    cv2.waitKey(0)
    
    
    image2=image.copy()
    
    for i in range(len(eyes)):
        e=eyes[i]
        for j in range(len(e)):
            points=e[j]
            
            
            # 六边形的顶点坐标
            pts = np.array(points, np.int32)
            pts = pts.reshape((-1, 1, 2))

            # 创建一个空白图像作为掩模
            mask = np.zeros_like(image)

            # 在掩模上绘制填充了白色的六边形
            cv2.fillPoly(mask, [pts], (255, 255, 255))

            # 创建一个白色背景图像
            white_background = np.ones_like(image) * 255

            # 在白色背景上绘制填充了原始图像的六边形
            cv2.fillPoly(white_background, [pts], (0, 0, 0))

            # 将原始图像和白色背景图像按位取反
            inverse_mask = cv2.bitwise_not(mask)

            # 将原始图像中六边形内的部分与白色背景中六边形外的部分相结合
            result = cv2.bitwise_and(image, mask) + cv2.bitwise_and(white_background, inverse_mask)
            
            # 显示最终结果
            cv2.imshow('Result', result)
            cv2.waitKey(0)
            
            
            # 找到最小长方形的左上角和右下角坐标
            min_x = min(point[0] for point in points)
            max_x = max(point[0] for point in points)
            min_y = min(point[1] for point in points)
            max_y = max(point[1] for point in points)
            cv2.rectangle(image2, (min_x, min_y), (max_x, max_y), (0, 255, 255), 2)
            
            # 获取最小长方形中的图像部分
            roi = result[min_y:max_y, min_x:max_x]

            # 将图像部分转换为灰度图
            gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)

            # define a threshold, 128 is the middle of black and white in grey scale
            thresh = 100
            
            # assign blue channel to zeros
            img_binary = cv2.threshold(gray_roi, thresh, 255, cv2.THRESH_BINARY)[1]
            
            cv2.imshow("Output", img_binary)
            cv2.waitKey(0)
            
            #处理眼睛反光形成的白点
            # 获取图像高度
            img_binary1=img_binary.copy()
            height = img_binary1.shape[0]

            # 遍历每一列像素
            for col in range(img_binary1.shape[1]):
                white_run_length = 0
                start_index = 0
                end_index = 0
                for row in range(height):
                    if img_binary1[row, col] == 255:  # 白色像素
                        if white_run_length == 0:
                            start_index = row
                        white_run_length += 1
                        end_index = row
                    else:  # 黑色像素
                        if white_run_length > 0 and white_run_length < height / 2 and (start_index == 0 or img_binary1[start_index - 1, col] == 0) and (end_index == height - 1 or img_binary1[end_index + 1, col] == 0):
                            img_binary1[start_index:end_index + 1, col] = 0  # 将连续的白色像素改为黑色像素
                        white_run_length = 0

            cv2.imshow("Output", img_binary1)
            cv2.waitKey(0)
            
            # 计算每一列的像素值总和
            column_sums = np.sum(img_binary1, axis=0)

            # 计算每一列与相邻的n列的总和
            neighbor_sums = np.convolve(column_sums, np.ones(1), mode='valid')

            # 找到结果最小的n列的列号
            min_indices = np.argpartition(neighbor_sums, 20)[:20]

            # 将列号按数字排序
            sorted_indices = np.sort(min_indices)

            # 找到这7列的最中间的列的列号
            middle_column = sorted_indices[len(sorted_indices) // 2]

            print("每一列与相邻的6列的总和:", neighbor_sums)
            print("结果最小的7列的列号(按数字排序):", sorted_indices)
            print("这7列的最中间的列的列号:", middle_column)

            
            cv2.line(image2, (min_x+middle_column, min_y), (min_x+middle_column, max_y), (255, 0, 0))
            
            # 找出结果最小的列
            min_column = img_binary[:, middle_column]

            # 找出连续相邻的白色像素点
            white_pixels = np.where(min_column == 255)[0]

            # 找出连续相邻的白色像素点中最长的一段
            longest_start = 0
            longest_end = 0
            max_length = 0

            current_start = 0
            current_length = 0

            for i in range(1, len(white_pixels)):
                if white_pixels[i] == white_pixels[i-1] + 1:
                    current_length += 1
                else:
                    if current_length > max_length:
                        max_length = current_length
                        longest_start = white_pixels[current_start]
                        longest_end = white_pixels[i-1]
                    current_start = i
                    current_length = 0

            # 判断起始位置和结束位置的中点在图像的上半部分还是下半部分
            mid_point = (longest_start + longest_end) // 2
            height = img_binary.shape[0]
            if mid_point < height // 2:
                print("最长的白色像素段起始位置:", longest_start)
                print("最长的白色像素段结束位置:", longest_end)
                print("连续的长度:", max_length)
                print("中点在图像的上半部分")
                cv2.line(image2, (min_x,min_y+ height // 2-max_length), (max_x,min_y+ height // 2-max_length), (255, 0, 0))
            else:
                print("最长的白色像素段起始位置:", longest_start)
                print("最长的白色像素段结束位置:", longest_end)
                print("连续的长度:", max_length)
                print("中点在图像的下半部分")
                cv2.line(image2, (min_x,min_y+ height // 2+max_length), (max_x,min_y+ height // 2+max_length), (255, 0, 0))
  
    
    cv2.imshow("Output", image2)
    cv2.waitKey(0)


if __name__ == "__main__":

    feature()

文件下载:
1.shape_predictor_68_face_landmarks.dat 下载地址

1.官方下载地址(会比较慢) http://dlib.net/files/

2.我的网盘: 链接:https://pan.baidu.com/s/1ORhqLS1bkHyyYfzbmftNpg 提取码:va12

3.我的资源(免费下载):https://download.csdn.net/download/qq_51985653/15122811

原文链接:https://blog.csdn.net/qq_51985653/article/details/113748025

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1314939.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙实操【ArkTS语言的运用】

ArkTS基础知识 使用声明式语法和组件化基础知识&#xff0c;搭建一个可刷新的排行榜页面。在排行榜页面中&#xff0c;使用循环渲染控制语法来实现列表数据渲染&#xff0c;使用Builder创建排行列表布局内容&#xff0c;使用装饰器State、Prop、Link来管理组件状态。最后我们点…

Linux Conda 安装 Jupyter

在Linux服务器Conda环境上安装Jupyter过程中遇到了无数的报错&#xff0c;特此记录。 目录 步骤一&#xff1a;安装Anaconda3 步骤二&#xff1a;配置Conda源 步骤三&#xff1a;安装Jupyter 安装报错&#xff1a;simplejson.errors.JSONDecodeError 安装报错&#xff1a;…

循环神经网络-1

目录 1 数据集构建 1.1 数据集的构建函数 1.2 加载数据并进行数据划分 1.3 构造Dataset类 2 模型构建 2.1 嵌入层 2.2 SRN层 2.3 线性层 2.4 模型汇总 3 模型训练 3.1 训练指定长度的数字预测模型 3.2 多组训练 3.3 损失曲线展示 4 模型评价 总结 参考文献 循环神经网络&…

VC++项目的32位、64位的配置和链接问题

新建一个项目&#xff0c;默认是x86配置&#xff1b; 添加包含目录、库目录&#xff0c;之后可以编译通过&#xff1b; 但是链接会出错&#xff0c;因为链接的dll是64位&#xff1b; 把项目配置改为x64&#xff1b; 需要把包含目录和库目录针对x64重新添加&#xff0c;否则会…

获取CAD图元名及图元信息(circle为例,用于选择集,对应dxf组码)

在CAD编程中往往需要用选择集&#xff0c;我们往往不知道相应图元对应的名称具体名字。比如我想选择所有的圆&#xff0c;ftype0,fdata应该是什么呢&#xff1f;是circle&#xff0c;acdbcircle&#xff0c;还是acadcircle? circle是一个对象&#xff0c;circle的vba类名为Ac…

【CSS】用 CSS 写一个渐变色边框的输入框

Using_CSS_gradients MDN 多渐变色输入框&#xff0c;群友问了下&#xff0c;就试着写了下&#xff0c;看了看 css 渐变色 MDN 文档&#xff0c;其实很简单&#xff0c;代码记录下&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta ch…

【unity小技巧】两种办法解决FPS游戏枪或者人物穿墙穿模问题

文章目录 前言第一种解决思路第二种方法总结感谢完结 前言 当我们开发FPS游戏时&#xff08;其实3d游戏基本都会遇到这样的问题&#xff09;&#xff0c;如果我们不做处理&#xff0c;肯定会出现人物或者枪的穿墙穿模问题&#xff0c;这是是一个常见的挑战。 这种问题会破坏…

Flink窗口的概念和分类

窗口的概念 Flink是一种流式计算引擎&#xff0c;主要是来处理无界数据流的&#xff0c;数据源源不断、无穷无尽。想要更加方便高效地处理无界流&#xff0c;一种方式就是将无限数据切割成有限的“数据块”进行处理&#xff0c;这就是所谓的“窗口”&#xff08;Window&#x…

指针相关知识(进阶)

前面的入门中已经介绍了指针的基础知识&#xff0c;接下来&#xff0c;让我们继续学习吧&#xff01; 一. 字符指针变量 char* 一般形式 int main() {char n w;char* pa &n;*pa w;return 0; } 这并不是把字符串hello world放在n中&#xff0c;而是把第一个字符的地址…

【论文解读】System 2 Attention提高大语言模型客观性和事实性

一、简要介绍 本文简要介绍了论文“System 2 Attention (is something you might need too) ”的相关工作。基于transformer的大语言模型&#xff08;LLM&#xff09;中的软注意很容易将上下文中的不相关信息合并到其潜在的表征中&#xff0c;这将对下一token的生成产生不利影响…

LeetCode day24

LeetCode day24 今天主打一个快乐happy(▽ʃ♡ƪ)&#xff0c;主要是今天写哈夫曼树被经典文件读取坑麻了&#xff08;为啥绝对路径能读取&#xff0c;相对不行。罢了&#xff09; 一个中等题&#xff0c;但是咋感觉很小学捏。。。 2177. 找到和为给定整数的三个连续整数 相…

320科技工作室ZEMAX培训通知

一 软件介绍 ZEMAX是一套综合性的光学设计软件。它提供先进的、且符合工业标准的分析、优化、公差分析功能&#xff0c;能够快速准确的完成光学成像及照明设计。 二 培训方式 本次培训全程线上授课, 采用一对一或者一对多方式进行, 以视频方式授课&#xff0c;工程案例讲解&…

自动化测试(三)webdriver的常用api(1)

目录 等待 sleep休眠 隐式等待 显式等待 打印信息 打印title 打印url 浏览器的操作 浏览器最大化 设置浏览器宽、高 操作浏览器的前进、后退 控制浏览器滚动条 键盘事件 键盘按键用法 键盘组合键用法 鼠标事件 定位一组元素 前面两章我们讲了selenium环境的…

初识JVM底层知识,一文读懂JVM知识文集。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

基于ssm医用物理学实验考核系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本医用物理学实验考核系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数…

什么是前端国际化(internationalization)和本地化(localization)?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…

北斗卫星在城市管网系统中的应用

北斗卫星在城市管网系统中的应用 北斗卫星是我国自主研发的卫星导航系统&#xff0c;已经应用于各个领域。而在城市管网系统中&#xff0c;北斗卫星也有着重要的应用。本文将详细介绍北斗卫星如何应用于城市管网系统&#xff0c;以及其所带来的优势和作用。 在城市管网系统中&a…

RK3568/RV1126/RV1109/RV1106 ISP调试方案

最近一直在做瑞芯微rv1126的开发&#xff0c;由于项目性质&#xff0c;与camera打的交道比较多&#xff0c;包括图像的采集&#xff0c;ISP处理&#xff0c;图像处理&#xff0c;H.264/H.265编解码等各个方面吧。学到了不少&#xff0c;在学习的过程中&#xff0c;也得到了不少…

UE5 Landscaping MapBox 学习笔记

1. Landscaping MapBox 操作录屏 https://www.bilibili.com/video/BV113411U7T9/?spm_id_from333.337.search-card.all.click&vd_source707ec8983cc32e6e065d5496a7f79ee6 安装Landscaping与LandscapingMapbox两个插件 打开Landscaping窗口&#xff0c;这里应该要在Proj…

源于自然感受好眠,GMN双子座深睡科技新品发布会愉悦亮相

2023年12月12日&#xff0c;中国成都——“睡眠愉悦领域专家”GMN双子座&#xff0c;以“睡不着贴双子座”为主题召开品牌正式成立后的首次线下发布会。品牌敏锐洞察现代人的睡眠困境&#xff0c;并始终致力于以自然与科技为基础&#xff0c;为大众带来自然愉悦的睡眠体验&…