今天写一个相对完善的眼动项目
基于磁场定位的眼动追踪技术
眼动追踪传感器选型
眼动追踪:梯度法精确定位眼中心(论文)
开源眼动-前导篇
开眼眼动-分析引擎
眼动追踪中的坐标+追踪原理
开源眼动追踪:GazeTracking(上:效果)
开源眼动追踪:GazeTracking(下:实现)
EyeLoop基于Python的眼动仪(超棒)
目前有几个方案,在下面:
以我目前念的这个书,我还是用视觉方案来实现:
我们在这里主要是直接给出ROI的区域减少算力。
在预处理步骤中,使用了高斯平滑和边缘检测来增强图像特征
在特征提取步骤中,使用了霍夫圆变换来检测圆形区域
参数
在目标检测步骤中,找到最大的圆形区域作为眼球,并在图像中标记出来
太简单了家人们!
但是这个程序太简单了,就是一个找特征啥的,有点傻。这次换个库:
dlib提供一个方法可将人脸图片数据映射到128维度的空间向量,如果两张图片来源于同一个人,那么两个图片所映射的空间向量距离就很近,否则就会很远。因此,可以通过提取图片并映射到128维空间向量再度量它们的欧氏距离(Euclidean distance)是否足够小来判定是否为同一个人。我不要人我就要眼睛。
上次觉得CMake碍眼,卸载了
稍等不知道多久,反正我吃了个橘子
python.exe -m pip install --upgrade pip
安装成功了
这个就是里面的dat文件,68个关键点
先找脸,再找眼,合理!
简单的转下颜色,然后直接找
dlib 库提供了两个用于人脸检测的功能。
第一个是HOG+线性SVM人脸检测器,另一个是深度学习MMOD CNN人脸检测器。
反正就是找一下面部的区域:
这些代码是再找眼部的位置
在范围之内就可以了
有极值坐标嘎嘎算
接着进一步给出瞳孔的坐标
我考你,你遇到这种情况怎么写?
先给外圈的大圆套上
再处理瞳孔
多数情况下,我们是要实时的检测的:
来一段从摄像头捕获的代码
祖传代码不能丢
更实用性的是两个摄像头来捕捉眼动:
这个写的比较呆逼,不过我在后面会有进行封装
现在是弹出两个框来输出图像,赶紧不好看捏!我们来让他并排排列!
使用OpenCV中的cv2.hconcat()函数将两个视频帧水平合并在一起,并使用cv2.imshow()函数将合并后的视频帧显示出来。
就很简单,其实这里就变成一个合并横向排列的视频组,但是在处理流程上面有问题,应该先单一处理,最后合并结果。
封装好啦!(有点傻逼哦~)
继续封装,注意视频流
工程问题的话,上面的代码还是太慢了,让我来加一点多线程的魔法!
设计两个线程分别处理左右眼视频帧的读取和合并:
队列无疑是最合适的数据结构
合并函数也是如此,因为图像这种处理的方式就适合队列
这个是提前设置好的
标准的流程
这个就是线程的启动了,然后一个循环不停的合并
也可以加一个日志的功能,直接写到最上面就行
现在的程序一点也不装逼,如果可以加一些文字什么的,就更好啦!
要把视频流封装成一个类,然后里面也是多线程处理
在下面调用的时候,就是实例化代码
这里就显示一个左眼的FPS信息叠加
代码中,putText函数用于将帧率信息添加到视频帧的左上角。其中,cv2.FONT_HERSHEY_SIMPLEX指定了字体类型,1指定了字体大小,(255, 255, 255)指定了字体颜色,2指定了字体线宽。
一方面显示是可视化,另一方面我们需要保存具体的眼动数据来后处理。可以在程序中添加一个函数来提取圆形框的坐标信息,并将它们保存到一个文件中。
这个是简单版本
eye_data 是一个包含眼动信息的列表,每个元素都是一个二元组,表示眼睛的坐标。在循环中,将每个元素写入文件中,每个坐标之间用逗号分隔,每行结束后添加一个换行符。
现在就是一个较为完善的函数了
再让我封装一下:
这个是信息保存的函数
目前实现的功能挺多的了,现在来写一个GUI吧!
就两个线程就行
初始化是捕获线程,在run函数里面进行了颜色的转换
使用定时器来更新帧率信息
这些都简单
都比较简单吧?
完整代码我放在Github上面了。
我们拿到了保存的数据,想重新把他们展示出来。假设眼动数据文件是一个文本文件,每行包含两个数字,分别代表左右眼的坐标。
使用matplotlib库中的plot函数来绘制左右眼的坐标。下面是一个简单的示例代码,绘制左眼的x坐标和y坐标:
来封装一下
好啦!
再实现一个功能吧!
在播放的时候点按鼠标就可以捕捉当前播放的数据而且在图片上面标注时间戳。
程序应该这样写:
读取
后面的功能分开写不好,这里合在一起写。
先读取视频帧,然后就是获取帧率,创建一个窗口
能看懂吧?
这个就是绘制标记
许久不用Python,然后坏了,各种运行出错,VSCode都扑街了,哭死,不知道咋办了。
解决不了
重新安装就好啦!
嘤嘤嘤,没看上
c:/Users/yunswj/AppData/Local/Programs/Python/Python310/python.exe -m
pip install ipykernel -U --user --force-reinstall
pip install opencv-python
https://cmake.org/download/
import cv2
import threading
class VideoStream:
def __init__(self, src=0, width=640, height=480, fps=30):
self.stream = cv2.VideoCapture(src)
self.stream.set(cv2.CAP_PROP_FRAME_WIDTH, width)
self.stream.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
self.stream.set(cv2.CAP_PROP_FPS, fps)
self.width = int(self.stream.get(cv2.CAP_PROP_FRAME_WIDTH))
self.height = int(self.stream.get(cv2.CAP_PROP_FRAME_HEIGHT))
self.fps = int(self.stream.get(cv2.CAP_PROP_FPS))
self.status = False
self.frame = None
def start(self):
if self.status:
return None
self.status = True
threading.Thread(target=self.update, args=()).start()
def update(self):
while self.status:
_, self.frame = self.stream.read()
def read(self):
return self.frame
def stop(self):
self.status = False
def main():
# 创建两个VideoStream对象,用于捕获左右眼视频流
left_cam = VideoStream(0)
right_cam = VideoStream(1)
# 开始捕获视频流
left_cam.start()
right_cam.start()
# 创建OpenCV窗口用于显示视频流
cv2.namedWindow("Video Stream", cv2.WINDOW_NORMAL)
while True:
# 读取左右眼视频流
left_frame = left_cam.read()
right_frame = right_cam.read()
# 在视频流上添加帧率信息
left_fps_text = f"FPS: {left_cam.fps}"
right_fps_text = f"FPS: {right_cam.fps}"
cv2.putText(
left_frame,
left_fps_text,
(10, 30),
cv2.FONT_HERSHEY_SIMPLEX,
1,
(255, 255, 255),
2,
)
cv2.putText(
right_frame,
right_fps_text,
(10, 30),
cv2.FONT_HERSHEY_SIMPLEX,
1,
(255, 255, 255),
2,
)
# 合并左右眼视频流并显示
merged_frame = cv2.hconcat([left_frame, right_frame])
cv2.imshow("Video Stream", merged_frame)
# 按'q'键退出
if cv2.waitKey(1) & 0xFF == ord("q"):
break
# 停止视频流捕获
left_cam.stop()
right_cam.stop()
# 关闭OpenCV窗口
cv2.destroyAllWindows()
if __name__ == "__main__":
main()