<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>tag:www.local-guru.net,2005:/blog/tag/multitouch</id>
  <link type="text/html" href="http://www.local-guru.net" rel="alternate"/>
  <link type="application/atom+xml" href="http://www.local-guru.net/blog/tag/multitouch.atom" rel="self"/>
  <title>GuruBlog : Articles about multitouch</title>
  <subtitle>local-guru.net</subtitle>
  <updated>2008-10-10T19:52:00+02:00</updated>
  <generator>GuruBlog engine</generator>
  <entry>
    <id>tag:www.local-guru.net,2005:Article/77</id>
    <published>2008-10-10T19:52:00+02:00</published>
    <updated>2008-10-10T19:52:00+02:00</updated>
    <link type="text/html" href="http://www.local-guru.net//blog/2008/10/10/touchless-multitouch-in-processing" rel="alternate"/>
    <author>
      <name>Nikolaus Gradwohl</name>
    </author>
    <title type="html">Touchless Multitouch in Processing</title>
    <category term="processing" scheme="http://www.local-guru.net/blog/tag/processing"/>
    <category term="video" scheme="http://www.local-guru.net/blog/tag/video"/>
    <category term="capture" scheme="http://www.local-guru.net/blog/tag/capture"/>
    <category term="multitouch" scheme="http://www.local-guru.net/blog/tag/multitouch"/>
    <content type="html">&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;update (23.11.2008):&lt;/em&gt; i made a &lt;a href="http://www.local-guru.net/blog/pages/colormatcher"&gt;processing library&lt;/a&gt; out of the codeexample above - &lt;em&gt;guru&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Today i read about &lt;a href="http://www.codeplex.com/touchless"&gt;touchless&lt;/a&gt;
an opensource sdk microsoft has released. it enables you to track objects using a
simple webcam to create apps similar to a multitouch display.&lt;/p&gt;

&lt;p&gt;then i spent about 20 minutes coding a &lt;a href="http://www.processing.org"&gt;processing&lt;/a&gt; sketch that does roughly the
same :-)&lt;/p&gt;

&lt;p&gt;as you can see in the screenshot i cant affort the same cool toys the microsoft
coder has in the demo video, so i  had to use some post-its instead&lt;/p&gt;

&lt;p&gt;below is the complete sourcecode of the app. I didn't export it as an
applet this time, because the applet couldn't use the quicktime api. It also
only works on mac and windows this time, because processing uses the quicktime
api. on linux the gstreamer based replacement from &lt;a href="http://codeanticode.wordpress.com/2007/12/12/cross-platform-video-library-for-processing/"&gt;codeanticode&lt;/a&gt;
can be used.&lt;/p&gt;

&lt;p&gt;when the programm starts hold up 2 different colored items (use colors that do not appear in the background)
then click on the first color with the left mousebutton and on the second color using the right
mousebutton. and then enjoy the "minority report"-experience zooming and rotating the image :-)&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.local-guru.net/img/guru/colormatcher.png" alt="colormatcher.png" /&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import processing.video.*;
import java.util.*;

Capture video;
PImage img; 

void setup() {
  size( 640, 480 );
  video = new Capture( this, width, height, 30 );
  img = loadImage( "mangoofdeath.jpg" );
  noStroke();
  smooth();
}

int searchColor1 = color( 128, 255, 0 );
int searchColor2 = color( 255, 0, 0 );

Point e1 = new Point( 0, 0 );
Point e2 = new Point( 640, 480);

boolean s1, s2;


void draw() {
  if ( video.available()) {
     video.read();
     pushMatrix();
     scale(-1,1);
     image( video, -width, 0, width, height );
     popMatrix();

     int idx = 0;

     ArrayList p1 = new ArrayList();
     ArrayList p2 = new ArrayList();
     for ( int y = 0; y &amp;lt; video.height ; y++ ) {
        for ( int x = video.width; x &amp;gt;0; x-- ) {
          if ( match( searchColor1, video.pixels[idx] )) {
            p1.add( new Point( x, y ));
            //fill( 255, 255, 0, 128 );
            //ellipse( x, y, 10, 10 );
          } else if (match( searchColor2, video.pixels[idx] )){
            p2.add( new Point( x, y ));
            //fill( 255, 0, 0, 128 );
            //ellipse( x, y, 10, 10 );
          }
          idx ++;
        }
     }

    noStroke();
    if (p1.size() &amp;gt; 0) e1 = avg( p1 );
    if (p2.size() &amp;gt; 0) e2 = avg( p2 );

    if (s1) {
      fill( 255, 255, 0, 128 );
      ellipse( e1.x, e1.y, 30, 30 );
    }
    if ( s2 ) {  
      fill( 255, 0, 0, 128 );
      ellipse( e2.x, e2.y, 30, 30 );
    }

    if (s1 &amp;amp;&amp;amp; s2 ) {
      pushMatrix();
      translate( e1.x, e1.y );
      int dx = e2.x - e1.x;
      int dy = e2.y - e1.y;
      rotate( - atan2( e2.x -  + e1.x, e2.y - e1.y) + atan2( img.width, img.height));
      float zoom = sqrt ( dx * dx + dy * dy ) / 
           sqrt(img.width * img.width + img.height * img.height);
      scale(zoom, zoom ); 


      tint( 255, 200 );

      image( img, 0, 0 );
      strokeWeight( 3 );
      stroke( 255 );
      noFill();
      rect( 0,0,img.width, img.height );
      noTint();
      popMatrix();
    }
  }
}

boolean match( int c1, int c2 ) {
  int limit = 20;
   int sr = c1 &amp;gt;&amp;gt; 16 &amp;amp; 0xFF;
   int sg = c1 &amp;gt;&amp;gt; 8 &amp;amp; 0xFF;
   int sb = c1 &amp;amp; 0xFF;

   int cr = c2 &amp;gt;&amp;gt; 16 &amp;amp; 0xFF;
   int cg = c2 &amp;gt;&amp;gt; 8 &amp;amp; 0xFF;
   int cb = c2 &amp;amp; 0xFF;

   return cr &amp;gt; sr - limit &amp;amp;&amp;amp; cr &amp;lt; sr + limit &amp;amp;&amp;amp;
     cg &amp;gt; sg - limit &amp;amp;&amp;amp; cg &amp;lt; sg + limit &amp;amp;&amp;amp; 
     cb &amp;gt; sb - limit &amp;amp;&amp;amp; cb &amp;lt; sb + limit;
}

void mousePressed() {
  if (mouseButton == LEFT) {
      searchColor1 = get( mouseX, mouseY );
      s1 = true;
  }
  if (mouseButton == RIGHT) {
     searchColor2 = get( mouseX, mouseY );
     s2 = true;
  }
}

void keyPressed() {
  s1 = false;
  s2 = false;
}

Point avg( ArrayList l ) {
  if (l.size() == 0) {
    return new Point( 0, 0 );
  }
  int x = 0;
  int y = 0;
  for( Iterator i = l.iterator(); i.hasNext(); ) {
      Point p = (Point)i.next();
      x += p.x;
      y += p.y;
  }
  return new Point( x  / l.size(), y / l.size());
}

public class Point {
  int x;
  int y;

  Point( int x, int y ) {
    this.x = x;
    this.y = y;
  }
}
&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.local-guru.net,2005:Article/33</id>
    <published>2008-05-11T16:26:00+02:00</published>
    <updated>2008-05-11T16:26:00+02:00</updated>
    <link type="text/html" href="http://www.local-guru.net//blog/2008/5/11/download-bots" rel="alternate"/>
    <author>
      <name>Nikolaus Gradwohl</name>
    </author>
    <title type="html">multitouch</title>
    <category term="multitouch" scheme="http://www.local-guru.net/blog/tag/multitouch"/>
    <content type="html">&lt;p&gt;since i have heard of &lt;a href="http://www.jazzmutant.com/"&gt;lemur&lt;/a&gt; i wanted to try a multitouch display.
i later found &lt;a href="http://www.instructables.com/id/Interactive-Multitouch-Display/"&gt;instructions&lt;/a&gt; on
&lt;a href="http://www.instructables.com/"&gt;instructables.com&lt;/a&gt; showing how to build an DYI-Multitouch Display
using a pictureframe, some infrared leds, a modified webcam and a lcd projector .&lt;/p&gt;

&lt;p&gt;i dont own a lcd projector and so my multitouch display still remained a dream.&lt;/p&gt;

&lt;p&gt;but recently i found a &lt;a href="http://ssandler.wordpress.com/mtmini/"&gt;blog-post&lt;/a&gt; showing how to build
a "moultitouch light". This version doesnt use infrared but normal light and it doesnt display
the picture on the display but uses a second monitor.&lt;/p&gt;

&lt;p&gt;i quickly hacked together my own version &lt;img src="http://www.local-guru.net/img/guru/multitouch.jpg" title="multitouch" alt="multitouch" /&gt;&lt;/p&gt;

&lt;p&gt;and managed to get some of the demos running :-) - VEEEEERY COOL&lt;/p&gt;

&lt;p&gt;(&lt;em&gt;aehm&lt;/em&gt; - yes the penguin is a webcam)&lt;/p&gt;

&lt;p&gt;i didnt manage to get the flash demos running on linux but the smoke demo and the calibration tool
are working now&lt;/p&gt;
</content>
  </entry>
</feed>

