8bit klubben // forum

about 55% of all CPUs sold in the world are 8-bit microcontrollers or microprocessors.
It is currently Thu Sep 09, 2010 10:48 am

All times are UTC + 1 hour [ DST ]




Post new topic Reply to topic  [ 3 posts ] 
Author Message
 Post subject: Halfmachine displays til Video?
PostPosted: Sat May 03, 2008 8:49 pm 
Offline
User avatar

Joined: Mon Jan 07, 2008 4:26 am
Posts: 11
Location: Nørrebronx
Hej alle:
Den nuværende konfiguration af Halfmachine's LED klodser er en stack på 3 x 4, forbundet i kolonner - det nye daisy chain system virker åbenbart fint!

http://www.flickr.com/photos/25649704@N06/2459834777/

Jeg har snakket med Jabstarr og Nicolas@HM om muligheden for at vise video på LED stacken ved at bruge Arduino som buffer/serial interface, med Processing til at koge video ned til noget der bruges. Her følger en stump rodet Processing kode der indeholder en primitiv simuleret visning af hvordan det kunne se ud, med en Floyd-Steinberg skravering der sætter lidt liv i pixelsne og tillader gråtoner i nogen forstand (bliver bedre med frameraten!). Kodens setup er lidt ambitiøst sat op til at køre med 4x6 klodser, men det er let at ændre disse parametre.


/* Video To Arduino To Half Machine LED bricks */
/* Hack and slash crap code by Sonny Windstrup */
/* For 8-bit Klubben and M/S Half Machine */


import processing.opengl.*;
import processing.serial.*;
import processing.video.*;

Capture video;
DiodeStack stack;
int scrWidth = screen.width;
int scrHeight = screen.height;
int pixWidth;
int pixHeight;
int pixRotSize;
int pixRows;
int pixCols;
float greenRot;
int numPixels;
int[] scanOrder;
int[][] targetPixMap;
int[][] actualPixMap;

void TransmitPixels()
{
// pseudocode: serial transmit frame sync pulse (0xff)
for (int i=0;i<numPixels;i++)
{
int coord = scanOrder[i]; // reads pixel coordinate from scan order table
int px = coord & 0xff;e
int py = (coord >> 8) & 0xff;
int value = actualPixMap[px][py];
int numeric = (int) map(value,0,256,0,3);
// should produce 0,1,2 (0 = black, 1 = 50%luma (red), 2 = 100% luma (red+green)
if (numeric == 2) numeric = 3; // bit pattern patch
// pseudocode: transmit numeric as byte (bit 0 : red; bit 1: green)
}
}

public void draw()
{
GrabPixels();
DitherPixels();
PaintPixels(); // omit in case of speed problems ?
TransmitPixels();
}

void setup()
{
println("SETUP");
size(scrWidth, scrHeight, OPENGL);
stack = new DiodeStack(4,6);
scanOrder = stack.GetCoordinates();
pixCols = stack._w;
pixRows = stack._h;
numPixels = stack.GetNumPixels();
targetPixMap = new int[pixCols][pixRows];
actualPixMap = new int[pixCols][pixRows];
greenRot = radians(45);

int pixSize = Math.min( (int) (scrWidth/pixCols), (int) (scrHeight/pixRows));
pixWidth = pixSize-1;
pixHeight = pixSize-1;
pixRotSize = pixSize * 2 / 3;

println(pixCols + " x " + pixRows );
println( (pixWidth * pixCols ) + " x " + (pixHeight * pixRows));
video = new Capture(this,pixCols,pixRows,30);
// video = new Capture(this,320,240,30);
println(numPixels + " video pixels defined.");
}

int LumaCurve(int source)
{
return (int) map(source, 15,240,0,255);
}

void GrabPixels()
{
if (video.available())
{
video.read();
video.loadPixels();
for (int y=0;y<pixRows;y++)
{
for (int x=0;x<pixCols;x++)
{
int coord = y*pixCols + x;
int videoPixel = video.pixels[coord];
int r = (videoPixel >> 16) & 0xff;
int g = (videoPixel >> 8) & 0xff;
int b = (videoPixel) & 0xff;
int luma = ((g * 59 + b * 11 + r * 30) / 100) & 0xff;
targetPixMap[x][y] = LumaCurve(luma);
}
}
}
}

int FindNearestColor(int source)
{
return (int) Math.floor(source * 3 / 256)*127;
}

void DitherPixels()
{
// floyd-steinberg
for (int y=0;y<pixRows-1;y++)
{
for(int x=1;x<pixCols-1;x++)
{
int coord = y*pixCols + x;
int oldpixel = targetPixMap[x][y];
int newpixel = FindNearestColor(oldpixel);
int quant_error = oldpixel - newpixel;
actualPixMap[x][y] = newpixel;
targetPixMap[x+1][y] += 7 * quant_error / 16;
targetPixMap[x-1][y+1] += 3 * quant_error / 16;
targetPixMap[x][y+1] += 5 * quant_error / 16;
targetPixMap[x+1][y+1] += 1 * quant_error / 16;
}
}
}

void PaintPixels()
{
background(0);
noStroke();
rectMode(CENTER);
translate(pixWidth/2,pixHeight/2,0);
for (int y=0;y<pixRows;y++)
{
for (int x=0;x<pixCols;x++)
{
int pixR = 0x00000000;
int pixG = 0x00000000;
int pixVal = actualPixMap[x][y];
if (pixVal > 84) pixR = 0xffff0000;
if (pixVal > 191) pixG = 0xff00ff00;
pushMatrix();
translate(x*pixWidth,y*pixHeight,0);
fill(pixR);
rect(0,0,pixWidth-2,pixHeight-2);
pushMatrix();
rotateZ(greenRot);
fill(pixG);
rect(0,0,pixRotSize-1,pixRotSize-1);
popMatrix();
popMatrix();
}
}
}



// diodebox classes - used for relatively opague modeling of scan pixel order

class DiodePixel
{
int _px;
int _py;

DiodePixel(int px, int py)
{
this._px = px;
this._py = py;
}

int GetCoordinate()
{
return (int) (this._py << 8) + this._px;
}
}

class DiodeQuad
{
int _px;
int _py;
DiodePixel[] _pixels;

DiodeQuad(int px, int py)
{
this._px = px;
this._py = py;
this._pixels = new DiodePixel[4];
for (int y=0;y<2;y++)
{
for (int x=0;x<2;x++)
{
int n = y*2+x;
this._pixels[n] = new DiodePixel(px+x,py+y);
}
}
}

int[] GetCoordinates()
{
int[] out = new int[4];
for (int i=0;i<4;i++)
{
out[i] = this._pixels[i].GetCoordinate();
}
return out;
}
}

class DiodeModule
{
int _px;
int _py;
DiodeQuad[] _quads;

DiodeModule(int px, int py)
{
this._px = px;
this._py = py;
this._quads = new DiodeQuad[8];
for (int y=0;y<4;y++)
{
for (int x=0;x<2;x++)
{
int n = y*2+x;
this._quads[n] = new DiodeQuad(px+x*2,py+y*2);
}
}
}

int[] GetCoordinates()
{
int[] out = new int[32];
for (int i=0;i<8;i++)
{
int[] tmp = this._quads[i].GetCoordinates();
for (int j=0;j<4;j++)
{
out[i*4+j] = tmp[j];
}
}
return out;
}
}

class DiodeBox
{
int _px;
int _py;
DiodeModule[] _modules;

DiodeBox(int px, int py)
{
this._px = px;
this._py = py;
this._modules = new DiodeModule[4];
for (int i=0;i<4;i++)
{
this._modules[i] = new DiodeModule(px+4*i,py);
}
}

int[] GetCoordinates()
{
int[] out = new int[128];
for (int i=0;i<4;i++)
{
int[] tmp = this._modules[i].GetCoordinates();
for (int j=0;j<32;j++)
{
out[i*32+j] = tmp[j];
}
}
return out;
}

}

class DiodeStack
{
int _w;
int _h;
int _numX;
int _numY;
int _numBoxes;
DiodeBox[] _boxes;

DiodeStack(int numX, int numY)
{
this._w = numX * 16;
this._h = numY * 8;
this._numX = numX;
this._numY = numY;
this._numBoxes = numX * numY;
this._boxes = new DiodeBox[this._numBoxes];
for (int y=0;y<_numY;y++)
{
for (int x=0;x<numX;x++)
{
// int n = y*numX + x; // stack linked by rows
int n = x * numY + y; // stack linked by columns
this._boxes[n] = new DiodeBox(x*16,y*8);
}
}
}

int[] GetCoordinates()
{
int[] out = new int[this.GetNumPixels()];
for (int i=0;i<this._numBoxes;i++)
{
int[] tmp = this._boxes[i].GetCoordinates();
for (int j=0;j<128;j++)
{
out[i*128+j] = tmp[j];
}
}
return out;
}

int GetNumBoxes()
{
return this._numBoxes;
}

int GetNumPixels()
{
return this._w * this._h;
}

DiodeBox GetBox(int i)
{
if ((i>=0) && (i<this._numBoxes))
{
return _boxes[i];
}
else
{
throw new RuntimeException("Invalid box reference");
}
}

}


Attachments:
File comment: Processing kode
diodeVideoBox2.zip [2.26 KiB]
Downloaded 71 times
Top
 Profile  
 
 Post subject: Re: Halfmachine displays til Video?
PostPosted: Tue May 06, 2008 9:39 am 
Offline
User avatar

Joined: Tue Aug 07, 2007 8:24 pm
Posts: 621
Location: copenhagen, denmark
yo
det ser sgu spændende ud for en GIF fanatiker som mig ;)
kan ikke umiddelbart få din processing sketch til at køre, men tror det er pga. forskelligt webcam setup
anyways -- glæder mig til at se det i funktion!


Top
 Profile  
 
 Post subject: Re: Halfmachine displays til Video?
PostPosted: Sun May 18, 2008 7:40 pm 
Offline
User avatar

Joined: Tue Aug 07, 2007 8:24 pm
Posts: 621
Location: copenhagen, denmark
sonny går amok :mrgreen:
Quote:
Hi guys,

I got the Wiring code working quite well and managed to draw reasonably recognizable graphics on the current standing stack of 2x4 diode boxes (32x32 pixels). Thanks to Mads for helping me figuring out the pin order. Here is a short video summary of what works now:


http://www.flickr.com/photos/25649704@N06/2499983441/


As you can see, I even got the video streaming from Processing to work except at a much lower frame rate than expected. I still need to figure that out.


Off the left side of the LED box stack I left attached a CAT5 connector for the Wiring board. It fits on Port 2, brown wire on pin 16. The dangling part is for GND. The code allows easy re-configuration of pins to fit the Arduino instead. Note: The Hline function is messed up and don't work reliably.


Attached is Wiring code configured to fit the cable, and the Processing monitor software that (semi-reliably) can be used to preview graphics running on the Wiring board. I hope it can be used for something and that you guys will have fun playing with it and improving it or maybe using it in a project.


The serial protocol isn't very good though, and it crudely uses 0xff as a special control character for vsync, etc, requiring data payload 0xff to be doubled. I'll improve the protocol when I get back and use a line-based format with checksum instead. Receiving video on the board sort of works, but it is surprisingly slow. Need to bypass all the Wiring serial code and use the serial port direct instead.


Code feature highlights:


- supports at least as many boxes as are presently available.

- supports any stack combination of x by y boxes

- video buffer allocates only 32 bytes of buffer memory per box (1 bit per pixel per color channel)

- has tidy pin abstraction with cat5 wire color references to help porting between Wiring and Arduino.

- implements setpixel, getpixel, fill, lines, rectangles, etc.

- implements a shoddy two-way serial connection: OUT from Wiring board to monitor video buffer contents in Processing; IN to receive video and graphics on Wiring board from Processing.



Once I get back from Japan I hope we can rig the stack and try running it with about 4 separate chains driven by the individual ports of the Wiring board, which should make their update faster and maybe support limited PWM shades instead of just one and off for each colors, like this:


http://www.flickr.com/photos/25649704@N ... 004331634/


http://www.flickr.com/photos/25649704@N ... 612433767/


It would require a chain refresh rate of something like 60 per second to get usable PWM shades. With 256 subpixels (128x2) per box that's about 180K pixels per second to update for a 3x4 stack, which by rights should be possible to achieve with a 16MHz atmega...


Best regards,

Sonny W. (off to Japan for a week now)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC + 1 hour [ DST ]


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group  
Design By Poker Bandits  
[ Time : 0.104s | 24 Queries | GZIP : Off ]