// eyeCapture3XML.pde ****** // Initially Coded by Sinan Ascioglu - 2007 // This work is licensed under a Creative Commons License // Attribution-ShareAlike 2.0 // http://creativecommons.org/licenses/by-sa/2.0/ // The further use of the code with/without improvements should include the name of the previous contributers. // This application creates an xml output of the eye tracking while a user is looking at the screen. // It can be fed with a prerecorded eye video, or live video connected to the system. // in this code, a movie file is also shown to see where the person is looking while watching the movie. import processing.opengl.*; import processing.video.*; import proxml.*; boolean liveCam = true; //if the input is live cam input. Otherwise, recorded eye video should be specified in eyeName variable. Movie eye1Movie; String movieName = "bjorkLargeJpeg.mov"; //name of the movie to be displayed //----------------------important variables : while application running, mouse and up/down keys can be used to reset these values---- float eyeCal = 1.8; //size of the eye: should be around the same size of the eye float pointCalX = 20.2; //wideness scale: this is the ratio where the movement of the eye is scaled to the level of the screen. int eye1posX=338; // also can be set with mouse click int eye1posY=286; // also can be set with mouse click String eyeName = "caleb2.mov"; //if the eye movement is prerecorded. below in the code, it can be changed to a camera input instead of a movie. //lucas: 1.8 10 371 185 //sinan: 1.8 10 392 161 //caleb: 1.8 9.6 369 202 //--------------------------------- int XMLupdateRate = 15; int blackThreshold = 20; //the brightness check threshold int startRec = 1; //start recording XML right away, otherwise wait for 'r': start recording by pressing 'r', save the xml file by pressing 's' XMLElement eyePathXML; XMLInOut xmlInOut; Capture cam; boolean initialized = true; boolean eye1set; boolean eye2set; int showCam = 1; int eye2posX; int eye2posY; int eyeH; int eyeW; //int frameCount=0; Movie rearWindow; MovieMaker mm; // Declare MovieMaker object int[] averageFilter = { //this filter can be extended by adding more 0s below. 0,0,0,0,0}; int averageIndex=0; //font settings PFont font; PFont fontA; // Set the font and its size (in units of pixels) void setup() { size(720, 480, P3D); frameRate(30); //cam = new Capture(this, 320, 240); if (liveCam){ //if the input is live cam, setup the cam cam = new Capture(this, 320, 240); } else{ eye1Movie = new Movie(this, eyeName); eye1Movie.loop(); } rearWindow = new Movie(this, movieName); //uncompressed is cut at 1:40:00 rearWindow.loop(); //background(0,0,0,20); //mm = new MovieMaker(this, width, height, "capture.mov", 30, MovieMaker.H264, MovieMaker.LOSSLESS); //initialized = false; eye1set = true; eye2set = true; //eye2* is not used in the code anymore. kept for just in case. eye2posX=0; eye2posY=0; eyeH = int(35*eyeCal); eyeW = int(80*eyeCal); fill(255,0,0,20); stroke(255, 0, 0,255); //font settings fontA = loadFont("Swiss721BT-RomanCondensed-48.vlw"); textFont(fontA, 25); //-----------------------------------------------------------XML stuff xmlInOut = new XMLInOut(this); eyePathXML = new XMLElement("eyePath"); } void draw() { //background(0,0,0); fill(0,0,0); rect(0,0,width,height); noFill(); image(rearWindow,width/2-720/2, height/2-480/2); //display the movie if(showCam==1){ //display the eye video if (liveCam){ updateCam(); if (showCam==1) image(cam, width/2-160, height/2-120); //initialized = true; } else{ image(eye1Movie, width/2-160, height/2-120); } } if (initialized != true){ //if not yet initialized, wait. if (eye1set){ ellipse(eye1posX, eye1posY, eyeW, eyeH); //draw first eye } if (eye2set){ //ellipse(eye2posX, eye2posY, eyeW, eyeH); //draw second eye } } else //if initialized, run { stroke(255, 0, 0); if(showCam==1){ ellipse(eye1posX, eye1posY, eyeW, eyeH); ellipse(eye1posX,eye1posY, 2,2); //ellipse(eye2posX, eye2posY, eyeW, eyeH); line(eye1posX-eyeW/2, eye1posY, eye1posX+eyeW/2, eye1posY); } int lookX =-int(blackPixelPosition(eye1posX,eye1posY)*pointCalX); //this returns the exact position of the eye looking on the screen lookX = width/2 - getAverage(lookX); //inversing in the case the cam is inverted int lookY= eye1posY; //to be changed with actual Y position stroke(255, 0, 0,200); ellipse(lookX, lookY, 30,20); //draw one point //create XML position for the eye pos if (frameCount%XMLupdateRate==0 && startRec==1){ int cTime = (int)(rearWindow.time()*1000); println(cTime); addPositionXML(lookX,lookY,cTime); } } printStuff(); //mm.addFrame(); } // -------XML FUNCTIONS----------s void addPositionXML(int x, int y, int z){ XMLElement position = new XMLElement("position"); //create position eyePathXML.addChild(position); //add it to the parent position.addAttribute("x",x); //add attributes position.addAttribute("y",y); position.addAttribute("z",z); print("ps "); } void saveXML(){ println(eyePathXML); xmlInOut.saveElement(eyePathXML,"eyePathXML.xml"); println("xml saved"); } // ------------------------------ int getAverage(int x){ averageFilter[averageIndex]=x; int average=0; averageIndex++; if(averageIndex == averageFilter.length){ averageIndex=0; } for (int i=0; i