飞道的博客

OpenCV-眼睛控制鼠标

379人阅读  评论(0)

找来了一篇好玩的 大伙可以试试啊

如何用眼睛来控制鼠标?一种基于单一前向视角的机器学习眼睛姿态估计方法。在此项目中,每次单击鼠标时,我们都会编写代码来裁剪你们的眼睛图像。使用这些数据,我们可以反向训练模型,从你们您的眼睛预测鼠标的位置。在开始项目之前,我们需要引入第三方库。


  
  1. import cv2 \# For performing array operations
  2. import numpy  as np \# For creating and removing directories
  3. import os
  4. import shutil \# For recognizing and performing actions on mouse presses
  5. from pynput.mouse  import Listener

首先让我们了解一下Pynput的Listener工作原理。pynput.mouse.Listener创建一个后台线程,该线程记录鼠标的移动和鼠标的点击。这是一个简化代码,当你们按下鼠标时,它会打印鼠标的坐标:


  
  1. from pynput.mouse  import Listener
  2. def  on_click( x, y, button, pressed):
  3. """
  4.   Args:
  5.     x: the x-coordinate of the mouse
  6.     y: the y-coordinate of the mouse
  7.     button: 1 or 0, depending on right-click or left-click
  8.     pressed: 1 or 0, whether the mouse was pressed or released
  9.   """
  10. if pressed:
  11. print (x, y)
  12. with Listener(on_click = on_click)  as listener:
  13.   listener.join()

现在,为了实现我们的目的,让我们扩展这个框架。但是,我们首先需要编写裁剪眼睛边界框的代码。我们稍后将在on_click函数内部调用此函数。我们使用Haar级联对象检测来确定用户眼睛的边界框。你们可以在此处下载检测器文件,让我们做一个简单的演示来展示它是如何工作的:


  
  1. import cv2
  2. # Load the cascade  classifier detection object
  3. cascade  = cv2.CascadeClassifier("haarcascade_eye.xml")
  4. # Turn  on the web camera
  5. video_capture  = cv2.VideoCapture( 0)
  6. # Read data  from the web camera ( get the frame)
  7. _, frame  = video_capture.read()
  8. Convert the image  to grayscale
  9. gray  = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  10. # Predict the bounding box  of the eyes
  11. boxes  = cascade.detectMultiScale(gray,  1.310)
  12. Filter  out images taken  from a bad angle  with errors
  13. # We want  to make sure  both eyes were detected,  and nothing  else
  14. if len(boxes)  = =  2:
  15. eyes  = []
  16. for box  in boxes:
  17.     #  Get the rectangle parameters  for the detected eye
  18. x, y, w, h  = box
  19.     # Crop the bounding box  from the frame
  20. eye  = frame[y:y  + h, x:x  + w]
  21.     # Resize the crop  to  32x32
  22. eye  = cv2.resize(eye, ( 3232))
  23.     #  Normalize
  24. eye  = (eye  - eye. min())  / (eye. max()  - eye. min())
  25.     # Further crop  to just around the eyeball
  26. eye  = eye[ 10: -105: -5]
  27.     # Scale  between [ 0255and  convert  to  int datatype
  28. eye  = (eye  *  255).astype(np.uint8)
  29.     #  Add the  current eye  to the list  of  2 eyes
  30. eyes.append(eye)
  31.   # Concatenate the two eye images  into  one
  32. eyes  = np.hstack(eyes)

现在,让我们使用此知识来编写用于裁剪眼睛图像的函数。首先,我们需要一个辅助函数来进行标准化:


  
  1. def  normalize( x):
  2.   minn, maxx = x. min(), x. max()
  3. return (x - minn) / (maxx - minn)

这是我们的眼睛裁剪功能。如果发现眼睛,它将返回图像。否则,它返回None:


  
  1. def  scan(image_size=( 3232)):
  2. _, frame = video_capture. read()
  3. gray = cv2. cvtColor(frame, cv2.COLOR_BGR2GRAY)
  4. boxes = cascade. detectMultiScale(gray,  1.310)
  5. if  len(boxes) ==  2:
  6. eyes = []
  7. for box in boxes:
  8. x, y, w, h = box
  9. eye = frame[y:y + h, x:x + w]
  10. eye = cv2. resize(eye, image_size)
  11. eye =  normalize(eye)
  12. eye = eye[ 10:- 105:- 5]
  13. eyes. append(eye)
  14. return (np. hstack(eyes) *  255). astype(np.uint8)
  15. else:
  16. return None

现在,让我们来编写我们的自动化,该自动化将在每次按下鼠标按钮时运行。(假设我们之前已经root在代码中将变量定义为我们要存储图像的目录):


  
  1. def  on_click( x, y, button, pressed): \ # If the action was a mouse PRESS (not a RELEASE)
  2. if pressed: \ # Crop the eyes
  3.     eyes = scan() \ # If the function returned None, something went wrong
  4. if  not eyes  is  None: \ # Save the image
  5.       filename = root +  "{} {} {}.jpeg". format(x, y, button)
  6.       cv2.imwrite(filename, eyes)

现在,我们可以回忆起pynput的实现Listener,并进行完整的代码实现:


  
  1. import cv2
  2. import numpy  as np
  3. import os
  4. import shutil
  5. from pynput.mouse  import Listener
  6. root =  input( "Enter the directory to store the images: ")
  7. if os.path.isdir(root):
  8.   resp =  ""
  9. while  not resp  in [ "Y""N"]:
  10.     resp =  input( "This directory already exists. If you continue, the contents of the existing directory will be deleted. If you would still like to proceed, enter [Y]. Otherwise, enter [N]: ")
  11. if resp ==  "Y"
  12.     shutil.rmtree(root)
  13. else:
  14.     exit()
  15. os.mkdir(root)
  16.  \ # Normalization helper function
  17. def  normalize( x):
  18.   minn, maxx = x. min(), x. max()
  19. return (x - minn) / (maxx - minn)
  20.  \ # Eye cropping function
  21. def  scan( image_size=(3232)):
  22.   _, frame = video_capture.read()
  23.   gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  24.   boxes = cascade.detectMultiScale(gray,  1.310)
  25. if  len(boxes) ==  2:
  26.     eyes = []
  27. for box  in boxes:
  28.       x, y, w, h = box
  29.       eye = frame[y:y + h, x:x + w]
  30.       eye = cv2.resize(eye, image_size)
  31.       eye = normalize(eye)
  32.       eye = eye[ 10:- 105:- 5]
  33.       eyes.append(eye)
  34. return (np.hstack(eyes) *  255).astype(np.uint8)
  35. else:
  36. return  None
  37. def  on_click( x, y, button, pressed): \ # If the action was a mouse PRESS (not a RELEASE)
  38. if pressed: \ # Crop the eyes
  39.     eyes = scan() \ # If the function returned None, something went wrong
  40. if  not eyes  is  None: \ # Save the image
  41.       filename = root +  "{} {} {}.jpeg". format(x, y, button)
  42.       cv2.imwrite(filename, eyes)
  43. cascade = cv2.CascadeClassifier( "haarcascade_eye.xml")
  44. video_capture = cv2.VideoCapture( 0)
  45. with Listener(on_click = on_click)  as listener:
  46.   listener.join()

运行此命令时,每次单击鼠标(如果两只眼睛都在视线中),它将自动裁剪网络摄像头并将图像保存到适当的目录中。图像的文件名将包含鼠标坐标信息,以及它是右击还是左击。

这是一个示例图像。在此图像中,我在分辨率为2560x1440的监视器上在坐标(385,686)上单击鼠标左键:

 

级联分类器非常准确,到目前为止,我尚未在自己的数据目录中看到任何错误。现在,让我们编写用于训练神经网络的代码,以给定你们的眼睛图像来预测鼠标的位置。


  
  1. import numpy  as np
  2. import os
  3. import cv2
  4. import pyautogui
  5. from tensorflow.keras.models  import *
  6. from tensorflow.keras.layers  import *
  7. from tensorflow.keras.optimizers  import *

现在,让我们添加级联分类器:


  
  1. cascade = cv2.CascadeClassifier( "haarcascade_eye.xml")
  2. video_capture = cv2.VideoCapture( 0)

正常化:


  
  1. def  normalize( x):
  2.   minn, maxx = x. min(), x. max()
  3. return (x - minn) / (maxx - minn)

捕捉眼睛:


  
  1. def  scan(image_size=( 3232)):
  2. _, frame = video_capture. read()
  3. gray = cv2. cvtColor(frame, cv2.COLOR_BGR2GRAY)
  4. boxes = cascade. detectMultiScale(gray,  1.310)
  5. if  len(boxes) ==  2:
  6. eyes = []
  7. for box in boxes:
  8. x, y, w, h = box
  9. eye = frame[y:y + h, x:x + w]
  10. eye = cv2. resize(eye, image_size)
  11. eye =  normalize(eye)
  12. eye = eye[ 10:- 105:- 5]
  13. eyes. append(eye)
  14. return (np. hstack(eyes) *  255). astype(np.uint8)
  15. else:
  16. return None

让我们定义显示器的尺寸。你们必须根据自己的计算机屏幕的分辨率更改以下参数:


  
  1. # Note that there  are actually  2560x1440 pixels  on my screen
  2. # I am simply recording  one less, so that  when we divide  by these
  3. # numbers, we will  normalize  between  0  and  1. Note that mouse
  4. # coordinates  are reported starting  at ( 00),  not ( 11)
  5. width, height  =  25591439

现在,让我们加载数据(同样,假设你们已经定义了root)。我们并不在乎是单击鼠标右键还是单击鼠标左键,因为我们的目标只是预测鼠标的位置:


  
  1. filepaths = os. listdir(root)
  2. X, Y = [], []
  3. for filepath in filepaths:
  4. x, y, _ = filepath. split( ' ')
  5. x =  float(x) / width
  6. y =  float(y) / height
  7. X. append(cv2. imread(root + filepath))
  8. Y. append([x, y])
  9. X = np. array(X) /  255.0
  10. Y = np. array(Y)
  11. print  (X.shape, Y.shape)

让我们定义我们的模型架构:


  
  1. model = Sequential()
  2. model. add(Conv2D( 3232, activation =  'relu', input_shape = ( 12443)))
  3. model. add(Conv2D( 6422, activation =  'relu'))
  4. model. add(Flatten())
  5. model. add(Dense( 32, activation =  'relu'))
  6. model. add(Dense( 2, activation =  'sigmoid'))
  7. model.compile(optimizer =  "adam", loss =  "mean_squared_error")
  8. model.summary()

这是我们的摘要:

接下来的任务是训练模型。我们将在图像数据中添加一些噪点:


  
  1. epochs  =  200
  2. for epoch  in  range (epochs ) :
  3.   model.fit (X , Y , batch_size  =  32 )

现在让我们使用我们的模型来实时移动鼠标。请注意,这需要大量数据才能正常工作。但是,作为概念证明,你们会注意到,实际上只有200张图像,它确实将鼠标移到了你们要查看的常规区域。当然,除非你们拥有更多的数据,否则这是不可控的。


  
  1. while True:
  2.   eyes =  scan()
  3. if not eyes is None:
  4.       eyes = np. expand_dims(eyes /  255.0, axis =  0)
  5.       x, y = model. predict(eyes)[ 0]
  6.       pyautogui. moveTo(x * width, y * height)

这是一个概念证明的例子。请注意,在进行此屏幕录像之前,我们只训练了很少的数据。这是我们的鼠标根据眼睛自动移动到终端应用程序窗口的视频。就像我说的那样,这很容易,因为数据很少。有了更多的数据,它有望稳定到足以以更高的特异性进行控制。仅用几百张图像,你们就只能将其移动到注视的整个区域内。另外,如果在整个数据收集过程中,你们在屏幕的特定区域(例如边缘)都没有拍摄任何图像,则该模型不太可能在该区域内进行预测。

 whaosoft aiot http://143ai.com   完事了 大伙赶紧试试吧


转载:https://blog.csdn.net/qq_29788741/article/details/127798912
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场