├── .gitignore ├── README.md ├── facetracker_servo_arduino.py ├── facetracker_servo_gpio.py └── haarcascade_frontalface_alt.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | py_servo_facetracker 2 | ==================== 3 | 4 | Face detection and pan & tilt tracking with OpenCV and Python -------------------------------------------------------------------------------- /facetracker_servo_arduino.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ 3 | This program is demonstration for face and object detection using haar-like features. 4 | The program finds faces in a camera image or video stream and displays a red box around them, 5 | then centers the webcam via two servos so the face is at the center of the screen 6 | Based on facedetect.py in the OpenCV samples directory 7 | """ 8 | import sys 9 | from optparse import OptionParser 10 | import serial 11 | import cv2.cv as cv 12 | 13 | # Parameters for haar detection 14 | # From the API: 15 | # The default parameters (scale_factor=2, min_neighbors=3, flags=0) are tuned 16 | # for accurate yet slow object detection. For a faster operation on real video 17 | # images the settings are: 18 | # scale_factor=1.2, min_neighbors=2, flags=CV_HAAR_DO_CANNY_PRUNING, 19 | # min_size=>> servo.move(2, 90) 87 | ... # "move servo #2 to 90 degrees"''' 88 | 89 | if (min_pwm <= angle <= max_pwm): 90 | ser.write(chr(255)) 91 | ser.write(chr(servo)) 92 | ser.write(chr(angle)) 93 | else: 94 | print "Servo angle must be an integer between 0 and 180.\n" 95 | 96 | if __name__ == '__main__': 97 | #ser=serial.Serial(port='/dev/ttyUSB0',baudrate=9600,timeout=1) 98 | ser=serial.Serial(port='/dev/ttyACM0',baudrate=9600,timeout=1) 99 | 100 | # parse cmd line options, setup Haar classifier 101 | parser = OptionParser(usage = "usage: %prog [options] [camera_index]") 102 | parser.add_option("-c", "--cascade", action="store", dest="cascade", type="str", help="Haar cascade file, default %default", default = "./haarcascade_frontalface_alt.xml") 103 | (options, args) = parser.parse_args() 104 | 105 | cascade = cv.Load(options.cascade) 106 | 107 | if len(args) != 1: 108 | parser.print_help() 109 | sys.exit(1) 110 | 111 | input_name = args[0] 112 | if input_name.isdigit(): 113 | capture = cv.CreateCameraCapture(int(input_name)) 114 | else: 115 | print "We need a camera input! Specify camera index e.g. 0" 116 | sys.exit(0) 117 | 118 | cv.NamedWindow("result", 1) 119 | 120 | if capture: 121 | frame_copy = None 122 | 123 | move(panGpioPin, servoPanPosition) 124 | move(tiltGpioPin, servoTiltPosition) 125 | 126 | while True: 127 | frame = cv.QueryFrame(capture) 128 | if not frame: 129 | cv.WaitKey(0) 130 | break 131 | if not frame_copy: 132 | frame_copy = cv.CreateImage((frame.width,frame.height), 133 | cv.IPL_DEPTH_8U, frame.nChannels) 134 | if frame.origin == cv.IPL_ORIGIN_TL: 135 | cv.Copy(frame, frame_copy) 136 | else: 137 | cv.Flip(frame, frame_copy, 0) 138 | 139 | midScreenX = (frame.width/2) 140 | midScreenY = (frame.height/2) 141 | 142 | midFace = detect_and_draw(frame_copy, cascade) 143 | 144 | if midFace is not None: 145 | midFaceX = midFace[0] 146 | midFaceY = midFace[1] 147 | 148 | #Find out if the X component of the face is to the left of the middle of the screen. 149 | if(midFaceX < (midScreenX - midScreenWindow)): 150 | #Update the pan position variable to move the servo to the right. 151 | servoPanPosition += panStepSize 152 | print str(midFaceX) + " > " + str(midScreenX) + " : Pan Right : " + str(servoPanPosition) 153 | #Find out if the X component of the face is to the right of the middle of the screen. 154 | elif(midFaceX > (midScreenX + midScreenWindow)): 155 | #Update the pan position variable to move the servo to the left. 156 | servoPanPosition -= panStepSize 157 | print str(midFaceX) + " < " + str(midScreenX) + " : Pan Left : " + str(servoPanPosition) 158 | else: 159 | print str(midFaceX) + " ~ " + str(midScreenX) + " : " + str(servoPanPosition) 160 | 161 | servoPanPosition = min(servoPanPosition, max_pwm) 162 | servoPanPosition = max(servoPanPosition, min_pwm) 163 | move(panGpioPin, servoPanPosition) 164 | 165 | #Find out if the Y component of the face is below the middle of the screen. 166 | if(midFaceY < (midScreenY - midScreenWindow)): 167 | if(servoTiltPosition <= max_pwm): 168 | #Update the tilt position variable to lower the tilt servo. 169 | servoTiltPosition -= tiltStepSize 170 | print str(midFaceY) + " > " + str(midScreenY) + " : Tilt Down : " + str(servoTiltPosition) 171 | #Find out if the Y component of the face is above the middle of the screen. 172 | elif(midFaceY > (midScreenY + midScreenWindow)): 173 | if(servoTiltPosition >= 1): 174 | #Update the tilt position variable to raise the tilt servo. 175 | servoTiltPosition += tiltStepSize 176 | print str(midFaceY) + " < " + str(midScreenY) + " : Tilt Up : " + str(servoTiltPosition) 177 | else: 178 | print str(midFaceY) + " ~ " + str(midScreenY) + " : " + str(servoTiltPosition) 179 | 180 | servoTiltPosition = min(servoTiltPosition, max_pwm) 181 | servoTiltPosition = max(servoTiltPosition, min_pwm) 182 | move(tiltGpioPin, servoTiltPosition) 183 | 184 | if cv.WaitKey(10) >= 0: # 10ms delay 185 | break 186 | 187 | cv.DestroyWindow("result") 188 | -------------------------------------------------------------------------------- /facetracker_servo_gpio.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ 3 | This program is demonstration for face and object detection using haar-like features. 4 | The program finds faces in a camera image or video stream and displays a red box around them, 5 | then centers the webcam via two servos so the face is at the center of the screen 6 | Based on facedetect.py in the OpenCV samples directory 7 | """ 8 | import sys 9 | from optparse import OptionParser 10 | import cv2.cv as cv 11 | import os 12 | 13 | # Parameters for haar detection 14 | # From the API: 15 | # The default parameters (scale_factor=2, min_neighbors=3, flags=0) are tuned 16 | # for accurate yet slow object detection. For a faster operation on real video 17 | # images the settings are: 18 | # scale_factor=1.2, min_neighbors=2, flags=CV_HAAR_DO_CANNY_PRUNING, 19 | # min_size=>> servo.move(2, 90) 87 | ... # "move servo #2 to 90 degrees"''' 88 | 89 | if (min_pwm <= angle <= max_pwm): 90 | command = 'echo %s=%s > /dev/servoblaster' % (str(servo), str(angle)) 91 | os.system(command) 92 | #print command 93 | else: 94 | print "Servo angle must be an integer between 0 and 249.\n" 95 | 96 | if __name__ == '__main__': 97 | # parse cmd line options, setup Haar classifier 98 | parser = OptionParser(usage = "usage: %prog [options] [camera_index]") 99 | parser.add_option("-c", "--cascade", action="store", dest="cascade", type="str", help="Haar cascade file, default %default", default = "./haarcascade_frontalface_alt.xml") 100 | (options, args) = parser.parse_args() 101 | 102 | cascade = cv.Load(options.cascade) 103 | 104 | if len(args) != 1: 105 | parser.print_help() 106 | sys.exit(1) 107 | 108 | input_name = args[0] 109 | if input_name.isdigit(): 110 | capture = cv.CreateCameraCapture(int(input_name)) 111 | else: 112 | print "We need a camera input! Specify camera index e.g. 0" 113 | sys.exit(0) 114 | 115 | cv.NamedWindow("result", 1) 116 | 117 | if capture: 118 | frame_copy = None 119 | 120 | move(panGpioPin, servoPanPosition) 121 | move(tiltGpioPin, servoTiltPosition) 122 | 123 | while True: 124 | frame = cv.QueryFrame(capture) 125 | if not frame: 126 | cv.WaitKey(0) 127 | break 128 | if not frame_copy: 129 | frame_copy = cv.CreateImage((frame.width,frame.height), 130 | cv.IPL_DEPTH_8U, frame.nChannels) 131 | if frame.origin == cv.IPL_ORIGIN_TL: 132 | cv.Copy(frame, frame_copy) 133 | else: 134 | cv.Flip(frame, frame_copy, 0) 135 | 136 | midScreenX = (frame.width/2) 137 | midScreenY = (frame.height/2) 138 | 139 | midFace = detect_and_draw(frame_copy, cascade) 140 | 141 | if midFace is not None: 142 | midFaceX = midFace[0] 143 | midFaceY = midFace[1] 144 | 145 | #Find out if the X component of the face is to the left of the middle of the screen. 146 | if(midFaceX < (midScreenX - midScreenWindow)): 147 | #Update the pan position variable to move the servo to the right. 148 | servoPanPosition += panStepSize 149 | print str(midFaceX) + " > " + str(midScreenX) + " : Pan Right : " + str(servoPanPosition) 150 | #Find out if the X component of the face is to the right of the middle of the screen. 151 | elif(midFaceX > (midScreenX + midScreenWindow)): 152 | #Update the pan position variable to move the servo to the left. 153 | servoPanPosition -= panStepSize 154 | print str(midFaceX) + " < " + str(midScreenX) + " : Pan Left : " + str(servoPanPosition) 155 | else: 156 | print str(midFaceX) + " ~ " + str(midScreenX) + " : " + str(servoPanPosition) 157 | 158 | servoPanPosition = min(servoPanPosition, max_pwm) 159 | servoPanPosition = max(servoPanPosition, min_pwm) 160 | move(panGpioPin, servoPanPosition) 161 | 162 | #Find out if the Y component of the face is below the middle of the screen. 163 | if(midFaceY < (midScreenY - midScreenWindow)): 164 | if(servoTiltPosition <= max_pwm): 165 | #Update the tilt position variable to lower the tilt servo. 166 | servoTiltPosition -= tiltStepSize 167 | print str(midFaceY) + " > " + str(midScreenY) + " : Tilt Down : " + str(servoTiltPosition) 168 | #Find out if the Y component of the face is above the middle of the screen. 169 | elif(midFaceY > (midScreenY + midScreenWindow)): 170 | if(servoTiltPosition >= 1): 171 | #Update the tilt position variable to raise the tilt servo. 172 | servoTiltPosition += tiltStepSize 173 | print str(midFaceY) + " < " + str(midScreenY) + " : Tilt Up : " + str(servoTiltPosition) 174 | else: 175 | print str(midFaceY) + " ~ " + str(midScreenY) + " : " + str(servoTiltPosition) 176 | 177 | servoTiltPosition = min(servoTiltPosition, max_pwm) 178 | servoTiltPosition = max(servoTiltPosition, min_pwm) 179 | move(tiltGpioPin, servoTiltPosition) 180 | 181 | if cv.WaitKey(10) >= 0: # 10ms delay 182 | break 183 | 184 | cv.DestroyWindow("result") 185 | --------------------------------------------------------------------------------