WordPress极简博客 WordPress极简博客
  • 新鲜事
  • 战疫情
  • UI素材
    • UI素材
    • 电商/节日
    • PPT
      • 节日庆典
      • 工作汇报
      • 商业计划书
    • word
      • 简历竞聘
      • 合同/公文
  • 创客头条
    • 音乐分享
    • 初创文章
    • 极客头条
    • 数码解说
    • 生活趣事
    • 生活日记
  • 全球科技
    • 新浪博客
    • A5资讯
    • 环球网新闻
  • 编程教学
    • Linux安全栏目
      • Linux运维安全汇总
      • DDOS攻击防护
      • XSS攻击防护
      • SQL安全防护
    • Python技术栏目
      • Python基础入门
      • Python基础结构
    • WordPress技术栏目
      • WP主题
      • WordPress技术教程
      • RIPRO主题美化
    • WordPress漏洞发布
    • 技术教程汇总
  • 专题
  • 基友
  • 隐私
  • 云优化
  • 注册
    登录
立即登录
  • 首页
  • 云优化
  • 新疫情
  • 新鲜事
    • 热文
    • 极客
    • 生活
  • 技术篇
    • WP主题
    • 技术教程
    • Python入门
    • Python基础
  • 专题篇
  • 友链君

Python人眼视线追踪系统

夏柔7月 17, 2020

视线追踪技术是现阶段的人机交互系统中比较关键的技术之一,在车辆、心理、军事、医学领域中都有着广泛的应用前景。从视线追踪技术产生到现在,研究者先后进行了侵入式与非侵入式方法的研究,由于侵入式对使用者而言比较麻烦,因此近年来很多学者都对非侵入式的视线追踪技术进行深入的研究,并取得了较快的发展,但由于人机交互系统与眼动本身的要求,现有的基于视线追踪的人机交互系统需要解决如下问题:减少对使用者的限制;提高系统的运行效率,达到实时性的要求;提高系统的检测准确度与稳定性。

本项目使用的是不需要用户佩戴任何硬件设备并对人体没有影响的非侵入式方法,进行视线追踪,基于摄像头实现人眼检测,瞳孔中心坐标提取,内眼角坐标提取,眼球空间建模,视线估计建模,眼睛空间与计算机屏幕空间映射函数的建立这几个方面。

1、项目简介

本项目首先使用将二维的加权系数降为一维的,并与中值滤波思想相结合的双边滤波算法预处理摄像头采集的图像,去除可能出现的高斯噪声与椒盐噪声,提高了图像质量,然后对图像进行直方图均衡化提高灰度对比度,增加动态范围,为以后更好地提取特征参数提供前提,接着使用基于Haar特征的Adaboost算法检测人脸与眼睛,根据眼睛图像的灰度分布特点,在处理后的图像上使用改进的混合投影函数粗略定位瞳孔中心,使用检测出的中心点像素值作为阀值对眼睛图像进行二值化处理,将处理后的瞳孔进行圆形拟合,求出精确的瞳孔中心,由于眼睛存在不自觉的微动现象,使得检测出来的瞳孔中心位置发生跳动,因此本文将3帧的瞳孔中心坐标的平均值作为特征参数,然后截取瞳孔中心右方包含眼角的眼睛局部图像,缩小内眼角检测的范围,使用改进的自适应阈值的SUSAN角点检测算法定位内眼角位置,此时视线追踪技术需要的眼睛特征参数就全部提取出来了,然后根据人眼的结构特点,本文提出了一个简单的眼睛三维空间模型,用于对将人眼三维坐标投影到二维坐标时产生的误差进行补偿,最后创建视线空间模型,建立眼睛平面参数与计算机屏幕坐标点之间的映射函数,在此函数中加入误差补偿参数,使得视线在屏幕中的落点更为准确,根据视线凝视此区域范围的时间来判断鼠标进行移动或单击操作。

本文使用最简单的USB摄像头,在Python开发平台上建立以视线方向落点为输入的鼠标操作人机交互系统,实现了对视线方向的估计,实验表明在自然光照的环境下,本系统能够比较准确的估计视线方向,实时的反应使用者眼动的情况,与主流的视线估计系统相比,具有硬件要求低,使用方便的优点。

2、系统适用行业和用途

由于视线分为凝视和扫视,而凝视总是能够体现人们感兴趣的区域,使用视线作为输入的人机交互系统能够帮助人们解决很多问题,方便,灵活,受到国内外相关研究人员的普遍关注。将眼动的规律特征与其他的诸如语言,鼠标等交互手段相结合,就会形成一-种更灵活,更接近人类之间正常交流的方式。视线追踪技术的应用范围可以分为以下几个大的领域:

1、计算机领域的应用

视线交互是由视线来完成用户与计算机之间的交流,使用用户的视线可以代替鼠标的操作,用户视线的落点就是鼠标箭头在计算机屏幕上的坐标,使用视线操作计算机时,通过移动视线的方向可以移动鼠标,完成相应的操作。例如,可以通过移动视线来完成向下滚动页面,完成阅读网页或电子书:通过在一个区域凝视规定时间以上来完成单击或双击操作;在操作界面上创建虛拟键盘,可以完成文字输入的功能;能够帮助老人、残疾人控制家用电器设备,实现间接的人机交互功能;通过对视线轨迹的记录对图像进行修改,能够很好的把握用户的意图;可以根据用户视线落点停留的时间决定是否将此点周围领域凸显出来,这就是视线跟随显示。

2、心理学领域

眼睛视线所收集的信息由神经系统进行处理,人类的视觉行为与感知与他的心理活动相关,是人类的想法的体现。因此研究人类的视线能够帮助人们了解人类的心理状况,例如:轮廓错觉的生理心理学调查,阅读心理学研究,视觉搜索机制研究,都使用了视线追踪技术。

3、工业工程

视线追踪技术可以应用于飞行模拟器中,记录模拟飞行时飞行员的眼睛和头部移动情况,提供了飞行员的注视点分析数据,为飞行员飞行提供数据,进行技术上的改进。视线追踪技术还可以应用于车辆驾驶中,可以帮助检查驾驶人的疲劳状态,不同情况下驾驶人的眼睛状态不同,眼睛运动的情况也不同。

3、系统的技术特点和功能

视线追踪技术按照系统的组成与视线方向检测方法不同,可以分为侵入式与非侵入式两种方式。

侵入式视线追踪系统指的是需要用户佩戴研发者特制的配置有光学系统的头盔等设备,能够实时,精确的对用户的眼睛动作做出反应,对视线落点的检测精度比较高,但是对用户的影响比较大。典型的侵入式视线追踪系统有美国应用科学实验室研发的ASL Model H6系列眼动仪,ASL Mobile Eye眼动追踪系统;加拿大SR Rese arch公司生产的EyeLinkII系统,这个系统对用户的动作要求不是很严格;德国SMI公司研发的iViewXHED系统,该系统操作自动化程度高,快速捕捉视线追踪数据等。

非侵入式视线追踪系统指的是使用者不需要佩戴任何设备,只需要利用一台摄像机来采集2D或3D图像,获取眼部和头部图像数据,视线特征数据,根据这些数据进行建模,估算出视线方向及视线的落点位置。对用户的影响比较小,但是需要建立的模型比较复杂,对视线方向的估计相对于侵入式的方法来说不是很准确。典型的非侵入式视线追踪系统有由美国EyeTechDigitalSystems公司研发的QuickGlance2系列,要求用户的眼睛不能离开摄像机的视野范围;美国SmartEye公司设计的SmartEyeAntiSleep2.0眼动仪,具有驾驶员头部模型自动初始化及视线方向初始化校准等功能。

要研究视线追踪的有关问题,首先需要了解有关眼睛的结构,眼睛运动,视觉注意以及,人眼视觉特性等相关特征,当头部有运动时还需要了解关于头部姿势,头部运动等特征。

早在古希腊时期就有关于眼动的研究,但真正对眼动进行观察和实验以及仪器的使用则是从中世纪开始的。阿拉伯人在中世纪早期就将动物的眼睛解剖开来进行研究分析,确定眼睛结构组成,记录光在眼睛中的折射率,将这些眼睛特性应用到眼动规律中,对视觉进行分析。

在最初时期,测量视线方向的手段比较匮乏,研究人员先后使用了机械、电流、电磁等记录眼睛运动的精确数据。机械记录法需要将辅助设备贴在眼睛上记录眼动数据,例如将薄片贴在眼睛角膜.上与标笔相连测试视线方向;电流记录法将特制电极设备放在眼睛的上下部位,利用电位差测试眼动情况;电磁感应法需要麻醉用户的眼睛,将装有探查线圈的镜片吸附在眼睛上,通上电压后根据电压的视敏度得到视线方向。这三种方法对使用者的眼睛都有影响,并且设备比较复杂,属于侵入式视线追踪方式。随着计算机技术,图像处理技术、机器视觉与模式识别技术的不断发展,视线追踪技术也不断的发展起来,技术手段也不断更新,利用摄像机采集人脸与眼睛,并使用图像处理的方法分析眼动数据的光学方法得到了广泛的应用。近年来研究者将注意力主.要集中到光学方法上,本项目中主要应用瞳孔-角膜反光法实现视线追踪。

瞳孔-角膜反光法:当用户看物体视线进行移动时,眼球也伴随着转动,但是人眼相对于头部有些不动的特征,当光线进入人眼时就在眼球的角膜外表面形成光点,这个光点叫做普尔钦斑,当眼球随着感兴趣的物体进行移动时,普尔钦斑相对于头部固定不动。瞳孔角膜反光法就是利用眼球转动时眼睛的--些不变特性与变动特征之间的函数关系,建立几何模型或映射模型来获取视线方向并取得视线的落点。由于这种方法在进行处理时比较方便并且对使用者不会造成不便,使用的情况比较多,比如利用红外线光源获取普尔钦斑与瞳孔中心形成的矢量建立模型获取视线落点位置,如下图所示:

4、项目效果 

使用Python+OpenCV实现实时眼动追踪,不需要高端硬件简单摄像头即可实现,效果图如下所示(项目演示视频)。

5、项目源码

项目主程序如下:

import sys
import cv2
import numpy as np
import process
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.uic import loadUi
from PyQt5.QtGui import QPixmap, QImage
 
 
class Window(QMainWindow):
    def __init__(self):
        super(Window, self).__init__()
        loadUi('GUImain.ui', self)
        with open("style.css", "r") as css:
            self.setStyleSheet(css.read())
        self.face_decector, self.eye_detector, self.detector = process.init_cv()
        self.startButton.clicked.connect(self.start_webcam)
        self.stopButton.clicked.connect(self.stop_webcam)
        self.camera_is_running = False
        self.previous_right_keypoints = None
        self.previous_left_keypoints = None
        self.previous_right_blob_area = None
        self.previous_left_blob_area = None
 
    def start_webcam(self):
        if not self.camera_is_running:
            self.capture = cv2.VideoCapture(cv2.CAP_DSHOW)  # VideoCapture(0) sometimes drops error #-1072875772
            if self.capture is None:
                self.capture = cv2.VideoCapture(0)
            self.camera_is_running = True
            self.timer = QTimer(self)
            self.timer.timeout.connect(self.update_frame)
            self.timer.start(2)
 
    def stop_webcam(self):
        if self.camera_is_running:
            self.capture.release()
            self.timer.stop()
            self.camera_is_running = not self.camera_is_running
 
    def update_frame(self):  # logic of the main loop
 
        _, base_image = self.capture.read()
        self.display_image(base_image)
 
        processed_image = cv2.cvtColor(base_image, cv2.COLOR_RGB2GRAY)
 
        face_frame, face_frame_gray, left_eye_estimated_position, right_eye_estimated_position, _, _ = process.detect_face(
            base_image, processed_image, self.face_decector)
 
        if face_frame is not None:
            left_eye_frame, right_eye_frame, left_eye_frame_gray, right_eye_frame_gray = process.detect_eyes(face_frame,
                                                                                                             face_frame_gray,
                                                                                                             left_eye_estimated_position,
                                                                                                             right_eye_estimated_position,
                                                                                                             self.eye_detector)
 
            if right_eye_frame is not None:
                if self.rightEyeCheckbox.isChecked():
                    right_eye_threshold = self.rightEyeThreshold.value()
                    right_keypoints, self.previous_right_keypoints, self.previous_right_blob_area = self.get_keypoints(
                        right_eye_frame, right_eye_frame_gray, right_eye_threshold,
                        previous_area=self.previous_right_blob_area,
                        previous_keypoint=self.previous_right_keypoints)
                    process.draw_blobs(right_eye_frame, right_keypoints)
 
                right_eye_frame = np.require(right_eye_frame, np.uint8, 'C')
                self.display_image(right_eye_frame, window='right')
 
            if left_eye_frame is not None:
                if self.leftEyeCheckbox.isChecked():
                    left_eye_threshold = self.leftEyeThreshold.value()
                    left_keypoints, self.previous_left_keypoints, self.previous_left_blob_area = self.get_keypoints(
                        left_eye_frame, left_eye_frame_gray, left_eye_threshold,
                        previous_area=self.previous_left_blob_area,
                        previous_keypoint=self.previous_left_keypoints)
                    process.draw_blobs(left_eye_frame, left_keypoints)
 
                left_eye_frame = np.require(left_eye_frame, np.uint8, 'C')
                self.display_image(left_eye_frame, window='left')
 
        if self.pupilsCheckbox.isChecked():  # draws keypoints on pupils on main window
            self.display_image(base_image)
 
    def get_keypoints(self, frame, frame_gray, threshold, previous_keypoint, previous_area):
 
        keypoints = process.process_eye(frame_gray, threshold, self.detector,
                                        prevArea=previous_area)
        if keypoints:
            previous_keypoint = keypoints
            previous_area = keypoints[0].size
        else:
            keypoints = previous_keypoint
        return keypoints, previous_keypoint, previous_area
 
    def display_image(self, img, window='main'):
        # Makes OpenCV images displayable on PyQT, displays them
        qformat = QImage.Format_Indexed8
        if len(img.shape) == 3:
            if img.shape[2] == 4:  # RGBA
                qformat = QImage.Format_RGBA8888
            else:  # RGB
                qformat = QImage.Format_RGB888
 
        out_image = QImage(img, img.shape[1], img.shape[0], img.strides[0], qformat)  # BGR to RGB
        out_image = out_image.rgbSwapped()
        if window == 'main':  # main window
            self.baseImage.setPixmap(QPixmap.fromImage(out_image))
            self.baseImage.setScaledContents(True)
        if window == 'left':  # left eye window
            self.leftEyeBox.setPixmap(QPixmap.fromImage(out_image))
            self.leftEyeBox.setScaledContents(True)
        if window == 'right':  # right eye window
            self.rightEyeBox.setPixmap(QPixmap.fromImage(out_image))
            self.rightEyeBox.setScaledContents(True)
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    window.setWindowTitle("GUI")
    window.show()
    sys.exit(app.exec_())

人眼检测程序如下:

import os
import cv2
import numpy as np
 
 
def init_cv():
    """loads all of cv2 tools"""
    face_detector = cv2.CascadeClassifier(
        os.path.join("Classifiers", "haar", "haarcascade_frontalface_default.xml"))
    eye_detector = cv2.CascadeClassifier(os.path.join("Classifiers", "haar", 'haarcascade_eye.xml'))
    detector_params = cv2.SimpleBlobDetector_Params()
    detector_params.filterByArea = True
    detector_params.maxArea = 1500
    detector = cv2.SimpleBlobDetector_create(detector_params)
 
    return face_detector, eye_detector, detector
 
 
def detect_face(img, img_gray, cascade):
    """
    Detects all faces, if multiple found, works with the biggest. Returns the following parameters:
    1. The face frame
    2. A gray version of the face frame
    2. Estimated left eye coordinates range
    3. Estimated right eye coordinates range
    5. X of the face frame
    6. Y of the face frame
    """
    coords = cascade.detectMultiScale(img, 1.3, 5)
 
    if len(coords) > 1:
        biggest = (0, 0, 0, 0)
        for i in coords:
            if i[3] > biggest[3]:
                biggest = i
        biggest = np.array([i], np.int32)
    elif len(coords) == 1:
        biggest = coords
    else:
        return None, None, None, None, None, None
    for (x, y, w, h) in biggest:
        frame = img[y:y + h, x:x + w]
        frame_gray = img_gray[y:y + h, x:x + w]
        lest = (int(w * 0.1), int(w * 0.45))
        rest = (int(w * 0.55), int(w * 0.9))
        X = x
        Y = y
 
    return frame, frame_gray, lest, rest, X, Y
 
 
def detect_eyes(img, img_gray, lest, rest, cascade):
    """
    :param img: image frame
    :param img_gray: gray image frame
    :param lest: left eye estimated position, needed to filter out nostril, know what eye is found
    :param rest: right eye estimated position
    :param cascade: Hhaar cascade
    :return: colored and grayscale versions of eye frames
    """
    leftEye = None
    rightEye = None
    leftEyeG = None
    rightEyeG = None
    coords = cascade.detectMultiScale(img_gray, 1.3, 5)
 
    if coords is None or len(coords) == 0:
        pass
    else:
        for (x, y, w, h) in coords:
            eyecenter = int(float(x) + (float(w) / float(2)))
            if lest[0] < eyecenter and eyecenter < lest[1]:
                leftEye = img[y:y + h, x:x + w]
                leftEyeG = img_gray[y:y + h, x:x + w]
                leftEye, leftEyeG = cut_eyebrows(leftEye, leftEyeG)
            elif rest[0] < eyecenter and eyecenter < rest[1]:
                rightEye = img[y:y + h, x:x + w]
                rightEyeG = img_gray[y:y + h, x:x + w]
                rightEye, rightEye = cut_eyebrows(rightEye, rightEyeG)
            else:
                pass  # nostril
    return leftEye, rightEye, leftEyeG, rightEyeG
 
 
def process_eye(img, threshold, detector, prevArea=None):
    """
    :param img: eye frame
    :param threshold: threshold value for threshold function
    :param detector:  blob detector
    :param prevArea: area of the previous keypoint(used for filtering)
    :return: keypoints
    """
    _, img = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY)
    img = cv2.erode(img, None, iterations=2)
    img = cv2.dilate(img, None, iterations=4)
    img = cv2.medianBlur(img, 5)
    keypoints = detector.detect(img)
    if keypoints and prevArea and len(keypoints) > 1:
        tmp = 1000
        for keypoint in keypoints:  # filter out odd blobs
            if abs(keypoint.size - prevArea) < tmp:
                ans = keypoint
                tmp = abs(keypoint.size - prevArea)
        keypoints = np.array(ans)
 
    return keypoints
 
def cut_eyebrows(img, imgG):
    height, width = img.shape[:2]
    img = img[15:height, 0:width]  # cut eyebrows out (15 px)
    imgG = imgG[15:height, 0:width]
 
    return img, imgG
 
 
def draw_blobs(img, keypoints):
    """Draws blobs"""
    cv2.drawKeypoints(img, keypoints, img, (0, 0, 255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
#人机交互系统#稳定性#视线追踪技术
0
分享
夏柔 站长
文章 709评论 23
赞赏
夏柔
相关文章
  • Python3逻辑运算符
  • Python3字典
  • Python3集合
  • Python3深浅复制
  • Python3字符串格式化
  • Python3字符串拼接
  • 字符串的编码与解码
  • 序列类型的讲解-字符串
  • 序列类型的讲解-元组
  • 序列类型的讲解-列表
27 5月, 2020
Elementor攻击:创造性的黑客如何结合漏洞来接管WordPress网站
夏柔
站长
夏山如碧 - 怀柔天下
709文章
23评论
58144K获赞
版权声明

文章采用创作共用版权 CC BY-NC-ND/2.5/CN 许可协议,与本站观点无关。

如果您认为本文侵犯了您的版权信息,请与我们联系修正或删除。
投诉邮箱wpsite@aliyun.com

栏目推荐
Python基础入门30
WordPress技术教程265
前沿技术情报所7
城市创新——新消费8
最近有哪些不可错过的热文5
程序员的养生之道0
疫情实况
英国变异新冠病毒或致更高死亡率
1月 23, 2021
31省新增确诊107例 本土90例
1月 23, 2021
拜登公布战时抗疫计划
1月 22, 2021
更多
每日快讯
警方调查西藏冒险王网传视频
1月 23, 2021
北京新增3例本土确诊 均在大兴
1月 23, 2021
英国变异新冠病毒或致更高死亡率
1月 23, 2021
云南昭通市盐津县发生4.7级地震
1月 23, 2021
外媒:特朗普弹劾案审讯2月8日开始
1月 23, 2021
31省新增确诊107例 本土90例
1月 23, 2021
美共和党议员提交弹劾拜登条款
1月 22, 2021
江苏镇江一人核酸检测结果可疑
1月 22, 2021
更多
  • 新鲜事
  • 疫情实况
  • UI素材
  • 技术教程
  • 音乐分享
  • 专题
  • 友情
  • 隐私
  • 云优化
Copyright © 2019-2021 WordPress极简博客. Designed by 骚老板. 辽公网安备21010502000474号