For this week's project, Shalini, Andrea and I decided to make a program to help children with disabilities work on keeping their heads elevated. Our goal was to make something fun that would encourage kids to hold their heads up for longer periods of time. We decided to use a cartoon clip that will play when the head is elevated to a certain point, and that will pause when it goes below that point.
Discussion topics:
What kinds of things can make this fun (or at least slightly more pleasant) for kids?
What kinds of things are appropriate for different populations - i.e., what can the kids wear for purposes of tracking motion? Should we use hats/glasses? Kids with sensory issues might find this irritating.. some kids may not though. Ultimately we decided to have different options, so the therapist and/or child can decide what they want to use.
Technical issues:
Our major issue was around audio playback. We wanted to have audio be a motivator and attention grabber. In processing, we were running a quicktime clip of Dora, but the audio was behaving erratically. We weren't able to control the audio with movie.play and movie.pause. As a result, we needed to export the audio separately as an *aiff and control it using the Ess library. As a result, the synching is a tiny bit off, but we can probably fix that.
The code can be found below.
/*Assistive Technology @ ITP
project - Shalini A M D'Souza / Andrea Dulko / Amanda Bernsohn - thank you to John Schimmel for code help */
//sound
import krister.Ess.*;
//bring in webcam
import processing.video.*;
//capture video feed
Capture video;
//name QT movie
//movie object
Movie myMovie;
//audio object
AudioChannel myChannel;
//variables
float targetRed,targetGreen, targetBlue;
int calibratedTop, calibratedBottom;
int yVal;
//x,y settings for target rectangle and dimensions
int targetX = 0;
int targetY = 0;
int targetWidth = 640;
int targetHeight;
color targetColor;
// don't really need this if not displaying target -- delete later
color defaultColor = color(255,255,255);
color winningColor = color(255,0,0);
Rectangle theRect;
boolean showVideo = true;
boolean showMovie = false;
void setup() {
size(1280, 480);
myMovie = new Movie(this, "dora_no.mov"); // bringing in QT
myMovie.loop();
video = new Capture(this, 640, 480, 12); // show capture feed
//default values for calibration
calibratedTop = targetHeight;
calibratedBottom = height;
println("--------------------------------------------");
println("To calibrate high point, raise head to maximum height, press the 'T' key");
//Bring in sound
// start up Ess
Ess.start(this);
// load "cell.aif" into a new AudioChannel
myChannel=new AudioChannel("dora.aif");
}
void captureEvent(Capture camera) {
camera.read();
horizontalFlip(); // flipping
if (showVideo) {
image(video, 0, 0); //show image on screen
}
else {
background(0);
}
}
void draw() {
image(myMovie, 640, 0);
Point bestPoint = trackColor(); //track the target color
fill(255,0,0);
theRect = new Rectangle(targetX, targetY, targetWidth, targetHeight);
noStroke();
yVal=bestPoint.y;
float adjustedY = map(yVal, calibratedTop, calibratedBottom, 0, height);
ellipse(bestPoint.x, bestPoint.y, 10, 10);
// println(bestPoint.y);
displayTarget();
checkTarget(bestPoint);
}
void keyReleased() {
int T = 84; //ascii code for the letter T (top)
int B = 66; // code for the letter B (bottom)
if (keyCode == T) {
calibratedTop = yVal;
targetHeight = calibratedTop;
println(targetHeight);
println("Calibration update : top is " + calibratedTop);
}
else if (keyCode == B) {
calibratedBottom = yVal;
println("Calibration update : bottom is " + calibratedBottom);
}
}
Point trackColor() {
float worldRecord = 1000.0; //intialize the worldrecord
int xFound = 0; // initialize the location of the red tracking ball
int yFound = 0;
for(int row=0; row
//find pixel in linear array using formula: pos = row*rowWidth+column
color pix = video.pixels[row*video.width+col];
//find the difference
int diff = (int) dist(targetRed,targetGreen,targetBlue, red(pix), green(pix), blue(pix));
if (diff< worldRecord){ // if this is closest to our target color
worldRecord = diff;
yFound = row; //mark the spot for drawing it later
xFound = col;
}
}
}
Point tempPoint = new Point(xFound, yFound);
return tempPoint;
}
void displayTarget() {
//fill(targetColor); //white
stroke(0); //dark
strokeWeight(2); //border
// rect(targetX, targetY, targetWidth, targetHeight);
}
void movieEvent(Movie m) {
if (showMovie == true){
m.read();
}
// else if (showMovie == false){
// myMovie.pause();
// }
}
/*void movieEvent(Movie m) {
if (showMovie == true){
m.read();
}
}
*/
//show movie boolean
void checkTarget(Point theObject) {
//set color of targe rect to red if point is contained within
if (theRect.contains(theObject)) {
showMovie = true;
myChannel.play();
//targetColor = winningColor; //RED
}
else {
showMovie = false;
myChannel.pause();
//super.stop();
}
}
void mousePressed(){
//allow the target color to be changed
color pix = video.pixels[mouseY*video.width+mouseX];
targetRed = red(pix); //get the color of the pixel they clicked on
targetGreen = green(pix);
targetBlue = blue(pix);
}
void horizontalFlip() {
//create an array to hold the newly arranged pixels
color[] newImage = new color[video.height*video.width];
//loop through the pixels of the video
for(int row=1; row
newImage[row*video.width+(width-col-1)] = video.pixels[row*video.width+col]; // the newImage array reorganizes the pixels so left is on the right and the right is on the left
}
}
video.pixels = newImage; //set video to the newImage array. replaces the old image with the new flipped version
}
void keyPressed(){
if (key == 'v' || key == 'V') {
showVideo = !showVideo;
}
}
posted by Amanda @ 2:21 PM,