2 Point Head Tracking,
1 not moving referenz point (at your nacklace)
1 moving point on a wooden bar on your helmet
60 frames per second, stable
benötigte Hardware:
– PS3 Eye Cam ( very high framerates )
– PC/Laptop ( DualCore 2×1,8GHz, 4GB, Win7 )
– USB Serial Light Adapter
– Arduino Leonardo
– PS4
– evtl. USB Hub
– LED infrared, 2 pieces
– Resistors 100 Ohm, 2 pieces
– Batteries/Accus, AA 1.2V , 4 pieces
– Batterie Case with switch, 2 pieces
– helmet or similar else
– wooden bar or similar else
– duct tape
Hardware Hacks:
– PS3 Eye Cam – IR blocking Filter entfernen und Tageslicht blocking filter (Diskette) einsetzen
Software:
– PC/Laptop – Windows Treiber der PS3 Eye Cam
– PC/Laptop – Python 2.7.3
– PC/Laptop – openCV
– PC/Laptop – pyserial
– PC/Laptop – Arduino IDE
– Arduino Leonardo – extra sketch
Following script holds everything together:
– reading camera stream,
– analysing, finding blobs,
– calculating distances,
– calculating movement between current versus previous frame
– sending relative movement over USBSerialLight Adapter to ArduinoLeonardo
import cv2 import time import numpy # python C:\python_scripts\opencv\cam_vid_60fps_binarized_flipped_blobs_SERIAL.py ########################################## # setting up serial communication import serial #import time import os """ C:\Users\XXXXX>python -m serial.tools.list_ports -a Usage: list_ports.py [options] [] list_ports.py: error: no such option: -a """ # C:\Python27\Lib\site-packages\serial\tools\list_ports.py <<< look for options.. discoverd -v ! write_to_path = "C:\\python_scripts\\py_serial\\actual_com_ports.txt" os.system("python -m serial.tools.list_ports -v > " + write_to_path) fr = open( write_to_path , "rb") frc = fr.read() fr.close() """ COM14 desc: Arduino Uno (COM14) hwid: USB VID:PID=2341:0001 SNR=55330343831351D03232 COM9 desc: Arduino Leonardo (COM9) hwid: USB VID:PID=2341:8036 2 ports found """ frc = str(frc).split("\n") com_dict = {} COM_id = "" for line in frc: #print line if line[0:3] == "COM": COM_id = line.strip() #print "COM_id: ",COM_id com_dict[COM_id] = "" if line.find("desc:") != -1: line2 = line line2 = line2.replace("desc: ","") line2 = line2.replace("("+COM_id+")","") line2 = line2.replace("\t","") line2 = line2.strip() #print "line2: >>",line2,"<<" com_dict[COM_id] = line2 arduino_leonardo_COMport = "" arduino_uno_COMport = "" for e in com_dict: print e, ": ", com_dict[e] if com_dict[e].lower().find("leonardo")!=-1: arduino_leonardo_COMport = e if com_dict[e].lower().find("uno")!=-1: arduino_uno_COMport = e print "arduino_uno_COMport: ",arduino_uno_COMport print "arduino_leonardo_COMport: ",arduino_leonardo_COMport if 1==1: #COMport = 14 #serial_port = COMport-1 #serial_port = "COM14" #serial_port = "COM11" #serial_port = arduino_leonardo_COMport serial_port = arduino_uno_COMport baudrate = 9600 connected = False """ ser = serial.Serial(port=serial_port, baudrate=baudrate, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_TWO, bytesize=serial.EIGHTBITS, timeout=0, xonxoff=0, rtscts=0) """ ser = serial.Serial( serial_port, baudrate) # open first serial port #ser.DtrEnable = "true"; print("initialising") time.sleep(2) # waiting the initialization... print ser.name # check which port was really used # EOF setting up serial ###################################################### # # python C:\python_scripts\opencv\cam_vid_60fps_binarized_flipped_blobs_SERIAL.py # http://docs.opencv.org/trunk/doc/py_tutorials/py_gui/py_video_display/py_video_display.html #cam = cv2.VideoCapture(0) # laptop eingebaute cam #cam = cv2.VideoCapture(1) # wenn direct am laptop cam = cv2.VideoCapture(1) print "cam.isOpened(): ",cam.isOpened() # - CV_CAP_PROP_FPS Frame rate. #print "FPS: ", cam.get( 10 ) # ? ret #print "x: ", cam.GetCaptureProperty( CV_CAP_PROP_FPS ) print "CV_CAP_PROP_FRAME_WIDTH: ", cam.get(3) print "CV_CAP_PROP_FRAME_HEIGHT: ", cam.get(4) print "CV_CAP_PROP_FPS: ", cam.get(5) # cam.get(propId) # propId integer 0 bis 18 if 1==11: size = (int(cv2.GetCaptureProperty(cam , cv2.CV_CAP_PROP_FRAME_WIDTH)), int(cv2.GetCaptureProperty(cam , cv2.CV_CAP_PROP_FRAME_HEIGHT))) print "size: ",size """ Property identifier. It can be one of the following: 0 CV_CAP_PROP_POS_MSEC Current position of the video file in milliseconds or video capture timestamp. 1 CV_CAP_PROP_POS_FRAMES 0-based index of the frame to be decoded/captured next. 2 CV_CAP_PROP_POS_AVI_RATIO Relative position of the video file: 0 - start of the film, 1 - end of the film. 3 CV_CAP_PROP_FRAME_WIDTH Width of the frames in the video stream. 4 CV_CAP_PROP_FRAME_HEIGHT Height of the frames in the video stream. 5 CV_CAP_PROP_FPS Frame rate. 6 CV_CAP_PROP_FOURCC 4-character code of codec. 7 CV_CAP_PROP_FRAME_COUNT Number of frames in the video file. 8 CV_CAP_PROP_FORMAT Format of the Mat objects returned by retrieve() . 9 CV_CAP_PROP_MODE Backend-specific value indicating the current capture mode. 10 CV_CAP_PROP_BRIGHTNESS Brightness of the image (only for cameras). 11 CV_CAP_PROP_CONTRAST Contrast of the image (only for cameras). 12 CV_CAP_PROP_SATURATION Saturation of the image (only for cameras). 13 CV_CAP_PROP_HUE Hue of the image (only for cameras). 14 CV_CAP_PROP_GAIN Gain of the image (only for cameras). 15 CV_CAP_PROP_EXPOSURE Exposure (only for cameras). 16 CV_CAP_PROP_CONVERT_RGB Boolean flags indicating whether images should be converted to RGB. 17 CV_CAP_PROP_WHITE_BALANCE Currently not supported 18 CV_CAP_PROP_RECTIFICATION Rectification flag for stereo cameras (note: only supported by DC1394 v 2.x backend currently) """ if 1==11: cam.set(3,320) # CV_CAP_PROP_FRAME_WIDTH cam.set(4,240) # CV_CAP_PROP_FRAME_HEIGHT cam.set(5,100) # fps print "cam get fps: ", cam.get(5) # fps # 'module' object has no attribute 'createBackgroundSubtractorMOG' #fgbg = cv2.BackgroundSubtractorMOG() #fgbg = cv2.BackgroundSubtractorMOG2() counter_frames = 0 start_time = time.time() fps = 0 # http://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html #kernel = numpy.ones((2,2),numpy.uint8) #kernel = numpy.ones((5,5),numpy.uint8) kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5)) #kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(5,5)) #kernel = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5)) #kernel = numpy.ones((10,10),numpy.uint8) cv2__THRESH_BINARY = cv2.THRESH_BINARY cv2__flip = cv2.flip cv2__morphologyEx = cv2.morphologyEx cv2__MORPH_OPEN = cv2.MORPH_OPEN cv2__MORPH_CLOSE = cv2.MORPH_CLOSE cv2__imshow = cv2.imshow cv2__waitKey = cv2.waitKey if 1==11: flags = [i for i in dir(cv2) if i.startswith('COLOR_')] for flag in flags: if flag.find("RGB")!=-1: print flag # COLOR_RGB2GRAY prev_frame_dist_x = 999999 #init value prev_frame_dist_y = 999999 # init value while(True): ret, frame = cam.read() #cv2.imshow('frame', frame) # Our operations on the frame come here frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #gray = cv2.cvtColor(frame, cv2.THRESH_BINARY) tresh = 253 #tresh = 200 ret,frame = cv2.threshold(frame,tresh,255,cv2__THRESH_BINARY ) frame = cv2__flip(frame, 1) # #print "frame.dtype: ",frame.dtype # http://opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/py_imgproc/py_morphological_ops/py_morphological_ops.html #frame = cv2.erode(frame,kernel,iterations = 1) frame = cv2.dilate(frame,kernel,iterations = 2) #frame = cv2.erode(frame,kernel,iterations = 3) #frame = cv2__morphologyEx(frame, cv2__MORPH_OPEN, kernel) # erosion followed by dilation #frame = cv2__morphologyEx(frame, cv2.MORPH_TOPHAT, kernel) #frame = cv2.morphologyEx(frame, cv2.MORPH_GRADIENT, kernel) # does outline cv2__imshow('frame', frame) #print "frame.dtype: ",frame.dtype #fgmask = fgbg.apply(frame) #cv2.imshow('frame',fgmask) #x = cv2.cvtColor(frame, cv2.CV_SHAPE_CROSS) #cv2.imshow('frame', x) #frame = cv2.convertTo(frame, CV_8U) # http://opencvpython.blogspot.de/2012/04/contour-features.html contours, _ = cv2.findContours(frame, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) contour_number = 0 contours_dict = {} for c in contours: rect = cv2.boundingRect(c) # rect[2] width # rect[3] height # sucht nur grosse blobs, alles kleiner 10x10 wird verworfen if rect[2] < 10 or rect[3] < 10: continue contour_number = contour_number+1 print "contour_number: ",contour_number #print cv2.contourArea(c) print rect # x,y width,height contours_dict[contour_number] = rect print if contour_number == 2: con1_x = contours_dict[1][0] con2_x = contours_dict[2][0] diff_con1_x_con2_x = con1_x - con2_x print "diff_con1_x_con2_x: ", diff_con1_x_con2_x con1_y = contours_dict[1][1] con2_y = contours_dict[2][1] diff_con1_y_con2_y = con1_y - con2_y print "diff_con1_y_con2_y: ", diff_con1_y_con2_y if diff_con1_x_con2_x != 999999: # 999999 is starting init val diff_prev_frame_x = diff_con1_x_con2_x - prev_frame_dist_x print "diff_prev_frame_x: ",diff_prev_frame_x # x negativ value = move to the right # x positiv value = move to the left diff_prev_frame_y = diff_con1_y_con2_y - prev_frame_dist_y # y negativ value = move to bottom # y positiv value = move to top print "diff_prev_frame_y: ",diff_prev_frame_y # send these values to arduino # diff_prev_frame_x # diff_prev_frame_y # or manipulate them multiple 2 or progressive diff_prev_frame_x = diff_prev_frame_x * -1 # ABSoluter wert, positiv.. d.h negativ number positiv machen ABS_x = diff_con1_x_con2_x if diff_con1_x_con2_x < 0: ABS_x = diff_con1_x_con2_x * -1 ABS_y = diff_con1_y_con2_y if diff_con1_y_con2_y < 0: ABS_y = diff_con1_y_con2_y * -1 print "ABS_x: ",ABS_x print "ABS_y: ",ABS_y if ABS_x < 100: diff_prev_frame_x = diff_prev_frame_x * 2 # 2 else: diff_prev_frame_x = diff_prev_frame_x * 4 # 240 center point y # 280 if ABS_y > 200 or ABS_y < 280: diff_prev_frame_y = diff_prev_frame_y * 4 # 4 else: diff_prev_frame_y = diff_prev_frame_y * 8 ser.write( str(diff_prev_frame_x) + "," + str(diff_prev_frame_y) + "\n" ) prev_frame_dist_x = diff_con1_x_con2_x prev_frame_dist_y = diff_con1_y_con2_y # now map the distances # to mouse movements # mouse movement # there is an posiblity to use absolute mouse via arduino, # http://forum.arduino.cc/index.php?topic=94140.0 # http://arduino.stackexchange.com/questions/3531/using-leonardo-and-getting-absolute-x-y-mouse-values # WE NEED DATA FROM PREV FRAME # CALC differences # these differences map to mouse moves #print rect.bx,rect.by #,c.bw,c.bh #frame = cv2.drawContours(frame, contours, -1, (0,255,0), 3) # to quit press key "q" if cv2__waitKey(1) & 0xFF == ord('q'): break if 1==1: counter_frames = counter_frames + 1 #print "counter_frames: ",counter_frames cur_time = time.time() #print "start_time: ",start_time #print "cur_time: ",cur_time diff_time = cur_time - start_time #print "diff_time: ",diff_time if diff_time > 5: # 5 sekunden fps = counter_frames / 5 # frames pro sekunde counter_frames = 0 start_time = time.time() print "fps: ",fps print "fps: ",fps cam.release() cv2.destroyAllWindows()
Sketch for Arduino Leonardo
/* Serial1-Mouse-Control The mouse movement is always relative. This sketch reads Serial1(RxTx) and uses incoming data to set the movement of the mouse (over Serial, Main USB Port). WARNING: When you use the Mouse.move() command, the Arduino takes over your mouse! Make sure you have control before you use the mouse commands. 2015-02 Ronny Gey */ #include // read string String mouse_x = ""; String mouse_y = ""; void setup() { // http://arduino.cc/en/pmwiki.php?n=Reference/Serial // http://arduino.cc/en/Main/ArduinoBoardLeonardo // Serial not needed, this is used by mouse control... // only for string tests, debugging //Serial.begin(9600); // first Serial Port, main USB port... OR sending to PS4 //while (!Serial) { // ; // wait for serial port to connect. Needed for Leonardo only //} // second Serial Port on Leonardo! pins Rx(Pin0)/Tx(Pin1) for receiving data from Laptop Serial1.begin(9600); while (!Serial1) { ; // wait for serial port to connect. Needed for Leonardo only } // initialize mouse control: Mouse.begin(); } void loop() { // read Serial_1, data coming from Laptop // send data only when you receive data: // read RxTx if (Serial1.available() > 0) { // getting x,y from Laptop over Serial1 // Kommaseparated value: x,y\n // -10,5 // 5,-10 // 10,10 mouse_x = Serial1.readStringUntil(','); mouse_y = Serial1.readStringUntil('\n'); //Serial1.print("I received X,Y: "); //Serial1.print(mouse_x); //Serial1.print(" : "); //Serial1.println(mouse_y); //Syntax: Mouse.move(xVal, yPos, wheel); Mouse.move(mouse_x.toInt(), mouse_y.toInt(), 0); // http://arduino.cc/en/Tutorial/StringToIntExample } // EOF serial1 available /* only for debugging purpose // send data only when you receive data: if (Serial.available() > 0) { // getting x,y from Laptop over Serial1 // comma separated value: x,y\n // -10,5 // 5,-10 // 10,10 mouse_x = Serial.readStringUntil(','); mouse_y = Serial.readStringUntil('\n'); Serial.print("I received X,Y: "); Serial.print(mouse_x); Serial.print(" : "); Serial.println(mouse_y); } // EOF serial available */ }
thats all