Amanda Bernsohn: @ITP | Images | Statement | CV | Projects

 



Final project revisited

The PinholePainter Camera now has a website detailing the process of its creation and where we intend to go with it.

posted by Amanda @ 12/17/2007 02:30:00 AM, ,




Final Final - Pinhole Painter Camera

I changed the concept for my final twice. In the end, because it ended up being more programming heavy than we had originally intended, I combined my PComp and ICM finals - working solely on the Pinhole Painter Camera with Chris Cerrito and Eduardo Lytton.

Our development Wiki can be found here:
http://pcomp.wetpaint.com/

The Pinhole Painter Camera is a lo-fi digital camera intended to combine aspects of digital and film photography. Our team was interested in the affordability and accessibility of digital imaging, but also think that it brings up some interesting concerns.

Our goals were to:
Learn more about programming
Learn more about sensors and electricity
Understand the basic ways in which cameras (and ccds) work
Bring serendipity and the unexpected to digital photography
Play with the ways in which users can become more involved in picture taking
Add a time element to digital picture taking - in early photography, taking a picture took a long time. Our thinking was that this commitment to the time that photographing took informed the experience of image capture.

Our process began with a lot of discussion around photography and the aspects of it that we appreciate in both film and digital. We agreed that we appreciate the unknowing of film photography, as well as the time it takes to craft an image and to receive the endeavor's results.

We approached the process from a number of different ways in the beginning. We were unclear about whether we would hack an existing digital camera, create a camera obscura and place a digital camera inside of it, or hack a scanner. In the end we agreed to create our own rudimentary ccd out of a grid of 16 (4x4) photocells. The image plane would be projected through the pinhole, onto the image plane (as an inverted image) and be recorded as light values (0-255) by an array of photo cells. Instead of having a grid of 1000 or so photo cells, we decided to use only sixteen, and to allow the user to have a part in moving that sensor around to capture the full image in the way that they want.

As the project stands, it leaves room for adding color values to the light readings by switching color filters in and out (R,G,B) and composing the images out of those values using processing.

The programming in this project was three-fold really. After crafting the array of photocells, we needed to write a program in Arduino that would send values to processing. The program is constantly sending sets of 18 ASCII values to processing. They are delineated twice, by an asterisk separating the photocell values from the IR readings, and then by commas separating the photocell values themselves. The Arduino program takes five readings for the IR values and prints the average in DEC, to be read by processing.

The Arduino code is below:

//ARDUINO CODE -- ASCII SENDER
int val[16];
int analog1[5];
int h, j;
long average1;
int analog2[5];
int p, q;
long average2;

int a= 3; // SELECT PIN A0 GOING INTO PIN 2
int b= 4; // SELECT PIN A1 GOING INTO PIN 3
int c= 5; // SELECT PIN A2 GOING INTO PIN 4
int d= 6; // SELECT PIN A3 GOING INTO PIN 5
//we are using three analog pins
int analog0 = 0;
//int analog1 = 0;
//int analog2 = 0;

void setup() {
Serial.begin(9600);
pinMode(analog0, INPUT);
pinMode(a,OUTPUT);
pinMode(b,OUTPUT);
pinMode(c,OUTPUT);
pinMode(d,OUTPUT);
//pinMode(analog1, INPUT);
//pinMode(analog2, INPUT);
}

void loop() {

for (int i = 0; i < 16; i++) {

// determine the four address pin values from i:
// mask off bit 0:
int pinOne = 1 & i;
// shift value 1 bit to the right, and mask all but bit 0:
int pinTwo = 1 & (i >> 1) ;
// shift value 2 bits to the right, and mask all but bit 0:
int pinThree = 1 & (i >> 2);
// shift value 3 bits to the right, and mask all but bit 0:
int pinFour = 1 & (i >> 3);

// set the address pins:
digitalWrite(a,pinOne);
digitalWrite(b,pinTwo);
digitalWrite(c,pinThree);
digitalWrite(d,pinFour);

// read the analog input and store it in the value array:
val[i] = analogRead(analog0);

//val[i] = i;
//Serial.print(val[i], DEC);
if (i < 15) {
//Serial.print(",");
//For IR 1

analog1[(h++ %5)] = analogRead(1);
average1 = 0;
for (j =0; j<5; j ++){
// add samples
average1 += analog1[j];
}
average1 = average1 / 5;
//For IR 2
analog2[(p++ %5)] = analogRead(2);
average2 = 0;
for (q =0; q<5; q ++){
// add samples
average2 += analog2[q];
}
average2 = average2 / 5;
//End Averaging
}
}
Serial.print("*");
Serial.print(average1, DEC);
Serial.print(",");
Serial.println(average2, DEC);
}

--------------------------------------------
The Processing program had several incarnations throughout the project. The first program was coded using hard numbers and calling each separate place in the array. The x & y positions of the large "pixels" (which were really 20x20) were coded using sensorValue1 and sensorValue2 as x and y respectively, and then offsetting each subsequent value by 20 pixels to create the grid effect. The results of this program were fine and the coding was simple, but far from dynamic. If we had wanted to re-scale the image or change the pixel size, we would have needed to change all of these values each time. It was important and helpful to begin in this way because it helped us to identify each separate cell and to know exactly when we were calling the value, dividing it, assigning it a variable, etc.. The later iteration of the program uses object orientation and arrays to achieve a similar visual effect, while being more dynamic and scalable.

The Processing code as it stands now is below (still in progress):
---
The A-Mazing Pinhole Painter Camera Processing Code ! ! ! !
Project by Amanda Bernsohn, Chris Cerrito, Eduardo Lytton
Fall 2007 ITP

// 3 arrays: PicPixArray (the big picture), cursorArray (our photosensor cursor), and vals
// range of values coming in from our photo sensors


import processing.serial.*;
Serial myPort;
//Declare your main string5
String inString;
boolean gotFirstString = false;



int cols = 40; //# of columns
int rows = 40; //# of rows
int videoScale = width/cols; //resolution of the overall picture grid - how wide a "bigPixel" is
int[] vals = new int[16]; //Arduino array of gray values coming in serially
int irOne; //IR sensor value for X
int irTwo; //IR sensor value for Y
int []pots; //the string leftover, what will contain IR x,y values
Cell[][] PicPixArray; //2D array of objects -- the overall grid of our image plane
Cell[][] cursorArray; //2D array of objects -- the "cursor" array
int gridScale; //resolution of the overall picture grid - how wide a "bigPixel" is
int gridCols = 4; //Number of columns in the grid
int gridRows = 4; //Number of rows in the grid
//int grayGridMasterValue = 5; //for NON-SERIAL COMMUN



//PImage img;

void setup() {
size (800, 800);
background(255);
println(Serial.list());
myPort = new Serial(this, Serial.list()[0], 9600);
//set a buffer - serialEvent will not happen until \n (new line feed)
//so serialEvent will always start on the first of our values
myPort.bufferUntil('\n'); // SERIAL COMMUNICATION

gridScale = width/cols;
cursorArray = new Cell[gridCols][gridRows];
//this is where we initialize the cursorArray a.k.a "cursor" or "photo sensors array"
for (int i = 0; i < gridCols; i++) {
for (int j = 0; j < gridRows; j++) {
//intialize each object
//int locLinearArray = i+j*4;
cursorArray [i][j] = new Cell(i*gridScale,j*gridScale,gridScale,gridScale,-1);//values[locLinearArray]);
//cursorArray[i][j].getArduinoValues();
//print(values[i] + "\t");
}
}

PicPixArray = new Cell[cols][rows];
//this is where we initialize the PicPixArray a.k.a "image plane" or "the big picture"

for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
int x = i*gridScale; //x position
int y = j*gridScale; //y position
int tempbigPixelGray = 150;
PicPixArray [i][j] = new Cell(x, y, gridScale, gridScale, tempbigPixelGray);
//PicPixArray [i][j].fillinPicPix();


}

//img = createImage(width,height,RGB);
}


//failed attempts / other ideas below
// PicPixArray[x][y] = values[(x*4) + y];
//PicPixArray [values [16] / 10 + x][values [17] / 10 + y] = cursorArray [x][y];


}

void serialEvent(Serial myPort) {
String myString = myPort.readStringUntil('\n');

if(myString != null) {
myString = trim(myString);
//println(myString);
//split your longer string, using the asterisk as a delimiter, creating two smaller strings
String[] strings = split(myString,'*');

if (strings.length > 1) {
//make an array of vals by splitting the first string wherever there is a commma
//you know it's the first string cause it's called strings[0]
vals = int(split(strings[0], ','));

//do the same things for the pots, split the second string up
//wherever there is a comma
pots = int(split(strings[1], ','));
//irOne is a variable, whose value is going to be the first value from the pots array
irOne = pots[0];
//irTwo is a variable, whose value is going to be the second value from the pots array
irTwo = pots[1];
//same thing here with val0, etc... they will eventually be val0-val15, which are variable
println(vals + "\t");

for(int i = 0; i < vals.length; i++){
vals[i] = vals[i]/4;
print(vals[i] + "\t");
}
println();

println("IRS");
println(irOne + "\t" + irTwo);
println();
//then print whichever cell values you would like to look at
println(vals[0] + "\t" + vals[1]);
}
}
}

//void keyPressed() {






void mousePressed() {
// img.save("test.jpg");

}

void draw() {
// background (0);
//irOne = mouseX; //change to include IR sensor value array value [16] - x
//irTwo = mouseY; //change to include IR sensor value array value [17] - y

//img.loadPixels();
noStroke();

for (int i = 0; i < gridCols; i++) {
for (int j = 0; j < gridRows; j++) {
int locLinearArray = i+j*4;
cursorArray [i][j].setArduinoValue(vals[locLinearArray]);

//cursorArray [i][j].setArduinoValue(150);
cursorArray [i][j].setPixel();
}
}
//img.updatePixels();




//image(img,0,0);
for (int i = 0; i < gridCols; i++) {
for (int j = 0; j < gridRows; j++) {
cursorArray [i][j].displayCursor();
}
}

/* for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
PicPixArray [i][j].display();
}
}*/

/*
for (int i = 0; i < gridCols; i++) {
for (int j = 0; j < gridRows; j++) {
int locLinearArrayCursor = i+j*4;
vals [i] = ((vals[locLinearArrayCursor])/4);
//this is the line that needs to be change to receive arduino bytes!
//vals[i] = int(random(255));//to test NON-SERIAL COMMUNICATION
// vals[i] = int(random(255)); //SERIAL COMMUNICATION
}
}*/
}

========
THE "CURSOR CELL" TAB


//a cell object
class Cell {
float xcell,ycell; //x,y location
float w,h; //width and height
int grayGridValue; // gray value for fill for each cursor Cell


//cell constructor
Cell(float tempxcell, float tempycell, float tempW, float tempH, int tempGrayGridValue) {
xcell = tempxcell;
ycell = tempycell;
w = tempW;
h = tempH;
grayGridValue = tempGrayGridValue;
}


void setArduinoValue(int temp) {
grayGridValue = temp;
}


void setPixel() {
//rect(irOne + xcell, irTwo + ycell,gridScale,gridScale); //replace mouseX with irOne value

/*int x = irOne + int(xcell)/gridScale;
int y = irTwo + int(ycell)/gridScale;

int loc = x+y*width; // NOT CORRECT
img.pixels[loc] = color(grayGridValue);*/


PicPixArray[int(xcell+irOne)/gridScale][int(ycell+irTwo)/gridScale].setArduinoValue(grayGridValue);

}


//void fillInPicPix() {
// //PicPixArray[i][j] = PicPixArray[irOne / videoScale + x][irTwo/ videoScale + y];
//


void displayCursor () {
noStroke(); //color and move to mouse position
color c = grayGridValue;
fill(c);
rect(irOne + xcell, irTwo + ycell,gridScale,gridScale); //replace mouseX with irOne value
}

void display () {
noStroke(); //color and move to mouse position
color c = grayGridValue;
fill(c);
rect(xcell,ycell,gridScale,gridScale); //replace mouseX with irOne value
}
}

Several images documenting our process and our results can be found below:
One of Chris Cerrito's early drawings of the concept.


We started with five photocells on the board, using analog ins, but no multiplexer.


Building the photocell array (something we ended up doing in its entirety twice, and are still tweaking)





We needed to use a multiplexer to increase the number of analog ins on the Arduino. we used one, which gave us an additional 15 analog ins, and only used one analog pin (and four digital). The code for the MUX (which involved bitshifting) ended up taking several days to work out.


We tried a number of different sensors to track the x and y positions of the cells inside the camera body. We ended up using IR sensors, one of which is shown below.



An early screenshot from the processing program, using ellipses with brightness values corresponding to available light. This is unfocused, ambiant light and the values are quite dark.

posted by Amanda @ 12/11/2007 11:29:00 PM, ,




Final concept - revisited

As expected, I changed the concept of my final project entirely. I am working on a Processing program that will 'map' where I and one other person are and display our intersections in a meaningful way with aesthetics in mind. For me this project is a challenge, in that it combines a number of things that I have been trying to get down all semester. At this point I have false GPS data in two local txt files. They are being pulled in as strings and then the lat is reading as an X value, and the long as a Y. I put 'mapping' in quotes, because the lat and long are not being used in any meaningful way independently. Instead, it is merely the relative location that I am concerned with. I am also interested in having objects that run in the background to convey the sense of chaos/movement of the city more generally. At this point I have a sense of which direction I am going in, as well as some of the basic classes set up. The challenge right now is scrolling through the values and connecting them.

posted by Amanda @ 12/03/2007 09:19:00 PM, ,




Final proposal

**under development**

For the ICM final project, I am proposing a dance looping application. I'd like the user to be able to build dance routines on screen using a variety of loops (comprised of gifs in various sequences?). It would be nice if the user could record their creations and view them again at a later time.

I would like to approach the project with a series of steps, similar to the way that the midterm was broken down. The first (and probably easiest) will be to choose a couple of moves that can be used

Expected limitations:
I would think that the primary limitation will be keeping file size low enough to run without complications. I've already run into some problems with running out of Processing memory.

For moves, I'll be starting off pieces of the crank [that]. Crank that Superman, Spiderman, Peter Pan, Lion King, etc.. I'm using this dance because footage of it is plentiful (at least 40,000 youTube videos at the moment) and they are pretty modular dances...i.e. one can have 8 bars of one move, then 8 bars of another,in pretty much any order and they fit together easily. I won't be using any audio.

Below are some moves that
These are just examples of movements that will be abstracted in processing. I don't intend to use these video clips.


Lion King:


Soulja Boy:


Peter Pan:


I've ripped a number of videos and vectorized them to get a sense of the movements.. the next step would be trace them to economize for memory limitations. I would like to abstract the image as much away from the original as possible, while keeping a sense of a body in motion.

video

posted by Amanda @ 11/06/2007 10:56:00 PM, ,




Midterm Project - The Versifier

It was initially my intention to create a project involving visualizing poetry parsed from XML on the web. The part I chose to implement was the actual motion that I wanted in the end. For the purposes of this first phase, the lines are being pulled from three text documents that live locally on my computer. The line motion is created by three potentiometers sending bytes to processing serially. Each text file contains one poem and they are not related. The values received from the Arduino are subtracted from the X position of each line respectively, creating a leftward motion across the screen. When the line leaves the screen, the poem is incremented by one line and a new one enters from the right. The user can use the potentiometers to alter the speed.

My project evolved into being a tool that a poet, or any artist really, can use to combat a block in creativity. Though not intended to provide lines to a writer, the program can juxtapose existing lines from poems in such a way that one's creativity may be triggered. In essence, the versifier is intended to be a writer's block buster. Once off the screen, the lines cannot be retrieved and brought back on screen (there is no movement toward the right on the X-axis). This is intentional as the versifier is meant to encourage experimentation and to discourage attachment to specific lines or groups of lines, especially since they have been written by someone else already.

The physical element is meant to invoke a blank sheet of paper, as is the screen, which is bare except for the three lines in play.

Code below:





The program running:

video

/*
Part one if ICM Midterm project for Dan Shiffman's class at ITP
For purposes of prototype, thanks to mark doty, max garland and
peter gizzi for words -- poem text used without permission
Text incrementing when text is less than negative xposition, from Shiffman's example
Arduino code adapted from Tom Igoe's serial lab using the punctuation method
*/

import processing.serial.*;

String[] headlines;
String[] tokens;
String[] headlines2;
String[] tokens2;
String[] headlines3;
String[] tokens3;
int index = 0;
int index2 = 0;
int index3 = 0;
PFont f;
float x = 300;
float t = 300;
float s = 300;
float moveLine1;
float moveLine2;
float moveLine3;

Serial myPort;
int[] sensorValues = new int[4];
int i = 0;
boolean firstContact = false;

void setup() {
size(600,600);
frameRate(30);
headlines = loadStrings("data.txt");
tokens = split(headlines[0], ";");
headlines2 = loadStrings("data2.txt");
tokens2 = split(headlines2[0], ";");
headlines3 = loadStrings("data3.txt");
tokens3 = split(headlines3[0], ";");
smooth();
f = loadFont("ArialMT-16.vlw");
println(Serial.list());
String portName = Serial.list()[2];
myPort = new Serial(this, portName, 9600);
myPort.write("A");
println("A");
}

void serialEvent(Serial myPort) {
int inByte = myPort.read();
if (inByte != 20) {
if (firstContact == true) {
sensorValues[i] = inByte;
}
i++;
}
else {
println (i);
i=0;
firstContact = true;
}
}
void draw() {
background(255);
fill(0);
textFont(f,16);
textAlign(CENTER);

text(tokens[index],x,270);
x = x - sensorValues[0];
text(tokens2[index2],t,height/2);
t = t - sensorValues[1];
text(tokens3[index3],s,330);
s = s - sensorValues[2];
float moveLine1 = textWidth(tokens[index]);
if (moveLine1<-x) {
x = width;
index = (index + 1) % tokens.length;
}
float moveLine2 = textWidth(tokens2[index2]);
if (moveLine2<-t) {
t = width;
index2 = (index2 + 1) % tokens2.length;
}
float moveLine3 = textWidth(tokens3[index3]);
if (moveLine3<-s) {
s = width;
index3= index3 + 1 % tokens3.length;
}
}

/*
int sensor[4];
void setup() {
Serial.begin(9600);
}
void loop() {
// for loop to count from 0 to 2:
for (int i = 0; i < 4; i++) {
// read one of the sensors, divide by 4, put into the array:
sensor[i] = analogRead(i)/50;
// if the value is 255, truncate it so we can use 255
// as a unique value to punctuate the sentence:
if (sensor[i] == 20) {
sensor[i] = 19;
}
// print the current sensor value:
Serial.print(sensor[i], BYTE);
}
// After the sensor values, print 20 (approximately 1023/50);
Serial.print(20, BYTE);
delay(10);
}
*/

posted by Amanda @ 10/22/2007 09:14:00 PM, ,


In week three of ICM now - here's my homework for the class. I think it's a slight improvement over last week.. We've started working with variables and mouse interaction a little bit. Today we'll be learning about functions and object oriented programming. Will post some notes later.

posted by Amanda @ 9/19/2007 09:38:00 AM, ,


For ICM this week, we had a couple of assignments. We had to, of course, download and install processing. We were asked to create a drawing, using processing, using size(), arc(), curve(), ellipse(), line(), point(), quad(), rect(), triangle(), and basic color functions: background(), colorMode(), fill(), noFill(), noStroke(), stroke(). Additionally, we were to read the first three chapters of the textbook and to read Hackers and Painters by Paul Graham. All of this is new to me. I managed to get an (albeit ridiculously simple) thing up on my screen. You can see that here (look for source code down below). I am surprised to say that I got very into it, to the point where it was 3am before i finally decided to turn off my machine. Reading the first three chapters of Daniel Shiffman's book was kind of a tease. I had already done my silly Hello World drawing, but the reading showed me just how much processing can do. I can't wait to get into it - or at least be less intimidated by it.

Reading hackers and painters was interesting. Truthfully I've never thought about hackers as 'maker', or truly about hackers much at all. I think that the idea that Graham puts forth about hackers and painters being similar is notable particularly as i enter an environment like ITP where this similarity seems to be the thread that runs through the whole department. I've only been around for a couple of days so far, but at least that's what i've found so far. It seems altogether appropriate that processing programs are called sketches.

posted by Amanda @ 9/08/2007 07:07:00 PM, ,