HornetsEye
m (→HornetsEye-0.10) |
(Adding PCA-object-recognition example) |
||
Line 6: | Line 6: | ||
* '''minimalistic''': The library is focused on real-time computer vision. Existing libraries are being made used of. | * '''minimalistic''': The library is focused on real-time computer vision. Existing libraries are being made used of. | ||
* '''consistent''':: A non-redundant set of data-types is used. Also the library tries to stay consistent with existing libraries. | * '''consistent''':: A non-redundant set of data-types is used. Also the library tries to stay consistent with existing libraries. | ||
+ | |||
+ | =Example= | ||
+ | The example program performs two-dimensional object recognition with three degrees of freedom. This is a customised algorithm which only works on images showing a single object which can be detected using colour-segmentation. In a controlled environment however this algorithm can be very useful as it is easy to implement. It is also possible to optimise it for real-time applications. | ||
+ | |||
+ | <pre> | ||
+ | #!/usr/bin/ruby | ||
+ | # Detect location and rotation of an object using color-segmentation and | ||
+ | # principal component analysis on resulting binary image. | ||
+ | require 'hornetseye' | ||
+ | require 'matrix' | ||
+ | raise "Syntax: test4.rb [media resource location]" if ARGV.size != 1 | ||
+ | input = Hornetseye::XineInput.new( ARGV[0] ) | ||
+ | # Object is black. | ||
+ | dominant = 0 | ||
+ | frame = 0 | ||
+ | old_eigenvector = Vector[ 1, 0 ] | ||
+ | while input.status? | ||
+ | # Read image. | ||
+ | img = input.read_grey | ||
+ | # Detect center and rotation of object using principal component analysis. | ||
+ | # Assuming object has a principal axis (otherwise this approach fails). | ||
+ | c = 0 | ||
+ | n = 0 | ||
+ | sum = [ 0, 0 ] | ||
+ | squares = [ [ 0, 0 ], [ 0, 0 ] ] | ||
+ | img.each do |v| | ||
+ | if v & 0xE0 == dominant | ||
+ | x = c % img.shape[1] | ||
+ | y = c / img.shape[1] | ||
+ | sum[ 0 ] += x | ||
+ | sum[ 1 ] += y | ||
+ | squares[ 0 ][ 0 ] += x * x | ||
+ | squares[ 0 ][ 1 ] += x * y | ||
+ | squares[ 1 ][ 0 ] += y * x | ||
+ | squares[ 1 ][ 1 ] += y * y | ||
+ | n += 1 | ||
+ | end | ||
+ | c += 1 | ||
+ | end | ||
+ | sum = Vector[*sum] | ||
+ | squares = Matrix[*squares] | ||
+ | center = sum * ( 1.0 / n ) | ||
+ | covariance = ( n * squares - | ||
+ | sum.covector.transpose*sum.covector ) / ( n ** 2 ).to_f | ||
+ | # "abs" is needed to deal with numerical errors. | ||
+ | discriminant = ( covariance.trace ** 2 - 4 * covariance.determinant ).abs | ||
+ | # Take smallest eigenvalue. Eigenvalues are "0.5 * ( tr +- dissqrt)" | ||
+ | lambda1 = 0.5 * ( covariance.trace + Math.sqrt( discriminant ) ) | ||
+ | eigenspace = covariance - lambda1 * Matrix.unit( 2 ) | ||
+ | # Compute eigenvector by projecting basis-vectors. | ||
+ | projected1 = eigenspace * Vector[1,0] | ||
+ | projected2 = eigenspace * Vector[0,1] | ||
+ | if projected1.r >= projected2.r | ||
+ | projected = projected1 * ( 1.0 / projected1.r ) | ||
+ | else | ||
+ | projected = projected2 * ( 1.0 / projected2.r ) | ||
+ | end | ||
+ | eigenvector = Vector[ -projected[ 1 ], projected[ 0 ] ] | ||
+ | # Resolv ambiguity by comparing with previous eigenvector. | ||
+ | if old_eigenvector.inner_product( eigenvector ) < 0 | ||
+ | eigenvector = Vector[ projected[ 1 ], -projected[ 0 ] ] | ||
+ | end | ||
+ | gc=Magick::Draw.new | ||
+ | pointer=center+eigenvector*30 | ||
+ | gc.fill_opacity(0) | ||
+ | gc.stroke('red').stroke_width(3) | ||
+ | gc.circle(center[0],center[1],pointer[0],pointer[1]) | ||
+ | gc.line(center[0],center[1],pointer[0],pointer[1]) | ||
+ | result=img.to_magick | ||
+ | gc.draw(result) | ||
+ | result.to_hornetseye.save( ( "%08d" % frame ) + ".jpg" ) | ||
+ | frame += 1 | ||
+ | end | ||
+ | </pre> | ||
+ | {|align="center" | ||
+ | |- | ||
+ | |[[Image:Polygon134.jpg|thumb|320px|135th input frame acquired from the [http://vision.eng.shu.ac.uk/jan/polygon.avi test-video] showing a polygon]]||[[Image:Polyresult134.jpg|thumb|320px|Resulting image indicating position and orientation of the object's principal axis]] | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | Thanks to [http://www.mach.uni-karlsruhe.de/seite10513.php Prof. Dr.-Ing. Christoph Stiller] for pointing out this algorithm. | ||
=Downloads= | =Downloads= | ||
==HornetsEye-0.10== | ==HornetsEye-0.10== | ||
− | [[Image:Hornetseye.jpg|48px]] '''Download [http://rubyforge.org/frs/?group_id=2714 HornetsEye-0.10] released on February 1st 2007''' | + | * [[Image:Hornetseye.jpg|48px]] '''Download [http://rubyforge.org/frs/?group_id=2714 HornetsEye-0.10] released on February 1st 2007''' |
+ | * [http://vision.eng.shu.ac.uk/jan/polygon.avi test-video with polygon] | ||
===Release Notes=== | ===Release Notes=== |
Revision as of 19:28, 5 March 2007
Contents |
Introduction
HornetsEye is a Ruby-extension for real-time computer vision under GNU/Linux offering interfaces to do image- and video-I/O with RMagick, Xine, firewire digital camera (DC1394) and video for Linux (V4L).
HornetsEye also is an attempt to use the Mimas library and create a minimalistic and consistent real-time computer vision library.
- minimalistic: The library is focused on real-time computer vision. Existing libraries are being made used of.
- consistent:: A non-redundant set of data-types is used. Also the library tries to stay consistent with existing libraries.
Example
The example program performs two-dimensional object recognition with three degrees of freedom. This is a customised algorithm which only works on images showing a single object which can be detected using colour-segmentation. In a controlled environment however this algorithm can be very useful as it is easy to implement. It is also possible to optimise it for real-time applications.
#!/usr/bin/ruby # Detect location and rotation of an object using color-segmentation and # principal component analysis on resulting binary image. require 'hornetseye' require 'matrix' raise "Syntax: test4.rb [media resource location]" if ARGV.size != 1 input = Hornetseye::XineInput.new( ARGV[0] ) # Object is black. dominant = 0 frame = 0 old_eigenvector = Vector[ 1, 0 ] while input.status? # Read image. img = input.read_grey # Detect center and rotation of object using principal component analysis. # Assuming object has a principal axis (otherwise this approach fails). c = 0 n = 0 sum = [ 0, 0 ] squares = [ [ 0, 0 ], [ 0, 0 ] ] img.each do |v| if v & 0xE0 == dominant x = c % img.shape[1] y = c / img.shape[1] sum[ 0 ] += x sum[ 1 ] += y squares[ 0 ][ 0 ] += x * x squares[ 0 ][ 1 ] += x * y squares[ 1 ][ 0 ] += y * x squares[ 1 ][ 1 ] += y * y n += 1 end c += 1 end sum = Vector[*sum] squares = Matrix[*squares] center = sum * ( 1.0 / n ) covariance = ( n * squares - sum.covector.transpose*sum.covector ) / ( n ** 2 ).to_f # "abs" is needed to deal with numerical errors. discriminant = ( covariance.trace ** 2 - 4 * covariance.determinant ).abs # Take smallest eigenvalue. Eigenvalues are "0.5 * ( tr +- dissqrt)" lambda1 = 0.5 * ( covariance.trace + Math.sqrt( discriminant ) ) eigenspace = covariance - lambda1 * Matrix.unit( 2 ) # Compute eigenvector by projecting basis-vectors. projected1 = eigenspace * Vector[1,0] projected2 = eigenspace * Vector[0,1] if projected1.r >= projected2.r projected = projected1 * ( 1.0 / projected1.r ) else projected = projected2 * ( 1.0 / projected2.r ) end eigenvector = Vector[ -projected[ 1 ], projected[ 0 ] ] # Resolv ambiguity by comparing with previous eigenvector. if old_eigenvector.inner_product( eigenvector ) < 0 eigenvector = Vector[ projected[ 1 ], -projected[ 0 ] ] end gc=Magick::Draw.new pointer=center+eigenvector*30 gc.fill_opacity(0) gc.stroke('red').stroke_width(3) gc.circle(center[0],center[1],pointer[0],pointer[1]) gc.line(center[0],center[1],pointer[0],pointer[1]) result=img.to_magick gc.draw(result) result.to_hornetseye.save( ( "%08d" % frame ) + ".jpg" ) frame += 1 end
Thanks to Prof. Dr.-Ing. Christoph Stiller for pointing out this algorithm.
Downloads
HornetsEye-0.10
- 48px Download HornetsEye-0.10 released on February 1st 2007
- test-video with polygon
Release Notes
See http://www.wedesoft.demon.co.uk/hornetseye-api/ for installation instructions.
Change log
- Made display method accept more element-types.
- Normalisation also works on blank image.
Older releases
See Hornetseye page at Rubyforge for older releases.
See Also
External Links
- Hornetseye homepage
- Hornetseye at Rubyforge
- Ruby programming language