diff options
-rw-r--r-- | CHANGES.LOG | 7 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | Makefile.in | 2 | ||||
-rwxr-xr-x | pix_opencv_of_bm-help.pd | 212 | ||||
-rwxr-xr-x | pix_opencv_of_bm.cc | 564 | ||||
-rwxr-xr-x | pix_opencv_of_bm.h | 106 | ||||
-rwxr-xr-x | pix_opencv_of_hs-help.pd | 202 | ||||
-rwxr-xr-x | pix_opencv_of_hs.cc | 519 | ||||
-rwxr-xr-x | pix_opencv_of_hs.h | 103 | ||||
-rwxr-xr-x | pix_opencv_of_lk-help.pd | 196 | ||||
-rwxr-xr-x | pix_opencv_of_lk.cc | 499 | ||||
-rwxr-xr-x | pix_opencv_of_lk.h | 99 |
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 @@ -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 |