Talk at Sheffield Ruby User Group

Machine Vision made easy with Ruby

I am going to give a presentation about Hornetseye at the Sheffield Ruby User Group (ShRUG). The meeting is on Monday June 14th from 7pm to 9pm at the GIST Lab in Sheffield Workstation. For more details see corresponding page on ShRUG website. Please register here if you want to attend.

I hope people will find the GIST lab. It’s the first side entrance to the Workstation when you walk down the small alley to the right of the building (as seen from Brown street). Also see Google Streetview.

Here is the source code of the main program

#!/usr/bin/env ruby
require 'hornetseye'
require 'x11test'
include Hornetseye
SHAPE = [ 320, 240 ]
DISPLAY = [ 320, 240 ]
SIZE = SHAPE[ 0 ] * SHAPE[ 1 ]
SIGMA = 1.5
ERROR = 1.0 / 32.0
THRESHOLD = 10
SKIP = 10
CAPTION = [ 'pointer' ]
RANGE = 0.001 .. 0.25
TIME = 0.3
ACCEL = 5
SPEED0 = 4
SPEED1 = 20
old_x, old_y = nil, nil
display = X11Display.new
output = ( 0 ... CAPTION.size ).collect { XImageOutput.new }
window = output.collect { |out| X11Window.new display, out, *DISPLAY }
window.zip( CAPTION ).each { |win,cap| win.title = cap }
window.each { |win| win.show }
input = DC1394Input.new '', 0, 0,
        DC1394Input::FORMAT_VGA_NONCOMPRESSED,
        DC1394Input::MODE_320x240_YUV422
index = MultiArray.int( *SHAPE ).indgen!
x, y = index % SHAPE[ 0 ], index / SHAPE[ 0 ]
border = MultiArray.int( *SHAPE ).fill! 1
border[ 1 ... SHAPE[ 0 ] - 1, 1 ... SHAPE[ 1 ] - 1 ] = 0
SKIP.times { input.read }
bg = input.read_sint
t_on, t_off = 0, 0
a = 0
while display.status?
  img = input.read_ubyte
  binary = img - bg <= THRESHOLD
  components = binary.components
  n_components = components.max + 1
  area = components.hist n_components
  mask_area = area.between? RANGE.min * SIZE,
                            RANGE.max * SIZE
  mask_border = components.
    hist_weighted( n_components, border ).eq 0
  mask = mask_area.and mask_border
  map = mask.to_ubyte.integral * mask.to_ubyte
  target = components.map map
  output[ 0 ].write target.normalise( 0 .. 127 ) + img / 2
  n_targets = target.max + 1
  t = Time.new.to_f
  if n_targets == 2
    sum_target = target.sum.to_f
    x_target = x.mask( target.to_bool ).sum / sum_target
    y_target = y.mask( target.to_bool ).sum / sum_target
    if old_x and old_y
      d_x, d_y = old_x - x_target, old_y - y_target
      if d_x.abs + d_y.abs < ACCEL
        d_x *= SPEED0
        d_y *= SPEED0
      else
        d_x *= SPEED1
        d_y *= SPEED1
      end
      display.
        fake_relative_motion_event d_x.to_i, d_y.to_i, 0
    else
      t_on = t
    end
    if t_off + TIME >= t
      display.fake_button_event 1, true, 0
      t_off = -1
    end
  else
    x_target, y_target = nil, nil
    if old_x and old_y
      display.fake_button_event 1, false, 0 if t_off == -1
      t_off = t
    end
    if t_on + TIME >= t
      display.fake_button_event 1, true, 0
      display.fake_button_event 1, false, 100
      t_on = 0
    end
  end
  old_x, old_y = x_target, y_target
  display.processEvents
end

And below follows the source code of the Ruby extension for adding pointer control to the HornetsEye library

#include <X11/extensions/XTest.h>
#define HAVE_X11
#include "rubytools.hh"
#include "x11display.hh"

using namespace Hornetseye;

VALUE wrapFakeButtonEvent( VALUE rbSelf,
                           VALUE rbButton,
                           VALUE rbPress,
                           VALUE rbDelay )
{
  Display *display = XOpenDisplay( NULL );
  XTestFakeButtonEvent( display, NUM2INT( rbButton ),
                        rbPress != Qfalse ? True : False,
                        NUM2INT( rbDelay ) );
  XCloseDisplay( display );
  return rbSelf;
}

VALUE wrapFakeRelativeMotionEvent( VALUE rbSelf,
                                   VALUE rbX,
                                   VALUE rbY,
                                   VALUE rbDelay )
{
  Display *display = XOpenDisplay( NULL );
  XTestFakeRelativeMotionEvent( display,
                                NUM2INT( rbX ),
                                NUM2INT( rbY ),
                                NUM2INT( rbDelay ) );
  XCloseDisplay( display );
  return rbSelf;
}

extern "C" {

  void Init_x11test(void)
  {
    rb_require( "hornetseye" );
    VALUE mHornetseye = rb_define_module( "Hornetseye" );
    VALUE cX11Display =
      rb_define_class_under( mHornetseye, "X11Display", rb_cObject );
    rb_define_method( cX11Display, "fake_button_event",
                      RUBY_METHOD_FUNC( wrapFakeButtonEvent ), 3 );
    rb_define_method( cX11Display, "fake_relative_motion_event",
                      RUBY_METHOD_FUNC( wrapFakeRelativeMotionEvent ),
                      3 );
  }

}

See also:

Patent Absurdity

Patent Absurdity movie poster

The Free Software Foundation has released a short film about software patents called Patent Absurdity. The film features interviews with Eben Moglen, Karen Sandler, Richard Stallman, Jesse Vincent, and other people involved with the Bilski case.

The Bilski case hopefully will help putting a stop to the rampant practise of patenting applications of mathematics, algorithms, and other abstract concepts to real life problems. To freely quote Bruce Perens (panel discussion at WSIS in Tunis): Mathematics is a field that is discovered - not invented!

See also:

Using Twinkle for VoIP

Twinkle is my favourite software for making VoIP calls (under GNU/Linux). Here is how you configure it if you have an account with VoIPTalk.

Configuring the user

Enter your user details. Note that the SIP password is different from the password to log in on the VoIPTalk website. Log in on the VoIPTalk website and click on VoIPtalk ID to view VoIPtalk (SIP) password. User

SIP server

Here you enter the details for the outbound proxy. SIP server

NAT

For firewall traversal you need to specify a STUN server. NAT

Alternatively you can set up port forwarding on your router. Then you don’t need to specify a STUN server. In any case make sure that your firewall doesn’t block SIP traffic. You can call sip:905@voiptalk.org in order to test whether the configuration works.

I recommend using G.711 u-law as preferred audio codec.

Update:

In Kubuntu 10.04 you have to set all audio devices (input, output, and ringtone) to ALSA/Default.

There is a freeware VoIP client for Microsoft Windows called X-Lite.

See also:

Flying in sand

If you are worried about the ash cloud of the current volcano eruption of Iceland, just think about more severe situations such as helicopter landings in the desert. Sand is essentially hard glass (and it melts in turbines of aircrafts leaving a nasty residue). Here’s a video of a helicopter landing in Kuwait.

Helicopter landing in Kuwait

See also:

Video of RubyConf 2009 presentation about computer vision

I gave a talk titled Computer vision using Ruby and libJIT at the RubyConf 2009 in San Francisco. Confreaks has provided the video now.

Computer vision using Ruby and libJIT

You can download the talk from this webpage. The presentation slides are available here.

Let me know if you have any questions.

See also: