Thursday, May 20, 2010

online countries

If social networks or online communities are going to become political entities, someone will say:

Where are you from?
I am from Facebook, and you?
I am from Twitter.

Online countries establish laws and taxes.



---

A facebooker, an ebayer and a twitterzen once met in second life:

I sell my privacy everyday: said the facebooker.

I sell my things everyday: said the ebayer.

I sell my opinions everyday, said the twitterzen.

Is sharing a lot different than selling?
Does the price really matter, if it is not paid in money?



---


See also this previous post of mine: evaporating borders.



m

( @mgua on twitter )

Wednesday, May 12, 2010

3D printing everywhere: commoditization of reality

Tridimensional TV is about to arrive, soon after the big boom of 3D movies currently exploding.


Tridimensional printing will be the next wave.


Technologies for tridimensional printing are not new, but recently they are getting a huge push and we will soon have low cost tridimensional printing at a very low cost.

This will probably completely change the world of manufacturing and have huge impact on logistics too.

Commoditization of reality means that manufacturing and distribution costs for whatever object will drop while availability will go up. This will be quite a revolutionary change in traditional manufacturing processes.


Tridimensional printing will evolve from easy to sell solutions for building simple things, in plastic and resins, to multi-material and high resolution and high precision objects.

Nanoscale three dimensional printing will ultimately allow to build whatever object in molecular resolution.


Some scientific data:

We currently have atomic scale accurate positioning in multilayer microelecronics, currently used in building memories and processors, with metals and semiconductors. These technologies are based on masking techniques, and thin layer (a few molecules) depositing of various materials.

We are developing increasing accurate technologies for 3-dimensional scanning, and currently the resolution of living tissue MRI imaging is ranging in the 20micrometers (20 * 10 ^-6 m) and we have MRFM (Magnetic Resonance Force Microscopy) able to "see" at resolution of some nanometers (5 * 10^-9 m).

There are significant studies available, like Minimal Manufacturing, by Kazuhiro Murata (AIST, Japan) where "ink" jet printing technology is considered as the future of manufacturing.
Current superfine ink jet printing can individually address and place sub micrometer sized dots (0.5 * 10^-6 m)

In the future, the difficulty will be not to build, because ink manufacturing will ease and allow much cheaper object building.

Advances in 3-d printing will allow cheap manufacturing, and very easy transition from design to objects.


------
Some pointers:

Do It yourself 3d printing:
Here is the reprap project, conceived to be completely open and self reproducing

RepRap from Adrian Bowyer on Vimeo.




And here is the makerbot project, similar, but less focused on self reproduction.


check also this for large scale object printing


3D printing is also for medical usage. Check organ printing and this.






Marco

( @mgua on twitter )





.

Sunday, March 28, 2010

Improving the world in small steps

World improvement projects do not necessarily need to be huge.

I believe in bottom-up approaches
Evolution is a bottom-up world improvement undirected project agent.


How to make a better world


We improve the world
every time we teach
every time we learn

whenever we avoid an unnecessary waste
whenever we share a good idea

with every smile
with every good action

We improve the world

in helping each other
in showing positive attitude

in spreading culture
in promoting critical thinking
and avoiding prejudices

in not accepting a ready-made reality
or a pre-digested truth

in showing how things really are
despite the propaganda.

We improve the world
when we put aside our racisms
when we value our differences

when we consider many options
when we put in each other shoes,
when we allow choices

when we accept and consider
different point of views
at the same time

We improve the world
building things
dreaming and inventing,
thinking,
sharing,
and constantly looking for better ways.





Marco ( @mgua on twitter )


.

Monday, March 22, 2010

Spring, finally

Today it is the first day of Spring.

We had a decent weekend after a quite bad winter season.
Even one of our turtles woke up after the winter letargy.

...And I still have to prune trees and trim hedges in my garden. I am lazy.

Today I had the chance of flying one of my rc planes for the first time after the winter.
And it was great. My kids enjoyed it. Lots of other kids were watching. I also had a somewhat hard landing against a tree and so I had to use some glue to fix a small break.
We did not have a super bright sunny day, but it was enough.

I hope that next weekend will be better.


Marco

Sunday, February 21, 2010

Will life extension cause tyrants oligarchies?

Life extension technologies are almost here.
Soon it will be possible to cure bodies from aging.
These cures will be probably very expensive, and initially access will be limited to the extremely rich.


What will this bring?
Here are a couple of possible futures:



Very Scary scenario:
Life extension treatments will be accessible only to a very small elite, made of ultra-rich, political and biotech leaders.
They will actually perpetuate the chain of power in their own hands, tightly controlling drugs and treatment access.
Masses will not have access to life extension, because of cost and sociopolitical control issues.
In this scenario, obviously the leaders will oppose to whatever change, as normally happens in dictatorship. This status will last indefinitely, because dictators will extend their lifespan.



Scary scenario:
Life extension treatments will be outrageously expensive in the beginning, then gradually the price will go down, becoming accessible to high class and middle-class citizens of developed countries. Since people will not die, population will be controlled by blocking, sanctioning or prohibiting reproduction.
New births will virtually cease. Children will not exist any more. Work will become eternal routine. Most people will become bored. Someone will decide to die/suicide, stopping taking the medecines.
Middle-age will become a sort of dull eternity. A gray limbo in which to stay, er... forever.
Psychotherapies will extend for centuries.
Time will lose or change its current meaning.
Interstellar trips will become feasible.
Digital transitionism will develop. People will be ablt to decide to go digital, evaporating into bits, leaving physical body and jumping into some digital virtual world.



Is anyone willing to share some other ideas for a hopefully happier future?



Marco  ( @mgua on twitter )





.

Saturday, February 6, 2010

screen view change basing on user face position

Here is another project from my free-time personal exploration of CV (Computer Vision).
The idea is to enhance human to computer interface via the detection of user face position in front of the screen.

The screen image is updated in real-time taking into account the head position of the user.

This is one of the many possible ways of building a 3D vision, but this method works only for one user at a time.




This project is developed in Processing, and requires the OpenCV library. I used the same setup described in the Arduino Processing Face Follower and in the previous Two Axis Controlled Laser Gun

The main idea is to adapt the presentation of a 3D image shown in a window, taking into account the user face coordinates, as detected from the webcam incoming flow.


Here is a (er.. ugly) video
I am sorry but the quality is quite low. My phone camera is not that good.









Description
A tridimensional object is shown on the screen, and it is rendered taking into account the viewing position of the user, determined by the position of his face, as detected by the webcam.
The code is not particularly clean and should be improved, but works. A future version will reduce some flickering due to mistakes in continuosly detecting the user face position, expecially near the boundaries of the webcam viewing field. (this was implemented in v. 0.3)
Code is reasonably parametric, basing on the initial windows resolution specifications maxx and maxy. If you change these values, or if you have a different webcam, some tweaking might be needed in the map statement used to define the value of r. (version 0.3)




Requirements
  • A working Processing and OpenCV installation ( I did it on a Windows XP SP3)
  • A webcam.
Important: you need to have the haarcascade_frontalface_alt.xml in your processing sketch directory in order
to have face detection algorithm working.
Please follow description of the setup in my other previously mentioned projects.



Code
Here is the Processing Code.
here is the 0.1 version (jan 31 2010)
here is the 0.2 version, with autorotation features.
here is the 0.3 version, with distance sensing and size dependent from distance (feb 6 2010)
here is the 0.4 version, with side colored lights and 3D boxes scattered around.

code listing of v 0.2 version is here:

/*
  Processing and OpenCV code
 
  Detects viewer face movement and redraws scene basing on face position
  by Marco Guardigli, mgua@tomware.it  @mgua on twitter
  see http://marco.guardigli.it/2010/01/screen-view-change-basing-on-user-face.html
 
  This code is released under GNU GPL license. See http://www.gnu.org
  jan 31.2010  v 0.1   Marco Guardigli
               v 0.2   Marco Guardigli, added autorotate
                                        disabled distance sensing (not working well)
                                        and introduced distance link to mouseX
 
*/

import hypermedia.video.*;  
OpenCV opencv;

boolean DEBUG = true; // set to TRUE for some debug output
boolean AUTOROTATE = true;  // set to true to enable autorotate

int maxx = 800;        // window size x
int maxy = 600;        // window size y
int cfacex, cfacey;    // center of the first face detected
float fw, fh;          // face width and face height (in relation to window size)
float rmin, rmax;      // range of perceived distance
float fwmin, fwmax;    // possible range of face width  (auto defined)

float ex, ey, ez;      // coordinates of the camera position (eye)
float upx, upy, upz;   // rotation of the camera (default 0,1,0);
float cx, cy, cz;      // center of the scene (where the camera points)
float ra;              // distance of the camera from center of the scene
float r, r_p, r_pp;    // 3 last values of measured distance, for averaging and smoothing

float drrp, maxdr;      

float thxz, thxy, thyz;   // angles on the three planes xz, xy, yz, growing counterclockwise 0-359
float fthxz, fthxy, fthyz;   // angles on the three planes xz, xy, yz, for face
float athxz, athxy, athyz;   // angles on the three planes xz, xy, yz, for autorotate

float dthxy;    // quantum of autorotate angle change (degrees)
float dthxz;    // quantum of autorotate angle change (degrees)
float radbydeg = TWO_PI / 360;  // radians per degree

void setup() {
  size(maxx, maxy, P3D);
  fill(204);
  r = 100;
  cx = 0;  cy = 0;  cz = 0;
  ex = r;  ey = 0;  ez = 0;
  upx = 0; upy = 1; upz = 0;
  thxz = 0; thxy = 0; thyz = 0;

  rmax = 0; rmin = r;           // initial values for minmax detection (switched on purpose)
  ra = r; r_p = r; r_pp = r;    // previous values for averaging
 
  fwmin = 1; fwmax = 0;         // initial values for minmax detection (switched on purpose)

  opencv = new OpenCV(this);
  opencv.capture( maxx, maxy );
  opencv.cascade( OpenCV.CASCADE_FRONTALFACE_ALT );    // load the FRONTALFACE description file
  opencv.read();
  if (AUTOROTATE) {
      dthxy = 0;
      dthxz = 5;    // vertical axis
  }
}

void draw() {
  lights();
  background(0);
  camera(ex, ey, ez, cx, cy, cz, upx, upy, upz);
  stroke(255,255,0);
  box(50,30,60);   // draw a 3d solid composed of
  box(40,20,70);   // three simple intersecating
  box(60,10,10);   // boxes
 
  opencv.read();
  Rectangle[] faces = opencv.detect();     // detect anything resembling a FRONTALFACE

  if (faces.length > 0) {       
    cfacex = faces[0].x; cfacey = faces[0].y;
    fw = faces[0].width;
    cfacex = cfacex + int(fw / 2);        // cfacex = x center of face 

    fh = faces[0].height;
    cfacey = cfacey + int(fh / 2);        // cfacey = y center of face 
    fh = fh / maxy;

    fw = fw / maxx;                       // portion of screen width taken by face width
    if (fw < fwmin) { fwmin = fw; }       // detect min and max face width for autoscale
    if (fw > fwmax) { fwmax = fw; } 
  
    fthxy = map (cfacey,0,height,-40,40);  // input range for xy is 80 degrees (-40..40)
    fthxz = map (cfacex,0,width,-50,50);   // input range for xz is 100 degrees (-50..50)
    athxy = (athxy + dthxy) % 360;         // dthxy and dthxz are autorotate steps
    athxz = (athxz + dthxz) % 360;
    thxy = (fthxy + athxy) % 360;          // add autorotate to face position
    thxz = (fthxz + athxz) % 360;

    ex = ra * cos(thxz * radbydeg);   // new camera coordinates x
    ey = ra * sin(thxy * radbydeg);   // new camera coordinates y
    ez = ra * sin(thxz * radbydeg);   // new camera coordinates z
   
    ra = mouseX + 100;

    if ( DEBUG ) {
      println("cfacex,cfacey=[" + cfacex + "],[" + cfacey + "]");
      println("facewidth,faceheight=[" + fw + "],[" + fh +"]");
      println("fwmin,fwmax=[" + fwmin + "],[" + fwmax +"]");
      println("r, rmin, rmax=[" + r + "],[" + rmin + "],[" + rmax + "]");
    }
   } else { println("face not detected");  }
 }



Similar projects
TrackEye, Real-Time Tracking Of Human Eyes Using a Webcam:  by Zafer Savas, is aimed at detecting user eyes coordinates.

Head Tracking for Desktop VR Displays using the Wii Remote: by Johnny Chung Lee, is a very nice hack that uses a wii remote device to sense the user head position thru the wii infrared sensor



Marco  ( @mgua on twitter )

.

Saturday, January 23, 2010

Growing transplantable organs with tissue engineering

Here is a great piece from TedMed Oct 2009, published in jan 2010

Anthony Atala's description of the research about how to build or regenerate organs to be used in transplants.

This TED talk is very powerful, fascinating and enthusiastic.
Here is the original link. http://www.ted.com/talks/anthony_atala_growing_organs_engineering_tissue.html








These topics and the current state of the research in these fields seem really to be science fiction but are real.

People will be allowed to access cell banks where to leave their healthy cells taken as biopsy from important organs, to be preserved for eventual later use.
When needed, the saved cells could be used to rebuild the failed organs to be transplanted.

Simply Amazing.


Marco ( @mgua on twitter )





.

Sunday, January 17, 2010

Arduino_Processing Face Follower

Here is another Arduino and Processing project I built this weekend.
A face follower allows to track people faces and to track them in a scene, captured thru a webcam.

My face follower is simple. It is built on the previous project LaserGun, and the code is very similar. The Arduino part is exactly the same, actually.









The differences rely in the use of a webcam, for continuosly acquiring the scene, and in the use of a computer vision library: OpenCV, which is the actual engine behind the magic.

Here you can fine some more information about it: Wikipedia OpenCV entry, OpenCV Wiki, .


Requirements: 
This project requires:
  1. A Windows XP pc with a webcam. I tried on Windows Vista but without success. Windows 7 is probably not working too.
  2. An Arduino board.
  3. Two servos.
  4. Some wires
  5. One toy laser (optional, for more accurate calibration system).

Building:
Building is exactly the same like the LaserGun project.
In place of  the laser you can put a toy figure face or a puppet face, so that when the servo move you will see the puppet face turning and tilting.


The Software
Arduino board software is exactly the same of LaserGun project

PC software requires some additional components, but starts from the same base.
I followed the instructions I found here http://ubaa.net/shared/processing/opencv/ which is the main site for OpenCV integration with Processing.

On your PC, you will need:
  1. Processing environment. Enough said.

  2. OpenCV libray: go download it and be sure NOT to download the wrong version. We need the 1.0 version. I suggest to install it in c:\opencv10 or to another simple path without spaces within the name. I do not recommend to install it in the default location which is probably dependent from your windows system language, and may contain spaces (like in "Program Files"). Also I suggest to use an all lowercase name.During installation, answer yes when prompted to alter the path adding the c:\opencv10\bin folder.
    After installation, I am suggesting to edit the system path so to have that folder at the beginning of the path, as shown in this picture (you can get to this via right click on My Computer, then Properties, then Advanced tab, then Environment Variables button.














    A reboot is not needed








  3. OpenCV Processing Libray: This is for interfacing your Processing environment with OpenCV.You can download it from here, expand it and put the contents inside the folder "libraries" into your processing installation root. In my case i put it in c:\inst\processing-1.0.9\processing-1.0.9\libraries


Optionally you can also install the Processing OpenCV examples. Get them from http://ubaa.net/shared/processing/opencv/download/opencv_examples.zip (5Mb)
and copy the opencv-examples folder accordingly in C:\inst\processing-1.0.9\processing-1.0.9\examples


Checking the environment installation
When starting Processing you should be able to open and run the example code. If you do not see camera feed, it is probably due to your OS not being XP. (I had black pitch camera feed on my Vista laptop)
In order for the face detection demos to work, you need to copy the proper haar recognizer data file into the sketch folder. Get the datafiles from C:\openCV10\data\haarcascades



Here is the code (also download from my google docs)

-----------

//
// Processing code for a two axis Face-Following interacting with arduino firmata
// to control servos connected to analog 9 and analog 10 pins
// project "facefollow" by Marco Guardigli, email: mgua@tomware.it  twitter: @mgua
//
// ATTENTION! This software makes a laser pointer track a face.
//            Use with extreme caution.
//
//
// see
// http://marco.guardigli.it/2010/01/arduinoprocessing-face-follower.html
// 
// this code is free software, released under the GNU GPL license
// see www.gnu.org for license details
//
// copyleft Marco Guardigli
//    2010 jan 18: first version
//
//
import hypermedia.video.*;   
import processing.serial.*;
import cc.arduino.*;       
OpenCV opencv;
Arduino arduino;

int maxx = 640;          // windows sizes, I suggest not to go over 640x480
int maxy = 480;

int calibrating = 0;     // nonzero during calibration, states are 0,1,2

int calibrateminx=80;    // recalibration window
int calibrateminy=80;
int calibratemaxx=100;
int calibratemaxy=100;
int cx1, cy1, cx2, cy2;  // screen mousex/y coords of the two calibration points

float dzx, dzy;
int cfacex, cfacey;    // center of the first face detected

int maxservox=170;     // maximum servo excursions - to be redefined in recalibration
int minservox=10;
int maxservoy=170;
int minservoy=10;
int initialservox = 90;
int initialservoy = 90;
int servox, servoy;    // current servos positions -in servo range-
int laseroff = 0;      // laser is controlled (improperly) as a servo
int laseron = 100;     // zero is not actually turned off, but is dimmer


void setup() {
  println(Arduino.list());

// IMPORTANT! This code will not work if you do not write the correct 
// id of the serial interface in the next line (in my case 2)
  arduino = new Arduino(this, Arduino.list()[2], 57600);
  arduino.analogWrite(9,initialservox);
  arduino.analogWrite(10,initialservoy);
  arduino.analogWrite(11,laseroff);  // laser off
  size(maxx,maxy);
 
  opencv = new OpenCV(this);
  opencv.capture( width, height );
  opencv.cascade( OpenCV.CASCADE_FRONTALFACE_ALT );    // load the FRONTALFACE description file

  opencv.read();
  image( opencv.image(), 0, 0 );
}


void draw() {
  opencv.read();
  image( opencv.image(), 0, 0 );
  Rectangle[] faces = opencv.detect();     // detect anything resembling a FRONTALFACE
  noFill(); stroke(255,0,0);
  for( int i=0; i < faces.length; i++ ) {
      rect( faces[i].x, faces[i].y, faces[i].width, faces[i].height );
  }
 
  switch (calibrating) {
    case 0: {  // no calibration in course: pointer follows face 
      if (faces.length > 0) {
        cfacex = int(faces[0].x + (faces[0].width / 2));
        cfacey = int(faces[0].y + (faces[0].height / 2));
        servox = int(map(cfacex,0,maxx,minservox,maxservox));
        servoy = int(map(cfacey,0,maxy,minservoy,maxservoy));
        arduino.analogWrite(9,servox);
        arduino.analogWrite(10,servoy);
      }
    }
    case 1: {   // need to read first calibration point
      cx1 = mouseX;
      cy1 = mouseY;
    }
    case 2: {   // need to read second calibration point
      cx2 = mouseX;
      cy2 = mouseY;
    }
  }
}


void mousePressed() {
    if (mouseButton == LEFT) {
      if (calibrating == 0) {                // draw shot on screen
        arduino.analogWrite(11,laseron);     // and intensify laser
        stroke(200,200,0);                 
        fill(200,0,0);
        ellipse(cfacex,cfacey,5,5);
        delay(500);
        ellipse(cfacex,cfacey,10,10); 
        arduino.analogWrite(11,laseroff); 
      }
    }
}   
   

void mouseReleased() {
  if (mouseButton == RIGHT) {
    switch (calibrating) {
      case 0: { 
            calibrating = 1;   // stops laser following mouse pointer
            arduino.analogWrite(9,calibrateminx);
            arduino.analogWrite(10,calibrateminy);
            arduino.analogWrite(11,laseron);     // and intensify laser           
            println("cx1/cy1: point mouse to where laser pointer is and RCLICK");
            break;
      }
      case 1: {  // arriving here after rclick release in calibration point 1
            calibrating = 2;
            arduino.analogWrite(9,calibratemaxx);
            arduino.analogWrite(10,calibratemaxy); 
            arduino.analogWrite(11,laseron);     // and intensify laser                       
            print("  calibration point1: "); print(cx1); print(" , ");  println(cy1);
            println("cx2/cy2: point mouse to where laser pointer is and RCLICK");
            break;
      }
      case 2: {  // arriving here after rclick release in calibration point 2
            print("  calibration point2: "); print(cx2); print(" , ");  println(cy2);
            // (cx1,cy1) corresponds to (calibrateminx, calibrateminy)
            // (cx2,cy2) corresponds to (calibratemaxx, calibratemaxy)
            // i will recalculate minservox, minservoy and maxservox, maxservoy
            if (((cx2-cx1) != 0) && ((cy2-cy1) != 0)) {
              stroke(200);
              line (cx1,cy1,cx1,cy2);
              line (cx1,cy2,cx2,cy2);
              line (cx2,cy2,cx2,cy1);
              line (cx2,cy1,cx1,cy1);
 
              dzx = (calibratemaxx - calibrateminx);

              dzx = dzx / (cx2 - cx1);              // dzx is how much servo per pixel
              dzy = calibratemaxy - calibrateminy;
              dzy = dzy / (cy2 - cy1);

              float leftx = calibrateminx - ( dzx * cx1 );
              float rightx = calibratemaxx + ( dzx * (maxx-cx2) );
              float upy = calibrateminy - ( dzy * cy1 );
              float downy = calibratemaxy + ( dzy * (maxy-cy2) );

              minservox = int(leftx);
              maxservox = int(rightx);
              minservoy = int(upy);
              maxservoy = int(downy);
            } else {
              println("Invalid calibration points selected.");
            }           
            calibrating = 0;
            arduino.analogWrite(11,laseroff);     // and dim laser           
            break;
          } // end case 2
      default: {
            break;
          } // end case default
      } // end switch
    } // end if mousebutton right
} // end mouseReleased

 



------------


Calibration
As in the LaserGun Project, it is important to properly calibrate the system so to have it work decently.
In order to minimize errors, try to keep the position of the lasers and the position of the webcam as close as possible.
Calibration is performed pressing right mouse click, and then right clicking on the screen where you see the pointer. Repeat for the two points asked.

Multiple faces: 
Currently, the OpenCV library is able to detect more than one face in the scene. The detected faces are not always presented in the same order. If you present two faces to the current system, it will be confused. A more accurate movement detection and tracking over time would be needed.


Caution
This code could be dangerous if improperly used. Never play with laser pointing it into people eyes and face.
Be smart, and always think before doing.



Marco  ( @mgua on Twitter )

.

Friday, January 15, 2010

Processing code for building multi-frame animated images

Barrier Grid Animations (or Scanimations® as elsewhere referenced -see endnote-) are fun.

See this nice video by brusspup on youtube to quickly understand the concept.










I wrote a software tool to produce the picture and the related mask to see it.
the code is written in Processing. It produces the picture and the related mask.

My software basically takes a number of images as input that are to be considered the frames of the animation to be built. You can change the parameter to define how many images do you want to use. Typically you can go with 4, and 6 is probably the maximum, otherwise the animation is too dark, because the final effect reduces the image brightness sensibly.

If you use 4 frames, only 1/4 columns of pixels are visible at a given time, reducing overall brightness to 25% of the original.
If you use 6 frames, final brightness goes to 17% of the original.

As source pictures, it is best to use some high contrast pictures, for example some high contrast dark shapes on white background. I tried with photos taken from my webcam but results were quite poor.

Simple parametrization is needed in the source, to adapt to your input image sequence and output resolution.


Printing the mask transparency and the multi-frame picture
Another tricky problem can be the printing of the mask bitmap. I used a standard laser printer, and printed on A4 sized transparencies.Usually printers perform dithering and anti-aliasing and introduce their "improvements" on printed data, but for this print job we do not need any halftoning.

I performed some tests, and was not satisfacted by any of the normal printing results from standard applications. I resorted to using Adobe Photoshop, and performed image scaling multiplying the original size of the image by a integer (i multiplied by 3 my original size and kept proportions).
It is critical and important that, when scaling, you multiply the image size by an integer, so that even spacing between resulting pixel columns is used. (doing this the scaling algorithm needs not to introduce new columns via interpolation).
In the resample image option of the Image/Image-size menu, I then selected "Nearest Neighbor". This option produces no dithering or halftoning upon image resizing.
If someone know how to obtain the same result without using Photoshop, please let me know.
(july 2010 note: paint.net has a similar option which is working fine)

Of course, you need to perform scaling of the picture following exactly the same rules. Exact size proportion between pixel column width must be preserved and must be the same in the mask and in the image.


Detail view
For better understanding, here is a detail zoom of a multi-frame image portion, prepared for 6 animation frames:
.

And here is a corresponding detail zoom of individual pixel of the mask for 6 animation frames. You can see 1 transparent column and 5 opaque columns




Code
To use this code you need a Processing development environment. You can download and install it from the Processing web site. It is open source and multiplatform, for Windows/Linux/Mac.
Then you create a new sketch, and paste the following code, saving the new project.
You then have to put in the sketch folder the pictures you want to create the animation from, naming each file with a name ending in a progressive digit starting from 0,1,2,3... See the source code for understanding better.

Here is the Processing code.

//------------
// scanimator
//
// a processing sketch for generating barrier grid animations

// see http://marco.guardigli.it/2010/01/scanimation-builder-processing-code.html
//
// this code is Free Software, released under GNU GPL license. See www.gnu.org for license details
// copyleft Marco Guardigli
//
//    email: mgua@tomware.it
//    twitter: @mgua
//
//
//  2009 dec 20  first draft
//  2010 jan 15  1.0
//
//

int nframes = 6;
int maxx, maxy;
int cframe = 1;
int xsize=320;  //resized image
int ysize=240;

String[] fname = new String[nframes];
String prjname = "giulio";       // project name: common initial part of the input frame filenames

PImage[] frame = new PImage[nframes];            // sequence of the initial frame to process
PImage scanimage;                                // resulting multi-frame scanimage
PImage maskimage;                                // mask

void setup() {
  for (int i=0; i < nframes; i++) {              // cycle on input frames
    fname[i] = prjname + "-" + i + ".jpg";
    println(fname[i]);
    frame[i] = loadImage(fname[i]);              // read frame
    frame[i].resize(xsize,ysize);
  }
  maxx = frame[0].width;
  maxy = frame[0].height;
  scanimage = createImage(maxx,maxy,ARGB);        // output scanimage
 
  for (int f = 0; f < nframes; f++) {             // cycle on input frames
    for (int c = 0; c < maxx; c += nframes ) {    // columns to keep of this frame
        for (int y = 0; y < maxy-1; y++) {        // cycle on each pixel of the column
          scanimage.pixels[maxx * y + f + c] = frame[f].pixels[maxx * y + f + c];
        }
    }
  }
  scanimage.save(prjname + "_scanimage.png");   // save resulting scanimage
 
 
  // mask preparation and file generation
  maskimage = createImage(maxx,maxy,RGB);
  for (int x = 0; x < maskimage.width; x += nframes ) {
    for (int y = 0; y < maskimage.height; y++ ) {
      maskimage.pixels[xsize * y + x] = color(255,255,255);
    }
  }
  maskimage.save(prjname + "_mask.png");
 
  // prepare for on screen display of scanimage
  size(maxx,maxy);
}


void draw() {
  background(255);
  image(scanimage,0,0);
  stroke(0);                    // draws a mask on top of the image
  for (int c = cframe; c < scanimage.width; c += nframes ) {
    for (int b=0; b < (nframes -1); b++) {
      line(c+b,0,c+b,scanimage.height);
    }
  }
}



void mousePressed() {          // when click shift mask
 cframe = (cframe + 1) % nframes;
 println("cframe=" + cframe);
}
// ---------------


Example
As an example, here are some webcam photos I made with my kids and the resulting mask and multi-frame image. It is a set of six frames:





 

 

And here is the processed multi-frame picture:

 
with the related mask


These on-screen pictures will be probably resized on your computer screen, so do not use them for printing expecting nice results, because they will probably not work.

Related websites:
  Rufus Butler Seder website (Eye Think inc)
  dudecraft's blog
  http://animbar.mnim.org/  
  A scratch similar project from MIT

Have fun and happy scanimaging.


Marco ( @mgua on twitter )






NOTE on copyright - added on 2010 july 27 after receiving a request from the trademark owner, resulting in removal of every occurrence of the words "scanimation" and "scanimations" in relation to my work:

Scanimation® is a federally registered trademark owned by Eye Think, Inc. and bearing U.S. Registration No. 2,614,549. The mark was federally registered in the United States on September 3, 2002.
http://www.eyethinkinc.com/





Monday, January 11, 2010

Arduino+PC two axis controlled Laser Gun

If you are a Maker (it seems not politically correct to say Hacker), or a DIY fan, here is a project that I developed in the weekend, playing with Arduino board and Processing.

I built this hack with my kids: Luca and Giulio, and we had lots of fun.


Our Laser Gun works using two servos, connected to an Arduino board. The Arduino is connected to the pc via usb. The thing is controlled via mouse movements on the pc, over a window with a picture taken from the gun position. The laser works normally in low intensity "pointing mode", and when fired is much powerful.
The arduino microcontroller and the pc communicate via serial usb interface. A custom Processing software allows mouse interaction, and enables guidance.

Parts used:
Arduino 2009 board
2 light servos (I used two standard hitec hs55 8gr)
1 laser pointer (savaged from a broken toy gun)
1 breadboard (not strictly necessary)
some adhesive putty (I used UHU Patafix)
some wires


Circuit description:
  • black cable of both servos is connected to ground
  • red cable of both servos is connected to +5Vcc
  • x-servo (the lower one, moving its head on the horizontal left-right plane) control yellow is connected to arduino digital pin 9

  • y-servo (the upper one, moving its head on the vertical top-down plane) control yellow is connected to arduino digital pin10
  • laser pointer negative is connected to ground
  • laser pointer positive is connected to Arduino digital pin 11 (the two laser intensities are managed controlling it -improperly- like a servo, but it works)


Software:
The project requires two pieces of software: one written in Arduino Language (based on Wiring) that is run on the Atmel 328 microcontroller on Arduino 2009 board, and a Processing sketch (Processing programs are called sketches) that is run on the pc.
The two softwares communicate with a standard serial protocol, called Firmata, which is implemented in libraries both on the Arduino side and on the PC Processing side.
I am currently using Microsoft Windows Vista Ultimate 64bit. This is not the best platform for development, and I do not recommend such a setting. A better solution would be Microsoft XP 32 bit.


On the pc, I installed the standard Arduino Development Environment (currently I run arduino-0017), with no additions. This allows to code, edit, compile and download to the Arduino board and test programs written in Wiring.
Arduino Development Environment is based on a version of Processing. Once installed


On the PC I also installed the latest Processing environment (1.0.9 in my case). An add-on library is needed so to support the Firmata serial protocol. This library zip file ( http://www.arduino.cc/playground/uploads/Interfacing/processing-arduino-0017.zip ) file has to be expanded and the three included directories (examples, libraries, src) must be copied to the following folder: \libraries\arduino





Here is the software to be downloaded to the Arduino controller. It is the standard Firmata "servo" template (you can find it among the included examples) with a very simple addition for managing the laser, connected on pin 11.

Note: Unfortunately, it seems that blogger does not like the "<" and ">" characters inside the listings.
the following first two lines are actually #include"<"Firmata.h">" and #include"<"Servo.h">" but you have to remove the " characters.
Additionally, I uploaded the code also on this posterous entry, and on these  two scribd entries: 1 and 2.

#include "<"Firmata.h">"
#include "<"Servo.h">"
Servo servo9;
Servo servo10;
Servo laser11;

void analogWriteCallback(byte pin, int value)
{
    if(pin == 9)
      servo9.write(value);
    if(pin == 10)
      servo10.write(value);
    if(pin == 11)
      laser11.write(value);
     
}


void setup()
{
    Firmata.setFirmwareVersion(0, 2);
    Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);

    servo9.attach(9);
    servo10.attach(10);
    laser11.attach(11);
  
    Firmata.begin(57600);
}

void loop()
{
    while(Firmata.available())
        Firmata.processInput();
}



And here follows the code to be run on the PC Processing environment

I am using a small font, so not to cause unwanted line breaks.
The Processing code will have to be adapted to your environment defining the correct identifier for the USB serial interface (see at the beginning of the setup  procedure), and also to define the proper name of the picture you want to display on screen while operating the laser (see setup procedure). I suggest to use a picture resized to 800x600, taken from the place in which the laser will be put, towards the target.
The picture file has to be put in the Processing sketch directory in which you will save the project. To access that directory just select the Sketch menu from the Processing environment, and then "show sketch folder".


//
// Processing code for interacting with arduino firmata
// to control servos connected to analog 9 and analog 10 pins
// project "lasergun" by Marco Guardigli, mgua@tomware.it 

//

// see
// http://marco.guardigli.it/2010/01/arduinopc-two-axis-controlled-laser-gun.html

// 
// this code is free software, released under the GNU GPL license
// see www.gnu.org for license details
//
// copyleft Marco Guardigli
//    2010 jan 11: first version
//
//

import processing.serial.*;
import cc.arduino.*;       
Arduino arduino;

int maxx;              // windows sizes
int maxy;
int calibrating = 0;   // nonzero during calibration, states are 0,1,2

int calibrateminx=80;  // recalibration window
int calibrateminy=80;
int calibratemaxx=100;
int calibratemaxy=100;
int cx1, cy1, cx2, cy2;  // screen mousex/y coords of the two calibration points

float dzx, dzy;

int maxservox=170;  // maximum servo excursions - to be redefined in recalibration
int minservox=10;
int maxservoy=170;
int minservoy=10;
int initialservox = 90;
int initialservoy = 90;
int servox, servoy;    // current servos positions -in servo range-
int laseroff = 0;      // laser is controlled (improperly) as a servo
int laseron = 170;     // zero is not actually turned off, but is dimmer

PImage room;           //  room background (photo shot from laser initial position)


void setup() {
  println(Arduino.list()); 

// IMPORTANT! This code will not work if you do not write the correct 
// id of the serial interface in the next line (in my case 3)
  arduino = new Arduino(this, Arduino.list()[3], 57600);
  arduino.analogWrite(9,initialservox);
  arduino.analogWrite(10,initialservoy);
  arduino.analogWrite(11,laseroff);  // laser off
// put in the next line the file name of your ambient picture

// taken placing the camera in the place where the lasergun is 
// so to have a correct perspective on the screen

  room = loadImage("myplace.jpg");   // put here the picture of your place
  maxx = room.width;                 // I suggest to resize the picture to 800x600
  maxy = room.height;
  size(maxx,maxy);
  background(120);
  image(room,0,0);  //show background room
}


void draw() {
  switch (calibrating) {
    case 0: {  // no calibration in course: laser follows pointer 
      servox = int(map(mouseX,0,maxx,minservox,maxservox));
      servoy = int(map(mouseY,0,maxy,minservoy,maxservoy));
      arduino.analogWrite(9,servox);
      arduino.analogWrite(10,servoy);
    }
    case 1: {   // need to read first calibration point
      cx1 = mouseX;
      cy1 = mouseY;
    }
    case 2: {   // need to read second calibration point
      cx2 = mouseX;
      cy2 = mouseY;
    }
  }
}


void mousePressed() {
    if (mouseButton == LEFT) {
      if (calibrating == 0) {                // draw shot on screen
        arduino.analogWrite(11,laseron);     // and intensify laser
        stroke(200,200,0);                 
        fill(200,0,0);
        ellipse(mouseX,mouseY,5,5);
        delay(500);
        ellipse(mouseX,mouseY,10,10); 
        arduino.analogWrite(11,laseroff); 
      }
    }
}   
   

void mouseReleased() {
  if (mouseButton == RIGHT) {
    switch (calibrating) {
      case 0: { 
            calibrating = 1;   // stops laser following mouse pointer
            arduino.analogWrite(9,calibrateminx);
            arduino.analogWrite(10,calibrateminy);
            arduino.analogWrite(11,laseron);     // and intensify laser           
            println("cx1/cy1: point mouse to where laser pointer is and RCLICK");
            break;
      }
      case 1: {  // arriving here after rclick release in calibration point 1
            calibrating = 2;
            arduino.analogWrite(9,calibratemaxx);
            arduino.analogWrite(10,calibratemaxy); 
            arduino.analogWrite(11,laseron);     // and intensify laser                       
            print("  calibration point1: "); print(cx1); print(" , ");  println(cy1);
            println("cx2/cy2: point mouse to where laser pointer is and RCLICK");
            break;
      }
      case 2: {  // arriving here after rclick release in calibration point 2
            print("  calibration point2: "); print(cx2); print(" , ");  println(cy2);
            // (cx1,cy1) corresponds to (calibrateminx, calibrateminy)
            // (cx2,cy2) corresponds to (calibratemaxx, calibratemaxy)
            // i will recalculate minservox, minservoy and maxservox, maxservoy
            if (((cx2-cx1) != 0) && ((cy2-cy1) != 0)) {
              stroke(200);
              line (cx1,cy1,cx1,cy2);
              line (cx1,cy2,cx2,cy2);
              line (cx2,cy2,cx2,cy1);
              line (cx2,cy1,cx1,cy1);
 
              dzx = (calibratemaxx - calibrateminx);
// for some reason dzx=(calibratemaxx-calibrateminx)/(cx2-cx1)
// was not calculated well ( !!! )
// so I had to split the calculation int two statements
              dzx = dzx / (cx2 - cx1);              // dzx is now "how much servo per pixel"
              dzy = calibratemaxy - calibrateminy;
              dzy = dzy / (cy2 - cy1);

              float leftx = calibrateminx - ( dzx * cx1 );
              float rightx = calibratemaxx + ( dzx * (maxx-cx2) );
              float upy = calibrateminy - ( dzy * cy1 );
              float downy = calibratemaxy + ( dzy * (maxy-cy2) );

              minservox = int(leftx);
              maxservox = int(rightx);
              minservoy = int(upy);
              maxservoy = int(downy);
            } else {
              println("Invalid calibration points selected.");
            }           
            calibrating = 0;
            arduino.analogWrite(11,laseroff);     // and dim laser           
            break;
          } // end case 2
      default: {
            break;
          } // end case default
      } // end switch
    } // end if mousebutton right
} // end mouseReleased




Features:
Once you have loaded the software on the Arduino board, this will be automatically run at boot. So you will not need to load it again unless you decide to change it.
Operation of the Laser Gun just requires you to connect the USB cable to the pc, and launch the Processing Environment. The Firmata library is designed to "bring outside" of the board all its features, allowing complete control from the PC.

Once initialization has been completed, you will see a window with the picture you loaded in the sketch folder. If you move the mouse, the laser gun should follow your mouse movements.
When you press the left mouse button, the led intensity will grow, to represent "fire". I will probably add some audio features, because the thing is too silent now :-).

You will soon notice that there is a non correspondance between what you point on the screen and the actual position of the laser. This happens because you need to calibrate the system.
Calibrating means that the servo min and max boundaries have to be redefined so to match the space in which you use the LaserGun.

Calibration is performed clicking the right button, and requires you to point with the mouse the place in the screen corresponding to the actual place where you see the laser in your room. When positioned, press again right click, and repeat for a second point that the system will ask. After this procedure has been completed you should have a (reasonable) correspondance between what you aim and what you kill. ( :-)



Safety precautions
Be careful not to point laser in people or animals eyes.
If you use more powerful servos, or a more powerful laser, it is better to power the system via an external power supply, and not thru the USB.






Feel free to improve and enjoy at will




Marco  ( @mgua on twitter )