{"id":87,"date":"2013-02-21T11:49:02","date_gmt":"2013-02-21T11:49:02","guid":{"rendered":"http:\/\/www.nicassio.it\/daniele\/blog\/?p=87"},"modified":"2013-02-21T11:49:02","modified_gmt":"2013-02-21T11:49:02","slug":"raspberry-pi-powered-face-follower-webcam","status":"publish","type":"post","link":"http:\/\/www.nicassio.it\/daniele\/blog\/?p=87","title":{"rendered":"Raspberry PI powered Face Follower Webcam"},"content":{"rendered":"<p>Hi everybody, here&#8217;s my new Raspberry PI project: the face follower webcam!<\/p>\n<p>When I received my first raspberry, I understood it would have been very funny to play with real world things: so i tried to make the PI react with environment, and I played a lot with speech recognition, various kind of sensors and so on. But then I suddenly realized that the real funny thing would have been to make it <em>see<\/em>. I also understood that real-time processing a webcam input would be a tough task for a device with those tiny resources.<\/p>\n<p>Now i discovered that this <strong>is<\/strong>\u00a0a tough task, but the raspberry can achieve it, as long as we keep things simple.<\/p>\n<p>&nbsp;<\/p>\n<h3>OpenCV Libraries<\/h3>\n<p>As soon as I started playing with webcams, I decided to look for existing libraries to parse the webcam input and to extract some information from it. I soon came across the OpenCV libraries, beautiful libraries to process images from the webcam with a lot of features (which I didn&#8217;t fully explore yet), such as <strong>face detection<\/strong>.<\/p>\n<p>I started trying to understand if it was possible to make them work on the PI, and yes, someone had already done that, with python too, and it was as easy as a<\/p>\n<p><code>sudo apt-get install python-opencv<\/code><\/p>\n<p>After some tries, i found out that the pi was slow in processing the frames to detect faces, but only because it buffered the frames and I soon had a workaround for that.. and soon I was done: face detection on raspberry PI!<\/p>\n<p>All you need to try face detection on your own is the <em>python-opencv<\/em> package and the pattern xml file\u00a0<code>haarcascade_frontalface_alt.xml<\/code>, i guess you can find it easily in the Internet.<\/p>\n<p>I started with a script found <a href=\"http:\/\/www.lucaamore.com\/?p=638\">here<\/a>\u00a0and then I modified it for my purposes.<\/p>\n<h3>The Project<\/h3>\n<p>Eventually, I decided to build with my PI a motor driven webcam which could &#8220;follow&#8221; a face detected by opencv in the webcam stream. I disassembled <strong>a lot of things<\/strong> before finding a suitable motor but then I managed to connect it to the Raspberry GPIO (I had to play a little with electronics here, because i didn&#8217;t have a servo motor &#8212; if you need some information about this part, i&#8217;ll be happy to provide it, but i&#8217;m not so good with electronics thus the only thing I can say is that it works). Here&#8217;s a picture of the circuit:<br \/>\n<img decoding=\"async\" alt=\"The circuit to control the motor from the PI.\" src=\"https:\/\/lh5.googleusercontent.com\/-sFfqTFq0Wns\/USU40hKqEpI\/AAAAAAAAHnQ\/O76T9zdciKA\/s1105\/20130220_215212.jpg\" \/><\/p>\n<p>And here are some photos of the motor, to which I mounted an additional gear wheel.<\/p>\n<p><img decoding=\"async\" alt=\"The circuit to control the motor from the PI.\" src=\"https:\/\/lh4.googleusercontent.com\/--LS1L0TlO1I\/UTcl7ECxyZI\/AAAAAAAAHtw\/j3WaByZbNC0\/s1105\/20130306_121432.jpg\" \/><\/p>\n<p><img decoding=\"async\" alt=\"The circuit to control the motor from the PI.\" src=\"https:\/\/lh6.googleusercontent.com\/-xMy4qFYAo0Q\/UTcmEqL393I\/AAAAAAAAHt8\/dsL1fHeZlzc\/s1105\/20130306_121640.jpg\" \/><\/p>\n<p><img decoding=\"async\" alt=\"The circuit to control the motor from the PI.\" src=\"https:\/\/lh6.googleusercontent.com\/-72E-5WbjZGc\/UTcmZJvHLjI\/AAAAAAAAHuU\/ammaAUIzpm8\/s1105\/20130306_121743.jpg\" \/><\/p>\n<p>Once the motor worked, I attached it to a webcam which i plugged into the PI, and then I joined the previous linked script with some GPIO scripting to achieve the goal, here is the result:<br \/>\n<code><\/code><\/p>\n<pre>import RPi.GPIO as GPIO\nimport time,sys\nimport cv,os\nfrom datetime import datetime\nimport Image\n\n#GPIO pins i used\nOUT0 = 11\nOUT1 = 13\n\nout0 = False #!enable line: when it's false, the motor turns, when it's true, it stops \nout1 = False #!the direction the motor turns (clockwise or counter clockwise, it depends on the circuit you made)\n\ndef DetectFace(image, faceCascade):\n\n    min_size = (20,20)\n    image_scale = 2\n    haar_scale = 1.1\n    min_neighbors = 3\n    haar_flags = 0\n\n    # Allocate the temporary images\n    grayscale = cv.CreateImage((image.width, image.height), 8, 1)\n    smallImage = cv.CreateImage(\n            (\n                cv.Round(image.width \/ image_scale),\n                cv.Round(image.height \/ image_scale)\n            ), 8 ,1)\n\n    # Convert color input image to grayscale\n    cv.CvtColor(image, grayscale, cv.CV_BGR2GRAY)\n\n    # Scale input image for faster processing\n    cv.Resize(grayscale, smallImage, cv.CV_INTER_LINEAR)\n\n    # Equalize the histogram\n    cv.EqualizeHist(smallImage, smallImage)\n\n    # Detect the faces\n    faces = cv.HaarDetectObjects(\n            smallImage, faceCascade, cv.CreateMemStorage(0),\n            haar_scale, min_neighbors, haar_flags, min_size\n        )\n\n    # If faces are found\n    if faces:\n        #os.system(\"espeak -v it salve\")\n        for ((x, y, w, h), n) in faces:\n            return (image_scale*(x+w\/2.0)\/image.width, image_scale*(y+h\/2.0)\/image.height)\n            # the input to cv.HaarDetectObjects was resized, so scale the\n            # bounding box of each face and convert it to two CvPoints\n            pt1 = (int(x * image_scale), int(y * image_scale))\n            pt2 = (int((x + w) * image_scale), int((y + h) * image_scale))\n            cv.Rectangle(image, pt1, pt2, cv.RGB(255, 0, 0), 5, 8, 0)\n\n    return False\n\ndef now():\n    return str(datetime.now());\n\ndef init():\n    GPIO.setmode(GPIO.BOARD)\n    GPIO.setup(OUT0, GPIO.OUT)\n    GPIO.setup(OUT1, GPIO.OUT)\n    stop()\n\ndef stop(): #stops the motor and return when it's stopped\n    global out0\n    if out0 == False: #sleep only if it was moving\n        out0 = True\n        GPIO.output(OUT0,out0)\n        time.sleep(1)\n    else:        \n        out0 = True\n        GPIO.output(OUT0,out0)\n\ndef go(side, t): #turns the motor towards side, for t seconds\n    print \"Turning: side: \" +str(side) + \" time: \" +str(t)\n    global out0\n    global out1\n\n    out1 = side\n    GPIO.output(OUT1,out1)\n    out0 = False\n    GPIO.output(OUT0,out0)\n    time.sleep(t)\n    stop()\n\n#getting camera number arg\ncam = 0\nif len(sys.argv) == 2:\n    cam = sys.argv[1]\n\n#init motor\ninit()\n\ncapture = cv.CaptureFromCAM(int(cam))\n#i had to take the resolution down to 480x320 becuase the pi gave me errors with the default (higher) resolution of the webcam\ncv.SetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_WIDTH, 480)\ncv.SetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_HEIGHT, 320)\n#capture = cv.CaptureFromFile(\"test.avi\")\n\n#faceCascade = cv.Load(\"haarcascades\/haarcascade_frontalface_default.xml\")\n#faceCascade = cv.Load(\"haarcascades\/haarcascade_frontalface_alt2.xml\")\nfaceCascade = cv.Load(\"\/usr\/share\/opencv\/haarcascades\/haarcascade_frontalface_alt.xml\")\n#faceCascade = cv.Load(\"haarcascades\/haarcascade_frontalface_alt_tree.xml\")\n\nwhile (cv.WaitKey(500)==-1):\n    print now() + \": Capturing image..\"\n    for i in range(5):\n        cv.QueryFrame(capture) #this is the workaround to avoid frame buffering\n\n    img = cv.QueryFrame(capture)\n    hasFaces = DetectFace(img, faceCascade)\n    if hasFaces == False:\n        print now() + \": Face not detected.\"\n    else:\n        print now() + \": \" + str(hasFaces)\n        val = abs(0.5-hasFaces[0])\/0.5 * 0.3\n        #print \"moving for \" + str(val) + \" secs\"\n        go(hasFaces[0] &lt; 0.5, val)\n    #cv.ShowImage(\"face detection test\", image)<\/pre>\n<p>&nbsp;<\/p>\n<p>Of course I had to play with timing to make the webcam turn well, and the timings like everything strictly connected to the motor depends on your specific motor and\/or circuit.<\/p>\n<p>Here&#8217;s the video (in italian):<br \/>\n<iframe loading=\"lazy\" src=\"http:\/\/www.youtube.com\/embed\/MhOMlnXqHSQ\" height=\"315\" width=\"560\" allowfullscreen=\"\" frameborder=\"0\"><\/iframe><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hi everybody, here&#8217;s my new Raspberry PI project: the face follower webcam! When I received my first raspberry, I understood it would have been very funny to play with real world things: so i tried to make the PI react with environment, and I played a lot with speech recognition, various kind of sensors and &hellip; <a href=\"http:\/\/www.nicassio.it\/daniele\/blog\/?p=87\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Raspberry PI powered Face Follower Webcam<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[13,9,11],"tags":[],"_links":{"self":[{"href":"http:\/\/www.nicassio.it\/daniele\/blog\/index.php?rest_route=\/wp\/v2\/posts\/87"}],"collection":[{"href":"http:\/\/www.nicassio.it\/daniele\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.nicassio.it\/daniele\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.nicassio.it\/daniele\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.nicassio.it\/daniele\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=87"}],"version-history":[{"count":0,"href":"http:\/\/www.nicassio.it\/daniele\/blog\/index.php?rest_route=\/wp\/v2\/posts\/87\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.nicassio.it\/daniele\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=87"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.nicassio.it\/daniele\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=87"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.nicassio.it\/daniele\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=87"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}