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: