aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.LOG7
-rw-r--r--Makefile4
-rw-r--r--Makefile.in2
-rwxr-xr-xpix_opencv_of_bm-help.pd212
-rwxr-xr-xpix_opencv_of_bm.cc564
-rwxr-xr-xpix_opencv_of_bm.h106
-rwxr-xr-xpix_opencv_of_hs-help.pd202
-rwxr-xr-xpix_opencv_of_hs.cc519
-rwxr-xr-xpix_opencv_of_hs.h103
-rwxr-xr-xpix_opencv_of_lk-help.pd196
-rwxr-xr-xpix_opencv_of_lk.cc499
-rwxr-xr-xpix_opencv_of_lk.h99
12 files changed, 2510 insertions, 3 deletions
diff --git a/CHANGES.LOG b/CHANGES.LOG
index 5162fcb..91229c4 100644
--- a/CHANGES.LOG
+++ b/CHANGES.LOG
@@ -1,3 +1,10 @@
+version 0.2-rc5 ( codename THANKSIBIT )
+
+* added optical flow objects :
+Block Matching : pix_opencv_of_bm
+Horn and Schunck : pix_opencv_of_hs
+Lucas/Kanade : pix_opencv_of_lk
+
version 0.2-rc4 ( codename BALTANIK )
* fixed colorspace problems for mac ppc
diff --git a/Makefile b/Makefile
index d47e444..3934dce 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-PD_DIR = /usr/local/pd
+PD_DIR = /Software/pd-svn/trunk/pd
GEM_DIR = /usr/local/pd/gem
GEM_OPENCV_VERSION = 0.2
@@ -29,7 +29,7 @@ endif
.SUFFIXES = $(EXTENSION)
-SOURCES = pix_opencv_edge.cc pix_opencv_laplace.cc pix_opencv_morphology.cc pix_opencv_distrans.cc pix_opencv_motempl.cc pix_opencv_haarcascade.cc pix_opencv_contours_boundingrect.cc pix_opencv_bgsubstract.cc pix_opencv_contours_convexity.cc pix_opencv_dft.cc pix_opencv_lk.cc pix_opencv_hist_compare.cc pix_opencv_knear.cc pix_opencv_threshold.cc pix_opencv_floodfill.cc pix_opencv_athreshold.cc pix_opencv_bgstats.cc pix_opencv_camshift.cc pix_opencv_hu_compare.cc pix_opencv_pgh_compare.cc pix_opencv_hough_circles.cc pix_opencv_hough_lines.cc pix_opencv_hu_moments.cc pix_opencv_contours_convexhull.cc pix_opencv_colorfilt.cc
+SOURCES = pix_opencv_edge.cc pix_opencv_laplace.cc pix_opencv_morphology.cc pix_opencv_distrans.cc pix_opencv_motempl.cc pix_opencv_haarcascade.cc pix_opencv_contours_boundingrect.cc pix_opencv_bgsubstract.cc pix_opencv_contours_convexity.cc pix_opencv_dft.cc pix_opencv_lk.cc pix_opencv_hist_compare.cc pix_opencv_knear.cc pix_opencv_threshold.cc pix_opencv_floodfill.cc pix_opencv_athreshold.cc pix_opencv_bgstats.cc pix_opencv_camshift.cc pix_opencv_hu_compare.cc pix_opencv_pgh_compare.cc pix_opencv_hough_circles.cc pix_opencv_hough_lines.cc pix_opencv_hu_moments.cc pix_opencv_contours_convexhull.cc pix_opencv_colorfilt.cc pix_opencv_of_bm.cc pix_opencv_of_hs.cc pix_opencv_of_lk.cc
SOURCES_OPT = pix_opencv_surf.cc
all: $(SOURCES:.cc=.$(EXTENSION)) $(SOURCES_OPT:.cc=.$(EXTENSION))
diff --git a/Makefile.in b/Makefile.in
index c7e2985..c3f54e9 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -29,7 +29,7 @@ endif
.SUFFIXES = $(EXTENSION)
-SOURCES = pix_opencv_edge.cc pix_opencv_laplace.cc pix_opencv_morphology.cc pix_opencv_distrans.cc pix_opencv_motempl.cc pix_opencv_haarcascade.cc pix_opencv_contours_boundingrect.cc pix_opencv_bgsubstract.cc pix_opencv_contours_convexity.cc pix_opencv_dft.cc pix_opencv_lk.cc pix_opencv_hist_compare.cc pix_opencv_knear.cc pix_opencv_threshold.cc pix_opencv_floodfill.cc pix_opencv_athreshold.cc pix_opencv_bgstats.cc pix_opencv_camshift.cc pix_opencv_hu_compare.cc pix_opencv_pgh_compare.cc pix_opencv_hough_circles.cc pix_opencv_hough_lines.cc pix_opencv_hu_moments.cc pix_opencv_contours_convexhull.cc pix_opencv_colorfilt.cc
+SOURCES = pix_opencv_edge.cc pix_opencv_laplace.cc pix_opencv_morphology.cc pix_opencv_distrans.cc pix_opencv_motempl.cc pix_opencv_haarcascade.cc pix_opencv_contours_boundingrect.cc pix_opencv_bgsubstract.cc pix_opencv_contours_convexity.cc pix_opencv_dft.cc pix_opencv_lk.cc pix_opencv_hist_compare.cc pix_opencv_knear.cc pix_opencv_threshold.cc pix_opencv_floodfill.cc pix_opencv_athreshold.cc pix_opencv_bgstats.cc pix_opencv_camshift.cc pix_opencv_hu_compare.cc pix_opencv_pgh_compare.cc pix_opencv_hough_circles.cc pix_opencv_hough_lines.cc pix_opencv_hu_moments.cc pix_opencv_contours_convexhull.cc pix_opencv_colorfilt.cc pix_opencv_of_bm.cc pix_opencv_of_hs.cc pix_opencv_of_lk.cc
SOURCES_OPT = @SOURCES_OPT@
all: $(SOURCES:.cc=.$(EXTENSION)) $(SOURCES_OPT:.cc=.$(EXTENSION))
diff --git a/pix_opencv_of_bm-help.pd b/pix_opencv_of_bm-help.pd
new file mode 100755
index 0000000..20a368c
--- /dev/null
+++ b/pix_opencv_of_bm-help.pd
@@ -0,0 +1,212 @@
+#N canvas 1 49 1424 780 10;
+#X obj 977 55 cnv 15 220 70 empty empty empty 20 12 0 14 -195568 -66577
+0;
+#N canvas 189 149 454 304 gemwin 0;
+#X obj 130 218 gemwin;
+#X obj 67 89 outlet;
+#X obj 67 10 inlet;
+#X obj 67 41 route create;
+#X msg 67 70 set destroy;
+#X msg 182 68 set create;
+#X msg 129 165 create \, 1;
+#X msg 205 166 destroy;
+#N canvas 87 154 363 340 Gem.init 0;
+#X obj 112 15 loadbang;
+#X msg 62 93 reset;
+#X obj 49 135 outlet;
+#X msg 107 89 dimen 320 240;
+#X obj 107 51 t b b b;
+#X connect 0 0 4 0;
+#X connect 1 0 2 0;
+#X connect 3 0 2 0;
+#X connect 4 0 3 0;
+#X connect 4 2 1 0;
+#X restore 289 80 pd Gem.init;
+#X obj 128 109 t b b b;
+#X msg 156 138 dimen 320 240;
+#X msg 247 137 frame 5;
+#X connect 2 0 3 0;
+#X connect 3 0 4 0;
+#X connect 3 0 9 0;
+#X connect 3 1 5 0;
+#X connect 3 1 7 0;
+#X connect 4 0 1 0;
+#X connect 5 0 1 0;
+#X connect 6 0 0 0;
+#X connect 7 0 0 0;
+#X connect 9 0 6 0;
+#X connect 9 1 10 0;
+#X connect 9 2 11 0;
+#X connect 10 0 0 0;
+#X connect 11 0 0 0;
+#X restore 984 101 pd gemwin;
+#X msg 984 80 create;
+#X text 987 62 Create window and render;
+#X text 979 189 with a great help from Mateu Batle & Alberto de Rodrigo
+from the iBit foundation.;
+#X text 978 153 the main purpose of this object is to calculate the
+average direction and the intensity of the movement in a video stream.
+;
+#X text 979 213 comments to ydegoyon@gmail.com;
+#X text 979 137 pix_opencv_of_bm : Block Matching optical flow algorithm.
+;
+#X obj 426 308 cnv 15 600 350 empty empty empty 20 12 0 14 -24198 -66577
+0;
+#X obj 455 540 pix_opencv_of_bm;
+#X obj 560 572 unpack f f;
+#X floatatom 646 572 5 0 0 0 - - -;
+#X floatatom 648 592 5 0 0 0 - - -;
+#X text 688 571 largest amplitude of movement;
+#X text 688 592 angle of the block with the maximum movement;
+#X text 686 604 ( in degrees [-180 \, 180]);
+#X floatatom 507 621 5 0 0 0 - - -;
+#X text 549 642 marked with a white arrow on the stream;
+#X text 549 618 average angle of the blocks movement;
+#X text 548 630 ( in degrees [-180 \, 180]);
+#X obj 600 463 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1
+1;
+#X msg 515 461 nightmode \$1;
+#X floatatom 573 332 5 0 0 0 - - -;
+#X msg 463 331 blocksize \$1 \$1;
+#X floatatom 585 355 5 0 0 0 - - -;
+#X floatatom 599 381 5 0 0 0 - - -;
+#X msg 475 354 shiftsize \$1 \$1;
+#X msg 489 380 maxrange \$1 \$1;
+#X floatatom 589 408 5 0 0 0 - - -;
+#X msg 502 406 threshold \$1;
+#X obj 621 492 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
+1;
+#X msg 521 491 useprevious \$1;
+#X text 614 332 set blocksize (width height) ( default 20x20 );
+#X text 625 354 set shiftsize (width height) ( default 20x20 );
+#X text 639 381 set maxrange (width height) ( default 20x10 );
+#X text 643 490 use previous results ( default : 0 \, surprising results
+when on );
+#X msg 505 435 minblocks \$1;
+#X floatatom 592 436 5 0 0 0 - - -;
+#X text 619 464 set nightmode;
+#X text 636 430 minimum nuber of blocks to detect the movement;
+#X text 631 405 threshold value for the detection of movement ( default
+10 );
+#X text 633 443 ( default 10 );
+#X obj 507 -62 gemhead;
+#X obj 454 228 separator;
+#X obj 561 -62 bng 25 250 50 0 load empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 618 113 bng 15 250 50 0 empty empty end_reached 20 7 0 10 -262144
+-1 -1;
+#X floatatom 600 78 5 0 10000 1 frame# - -;
+#X obj 561 -33 openpanel;
+#X msg 561 -13 open \$1;
+#X obj 549 96 pix_film;
+#X msg 567 30 auto \$1;
+#X obj 567 12 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X msg 662 13 colorspace RGBA;
+#X msg 767 13 colorspace RGB;
+#X msg 871 13 colorspace Grey;
+#X obj 662 -15 loadbang;
+#X obj 296 224 separator;
+#X obj 296 250 translateXYZ -2 0 0;
+#X obj 454 253 translateXYZ 2 0 0;
+#N canvas 0 0 450 300 vswitch 0;
+#X obj 144 263 outlet;
+#X obj 36 26 inlet;
+#X obj 260 29 inlet;
+#X obj 36 133 spigot;
+#X obj 260 141 spigot;
+#X obj 119 64 loadbang;
+#X msg 83 93 0;
+#X msg 113 93 1;
+#X msg 296 104 0;
+#X msg 326 104 1;
+#X obj 399 34 inlet;
+#X obj 399 67 select 0;
+#X connect 1 0 3 0;
+#X connect 2 0 4 0;
+#X connect 3 0 0 0;
+#X connect 4 0 0 0;
+#X connect 5 0 7 0;
+#X connect 5 0 8 0;
+#X connect 6 0 3 1;
+#X connect 7 0 3 1;
+#X connect 8 0 4 1;
+#X connect 9 0 4 1;
+#X connect 10 0 11 0;
+#X connect 11 0 7 0;
+#X connect 11 0 8 0;
+#X connect 11 1 9 0;
+#X connect 11 1 6 0;
+#X restore 385 125 pd vswitch;
+#X obj 453 125 tgl 15 0 empty load empty 17 7 0 10 -262144 -1 -1 1
+1;
+#X obj 312 94 pix_video;
+#X obj 315 -64 gemhead;
+#X msg 351 19 device 0;
+#X msg 360 50 driver 1;
+#X msg 337 -35 dialog;
+#X msg 342 -5 device /dev/dv1394-0;
+#X obj 294 659 pix_texture;
+#X obj 294 687 square 2;
+#X obj 455 664 pix_texture;
+#X obj 454 282 pix_resize 320 240;
+#X text 504 146 this object can work on small frames unless you have
+a very powerful machine;
+#X obj 456 688 square 2;
+#X obj 295 201 pix_separator;
+#X obj 454 205 pix_separator;
+#X connect 1 0 2 0;
+#X connect 2 0 1 0;
+#X connect 9 0 69 0;
+#X connect 9 1 16 0;
+#X connect 9 2 10 0;
+#X connect 10 0 11 0;
+#X connect 10 1 12 0;
+#X connect 20 0 21 0;
+#X connect 21 0 9 0;
+#X connect 22 0 23 0;
+#X connect 23 0 9 0;
+#X connect 24 0 26 0;
+#X connect 25 0 27 0;
+#X connect 26 0 9 0;
+#X connect 27 0 9 0;
+#X connect 28 0 29 0;
+#X connect 29 0 9 0;
+#X connect 30 0 31 0;
+#X connect 31 0 9 0;
+#X connect 36 0 9 0;
+#X connect 37 0 36 0;
+#X connect 42 0 49 0;
+#X connect 43 0 58 0;
+#X connect 44 0 47 0;
+#X connect 45 0 46 0;
+#X connect 46 0 49 1;
+#X connect 47 0 48 0;
+#X connect 48 0 49 0;
+#X connect 49 0 59 1;
+#X connect 49 2 45 0;
+#X connect 50 0 49 0;
+#X connect 51 0 50 0;
+#X connect 52 0 49 0;
+#X connect 52 0 61 0;
+#X connect 53 0 49 0;
+#X connect 54 0 49 0;
+#X connect 55 0 52 0;
+#X connect 55 0 51 0;
+#X connect 56 0 57 0;
+#X connect 57 0 67 0;
+#X connect 58 0 70 0;
+#X connect 59 0 73 0;
+#X connect 59 0 74 0;
+#X connect 60 0 59 2;
+#X connect 61 0 59 0;
+#X connect 62 0 61 0;
+#X connect 63 0 61 0;
+#X connect 64 0 61 0;
+#X connect 65 0 61 0;
+#X connect 66 0 61 0;
+#X connect 67 0 68 0;
+#X connect 69 0 72 0;
+#X connect 70 0 9 0;
+#X connect 73 0 56 0;
+#X connect 74 0 43 0;
diff --git a/pix_opencv_of_bm.cc b/pix_opencv_of_bm.cc
new file mode 100755
index 0000000..00eb8fb
--- /dev/null
+++ b/pix_opencv_of_bm.cc
@@ -0,0 +1,564 @@
+
+//
+// GEM - Graphics Environment for Multimedia
+//
+// zmoelnig@iem.kug.ac.at
+//
+// Implementation file
+//
+// Copyright (c) 1997-2000 Mark Danks.
+// Copyright (c) Günther Geiger.
+// Copyright (c) 2001-2002 IOhannes m zmoelnig. forum::für::umläute. IEM
+// Copyright (c) 2002 James Tittle & Chris Clepper
+// For information on usage and redistribution, and for a DISCLAIMER OF ALL
+// WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
+//
+/////////////////////////////////////////////////////////
+
+#include "pix_opencv_of_bm.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_of_bm)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_of_bm
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_of_bm :: pix_opencv_of_bm()
+{
+ comp_xsize=320;
+ comp_ysize=240;
+
+ m_meanout = outlet_new(this->x_obj, &s_anything);
+ m_maxout = outlet_new(this->x_obj, &s_anything);
+
+ x_nightmode=0;
+ x_threshold=10;
+ x_blocksize.width = 20;
+ x_blocksize.height = 20;
+ x_shiftsize.width = 20;
+ x_shiftsize.height = 20;
+ x_maxrange.width = 10;
+ x_maxrange.height = 10;
+ x_useprevious = 0;
+ x_minblocks = 10;
+ x_velsize.width = (comp_xsize-x_blocksize.width)/x_shiftsize.width;
+ x_velsize.height = (comp_ysize-x_blocksize.height)/x_shiftsize.height;
+
+ // initialize font
+ cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 0, 1, 8 );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_of_bm :: ~pix_opencv_of_bm()
+{
+ // Destroy cv_images
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_of_bm :: processRGBAImage(imageStruct &image)
+{
+ int px,py;
+ double meanangle=0.0, meanx=0.0, meany=0.0, maxamp=0.0, maxangle=0.0;
+ int nbblocks=0;
+ CvPoint orig, dest;
+ double angle=0.0;
+ double hypotenuse=0.0;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ x_velsize.width = (comp_xsize-x_blocksize.width)/x_shiftsize.width;
+ x_velsize.height = (comp_ysize-x_blocksize.height)/x_shiftsize.height;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+
+ // Convert to hsv
+ cvCvtColor(rgba, rgb, CV_BGRA2BGR);
+ cvCvtColor(rgb, grey, CV_BGR2GRAY);
+
+ if( x_nightmode )
+ cvZero( rgb );
+
+ cvCalcOpticalFlowBM( prev_grey, grey,
+ x_blocksize, x_shiftsize,
+ x_maxrange, x_useprevious,
+ x_velx, x_vely );
+
+ nbblocks = 0;
+ for( py=0; py<x_velsize.height; py++ )
+ {
+ for( px=0; px<x_velsize.width; px++ )
+ {
+ orig.x = (px*comp_xsize)/x_velsize.width;
+ orig.y = (py*comp_ysize)/x_velsize.height;
+ dest.x = (int)(orig.x + cvGet2D(x_velx, py, px).val[0]);
+ dest.y = (int)(orig.y + cvGet2D(x_vely, py, px).val[0]);
+ angle = -atan2( (double) cvGet2D(x_vely, py, px).val[0], (double) cvGet2D(x_velx, py, px).val[0] );
+ hypotenuse = sqrt( pow(orig.y - dest.y, 2) + pow(orig.x - dest.x, 2) );
+
+ /* Now draw the tips of the arrow. I do some scaling so that the
+ * tips look proportional to the main line of the arrow.
+ */
+ if (hypotenuse >= x_threshold)
+ {
+ cvLine( rgb, orig, dest, CV_RGB(0,255,0), (int)hypotenuse/10, CV_AA, 0 );
+
+ orig.x = (int) (dest.x - (x_shiftsize.width/4) * cos(angle + M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/4) * sin(angle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), (int)hypotenuse/10, CV_AA, 0 );
+ orig.x = (int) (dest.x - (x_shiftsize.width/4) * cos(angle - M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/4) * sin(angle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), (int)hypotenuse/10, CV_AA, 0 );
+ meanx = (meanx*nbblocks+cvGet2D(x_velx, py, px).val[0])/(nbblocks+1);
+ meany = (meanx*nbblocks+cvGet2D(x_vely, py, px).val[0])/(nbblocks+1);
+ if ( hypotenuse > maxamp )
+ {
+ maxamp = hypotenuse;
+ maxangle = angle;
+ }
+ // post( "pdp_opencv_of_bm : block %d : amp : %f : angle : %f", nbblocks, hypotenuse, (angle*180)/M_PI );
+ nbblocks++;
+ }
+
+ }
+ }
+
+ meanangle=-atan2( meany, meanx );
+ // post( "pdp_opencv_of_bm : meanangle : %f", (meanangle*180)/M_PI );
+
+ if ( nbblocks >= x_minblocks )
+ {
+ orig.x = (int) (comp_xsize/2);
+ orig.y = (int) (comp_ysize/2);
+ dest.x = (int) (orig.x+((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*cos(meanangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(meanangle));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (x_shiftsize.width/2) * cos(meanangle + M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/2) * sin(meanangle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (x_shiftsize.width/2) * cos(meanangle - M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/2) * sin(meanangle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ meanangle = (meanangle*180)/M_PI;
+ SETFLOAT(&x_list[0], meanangle);
+ outlet_list( m_meanout, 0, 1, x_list );
+
+ // outputs the amplitude and angle of the maximum movement
+ maxangle = (maxangle*180)/M_PI;
+ SETFLOAT(&x_list[0], maxamp);
+ SETFLOAT(&x_list[1], maxangle);
+ outlet_list( m_maxout, 0, 2, x_list );
+ }
+
+ memcpy( prev_grey->imageData, grey->imageData, image.xsize*image.ysize );
+
+ cvCvtColor(rgb, rgba, CV_BGR2BGRA);
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_of_bm :: processRGBImage(imageStruct &image)
+{
+ int px,py;
+ double meanangle=0.0, meanx=0.0, meany=0.0, maxamp=0.0, maxangle=0.0;
+ int nbblocks=0;
+ CvPoint orig, dest;
+ double angle=0.0;
+ double hypotenuse=0.0;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ x_velsize.width = (comp_xsize-x_blocksize.width)/x_shiftsize.width;
+ x_velsize.height = (comp_ysize-x_blocksize.height)/x_shiftsize.height;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ }
+
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ // Convert to hsv
+ cvCvtColor(rgba, rgb, CV_BGRA2BGR);
+ cvCvtColor(rgb, grey, CV_BGR2GRAY);
+
+ if( x_nightmode )
+ cvZero( rgb );
+
+ cvCalcOpticalFlowBM( prev_grey, grey,
+ x_blocksize, x_shiftsize,
+ x_maxrange, x_useprevious,
+ x_velx, x_vely );
+
+ nbblocks = 0;
+ for( py=0; py<x_velsize.height; py++ )
+ {
+ for( px=0; px<x_velsize.width; px++ )
+ {
+ // post( "pdp_opencv_of_bm : (%d,%d) values (%f,%f)", px, py, velxf, velyf );
+ orig.x = (px*comp_xsize)/x_velsize.width;
+ orig.y = (py*comp_ysize)/x_velsize.height;
+ dest.x = (int)(orig.x + cvGet2D(x_velx, py, px).val[0]);
+ dest.y = (int)(orig.y + cvGet2D(x_vely, py, px).val[0]);
+ angle = -atan2( (double) cvGet2D(x_vely, py, px).val[0], (double) cvGet2D(x_velx, py, px).val[0] );
+ hypotenuse = sqrt( pow(orig.y - dest.y, 2) + pow(orig.x - dest.x, 2) );
+
+ /* Now draw the tips of the arrow. I do some scaling so that the
+ * tips look proportional to the main line of the arrow.
+ */
+ if (hypotenuse >= x_threshold)
+ {
+ cvLine( rgb, orig, dest, CV_RGB(0,255,0), (int)hypotenuse/10, CV_AA, 0 );
+
+ orig.x = (int) (dest.x - (x_shiftsize.width/4) * cos(angle + M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/4) * sin(angle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), (int)hypotenuse/10, CV_AA, 0 );
+ orig.x = (int) (dest.x - (x_shiftsize.width/4) * cos(angle - M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/4) * sin(angle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), (int)hypotenuse/10, CV_AA, 0 );
+ meanx = (meanx*nbblocks+cvGet2D(x_velx, py, px).val[0])/(nbblocks+1);
+ meany = (meanx*nbblocks+cvGet2D(x_vely, py, px).val[0])/(nbblocks+1);
+ if ( hypotenuse > maxamp )
+ {
+ maxamp = hypotenuse;
+ maxangle = angle;
+ }
+ // post( "pdp_opencv_of_bm : block %d : amp : %f : angle : %f", nbblocks, hypotenuse, (angle*180)/M_PI );
+ nbblocks++;
+ }
+
+ }
+ }
+
+ meanangle=-atan2( meany, meanx );
+ // post( "pdp_opencv_of_bm : meanangle : %f", (meanangle*180)/M_PI );
+
+ if ( nbblocks >= x_minblocks )
+ {
+ orig.x = (int) (comp_xsize/2);
+ orig.y = (int) (comp_ysize/2);
+ dest.x = (int) (orig.x+((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*cos(meanangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(meanangle));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (x_shiftsize.width/2) * cos(meanangle + M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/2) * sin(meanangle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (x_shiftsize.width/2) * cos(meanangle - M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/2) * sin(meanangle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ meanangle = (meanangle*180)/M_PI;
+ SETFLOAT(&x_list[0], meanangle);
+ outlet_list( m_meanout, 0, 1, x_list );
+
+ // outputs the amplitude and angle of the maximum movement
+ maxangle = (maxangle*180)/M_PI;
+ SETFLOAT(&x_list[0], maxamp);
+ SETFLOAT(&x_list[1], maxangle);
+ outlet_list( m_maxout, 0, 2, x_list );
+ }
+
+ memcpy( prev_grey->imageData, grey->imageData, image.xsize*image.ysize );
+
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_of_bm :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_of_bm : yuv format not supported" );
+}
+
+void pix_opencv_of_bm :: processGrayImage(imageStruct &image)
+{
+ int px,py;
+ double meanangle=0.0, meanx=0.0, meany=0.0, maxamp=0.0, maxangle=0.0;
+ int nbblocks=0;
+ CvPoint orig, dest;
+ double angle=0.0;
+ double hypotenuse=0.0;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ x_velsize.width = (comp_xsize-x_blocksize.width)/x_shiftsize.width;
+ x_velsize.height = (comp_ysize-x_blocksize.height)/x_shiftsize.height;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ }
+
+ memcpy( grey->imageData, image.data, image.xsize*image.ysize );
+
+ if( x_nightmode )
+ cvZero( grey );
+
+ cvCalcOpticalFlowBM( prev_grey, grey,
+ x_blocksize, x_shiftsize,
+ x_maxrange, x_useprevious,
+ x_velx, x_vely );
+
+ nbblocks = 0;
+ for( py=0; py<x_velsize.height; py++ )
+ {
+ for( px=0; px<x_velsize.width; px++ )
+ {
+ // post( "pdp_opencv_of_bm : (%d,%d) values (%f,%f)", px, py, velxf, velyf );
+ orig.x = (px*comp_xsize)/x_velsize.width;
+ orig.y = (py*comp_ysize)/x_velsize.height;
+ dest.x = (int)(orig.x + cvGet2D(x_velx, py, px).val[0]);
+ dest.y = (int)(orig.y + cvGet2D(x_vely, py, px).val[0]);
+ angle = -atan2( (double) cvGet2D(x_vely, py, px).val[0], (double) cvGet2D(x_velx, py, px).val[0] );
+ hypotenuse = sqrt( pow(orig.y - dest.y, 2) + pow(orig.x - dest.x, 2) );
+
+ /* Now draw the tips of the arrow. I do some scaling so that the
+ * tips look proportional to the main line of the arrow.
+ */
+ if (hypotenuse >= x_threshold)
+ {
+ cvLine( grey, orig, dest, CV_RGB(0,255,0), (int)hypotenuse/10, CV_AA, 0 );
+
+ orig.x = (int) (dest.x - (x_shiftsize.width/4) * cos(angle + M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/4) * sin(angle + M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(0,0,255), (int)hypotenuse/10, CV_AA, 0 );
+ orig.x = (int) (dest.x - (x_shiftsize.width/4) * cos(angle - M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/4) * sin(angle - M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(0,0,255), (int)hypotenuse/10, CV_AA, 0 );
+ meanx = (meanx*nbblocks+cvGet2D(x_velx, py, px).val[0])/(nbblocks+1);
+ meany = (meanx*nbblocks+cvGet2D(x_vely, py, px).val[0])/(nbblocks+1);
+ if ( hypotenuse > maxamp )
+ {
+ maxamp = hypotenuse;
+ maxangle = angle;
+ }
+ // post( "pdp_opencv_of_bm : block %d : amp : %f : angle : %f", nbblocks, hypotenuse, (angle*180)/M_PI );
+ nbblocks++;
+ }
+
+ }
+ }
+
+ meanangle=-atan2( meany, meanx );
+ // post( "pdp_opencv_of_bm : meanangle : %f", (meanangle*180)/M_PI );
+
+ if ( nbblocks >= x_minblocks )
+ {
+ orig.x = (int) (comp_xsize/2);
+ orig.y = (int) (comp_ysize/2);
+ dest.x = (int) (orig.x+((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*cos(meanangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(meanangle));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (x_shiftsize.width/2) * cos(meanangle + M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/2) * sin(meanangle + M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (x_shiftsize.width/2) * cos(meanangle - M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/2) * sin(meanangle - M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ meanangle = (meanangle*180)/M_PI;
+ SETFLOAT(&x_list[0], meanangle);
+ outlet_list( m_meanout, 0, 1, x_list );
+
+ // outputs the amplitude and angle of the maximum movement
+ maxangle = (maxangle*180)/M_PI;
+ SETFLOAT(&x_list[0], maxamp);
+ SETFLOAT(&x_list[1], maxangle);
+ outlet_list( m_maxout, 0, 2, x_list );
+ }
+
+ memcpy( prev_grey->imageData, grey->imageData, image.xsize*image.ysize );
+
+ memcpy( image.data, grey->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_of_bm :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_bm::nightModeMessCallback,
+ gensym("nightmode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_bm::tresholdMessCallback,
+ gensym("threshold"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_bm::blocksizeMessCallback,
+ gensym("blocksize"), A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_bm::shiftsizeMessCallback,
+ gensym("shiftsize"), A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_bm::maxrangeMessCallback,
+ gensym("maxrange"), A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_bm::usePreviousMessCallback,
+ gensym("useprevious"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_bm::minBlocksMessCallback,
+ gensym("minblocks"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_of_bm :: nightModeMessCallback(void *data, t_floatarg nightmode)
+{
+ GetMyClass(data)->nightModeMess((float)nightmode);
+}
+
+void pix_opencv_of_bm :: tresholdMessCallback(void *data, t_floatarg threshold)
+{
+ GetMyClass(data)->tresholdMess((float)threshold);
+}
+
+void pix_opencv_of_bm :: blocksizeMessCallback(void *data, t_floatarg fwidth, t_floatarg fheight)
+{
+ GetMyClass(data)->blocksizeMess((float)fwidth, (float)fheight);
+}
+
+void pix_opencv_of_bm :: shiftsizeMessCallback(void *data, t_floatarg fwidth, t_floatarg fheight)
+{
+ GetMyClass(data)->shiftsizeMess((float)fwidth, (float)fheight);
+}
+
+void pix_opencv_of_bm :: maxrangeMessCallback(void *data, t_floatarg fwidth, t_floatarg fheight)
+{
+ GetMyClass(data)->maxrangeMess((float)fwidth, (float)fheight);
+}
+
+void pix_opencv_of_bm :: usePreviousMessCallback(void *data, t_floatarg previous)
+{
+ GetMyClass(data)->usePreviousMess((float)previous);
+}
+
+void pix_opencv_of_bm :: minBlocksMessCallback(void *data, t_floatarg minblocks)
+{
+ GetMyClass(data)->minBlocksMess((float)minblocks);
+}
+
+void pix_opencv_of_bm :: nightModeMess(float nightmode)
+{
+ if ( ( (int)nightmode==0 ) || ( (int)nightmode==1 ) ) x_nightmode = (int)nightmode;
+}
+
+void pix_opencv_of_bm :: tresholdMess(float threshold)
+{
+ if ( (int)threshold>0 ) x_threshold = (int)threshold;
+}
+
+void pix_opencv_of_bm :: blocksizeMess(float fwidth, float fheight)
+{
+ if (fwidth>=5.0) x_blocksize.width = (int)fwidth;
+ if (fheight>=5.0) x_blocksize.height = (int)fheight;
+
+ x_velsize.width = (comp_xsize-x_blocksize.width)/x_shiftsize.width;
+ x_velsize.height = (comp_ysize-x_blocksize.height)/x_shiftsize.height;
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+
+}
+
+void pix_opencv_of_bm :: shiftsizeMess(float fwidth, float fheight)
+{
+ if (fwidth>=5.0) x_shiftsize.width = (int)fwidth;
+ if (fheight>=5.0) x_shiftsize.height = (int)fheight;
+
+ x_velsize.width = (comp_xsize-x_blocksize.width)/x_shiftsize.width;
+ x_velsize.height = (comp_ysize-x_blocksize.height)/x_shiftsize.height;
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+
+}
+
+void pix_opencv_of_bm :: maxrangeMess(float fwidth, float fheight)
+{
+ if (fwidth>=5.0) x_maxrange.width = (int)fwidth;
+ if (fheight>=5.0) x_maxrange.height = (int)fheight;
+}
+
+void pix_opencv_of_bm :: usePreviousMess(float previous)
+{
+ if ((previous==0.0)||(previous==1.0)) x_useprevious = (int)previous;
+}
+
+void pix_opencv_of_bm :: minBlocksMess(float minblocks)
+{
+ if (minblocks>=1.0) x_minblocks = (int)minblocks;
+}
+
diff --git a/pix_opencv_of_bm.h b/pix_opencv_of_bm.h
new file mode 100755
index 0000000..814fdb8
--- /dev/null
+++ b/pix_opencv_of_bm.h
@@ -0,0 +1,106 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Optical Flow block Matching algorithm
+
+ Copyright (c) 1997-1999 Mark Danks. mark@danks.org
+ Copyright (c) Günther Geiger. geiger@epy.co.at
+ Copyright (c) 2001-2002 IOhannes m zmoelnig. forum::für::umläute. IEM. zmoelnig@iem.kug.ac.at
+ Copyright (c) 2002 James Tittle & Chris Clepper
+ For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
+
+-----------------------------------------------------------------*/
+
+#ifndef INCLUDE_PIX_OPENCV_OF_BM_H_
+#define INCLUDE_PIX_OPENCV_OF_BM_H_
+
+#ifndef _EiC
+#ifdef __APPLE__
+#include <OpenCV/OpenCV.h>
+#else
+#include "cv.h"
+#endif
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_of_bm
+
+ Optical Flow block Matching algorithm
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXTERN pix_opencv_of_bm : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_of_bm, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_of_bm();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_of_bm();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ void nightModeMess(float nightmode);
+ void tresholdMess(float threshold);
+ void blocksizeMess(float width, float height);
+ void shiftsizeMess(float width, float height);
+ void maxrangeMess(float width, float height);
+ void usePreviousMess(float previous);
+ void minBlocksMess(float mblocks);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ t_outlet *m_meanout;
+ t_outlet *m_maxout;
+
+ CvSize x_blocksize, x_shiftsize, x_maxrange, x_velsize;
+
+ int x_nightmode;
+ int x_threshold;
+ int x_useprevious;
+ int x_minblocks;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void nightModeMessCallback(void *data, float nightmode);
+ static void tresholdMessCallback(void *data, float threshold);
+ static void blocksizeMessCallback(void *data, float width, float height);
+ static void shiftsizeMessCallback(void *data, float width, float height);
+ static void maxrangeMessCallback(void *data, float width, float height);
+ static void usePreviousMessCallback(void *data, float previous);
+ static void minBlocksMessCallback(void *data, float mblocks);
+
+ // Internal Open CV data
+ IplImage *rgba, *rgb, *grey, *prev_grey, *swap_temp;
+ IplImage *x_velx, *x_vely;
+ CvFont font;
+
+ t_atom x_list[3];
+};
+
+#endif // for header file
diff --git a/pix_opencv_of_hs-help.pd b/pix_opencv_of_hs-help.pd
new file mode 100755
index 0000000..6cb54e0
--- /dev/null
+++ b/pix_opencv_of_hs-help.pd
@@ -0,0 +1,202 @@
+#N canvas 1 49 1424 780 10;
+#X obj 977 55 cnv 15 220 70 empty empty empty 20 12 0 14 -195568 -66577
+0;
+#N canvas 189 149 454 304 gemwin 0;
+#X obj 130 218 gemwin;
+#X obj 67 89 outlet;
+#X obj 67 10 inlet;
+#X obj 67 41 route create;
+#X msg 67 70 set destroy;
+#X msg 182 68 set create;
+#X msg 129 165 create \, 1;
+#X msg 205 166 destroy;
+#N canvas 87 154 363 340 Gem.init 0;
+#X obj 112 15 loadbang;
+#X msg 62 93 reset;
+#X obj 49 135 outlet;
+#X msg 107 89 dimen 320 240;
+#X obj 107 51 t b b b;
+#X connect 0 0 4 0;
+#X connect 1 0 2 0;
+#X connect 3 0 2 0;
+#X connect 4 0 3 0;
+#X connect 4 2 1 0;
+#X restore 289 80 pd Gem.init;
+#X obj 128 109 t b b b;
+#X msg 156 138 dimen 320 240;
+#X msg 247 137 frame 5;
+#X connect 2 0 3 0;
+#X connect 3 0 4 0;
+#X connect 3 0 9 0;
+#X connect 3 1 5 0;
+#X connect 3 1 7 0;
+#X connect 4 0 1 0;
+#X connect 5 0 1 0;
+#X connect 6 0 0 0;
+#X connect 7 0 0 0;
+#X connect 9 0 6 0;
+#X connect 9 1 10 0;
+#X connect 9 2 11 0;
+#X connect 10 0 0 0;
+#X connect 11 0 0 0;
+#X restore 983 98 pd gemwin;
+#X msg 983 77 create;
+#X text 986 59 Create window and render;
+#X obj 427 308 cnv 15 600 300 empty empty empty 20 12 0 14 -24198 -66577
+0;
+#X obj 559 504 unpack f f;
+#X floatatom 645 504 5 0 0 0 - - -;
+#X floatatom 647 524 5 0 0 0 - - -;
+#X text 687 503 largest amplitude of movement;
+#X text 687 524 angle of the block with the maximum movement;
+#X text 685 536 ( in degrees [-180 \, 180]);
+#X floatatom 506 553 5 0 0 0 - - -;
+#X text 548 574 marked with a white arrow on the stream;
+#X text 548 550 average angle of the blocks movement;
+#X text 547 562 ( in degrees [-180 \, 180]);
+#X obj 507 -62 gemhead;
+#X obj 454 228 separator;
+#X obj 561 -62 bng 25 250 50 0 load empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 618 113 bng 15 250 50 0 empty empty end_reached 20 7 0 10 -262144
+-1 -1;
+#X floatatom 600 78 5 0 10000 1 frame# - -;
+#X obj 561 -33 openpanel;
+#X msg 561 -13 open \$1;
+#X obj 549 96 pix_film;
+#X msg 567 30 auto \$1;
+#X obj 567 12 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X msg 662 13 colorspace RGBA;
+#X msg 767 13 colorspace RGB;
+#X msg 871 13 colorspace Grey;
+#X obj 662 -15 loadbang;
+#X obj 296 224 separator;
+#X obj 296 250 translateXYZ -2 0 0;
+#X obj 454 253 translateXYZ 2 0 0;
+#N canvas 0 0 450 300 vswitch 0;
+#X obj 144 263 outlet;
+#X obj 36 26 inlet;
+#X obj 260 29 inlet;
+#X obj 36 133 spigot;
+#X obj 260 141 spigot;
+#X obj 119 64 loadbang;
+#X msg 83 93 0;
+#X msg 113 93 1;
+#X msg 296 104 0;
+#X msg 326 104 1;
+#X obj 399 34 inlet;
+#X obj 399 67 select 0;
+#X connect 1 0 3 0;
+#X connect 2 0 4 0;
+#X connect 3 0 0 0;
+#X connect 4 0 0 0;
+#X connect 5 0 7 0;
+#X connect 5 0 8 0;
+#X connect 6 0 3 1;
+#X connect 7 0 3 1;
+#X connect 8 0 4 1;
+#X connect 9 0 4 1;
+#X connect 10 0 11 0;
+#X connect 11 0 7 0;
+#X connect 11 0 8 0;
+#X connect 11 1 9 0;
+#X connect 11 1 6 0;
+#X restore 385 125 pd vswitch;
+#X obj 453 125 tgl 15 0 empty load empty 17 7 0 10 -262144 -1 -1 0
+1;
+#X obj 312 94 pix_video;
+#X obj 315 -64 gemhead;
+#X msg 351 19 device 0;
+#X msg 360 50 driver 1;
+#X msg 337 -35 dialog;
+#X msg 342 -5 device /dev/dv1394-0;
+#X obj 293 613 pix_texture;
+#X obj 293 641 square 2;
+#X obj 454 618 pix_texture;
+#X obj 454 278 pix_resize 320 240;
+#X text 504 146 this object can work on small frames unless you have
+a very powerful machine;
+#X obj 455 642 square 2;
+#X obj 295 201 pix_separator;
+#X obj 454 205 pix_separator;
+#X obj 454 472 pix_opencv_of_hs;
+#X obj 596 402 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
+1;
+#X msg 511 400 nightmode \$1;
+#X floatatom 557 323 5 0 0 0 - - -;
+#X floatatom 585 347 5 0 0 0 - - -;
+#X msg 498 345 threshold \$1;
+#X obj 620 433 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
+1;
+#X msg 520 432 useprevious \$1;
+#X text 642 431 use previous results ( default : 0 \, surprising results
+when on );
+#X msg 501 374 minblocks \$1;
+#X floatatom 588 375 5 0 0 0 - - -;
+#X text 615 403 set nightmode;
+#X text 632 369 minimum nuber of blocks to detect the movement;
+#X msg 487 320 lambda \$1;
+#X text 600 320 set Lagragian multiplier ( default 1 );
+#X text 629 382 ( default 10 );
+#X text 627 344 threshold value for the detection of movement ( default
+100 );
+#X text 981 178 with a great help from Mateu Batle & Alberto de Rodrigo
+from the iBit foundation.;
+#X text 980 142 the main purpose of this object is to calculate the
+average direction and the intensity of the movement in a video stream.
+;
+#X text 981 202 comments to ydegoyon@gmail.com;
+#X text 980 126 pix_opencv_of_hs : Haron and Schunck optical flow algorithm.
+;
+#X connect 1 0 2 0;
+#X connect 2 0 1 0;
+#X connect 5 0 6 0;
+#X connect 5 1 7 0;
+#X connect 15 0 22 0;
+#X connect 16 0 31 0;
+#X connect 17 0 20 0;
+#X connect 18 0 19 0;
+#X connect 19 0 22 1;
+#X connect 20 0 21 0;
+#X connect 21 0 22 0;
+#X connect 22 0 32 1;
+#X connect 22 2 18 0;
+#X connect 23 0 22 0;
+#X connect 24 0 23 0;
+#X connect 25 0 22 0;
+#X connect 25 0 34 0;
+#X connect 26 0 22 0;
+#X connect 27 0 22 0;
+#X connect 28 0 25 0;
+#X connect 28 0 24 0;
+#X connect 29 0 30 0;
+#X connect 30 0 40 0;
+#X connect 31 0 43 0;
+#X connect 32 0 46 0;
+#X connect 32 0 47 0;
+#X connect 33 0 32 2;
+#X connect 34 0 32 0;
+#X connect 35 0 34 0;
+#X connect 36 0 34 0;
+#X connect 37 0 34 0;
+#X connect 38 0 34 0;
+#X connect 39 0 34 0;
+#X connect 40 0 41 0;
+#X connect 42 0 45 0;
+#X connect 43 0 48 0;
+#X connect 46 0 29 0;
+#X connect 47 0 16 0;
+#X connect 48 0 42 0;
+#X connect 48 1 11 0;
+#X connect 48 2 5 0;
+#X connect 49 0 50 0;
+#X connect 50 0 48 0;
+#X connect 51 0 61 0;
+#X connect 52 0 53 0;
+#X connect 53 0 48 0;
+#X connect 54 0 55 0;
+#X connect 55 0 48 0;
+#X connect 57 0 48 0;
+#X connect 58 0 57 0;
+#X connect 61 0 48 0;
diff --git a/pix_opencv_of_hs.cc b/pix_opencv_of_hs.cc
new file mode 100755
index 0000000..fca8c75
--- /dev/null
+++ b/pix_opencv_of_hs.cc
@@ -0,0 +1,519 @@
+
+//
+// GEM - Graphics Environment for Multimedia
+//
+// zmoelnig@iem.kug.ac.at
+//
+// Implementation file
+//
+// Copyright (c) 1997-2000 Mark Danks.
+// Copyright (c) Günther Geiger.
+// Copyright (c) 2001-2002 IOhannes m zmoelnig. forum::für::umläute. IEM
+// Copyright (c) 2002 James Tittle & Chris Clepper
+// For information on usage and redistribution, and for a DISCLAIMER OF ALL
+// WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
+//
+/////////////////////////////////////////////////////////
+
+#include "pix_opencv_of_hs.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_of_hs)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_of_hs
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_of_hs :: pix_opencv_of_hs()
+{
+ comp_xsize=320;
+ comp_ysize=240;
+
+ m_meanout = outlet_new(this->x_obj, &s_anything);
+ m_maxout = outlet_new(this->x_obj, &s_anything);
+
+ x_nightmode=0;
+ x_threshold=100;
+ x_lambda=1.0;
+ x_useprevious = 0;
+ x_minblocks = 10;
+ x_velsize.width = comp_xsize;
+ x_velsize.height = comp_ysize;
+
+ // initialize font
+ cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 0, 1, 8 );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_of_hs :: ~pix_opencv_of_hs()
+{
+ // Destroy cv_images
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_of_hs :: processRGBAImage(imageStruct &image)
+{
+ int px,py;
+ double meanangle=0.0, meanx=0.0, meany=0.0, maxamp=0.0, maxangle=0.0;
+ int nbblocks=0;
+ CvPoint orig, dest;
+ double angle=0.0;
+ double hypotenuse=0.0;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ x_velsize.width = comp_xsize;
+ x_velsize.height = comp_ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+
+ // Convert to hsv
+ cvCvtColor(rgba, rgb, CV_BGRA2BGR);
+ cvCvtColor(rgb, grey, CV_BGR2GRAY);
+
+ if( x_nightmode )
+ cvZero( rgb );
+
+ cvCalcOpticalFlowHS( prev_grey, grey,
+ x_useprevious,
+ x_velx, x_vely,
+ x_lambda,
+ cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03) );
+
+ nbblocks = 0;
+ for( py=0; py<x_velsize.height; py++ )
+ {
+ for( px=0; px<x_velsize.width; px++ )
+ {
+ orig.x = (px*comp_xsize)/x_velsize.width;
+ orig.y = (py*comp_ysize)/x_velsize.height;
+ dest.x = (int)(orig.x + cvGet2D(x_velx, py, px).val[0]);
+ dest.y = (int)(orig.y + cvGet2D(x_vely, py, px).val[0]);
+ angle = -atan2( (double) cvGet2D(x_vely, py, px).val[0], (double) cvGet2D(x_velx, py, px).val[0] );
+ hypotenuse = sqrt( pow(orig.y - dest.y, 2) + pow(orig.x - dest.x, 2) );
+
+ /* Now draw the tips of the arrow. I do some scaling so that the
+ * tips look proportional to the main line of the arrow.
+ */
+ if (hypotenuse >= x_threshold)
+ {
+ cvLine( rgb, orig, dest, CV_RGB(0,255,0), 1, CV_AA, 0 );
+
+ orig.x = (int) (dest.x - (6) * cos(angle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(angle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ meanx = (meanx*nbblocks+cvGet2D(x_velx, py, px).val[0])/(nbblocks+1);
+ meany = (meanx*nbblocks+cvGet2D(x_vely, py, px).val[0])/(nbblocks+1);
+ if ( hypotenuse > maxamp )
+ {
+ maxamp = hypotenuse;
+ maxangle = angle;
+ }
+ // post( "pdp_opencv_of_bm : block %d : amp : %f : angle : %f", nbblocks, hypotenuse, (angle*180)/M_PI );
+ nbblocks++;
+ }
+
+ }
+ }
+
+ meanangle=-atan2( meany, meanx );
+ // post( "pdp_opencv_of_bm : meanangle : %f", (meanangle*180)/M_PI );
+
+ if ( nbblocks >= x_minblocks )
+ {
+ orig.x = (int) (comp_xsize/2);
+ orig.y = (int) (comp_ysize/2);
+ dest.x = (int) (orig.x+((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*cos(meanangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(meanangle));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ meanangle = (meanangle*180)/M_PI;
+ SETFLOAT(&x_list[0], meanangle);
+ outlet_list( m_meanout, 0, 1, x_list );
+
+ // outputs the amplitude and angle of the maximum movement
+ maxangle = (maxangle*180)/M_PI;
+ SETFLOAT(&x_list[0], maxamp);
+ SETFLOAT(&x_list[1], maxangle);
+ outlet_list( m_maxout, 0, 2, x_list );
+ }
+
+ memcpy( prev_grey->imageData, grey->imageData, image.xsize*image.ysize );
+
+ cvCvtColor(rgb, rgba, CV_BGR2BGRA);
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_of_hs :: processRGBImage(imageStruct &image)
+{
+ int px,py;
+ double meanangle=0.0, meanx=0.0, meany=0.0, maxamp=0.0, maxangle=0.0;
+ int nbblocks=0;
+ CvPoint orig, dest;
+ double angle=0.0;
+ double hypotenuse=0.0;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ x_velsize.width = comp_xsize;
+ x_velsize.height = comp_ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ }
+
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ // Convert to hsv
+ cvCvtColor(rgba, rgb, CV_BGRA2BGR);
+ cvCvtColor(rgb, grey, CV_BGR2GRAY);
+
+ if( x_nightmode )
+ cvZero( rgb );
+
+ cvCalcOpticalFlowHS( prev_grey, grey,
+ x_useprevious,
+ x_velx, x_vely,
+ x_lambda,
+ cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03) );
+
+ nbblocks = 0;
+ for( py=0; py<x_velsize.height; py++ )
+ {
+ for( px=0; px<x_velsize.width; px++ )
+ {
+ // post( "pdp_opencv_of_bm : (%d,%d) values (%f,%f)", px, py, velxf, velyf );
+ orig.x = (px*comp_xsize)/x_velsize.width;
+ orig.y = (py*comp_ysize)/x_velsize.height;
+ dest.x = (int)(orig.x + cvGet2D(x_velx, py, px).val[0]);
+ dest.y = (int)(orig.y + cvGet2D(x_vely, py, px).val[0]);
+ angle = -atan2( (double) cvGet2D(x_vely, py, px).val[0], (double) cvGet2D(x_velx, py, px).val[0] );
+ hypotenuse = sqrt( pow(orig.y - dest.y, 2) + pow(orig.x - dest.x, 2) );
+
+ /* Now draw the tips of the arrow. I do some scaling so that the
+ * tips look proportional to the main line of the arrow.
+ */
+ if (hypotenuse >= x_threshold)
+ {
+ cvLine( rgb, orig, dest, CV_RGB(0,255,0), 1, CV_AA, 0 );
+
+ orig.x = (int) (dest.x - (6) * cos(angle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(angle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ meanx = (meanx*nbblocks+cvGet2D(x_velx, py, px).val[0])/(nbblocks+1);
+ meany = (meanx*nbblocks+cvGet2D(x_vely, py, px).val[0])/(nbblocks+1);
+ if ( hypotenuse > maxamp )
+ {
+ maxamp = hypotenuse;
+ maxangle = angle;
+ }
+ // post( "pdp_opencv_of_bm : block %d : amp : %f : angle : %f", nbblocks, hypotenuse, (angle*180)/M_PI );
+ nbblocks++;
+ }
+
+ }
+ }
+
+ meanangle=-atan2( meany, meanx );
+ // post( "pdp_opencv_of_bm : meanangle : %f", (meanangle*180)/M_PI );
+
+ if ( nbblocks >= x_minblocks )
+ {
+ orig.x = (int) (comp_xsize/2);
+ orig.y = (int) (comp_ysize/2);
+ dest.x = (int) (orig.x+((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*cos(meanangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(meanangle));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ meanangle = (meanangle*180)/M_PI;
+ SETFLOAT(&x_list[0], meanangle);
+ outlet_list( m_meanout, 0, 1, x_list );
+
+ // outputs the amplitude and angle of the maximum movement
+ maxangle = (maxangle*180)/M_PI;
+ SETFLOAT(&x_list[0], maxamp);
+ SETFLOAT(&x_list[1], maxangle);
+ outlet_list( m_maxout, 0, 2, x_list );
+ }
+
+ memcpy( prev_grey->imageData, grey->imageData, image.xsize*image.ysize );
+
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_of_hs :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_of_hs : yuv format not supported" );
+}
+
+void pix_opencv_of_hs :: processGrayImage(imageStruct &image)
+{
+ int px,py;
+ double meanangle=0.0, meanx=0.0, meany=0.0, maxamp=0.0, maxangle=0.0;
+ int nbblocks=0;
+ CvPoint orig, dest;
+ double angle=0.0;
+ double hypotenuse=0.0;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ x_velsize.width = comp_xsize;
+ x_velsize.height = comp_ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ }
+
+ memcpy( grey->imageData, image.data, image.xsize*image.ysize );
+
+ if( x_nightmode )
+ cvZero( grey );
+
+ cvCalcOpticalFlowHS( prev_grey, grey,
+ x_useprevious,
+ x_velx, x_vely,
+ x_lambda,
+ cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03) );
+
+ nbblocks = 0;
+ for( py=0; py<x_velsize.height; py++ )
+ {
+ for( px=0; px<x_velsize.width; px++ )
+ {
+ // post( "pdp_opencv_of_bm : (%d,%d) values (%f,%f)", px, py, velxf, velyf );
+ orig.x = (px*comp_xsize)/x_velsize.width;
+ orig.y = (py*comp_ysize)/x_velsize.height;
+ dest.x = (int)(orig.x + cvGet2D(x_velx, py, px).val[0]);
+ dest.y = (int)(orig.y + cvGet2D(x_vely, py, px).val[0]);
+ angle = -atan2( (double) cvGet2D(x_vely, py, px).val[0], (double) cvGet2D(x_velx, py, px).val[0] );
+ hypotenuse = sqrt( pow(orig.y - dest.y, 2) + pow(orig.x - dest.x, 2) );
+
+ /* Now draw the tips of the arrow. I do some scaling so that the
+ * tips look proportional to the main line of the arrow.
+ */
+ if (hypotenuse >= x_threshold)
+ {
+ cvLine( grey, orig, dest, CV_RGB(0,255,0), 1, CV_AA, 0 );
+
+ orig.x = (int) (dest.x - (6) * cos(angle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle + M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(angle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle - M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ meanx = (meanx*nbblocks+cvGet2D(x_velx, py, px).val[0])/(nbblocks+1);
+ meany = (meanx*nbblocks+cvGet2D(x_vely, py, px).val[0])/(nbblocks+1);
+ if ( hypotenuse > maxamp )
+ {
+ maxamp = hypotenuse;
+ maxangle = angle;
+ }
+ // post( "pdp_opencv_of_bm : block %d : amp : %f : angle : %f", nbblocks, hypotenuse, (angle*180)/M_PI );
+ nbblocks++;
+ }
+
+ }
+ }
+
+ meanangle=-atan2( meany, meanx );
+ // post( "pdp_opencv_of_bm : meanangle : %f", (meanangle*180)/M_PI );
+
+ if ( nbblocks >= x_minblocks )
+ {
+ orig.x = (int) (comp_xsize/2);
+ orig.y = (int) (comp_ysize/2);
+ dest.x = (int) (orig.x+((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*cos(meanangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(meanangle));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle + M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle - M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ meanangle = (meanangle*180)/M_PI;
+ SETFLOAT(&x_list[0], meanangle);
+ outlet_list( m_meanout, 0, 1, x_list );
+
+ // outputs the amplitude and angle of the maximum movement
+ maxangle = (maxangle*180)/M_PI;
+ SETFLOAT(&x_list[0], maxamp);
+ SETFLOAT(&x_list[1], maxangle);
+ outlet_list( m_maxout, 0, 2, x_list );
+ }
+
+ memcpy( prev_grey->imageData, grey->imageData, image.xsize*image.ysize );
+
+ memcpy( image.data, grey->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_of_hs :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_hs::nightModeMessCallback,
+ gensym("nightmode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_hs::tresholdMessCallback,
+ gensym("threshold"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_hs::lambdaMessCallback,
+ gensym("lambda"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_hs::usePreviousMessCallback,
+ gensym("useprevious"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_hs::minBlocksMessCallback,
+ gensym("minblocks"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_of_hs :: nightModeMessCallback(void *data, t_floatarg nightmode)
+{
+ GetMyClass(data)->nightModeMess((float)nightmode);
+}
+
+void pix_opencv_of_hs :: tresholdMessCallback(void *data, t_floatarg threshold)
+{
+ GetMyClass(data)->tresholdMess((float)threshold);
+}
+
+void pix_opencv_of_hs :: lambdaMessCallback(void *data, t_floatarg lambda)
+{
+ GetMyClass(data)->lambdaMess((float)lambda);
+}
+
+void pix_opencv_of_hs :: usePreviousMessCallback(void *data, t_floatarg previous)
+{
+ GetMyClass(data)->usePreviousMess((float)previous);
+}
+
+void pix_opencv_of_hs :: minBlocksMessCallback(void *data, t_floatarg minblocks)
+{
+ GetMyClass(data)->minBlocksMess((float)minblocks);
+}
+
+void pix_opencv_of_hs :: nightModeMess(float nightmode)
+{
+ if ( ( (int)nightmode==0 ) || ( (int)nightmode==1 ) ) x_nightmode = (int)nightmode;
+}
+
+void pix_opencv_of_hs :: tresholdMess(float threshold)
+{
+ if ( (int)threshold>0 ) x_threshold = (int)threshold;
+}
+
+void pix_opencv_of_hs :: lambdaMess(float lambda)
+{
+ if (lambda>0.0) x_lambda = (double)lambda;
+}
+
+void pix_opencv_of_hs :: usePreviousMess(float previous)
+{
+ if ((previous==0.0)||(previous==1.0)) x_useprevious = (int)previous;
+}
+
+void pix_opencv_of_hs :: minBlocksMess(float minblocks)
+{
+ if (minblocks>=1.0) x_minblocks = (int)minblocks;
+}
+
diff --git a/pix_opencv_of_hs.h b/pix_opencv_of_hs.h
new file mode 100755
index 0000000..3152f8c
--- /dev/null
+++ b/pix_opencv_of_hs.h
@@ -0,0 +1,103 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Horn and Schunck Optical Flow algorithm
+
+ Copyright (c) 1997-1999 Mark Danks. mark@danks.org
+ Copyright (c) Günther Geiger. geiger@epy.co.at
+ Copyright (c) 2001-2002 IOhannes m zmoelnig. forum::für::umläute. IEM. zmoelnig@iem.kug.ac.at
+ Copyright (c) 2002 James Tittle & Chris Clepper
+ For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
+
+-----------------------------------------------------------------*/
+
+#ifndef INCLUDE_PIX_OPENCV_OF_HS_H_
+#define INCLUDE_PIX_OPENCV_OF_HS_H_
+
+#ifndef _EiC
+#ifdef __APPLE__
+#include <OpenCV/OpenCV.h>
+#else
+#include "cv.h"
+#endif
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_of_hs
+
+ Horn and Schunck Optical Flow algorithm
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXTERN pix_opencv_of_hs : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_of_hs, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_of_hs();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_of_hs();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ void nightModeMess(float nightmode);
+ void tresholdMess(float threshold);
+ void lambdaMess(float lambda);
+ void usePreviousMess(float previous);
+ void minBlocksMess(float mblocks);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ t_outlet *m_meanout;
+ t_outlet *m_maxout;
+
+ CvSize x_velsize;
+ double x_lambda;
+
+ int x_nightmode;
+ int x_threshold;
+ int x_useprevious;
+ int x_minblocks;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void nightModeMessCallback(void *data, float nightmode);
+ static void tresholdMessCallback(void *data, float threshold);
+ static void lambdaMessCallback(void *data, float lambda);
+ static void usePreviousMessCallback(void *data, float previous);
+ static void minBlocksMessCallback(void *data, float mblocks);
+
+ // Internal Open CV data
+ IplImage *rgba, *rgb, *grey, *prev_grey, *swap_temp;
+ IplImage *x_velx, *x_vely;
+ CvFont font;
+
+ t_atom x_list[3];
+};
+
+#endif // for header file
diff --git a/pix_opencv_of_lk-help.pd b/pix_opencv_of_lk-help.pd
new file mode 100755
index 0000000..d2a3d7b
--- /dev/null
+++ b/pix_opencv_of_lk-help.pd
@@ -0,0 +1,196 @@
+#N canvas 1 49 1424 780 10;
+#X obj 977 55 cnv 15 220 70 empty empty empty 20 12 0 14 -195568 -66577
+0;
+#N canvas 189 149 454 304 gemwin 0;
+#X obj 130 218 gemwin;
+#X obj 67 89 outlet;
+#X obj 67 10 inlet;
+#X obj 67 41 route create;
+#X msg 67 70 set destroy;
+#X msg 182 68 set create;
+#X msg 129 165 create \, 1;
+#X msg 205 166 destroy;
+#N canvas 87 154 363 340 Gem.init 0;
+#X obj 112 15 loadbang;
+#X msg 62 93 reset;
+#X obj 49 135 outlet;
+#X msg 107 89 dimen 320 240;
+#X obj 107 51 t b b b;
+#X connect 0 0 4 0;
+#X connect 1 0 2 0;
+#X connect 3 0 2 0;
+#X connect 4 0 3 0;
+#X connect 4 2 1 0;
+#X restore 289 80 pd Gem.init;
+#X obj 128 109 t b b b;
+#X msg 156 138 dimen 320 240;
+#X msg 247 137 frame 5;
+#X connect 2 0 3 0;
+#X connect 3 0 4 0;
+#X connect 3 0 9 0;
+#X connect 3 1 5 0;
+#X connect 3 1 7 0;
+#X connect 4 0 1 0;
+#X connect 5 0 1 0;
+#X connect 6 0 0 0;
+#X connect 7 0 0 0;
+#X connect 9 0 6 0;
+#X connect 9 1 10 0;
+#X connect 9 2 11 0;
+#X connect 10 0 0 0;
+#X connect 11 0 0 0;
+#X restore 984 101 pd gemwin;
+#X msg 984 80 create;
+#X text 987 62 Create window and render;
+#X obj 426 308 cnv 15 600 300 empty empty empty 20 12 0 14 -24198 -66577
+0;
+#X obj 454 444 pix_opencv_of_bm;
+#X obj 559 476 unpack f f;
+#X floatatom 645 476 5 0 0 0 - - -;
+#X floatatom 647 496 5 0 0 0 - - -;
+#X text 687 475 largest amplitude of movement;
+#X text 687 496 angle of the block with the maximum movement;
+#X text 685 508 ( in degrees [-180 \, 180]);
+#X floatatom 506 525 5 0 0 0 - - -;
+#X text 548 546 marked with a white arrow on the stream;
+#X text 548 522 average angle of the blocks movement;
+#X text 547 534 ( in degrees [-180 \, 180]);
+#X obj 507 -62 gemhead;
+#X obj 454 228 separator;
+#X obj 561 -62 bng 25 250 50 0 load empty empty 0 -6 0 8 -262144 -1
+-1;
+#X obj 618 113 bng 15 250 50 0 empty empty end_reached 20 7 0 10 -262144
+-1 -1;
+#X floatatom 600 78 5 0 10000 1 frame# - -;
+#X obj 561 -33 openpanel;
+#X msg 561 -13 open \$1;
+#X obj 549 96 pix_film;
+#X msg 567 30 auto \$1;
+#X obj 567 12 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X msg 662 13 colorspace RGBA;
+#X msg 767 13 colorspace RGB;
+#X msg 871 13 colorspace Grey;
+#X obj 662 -15 loadbang;
+#X obj 296 224 separator;
+#X obj 296 250 translateXYZ -2 0 0;
+#X obj 454 253 translateXYZ 2 0 0;
+#N canvas 0 0 450 300 vswitch 0;
+#X obj 144 263 outlet;
+#X obj 36 26 inlet;
+#X obj 260 29 inlet;
+#X obj 36 133 spigot;
+#X obj 260 141 spigot;
+#X obj 119 64 loadbang;
+#X msg 83 93 0;
+#X msg 113 93 1;
+#X msg 296 104 0;
+#X msg 326 104 1;
+#X obj 399 34 inlet;
+#X obj 399 67 select 0;
+#X connect 1 0 3 0;
+#X connect 2 0 4 0;
+#X connect 3 0 0 0;
+#X connect 4 0 0 0;
+#X connect 5 0 7 0;
+#X connect 5 0 8 0;
+#X connect 6 0 3 1;
+#X connect 7 0 3 1;
+#X connect 8 0 4 1;
+#X connect 9 0 4 1;
+#X connect 10 0 11 0;
+#X connect 11 0 7 0;
+#X connect 11 0 8 0;
+#X connect 11 1 9 0;
+#X connect 11 1 6 0;
+#X restore 385 125 pd vswitch;
+#X obj 453 125 tgl 15 0 empty load empty 17 7 0 10 -262144 -1 -1 0
+1;
+#X obj 312 94 pix_video;
+#X obj 315 -64 gemhead;
+#X msg 351 19 device 0;
+#X msg 360 50 driver 1;
+#X msg 337 -35 dialog;
+#X msg 342 -5 device /dev/dv1394-0;
+#X obj 293 612 pix_texture;
+#X obj 293 640 square 2;
+#X obj 454 617 pix_texture;
+#X obj 454 282 pix_resize 320 240;
+#X text 504 146 this object can work on small frames unless you have
+a very powerful machine;
+#X obj 455 641 square 2;
+#X obj 295 201 pix_separator;
+#X obj 454 205 pix_separator;
+#X text 977 181 with a great help from Mateu Batle & Alberto de Rodrigo
+from the iBit foundation.;
+#X text 976 145 the main purpose of this object is to calculate the
+average direction and the intensity of the movement in a video stream.
+;
+#X text 977 205 comments to ydegoyon@gmail.com;
+#X text 976 129 pix_opencv_of_lk : Lucas Kanade optical flow algorithm.
+;
+#X obj 600 405 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0
+1;
+#X msg 515 403 nightmode \$1;
+#X floatatom 585 325 5 0 0 0 - - -;
+#X floatatom 589 350 5 0 0 0 - - -;
+#X msg 502 348 threshold \$1;
+#X msg 505 377 minblocks \$1;
+#X floatatom 592 378 5 0 0 0 - - -;
+#X text 619 406 set nightmode;
+#X text 636 372 minimum nuber of blocks to detect the movement;
+#X text 633 385 ( default 10 );
+#X msg 491 323 winsize \$1 \$2;
+#X text 628 322 set window size ( width height ) [ default : 10x10
+];
+#X text 630 346 threshold value for the detection of movement ( default
+100 );
+#X connect 1 0 2 0;
+#X connect 2 0 1 0;
+#X connect 5 0 43 0;
+#X connect 5 1 12 0;
+#X connect 5 2 6 0;
+#X connect 6 0 7 0;
+#X connect 6 1 8 0;
+#X connect 16 0 23 0;
+#X connect 17 0 32 0;
+#X connect 18 0 21 0;
+#X connect 19 0 20 0;
+#X connect 20 0 23 1;
+#X connect 21 0 22 0;
+#X connect 22 0 23 0;
+#X connect 23 0 33 1;
+#X connect 23 2 19 0;
+#X connect 24 0 23 0;
+#X connect 25 0 24 0;
+#X connect 26 0 23 0;
+#X connect 26 0 35 0;
+#X connect 27 0 23 0;
+#X connect 28 0 23 0;
+#X connect 29 0 26 0;
+#X connect 29 0 25 0;
+#X connect 30 0 31 0;
+#X connect 31 0 41 0;
+#X connect 32 0 44 0;
+#X connect 33 0 47 0;
+#X connect 33 0 48 0;
+#X connect 34 0 33 2;
+#X connect 35 0 33 0;
+#X connect 36 0 35 0;
+#X connect 37 0 35 0;
+#X connect 38 0 35 0;
+#X connect 39 0 35 0;
+#X connect 40 0 35 0;
+#X connect 41 0 42 0;
+#X connect 43 0 46 0;
+#X connect 44 0 5 0;
+#X connect 47 0 30 0;
+#X connect 48 0 17 0;
+#X connect 53 0 54 0;
+#X connect 54 0 5 0;
+#X connect 55 0 63 0;
+#X connect 56 0 57 0;
+#X connect 57 0 5 0;
+#X connect 58 0 5 0;
+#X connect 59 0 58 0;
+#X connect 63 0 5 0;
diff --git a/pix_opencv_of_lk.cc b/pix_opencv_of_lk.cc
new file mode 100755
index 0000000..d3c5460
--- /dev/null
+++ b/pix_opencv_of_lk.cc
@@ -0,0 +1,499 @@
+
+//
+// GEM - Graphics Environment for Multimedia
+//
+// zmoelnig@iem.kug.ac.at
+//
+// Implementation file
+//
+// Copyright (c) 1997-2000 Mark Danks.
+// Copyright (c) Günther Geiger.
+// Copyright (c) 2001-2002 IOhannes m zmoelnig. forum::für::umläute. IEM
+// Copyright (c) 2002 James Tittle & Chris Clepper
+// For information on usage and redistribution, and for a DISCLAIMER OF ALL
+// WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
+//
+/////////////////////////////////////////////////////////
+
+#include "pix_opencv_of_lk.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_of_lk)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_of_lk
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_of_lk :: pix_opencv_of_lk()
+{
+ comp_xsize=320;
+ comp_ysize=240;
+
+ m_meanout = outlet_new(this->x_obj, &s_anything);
+ m_maxout = outlet_new(this->x_obj, &s_anything);
+
+ x_nightmode=0;
+ x_threshold=100;
+ x_winsize.width = 10;
+ x_winsize.height = 10;
+ x_minblocks = 10;
+ x_velsize.width = comp_xsize;
+ x_velsize.height = comp_ysize;
+
+ // initialize font
+ cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 0, 1, 8 );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_of_lk :: ~pix_opencv_of_lk()
+{
+ // Destroy cv_images
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_of_lk :: processRGBAImage(imageStruct &image)
+{
+ int px,py;
+ double meanangle=0.0, meanx=0.0, meany=0.0, maxamp=0.0, maxangle=0.0;
+ int nbblocks=0;
+ CvPoint orig, dest;
+ double angle=0.0;
+ double hypotenuse=0.0;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ x_velsize.width = comp_xsize;
+ x_velsize.height = comp_ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+
+ // Convert to hsv
+ cvCvtColor(rgba, rgb, CV_BGRA2BGR);
+ cvCvtColor(rgb, grey, CV_BGR2GRAY);
+
+ if( x_nightmode )
+ cvZero( rgb );
+
+ cvCalcOpticalFlowLK( prev_grey, grey,
+ x_winsize, x_velx, x_vely );
+
+ nbblocks = 0;
+ for( py=0; py<x_velsize.height; py++ )
+ {
+ for( px=0; px<x_velsize.width; px++ )
+ {
+ orig.x = (px*comp_xsize)/x_velsize.width;
+ orig.y = (py*comp_ysize)/x_velsize.height;
+ dest.x = (int)(orig.x + cvGet2D(x_velx, py, px).val[0]);
+ dest.y = (int)(orig.y + cvGet2D(x_vely, py, px).val[0]);
+ angle = -atan2( (double) cvGet2D(x_vely, py, px).val[0], (double) cvGet2D(x_velx, py, px).val[0] );
+ hypotenuse = sqrt( pow(orig.y - dest.y, 2) + pow(orig.x - dest.x, 2) );
+
+ /* Now draw the tips of the arrow. I do some scaling so that the
+ * tips look proportional to the main line of the arrow.
+ */
+ if (hypotenuse >= x_threshold)
+ {
+ cvLine( rgb, orig, dest, CV_RGB(0,255,0), 1, CV_AA, 0 );
+
+ orig.x = (int) (dest.x - (6) * cos(angle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(angle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ meanx = (meanx*nbblocks+cvGet2D(x_velx, py, px).val[0])/(nbblocks+1);
+ meany = (meanx*nbblocks+cvGet2D(x_vely, py, px).val[0])/(nbblocks+1);
+ if ( hypotenuse > maxamp )
+ {
+ maxamp = hypotenuse;
+ maxangle = angle;
+ }
+ // post( "pdp_opencv_of_bm : block %d : amp : %f : angle : %f", nbblocks, hypotenuse, (angle*180)/M_PI );
+ nbblocks++;
+ }
+
+ }
+ }
+
+ meanangle=-atan2( meany, meanx );
+ // post( "pdp_opencv_of_bm : meanangle : %f", (meanangle*180)/M_PI );
+
+ if ( nbblocks >= x_minblocks )
+ {
+ orig.x = (int) (comp_xsize/2);
+ orig.y = (int) (comp_ysize/2);
+ dest.x = (int) (orig.x+((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*cos(meanangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(meanangle));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ meanangle = (meanangle*180)/M_PI;
+ SETFLOAT(&x_list[0], meanangle);
+ outlet_list( m_meanout, 0, 1, x_list );
+
+ // outputs the amplitude and angle of the maximum movement
+ maxangle = (maxangle*180)/M_PI;
+ SETFLOAT(&x_list[0], maxamp);
+ SETFLOAT(&x_list[1], maxangle);
+ outlet_list( m_maxout, 0, 2, x_list );
+ }
+
+ memcpy( prev_grey->imageData, grey->imageData, image.xsize*image.ysize );
+
+ cvCvtColor(rgb, rgba, CV_BGR2BGRA);
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_of_lk :: processRGBImage(imageStruct &image)
+{
+ int px,py;
+ double meanangle=0.0, meanx=0.0, meany=0.0, maxamp=0.0, maxangle=0.0;
+ int nbblocks=0;
+ CvPoint orig, dest;
+ double angle=0.0;
+ double hypotenuse=0.0;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ x_velsize.width = comp_xsize;
+ x_velsize.height = comp_ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ }
+
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ // Convert to hsv
+ cvCvtColor(rgba, rgb, CV_BGRA2BGR);
+ cvCvtColor(rgb, grey, CV_BGR2GRAY);
+
+ if( x_nightmode )
+ cvZero( rgb );
+
+ cvCalcOpticalFlowLK( prev_grey, grey,
+ x_winsize, x_velx, x_vely );
+
+ nbblocks = 0;
+ for( py=0; py<x_velsize.height; py++ )
+ {
+ for( px=0; px<x_velsize.width; px++ )
+ {
+ // post( "pdp_opencv_of_bm : (%d,%d) values (%f,%f)", px, py, velxf, velyf );
+ orig.x = (px*comp_xsize)/x_velsize.width;
+ orig.y = (py*comp_ysize)/x_velsize.height;
+ dest.x = (int)(orig.x + cvGet2D(x_velx, py, px).val[0]);
+ dest.y = (int)(orig.y + cvGet2D(x_vely, py, px).val[0]);
+ angle = -atan2( (double) cvGet2D(x_vely, py, px).val[0], (double) cvGet2D(x_velx, py, px).val[0] );
+ hypotenuse = sqrt( pow(orig.y - dest.y, 2) + pow(orig.x - dest.x, 2) );
+
+ /* Now draw the tips of the arrow. I do some scaling so that the
+ * tips look proportional to the main line of the arrow.
+ */
+ if (hypotenuse >= x_threshold)
+ {
+ cvLine( rgb, orig, dest, CV_RGB(0,255,0), 1, CV_AA, 0 );
+
+ orig.x = (int) (dest.x - (6) * cos(angle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(angle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ meanx = (meanx*nbblocks+cvGet2D(x_velx, py, px).val[0])/(nbblocks+1);
+ meany = (meanx*nbblocks+cvGet2D(x_vely, py, px).val[0])/(nbblocks+1);
+ if ( hypotenuse > maxamp )
+ {
+ maxamp = hypotenuse;
+ maxangle = angle;
+ }
+ // post( "pdp_opencv_of_bm : block %d : amp : %f : angle : %f", nbblocks, hypotenuse, (angle*180)/M_PI );
+ nbblocks++;
+ }
+
+ }
+ }
+
+ meanangle=-atan2( meany, meanx );
+ // post( "pdp_opencv_of_bm : meanangle : %f", (meanangle*180)/M_PI );
+
+ if ( nbblocks >= x_minblocks )
+ {
+ orig.x = (int) (comp_xsize/2);
+ orig.y = (int) (comp_ysize/2);
+ dest.x = (int) (orig.x+((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*cos(meanangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(meanangle));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ meanangle = (meanangle*180)/M_PI;
+ SETFLOAT(&x_list[0], meanangle);
+ outlet_list( m_meanout, 0, 1, x_list );
+
+ // outputs the amplitude and angle of the maximum movement
+ maxangle = (maxangle*180)/M_PI;
+ SETFLOAT(&x_list[0], maxamp);
+ SETFLOAT(&x_list[1], maxangle);
+ outlet_list( m_maxout, 0, 2, x_list );
+ }
+
+ memcpy( prev_grey->imageData, grey->imageData, image.xsize*image.ysize );
+
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_of_lk :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_of_lk : yuv format not supported" );
+}
+
+void pix_opencv_of_lk :: processGrayImage(imageStruct &image)
+{
+ int px,py;
+ double meanangle=0.0, meanx=0.0, meany=0.0, maxamp=0.0, maxangle=0.0;
+ int nbblocks=0;
+ CvPoint orig, dest;
+ double angle=0.0;
+ double hypotenuse=0.0;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ x_velsize.width = comp_xsize;
+ x_velsize.height = comp_ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ }
+
+ memcpy( grey->imageData, image.data, image.xsize*image.ysize );
+
+ if( x_nightmode )
+ cvZero( grey );
+
+ cvCalcOpticalFlowLK( prev_grey, grey,
+ x_winsize, x_velx, x_vely );
+
+ nbblocks = 0;
+ for( py=0; py<x_velsize.height; py++ )
+ {
+ for( px=0; px<x_velsize.width; px++ )
+ {
+ // post( "pdp_opencv_of_bm : (%d,%d) values (%f,%f)", px, py, velxf, velyf );
+ orig.x = (px*comp_xsize)/x_velsize.width;
+ orig.y = (py*comp_ysize)/x_velsize.height;
+ dest.x = (int)(orig.x + cvGet2D(x_velx, py, px).val[0]);
+ dest.y = (int)(orig.y + cvGet2D(x_vely, py, px).val[0]);
+ angle = -atan2( (double) cvGet2D(x_vely, py, px).val[0], (double) cvGet2D(x_velx, py, px).val[0] );
+ hypotenuse = sqrt( pow(orig.y - dest.y, 2) + pow(orig.x - dest.x, 2) );
+
+ /* Now draw the tips of the arrow. I do some scaling so that the
+ * tips look proportional to the main line of the arrow.
+ */
+ if (hypotenuse >= x_threshold)
+ {
+ cvLine( grey, orig, dest, CV_RGB(0,255,0), 1, CV_AA, 0 );
+
+ orig.x = (int) (dest.x - (6) * cos(angle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle + M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(angle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(angle - M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(0,0,255), 1, CV_AA, 0 );
+ meanx = (meanx*nbblocks+cvGet2D(x_velx, py, px).val[0])/(nbblocks+1);
+ meany = (meanx*nbblocks+cvGet2D(x_vely, py, px).val[0])/(nbblocks+1);
+ if ( hypotenuse > maxamp )
+ {
+ maxamp = hypotenuse;
+ maxangle = angle;
+ }
+ // post( "pdp_opencv_of_bm : block %d : amp : %f : angle : %f", nbblocks, hypotenuse, (angle*180)/M_PI );
+ nbblocks++;
+ }
+
+ }
+ }
+
+ meanangle=-atan2( meany, meanx );
+ // post( "pdp_opencv_of_bm : meanangle : %f", (meanangle*180)/M_PI );
+
+ if ( nbblocks >= x_minblocks )
+ {
+ orig.x = (int) (comp_xsize/2);
+ orig.y = (int) (comp_ysize/2);
+ dest.x = (int) (orig.x+((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*cos(meanangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(meanangle));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle + M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(meanangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(meanangle - M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ meanangle = (meanangle*180)/M_PI;
+ SETFLOAT(&x_list[0], meanangle);
+ outlet_list( m_meanout, 0, 1, x_list );
+
+ // outputs the amplitude and angle of the maximum movement
+ maxangle = (maxangle*180)/M_PI;
+ SETFLOAT(&x_list[0], maxamp);
+ SETFLOAT(&x_list[1], maxangle);
+ outlet_list( m_maxout, 0, 2, x_list );
+ }
+
+ memcpy( prev_grey->imageData, grey->imageData, image.xsize*image.ysize );
+
+ memcpy( image.data, grey->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_of_lk :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_lk::nightModeMessCallback,
+ gensym("nightmode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_lk::tresholdMessCallback,
+ gensym("threshold"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_lk::winsizeMessCallback,
+ gensym("winsize"), A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_of_lk::minBlocksMessCallback,
+ gensym("minblocks"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_of_lk :: nightModeMessCallback(void *data, t_floatarg nightmode)
+{
+ GetMyClass(data)->nightModeMess((float)nightmode);
+}
+
+void pix_opencv_of_lk :: tresholdMessCallback(void *data, t_floatarg threshold)
+{
+ GetMyClass(data)->tresholdMess((float)threshold);
+}
+
+void pix_opencv_of_lk :: winsizeMessCallback(void *data, t_floatarg fwidth, t_floatarg fheight)
+{
+ GetMyClass(data)->winsizeMess((float)fwidth, (float)fheight);
+}
+
+void pix_opencv_of_lk :: minBlocksMessCallback(void *data, t_floatarg minblocks)
+{
+ GetMyClass(data)->minBlocksMess((float)minblocks);
+}
+
+void pix_opencv_of_lk :: nightModeMess(float nightmode)
+{
+ if ( ( (int)nightmode==0 ) || ( (int)nightmode==1 ) ) x_nightmode = (int)nightmode;
+}
+
+void pix_opencv_of_lk :: tresholdMess(float threshold)
+{
+ if ( (int)threshold>0 ) x_threshold = (int)threshold;
+}
+
+void pix_opencv_of_lk :: winsizeMess(float fwidth, float fheight)
+{
+ if (fwidth>=5.0) x_winsize.width = (int)fwidth;
+ if (fheight>=5.0) x_winsize.height = (int)fheight;
+}
+
+void pix_opencv_of_lk :: minBlocksMess(float minblocks)
+{
+ if (minblocks>=1.0) x_minblocks = (int)minblocks;
+}
+
diff --git a/pix_opencv_of_lk.h b/pix_opencv_of_lk.h
new file mode 100755
index 0000000..96ccb10
--- /dev/null
+++ b/pix_opencv_of_lk.h
@@ -0,0 +1,99 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Lucas / Kanade Optical Flow algorithm
+
+ Copyright (c) 1997-1999 Mark Danks. mark@danks.org
+ Copyright (c) Günther Geiger. geiger@epy.co.at
+ Copyright (c) 2001-2002 IOhannes m zmoelnig. forum::für::umläute. IEM. zmoelnig@iem.kug.ac.at
+ Copyright (c) 2002 James Tittle & Chris Clepper
+ For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
+
+-----------------------------------------------------------------*/
+
+#ifndef INCLUDE_PIX_OPENCV_OF_LK_H_
+#define INCLUDE_PIX_OPENCV_OF_LK_H_
+
+#ifndef _EiC
+#ifdef __APPLE__
+#include <OpenCV/OpenCV.h>
+#else
+#include "cv.h"
+#endif
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_of_lk
+
+ Lucas / Kanade Optical Flow algorithm
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXTERN pix_opencv_of_lk : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_of_lk, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_of_lk();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_of_lk();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ void nightModeMess(float nightmode);
+ void tresholdMess(float threshold);
+ void winsizeMess(float width, float height);
+ void minBlocksMess(float mblocks);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ t_outlet *m_meanout;
+ t_outlet *m_maxout;
+
+ CvSize x_velsize, x_winsize;
+
+ int x_nightmode;
+ int x_threshold;
+ int x_minblocks;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void nightModeMessCallback(void *data, float nightmode);
+ static void tresholdMessCallback(void *data, float threshold);
+ static void winsizeMessCallback(void *data, float width, float height);
+ static void minBlocksMessCallback(void *data, float mblocks);
+
+ // Internal Open CV data
+ IplImage *rgba, *rgb, *grey, *prev_grey;
+ IplImage *x_velx, *x_vely;
+ CvFont font;
+
+ t_atom x_list[3];
+};
+
+#endif // for header file