aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Blob.hpp56
-rw-r--r--src/Makefile.am129
-rw-r--r--src/blobtrack.h121
-rw-r--r--src/pix_opencv.cc38
-rw-r--r--src/pix_opencv_athreshold.cc261
-rw-r--r--src/pix_opencv_athreshold.h95
-rw-r--r--src/pix_opencv_backgroundsubtractor.cc374
-rw-r--r--src/pix_opencv_backgroundsubtractor.h96
-rw-r--r--src/pix_opencv_bgstats.cc251
-rw-r--r--src/pix_opencv_bgstats.h100
-rw-r--r--src/pix_opencv_bgsubstract.cc258
-rw-r--r--src/pix_opencv_bgsubstract.h86
-rw-r--r--src/pix_opencv_blobtrack.cc693
-rw-r--r--src/pix_opencv_blobtrack.h106
-rw-r--r--src/pix_opencv_calibration.cc547
-rw-r--r--src/pix_opencv_calibration.h120
-rw-r--r--src/pix_opencv_camshift.cc450
-rw-r--r--src/pix_opencv_camshift.h110
-rw-r--r--src/pix_opencv_clahe.cc171
-rw-r--r--src/pix_opencv_clahe.h84
-rw-r--r--src/pix_opencv_colorfilt.cc299
-rw-r--r--src/pix_opencv_colorfilt.h103
-rw-r--r--src/pix_opencv_contours.cc591
-rw-r--r--src/pix_opencv_contours.h89
-rw-r--r--src/pix_opencv_contours_boundingrect.cc772
-rw-r--r--src/pix_opencv_contours_boundingrect.h124
-rw-r--r--src/pix_opencv_contours_convexhull.cc582
-rw-r--r--src/pix_opencv_contours_convexhull.h88
-rw-r--r--src/pix_opencv_contours_convexity.cc685
-rw-r--r--src/pix_opencv_contours_convexity.h88
-rw-r--r--src/pix_opencv_dft.cc462
-rw-r--r--src/pix_opencv_dft.h88
-rw-r--r--src/pix_opencv_distrans.cc420
-rw-r--r--src/pix_opencv_distrans.h86
-rw-r--r--src/pix_opencv_edge.cc234
-rw-r--r--src/pix_opencv_edge.h82
-rw-r--r--src/pix_opencv_facetracker.cc302
-rw-r--r--src/pix_opencv_facetracker.h107
-rw-r--r--src/pix_opencv_findchessboardcorners.cc220
-rw-r--r--src/pix_opencv_findchessboardcorners.h98
-rw-r--r--src/pix_opencv_floodfill.cc442
-rw-r--r--src/pix_opencv_floodfill.h111
-rw-r--r--src/pix_opencv_haarcascade.cc526
-rw-r--r--src/pix_opencv_haarcascade.h113
-rw-r--r--src/pix_opencv_hist_compare.cc279
-rw-r--r--src/pix_opencv_hist_compare.h88
-rw-r--r--src/pix_opencv_hough_circles.cc315
-rw-r--r--src/pix_opencv_hough_circles.h102
-rw-r--r--src/pix_opencv_hough_lines.cc600
-rw-r--r--src/pix_opencv_hough_lines.h110
-rw-r--r--src/pix_opencv_hu_compare.cc468
-rw-r--r--src/pix_opencv_hu_compare.h93
-rw-r--r--src/pix_opencv_hu_moments.cc207
-rw-r--r--src/pix_opencv_hu_moments.h86
-rw-r--r--src/pix_opencv_knear.cc502
-rw-r--r--src/pix_opencv_knear.h108
-rw-r--r--src/pix_opencv_laplace.cc228
-rw-r--r--src/pix_opencv_laplace.h83
-rw-r--r--src/pix_opencv_lk.cc1170
-rw-r--r--src/pix_opencv_lk.h132
-rw-r--r--src/pix_opencv_matchshape.cc226
-rw-r--r--src/pix_opencv_matchshape.h74
-rw-r--r--src/pix_opencv_morphology.cc291
-rw-r--r--src/pix_opencv_morphology.h87
-rw-r--r--src/pix_opencv_motempl.cc708
-rw-r--r--src/pix_opencv_motempl.h132
-rw-r--r--src/pix_opencv_of_bm.cc326
-rw-r--r--src/pix_opencv_of_bm.h101
-rw-r--r--src/pix_opencv_of_hs.cc531
-rw-r--r--src/pix_opencv_of_hs.h100
-rw-r--r--src/pix_opencv_of_lk.cc516
-rw-r--r--src/pix_opencv_of_lk.h96
-rw-r--r--src/pix_opencv_opticalflow.cc229
-rw-r--r--src/pix_opencv_opticalflow.h96
-rw-r--r--src/pix_opencv_patreco.h95
-rw-r--r--src/pix_opencv_pgh_compare.cc481
-rw-r--r--src/pix_opencv_pgh_compare.h91
-rw-r--r--src/pix_opencv_surf.cc1054
-rw-r--r--src/pix_opencv_surf.h122
-rw-r--r--src/pix_opencv_template.cc63
-rw-r--r--src/pix_opencv_template.h57
-rw-r--r--src/pix_opencv_threshold.cc262
-rw-r--r--src/pix_opencv_threshold.h88
-rw-r--r--src/pix_opencv_trackKnn.cc602
-rw-r--r--src/pix_opencv_trackKnn.h93
-rw-r--r--src/pix_opencv_warpperspective.cc313
-rw-r--r--src/pix_opencv_warpperspective.h100
87 files changed, 22363 insertions, 0 deletions
diff --git a/src/Blob.hpp b/src/Blob.hpp
new file mode 100644
index 0000000..fe7f836
--- /dev/null
+++ b/src/Blob.hpp
@@ -0,0 +1,56 @@
+/*
+* Blob.hpp
+*
+*
+* A blob is a homogenous patch represented by a polygonal contour.
+* Typically a blob tracker uses the contour to figure out the blob's
+* persistence and "upgrades" it with ids and other temporal
+* information.
+*
+*/
+
+#ifndef BLOB_H
+#define BLOB_H
+
+#include <vector>
+
+class Blob {
+
+public:
+
+ std::vector <cv::Point> pts; // the contour of the blob
+ int nPts; // number of pts;
+ int id;
+ float area;
+ float length;
+ float angle;
+ float maccel; //distance traveled since last frame
+ float age; //how long the blob has been at war
+ float sitting; //how long hes been sitting in the same place
+ float downTime;
+ float lastTimeTimeWasChecked;
+ cv::Rect boundingRect;
+ cv::RotatedRect angleBoundingRect;
+ cv::Point2f centroid, lastCentroid, D;
+ bool simulated;
+ bool hole;
+ cv::Scalar color;
+
+ //----------------------------------------
+ Blob() {
+ area = 0.0f;
+ length = 0.0f;
+ hole = false;
+ nPts = 0;
+ simulated = false;
+ age = 0.0f;
+ sitting = 0.0f;
+ color = 0xFFFFFF;
+
+ //freakishly long variable name (ala Apple)
+ //~lastTimeTimeWasChecked = ofGetElapsedTimeMillis(); //get current time as of creation
+ }
+};
+#endif
+
+
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..2457e78
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,129 @@
+#~bin_PROGRAMS = pix_opencv
+AUTOMAKE_OPTIONS = foreign
+pkglib_LTLIBRARIES = pix_opencv.la
+
+pix_opencv_la_CXXFLAGS = @GEM_CPPFLAGS@ @PD_CPPFLAGS@ @FACETRACKER_CPPFLAGS@
+pix_opencv_la_LDFLAGS = -module -avoid-version -shared -shrext .@EXTENSION@ @GEM_LDFLAGS@ @PD_LDFLAGS@ @FACETRACKER_LDFLAGS@
+pix_opencv_la_includedir=$(includedir)/src
+pix_opencv_la_include_HEADERS = Blob.hpp \
+ blobtrack.h \
+ pix_opencv_athreshold.h \
+ pix_opencv_backgroundsubtractor.h \
+ pix_opencv_bgstats.h \
+ pix_opencv_bgsubstract.h \
+ pix_opencv_blobtrack.h \
+ pix_opencv_calibration.h \
+ pix_opencv_camshift.h \
+ pix_opencv_clahe.h \
+ pix_opencv_colorfilt.h \
+ pix_opencv_contours_boundingrect.h \
+ pix_opencv_contours_convexhull.h \
+ pix_opencv_contours_convexity.h \
+ pix_opencv_contours.h \
+ pix_opencv_dft.h \
+ pix_opencv_distrans.h \
+ pix_opencv_edge.h \
+ pix_opencv_facetracker.h \
+ pix_opencv_findchessboardcorners.h \
+ pix_opencv_floodfill.h \
+ pix_opencv_haarcascade.h \
+ pix_opencv_hist_compare.h \
+ pix_opencv_hough_circles.h \
+ pix_opencv_hough_lines.h \
+ pix_opencv_hu_compare.h \
+ pix_opencv_hu_moments.h \
+ pix_opencv_knear.h \
+ pix_opencv_laplace.h \
+ pix_opencv_lk.h \
+ pix_opencv_matchshape.h \
+ pix_opencv_morphology.h \
+ pix_opencv_motempl.h \
+ pix_opencv_of_bm.h \
+ pix_opencv_of_hs.h \
+ pix_opencv_of_lk.h \
+ pix_opencv_opticalflow.h \
+ pix_opencv_patreco.h \
+ pix_opencv_pgh_compare.h \
+ pix_opencv_surf.h \
+ pix_opencv_template.h \
+ pix_opencv_threshold.h \
+ pix_opencv_trackKnn.h \
+ pix_opencv_warpperspective.h
+
+OVERVIEW=../pix_opencv-help.pd
+
+pix_opencv_la_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 \
+ pix_opencv_calibration.cc \
+ pix_opencv_warpperspective.cc \
+ pix_opencv_findchessboardcorners.cc \
+ pix_opencv_blobtrack.cc \
+ pix_opencv_contours.cc \
+ pix_opencv_matchshape.cc \
+ pix_opencv_opticalflow.cc \
+ pix_opencv_trackKnn.cc \
+ pix_opencv_backgroundsubtractor.cc \
+ pix_opencv_clahe.cc \
+ pix_opencv.cc \
+ pix_opencv_surf.cc \
+ pix_opencv_facetracker.cc
+
+#~ Override pix_opencv.la target to add ".WAIT" but it doesn't seems to work...
+#~pix_opencv.la: .WAIT $(pix_opencv_la_OBJECTS) $(pix_opencv_la_DEPENDENCIES) $(EXTRA_pix_opencv_la_DEPENDENCIES)
+#~$(AM_V_CXXLD)$(pix_opencv_la_LINK) -rpath $(pkglibdir) $(pix_opencv_la_OBJECTS) $(pix_opencv_la_LIBADD) $(LIBS)
+#~
+#~.WAIT:
+
+## uha, this is ugly
+$(abs_builddir)/.libs/pix_opencv.@GEXTENSION@: pix_opencv.la
+
+$(top_builddir)/pix_opencv.@EXTENSION@: $(abs_builddir)/.libs/pix_opencv.@EXTENSION@
+ rm -f $@ && cd $(top_builddir) && test -e $< && $(LN_S) $< pix_opencv.@EXTENSION@ || true
+
+.PHONY: clean-conveniencelink help
+
+clean-conveniencelink:
+ test -L $(top_builddir)/pix_opencv.@EXTENSION@ && rm -f $(top_builddir)/pix_opencv.@EXTENSION@ || true
+
+
+all-local:: $(top_builddir)/pix_opencv.@EXTENSION@ help
+
+clean-local:: clean-conveniencelink
+
+.o: .cc
+ $(CXX) $(ALL_CFLAGS) -o "$*.o" -c "$*.cc"
+
+help:
+ echo "#N canvas 147 197 1566 537 10;" > $(OVERVIEW)
+ echo "#X text 126 15 overview of all available pix_opencv objects;" >> $(OVERVIEW)
+ echo "#X obj 30 20 pix_opencv;" >> $(OVERVIEW)
+ ID=0 ; \
+ for extern in $(pix_opencv_la_SOURCES:.cc=""); do \
+ echo "#X obj `expr $$ID % 5 \* 300 + 50` `expr $$ID / 5 \* 30 + 40` $$extern;" >> $(OVERVIEW) && ID=`expr $$ID + 1` ; \
+ done ;
diff --git a/src/blobtrack.h b/src/blobtrack.h
new file mode 100644
index 0000000..e95feed
--- /dev/null
+++ b/src/blobtrack.h
@@ -0,0 +1,121 @@
+#include "opencv2/video/background_segm.hpp"
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/legacy/blobtrack.hpp"
+#include <opencv2/imgproc/imgproc_c.h>
+#include <stdio.h>
+
+/* Select appropriate case insensitive string comparison function: */
+#if defined WIN32 || defined _MSC_VER
+ #define MY_STRNICMP strnicmp
+ #define MY_STRICMP stricmp
+#else
+ #define MY_STRNICMP strncasecmp
+ #define MY_STRICMP strcasecmp
+#endif
+
+/* List of foreground (FG) DETECTION modules: */
+static CvFGDetector* cvCreateFGDetector0 () { return cvCreateFGDetectorBase(CV_BG_MODEL_FGD, NULL); }
+static CvFGDetector* cvCreateFGDetector0Simple() { return cvCreateFGDetectorBase(CV_BG_MODEL_FGD_SIMPLE, NULL); }
+static CvFGDetector* cvCreateFGDetector1 () { return cvCreateFGDetectorBase(CV_BG_MODEL_MOG, NULL); }
+
+typedef struct DefModule_FGDetector
+{
+ CvFGDetector* (*create)();
+ const char* nickname;
+ const char* description;
+} DefModule_FGDetector;
+
+DefModule_FGDetector FGDetector_Modules[] =
+{
+ {cvCreateFGDetector0,"FG_0","Foreground Object Detection from Videos Containing Complex Background. ACM MM2003."},
+ {cvCreateFGDetector0Simple,"FG_0S","Simplified version of FG_0"},
+ {cvCreateFGDetector1,"FG_1","Adaptive background mixture models for real-time tracking. CVPR1999"},
+ {NULL,NULL,NULL}
+};
+
+/* List of BLOB DETECTION modules: */
+typedef struct DefModule_BlobDetector
+{
+ CvBlobDetector* (*create)();
+ const char* nickname;
+ const char* description;
+} DefModule_BlobDetector;
+
+DefModule_BlobDetector BlobDetector_Modules[] =
+{
+ {cvCreateBlobDetectorCC,"BD_CC","Detect new blob by tracking CC of FG mask"},
+ {cvCreateBlobDetectorSimple,"BD_Simple","Detect new blob by uniform moving of connected components of FG mask"},
+ {NULL,NULL,NULL}
+};
+
+/* List of BLOB TRACKING modules: */
+typedef struct DefModule_BlobTracker
+{
+ CvBlobTracker* (*create)();
+ const char* nickname;
+ const char* description;
+} DefModule_BlobTracker;
+
+DefModule_BlobTracker BlobTracker_Modules[] =
+{
+ {cvCreateBlobTrackerCCMSPF,"CCMSPF","connected component tracking and MSPF resolver for collision"},
+ {cvCreateBlobTrackerCC,"CC","Simple connected component tracking"},
+ {cvCreateBlobTrackerMS,"MS","Mean shift algorithm "},
+ {cvCreateBlobTrackerMSFG,"MSFG","Mean shift algorithm with FG mask using"},
+ {cvCreateBlobTrackerMSPF,"MSPF","Particle filtering based on MS weight"},
+ {NULL,NULL,NULL}
+};
+
+/* List of BLOB TRAJECTORY GENERATION modules: */
+typedef struct DefModule_BlobTrackGen
+{
+ CvBlobTrackGen* (*create)();
+ const char* nickname;
+ const char* description;
+} DefModule_BlobTrackGen;
+
+DefModule_BlobTrackGen BlobTrackGen_Modules[] =
+{
+ {cvCreateModuleBlobTrackGenYML,"YML","Generate track record in YML format as synthetic video data"},
+ {cvCreateModuleBlobTrackGen1,"RawTracks","Generate raw track record (x,y,sx,sy),()... in each line"},
+ {NULL,NULL,NULL}
+};
+
+/* List of BLOB TRAJECTORY POST PROCESSING modules: */
+typedef struct DefModule_BlobTrackPostProc
+{
+ CvBlobTrackPostProc* (*create)();
+ const char* nickname;
+ const char* description;
+} DefModule_BlobTrackPostProc;
+
+DefModule_BlobTrackPostProc BlobTrackPostProc_Modules[] =
+{
+ {cvCreateModuleBlobTrackPostProcKalman,"Kalman","Kalman filtering of blob position and size"},
+ {NULL,"None","No post processing filter"},
+// {cvCreateModuleBlobTrackPostProcTimeAverRect,"TimeAverRect","Average by time using rectangle window"},
+// {cvCreateModuleBlobTrackPostProcTimeAverExp,"TimeAverExp","Average by time using exponential window"},
+ {NULL,NULL,NULL}
+};
+
+/* List of BLOB TRAJECTORY ANALYSIS modules: */
+CvBlobTrackAnalysis* cvCreateModuleBlobTrackAnalysisDetector();
+
+typedef struct DefModule_BlobTrackAnalysis
+{
+ CvBlobTrackAnalysis* (*create)();
+ const char* nickname;
+ const char* description;
+} DefModule_BlobTrackAnalysis;
+
+DefModule_BlobTrackAnalysis BlobTrackAnalysis_Modules[] =
+{
+ {cvCreateModuleBlobTrackAnalysisHistPVS,"HistPVS","Histogram of 5D feature vector analysis (x,y,vx,vy,state)"},
+ {NULL,"None","No trajectory analiser"},
+ {cvCreateModuleBlobTrackAnalysisHistP,"HistP","Histogram of 2D feature vector analysis (x,y)"},
+ {cvCreateModuleBlobTrackAnalysisHistPV,"HistPV","Histogram of 4D feature vector analysis (x,y,vx,vy)"},
+ {cvCreateModuleBlobTrackAnalysisHistSS,"HistSS","Histogram of 4D feature vector analysis (startpos,endpos)"},
+ {cvCreateModuleBlobTrackAnalysisTrackDist,"TrackDist","Compare tracks directly"},
+ {cvCreateModuleBlobTrackAnalysisIOR,"IOR","Integrator (by OR operation) of several analysers "},
+ {NULL,NULL,NULL}
+};
diff --git a/src/pix_opencv.cc b/src/pix_opencv.cc
new file mode 100644
index 0000000..4d3eff8
--- /dev/null
+++ b/src/pix_opencv.cc
@@ -0,0 +1,38 @@
+#include "m_pd.h"
+
+extern "C" {
+
+typedef struct pix_opencv
+{
+ t_object x_ob;
+} t_pix_opencv;
+
+t_class *pix_opencv_class;
+
+ /* this is called when a new "pix_opencv" object is created. */
+void *pix_opencv_new(void)
+{
+ t_pix_opencv *x = (t_pix_opencv *)pd_new(pix_opencv_class);
+ return (void *)x;
+}
+
+void pix_opencv_setup(void)
+{
+ verbose(-1,"pix_opencv %s library by Antoine Villeret 2012-2014",VERSION);
+ verbose(-1,"\tbased on work by Yves Degoyon and Lluis Gomez i Bigorda");
+ verbose(-1,"\tbuilt on %s",__DATE__);
+#if HAVE_LIBOPENCV_NONFREE
+ verbose(-1,"\twith non-free features");
+#else
+ verbose(-1,"\twithout non-free features");
+#endif
+#ifdef HAVE_FACETRACKER
+ verbose(-1,"\twith FaceTracker.");
+#else
+ verbose(-1,"\tand without FaceTracker.");
+#endif
+ pix_opencv_class = class_new(gensym("pix_opencv"), (t_newmethod)pix_opencv_new, 0,
+ sizeof(t_pix_opencv), 0, (t_atomtype) 0);
+}
+
+} // extern "C"
diff --git a/src/pix_opencv_athreshold.cc b/src/pix_opencv_athreshold.cc
new file mode 100644
index 0000000..a4f6e15
--- /dev/null
+++ b/src/pix_opencv_athreshold.cc
@@ -0,0 +1,261 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+
+#if HAVE_LIBOPENCV_LEGACY
+
+#include "pix_opencv_athreshold.h"
+
+CPPEXTERN_NEW(pix_opencv_athreshold)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_athreshold
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_athreshold :: pix_opencv_athreshold()
+{
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("max_value"));
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("blocksize"));
+
+ max_value = 255;
+ x_threshold_mode = 0;
+ x_threshold_method = CV_ADAPTIVE_THRESH_MEAN_C;
+ x_blocksize = 3;
+ x_dim = 0;
+
+ comp_xsize=320;
+ comp_ysize=240;
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_athreshold :: ~pix_opencv_athreshold()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_athreshold :: processRGBAImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgba)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if ( rgba )
+ {
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+ }
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 1);
+ }
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ cvCvtColor(rgba, gray, CV_BGRA2GRAY);
+
+ // Applies fixed-level thresholding to single-channel array.
+ switch(x_threshold_mode) {
+ case 0:
+ cvAdaptiveThreshold(gray, gray, (float)max_value, x_threshold_method, CV_THRESH_BINARY, x_blocksize, x_dim);
+ break;
+ case 1:
+ cvAdaptiveThreshold(gray, gray, (float)max_value, x_threshold_method, CV_THRESH_BINARY_INV, x_blocksize, x_dim);
+ break;
+ }
+
+ cvCvtColor(gray, rgba, CV_GRAY2BGRA);
+ //copy back the processed frame to image
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_athreshold :: processRGBImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if ( rgb )
+ {
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+ }
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+
+ }
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+ cvCvtColor(rgb, gray, CV_RGB2GRAY);
+
+ // Applies fixed-level thresholding to single-channel array.
+ switch(x_threshold_mode) {
+ case 0:
+ cvAdaptiveThreshold(gray, gray, (float)max_value, x_threshold_method, CV_THRESH_BINARY, x_blocksize, x_dim);
+ break;
+ case 1:
+ cvAdaptiveThreshold(gray, gray, (float)max_value, x_threshold_method, CV_THRESH_BINARY_INV, x_blocksize, x_dim);
+ break;
+ }
+
+ cvCvtColor(gray, rgb, CV_GRAY2BGR);
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_athreshold :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_athreshold : yuv format not supported" );
+}
+
+void pix_opencv_athreshold :: processGrayImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if ( rgb )
+ {
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+ }
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+
+ }
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+
+ // Applies fixed-level thresholding to single-channel array.
+ switch(x_threshold_mode) {
+ case 0:
+ cvAdaptiveThreshold(gray, gray, (float)max_value, x_threshold_method, CV_THRESH_BINARY, x_blocksize, x_dim);
+ break;
+ case 1:
+ cvAdaptiveThreshold(gray, gray, (float)max_value, x_threshold_method, CV_THRESH_BINARY_INV, x_blocksize, x_dim);
+ break;
+ }
+
+ memcpy( image.data, gray->imageData, image.xsize*image.ysize );
+}
+
+void pix_opencv_athreshold :: floatMaxValueMess (float maxvalue)
+{
+ if ( (int)maxvalue>0 ) max_value = (int)maxvalue;
+}
+
+void pix_opencv_athreshold :: floatModeMess (float mode)
+{
+ if ( ( (int)mode==0 ) || ( (int)mode==1 ) ) x_threshold_mode = (int)mode;
+}
+
+void pix_opencv_athreshold :: floatMethodMess (float method)
+{
+ if ( (int)method==CV_ADAPTIVE_THRESH_MEAN_C ) x_threshold_method = CV_ADAPTIVE_THRESH_MEAN_C;
+ if ( (int)method==CV_ADAPTIVE_THRESH_GAUSSIAN_C ) x_threshold_method = CV_ADAPTIVE_THRESH_GAUSSIAN_C;
+}
+
+void pix_opencv_athreshold :: floatBlockSizeMess (float blocksize)
+{
+ if ( ( (int)blocksize>=3 ) && ( (int)(blocksize+1)%2 == 0 ) )
+ {
+ x_blocksize = (int)blocksize;
+ }
+}
+
+void pix_opencv_athreshold :: floatDimMess (float dim)
+{
+ if ( (int)dim>0 ) x_dim = (int)dim;
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_athreshold :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_athreshold::floatModeMessCallback,
+ gensym("mode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_athreshold::floatMethodMessCallback,
+ gensym("method"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_athreshold::floatMaxValueMessCallback,
+ gensym("max_value"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_athreshold::floatBlockSizeMessCallback,
+ gensym("blocksize"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_athreshold::floatDimMessCallback,
+ gensym("dim"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_athreshold :: floatModeMessCallback(void *data, t_floatarg mode)
+{
+ GetMyClass(data)->floatModeMess((float)mode);
+}
+
+void pix_opencv_athreshold :: floatMethodMessCallback(void *data, t_floatarg method)
+{
+ GetMyClass(data)->floatMethodMess((float)method);
+}
+
+void pix_opencv_athreshold :: floatMaxValueMessCallback(void *data, t_floatarg maxvalue)
+{
+ GetMyClass(data)->floatMaxValueMess((float)maxvalue);
+}
+
+void pix_opencv_athreshold :: floatBlockSizeMessCallback(void *data, t_floatarg blocksize)
+{
+ GetMyClass(data)->floatBlockSizeMess((float)blocksize);
+}
+
+void pix_opencv_athreshold :: floatDimMessCallback(void *data, t_floatarg dim)
+{
+ GetMyClass(data)->floatDimMess((float)dim);
+}
+#endif /*HAVE_LIBOPENCV_LEGACY*/
diff --git a/src/pix_opencv_athreshold.h b/src/pix_opencv_athreshold.h
new file mode 100644
index 0000000..99b9a40
--- /dev/null
+++ b/src/pix_opencv_athreshold.h
@@ -0,0 +1,95 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Adaptive threshold object
+
+ 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_ATHRESHOLD_H_
+#define INCLUDE_PIX_OPENCV_ATHRESHOLD_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_athreshold
+
+ Adaptive threshold object
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_athreshold : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_athreshold, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_athreshold();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_athreshold();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void floatMaxValueMess(float maxvalue);
+ void floatModeMess(float mode);
+ void floatMethodMess(float method);
+ void floatBlockSizeMess(float blocksize);
+ void floatDimMess(float dim);
+
+ // The new edge threshold
+ int max_value;
+ int x_threshold_mode;
+ int x_threshold_method;
+ int x_blocksize;
+ int x_dim;
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatMaxValueMessCallback(void *data, float maxvalue);
+ static void floatModeMessCallback(void *data, float mode);
+ static void floatMethodMessCallback(void *data, float method);
+ static void floatBlockSizeMessCallback(void *data, float blocksize);
+ static void floatDimMessCallback(void *data, float dim);
+
+ /////////
+ // IplImage needed
+ IplImage *rgba, *rgb, *gray;
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_backgroundsubtractor.cc b/src/pix_opencv_backgroundsubtractor.cc
new file mode 100644
index 0000000..bf079b4
--- /dev/null
+++ b/src/pix_opencv_backgroundsubtractor.cc
@@ -0,0 +1,374 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org ) (pix_opencv)
+// Template for pix_opencv class
+
+#if HAVE_BGSUB
+#include "pix_opencv_backgroundsubtractor.h"
+
+using namespace cv;
+using namespace std;
+
+CPPEXTERN_NEW(pix_opencv_backgroundsubtractor);
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_backgroundsubtractor
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_backgroundsubtractor :: pix_opencv_backgroundsubtractor() : m_forceCPU(false) {
+ initModule_video();
+ setUseOptimized(true);
+ setNumThreads(8);
+
+ vector<string> algorithms;
+ Algorithm::getList(algorithms);
+ string model = "BackgroundSubtractor.";
+
+ for (size_t i=0; i < algorithms.size(); i++){
+ if ( model.compare(0,model.length(),algorithms[i],0,model.length()) == 0 ){ // && npos >= model.length() ){
+ m_bgsub_algos.push_back(algorithms[i]);
+ }
+ }
+
+ if ( m_bgsub_algos.size() == 0){
+ throw(GemException("Can't find any background subtractor algorithm."));
+ }
+
+ if ( m_bgsub_algos[0] == "BackgroundSubtractor.GMG"){
+ m_fgbgGMG = Algorithm::create<BackgroundSubtractorGMG>(m_bgsub_algos[0]);
+ } else {
+ m_fgbgMOG = Algorithm::create<BackgroundSubtractor>(m_bgsub_algos[0]);
+ }
+ if (m_fgbgMOG.empty() && m_fgbgGMG.empty())
+ {
+ throw(GemException("Failed to create BackgroundSubtractor Algorithm."));
+ }
+
+ m_dataout = outlet_new(this->x_obj, 0);
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_backgroundsubtractor :: ~pix_opencv_backgroundsubtractor()
+{
+}
+
+/////////////////////////////////////////////////////////
+// StartRendering
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_backgroundsubtractor :: startRendering(){
+
+#if HAVE_LIBOPENCV_CL
+ ocl::DevicesInfo devicesInfo;
+ ocl::getOpenCLDevices(devicesInfo);
+ post("Found %d OpenCL device(s).", devicesInfo.size());
+ for ( size_t i = 0; i < devicesInfo.size(); i++){
+ post("%s %s", devicesInfo[i]->deviceVendor.c_str(), devicesInfo[i]->deviceName.c_str());
+ }
+
+ if ( devicesInfo.size() == 0 || m_forceCPU ){
+ post("can't find OpenCL device, switch to CPU mode");
+ m_gpuMode = false;
+ } else {
+ m_gpuMode = true;
+ }
+#else
+ verbose(2,"no OpenCL support, it could be very slow !");
+#endif
+
+ m_rendering = true;
+}
+
+void pix_opencv_backgroundsubtractor :: stopRendering(){
+ m_rendering = false;
+
+ //~ this crashes when no OCL device is used
+ //~m_oclMOG.release();
+ //~m_oclMOG2.release();
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_backgroundsubtractor :: processImage(imageStruct &image)
+{
+ Mat imgMat, input;
+ if ( image.csize == 1 ){
+ imgMat = Mat( image.ysize, image.xsize, CV_8UC1, image.data, image.csize*image.xsize); // just transform imageStruct to cv::Mat without copying data
+ input = imgMat;
+ } else if ( image.csize == 4 ){
+ imgMat = Mat( image.ysize, image.xsize, CV_8UC4, image.data, image.csize*image.xsize); // just transform imageStruct to cv::Mat without copying data
+ cvtColor(imgMat, input, CV_RGBA2RGB);
+ } else {
+ verbose(1,"suport only RGBA or GRAY image");
+ return;
+ }
+
+#if HAVE_LIBOPENCV_CL
+ if ( m_gpuMode ) {
+ try {
+ d_input = input;
+ if ( m_algoName == "MOG" ){
+ m_oclMOG( d_input, d_fgmask );
+ } else if ( m_algoName == "MOG2" ){
+ m_oclMOG2( d_input, d_fgmask, 0.01f );
+ } else {
+ error("there is no GPU version of algo %s", m_algoName.c_str());
+ m_gpuMode = false;
+ return;
+ }
+ d_fgmask.download(m_fgmask);
+ } catch (cv::Exception& e) {
+ error("can't use OpenCL, do you have OpenCL driver installed ?");
+ error("error %d : %s", e.code, e.err.c_str());
+ m_gpuMode = false;
+ return;
+ }
+#else
+ if ( 0 ) {
+#endif /* HAVE_LIBOPENCV_CL */
+ } else if (!m_fgbgMOG.empty()){
+ (*m_fgbgMOG)(input, m_fgmask);
+ } else if (!m_fgbgGMG.empty()) {
+ (*m_fgbgGMG)(input, m_fgmask);
+ } else {
+ error("Please load a valid algorithm before processing.");
+ return;
+ }
+
+ if ( image.csize == 1 ){ // if grayscale, send out fgmask
+ image.data = m_fgmask.data;
+ } else { // else, set only alpha channel
+ std::vector<cv::Mat> split;
+ cv::split(imgMat, split);
+ split.pop_back();
+ split.push_back(m_fgmask); // add fgmask as alpha channel
+ cv::merge(split, imgMat);
+ }
+}
+
+void pix_opencv_backgroundsubtractor :: paramHelpMess(){
+ vector<string> paramList;
+ if (!m_fgbgMOG.empty()){
+ m_fgbgMOG->getParams(paramList);
+ post("%s parameters help :",m_fgbgMOG->name().c_str());
+ for (size_t i=0; i < paramList.size(); i++){
+ post("%s : %s", paramList[i].c_str(), m_fgbgMOG->paramHelp(paramList[i]).c_str());
+ }
+ } else if (!m_fgbgGMG.empty()) {
+ m_fgbgGMG->getParams(paramList);
+ post("%s parameters help :",m_fgbgGMG->name().c_str());
+ for (size_t i=0; i < paramList.size(); i++){
+ post("%s : %s", paramList[i].c_str(), m_fgbgGMG->paramHelp(paramList[i]).c_str());
+ }
+ } else {
+ error("Please load a valid algorithm before requesting paramHelp.");
+ return;
+ }
+}
+
+void pix_opencv_backgroundsubtractor :: enumParamsMess(){
+ vector<string> paramList;
+ if (!m_fgbgMOG.empty()){
+ m_fgbgMOG->getParams(paramList);
+ } else if ( !m_fgbgGMG.empty() ){
+ m_fgbgGMG->getParams(paramList);
+ } else {
+ error("please choose an algo before enumerating parameters.");
+ return;
+ }
+
+ t_atom a_prop[2];
+ SETFLOAT(a_prop, paramList.size());
+ outlet_anything( m_dataout, gensym("params"), 1, a_prop);
+
+ for (size_t i=0; i < paramList.size(); i++){
+ SETSYMBOL(a_prop, gensym(paramList[i].c_str()));
+ SETFLOAT(a_prop+1,0.);
+ outlet_anything(m_dataout, gensym("paramList"), 2, a_prop);
+ }
+}
+
+void pix_opencv_backgroundsubtractor :: setParamMess(t_symbol *s, int argc, t_atom* argv){
+ if ( argc < 2 ){
+ error("setParam needs 2 args: paramName, value");
+ return;
+ }
+
+ if ( argv[0].a_type != A_SYMBOL ){
+ error("1st argument should be paramName (symbol)");
+ return;
+ }
+ t_symbol* paramName = atom_getsymbol(argv);
+
+ if ( argv[1].a_type != A_FLOAT ){
+ error("currently support only float parameter values"); // I don't know the signification of output of paramType(), so only support float
+ }
+
+ vector<string> paramList;
+ if (!m_fgbgMOG.empty()){
+ m_fgbgMOG->getParams(paramList);
+ } else if ( !m_fgbgGMG.empty() ){
+ m_fgbgGMG->getParams(paramList);
+ } else {
+ error("please choose an algo before enumerating parameters.");
+ return;
+ }
+ size_t i;
+ for (i=0; i < paramList.size(); i++){
+ if ( paramList[i] == paramName->s_name ) break;
+ }
+ if ( i == paramList.size()){
+ error("can't find parameter %s", paramName->s_name);
+ return;
+ }
+
+ float val = atom_getfloat(argv+1);
+ try {
+ if (!m_fgbgMOG.empty()){
+ m_fgbgMOG->set(paramList[i], val);
+ } else if ( !m_fgbgGMG.empty() ) {
+ m_fgbgGMG->set(paramList[i], val);
+ }
+ } catch (cv::Exception& e) {
+ error("can't set parameter %s value",paramList[i].c_str());
+ error("error %d : %s", e.code, e.err.c_str());
+ return;
+ }
+}
+
+void pix_opencv_backgroundsubtractor :: getParamMess(t_symbol *paramName){
+ vector<string> paramList;
+ if (!m_fgbgMOG.empty()){
+ m_fgbgMOG->getParams(paramList);
+ } else if ( !m_fgbgGMG.empty() ){
+ m_fgbgGMG->getParams(paramList);
+ } else {
+ error("please choose an algo before enumerating parameters.");
+ return;
+ }
+ size_t i;
+ for (i=0; i < paramList.size(); i++){
+ if ( paramList[i] == paramName->s_name ) break;
+ }
+ if ( i == paramList.size()){
+ error("can't find parameter %s", paramName->s_name);
+ return;
+ }
+ double val=0;
+ try {
+ if (!m_fgbgMOG.empty()){
+ val = m_fgbgMOG->get<double>(paramList[i]);
+ } else if ( !m_fgbgGMG.empty() ){
+ val = m_fgbgGMG->get<double>(paramList[i]);
+ }
+ } catch (cv::Exception& e) {
+ error("can't get parameter %s value",paramList[i].c_str());
+ error("error %d : %s", e.code, e.err.c_str());
+ return;
+ }
+
+ t_atom a_val[2];
+ SETSYMBOL(a_val, paramName);
+ SETFLOAT(a_val+1, val);
+ outlet_anything(m_dataout, gensym("param"), 2, a_val);
+
+
+}
+
+void pix_opencv_backgroundsubtractor :: algoMess(t_symbol *s, int argc, t_atom* argv){
+ if ( argc == 0 ) {
+ t_atom a_prop[2];
+ SETFLOAT(a_prop, m_bgsub_algos.size());
+ outlet_anything( m_dataout, gensym("algos"), 1, a_prop);
+
+ for (size_t i=0; i < m_bgsub_algos.size(); i++){
+ SETFLOAT(a_prop,i);
+ SETSYMBOL(a_prop+1, gensym(m_bgsub_algos[i].c_str()));
+ outlet_anything(m_dataout, gensym("algoList"), 2, a_prop);
+ }
+ return;
+ }
+
+ if ( !m_fgbgMOG.empty() ) m_fgbgMOG.release();
+ if ( !m_fgbgGMG.empty() ) m_fgbgGMG.release();
+
+ m_algoName = "NONE";
+
+ if ( argv[0].a_type == A_FLOAT ){
+ int id_max = m_bgsub_algos.size()-1;
+ int id = atom_getfloat(argv);
+ if ( id > id_max ) id = id_max;
+ if ( m_bgsub_algos[id] == "BackgroundSubtractor.GMG"){
+ m_fgbgGMG = Algorithm::create<BackgroundSubtractorGMG>(m_bgsub_algos[id]);
+ } else {
+ m_fgbgMOG = Algorithm::create<BackgroundSubtractor>(m_bgsub_algos[id]);
+ }
+ if (m_fgbgMOG.empty() && m_fgbgGMG.empty())
+ {
+ error("Failed to create %s Algorithm.", m_bgsub_algos[id].c_str());
+ } else {
+ verbose(2,"bgsub %d : \"%s\" created.",id, m_bgsub_algos[id].c_str());
+ m_algoName = m_bgsub_algos[id].substr(21);
+ }
+ } else if ( argv[0].a_type == A_SYMBOL ) {
+ t_symbol* algoSym = atom_getsymbol(argv);
+ string algo = algoSym->s_name;
+ if ( algo == "BackgroundSubtractor.GMG"){
+ m_fgbgGMG = Algorithm::create<BackgroundSubtractorGMG>(algo);
+ } else {
+ m_fgbgMOG = Algorithm::create<BackgroundSubtractor>(algo);
+ }
+ if (m_fgbgMOG.empty() && m_fgbgGMG.empty())
+ {
+ error("Failed to create %s Algorithm.", algo.c_str());
+ } else {
+ verbose(2,"bgsub : \"%s\" created.",algo.c_str());
+ m_algoName = algo.substr(21);
+ }
+ }
+ if ( !m_forceCPU) m_gpuMode=true;
+}
+
+void pix_opencv_backgroundsubtractor :: cpuModeMess( int val ){
+ m_forceCPU = val > 0;
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_backgroundsubtractor :: obj_setupCallback(t_class *classPtr)
+{
+ CPPEXTERN_MSG(classPtr, "algo", algoMess);
+ CPPEXTERN_MSG0(classPtr, "enumParams", enumParamsMess);
+ CPPEXTERN_MSG(classPtr, "setParam", setParamMess);
+ CPPEXTERN_MSG1(classPtr, "getParam", getParamMess, t_symbol*);
+ CPPEXTERN_MSG0(classPtr, "paramHelp", paramHelpMess);
+ CPPEXTERN_MSG1(classPtr, "cpuMode", cpuModeMess, int);
+}
+#endif /* HAVE_BGSUB */
+
diff --git a/src/pix_opencv_backgroundsubtractor.h b/src/pix_opencv_backgroundsubtractor.h
new file mode 100644
index 0000000..cffa9b2
--- /dev/null
+++ b/src/pix_opencv_backgroundsubtractor.h
@@ -0,0 +1,96 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Threshold filter
+
+ 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_BACKGROUNDSUBTRACTOR_H_
+#define INCLUDE_PIX_OPENCV_BACKGROUNDSUBTRACTOR_H_
+
+#include "opencv2/opencv.hpp"
+#if HAVE_LIBOPENCV_CL
+#include "opencv2/ocl/ocl.hpp"
+#endif /* HAVE_LIBOPENCV_CL */
+
+#include "Base/GemPixObj.h"
+#include <RTE/MessageCallbacks.h>
+#include "Gem/Exception.h"
+
+#include <iostream>
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_backgroundsubtractor
+
+ generic dynamic background subtractor, should implement severals algo
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_backgroundsubtractor: public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_backgroundsubtractor, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_backgroundsubtractor();
+
+ protected:
+
+ // Message handling
+ void enumParamsMess();
+ void setParamMess(t_symbol *s, int argc, t_atom* argv);
+ void getParamMess(t_symbol *paramName);
+ void paramHelpMess();
+ void algoMess(t_symbol *s, int argc, t_atom* argv);
+ void cpuModeMess(int val);
+
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_backgroundsubtractor();
+
+ //////////
+ // Do the processing
+ virtual void processImage(imageStruct &image);
+ virtual void startRendering();
+ virtual void stopRendering();
+
+ private:
+ cv::Ptr<cv::BackgroundSubtractor> m_fgbgMOG;
+ cv::Ptr<cv::BackgroundSubtractorGMG> m_fgbgGMG;
+
+#if HAVE_LIBOPENCV_CL
+ cv::ocl::MOG m_oclMOG;
+ cv::ocl::MOG2 m_oclMOG2;
+ //~ cv::ocl::GMG m_oclGMG;
+ cv::ocl::oclMat d_input, d_fgmask;
+#endif /* HAVE_LIBOPENCV_CL */
+
+ cv::Mat m_fgmask, m_segm;
+
+ std::string m_algoName;
+
+ t_outlet *m_dataout;
+
+ t_float m_threshold, m_initFrames;
+ bool m_rendering, m_gpuMode, m_forceCPU;
+
+ std::vector<std::string> m_bgsub_algos;
+
+};
+#endif // for header file
diff --git a/src/pix_opencv_bgstats.cc b/src/pix_opencv_bgstats.cc
new file mode 100644
index 0000000..c1fe298
--- /dev/null
+++ b/src/pix_opencv_bgstats.cc
@@ -0,0 +1,251 @@
+////////////////////////////////////////////////////////
+//
+// 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_bgstats.h"
+
+CPPEXTERN_NEW(pix_opencv_bgstats)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_bgstats
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_bgstats :: pix_opencv_bgstats()
+{
+ comp_xsize=320;
+ comp_ysize=240;
+
+ x_erode = 1;
+ x_minarea = CV_BGFG_FGD_MINAREA;
+ x_alpha = CV_BGFG_FGD_ALPHA_1;
+ x_frames = 0;
+
+ foreground = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ incoming = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ x_model = NULL;
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_bgstats :: ~pix_opencv_bgstats()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&foreground);
+ cvReleaseImage(&incoming);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_bgstats :: processRGBAImage(imageStruct &image)
+{
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgba)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+ x_frames = 0;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&foreground);
+ cvReleaseImage(&incoming);
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 1);
+
+ incoming = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ foreground = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+
+ cvReleaseBGStatModel( &x_model );
+ x_model = NULL;
+
+ }
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ cvCvtColor(rgba, incoming, CV_BGRA2BGR);
+
+ if ( x_model == NULL )
+ {
+ x_model = cvCreateFGDStatModel( incoming );
+ }
+ else if ( x_frames == 5 )
+ {
+ // strange model stabilize after a few frames
+ //cvReleaseBGStatModel( &x_model );
+ //x_model = cvCreateFGDStatModel( incoming, &x_modelparams );
+ }
+ else
+ {
+ cvUpdateBGStatModel( incoming, x_model );
+ }
+
+ x_frames++;
+ cvCvtColor(x_model->foreground, rgba, CV_GRAY2BGRA);
+
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_bgstats :: processRGBImage(imageStruct &image)
+{
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgba)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+ x_frames = 0;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&foreground);
+ cvReleaseImage(&incoming);
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 1);
+
+ incoming = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ foreground = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+
+ cvReleaseBGStatModel( &x_model );
+ x_model = NULL;
+ }
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+ memcpy( incoming->imageData, image.data, image.xsize*image.ysize*3 );
+
+ if ( x_model == NULL )
+ {
+ x_model = cvCreateFGDStatModel( incoming );
+ }
+ else if ( x_frames == 5 )
+ {
+ // strange model stabilize after a few frames
+ //cvReleaseBGStatModel( &x_model );
+ //x_model = cvCreateFGDStatModel( incoming, &x_modelparams );
+ }
+ else
+ {
+ cvUpdateBGStatModel( incoming, x_model );
+ }
+
+ x_frames++;
+ cvCvtColor(x_model->foreground, rgb, CV_GRAY2BGR);
+
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+
+}
+
+void pix_opencv_bgstats :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_bgstats : yuv format not supported" );
+}
+
+void pix_opencv_bgstats :: processGrayImage(imageStruct &image)
+{
+ post( "pix_opencv_bgstats : gray format not supported" );
+}
+
+/////////////////////////////////////////////////////////
+// floatThreshMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_bgstats :: floatMinAreaMess (float minarea)
+{
+ if ( ( (int)minarea>0 ) && ( x_model != NULL ) )
+ {
+ x_minarea = minarea;
+ }
+}
+
+void pix_opencv_bgstats :: floatErodeMess(float erode)
+{
+ if ( ( (int)erode>0 ) && ( x_model != NULL ) )
+ {
+ x_erode = (int)erode;
+ }
+}
+
+void pix_opencv_bgstats :: floatAlphaMess(float alpha)
+{
+ if ( ( alpha>0.0 ) && ( x_model != NULL ) )
+ {
+ x_alpha = alpha;
+ }
+}
+
+void pix_opencv_bgstats :: resetMess ()
+{
+ if ( x_model != NULL )
+ {
+ }
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_bgstats :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_bgstats::floatMinAreaMessCallback,
+ gensym("minarea"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_bgstats::floatErodeMessCallback,
+ gensym("erode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_bgstats::floatAlphaMessCallback,
+ gensym("alpha"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_bgstats::resetMessCallback,
+ gensym("reset"), A_NULL);
+
+}
+
+void pix_opencv_bgstats :: floatMinAreaMessCallback(void *data, t_floatarg minarea)
+{
+ GetMyClass(data)->floatMinAreaMess((float)minarea);
+}
+
+void pix_opencv_bgstats :: floatErodeMessCallback(void *data, t_floatarg erode)
+{
+ GetMyClass(data)->floatErodeMess((float)erode);
+}
+
+void pix_opencv_bgstats :: floatAlphaMessCallback(void *data, t_floatarg alpha)
+{
+ GetMyClass(data)->floatAlphaMess((float)alpha);
+}
+
+void pix_opencv_bgstats :: resetMessCallback(void *data)
+{
+ GetMyClass(data)->resetMess();
+}
diff --git a/src/pix_opencv_bgstats.h b/src/pix_opencv_bgstats.h
new file mode 100644
index 0000000..199fe0d
--- /dev/null
+++ b/src/pix_opencv_bgstats.h
@@ -0,0 +1,100 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Statistical background substraction
+
+ 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_BGSTATS_H
+#define INCLUDE_PIX_OPENCV_BGSTATS_H
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/video/background_segm.hpp"
+
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_bgstats
+
+ Statistical background substraction
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_bgstats : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_bgstats, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_bgstats();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_bgstats();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void floatMinAreaMess(float minarea);
+ void floatErodeMess(float erode);
+ void floatAlphaMess(float alpha);
+ void resetMess(void);
+
+ // The new threshold
+ int x_erode;
+ float x_minarea;
+ float x_alpha;
+ int x_frames;
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatMinAreaMessCallback(void *data, float minarea);
+ static void floatErodeMessCallback(void *data, float erode);
+ static void floatAlphaMessCallback(void *data, float alpha);
+ static void resetMessCallback(void *data);
+
+ /////////
+ // IplImage needed
+ IplImage *rgba, *rgb, *gray;
+ IplImage *foreground, *incoming;
+
+ // Stat background model data
+ CvBGStatModel *x_model;
+ CvFGDStatModelParams x_modelparams;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_bgsubstract.cc b/src/pix_opencv_bgsubstract.cc
new file mode 100644
index 0000000..ca3bb03
--- /dev/null
+++ b/src/pix_opencv_bgsubstract.cc
@@ -0,0 +1,258 @@
+////////////////////////////////////////////////////////
+//
+// 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_bgsubstract.h"
+
+CPPEXTERN_NEW(pix_opencv_bgsubstract)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_bgsubstract
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_bgsubstract :: pix_opencv_bgsubstract()
+{
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("ft1"));
+ x_threshold = 13;
+ x_set = 1;
+ comp_xsize = 0;
+ comp_ysize = 0;
+ orig = NULL;
+ gray = NULL;
+ rgb = NULL;
+ grayLow = NULL;
+ grayUp = NULL;
+ prev_gray = NULL;
+ diff_8U = NULL;
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_bgsubstract :: ~pix_opencv_bgsubstract()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&prev_gray);
+ cvReleaseImage(&grayLow);
+ cvReleaseImage(&grayUp);
+ cvReleaseImage(&diff_8U);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_bgsubstract :: processRGBAImage(imageStruct &image)
+{
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!orig)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&prev_gray);
+ cvReleaseImage(&grayLow);
+ cvReleaseImage(&grayUp);
+ cvReleaseImage(&diff_8U);
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+
+ // Create the output images with new sizes
+ gray = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ grayLow = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ grayUp = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ prev_gray = cvCreateImage(cvSize(orig->width,orig->height), 8, 1);
+ diff_8U = cvCreateImage(cvSize(orig->width,orig->height), 8, 1);
+ }
+ // Here we make a copy of the pixel data from image to orig->imageData
+ // orig is a IplImage struct, the default image type in openCV, take a look on the IplImage data structure here
+ // http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
+ memcpy( orig->imageData, image.data, image.xsize*image.ysize*4 );
+
+ // Convert to grayscale
+ cvCvtColor(orig, gray, CV_BGRA2GRAY);
+
+ if (x_set) {
+ memcpy( prev_gray->imageData, gray->imageData, image.xsize*image.ysize );
+ x_set=0;
+ }
+
+ cvSubS (prev_gray,cvScalar(x_threshold,x_threshold,x_threshold,x_threshold),grayLow,NULL);
+ cvAddS (prev_gray,cvScalar(x_threshold,x_threshold,x_threshold,x_threshold),grayUp,NULL);
+ cvInRange (gray, grayLow, grayUp, diff_8U);
+
+ cvNot (diff_8U,diff_8U);
+
+ cvCvtColor(diff_8U, orig, CV_GRAY2BGRA);
+
+ //copy back the processed frame to image
+ memcpy( image.data, orig->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_bgsubstract :: processRGBImage(imageStruct &image)
+{
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!orig)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&prev_gray);
+ cvReleaseImage(&grayLow);
+ cvReleaseImage(&grayUp);
+ cvReleaseImage(&diff_8U);
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+
+ // Create the output images with new sizes
+ gray = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ grayLow = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ grayUp = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ prev_gray = cvCreateImage(cvSize(orig->width,orig->height), 8, 1);
+ diff_8U = cvCreateImage(cvSize(orig->width,orig->height), 8, 1);
+ }
+ // Here we make a copy of the pixel data from image to orig->imageData
+ // orig is a IplImage struct, the default image type in openCV, take a look on the IplImage data structure here
+ // http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ // Convert to grayscale
+ cvCvtColor(rgb, gray, CV_BGRA2GRAY);
+
+ if (x_set) {
+ memcpy( prev_gray->imageData, gray->imageData, image.xsize*image.ysize );
+ x_set=0;
+ }
+
+ cvSubS (prev_gray,cvScalar(x_threshold,x_threshold,x_threshold,x_threshold),grayLow,NULL);
+ cvAddS (prev_gray,cvScalar(x_threshold,x_threshold,x_threshold,x_threshold),grayUp,NULL);
+ cvInRange (gray, grayLow, grayUp, diff_8U);
+
+ cvNot (diff_8U,diff_8U);
+
+ cvCvtColor(diff_8U, rgb, CV_GRAY2BGR);
+
+ //copy back the processed frame to image
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+
+}
+
+void pix_opencv_bgsubstract :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_bgsubstract : yuv format not supported" );
+}
+
+void pix_opencv_bgsubstract :: processGrayImage(imageStruct &image)
+{
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!orig)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&prev_gray);
+ cvReleaseImage(&grayLow);
+ cvReleaseImage(&grayUp);
+ cvReleaseImage(&diff_8U);
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+
+ // Create the output images with new sizes
+ gray = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ grayLow = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ grayUp = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ prev_gray = cvCreateImage(cvSize(orig->width,orig->height), 8, 1);
+ diff_8U = cvCreateImage(cvSize(orig->width,orig->height), 8, 1);
+ }
+ // Here we make a copy of the pixel data from image to orig->imageData
+ // orig is a IplImage struct, the default image type in openCV, take a look on the IplImage data structure here
+ // http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+
+
+ if (x_set) {
+ memcpy( prev_gray->imageData, gray->imageData, image.xsize*image.ysize );
+ x_set=0;
+ }
+
+ cvSubS (prev_gray,cvScalar(x_threshold,x_threshold,x_threshold,x_threshold),grayLow,NULL);
+ cvAddS (prev_gray,cvScalar(x_threshold,x_threshold,x_threshold,x_threshold),grayUp,NULL);
+ cvInRange (gray, grayLow, grayUp, diff_8U);
+
+ cvNot (diff_8U,diff_8U);
+
+ //copy back the processed frame to image
+ memcpy( image.data, diff_8U->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// floatThreshMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_bgsubstract :: floatThreshMess (float x_threshold)
+{
+ this->x_threshold = (int)x_threshold;
+}
+void pix_opencv_bgsubstract :: SetMess ()
+{
+ this->x_set = 1;
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_bgsubstract :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_bgsubstract::floatTreshMessCallback,
+ gensym("ft1"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_bgsubstract::SetMessCallback,
+ gensym("set"), A_NULL);
+}
+void pix_opencv_bgsubstract :: floatTreshMessCallback(void *data, t_floatarg x_threshold)
+{
+ GetMyClass(data)->floatThreshMess((float)x_threshold);
+}
+void pix_opencv_bgsubstract :: SetMessCallback(void *data)
+{
+ GetMyClass(data)->SetMess();
+}
diff --git a/src/pix_opencv_bgsubstract.h b/src/pix_opencv_bgsubstract.h
new file mode 100644
index 0000000..5aa43cd
--- /dev/null
+++ b/src/pix_opencv_bgsubstract.h
@@ -0,0 +1,86 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Change pix to greyscale
+
+ 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_BGSUBSTRACT_H_
+#define INCLUDE_PIX_OPENCV_BGSUBSTRACT_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_bgsubstract
+
+ Change pix to greyscale
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_bgsubstract : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_bgsubstract, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_bgsubstract();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_bgsubstract();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void floatThreshMess(float x_threshold);
+ void SetMess();
+ // The new threshold
+ int x_threshold;
+ int x_set;
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatTreshMessCallback(void *data, t_floatarg thresh_value);
+ static void SetMessCallback(void *data);
+
+ /////////
+ // IplImage needed
+ IplImage *orig, *rgb, *gray, *prev_gray, *grayLow, *grayUp, *diff_8U;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_blobtrack.cc b/src/pix_opencv_blobtrack.cc
new file mode 100644
index 0000000..909dd76
--- /dev/null
+++ b/src/pix_opencv_blobtrack.cc
@@ -0,0 +1,693 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org ) (pix_opencv)
+// mainly copy and paste from blobtrack_sample.cpp provided by OpenCV 2.3 SVN rev7875
+
+#include "pix_opencv_blobtrack.h"
+
+
+
+CPPEXTERN_NEW(pix_opencv_blobtrack)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_blobtrack
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_blobtrack :: pix_opencv_blobtrack() : m_fg_name(FGDetector_Modules[0].nickname), \
+ m_bd_name(BlobDetector_Modules[0].nickname), \
+ m_bt_name(BlobTracker_Modules[0].nickname), \
+ m_btpp_name(BlobTrackPostProc_Modules[0].nickname), \
+ m_bta_name(BlobTrackAnalysis_Modules[0].nickname), \
+ m_bt_corr("none"), \
+ m_FGTrainFrames(6), \
+ m_monitoring_stage(0), \
+ m_areaThreshold(0.), \
+ m_tracker(NULL)
+{
+ m_param.pBT=NULL;
+ m_param.pBD=NULL;
+ m_param.pBTGen=NULL;
+ m_param.pBTA=NULL;
+ m_param.pFG=NULL;
+ m_dataout = outlet_new(this->x_obj, 0);
+ setupModules();
+ createModules();
+ //~printParamsMess();
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_blobtrack :: ~pix_opencv_blobtrack()
+{
+ releaseModules();
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_blobtrack :: processRGBAImage(imageStruct &image)
+{
+ cv::Mat imgMat( image.ysize, image.xsize, CV_8UC4, image.data, image.csize*image.xsize); // just transform imageStruct to IplImage without copying data
+ IplImage img = imgMat; // convert cv::Mat to IplImage
+ RunBlobTrackingAuto( &img );
+}
+
+void pix_opencv_blobtrack :: processRGBImage(imageStruct &image) {
+ cv::Mat imgMat( image.ysize, image.xsize, CV_8UC3, image.data, image.csize*image.xsize); // just transform imageStruct to IplImage without copying data
+ IplImage img = imgMat;
+ RunBlobTrackingAuto( &img );
+}
+
+void pix_opencv_blobtrack :: processYUVImage(imageStruct &image) {
+
+ error("YVU format not supported");
+}
+
+void pix_opencv_blobtrack :: processGrayImage(imageStruct &image)
+{
+ cv::Mat imgMat( image.ysize, image.xsize, CV_8UC1, image.data, image.csize*image.xsize); // convert imageStruct to cv::Mat without copying data
+ IplImage img = imgMat; // convert cv::Mat to IplImage
+ RunBlobTrackingAuto( &img );
+}
+
+void pix_opencv_blobtrack :: RunBlobTrackingAuto( IplImage* img )
+{
+ IplImage* pMask = NULL;
+ IplImage* imgRGB = NULL;
+
+ if ( x_size != img->width || y_size != img->height ){
+ releaseModules();
+ createModules();
+ x_size = img->width;
+ y_size = img->height;
+ }
+
+ imgRGB = cvCreateImage(cvSize(img->width,img->height),img->depth,3);
+ switch (img->nChannels){
+ case 1:
+ cvCvtColor(img, imgRGB, CV_GRAY2RGB);
+ break;
+ case 4:
+ cvCvtColor(img, imgRGB, CV_RGBA2RGB);
+ break;
+ }
+
+ /* Process: */
+ m_tracker->Process(imgRGB, pMask);
+
+ //~ t_atom blob_num;
+ //~ SETFLOAT(&blob_num, m_tracker->GetBlobNum());
+ //~ outlet_anything(m_dataout, gensym("blobnum"), 1, &blob_num);
+
+ int blob_num=m_tracker->GetBlobNum();
+ int blobMatrixWidth=6;
+ int blob_atom_size = 2+blob_num*blobMatrixWidth;
+
+ t_atom* blob_atom = new t_atom[blob_atom_size];
+ SETFLOAT(&blob_atom[0], blob_num);
+ SETFLOAT(&blob_atom[1], blobMatrixWidth);
+
+ for(int i=0; i<blob_num; i++){
+ CvBlob* blob = m_tracker->GetBlob(i);
+ if ( blob->w*blob->h/(img->width*img->height) > m_areaThreshold ){
+ SETFLOAT(&blob_atom[2+i*blobMatrixWidth], blob->ID);
+ SETFLOAT(&blob_atom[3+i*blobMatrixWidth], blob->x/img->width);
+ SETFLOAT(&blob_atom[4+i*blobMatrixWidth], blob->y/img->height);
+ SETFLOAT(&blob_atom[5+i*blobMatrixWidth], blob->w/img->width);
+ SETFLOAT(&blob_atom[6+i*blobMatrixWidth], blob->h/img->height);
+ SETFLOAT(&blob_atom[7+i*blobMatrixWidth], m_tracker->GetState(blob->ID));
+ }
+ }
+
+ outlet_anything(m_dataout, gensym("cvblob"), blob_atom_size, blob_atom);
+
+ if(blob_atom) delete blob_atom;
+ blob_atom = NULL;
+
+ if ( m_monitoring_stage == 1 ) { // show foreground
+ IplImage* fg = m_tracker->GetFGMask();
+ switch (img->nChannels){
+ case 1:
+ cvCopy(fg, img);
+ break;
+ case 4:
+ cvCvtColor(fg, img, CV_GRAY2RGBA);
+ break;
+ }
+ }
+
+ if ( m_monitoring_stage == 2 ) {
+ /* Draw all information about test sequence: */
+ char str[1024];
+ int line_type = CV_AA; // Change it to 8 to see non-antialiased graphics.
+ CvFont font;
+ int i;
+
+ cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 0.7, 0.7, 0, 1, line_type );
+
+ for(i=m_tracker->GetBlobNum(); i>0; i--)
+ {
+ CvSize TextSize;
+ CvBlob* pB = m_tracker->GetBlob(i-1);
+ CvPoint p = cvPoint(cvRound(pB->x*256),cvRound(pB->y*256));
+ CvSize s = cvSize(MAX(1,cvRound(CV_BLOB_RX(pB)*256)), MAX(1,cvRound(CV_BLOB_RY(pB)*256)));
+ int c = cvRound(255*m_tracker->GetState(CV_BLOB_ID(pB)));
+
+ cvEllipse( imgRGB,
+ p,
+ s,
+ 0, 0, 360,
+ CV_RGB(c,255-c,0), cvRound(1+(3*0)/255), CV_AA, 8 );
+
+ p.x >>= 8;
+ p.y >>= 8;
+ s.width >>= 8;
+ s.height >>= 8;
+ sprintf(str,"%03d",CV_BLOB_ID(pB));
+ cvGetTextSize( str, &font, &TextSize, NULL );
+ p.y -= s.height;
+ cvPutText( imgRGB, str, p, &font, CV_RGB(0,255,255));
+ {
+ const char* pS = m_tracker->GetStateDesc(CV_BLOB_ID(pB));
+
+ if(pS)
+ {
+ char* pStr = strdup(pS);
+ char* pStrFree = pStr;
+
+ while (pStr && strlen(pStr) > 0)
+ {
+ char* str_next = strchr(pStr,'\n');
+
+ if(str_next)
+ {
+ str_next[0] = 0;
+ str_next++;
+ }
+
+ p.y += TextSize.height+1;
+ cvPutText( imgRGB, pStr, p, &font, CV_RGB(0,255,255));
+ pStr = str_next;
+ }
+ free(pStrFree);
+ }
+ }
+
+ } /* Next blob. */;
+ switch (img->nChannels){
+ case 1:
+ cvCvtColor(imgRGB, img, CV_RGB2GRAY);
+ break;
+ case 4:
+ cvCvtColor(imgRGB, img, CV_RGB2RGBA);
+ break;
+ }
+
+ }/* Draw all information about test sequence. */
+ cvReleaseImage(&imgRGB);
+ imgRGB = NULL;
+
+} /* RunBlobTrackingAuto */
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_blobtrack :: obj_setupCallback(t_class *classPtr)
+{
+ CPPEXTERN_MSG1(classPtr, "monitorStage", monitorStageMess, int);
+ CPPEXTERN_MSG1(classPtr, "fgTrainFrames", fgTrainFramesMess, int);
+ CPPEXTERN_MSG (classPtr, "getParam", getParamMess);
+ CPPEXTERN_MSG (classPtr, "setParam", setParamMess);
+ CPPEXTERN_MSG (classPtr, "getModule", getModuleMess);
+ CPPEXTERN_MSG (classPtr, "setModule", setModuleMess);
+ CPPEXTERN_MSG1 (classPtr, "areaThreshold", areaThresholdMess, t_float);
+}
+
+/////////////////////////////////////////////////////////
+// private function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_blobtrack :: setupModules()
+{
+ const char* fg_name = m_fg_name.c_str();
+ const char* bd_name = m_bd_name.c_str();
+ const char* bt_name = m_bt_name.c_str();
+ const char* btpp_name = m_btpp_name.c_str();
+ const char* bta_name = m_bta_name.c_str();
+
+ for(m_FGModule=FGDetector_Modules; m_FGModule->nickname; ++m_FGModule)
+ if( fg_name && MY_STRICMP(fg_name,m_FGModule->nickname)==0 ) break;
+ if ( m_FGModule->nickname == NULL ){
+ error("FG Module %s doesn't exist, swith to default one", fg_name);
+ m_FGModule=FGDetector_Modules;
+ }
+
+ for(m_BDModule=BlobDetector_Modules; m_BDModule->nickname; ++m_BDModule)
+ if( bd_name && MY_STRICMP(bd_name,m_BDModule->nickname)==0 ) break;
+ if ( m_BDModule->nickname == NULL ){
+ error("BD Module %s doesn't exist, swith to default one", bd_name);
+ m_BDModule=BlobDetector_Modules;
+ }
+
+ for(m_BTModule=BlobTracker_Modules; m_BTModule->nickname; ++m_BTModule)
+ if( bt_name && MY_STRICMP(bt_name,m_BTModule->nickname)==0 ) break;
+ if ( m_BTModule->nickname == NULL ){
+ error("BT Module %s doesn't exist, swith to default one", bt_name);
+ m_BTModule=BlobTracker_Modules;
+ }
+
+ for(m_BTPostProcModule=BlobTrackPostProc_Modules; m_BTPostProcModule->nickname; ++m_BTPostProcModule)
+ if( btpp_name && MY_STRICMP(btpp_name,m_BTPostProcModule->nickname)==0 ) break;
+ if ( m_BTPostProcModule->nickname == NULL ){
+ error("BTPP Module %s doesn't exist, swith to default one", btpp_name);
+ m_BTPostProcModule=BlobTrackPostProc_Modules;
+ }
+
+ for(m_BTAnalysisModule=BlobTrackAnalysis_Modules; m_BTAnalysisModule->nickname; ++m_BTAnalysisModule)
+ if( bta_name && MY_STRICMP(bta_name,m_BTAnalysisModule->nickname)==0 ) break;
+ if ( m_BTAnalysisModule->nickname == NULL ){
+ error("BTA Module %s doesn't exist, swith to default one", bta_name);
+ m_BTAnalysisModule=BlobTrackAnalysis_Modules;
+ }
+} /* setupModules */
+
+void pix_opencv_blobtrack :: createModules()
+{ /* Create autotracker module and its components: */
+
+ m_param.FGTrainFrames = m_FGTrainFrames;
+ /* Create FG Detection module: */
+ m_param.pFG = m_FGModule->create();
+ if(!m_param.pFG){
+ error("Can not create FGDetector module");
+ return;
+ }
+ m_param.pFG->SetNickName(m_FGModule->nickname);
+ //~ set_params(argc, argv, m_param.pFG, "fg", m_FGModule->nickname);
+ /* Create Blob Entrance Detection module: */
+ m_param.pBD = m_BDModule->create();
+ if(!m_param.pBD){
+ error("Can not create BlobDetector module");
+ return;
+ }
+ m_param.pBD->SetNickName(m_BDModule->nickname);
+ //~ set_params(argc, argv, m_param.pBD, "bd", m_BDModule->nickname);
+
+ /* Create blob tracker module: */
+ m_param.pBT = m_BTModule->create();
+ if(!m_param.pBT){
+ error("Can not create BlobTracker module");
+ }
+ m_param.pBT->SetNickName(m_BTModule->nickname);
+ //~ set_params(argc, argv, m_param.pBT, "bt", m_BTModule->nickname);
+
+ /* Create blob trajectory generation module: */
+ /*
+ m_param.pBTGen = NULL;
+ if(m_BTGenModule && track_name && m_BTGenModule->create)
+ {
+ m_param.pBTGen = m_BTGenModule->create();
+ m_param.pBTGen->SetFileName(track_name);
+ }
+ if(m_param.pBTGen)
+ {
+ m_param.pBTGen->SetNickName(m_BTGenModule->nickname);
+ //~ set_params(argc, argv, m_param.pBTGen, "btgen", m_BTGenModule->nickname);
+ }
+ */
+
+ /* Create blob trajectory post processing module: */
+ m_param.pBTPP = NULL;
+ if(m_BTPostProcModule && m_BTPostProcModule->create)
+ {
+ m_param.pBTPP = m_BTPostProcModule->create();
+ }
+ if(m_param.pBTPP)
+ {
+ m_param.pBTPP->SetNickName(m_BTPostProcModule->nickname);
+ //~ set_params(argc, argv, m_param.pBTPP, "btpp", pBTPostProcModule->nickname);
+ }
+
+ const char * bt_corr = m_bt_corr.c_str();
+
+ m_param.UsePPData = (bt_corr && MY_STRICMP(bt_corr,"PostProcRes")==0);
+
+ /* Create blob trajectory analysis module: */
+ m_param.pBTA = NULL;
+ if(m_BTAnalysisModule && m_BTAnalysisModule->create)
+ {
+ m_param.pBTA = m_BTAnalysisModule->create();
+ //~ m_param.pBTA->SetFileName(bta_data_name);
+ }
+ if(m_param.pBTA)
+ {
+ m_param.pBTA->SetNickName(m_BTAnalysisModule->nickname);
+ //~ set_params(argc, argv, m_param.pBTA, "bta", m_BTAnalysisModule->nickname);
+ }
+
+ /* Create whole pipline: */
+ m_tracker = cvCreateBlobTrackerAuto1(&m_param);
+ if(!m_tracker)
+ error("Can not create BlobTrackerAuto");
+} /* createModules */
+
+void pix_opencv_blobtrack :: releaseModules(void)
+{
+ if(m_param.pBT)cvReleaseBlobTracker(&m_param.pBT);
+ if(m_param.pBD)cvReleaseBlobDetector(&m_param.pBD);
+ if(m_param.pBTGen)cvReleaseBlobTrackGen(&m_param.pBTGen);
+ if(m_param.pBTA)cvReleaseBlobTrackAnalysis(&m_param.pBTA);
+ if(m_param.pFG)cvReleaseFGDetector(&m_param.pFG);
+ if(m_tracker)cvReleaseBlobTrackerAuto(&m_tracker);
+} /* releaseModules */
+
+void pix_opencv_blobtrack :: print_params(CvVSModule* pM, const char* module)
+{
+ int i;
+ if(pM->GetParamName(0) == NULL ) return;
+
+
+ post("%s(%s) module parameters:",module,pM->GetNickName());
+
+ for (i=0; ; ++i)
+ {
+ const char* param = pM->GetParamName(i);
+ const char* str = param?pM->GetParamStr(param):NULL;
+ if(param == NULL)break;
+ if(str)
+ {
+ post(" %s: %s",param,str);
+ }
+ else
+ {
+ post(" %s: %g",param,pM->GetParam(param));
+ }
+ }
+} /* print_params */
+
+/////////////////////////////////////////////////////////
+// messages handling
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_blobtrack :: monitorStageMess(int arg)
+{
+ m_monitoring_stage = int(arg);
+ t_atom data_out;
+ SETFLOAT(&data_out, m_monitoring_stage);
+ outlet_anything( m_dataout, gensym("monitorStage"), 1, &data_out);
+}
+
+void pix_opencv_blobtrack :: areaThresholdMess(t_float arg)
+{
+ m_areaThreshold = arg;
+ t_atom data_out;
+ SETFLOAT(&data_out, m_areaThreshold);
+ outlet_anything( m_dataout, gensym("areaThreshold"), 1, &data_out);
+}
+
+void pix_opencv_blobtrack :: fgTrainFramesMess(int arg)
+{
+ //~ m_FGTrainFrames = int(arg);
+ t_atom data_out;
+ SETFLOAT(&data_out, m_FGTrainFrames);
+ outlet_anything( m_dataout, gensym("FGTrainFrames"), 1, &data_out);
+}
+
+void pix_opencv_blobtrack :: printParamsMess(void)
+{ /* Print module parameters: */
+ struct DefMMM
+ {
+ CvVSModule* pM;
+ const char* name;
+ } Modules[] = {
+ {(CvVSModule*)m_param.pFG,"FGdetector"},
+ {(CvVSModule*)m_param.pBD,"BlobDetector"},
+ {(CvVSModule*)m_param.pBT,"BlobTracker"},
+ {(CvVSModule*)m_param.pBTGen,"TrackGen"},
+ {(CvVSModule*)m_param.pBTPP,"PostProcessing"},
+ {(CvVSModule*)m_param.pBTA,"TrackAnalysis"},
+ {NULL,NULL}
+ };
+ int i;
+ for(i=0; Modules[i].name; ++i)
+ {
+ if(Modules[i].pM)
+ print_params(Modules[i].pM,Modules[i].name);
+ }
+} /* Print module parameters. */
+
+void pix_opencv_blobtrack :: getParamMess(t_symbol*s, int argc, t_atom*argv)
+{
+ int i;
+ CvVSModule* pM;
+ pM=NULL;
+ if ( argc < 1 ){
+ error("getParam need a module name as symbol arg");
+ return;
+ }
+
+ if ( argv[0].a_type == A_SYMBOL ) {
+ if (std::string(argv[0].a_w.w_symbol->s_name) == "fg") {
+ pM=m_param.pFG;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "bd") {
+ pM=m_param.pBD;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "bt") {
+ pM=m_param.pBT;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "btpp") {
+ pM=m_param.pBTPP;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "bta") {
+ pM=m_param.pBTA;
+ }
+ else {
+ error("unknown module : %s",argv[0].a_w.w_symbol->s_name);
+ return;
+ }
+ } else {
+ error("getParam need a module name as symbol arg");
+ }
+
+ t_atom a_param[4];
+ t_atom a_params;
+
+ a_param[0] = argv[0];
+
+ if(pM->GetParamName(0) == NULL ) return;
+
+ for (i=0; ; ++i)
+ {
+ const char* param = pM->GetParamName(i);
+ if(param == NULL)break;
+ }
+ SETFLOAT(&a_params, i);
+ outlet_anything(m_dataout, gensym("params"), 1, &a_params);
+
+ for (i=0; ; ++i)
+ {
+ const char* param = pM->GetParamName(i);
+ const char* str = param?pM->GetParamStr(param):NULL;
+
+ if(param == NULL)break;
+ SETSYMBOL(a_param+1,gensym(param));
+ if(str)
+ {
+ SETSYMBOL(a_param+2,gensym(str));
+ }
+ else
+ {
+ SETFLOAT(a_param+2,pM->GetParam(param));
+ }
+ const char* comment = pM->GetParamComment(param);
+ if ( !comment ) comment = "N/A";
+ SETSYMBOL(a_param+3,gensym(comment));
+
+ outlet_anything(m_dataout, gensym("param"), 4, a_param);
+ }
+} /* getParamMess */
+
+void pix_opencv_blobtrack :: setParamMess(t_symbol*s, int argc, t_atom*argv)
+{
+ if ( argc != 3 ){
+ error("setParam need 3 args : <module name> <parameter name> <value>");
+ return;
+ }
+
+ int i;
+ CvVSModule* pM;
+ pM=NULL;
+
+ if ( argv[0].a_type == A_SYMBOL ) {
+ if (std::string(argv[0].a_w.w_symbol->s_name) == "fg") {
+ pM=m_param.pFG;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "bd") {
+ pM=m_param.pBD;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "bt") {
+ pM=m_param.pBT;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "btpp") {
+ pM=m_param.pBTPP;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "bta") {
+ pM=m_param.pBTA;
+ }
+ else {
+ error("unknown module : %s",argv[0].a_w.w_symbol->s_name);
+ return;
+ }
+ } else {
+ error("getParam need a module name as symbol arg");
+ }
+
+ const char* param;
+ const char* str;
+ for ( i = 0 ; ; i++ ){
+ param = pM->GetParamName(i);
+ str = param?pM->GetParamStr(param):NULL;
+
+ if(param == NULL){
+ error("module %s doesn't have a %s parameter", argv[0].a_w.w_symbol->s_name, argv[1].a_w.w_symbol->s_name);
+ return;
+ }
+
+ if( std::string(argv[1].a_w.w_symbol->s_name) == param )
+ break;
+ }
+
+ if ( str ){ /* parameter is a string */
+ if ( argv[2].a_type != A_SYMBOL ){
+ error("module (%s) parameter (%s) is a symbol");
+ return;
+ } else {
+ pM->SetParamStr(param, argv[2].a_w.w_symbol->s_name);
+ }
+ } else {
+ if ( argv[2].a_type != A_FLOAT ){
+ error("module (%s) parameter (%s) is a float");
+ return;
+ } else {
+ pM->SetParam(param, argv[2].a_w.w_float);
+ }
+ }
+
+ pM->ParamUpdate();
+}
+
+void pix_opencv_blobtrack :: getModuleMess(t_symbol*s, int argc, t_atom*argv)
+{
+ if ( argc == 0 ){ // no args : print all modules in pipeline
+ post("avaible modules in pipeline :\n \
+ fg : ForeGround detector\n \
+ bd : Blob Detector\n \
+ bt : Blob Tracker\n \
+ btpp : Blob Tracker Post Processing\n \
+ bta : Blob Tracker Analysis");
+
+ t_atom module_list[5];
+ SETSYMBOL(&module_list[0], gensym("fg"));
+ SETSYMBOL(&module_list[1], gensym("bd"));
+ SETSYMBOL(&module_list[2], gensym("bt"));
+ SETSYMBOL(&module_list[3], gensym("btpp"));
+ SETSYMBOL(&module_list[4], gensym("bta"));
+ outlet_anything(m_dataout, gensym("modulelist"), 5, module_list);
+
+ } else if ( argc == 1 ) { // one arg : print available algo for specified module
+ if ( argv[0].a_type == A_SYMBOL ) {
+ t_atom algo_list[512];
+ int i;
+ if ( std::string(argv[0].a_w.w_symbol->s_name) == "fg" ){
+ post("available foreground detector algo :");
+ for (i = 0 ; FGDetector_Modules[i].nickname!=NULL ; i++){
+ post("\t%s : %s", FGDetector_Modules[i].nickname, FGDetector_Modules[i].description);
+ SETSYMBOL(&algo_list[i],gensym(FGDetector_Modules[i].nickname));
+ }
+ outlet_anything(m_dataout, gensym("fg_algo"), i, algo_list);
+ } else if ( std::string(argv[0].a_w.w_symbol->s_name) == "bd" ){
+ post("available blob detector algo :");
+ for (i = 0 ; BlobDetector_Modules[i].nickname!=NULL ; i++){
+ post("\t%s : %s", BlobDetector_Modules[i].nickname, BlobDetector_Modules[i].description);
+ SETSYMBOL(&algo_list[i],gensym(BlobDetector_Modules[i].nickname));
+ }
+ outlet_anything(m_dataout, gensym("bd_algo"), i, algo_list);
+ } else if ( std::string(argv[0].a_w.w_symbol->s_name) == "bt" ){
+ post("available blob tracker algo :");
+ for (i = 0 ; BlobTracker_Modules[i].nickname!=NULL ; i++){
+ post("\t%s : %s", BlobTracker_Modules[i].nickname, BlobTracker_Modules[i].description);
+ SETSYMBOL(&algo_list[i],gensym(BlobTracker_Modules[i].nickname));
+ }
+ outlet_anything(m_dataout, gensym("bt_algo"), i, algo_list);
+ } else if ( std::string(argv[0].a_w.w_symbol->s_name) == "btpp" ){
+ post("available blob tracker post processing algo :");
+ for (i = 0 ; BlobTrackPostProc_Modules[i].nickname!=NULL ; i++){
+ post("\t%s : %s", BlobTrackPostProc_Modules[i].nickname, BlobTrackPostProc_Modules[i].description);
+ SETSYMBOL(&algo_list[i],gensym(BlobTrackPostProc_Modules[i].nickname));
+ }
+ outlet_anything(m_dataout, gensym("btpp_algo"), i, algo_list);
+ } else if ( std::string(argv[0].a_w.w_symbol->s_name) == "bta" ){
+ post("available blob tracker analysis algo :");
+ for (i = 0 ; BlobTrackAnalysis_Modules[i].nickname!=NULL ; i++){
+ post("\t%s : %s", BlobTrackAnalysis_Modules[i].nickname, BlobTrackAnalysis_Modules[i].description);
+ SETSYMBOL(&algo_list[i],gensym(BlobTrackAnalysis_Modules[i].nickname));
+ }
+ outlet_anything(m_dataout, gensym("bd_algo"), i, algo_list);
+ }
+ } else error("getModules <symbol> [<symbol>]");
+ } else error("getModules <symbol> [<symbol>] : need 1 or 2 args");
+}
+
+void pix_opencv_blobtrack :: setModuleMess(t_symbol*s, int argc, t_atom*argv)
+{
+ if ( argc != 2 ){
+ error("use : setModule <module> <module_name>");
+ error("try getModule to get available module list");
+ return;
+ }
+
+ if ( argv[0].a_type == A_SYMBOL ) {
+ if (std::string(argv[0].a_w.w_symbol->s_name) == "fg") {
+ m_fg_name = argv[1].a_w.w_symbol->s_name;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "bd") {
+ m_bd_name = argv[1].a_w.w_symbol->s_name;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "bt") {
+ m_bt_name = argv[1].a_w.w_symbol->s_name;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "btpp") {
+ m_btpp_name = argv[1].a_w.w_symbol->s_name;
+ }
+ else if (std::string(argv[0].a_w.w_symbol->s_name) == "bta") {
+ m_bta_name = argv[1].a_w.w_symbol->s_name;
+ }
+ releaseModules();
+ setupModules();
+ createModules();
+ printParamsMess();
+ }
+}
diff --git a/src/pix_opencv_blobtrack.h b/src/pix_opencv_blobtrack.h
new file mode 100644
index 0000000..2e00513
--- /dev/null
+++ b/src/pix_opencv_blobtrack.h
@@ -0,0 +1,106 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ 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_BLOBTRACK_H_
+#define INCLUDE_PIX_OPENCV_BLOBTRACK_H_
+
+#ifndef _EiC
+#include "opencv2/video/background_segm.hpp"
+#include "opencv2/legacy/blobtrack.hpp"
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/highgui/highgui.hpp"
+#include <opencv2/imgproc/imgproc_c.h>
+#endif
+
+#include "blobtrack.h"
+
+#include "Base/GemPixObj.h"
+#include <stdio.h>
+#include <RTE/MessageCallbacks.h>
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_blobtrack
+
+ advanced blob tracker
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_blobtrack : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_blobtrack, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_blobtrack();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_blobtrack();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+ void RunBlobTrackingAuto( IplImage* img );
+
+ //////////
+ // Messages handling
+ void monitorStageMess(int arg);
+ void areaThresholdMess(t_float arg);
+ void fgTrainFramesMess(int arg);
+ void printParamsMess(void);
+ void getParamMess(t_symbol*s, int argc, t_atom*argv); // get available params or param value
+ void setParamMess(t_symbol*s, int argc, t_atom*argv); // set param
+ void getModuleMess(t_symbol*s, int argc, t_atom*argv); // list available and currently used modules
+ void setModuleMess(t_symbol*s, int argc, t_atom*argv); // set selected modules
+
+ // Members
+ std::string m_fg_name, m_bd_name, m_bt_name, m_btgen_name, m_btpp_name, m_bta_name, m_bt_corr;
+ int m_FGTrainFrames;
+ int m_monitoring_stage; // 0 : input image, 1 : FG 3 : input with trackng info
+ t_float m_areaThreshold;
+ int x_size, y_size;
+
+
+ CvBlobTrackerAuto* m_tracker;
+ CvBlobTrackerAutoParam1 m_param;
+
+ DefModule_FGDetector* m_FGModule;
+ DefModule_BlobDetector* m_BDModule;
+ DefModule_BlobTracker* m_BTModule;
+ DefModule_BlobTrackPostProc* m_BTPostProcModule;
+ DefModule_BlobTrackGen* m_BTGenModule;
+ DefModule_BlobTrackAnalysis* m_BTAnalysisModule;
+
+
+ private:
+
+ void setupModules();
+ void createModules();
+ void releaseModules(void);
+ void print_params(CvVSModule* pM, const char* module);
+ t_outlet *m_dataout; // info outlet
+
+};
+#endif // for header file
diff --git a/src/pix_opencv_calibration.cc b/src/pix_opencv_calibration.cc
new file mode 100644
index 0000000..053a6a5
--- /dev/null
+++ b/src/pix_opencv_calibration.cc
@@ -0,0 +1,547 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org )
+// camera calibration function by Antoine Villeret helped by Cyrille Henry
+
+#include "pix_opencv_calibration.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_calibration)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_calibration
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_calibration :: pix_opencv_calibration()
+{
+ m_dataout = outlet_new(this->x_obj, 0);
+
+ find_rgb = NULL;
+ find_gray = NULL;
+ rgb = NULL;
+ gray = NULL;
+ tmp = NULL;
+ mapx = NULL;
+ mapy = NULL;
+
+ success_count = 0;
+ board_view_nb = 10;
+ calibration = 0;
+ patternSize[0] = 6;
+ patternSize[1] = 7;
+ frame = 0;
+ wait_n_frame = 10;
+ findChessFlag = CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FILTER_QUADS;
+
+ // allocate storage matrix
+ image_points = cvCreateMat(patternSize[0]*patternSize[1]*board_view_nb, 2, CV_32FC1);
+ object_points = cvCreateMat(patternSize[0]*patternSize[1]*board_view_nb, 3, CV_32FC1);
+ point_counts = cvCreateMat(board_view_nb, 1, CV_32SC1);
+ intrinsic_matrix = cvCreateMat(3, 3, CV_32FC1);
+ distortion_coeffs = cvCreateMat(5, 1, CV_32FC1);
+
+ pix_opencv_calibration :: resetCorrectionMatrix();
+ //~ post("pix_opencv_calibration build on %s at %s", __DATE__, __TIME__);
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_calibration :: ~pix_opencv_calibration()
+{
+ //Destroy cv_images to clean memory
+ if ( find_rgb ) cvReleaseImage(&find_rgb);
+ if ( find_gray ) cvReleaseImage(&find_gray);
+ if ( gray ) cvReleaseImage(&gray);
+ if ( rgb ) cvReleaseImage(&rgb);
+ if ( tmp ) cvReleaseImage(&tmp);
+ if ( mapx ) cvReleaseImage(&mapx);
+ if ( mapy ) cvReleaseImage(&mapy);
+ cvReleaseMat(&intrinsic_matrix);
+ cvReleaseMat(&distortion_coeffs);
+ cvReleaseMat(&image_points);
+ cvReleaseMat(&object_points);
+ cvReleaseMat(&point_counts);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_calibration :: processRGBAImage(imageStruct &image)
+{
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+ if ( calibration ) error ( "image size changed, calibration was cancelled");
+ calibration = 0;
+
+ if ( find_rgb ) cvReleaseImage(&find_rgb);
+ if ( find_gray ) cvReleaseImage(&find_gray);
+ if ( gray ) cvReleaseImage(&gray); // TODO : cette ligne crash qd on passe du gray a couleur apres ou pendant calibration
+ if ( rgb ) cvReleaseImage(&rgb);
+ if ( tmp ) cvReleaseImage(&tmp);
+ if ( mapx ) cvReleaseImage(&mapx);
+ if ( mapy ) cvReleaseImage(&mapy);
+
+ // used in findCorners
+ find_rgb = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 4);
+ find_gray = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 1);
+
+ //create the images with new size
+ rgb = cvCreateImageHeader(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 4);
+ tmp = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 4);
+ mapx = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_32F, 1);
+ mapy = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_32F, 1);
+
+ // create new map
+ cvInitUndistortMap(this->intrinsic_matrix, this->distortion_coeffs, this->mapx, this->mapy);
+ }
+
+ // no need to copy a lot of memory, just point to it...
+ rgb->imageData = (char*) image.data;
+
+ // this will loop until we got enought views (x->board_view_nb) with all corners visible
+ if ( success_count < board_view_nb && calibration != 0 ) {
+ findCorners( rgb );
+ image.data = (unsigned char*) rgb->imageData;
+ }
+ else if ( success_count >= board_view_nb && calibration != 0 ) {
+ computeCalibration( rgb );
+ image.data = (unsigned char*) rgb->imageData;
+ }
+ else if ( this->calibration == 0 ) {
+ cvRemap(rgb,tmp,mapx,mapy);
+ image.data = (unsigned char*) tmp->imageData;
+ }
+}
+
+void pix_opencv_calibration :: processRGBImage(imageStruct &image) {
+ error( "pix_opencv_calibration : rgb format not supported");
+}
+
+void pix_opencv_calibration :: processYUVImage(imageStruct &image) {
+ error( "pix_opencv_calibration : yuv format not supported" );
+}
+
+void pix_opencv_calibration :: processGrayImage(imageStruct &image)
+{
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!gray)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+ if ( calibration ) {
+ error ( "image size changed, calibration was cancelled");
+ calibration = 0;
+
+ t_atom data_out;
+ SETFLOAT(&data_out, calibration);
+ outlet_anything( this->m_dataout, gensym("calibration"), 1, &data_out);
+ }
+
+ if ( find_rgb ) cvReleaseImage(&find_rgb);
+ if ( find_gray ) cvReleaseImage(&find_gray);
+ if ( gray ) cvReleaseImage(&gray);
+ if ( rgb ) cvReleaseImage(&rgb);
+ if ( tmp ) cvReleaseImage(&tmp);
+ if ( mapx ) cvReleaseImage(&mapx);
+ if ( mapy ) cvReleaseImage(&mapy);
+
+ // used in findCorners
+ find_rgb = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 4);
+ find_gray = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 1);
+
+ //create the images with new size
+ gray = cvCreateImageHeader(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 1);
+ tmp = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_8U, 1);
+ mapx = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_32F, 1);
+ mapy = cvCreateImage(cvSize(this->comp_xsize,this->comp_ysize), IPL_DEPTH_32F, 1);
+
+ // create new map
+ cvInitUndistortMap(this->intrinsic_matrix, this->distortion_coeffs, this->mapx, this->mapy);
+ }
+
+ // no need to copy a lot of memory, just point to it...
+ gray->imageData = (char*) image.data;
+
+ // this will loop until we got enought views (x->board_view_nb) with all corners visible
+ if ( success_count < board_view_nb && calibration != 0 ) {
+ findCorners( gray );
+ image.data = (unsigned char*) gray->imageData;
+ }
+ else if ( success_count >= board_view_nb && calibration != 0 ) {
+ computeCalibration( gray );
+ image.data = (unsigned char*) gray->imageData;
+ }
+ else if ( this->calibration == 0 ) {
+ cvRemap(gray,tmp,mapx,mapy);
+ image.data = (unsigned char*) tmp->imageData;
+ }
+}
+
+/////////////////////////////////////////////////////////
+// findCorners
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_calibration :: findCorners ( IplImage *image )
+{
+ int board_point_nb = this->patternSize[0]*this->patternSize[1];
+ CvPoint2D32f *corners = new CvPoint2D32f[board_point_nb];
+ int corner_count;
+ int step;
+ CvSize patternSize, image_size;
+
+ patternSize = cvSize( this->patternSize[0], this->patternSize[1] );
+ image_size = cvSize( image->width, image->height );
+
+ // find chessboard corners (gray or RGBA image...)
+ int found = cvFindChessboardCorners(image,
+ patternSize,
+ corners,
+ &corner_count,
+ findChessFlag);
+ if (image->nChannels == 4) {
+ cvCopy(image, find_rgb) ;
+ cvCvtColor( image , find_gray , CV_RGBA2GRAY); // convert color to gray
+ } else {
+ cvCopy(image, find_gray) ;
+ }
+
+ // get subpixel accuracy on those corners (grayscale image only)
+ cvFindCornerSubPix(find_gray,
+ corners,
+ corner_count,
+ cvSize(11,11),
+ cvSize(-1,-1),
+ cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 30, 0.1));
+
+
+ // draw chessboard corner (color image only)
+ if (image->nChannels == 4) cvDrawChessboardCorners(find_rgb, patternSize, corners, corner_count, found);
+ else
+ {
+ cvCvtColor( find_gray , find_rgb , CV_GRAY2RGBA); // convert gray to color
+ cvDrawChessboardCorners(find_rgb, patternSize, corners, corner_count, found);
+ }
+
+ this->frame++;
+ if ( this->frame % this->wait_n_frame == 0 ) {
+ // update arrays
+
+ if( corner_count == board_point_nb ) {
+ step = this->success_count*board_point_nb;
+ for( int i=step, j=0; j<board_point_nb; ++i,++j ) {
+ CV_MAT_ELEM(*this->image_points, float,i,0) = corners[j].x;
+ CV_MAT_ELEM(*this->image_points, float,i,1) = corners[j].y;
+ CV_MAT_ELEM(*this->object_points,float,i,0) = j/this->patternSize[0];
+ CV_MAT_ELEM(*this->object_points,float,i,1) = j%this->patternSize[0];
+ CV_MAT_ELEM(*this->object_points,float,i,2) = 0.0f;
+ }
+ CV_MAT_ELEM(*this->point_counts, int,this->success_count,0) = board_point_nb;
+ this->success_count++;
+
+ cvNot( find_rgb , find_rgb );
+ }
+
+ t_atom data_out;
+ SETFLOAT(&data_out, success_count);
+ outlet_anything( this->m_dataout, gensym("take"), 1, &data_out);
+
+ }
+
+ // convert color to gray
+ if (image->nChannels == 1) {
+ cvCvtColor( find_rgb , image, CV_RGBA2GRAY); // convert color to gray
+ } else {
+ cvCopy(find_rgb, image);
+ }
+
+}
+/////////////////////////////////////////////////////////
+// computeCalibration
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_calibration :: computeCalibration ( IplImage *image )
+{
+ //CALIBRATE THE CAMERA!
+ cvCalibrateCamera2(this->object_points,
+ this->image_points,
+ this->point_counts,
+ cvSize( image->width , image->height ),
+ this->intrinsic_matrix,
+ this->distortion_coeffs,
+ NULL,
+ NULL,
+ 0);
+ cvReleaseImage(&mapx);
+ cvReleaseImage(&mapy);
+ this->mapx = cvCreateImage( cvSize( image->width, image->height ), IPL_DEPTH_32F, 1 );
+ this->mapy = cvCreateImage( cvSize( image->width, image->height ), IPL_DEPTH_32F, 1 );
+
+ cvInitUndistortMap(this->intrinsic_matrix, this->distortion_coeffs, this->mapx, this->mapy);
+
+ t_atom intra_out[9];
+ for ( int i = 0 ; i < 9 ; i++ ){
+ SETFLOAT(&intra_out[i], CV_MAT_ELEM( *intrinsic_matrix, float, i%3, i/3));
+ }
+ outlet_anything( this->m_dataout, gensym("intrinsic_matrix"), 9, intra_out);
+
+ t_atom dist_out[5];
+ for ( int i = 0 ; i < 5 ; i++ ){
+ SETFLOAT(&dist_out[i], CV_MAT_ELEM( *distortion_coeffs, float, i, 0));
+ }
+ outlet_anything( this->m_dataout, gensym("distortion_coeffs"), 5, dist_out);
+
+ calibration = 0;
+
+ t_atom data_out;
+ SETFLOAT(&data_out, calibration);
+ outlet_anything( this->m_dataout, gensym("calibration"), 1, &data_out);
+}
+/////////////////////////////////////////////////////////
+// LoadMess
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_calibration :: loadIntraMess (t_symbol *filename)
+{
+ if ( filename->s_name[0] == 0 ) {
+ error("no filename passed to loadIntra message");
+ return;
+ }
+ if ( filename == NULL ) { error("%s is not a valid matrix", filename->s_name); return;}
+ this->intrinsic_matrix = (CvMat*)cvLoad(filename->s_name, 0, 0, 0);// TODO crash when passing non-XML file
+
+ if (intrinsic_matrix == NULL) {
+ intrinsic_matrix = cvCreateMat(3, 3, CV_32FC1);
+ error("can't open file %s", filename->s_name);
+ resetCorrectionMatrix();
+ }
+ else if ( intrinsic_matrix->rows != 3 || intrinsic_matrix->cols != 3 || CV_MAT_TYPE(intrinsic_matrix->type) != CV_32FC1 ) {
+ error("%s is not a valid intrinsic matrix", filename->s_name);
+ cvReleaseMat(&intrinsic_matrix);
+ intrinsic_matrix = cvCreateMat(3, 3, CV_32FC1);
+ resetCorrectionMatrix();
+ }
+ else post("load transformation matrix from %s",filename->s_name);
+
+ t_atom intra_out[9];
+ for ( int i = 0 ; i < 9 ; i++ ){
+ SETFLOAT(&intra_out[i], CV_MAT_ELEM( *intrinsic_matrix, float, i%3, i/3));
+ }
+ outlet_anything( this->m_dataout, gensym("intrinsic_matrix"), 9, intra_out);
+
+ // reinitialise size to force reinitialisation of mapx and mapy on next frame
+ this->comp_xsize = 0;
+}
+
+void pix_opencv_calibration :: loadDistMess (t_symbol *filename)
+{
+ if ( filename->s_name[0] == 0 ) {
+ error("no filename passed to loadDist message");
+ return;
+ }
+ if ( filename == NULL ) { error("NULL pointer passed to function loadDist"); return;}
+ distortion_coeffs = (CvMat*)cvLoad(filename->s_name); // TODO crash when passing non-XML file
+
+ if (distortion_coeffs == NULL) {
+ distortion_coeffs = cvCreateMat(5, 1, CV_32FC1);
+ error("can't open file %s", filename->s_name);
+ resetCorrectionMatrix();
+ }
+ else if( distortion_coeffs->rows != 5 || distortion_coeffs->cols != 1 || CV_MAT_TYPE(distortion_coeffs->type) != CV_32FC1 ) {
+ error("%s is not a valid distortions coeffs file", filename->s_name);
+ cvReleaseMat(&distortion_coeffs);
+ distortion_coeffs = cvCreateMat(3, 3, CV_32FC1);
+ resetCorrectionMatrix();
+ }
+ else post("load distortion coefficients from %s",filename->s_name);
+
+ t_atom dist_out[5];
+ for ( int i = 0 ; i < 5 ; i++ ){
+ SETFLOAT(&dist_out[i], CV_MAT_ELEM( *distortion_coeffs, float, i, 0));
+ }
+ outlet_anything( this->m_dataout, gensym("distortion_coeffs"), 5, dist_out);
+
+ // reinitialise size to force reinitialisation of mapx and mapy on next frame
+ this->comp_xsize = 0;
+}
+
+void pix_opencv_calibration :: writeIntraMess (t_symbol *filename)
+{
+ cvSave(filename->s_name,intrinsic_matrix);
+}
+
+void pix_opencv_calibration :: writeDistMess (t_symbol *filename)
+{
+ cvSave(filename->s_name,distortion_coeffs);
+}
+
+void pix_opencv_calibration :: floatCalibrationhMess (float calib_flag)
+{
+ this->calibration=calib_flag;
+ if ( this->calibration == 1 ) {
+ this->success_count = 0;
+ this->frame = 0;
+ }
+ t_atom data_out;
+ SETFLOAT(&data_out, calibration);
+ outlet_anything( this->m_dataout, gensym("calibration"), 1, &data_out);
+}
+
+void pix_opencv_calibration :: patternSizeMess (float xsize, float ysize)
+{
+ if (calibration) {error("you can't change pattern size during calibration"); return;}
+ if ( xsize < 3 || ysize < 3 ) {error("patternSize should be at least 3x3"); return;}
+ this->patternSize[0]=xsize;
+ this->patternSize[1]=ysize;
+
+ // reallocate matrix
+ cvReleaseMat(&image_points);
+ cvReleaseMat(&object_points);
+ cvReleaseMat(&point_counts);
+ image_points = cvCreateMat(patternSize[0]*patternSize[1]*board_view_nb, 2, CV_32FC1);
+ object_points = cvCreateMat(patternSize[0]*patternSize[1]*board_view_nb, 3, CV_32FC1);
+ point_counts = cvCreateMat(board_view_nb, 1, CV_32SC1);
+}
+
+void pix_opencv_calibration :: viewMess (int view)
+{
+ if ( calibration == 1 ) {error("you can't change view number during calibration !"); return;}
+ board_view_nb=view<2?2:view;
+ if (view < 2) error("view should be greater or equal to 2");
+
+ // reallocate matrix
+ cvReleaseMat(&image_points);
+ cvReleaseMat(&object_points);
+ cvReleaseMat(&point_counts);
+ image_points = cvCreateMat(patternSize[0]*patternSize[1]*board_view_nb, 2, CV_32FC1);
+ object_points = cvCreateMat(patternSize[0]*patternSize[1]*board_view_nb, 3, CV_32FC1);
+ point_counts = cvCreateMat(board_view_nb, 1, CV_32SC1);
+
+}
+
+void pix_opencv_calibration :: waitMess (int wait)
+{
+ wait_n_frame=wait<1?1:wait;
+ if (wait < 1) error("wait should be greater or equal to 1, you can't calibrate more often than each frame !");
+}
+
+void pix_opencv_calibration :: findChessFlagMess(int adaptThres, int normalize, int filter)
+{
+ adaptThres=adaptThres<=0?0:adaptThres>=1?1:adaptThres;
+ normalize=normalize<=0?0:normalize>=1?1:normalize;
+ filter=filter<=0?0:filter>=1?1:filter;
+ findChessFlag = CV_CALIB_CB_ADAPTIVE_THRESH * adaptThres + CV_CALIB_CB_NORMALIZE_IMAGE * normalize + CV_CALIB_CB_FILTER_QUADS * filter;
+}
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_calibration :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_calibration::loadIntraMessCallback,
+ gensym("loadIntra"), A_SYMBOL, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_calibration::loadDistMessCallback,
+ gensym("loadDist"), A_SYMBOL, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_calibration::writeIntraMessCallback,
+ gensym("writeIntra"), A_SYMBOL, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_calibration::writeDistMessCallback,
+ gensym("writeDist"), A_SYMBOL, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_calibration::floatCalibrationMessCallback,
+ gensym("calibration"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_calibration::patternSizeMessCallback,
+ gensym("patternSize"), A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_calibration::viewMessCallback,
+ gensym("view"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_calibration::waitMessCallback,
+ gensym("wait"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_calibration::findChessFlagMessCallback,
+ gensym("findChessFlag"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_calibration::resetMessCallback,
+ gensym("reset"), A_NULL);
+
+}
+void pix_opencv_calibration :: loadIntraMessCallback(void *data, t_symbol* filename)
+{
+ GetMyClass(data)->loadIntraMess(filename);
+}
+void pix_opencv_calibration :: loadDistMessCallback(void *data, t_symbol* filename)
+{
+ GetMyClass(data)->loadDistMess(filename);
+}
+void pix_opencv_calibration :: writeIntraMessCallback(void *data, t_symbol* filename)
+{
+ GetMyClass(data)->writeIntraMess(filename);
+}
+void pix_opencv_calibration :: writeDistMessCallback(void *data, t_symbol* filename)
+{
+ GetMyClass(data)->writeDistMess(filename);
+}
+void pix_opencv_calibration :: floatCalibrationMessCallback(void *data, t_floatarg calib_flag)
+{
+ GetMyClass(data)->floatCalibrationhMess((float)calib_flag);
+}
+void pix_opencv_calibration :: patternSizeMessCallback(void *data, t_floatarg xsize, t_floatarg ysize)
+{
+ GetMyClass(data)->patternSizeMess((float)xsize, (float)ysize);
+}
+void pix_opencv_calibration :: viewMessCallback(void *data, t_floatarg view)
+{
+ GetMyClass(data)->viewMess((int)view);
+}
+void pix_opencv_calibration :: waitMessCallback(void *data, t_floatarg wait)
+{
+ GetMyClass(data)->waitMess((int)wait);
+}
+void pix_opencv_calibration :: findChessFlagMessCallback(void *data, t_floatarg adaptThres, t_floatarg normalize, t_floatarg filter)
+{
+ GetMyClass(data)->findChessFlagMess((int) adaptThres, (int) normalize, (int) filter);
+}
+
+void pix_opencv_calibration :: resetMessCallback(void *data)
+{
+ GetMyClass(data)->resetCorrectionMatrix();
+}
+
+void pix_opencv_calibration :: resetCorrectionMatrix()
+{
+ // make an "empty" intrinsinc matrix
+ CV_MAT_ELEM( *intrinsic_matrix, float, 0, 0 ) = 8;
+ CV_MAT_ELEM( *intrinsic_matrix, float, 1, 0 ) = 0;
+ CV_MAT_ELEM( *intrinsic_matrix, float, 2, 0 ) = 0;
+ CV_MAT_ELEM( *intrinsic_matrix, float, 0, 1 ) = 0;
+ CV_MAT_ELEM( *intrinsic_matrix, float, 1, 1 ) = 8;
+ CV_MAT_ELEM( *intrinsic_matrix, float, 2, 1 ) = 0;
+ CV_MAT_ELEM( *intrinsic_matrix, float, 0, 2 ) = 3;
+ CV_MAT_ELEM( *intrinsic_matrix, float, 1, 2 ) = 3;
+ CV_MAT_ELEM( *intrinsic_matrix, float, 2, 2 ) = 1;
+
+ // zeros distortion coeffs
+ for ( int i = 0 ; i < 5 ; i++ ) {
+ CV_MAT_ELEM( *distortion_coeffs, float, i, 0) = 0.0;
+ }
+
+ // reinitialise size to force reinitialisation of mapx and mapy on next frame
+ this->comp_xsize = 0;
+}
diff --git a/src/pix_opencv_calibration.h b/src/pix_opencv_calibration.h
new file mode 100644
index 0000000..c6bbaaa
--- /dev/null
+++ b/src/pix_opencv_calibration.h
@@ -0,0 +1,120 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Threshold filter
+
+ 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_calibration_H_
+#define INCLUDE_pix_opencv_calibration_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_calibration
+
+ Threshold filter
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_calibration : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_calibration, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_calibration();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_calibration();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ void findCorners ( IplImage *image );
+ void computeCalibration ( IplImage *image );
+
+ //////////
+ // Set the new edge threshold
+ void loadIntraMess(t_symbol *filename);
+ void loadDistMess(t_symbol *filename);
+ void writeIntraMess(t_symbol *filename);
+ void writeDistMess(t_symbol *filename);
+ void floatCalibrationhMess (float calib_flag);
+ void patternSizeMess (float xsize, float ysize);
+ void viewMess (int view);
+ void waitMess (int wait);
+ void findChessFlagMess(int adaptThres, int normalize, int filter);
+ void resetCorrectionMatrix();
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ int success_count, /* number of images on wich we correctly found corners */
+ board_view_nb, /* number of views to take */
+ calibration, /* calibration flag */
+ patternSize[2], /* size of the calibration chessboard */
+ frame, /* number of frames analysed for chessboard corner */
+ wait_n_frame, /* number of frames to wait between two take */
+ findChessFlag; // flag for cvFindChessboardCorners
+
+
+ private:
+
+ t_outlet *m_dataout;
+
+ //////////
+ // Static member functions
+ static void loadIntraMessCallback(void *data, t_symbol* filename);
+ static void loadDistMessCallback(void *data, t_symbol* filename);
+ static void writeIntraMessCallback(void *data, t_symbol* filename);
+ static void writeDistMessCallback(void *data, t_symbol* filename);
+ static void floatCalibrationMessCallback(void *data, t_floatarg calib_flag);
+ static void patternSizeMessCallback(void *data, t_floatarg xsize, t_floatarg ysize);
+ static void viewMessCallback(void *data, t_floatarg view);
+ static void waitMessCallback(void *data, t_floatarg wait);
+ static void findChessFlagMessCallback(void *data, t_floatarg adaptThres, t_floatarg normalize, t_floatarg filter);
+ static void resetMessCallback(void *data);
+ /////////
+
+ // CvMat needed
+ CvMat *image_points,
+ *object_points,
+ *point_counts,
+ *intrinsic_matrix,
+ *distortion_coeffs;
+
+ /////////
+ // IplImage needed
+ IplImage *find_rgb, *find_gray, *rgb, *gray, *tmp, *mapx, *mapy;
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_camshift.cc b/src/pix_opencv_camshift.cc
new file mode 100644
index 0000000..879a9f2
--- /dev/null
+++ b/src/pix_opencv_camshift.cc
@@ -0,0 +1,450 @@
+
+//
+// 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_camshift.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_camshift)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_camshift
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_camshift :: pix_opencv_camshift()
+{
+ int i;
+ int hdims = 16;
+ float hranges_arr[] = {0,180};
+ float* hranges = hranges_arr;
+
+ comp_xsize=320;
+ comp_ysize=240;
+
+ m_dataout = outlet_new(this->x_obj, &s_anything);
+
+ x_track = 0;
+ x_init = 0;
+ x_rwidth = 20;
+ x_rheight = 20;
+ x_backproject = 0;
+ x_vmin = 50;
+ x_vmax = 256;
+ x_smin = 30;
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ hsv = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ hue = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ mask = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ backproject = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ hist = cvCreateHist( 1, &hdims, CV_HIST_ARRAY, &hranges, 1 );
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_camshift :: ~pix_opencv_camshift()
+{
+ // Destroy cv_images
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&hsv);
+ cvReleaseImage(&hue);
+ cvReleaseImage(&mask);
+ cvReleaseImage(&backproject);
+ cvReleaseHist(&hist);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_camshift :: processRGBAImage(imageStruct &image)
+{
+ int i, k;
+ int im;
+ int marked;
+ int hdims = 16;
+ float hranges_arr[] = {0,180};
+ float* hranges = hranges_arr;
+
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&hsv);
+ cvReleaseImage(&hue);
+ cvReleaseImage(&mask);
+ cvReleaseImage(&backproject);
+ cvReleaseHist(&hist);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ hsv = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ hue = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ mask = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ backproject = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ hist = cvCreateHist( 1, &hdims, CV_HIST_ARRAY, &hranges, 1 );
+
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+
+ // Convert to hsv
+ cvCvtColor(rgba, rgb, CV_BGRA2BGR);
+ cvCvtColor(rgb, hsv, CV_BGR2HSV);
+
+ if ( x_track )
+ {
+ cvInRangeS( hsv, cvScalar(0,x_smin,MIN(x_vmin,x_vmax),0), cvScalar(180,256,MAX(x_vmin,x_vmax),0), mask );
+ cvSplit( hsv, hue, 0, 0, 0 );
+
+ if ( x_init )
+ {
+ float max_val = 0.f;
+ x_init = 0;
+ cvSetImageROI( hue, selection );
+ cvSetImageROI( mask, selection );
+ cvCalcHist( &hue, hist, 0, mask );
+ cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 );
+ cvConvertScale( hist->bins, hist->bins, max_val ? 255. / max_val : 0., 0 );
+ cvResetImageROI( hue );
+ cvResetImageROI( mask );
+ trackwindow = selection;
+ }
+
+ cvCalcBackProject( (IplImage**)&(hue), (CvArr*)backproject, (const CvHistogram*)hist );
+ cvAnd( backproject, mask, backproject, 0 );
+ cvCamShift( backproject, trackwindow,
+ cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ),
+ &trackcomp, &trackbox );
+ trackwindow = trackcomp.rect;
+
+ if( x_backproject )
+ cvCvtColor( backproject, rgb, CV_GRAY2BGR );
+ if( !rgb->origin )
+ trackbox.angle = -trackbox.angle;
+ cvEllipseBox( rgb, trackbox, CV_RGB(255,0,0), 3, CV_AA, 0 );
+ SETFLOAT(&x_list[0], trackbox.center.x);
+ SETFLOAT(&x_list[1], trackbox.center.y);
+ SETFLOAT(&x_list[2], trackbox.size.width);
+ SETFLOAT(&x_list[3], trackbox.size.height);
+ SETFLOAT(&x_list[4], trackbox.angle);
+ outlet_list( m_dataout, 0, 5, x_list );
+ }
+
+ cvCvtColor(rgb, rgba, CV_BGR2BGRA);
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_camshift :: processRGBImage(imageStruct &image)
+{
+ int i, k;
+ int im;
+ int marked;
+ int hdims = 16;
+ float hranges_arr[] = {0,180};
+ float* hranges = hranges_arr;
+
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&hsv);
+ cvReleaseImage(&hue);
+ cvReleaseImage(&mask);
+ cvReleaseImage(&backproject);
+ cvReleaseHist(&hist);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ hsv = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ hue = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ mask = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ backproject = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ hist = cvCreateHist( 1, &hdims, CV_HIST_ARRAY, &hranges, 1 );
+
+ }
+
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ // Convert to hsv
+ cvCvtColor(rgb, hsv, CV_BGR2HSV);
+
+ if ( x_track )
+ {
+ cvInRangeS( hsv, cvScalar(0,x_smin,MIN(x_vmin,x_vmax),0), cvScalar(180,256,MAX(x_vmin,x_vmax),0), mask );
+ cvSplit( hsv, hue, 0, 0, 0 );
+
+ if ( x_init )
+ {
+ float max_val = 0.f;
+ x_init = 0;
+ cvSetImageROI( hue, selection );
+ cvSetImageROI( mask, selection );
+ cvCalcHist( &hue, hist, 0, mask );
+ cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 );
+ cvConvertScale( hist->bins, hist->bins, max_val ? 255. / max_val : 0., 0 );
+ cvResetImageROI( hue );
+ cvResetImageROI( mask );
+ trackwindow = selection;
+ }
+
+ cvCalcBackProject( (IplImage**)&(hue), (CvArr*)backproject, (const CvHistogram*)hist );
+ cvAnd( backproject, mask, backproject, 0 );
+ cvCamShift( backproject, trackwindow,
+ cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ),
+ &trackcomp, &trackbox );
+ trackwindow = trackcomp.rect;
+
+ if( x_backproject )
+ cvCvtColor( backproject, rgb, CV_GRAY2BGR );
+ if( !rgb->origin )
+ trackbox.angle = -trackbox.angle;
+ cvEllipseBox( rgb, trackbox, CV_RGB(255,0,0), 3, CV_AA, 0 );
+ SETFLOAT(&x_list[0], trackbox.center.x);
+ SETFLOAT(&x_list[1], trackbox.center.y);
+ SETFLOAT(&x_list[2], trackbox.size.width);
+ SETFLOAT(&x_list[3], trackbox.size.height);
+ SETFLOAT(&x_list[4], trackbox.angle);
+ outlet_list( m_dataout, 0, 5, x_list );
+ }
+
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_camshift :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_camshift : yuv format not supported" );
+}
+
+void pix_opencv_camshift :: processGrayImage(imageStruct &image)
+{
+ int i, k;
+ int im;
+ int marked;
+ int hdims = 16;
+ float hranges_arr[] = {0,180};
+ float* hranges = hranges_arr;
+
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&hsv);
+ cvReleaseImage(&hue);
+ cvReleaseImage(&mask);
+ cvReleaseImage(&backproject);
+ cvReleaseHist(&hist);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ hsv = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ hue = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ mask = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ backproject = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ hist = cvCreateHist( 1, &hdims, CV_HIST_ARRAY, &hranges, 1 );
+
+ }
+
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+
+ // Convert to hsv
+ cvCvtColor(gray, rgb, CV_GRAY2BGR);
+ cvCvtColor(rgb, hsv, CV_BGR2HSV);
+
+ if ( x_track )
+ {
+ cvInRangeS( hsv, cvScalar(0,x_smin,MIN(x_vmin,x_vmax),0), cvScalar(180,256,MAX(x_vmin,x_vmax),0), mask );
+ cvSplit( hsv, hue, 0, 0, 0 );
+
+ if ( x_init )
+ {
+ float max_val = 0.f;
+ x_init = 0;
+ cvSetImageROI( hue, selection );
+ cvSetImageROI( mask, selection );
+ cvCalcHist( &hue, hist, 0, mask );
+ cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 );
+ cvConvertScale( hist->bins, hist->bins, max_val ? 255. / max_val : 0., 0 );
+ cvResetImageROI( hue );
+ cvResetImageROI( mask );
+ trackwindow = selection;
+ }
+
+ cvCalcBackProject( (IplImage**)&(hue), (CvArr*)backproject, (const CvHistogram*)hist );
+ cvAnd( backproject, mask, backproject, 0 );
+ cvCamShift( backproject, trackwindow,
+ cvTermCriteria( CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1 ),
+ &trackcomp, &trackbox );
+ trackwindow = trackcomp.rect;
+
+ if( x_backproject )
+ memcpy( gray->imageData, backproject->imageData, image.xsize*image.ysize );
+ if( !rgb->origin )
+ trackbox.angle = -trackbox.angle;
+ cvEllipseBox( gray, trackbox, CV_RGB(255,0,0), 3, CV_AA, 0 );
+ SETFLOAT(&x_list[0], trackbox.center.x);
+ SETFLOAT(&x_list[1], trackbox.center.y);
+ SETFLOAT(&x_list[2], trackbox.size.width);
+ SETFLOAT(&x_list[3], trackbox.size.height);
+ SETFLOAT(&x_list[4], trackbox.angle);
+ outlet_list( m_dataout, 0, 5, x_list );
+ }
+
+ memcpy( image.data, gray->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_camshift :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_camshift::backProjectMessCallback,
+ gensym("backproject"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_camshift::vMinMessCallback,
+ gensym("vmin"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_camshift::vMaxMessCallback,
+ gensym("vmax"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_camshift::sMinMessCallback,
+ gensym("smin"), A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_camshift::trackMessCallback,
+ gensym("track"), A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_camshift::rWidthMessCallback,
+ gensym("rwidth"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_camshift::rHeightMessCallback,
+ gensym("rheight"), A_NULL);
+}
+
+void pix_opencv_camshift :: backProjectMessCallback(void *data, t_floatarg backproject)
+{
+ GetMyClass(data)->backProjectMess((float)backproject);
+}
+
+void pix_opencv_camshift :: vMinMessCallback(void *data, t_floatarg vmin)
+{
+ GetMyClass(data)->vMinMess((float)vmin);
+}
+
+void pix_opencv_camshift :: vMaxMessCallback(void *data, t_floatarg vmax)
+{
+ GetMyClass(data)->vMaxMess((float)vmax);
+}
+
+void pix_opencv_camshift :: sMinMessCallback(void *data, t_floatarg smin)
+{
+ GetMyClass(data)->sMinMess((float)smin);
+}
+
+void pix_opencv_camshift :: trackMessCallback(void *data, t_floatarg px, t_floatarg py)
+{
+ GetMyClass(data)->trackMess((float)px, (float)py);
+}
+
+void pix_opencv_camshift :: rWidthMessCallback(void *data, t_floatarg rwidth)
+{
+ GetMyClass(data)->rWidthMess((float)rwidth);
+}
+
+void pix_opencv_camshift :: rHeightMessCallback(void *data, t_floatarg rheight)
+{
+ GetMyClass(data)->rHeightMess((float)rheight);
+}
+
+void pix_opencv_camshift :: backProjectMess(float backproject)
+{
+ if ( ( (int)backproject==0 ) || ( (int)backproject==1 ) ) x_backproject = (int)backproject;
+}
+
+void pix_opencv_camshift :: vMinMess(float vmin)
+{
+ if ( ( (int)vmin>=0 ) || ( (int)vmin<256 ) ) x_vmin = (int)vmin;
+}
+
+void pix_opencv_camshift :: vMaxMess(float vmax)
+{
+ if ( ( (int)vmax>=0 ) || ( (int)vmax<256 ) ) x_vmax = (int)vmax;
+}
+
+void pix_opencv_camshift :: sMinMess(float smin)
+{
+ if ( ( (int)smin>=0 ) || ( (int)smin<256 ) ) x_smin = (int)smin;
+}
+
+void pix_opencv_camshift :: trackMess(float px, float py)
+{
+ int rx, ry, w, h;
+
+ if ( ( px<0.0 ) || ( px>comp_xsize ) || ( py<0.0 ) || ( py>comp_ysize ) ) return;
+
+ //py = comp_ysize - py;
+ origin = cvPoint((int)px,(int)py);
+ rx = ( (int)px-(x_rwidth/2) < 0 )? 0:(int)px-(x_rwidth/2);
+ ry = ( (int)py-(x_rheight/2) < 0 )? 0:(int)py-(x_rheight/2);
+ w = (rx+x_rwidth>comp_xsize ) ? ( comp_xsize - rx ):x_rwidth;
+ h = (ry+x_rheight>comp_ysize ) ? ( comp_ysize - ry ):x_rheight;
+ selection = cvRect(rx,ry,w,h);
+ post( "pix_opencv_camshift : track point (%f,%f) region (%d %d %d %d)", px, py, rx, ry, w, h );
+ x_track = 1;
+ x_init = 1;
+}
+
+void pix_opencv_camshift :: rWidthMess(float rwidth)
+{
+ if ( (int)rwidth>=0 ) x_rwidth = (int)rwidth;
+ // refresh selection zone
+ trackMess( (float)origin.x, (float)origin.y );
+}
+
+void pix_opencv_camshift :: rHeightMess(float rheight)
+{
+ if ( (int)rheight>=0 ) x_rheight = (int)rheight;
+ // refresh selection zone
+ trackMess( (float)origin.x, (float)origin.y );
+}
+
diff --git a/src/pix_opencv_camshift.h b/src/pix_opencv_camshift.h
new file mode 100644
index 0000000..c649b2e
--- /dev/null
+++ b/src/pix_opencv_camshift.h
@@ -0,0 +1,110 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Continously adaptive mean-shift tracker
+
+ 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_CAMSHIFT_H_
+#define INCLUDE_PIX_OPENCV_CAMSHIFT_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/video/tracking.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+#define MAX_MARKERS 500
+const int MAX_COUNT = 500;
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_camshift
+
+ Continously adaptive mean-shift tracker
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_camshift : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_camshift, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_camshift();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_camshift();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ void backProjectMess(float backproject);
+ void vMinMess(float vmin);
+ void vMaxMess(float vmax);
+ void sMinMess(float smin);
+ void trackMess(float px, float py);
+ void rWidthMess(float rwidth);
+ void rHeightMess(float rheight);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ t_outlet *m_dataout;
+ int x_track;
+ int x_init;
+ int x_rwidth;
+ int x_rheight;
+ int x_backproject;
+ int x_vmin;
+ int x_vmax;
+ int x_smin;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void backProjectMessCallback(void *data, float backproject);
+ static void vMinMessCallback(void *data, float vmin);
+ static void vMaxMessCallback(void *data, float vmax);
+ static void sMinMessCallback(void *data, float smin);
+ static void trackMessCallback(void *data, float px, float py);
+ static void rWidthMessCallback(void *data, float rwidth);
+ static void rHeightMessCallback(void *data, float rheight);
+
+ // Internal Open CV data
+ IplImage *rgba, *rgb, *gray, *hsv, *hue, *mask, *backproject;
+ CvHistogram *hist;
+ CvPoint origin;
+ CvRect selection;
+ CvRect trackwindow;
+ CvBox2D trackbox;
+ CvConnectedComp trackcomp;
+
+ t_atom x_list[5];
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_clahe.cc b/src/pix_opencv_clahe.cc
new file mode 100644
index 0000000..4de7cfc
--- /dev/null
+++ b/src/pix_opencv_clahe.cc
@@ -0,0 +1,171 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org ) (pix_opencv)
+// Template for pix_opencv class
+
+#if HAVE_CLAHE
+#include "pix_opencv_clahe.h"
+
+using namespace cv;
+
+CPPEXTERN_NEW_WITH_THREE_ARGS(pix_opencv_clahe, t_floatarg, A_DEFFLOAT, t_floatarg, A_DEFFLOAT, t_floatarg, A_DEFFLOAT);
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_clahe
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_clahe :: pix_opencv_clahe(t_float clipLimit, int width, int height)
+ : m_clipLimit(40), m_tileGridSize(Size(8,8)), m_rendering(false)
+{
+ if ( clipLimit > 0. ){
+ m_clipLimit = clipLimit;
+ }
+ if ( width > 0 && height > 0 ){
+ m_tileGridSize=cv::Size(width, height);
+ } else if (width > 0 || height > 0) {
+ int max = width > height ? width : height;
+ m_tileGridSize=Size ( max, max );
+ }
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_clahe :: ~pix_opencv_clahe()
+{
+}
+
+// StartRendering
+void pix_opencv_clahe :: startRendering(){
+#if HAVE_LIBOPENCV_CL
+ ocl::DevicesInfo devicesInfo;
+ ocl::getOpenCLDevices(devicesInfo);
+ post("Found %d OpenCL device(s).", devicesInfo.size());
+ for ( size_t i = 0; i < devicesInfo.size(); i++){
+ post("%s %s", devicesInfo[i]->deviceVendor.c_str(), devicesInfo[i]->deviceName.c_str());
+ }
+
+ m_cpuFilter = createCLAHE();
+
+ if ( devicesInfo.size() == 0 ){
+ post("can't find OpenCL device, switch to CPU mode");
+ m_gpuMode = false;
+ } else {
+ m_oclFilter = ocl::createCLAHE();
+ m_gpuMode = true;
+ }
+ clipLimitMess(m_clipLimit);
+ tileGridSizeMess(m_tileGridSize.width,m_tileGridSize.height);
+#else
+ verbose(2,"no OpenCL support, it could be very slow !!");
+#endif /* HAVE_LIBOPENCV_CL */
+
+ m_rendering = true;
+}
+
+void pix_opencv_clahe :: stopRendering(){
+ m_rendering = false;
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_clahe :: processImage(imageStruct &image)
+{
+ if ( image.csize == 1 ){
+ m_gray = Mat( image.ysize, image.xsize, CV_8UC1, image.data, image.csize*image.xsize); // just transform imageStruct to cv::Mat without copying data
+ } else if (image.csize == 4) {
+ m_imgMat = Mat( image.ysize, image.xsize, CV_8UC4, image.data, image.csize*image.xsize); // just transform imageStruct to cv::Mat without copying data
+ cvtColor(m_imgMat,m_gray,CV_RGBA2GRAY);
+ } else {
+ error("only support grayscale and RGBA image");
+ }
+
+#if HAVE_LIBOPENCV_CL
+ if ( m_gpuMode ) {
+ try {
+ d_outframe = m_gray;
+ m_oclFilter->apply(d_outframe, d_outframe);
+ d_outframe.download(m_gray);
+ } catch (cv::Exception& e) {
+ error("can't use OpenCL, do you have OpenCL driver installed ?");
+ error("error %d : %s", e.code, e.err.c_str());
+ m_gpuMode = false;
+ return;
+ }
+#else
+ if ( 0 ) {
+#endif /* HAVE_LIBOPENCV_CL */
+ } else {
+ m_cpuFilter->apply(m_gray, m_gray);
+ }
+
+ if ( image.csize == 4 ){
+ std::vector<Mat> split;
+ cv::split(m_imgMat,split);
+ split.pop_back();
+ split.push_back(m_gray);
+ cv::merge(split,m_imgMat);
+ }
+}
+
+void pix_opencv_clahe :: clipLimitMess(t_float limit){
+ m_clipLimit=limit;
+ if ( m_rendering ){
+#if HAVE_LIBOPENCV_CL
+ if ( m_gpuMode ){
+ m_oclFilter->setClipLimit(m_clipLimit);
+#else
+ if ( 0 ) {
+#endif /* HAVE_LIBOPENCV_CL */
+ } else {
+ m_cpuFilter->setClipLimit(m_clipLimit);
+ }
+ }
+}
+
+void pix_opencv_clahe :: tileGridSizeMess(int width, int height){
+ m_tileGridSize=cv::Size(MAX(width,1),MAX(height,1));
+ if ( m_rendering ){
+#if HAVE_LIBOPENCV_CL
+ if ( m_gpuMode ){
+ m_oclFilter->setTilesGridSize(m_tileGridSize);
+#else
+ if ( 0 ) {
+#endif /* HAVE_LIBOPENCV_CL */
+ } else {
+ m_cpuFilter->setTilesGridSize(m_tileGridSize);
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_clahe :: obj_setupCallback(t_class *classPtr)
+{
+ CPPEXTERN_MSG1(classPtr, "clipLimit", clipLimitMess, t_float);
+ CPPEXTERN_MSG2(classPtr, "tileGridSize", tileGridSizeMess, int, int);
+}
+#endif /* HAVE_CLAHE */
diff --git a/src/pix_opencv_clahe.h b/src/pix_opencv_clahe.h
new file mode 100644
index 0000000..6d03a53
--- /dev/null
+++ b/src/pix_opencv_clahe.h
@@ -0,0 +1,84 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Threshold filter
+
+ 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_CLAHE_H_
+#define INCLUDE_PIX_OPENCV_CLAHE_H_
+
+#include "opencv2/opencv.hpp"
+#if HAVE_LIBOPENCV_CL
+#include "opencv2/ocl/ocl.hpp"
+#endif /* HAVE_LIBOPENCV_CL */
+
+
+#include "Base/GemPixObj.h"
+#include "RTE/MessageCallbacks.h"
+#include "Gem/Exception.h"
+
+
+using namespace cv;
+
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_clahe
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_clahe : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_clahe, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_clahe(t_float clipLimit, int width, int height);
+
+ /////////
+ // Message handler
+ void clipLimitMess(float);
+ void tileGridSizeMess(int,int);
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_clahe();
+
+ //////////
+ // Do the processing
+ virtual void processImage(imageStruct &image);
+ virtual void startRendering();
+ virtual void stopRendering();
+
+ private:
+
+ Mat m_imgMat, m_gray;
+#if HAVE_LIBOPENCV_CL
+ ocl::oclMat d_outframe, d_frame;
+#endif /* HAVE_LIBOPENCV_CL */
+
+ Ptr<CLAHE> m_oclFilter, m_cpuFilter;
+
+ bool m_gpuMode;
+ float m_clipLimit;
+ Size m_tileGridSize;
+ bool m_rendering;
+};
+#endif // for header file
diff --git a/src/pix_opencv_colorfilt.cc b/src/pix_opencv_colorfilt.cc
new file mode 100644
index 0000000..d977e0a
--- /dev/null
+++ b/src/pix_opencv_colorfilt.cc
@@ -0,0 +1,299 @@
+////////////////////////////////////////////////////////
+//
+// 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_colorfilt.h"
+#include "g_canvas.h"
+
+CPPEXTERN_NEW(pix_opencv_colorfilt)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_colorfilt
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_colorfilt :: pix_opencv_colorfilt()
+{
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, &s_float, gensym("R"));
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, &s_float, gensym("G"));
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, &s_float, gensym("B"));
+
+ x_R = outlet_new(this->x_obj, &s_float);
+ x_G = outlet_new(this->x_obj, &s_float);
+ x_B = outlet_new(this->x_obj, &s_float);
+
+ x_colorR = 128;
+ x_colorG = 128;
+ x_colorB = 128;
+
+ outlet_float( x_R, x_colorR );
+ outlet_float( x_G, x_colorG );
+ outlet_float( x_B, x_colorB );
+
+ comp_xsize=320;
+ comp_ysize=240;
+
+ x_tolerance = 50;
+
+ x_canvas = canvas_getcurrent();
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ brgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_colorfilt :: ~pix_opencv_colorfilt()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&brgb);
+}
+
+void pix_opencv_colorfilt :: drawColor()
+{
+ int width, height;
+ char color[32];
+
+ sprintf( color, "#%.2X%.2X%.2X", x_colorR, x_colorG, x_colorB );
+ width = rtext_width( glist_findrtext( (t_glist*)x_canvas, (t_text *)this->x_obj ) );
+ height = rtext_height( glist_findrtext( (t_glist*)x_canvas, (t_text *)this->x_obj ) );
+ sys_vgui((char*)".x%x.c delete rectangle %xCOLOR\n", x_canvas, this->x_obj );
+ sys_vgui((char*)".x%x.c create rectangle %d %d %d %d -fill %s -tags %xCOLOR\n",
+ x_canvas, this->x_obj->te_xpix+width+5, this->x_obj->te_ypix,
+ this->x_obj->te_xpix+width+height+5,
+ this->x_obj->te_ypix+height, color, this->x_obj );
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_colorfilt :: processRGBAImage(imageStruct &image)
+{
+ int px,py;
+ unsigned char r,g,b;
+ int diff;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgba)) {
+
+ comp_xsize = image.xsize;
+ comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if ( rgba )
+ {
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&brgb);
+ }
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ brgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ }
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ cvCopy(rgba, brgb);
+
+ for( py=0; py<rgba->height; py++ ) {
+ for( px=0; px<rgba->width; px++ ) {
+#ifdef __APPLE__
+ g = ((uchar*)(rgba->imageData + rgba->widthStep*(int)py))[(int)px*4];
+ r = ((uchar*)(rgba->imageData + rgba->widthStep*(int)py))[(int)px*4+1];
+ b = ((uchar*)(rgba->imageData + rgba->widthStep*(int)py))[(int)px*4+3];
+#else
+ r = ((uchar*)(rgba->imageData + rgba->widthStep*(int)py))[(int)px*4];
+ g = ((uchar*)(rgba->imageData + rgba->widthStep*(int)py))[(int)px*4+1];
+ b = ((uchar*)(rgba->imageData + rgba->widthStep*(int)py))[(int)px*4+2];
+#endif
+
+ diff = 0;
+ diff = abs(r-x_colorR );
+ diff += abs(g-x_colorG );
+ diff += abs(b-x_colorB );
+ diff = diff/3;
+
+ if ( diff > x_tolerance )
+ {
+ (rgba->imageData + rgba->widthStep*(int)py)[(int)px*4] = 0x0;
+ (rgba->imageData + rgba->widthStep*(int)py)[(int)px*4+1] = 0x0;
+ (rgba->imageData + rgba->widthStep*(int)py)[(int)px*4+2] = 0x0;
+ (rgba->imageData + rgba->widthStep*(int)py)[(int)px*4+3] = 0x0;
+ }
+ }
+ }
+
+ //copy back the processed frame to image
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_colorfilt :: processRGBImage(imageStruct &image)
+{
+ unsigned char r,g,b;
+ int diff;
+ int px, py;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if ( rgb )
+ {
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&brgb);
+ }
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ brgb = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 4);
+
+ }
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+ cvCopy(rgb, brgb);
+
+ for( py=0; py<rgb->height; py++ ) {
+ for( px=0; px<rgb->width; px++ ) {
+ b = ((uchar*)(rgb->imageData + rgb->widthStep*(int)py))[(int)px*3];
+ g = ((uchar*)(rgb->imageData + rgb->widthStep*(int)py))[(int)px*3+1];
+ r = ((uchar*)(rgb->imageData + rgb->widthStep*(int)py))[(int)px*3+2];
+
+ diff = 0;
+ diff = abs(r-x_colorR );
+ diff += abs(g-x_colorG );
+ diff += abs(b-x_colorB );
+
+ if ( diff > x_tolerance )
+ {
+ (rgb->imageData + rgb->widthStep*(int)py)[(int)px*3] = 0x0;
+ (rgb->imageData + rgb->widthStep*(int)py)[(int)px*3+1] = 0x0;
+ (rgb->imageData + rgb->widthStep*(int)py)[(int)px*3+2] = 0x0;
+ }
+ }
+ }
+
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_colorfilt :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_colorfilt : yuv format not supported" );
+}
+
+void pix_opencv_colorfilt :: processGrayImage(imageStruct &image)
+{
+ post( "pix_opencv_colorfilt : gray format not supported" );
+}
+
+void pix_opencv_colorfilt :: floatToleranceMess (float tolerance)
+{
+ if ( (int)tolerance>0 ) x_tolerance = (int)tolerance;
+}
+
+void pix_opencv_colorfilt :: floatRMess (float r)
+{
+ if ( ( (int)r>=0 ) && ( (int)r<=255 ) ) x_colorR = (int)r;
+ if (glist_isvisible(x_canvas)) drawColor();
+}
+
+void pix_opencv_colorfilt :: floatGMess (float g)
+{
+ if ( ( (int)g>=0 ) && ( (int)g<=255 ) ) x_colorG = (int)g;
+ if (glist_isvisible(x_canvas)) drawColor();
+}
+
+void pix_opencv_colorfilt :: floatBMess (float b)
+{
+ if ( ( (int)b>=0 ) && ( (int)b<=255 ) ) x_colorB = (int)b;
+ if (glist_isvisible(x_canvas)) drawColor();
+}
+
+void pix_opencv_colorfilt :: pickMess (float xcur, float ycur)
+{
+ if ( ( xcur >= 0. ) && ( xcur <= comp_xsize )
+ && ( ycur > 0. ) && ( ycur < comp_ysize ) )
+ {
+#ifdef __APPLE__
+ x_colorR = ((uchar*)(brgb->imageData + brgb->widthStep*(int)ycur))[(int)xcur*4+1];
+ x_colorG = ((uchar*)(brgb->imageData + brgb->widthStep*(int)ycur))[(int)xcur*4+2];
+ x_colorB = ((uchar*)(brgb->imageData + brgb->widthStep*(int)ycur))[(int)xcur*4+3];
+#else
+ //ycur = brgb->height - ycur;
+ x_colorR = ((uchar*)(brgb->imageData + brgb->widthStep*(int)ycur))[(int)xcur*4];
+ x_colorG = ((uchar*)(brgb->imageData + brgb->widthStep*(int)ycur))[(int)xcur*4+1];
+ x_colorB = ((uchar*)(brgb->imageData + brgb->widthStep*(int)ycur))[(int)xcur*4+2];
+#endif
+ outlet_float( x_R, x_colorR );
+ outlet_float( x_G, x_colorG );
+ outlet_float( x_B, x_colorB );
+
+ if (glist_isvisible(x_canvas)) drawColor();
+ }
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_colorfilt :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_colorfilt::floatToleranceMessCallback,
+ gensym("tolerance"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_colorfilt::floatRMessCallback,
+ gensym("R"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_colorfilt::floatGMessCallback,
+ gensym("G"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_colorfilt::floatBMessCallback,
+ gensym("B"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_colorfilt::pickMessCallback,
+ gensym("pick"), A_FLOAT, A_FLOAT, A_NULL);
+}
+
+void pix_opencv_colorfilt :: floatToleranceMessCallback(void *data, t_floatarg tolerance)
+{
+ GetMyClass(data)->floatToleranceMess((float)tolerance);
+}
+
+void pix_opencv_colorfilt :: floatRMessCallback(void *data, t_floatarg r)
+{
+ GetMyClass(data)->floatRMess((float)r);
+}
+
+void pix_opencv_colorfilt :: floatGMessCallback(void *data, t_floatarg g)
+{
+ GetMyClass(data)->floatGMess((float)g);
+}
+
+void pix_opencv_colorfilt :: floatBMessCallback(void *data, t_floatarg b)
+{
+ GetMyClass(data)->floatBMess((float)b);
+}
+
+void pix_opencv_colorfilt :: pickMessCallback(void *data, t_floatarg xcur, t_floatarg ycur)
+{
+ GetMyClass(data)->pickMess((float)xcur,(float)ycur);
+}
diff --git a/src/pix_opencv_colorfilt.h b/src/pix_opencv_colorfilt.h
new file mode 100644
index 0000000..edbc6af
--- /dev/null
+++ b/src/pix_opencv_colorfilt.h
@@ -0,0 +1,103 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Color filter
+
+ 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_COLORFILT_H_
+#define INCLUDE_PIX_OPENCV_COLORFILT_H_
+
+#include <stdio.h>
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_colorfilt
+
+ Color filter object
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_colorfilt : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_colorfilt, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_colorfilt();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_colorfilt();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void floatToleranceMess(float tolerance);
+ void floatRMess(float r);
+ void floatGMess(float g);
+ void floatBMess(float b);
+ void pickMess(float xcur, float ycur);
+ void drawColor(void);
+
+ // The color tolerance
+ int x_tolerance;
+ unsigned char x_colorR; // RGB components of binary mask
+ unsigned char x_colorG;
+ unsigned char x_colorB;
+
+ t_outlet *x_R; // output R component of selected color
+ t_outlet *x_G; // output G component of selected color
+ t_outlet *x_B; // output B component of selected color
+
+ t_canvas *x_canvas;
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatToleranceMessCallback(void *data, float tolerance);
+ static void floatRMessCallback(void *data, float r);
+ static void floatGMessCallback(void *data, float g);
+ static void floatBMessCallback(void *data, float b);
+ static void pickMessCallback(void *data, float xcur, float ycur);
+
+ /////////
+ // IplImage needed
+ IplImage *rgba, *rgb, *brgb;
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_contours.cc b/src/pix_opencv_contours.cc
new file mode 100644
index 0000000..bf4410d
--- /dev/null
+++ b/src/pix_opencv_contours.cc
@@ -0,0 +1,591 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org ) (pix_opencv)
+// pix_opencv_contours extract and simplify contours of incomming image
+// by Antoine Villeret - 2012
+
+#include "pix_opencv_contours.h"
+#include <stdio.h>
+#include <RTE/MessageCallbacks.h>
+
+using namespace cv;
+
+CPPEXTERN_NEW(pix_opencv_contours)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_contours
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_contours :: pix_opencv_contours() : \
+ m_repeat_point(1), \
+ m_epsilon(2), \
+ m_enable_contours(1), \
+ m_enable_hulls(1), \
+ m_enable_defects(1), \
+ m_hierarchy_level(-1), \
+ m_taboutput(0), \
+ m_enable_cvblob(0), \
+ m_areaThreshold(30), \
+ m_totalPointsCount(0), \
+ m_autoresize(0), \
+ m_x_arrayname(NULL), \
+ m_y_arrayname(NULL), \
+ m_z_arrayname(NULL)
+{
+ m_dataout_middle = outlet_new(this->x_obj, 0);
+ m_dataout_right = outlet_new(this->x_obj, 0);
+
+ //~ post("build on %s at %s", __DATE__, __TIME__);
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_contours :: ~pix_opencv_contours()
+{
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours :: processImage(imageStruct &image)
+{
+ if ( image.xsize < 0 || image.ysize < 0 ) return;
+
+ Mat imgMat2, input;
+ std::vector<cv::Mat> split_array;
+
+ if ( image.csize == 1 ){
+ imgMat2 = Mat( image.ysize, image.xsize, CV_8UC1, image.data, image.csize*image.xsize); // just transform imageStruct to cv::Mat without copying data
+ input = imgMat2;
+ } else if ( image.csize == 4 ){
+ imgMat2 = Mat( image.ysize, image.xsize, CV_8UC4, image.data, image.csize*image.xsize); // just transform imageStruct to cv::Mat without copying data
+ split(imgMat2,split_array);
+ input = split_array[3]; // select alpha channel to find contours
+ } else {
+ error("suport only RGBA or GRAY image");
+ return;
+ }
+ cv::Mat imgMat = input.clone(); // copy data because findContours will destroy it...
+
+ m_contours.clear();
+ m_convexhulls.clear();
+ m_area.clear();
+
+ /*****************/
+ /* Find Contours */
+ /*****************/
+
+ std::vector<std::vector<cv::Point> > contours;
+ std::vector<cv::Vec4i> hierarchy;
+ cv::findContours(imgMat, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
+
+ /*
+ std::cout << "hierarchy : \n" << std::endl;
+ std::cout << "id\tnext\tprev\tchild\tparent" << std::endl;
+ for ( size_t i = 0; i < contours.size(); i++ )
+ {
+ std::cout << i << "\t" << hierarchy[i][0] << "\t" << hierarchy[i][1] << "\t" << hierarchy[i][2] << "\t" << hierarchy[i][3] << std::endl;
+ }
+ */
+
+ if ( m_hierarchy_level == -1 ) {
+ for ( size_t i = 0; i < contours.size(); i++ )
+ {
+ int area = cv::contourArea(contours[i], false);
+ if ( area > m_areaThreshold ){
+ std::vector<cv::Point> one_contour;
+ if (m_epsilon > 0) {
+ cv::approxPolyDP(contours[i], one_contour, m_epsilon, true);
+ } else {
+ one_contour = contours[i];
+ }
+ m_contours.push_back(one_contour);
+ m_area.push_back(area);
+ }
+ }
+ } else if ( m_hierarchy_level==-2 ) {
+ for ( size_t i = 0; i < contours.size(); i++ )
+ {
+ int area = cv::contourArea(contours[i], false);
+ if ( area > m_areaThreshold && hierarchy[i][2] == -1 ){ // if contour area > threshold and if contour has no child (a hole)
+ std::vector<cv::Point> one_contour;
+ if (m_epsilon > 0) {
+ cv::approxPolyDP(contours[i], one_contour, m_epsilon, true);
+ } else {
+ one_contour = contours[i];
+ }
+ m_contours.push_back(one_contour);
+ m_area.push_back(area);
+ }
+ }
+ } else {
+ int i=0;
+ int hierarchy_level=0;
+
+ while ( i < (int) contours.size() && i!=-1 && hierarchy_level != -1 )
+ {
+ int area = cv::contourArea(contours[i], false);
+ if ( area > m_areaThreshold && hierarchy_level == m_hierarchy_level )
+ {
+ std::vector<cv::Point> one_contour;
+ if (m_epsilon > 0) {
+ cv::approxPolyDP(contours[i], one_contour, m_epsilon, true);
+ } else {
+ one_contour = contours[i];
+ }
+ m_contours.push_back(one_contour); // push contour if it's big enough
+ m_area.push_back(area);
+ }
+ if ( hierarchy_level < m_hierarchy_level && hierarchy[i][2] != -1 ){ // si on n'a pas atteint le niveau choisi et qu'il y a un enfant on le prend
+ hierarchy_level++;
+ int j = i;
+ i=hierarchy[j][2]; // get the first child
+ } else if ( hierarchy[i][0] != -1 ) {
+ i=hierarchy[i][0]; // get the next contour at this hierarchy level if it exists
+ } else {
+ while ( hierarchy_level != -1 )
+ {
+ hierarchy_level--;
+ i=hierarchy[i][3];
+ if ( i < 0 ) break; // pas de parent...
+ if ( hierarchy[i][0] != -1 ) {
+ i=hierarchy[i][0]; // next du parent
+ break;
+ }
+ }
+
+ }
+ }
+ }
+
+ outputCount();
+ outputBlobs(image);
+ outputContours(image);
+
+ //~ cv::drawContours(imgMat2, m_contours, -1, cv::Scalar(128,255,255), 3);
+
+ /**********************/
+ /* Compute Convexhull */
+ /**********************/
+ if ( m_enable_defects || m_enable_hulls )
+ {
+ for ( size_t i = 0; i < m_contours.size(); i++ )
+ {
+ std::vector<int> convexhull;
+ cv::convexHull(m_contours[i], convexhull);
+ m_convexhulls.push_back(convexhull);
+ }
+ }
+
+ if ( m_enable_hulls )
+ {
+ for ( size_t i = 0 ; i < m_convexhulls.size() ; i++ )
+ {
+ int list_size=(int) m_convexhulls[i].size()*2+2;
+
+ t_atom* data = new t_atom[list_size];
+
+ SETFLOAT(data,m_convexhulls[i].size()); // nb of points for current convexhull
+ SETFLOAT(data+1, 2); // each point is represented by 2 values
+
+ t_atom* apt=data+2;
+
+ for ( size_t j = 0 ; j < m_convexhulls[i].size() ; j++){
+ int k = m_convexhulls[i][j];
+ cv::Point pt = m_contours[i][k];
+ SETFLOAT(apt, (float) pt.x/image.xsize);
+ SETFLOAT(apt+1, (float) pt.y/image.ysize);
+ apt+=2;
+ }
+ outlet_anything(m_dataout_middle, gensym("convexhull"), list_size, data);
+
+ if (data) delete data;
+ data = NULL;
+ }
+ }
+
+ /*****************************/
+ /* Compute convexity defects */
+ /*****************************/
+ if ( m_enable_defects )
+ {
+ for ( size_t i = 0 ; i < m_contours.size() ; i++ )
+ {
+ std::vector<cv::Vec4i> defects(m_convexhulls[i].size());
+
+ cv::Ptr<CvMemStorage> storage = cvCreateMemStorage();
+ cv::InputArray _points = m_contours[i];
+ cv::InputArray _hull = m_convexhulls[i];
+
+ cv::Mat points = _points.getMat();
+ cv::Mat hull = _hull.getMat();
+
+ CvMat c_points = points, c_hull = hull;
+
+ CvSeq* seq = cvConvexityDefects(&c_points, &c_hull, storage);
+
+ double norm = sqrtf( image.xsize*image.ysize );
+
+ if ( !seq ) {
+ error("seq undefined...");
+ continue;
+ }
+
+ int list_size=(int) seq->total*7+2;
+
+ if (seq->total > 0)
+ {
+ t_atom* data = new t_atom[list_size];
+
+ SETFLOAT(data, seq->total); // number of defect for current contour
+ SETFLOAT(data+1, 7); // a defect is represented by 7 values : start point (x,y), end point (x,y), farthest point (x,y) and defect depth
+
+ cv::SeqIterator<CvConvexityDefect> it = cv::Seq<CvConvexityDefect>(seq).begin(); // TODO : crash sometimes but don't know why yet...
+ t_atom* apt = data+2;
+
+ for ( int j = 0 ; j < seq->total ; j++, ++it )
+ {
+ CvConvexityDefect& defect = *it;
+ SETFLOAT(apt, (float) defect.start->x/image.xsize);
+ SETFLOAT(apt+1, (float) defect.start->y/image.ysize);
+ SETFLOAT(apt+2, (float) defect.end->x/image.xsize);
+ SETFLOAT(apt+3, (float) defect.end->y/image.ysize);
+ SETFLOAT(apt+4, (float) defect.depth_point->x/image.xsize);
+ SETFLOAT(apt+5, (float) defect.depth_point->y/image.ysize);
+ SETFLOAT(apt+6, (float) defect.depth/norm);
+ apt+=7;
+ }
+
+ outlet_anything(m_dataout_middle, gensym("convexitydefects"), list_size, data);
+
+ if (data) delete data;
+ data = NULL;
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours :: obj_setupCallback(t_class *classPtr)
+{
+ CPPEXTERN_MSG1(classPtr, "epsilon", epsilonMess, double);
+ CPPEXTERN_MSG1(classPtr, "area", areaMess, double);
+ CPPEXTERN_MSG1(classPtr, "contours", contoursMess, double);
+ CPPEXTERN_MSG1(classPtr, "cvblobOutput", cvblobMess, double);
+ CPPEXTERN_MSG1(classPtr, "convexhulls", convexhullsMess, double);
+ CPPEXTERN_MSG1(classPtr, "convexitydefects", convexitydefectsMess, double);
+ CPPEXTERN_MSG1(classPtr, "hierarchy_level", hierarchyMess, double);
+ CPPEXTERN_MSG1(classPtr, "taboutput", taboutputMess, float);
+ CPPEXTERN_MSG3(classPtr, "settab", tableMess, t_symbol*, t_symbol*, t_symbol*);
+ CPPEXTERN_MSG1(classPtr, "repeat_point", repeat_pointMess, float);
+}
+
+void pix_opencv_contours :: outputCount(){
+ m_totalPointsCount=0;
+ for( size_t i = 0 ; i < m_contours.size(); i++ ){
+ m_totalPointsCount+=m_contours[i].size();
+ }
+ m_totalPointsCount+=m_contours.size()*m_repeat_point*2; // add 2 points for each contour (on start and end)
+
+ t_atom count_atom[2];
+ SETFLOAT(count_atom, m_contours.size());
+ SETFLOAT(count_atom+1, m_totalPointsCount);
+ outlet_anything(m_dataout_right, gensym("count"), 2, count_atom);
+}
+
+void pix_opencv_contours :: outputBlobs(imageStruct &image){
+
+ if ( m_enable_cvblob )
+ {
+ int blob_num=m_contours.size();
+ int blobMatrixWidth=17;
+ int blob_atom_size = 2+blob_num*blobMatrixWidth;
+
+ t_atom* blob_atom = new t_atom[blob_atom_size];
+ for ( int i = 0; i < blob_atom_size; i++){
+ SETFLOAT(blob_atom+i,0);
+ }
+
+ SETFLOAT(blob_atom+1, blobMatrixWidth);
+
+ int count(0);
+ int imageArea = image.xsize * image.ysize;
+ for( size_t i = 0 ; i < m_contours.size(); i++ )
+ {
+ if (!m_contours[i].empty() && m_contours[i].size() > 2) {
+
+ /* compute centroid */
+ Moments mu = moments(m_contours[i]);
+ Point2f centroid;
+ centroid.x=mu.m10/mu.m00;
+ centroid.y=mu.m01/mu.m00;
+ cv::RotatedRect rot_rect = cv::minAreaRect(m_contours[i]);
+ cv::Point2f corners[4];
+ rot_rect.points(corners);
+ double length = cv::arcLength(m_contours[i],true);
+ float area = m_area[i];
+
+ t_atom* apt = blob_atom+2+i*blobMatrixWidth;
+
+ SETFLOAT(apt, count); // set Id
+ count++;
+ SETFLOAT(apt+1, rot_rect.center.x/image.xsize); // rotrect center
+ SETFLOAT(apt+2, rot_rect.center.y/image.ysize);
+ SETFLOAT(apt+3, rot_rect.size.width/image.xsize); // blob size
+ SETFLOAT(apt+4, rot_rect.size.height/image.ysize);
+ SETFLOAT(apt+5, rot_rect.angle); // rotrect angle
+ SETFLOAT(apt+6, area/imageArea); // blob area in % of image sizes
+
+ t_atom* apt2 = apt+7;
+
+ // blob rot rect 4 corners
+ for (int j=0;j<4;j++) {
+ SETFLOAT(apt2, corners[j].x/image.xsize);
+ SETFLOAT(apt2+1, corners[j].y/image.ysize);
+ apt2+=2;
+ }
+
+ SETFLOAT(apt+15, m_contours[i].size()+m_repeat_point*2); // number of points in segment
+ SETFLOAT(apt+16, (float) length);
+ }
+ }
+
+ SETFLOAT(blob_atom, (float) count);
+ if (count) outlet_anything(m_dataout_right, gensym("cvblob"), count*blobMatrixWidth+2, blob_atom);
+ else outlet_float(m_dataout_right, 0);
+
+ if (blob_atom) delete blob_atom;
+ blob_atom = NULL;
+ }
+}
+
+void pix_opencv_contours :: outputContours(imageStruct &image){
+ if ( m_enable_contours ){
+ if ( !m_taboutput ){
+ for( size_t i = 0 ; i < m_contours.size() ; i++ )
+ {
+
+ if (!m_contours[i].empty() && m_contours[i].size() > 2) {
+ int size = 2+(m_repeat_point*2+m_contours[i].size())*2;
+ t_atom*acontours = new t_atom[size];
+ t_atom* apt=acontours;
+ SETFLOAT(apt, static_cast<t_float>(m_repeat_point*2+m_contours[i].size()));
+ SETFLOAT(apt+1, 2.0);
+
+ apt+=2;
+
+ for ( size_t j = 0 ; j < m_repeat_point ; j++){
+ cv::Point pt = m_contours[i][0];
+ SETFLOAT(apt, (float) pt.x/image.xsize);
+ SETFLOAT(apt+1,(float) pt.y/image.ysize);
+ apt+=2;
+ }
+
+ for ( size_t j = 1 ; j < m_contours[i].size() ; j++){
+ cv::Point pt = m_contours[i][j];
+ SETFLOAT(apt,(float) pt.x/image.xsize);
+ SETFLOAT(apt+1,(float) pt.y/image.ysize);
+ apt+=2;
+ }
+
+ for ( size_t j = 0 ; j < m_repeat_point ; j++){
+ cv::Point pt = m_contours[i][0]; // repeat the first point to close the contour
+ SETFLOAT(apt, (float) pt.x/image.xsize);
+ SETFLOAT(apt+1,(float) pt.y/image.ysize);
+ apt+=2;
+ }
+
+ outlet_anything(m_dataout_middle, gensym("contour"), size, acontours);
+ if(acontours) {
+ delete acontours;
+ acontours=NULL;
+ }
+ }
+ }
+ } else {
+
+ //~ put contours in 3 tables.
+ //~ contours are separated by 0 values
+
+ if ( m_x_arrayname == NULL || m_y_arrayname == NULL || m_z_arrayname == NULL){
+ error("please settab before trying to write into...");
+ return;
+ }
+
+ int vecxsize(0), vecysize(0), veczsize(0);
+ t_garray *ax, *ay, *az;
+ t_word *vecx, *vecy, *vecz;
+
+ //~ check if array exist
+ if (!(ax = (t_garray *)pd_findbyclass(m_x_arrayname, garray_class))){
+ error("%s: no such array", m_x_arrayname->s_name);
+ return;
+ }
+ if (!(ay = (t_garray *)pd_findbyclass(m_y_arrayname, garray_class))){
+ error("%s: no such array", m_y_arrayname->s_name);
+ return;
+ }
+ if (!(az = (t_garray *)pd_findbyclass(m_z_arrayname, garray_class))){
+ error("%s: no such array", m_z_arrayname->s_name);
+ return;
+ }
+
+ if (!garray_getfloatwords(ax, &vecxsize, &vecx)){
+ error("%s: bad template for tabwrite", m_x_arrayname->s_name);
+ return;
+ } else if ( vecxsize != m_totalPointsCount && m_autoresize ){
+ garray_resize_long(ax,m_totalPointsCount);
+ if (!garray_getfloatwords(ax, &vecxsize, &vecx)){
+ error("%s: can't resize correctly", m_x_arrayname->s_name);
+ return;
+ }
+ }
+
+ if (!garray_getfloatwords(ay, &vecysize, &vecy)){
+ error("%s: bad template for tabwrite", m_y_arrayname->s_name);
+ return;
+ } else if ( vecysize != m_totalPointsCount && m_autoresize ){
+ garray_resize_long(ay,m_totalPointsCount);
+ if (!garray_getfloatwords(ay, &vecysize, &vecy)){
+ error("%s: can't resize correctly", m_y_arrayname->s_name);
+ return;
+ }
+ }
+
+ if (!garray_getfloatwords(az, &veczsize, &vecz)){
+ error("%s: bad template for tabwrite", m_z_arrayname->s_name);
+ return;
+ } else if ( veczsize != m_totalPointsCount && m_autoresize){
+ garray_resize_long(az,m_totalPointsCount);
+ if (!garray_getfloatwords(az, &veczsize, &vecz)){
+ error("%s: can't resize correctly", m_z_arrayname->s_name);
+ return;
+ }
+ }
+
+ int n=0;
+
+ for( size_t i = 0 ; i < m_contours.size(); i++ )
+ {
+ if (n >= vecxsize || n>=vecysize || n>=veczsize)
+ {
+ error("array are not wide enough");
+ break;
+ }
+
+ unsigned int j;
+ cv::Point pt;
+ pt = m_contours[i][0];
+ //~ start with blank point
+ for (j=0; j<m_repeat_point; j++){
+ vecx[n].w_float = (float) pt.x/image.xsize;
+ vecy[n].w_float = (float) pt.y/image.ysize;
+ vecz[n].w_float = 0.;
+ n++;
+ }
+
+ for ( j = 0 ; j < m_contours[i].size() && n < vecxsize ; j++) {
+
+ pt = m_contours[i][j];
+
+ vecx[n].w_float = (float) pt.x/image.xsize;
+ vecy[n].w_float = (float) pt.y/image.ysize;
+ vecz[n].w_float = 1.;
+ n++;
+ }
+ // close contour
+ pt = m_contours[i][0];
+ for (j=0; j<m_repeat_point && n < vecxsize; j++){ // TODO what is this loop ??? m_repeat_point is either 0 or 1...
+ vecx[n].w_float = (float) pt.x/image.xsize;
+ vecy[n].w_float = (float) pt.y/image.ysize;
+ vecz[n].w_float = 0.;
+ n++;
+ }
+
+
+ }
+ //~ comment the redraw fnt if not needed
+ garray_redraw(ax);
+ garray_redraw(ay);
+ garray_redraw(az);
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////
+// messages handling
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours :: epsilonMess(double arg)
+{
+ m_epsilon = arg > 0 ? arg : 0.;
+}
+void pix_opencv_contours :: areaMess(double arg)
+{
+ m_areaThreshold = arg > 0 ? arg : 30.;
+}
+void pix_opencv_contours :: contoursMess(double arg)
+{
+ m_enable_contours = arg > 0;
+}
+void pix_opencv_contours :: cvblobMess(double arg)
+{
+ m_enable_cvblob = arg > 0;
+}
+void pix_opencv_contours :: convexhullsMess(double arg)
+{
+ m_enable_hulls = arg > 0;
+}
+void pix_opencv_contours :: convexitydefectsMess(double arg)
+{
+ m_enable_defects = arg > 0;
+}
+void pix_opencv_contours :: hierarchyMess(int arg)
+{
+ m_hierarchy_level = arg < -2 ? -1 : arg;
+ m_mode = m_hierarchy_level == -1 ? CV_RETR_LIST : CV_RETR_TREE;
+}
+
+void pix_opencv_contours :: taboutputMess(float arg)
+{
+ m_taboutput = arg > 0;
+}
+
+void pix_opencv_contours :: repeat_pointMess(float arg)
+{
+ m_repeat_point = arg > 1 ? arg : 1;
+}
+
+void pix_opencv_contours :: tableMess(t_symbol*xarray, t_symbol*yarray, t_symbol*zarray)
+{
+ // check if arrays exist
+ m_x_arrayname = xarray;
+ m_y_arrayname = yarray;
+ m_z_arrayname = zarray;
+
+ m_taboutput = 1;
+}
diff --git a/src/pix_opencv_contours.h b/src/pix_opencv_contours.h
new file mode 100644
index 0000000..01810e3
--- /dev/null
+++ b/src/pix_opencv_contours.h
@@ -0,0 +1,89 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ 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_CONTOURS_H_
+#define INCLUDE_PIX_OPENCV_CONTOURS_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_contours
+
+ detects contours and send them out
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_contours : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_contours, GemPixObj)
+
+public:
+
+ //////////
+ // Constructor
+ pix_opencv_contours();
+
+protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_contours();
+
+ //////////
+ // Do the processing
+ virtual void processImage(imageStruct &image);
+
+ // Messages handling
+ void epsilonMess(double arg);
+ void areaMess(double arg);
+ void contoursMess(double arg);
+ void cvblobMess(double arg);
+ void convexhullsMess(double arg);
+ void convexitydefectsMess(double arg);
+ void hierarchyMess(int arg);
+ void taboutputMess(float arg);
+ void tableMess(t_symbol*x, t_symbol*y, t_symbol*z);
+ void repeat_pointMess(float arg);
+
+private:
+
+ void outputCount();
+ void outputBlobs(imageStruct &image);
+ void outputContours(imageStruct &image);
+ t_outlet *m_dataout_middle; // contour outlet
+ t_outlet *m_dataout_right; // info outlet
+ std::vector<std::vector<cv::Point> > m_contours;
+ std::vector<std::vector<int> > m_convexhulls;
+ std::vector<int> m_area;
+
+ unsigned int m_repeat_point;
+ double m_epsilon;
+
+
+ int m_enable_contours, m_enable_hulls, m_enable_defects, m_hierarchy_level, m_mode, m_taboutput, m_enable_cvblob, m_areaThreshold;
+ int m_totalPointsCount, m_autoresize;
+
+ t_symbol *m_x_arrayname, *m_y_arrayname, *m_z_arrayname;
+
+};
+#endif // for header file
diff --git a/src/pix_opencv_contours_boundingrect.cc b/src/pix_opencv_contours_boundingrect.cc
new file mode 100644
index 0000000..2d425d6
--- /dev/null
+++ b/src/pix_opencv_contours_boundingrect.cc
@@ -0,0 +1,772 @@
+////////////////////////////////////////////////////////
+//
+// 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_contours_boundingrect.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_contours_boundingrect)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_contours_boundingrect
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_contours_boundingrect :: pix_opencv_contours_boundingrect()
+{
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("minarea"));
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("maxarea"));
+ m_dataout = outlet_new(this->x_obj, 0);
+ m_countout = outlet_new(this->x_obj, 0);
+ minarea = 10*10;
+ maxarea = 320*240;
+ comp_xsize = 320;
+ comp_ysize = 240;
+ orig = NULL;
+ gray = NULL;
+ cnt_img = NULL;
+ rgb = NULL;
+ x_ftolerance = 5;
+ x_mmove = 20;
+ x_nightmode = 0;
+ x_show = 0;
+ x_draw = 1;
+ x_cmode = CV_RETR_LIST;
+ x_cmethod = CV_CHAIN_APPROX_SIMPLE;
+
+ // initialize font
+ cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 1.0, 1, 8 );
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_contours_boundingrect :: ~pix_opencv_contours_boundingrect()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&cnt_img);
+ cvReleaseImage(&rgb);
+}
+
+/////////////////////////////////////////////////////////
+// Mark a contour
+//
+/////////////////////////////////////////////////////////
+int pix_opencv_contours_boundingrect :: mark(float fx, float fy, float fw, float fh )
+{
+ int i;
+
+ if ( ( fx < 0.0 ) || ( fx > this->comp_xsize ) || ( fy < 0 ) || ( fy > this->comp_ysize ) )
+ {
+ return -1;
+ }
+
+ for ( i=0; i<MAX_MARKERS; i++)
+ {
+ if ( x_xmark[i] == -1 )
+ {
+ x_xmark[i] = (float)(fx+(fw/2));
+ x_ymark[i] = (float)(fy+(fh/2));
+ x_wmark[i] = (int)fw;
+ x_hmark[i] = (int)fh;
+ x_found[i] = x_ftolerance;
+ return i;
+ }
+ }
+
+ // post( "pix_opencv_contours_boundingrect : max markers reached" );
+ return -1;
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours_boundingrect :: processRGBAImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ char tindex[4];
+ int im = 0, i, ic; // Indicator of markers.
+ int oi;
+ float dist, odist; // Distances
+ t_atom rlist[5];
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!orig))
+ {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&cnt_img);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+
+ // Create the output images with new sizes
+ rgb = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 3);
+
+ gray = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ cnt_img = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+
+ }
+ // Here we make a copy of the pixel data from image to orig->imageData
+ // orig is a IplImage struct, the default image type in openCV, take a look on the IplImage data structure here
+ // http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
+ memcpy( orig->imageData, image.data, image.xsize*image.ysize*4 );
+
+ // Convert to grayscale
+ cvCvtColor(orig, gray, CV_RGBA2GRAY);
+ if ( x_nightmode )
+ {
+ cvZero( orig );
+ }
+
+ CvSeq* contours;
+ CvMemStorage* stor02;
+ stor02 = cvCreateMemStorage(0);
+
+ cvFindContours( gray, stor02, &contours, sizeof(CvContour), x_cmode, x_cmethod, cvPoint(0,0) );
+ if (contours) contours = cvApproxPoly( contours, sizeof(CvContour), stor02, CV_POLY_APPROX_DP, 3, 1 );
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( x_xmark[im] != -1.0 )
+ {
+ x_found[im]--;
+ }
+ }
+
+ i = 0; // Indicator of cycles.
+ ic = 0; // Indicator of contours.
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ int count = contours->total; // This is number point in contour
+ CvRect rect;
+
+ rect = cvContourBoundingRect( contours, 1);
+
+ if ( ( (rect.width*rect.height) > minarea ) && ( (rect.width*rect.height) < maxarea ) )
+ {
+
+ oi = -1;
+ dist=(comp_xsize>comp_ysize)?comp_xsize:comp_ysize;
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+
+ if ( x_xmark[im]==-1 ) continue; // no contours
+
+ odist=sqrt( pow( ((float)rect.x+rect.width/2)-x_xmark[im], 2 ) + pow( ((float)rect.y+rect.height/2)-x_ymark[im], 2 ) );
+
+ // search for the closest known contour
+ // that is likely to be this one
+ if ( odist < x_mmove )
+ {
+ if ( odist < dist )
+ {
+ oi=im;
+ dist=odist;
+ }
+ }
+ }
+
+ // new object detected
+ if ( oi == -1 )
+ {
+ oi = this->mark(rect.x, rect.y, rect.width, rect.height );
+ }
+ else
+ {
+ x_xmark[oi] = (float)(rect.x+rect.width/2);
+ x_ymark[oi] = (float)(rect.y+rect.height/2);
+ x_wmark[oi] = (int)rect.width;
+ x_hmark[oi] = (int)rect.height;
+ x_found[oi] = x_ftolerance;
+ }
+
+ if ( x_draw )
+ {
+ cvRectangle( orig, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(255,0,0), 2, 8 , 0 );
+ sprintf( tindex, "%d", oi );
+ cvPutText( orig, tindex, cvPoint(x_xmark[oi],x_ymark[oi]), &font, CV_RGB(255,0,255));
+ }
+
+ if ( x_show )
+ {
+ cvDrawContours( orig, contours, CV_RGB(255,255,255), CV_RGB(255,255,255), 0, 1, 8, cvPoint(0,0) );
+ }
+
+ SETFLOAT(&rlist[0], oi);
+ SETFLOAT(&rlist[1], rect.x);
+ SETFLOAT(&rlist[2], rect.y);
+ SETFLOAT(&rlist[3], rect.width);
+ SETFLOAT(&rlist[4], rect.height);
+
+ outlet_list( m_dataout, 0, 5, rlist );
+ i++;
+ ic++;
+ }
+ }
+
+ outlet_float( m_countout, ic );
+
+ // delete lost objects
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( x_found[im] < 0 )
+ {
+ x_xmark[im] = -1.0;
+ x_ymark[im] = -1,0;
+ x_found[im] = x_ftolerance;
+ SETFLOAT(&rlist[0], im);
+ SETFLOAT(&rlist[1], -1.0);
+ SETFLOAT(&rlist[2], -1.0);
+ SETFLOAT(&rlist[3], 0.0);
+ SETFLOAT(&rlist[4], 0.0);
+ outlet_list( m_dataout, 0, 5, rlist );
+ }
+ }
+
+ cvReleaseMemStorage( &stor02 );
+
+ //copy back the processed frame to image
+ memcpy( image.data, orig->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_contours_boundingrect :: processRGBImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ char tindex[4];
+ t_atom rlist[5];
+ int im = 0; // Indicator of markers.
+ int oi;
+ float dist, odist; // Distances
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb))
+ {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&cnt_img);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+ cnt_img = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ // Convert to grayscale
+ cvCvtColor(rgb, gray, CV_RGB2GRAY);
+ if ( x_nightmode )
+ {
+ cvZero( rgb );
+ }
+
+ CvSeq* contours;
+ CvMemStorage* stor02;
+ stor02 = cvCreateMemStorage(0);
+
+ cvFindContours( gray, stor02, &contours, sizeof(CvContour), x_cmode, x_cmethod, cvPoint(0,0) );
+ if (contours) contours = cvApproxPoly( contours, sizeof(CvContour), stor02, CV_POLY_APPROX_DP, 3, 1 );
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( x_xmark[im] != -1.0 )
+ {
+ x_found[im]--;
+ }
+ }
+
+ int i = 0; // Indicator of cycles.
+ int ic = 0; // Indicator of contours.
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ int count = contours->total; // This is number point in contour
+ CvRect rect;
+
+ rect = cvContourBoundingRect( contours, 1);
+ if ( ( (rect.width*rect.height) > minarea ) && ( (rect.width*rect.height) < maxarea ) )
+ {
+
+ oi = -1;
+ dist=(comp_xsize>comp_ysize)?comp_xsize:comp_ysize;
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+
+ if ( x_xmark[im]==-1 ) continue;
+
+ odist=sqrt( pow( ((float)rect.x+rect.width/2)-x_xmark[im], 2 ) + pow( ((float)rect.y+rect.height/2)-x_ymark[im], 2 ) );
+
+ // search for the closest known contour
+ // that is likely to be this one
+ if ( odist < x_mmove )
+ {
+ if ( odist < dist )
+ {
+ oi=im;
+ dist=odist;
+ }
+ }
+ }
+
+ // new object detected
+ if ( oi == -1 )
+ {
+ oi = this->mark(rect.x, rect.y, rect.width, rect.height );
+ }
+ else
+ {
+ x_xmark[oi] = (float)(rect.x+rect.width/2);
+ x_ymark[oi] = (float)(rect.y+rect.height/2);
+ x_wmark[oi] = (int)rect.width;
+ x_hmark[oi] = (int)rect.height;
+ x_found[oi] = x_ftolerance;
+ }
+
+ if ( x_draw )
+ {
+ cvRectangle( rgb, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(255,0,0), 2, 8 , 0 );
+ sprintf( tindex, "%d", oi );
+ cvPutText( rgb, tindex, cvPoint(x_xmark[oi],x_ymark[oi]), &font, CV_RGB(255,0,255));
+ }
+
+ if ( x_show )
+ {
+ cvDrawContours( rgb, contours, CV_RGB(255,255,255), CV_RGB(255,255,255), 0, 1, 8, cvPoint(0,0) );
+ }
+
+ SETFLOAT(&rlist[0], oi);
+ SETFLOAT(&rlist[1], rect.x);
+ SETFLOAT(&rlist[2], rect.y);
+ SETFLOAT(&rlist[3], rect.width);
+ SETFLOAT(&rlist[4], rect.height);
+
+ outlet_list( m_dataout, 0, 5, rlist );
+ i++;
+ ic++;
+ }
+ }
+ outlet_float( m_countout, ic );
+
+ // delete lost objects
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( x_found[im] < 0 )
+ {
+ x_xmark[im] = -1.0;
+ x_ymark[im] = -1,0;
+ x_found[im] = x_ftolerance;
+ SETFLOAT(&rlist[0], im);
+ SETFLOAT(&rlist[1], -1.0);
+ SETFLOAT(&rlist[2], -1.0);
+ SETFLOAT(&rlist[3], 0.0);
+ SETFLOAT(&rlist[4], 0.0);
+ outlet_list( m_dataout, 0, 5, rlist );
+ }
+ }
+
+ cvReleaseMemStorage( &stor02 );
+
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_contours_boundingrect :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_contours_boundingrect : yuv format not supported" );
+}
+
+void pix_opencv_contours_boundingrect :: processGrayImage(imageStruct &image)
+{
+ char tindex[4];
+ t_atom rlist[5];
+ int im = 0; // Indicator of markers.
+ int oi;
+ float dist, odist; // Distances
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!orig))
+ {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&cnt_img);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+
+ // Create the output images with new sizes
+ rgb = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 3);
+
+ gray = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ cnt_img = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+
+ }
+ // Here we make a copy of the pixel data from image to orig->imageData
+ // orig is a IplImage struct, the default image type in openCV, take a look on the IplImage data structure here
+ // http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+ memcpy( cnt_img->imageData, image.data, image.xsize*image.ysize );
+ if ( x_nightmode )
+ {
+ cvZero( cnt_img );
+ }
+
+ CvSeq* contours;
+ CvMemStorage* stor02;
+ stor02 = cvCreateMemStorage(0);
+
+ cvFindContours( gray, stor02, &contours, sizeof(CvContour), x_cmode, x_cmethod, cvPoint(0,0) );
+ if (contours) contours = cvApproxPoly( contours, sizeof(CvContour), stor02, CV_POLY_APPROX_DP, 3, 1 );
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( x_xmark[im] != -1.0 )
+ {
+ x_found[im]--;
+ }
+ }
+
+ int i = 0; // Indicator of cycles.
+ int ic = 0; // Indicator of contours.
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ int count = contours->total; // This is number point in contour
+ CvRect rect;
+
+ rect = cvContourBoundingRect( contours, 1);
+ if ( ( (rect.width*rect.height) > minarea ) && ( (rect.width*rect.height) < maxarea ) )
+ {
+
+ oi = -1;
+ dist=(comp_xsize>comp_ysize)?comp_xsize:comp_ysize;
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+
+ if ( x_xmark[im]==-1 ) continue;
+
+ odist=sqrt( pow( ((float)rect.x+rect.width/2)-x_xmark[im], 2 ) + pow( ((float)rect.y+rect.height/2)-x_ymark[im], 2 ) );
+
+ // search for the closest known contour
+ // that is likely to be this one
+ if ( odist < x_mmove )
+ {
+ if ( odist < dist )
+ {
+ oi=im;
+ dist=odist;
+ }
+ }
+ }
+
+ // new object detected
+ if ( oi == -1 )
+ {
+ oi = this->mark(rect.x, rect.y, rect.width, rect.height );
+ }
+ else
+ {
+ x_xmark[oi] = (float)(rect.x+rect.width/2);
+ x_ymark[oi] = (float)(rect.y+rect.height/2);
+ x_wmark[oi] = (int)rect.width;
+ x_hmark[oi] = (int)rect.height;
+ x_found[oi] = x_ftolerance;
+ }
+
+ if ( x_draw )
+ {
+ cvRectangle( cnt_img, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), cvScalarAll(255), 2, 8 , 0 );
+ sprintf( tindex, "%d", oi );
+ cvPutText( cnt_img, tindex, cvPoint(x_xmark[oi],x_ymark[oi]), &font, cvScalarAll(255));
+ }
+
+ if ( x_show )
+ {
+ cvDrawContours( cnt_img, contours, CV_RGB(255,255,255), CV_RGB(255,255,255), 0, 1, 8, cvPoint(0,0) );
+ }
+
+ SETFLOAT(&rlist[0], oi);
+ SETFLOAT(&rlist[1], rect.x);
+ SETFLOAT(&rlist[2], rect.y);
+ SETFLOAT(&rlist[3], rect.width);
+ SETFLOAT(&rlist[4], rect.height);
+
+ outlet_list( m_dataout, 0, 5, rlist );
+ i++;
+ ic++;
+ }
+ }
+
+ outlet_float( m_countout, ic );
+
+ // delete lost objects
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( x_found[im] < 0 )
+ {
+ x_xmark[im] = -1.0;
+ x_ymark[im] = -1,0;
+ x_found[im] = x_ftolerance;
+ SETFLOAT(&rlist[0], im);
+ SETFLOAT(&rlist[1], -1.0);
+ SETFLOAT(&rlist[2], -1.0);
+ SETFLOAT(&rlist[3], 0.0);
+ SETFLOAT(&rlist[4], 0.0);
+ outlet_list( m_dataout, 0, 5, rlist );
+ }
+ }
+
+ cvReleaseMemStorage( &stor02 );
+
+ //copy back the processed frame to image
+ memcpy( image.data, cnt_img->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// floatThreshMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours_boundingrect :: floatMinAreaMess (float minarea)
+{
+ if (minarea>0) this->minarea = (int)minarea;
+}
+
+void pix_opencv_contours_boundingrect :: floatMaxAreaMess (float maxarea)
+{
+ if (maxarea>0) this->maxarea = (int)maxarea;
+}
+
+void pix_opencv_contours_boundingrect :: floatFToleranceMess (float ftolerance)
+{
+ if ((int)ftolerance>=1) x_ftolerance = (int)ftolerance;
+}
+
+void pix_opencv_contours_boundingrect :: floatMMoveMess (float mmove)
+{
+ if ((int)mmove>=1) x_mmove = (int)mmove;
+}
+
+void pix_opencv_contours_boundingrect :: floatCModeMess (float cmode)
+{
+ // CV_RETR_EXTERNAL || CV_RETR_LIST || CV_RETR_CCOMP || CV_RETR_TREE
+ int mode = (int)cmode;
+
+ if ( mode == CV_RETR_EXTERNAL )
+ {
+ x_cmode = CV_RETR_EXTERNAL;
+ post( "pix_opencv_contours_boundingrect : mode set to CV_RETR_EXTERNAL" );
+ }
+ if ( mode == CV_RETR_LIST )
+ {
+ x_cmode = CV_RETR_LIST;
+ post( "pix_opencv_contours_boundingrect : mode set to CV_RETR_LIST" );
+ }
+ if ( mode == CV_RETR_CCOMP )
+ {
+ x_cmode = CV_RETR_CCOMP;
+ post( "pix_opencv_contours_boundingrect : mode set to CV_RETR_CCOMP" );
+ }
+ if ( mode == CV_RETR_TREE )
+ {
+ x_cmode = CV_RETR_TREE;
+ post( "pix_opencv_contours_boundingrect : mode set to CV_RETR_TREE" );
+ }
+}
+
+void pix_opencv_contours_boundingrect :: floatCMethodMess (float cmethod)
+{
+ int method = (int)cmethod;
+
+ // CV_CHAIN_CODE || CV_CHAIN_APPROX_NONE || CV_CHAIN_APPROX_SIMPLE || CV_CHAIN_APPROX_TC89_L1 || CV_CHAIN_APPROX_TC89_KCOS || CV_LINK_RUNS
+ if ( method == CV_CHAIN_CODE )
+ {
+ post( "pix_opencv_contours_boundingrect : not supported method : CV_CHAIN_CODE" );
+ }
+ if ( method == CV_CHAIN_APPROX_NONE )
+ {
+ x_cmethod = CV_CHAIN_APPROX_NONE;
+ post( "pix_opencv_contours_boundingrect : method set to CV_CHAIN_APPROX_NONE" );
+ }
+ if ( method == CV_CHAIN_APPROX_SIMPLE )
+ {
+ x_cmethod = CV_CHAIN_APPROX_SIMPLE;
+ post( "pix_opencv_contours_boundingrect : method set to CV_CHAIN_APPROX_SIMPLE" );
+ }
+ if ( method == CV_CHAIN_APPROX_TC89_L1 )
+ {
+ x_cmethod = CV_CHAIN_APPROX_TC89_L1;
+ post( "pix_opencv_contours_boundingrect : method set to CV_CHAIN_APPROX_TC89_L1" );
+ }
+ if ( method == CV_CHAIN_APPROX_TC89_KCOS )
+ {
+ x_cmethod = CV_CHAIN_APPROX_TC89_KCOS;
+ post( "pix_opencv_contours_boundingrect : method set to CV_CHAIN_APPROX_TC89_KCOS" );
+ }
+ if ( ( method == CV_LINK_RUNS ) && ( x_cmode == CV_RETR_LIST ) )
+ {
+ x_cmethod = CV_LINK_RUNS;
+ post( "pix_opencv_contours_boundingrect : method set to CV_LINK_RUNS" );
+ }
+
+}
+
+void pix_opencv_contours_boundingrect :: deleteMark(t_floatarg findex )
+{
+ int i;
+
+ if ( ( findex < 0.0 ) || ( findex >= MAX_MARKERS ) )
+ {
+ return;
+ }
+
+ x_xmark[(int)findex] = -1;
+ x_ymark[(int)findex] = -1;
+ x_wmark[(int)findex] = -1;
+ x_hmark[(int)findex] = -1;
+}
+
+
+void pix_opencv_contours_boundingrect :: floatClearMess (void)
+{
+ int i;
+
+ for ( i=0; i<MAX_MARKERS; i++)
+ {
+ x_xmark[i] = -1;
+ x_ymark[i] = -1;
+ x_wmark[i] = -1;
+ x_hmark[i] = -1;
+ x_found[i] = x_ftolerance;
+ }
+}
+
+void pix_opencv_contours_boundingrect :: floatNightmodeMess (float nightmode)
+{
+ if ( ((int)nightmode==1) || ((int)nightmode==0) ) x_nightmode = (int)nightmode;
+}
+
+void pix_opencv_contours_boundingrect :: floatShowMess (float show)
+{
+ if ( ((int)show==1) || ((int)show==0) ) x_show = (int)show;
+}
+
+void pix_opencv_contours_boundingrect :: floatDrawMess (float draw)
+{
+ if ( ((int)draw==1) || ((int)draw==0) ) x_draw = (int)draw;
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours_boundingrect :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_boundingrect::floatMinAreaMessCallback,
+ gensym("minarea"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_boundingrect::floatMaxAreaMessCallback,
+ gensym("maxarea"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_boundingrect::floatFToleranceMessCallback,
+ gensym("ftolerance"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_boundingrect::floatMMoveMessCallback,
+ gensym("maxmove"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_boundingrect::floatCModeMessCallback,
+ gensym("mode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_boundingrect::floatCMethodMessCallback,
+ gensym("method"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_boundingrect::floatClearMessCallback,
+ gensym("clear"), A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_boundingrect::floatNightmodeMessCallback,
+ gensym("nightmode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_boundingrect::floatShowMessCallback,
+ gensym("show"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_boundingrect::floatDrawMessCallback,
+ gensym("draw"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_contours_boundingrect :: floatMaxAreaMessCallback(void *data, t_floatarg maxarea)
+{
+ GetMyClass(data)->floatMaxAreaMess((float)maxarea);
+}
+
+void pix_opencv_contours_boundingrect :: floatMinAreaMessCallback(void *data, t_floatarg minarea)
+{
+ GetMyClass(data)->floatMinAreaMess((float)minarea);
+}
+
+void pix_opencv_contours_boundingrect :: floatFToleranceMessCallback(void *data, t_floatarg ftolerance)
+{
+ GetMyClass(data)->floatFToleranceMess((float)ftolerance);
+}
+
+void pix_opencv_contours_boundingrect :: floatMMoveMessCallback(void *data, t_floatarg mmove)
+{
+ GetMyClass(data)->floatMMoveMess((float)mmove);
+}
+
+void pix_opencv_contours_boundingrect :: floatCModeMessCallback(void *data, t_floatarg cmode)
+{
+ GetMyClass(data)->floatCModeMess((float)cmode);
+}
+
+void pix_opencv_contours_boundingrect :: floatCMethodMessCallback(void *data, t_floatarg cmethod)
+{
+ GetMyClass(data)->floatCMethodMess((float)cmethod);
+}
+
+void pix_opencv_contours_boundingrect :: floatClearMessCallback(void *data)
+{
+ GetMyClass(data)->floatClearMess();
+}
+
+void pix_opencv_contours_boundingrect :: floatNightmodeMessCallback(void *data, t_floatarg nightmode)
+{
+ GetMyClass(data)->floatNightmodeMess(nightmode);
+}
+
+void pix_opencv_contours_boundingrect :: floatShowMessCallback(void *data, t_floatarg show)
+{
+ GetMyClass(data)->floatShowMess(show);
+}
+
+void pix_opencv_contours_boundingrect :: floatDrawMessCallback(void *data, t_floatarg draw)
+{
+ GetMyClass(data)->floatDrawMess(draw);
+}
diff --git a/src/pix_opencv_contours_boundingrect.h b/src/pix_opencv_contours_boundingrect.h
new file mode 100644
index 0000000..8b47cbb
--- /dev/null
+++ b/src/pix_opencv_contours_boundingrect.h
@@ -0,0 +1,124 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Contours Bounding Rectangle detection
+
+ 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_CONTOURS_BOUNDINGRECT_H_
+#define INCLUDE_PIX_OPENCV_CONTOURS_BOUNDINGRECT_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/legacy/compat.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+#define MAX_MARKERS 500
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_contours_boundingrect
+
+ Contours Bounding Rectangle detection
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_contours_boundingrect : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_contours_boundingrect, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_contours_boundingrect();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_contours_boundingrect();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void floatMinAreaMess(float minarea);
+ void floatMaxAreaMess(float maxarea);
+ void floatFToleranceMess(float maxarea);
+ void floatMMoveMess(float maxarea);
+ void floatCModeMess(float maxarea);
+ void floatCMethodMess(float maxarea);
+ void floatClearMess(void);
+ void floatNightmodeMess(float nightmode);
+ void floatShowMess(float show);
+ void floatDrawMess(float draw);
+ int mark(float fx, float fy, float fw, float fh );
+ void deleteMark(float findex);
+ // The new minimal/maximal area
+ int minarea;
+ int maxarea;
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+ float x_xmark[MAX_MARKERS];
+ float x_ymark[MAX_MARKERS];
+ int x_wmark[MAX_MARKERS];
+ int x_hmark[MAX_MARKERS];
+ int x_found[MAX_MARKERS];
+ int x_ftolerance;
+ int x_mmove;
+ int x_nightmode;
+ int x_draw;
+ int x_show;
+
+ // contours retrieval mode
+ int x_cmode;
+ // contours retrieval method
+ int x_cmethod;
+
+ private:
+
+ t_outlet *m_dataout;
+ t_outlet *m_countout;
+ //////////
+ // Static member functions
+ static void floatMinAreaMessCallback(void *data, t_floatarg minarea);
+ static void floatMaxAreaMessCallback(void *data, t_floatarg maxarea);
+ static void floatFToleranceMessCallback(void *data, t_floatarg ftolerance);
+ static void floatMMoveMessCallback(void *data, t_floatarg mmove);
+ static void floatCModeMessCallback(void *data, t_floatarg cmode);
+ static void floatCMethodMessCallback(void *data, t_floatarg cmethod);
+ static void floatClearMessCallback(void *data);
+ static void floatNightmodeMessCallback(void *data, t_floatarg nightmode);
+ static void floatShowMessCallback(void *data, t_floatarg show);
+ static void floatDrawMessCallback(void *data, t_floatarg draw);
+
+ /////////
+ // IplImage needed
+ IplImage *rgb, *orig, *cnt_img, *gray;
+ CvFont font;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_contours_convexhull.cc b/src/pix_opencv_contours_convexhull.cc
new file mode 100644
index 0000000..b84ea4d
--- /dev/null
+++ b/src/pix_opencv_contours_convexhull.cc
@@ -0,0 +1,582 @@
+////////////////////////////////////////////////////////
+//
+// 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_contours_convexhull.h"
+
+CPPEXTERN_NEW(pix_opencv_contours_convexhull)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_contours_convexhull
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_contours_convexhull :: pix_opencv_contours_convexhull()
+{
+ //inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("minarea"));
+ //inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("maxarea"));
+ m_nomdef = outlet_new(this->x_obj, 0);
+ m_dataout = outlet_new(this->x_obj, 0);
+ minarea = 1;
+ maxarea = 320*240;
+ comp_xsize = 0;
+ comp_ysize = 0;
+ orig = NULL;
+ gray = NULL;
+ rgb = NULL;
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_contours_convexhull :: ~pix_opencv_contours_convexhull()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours_convexhull :: processRGBAImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!orig)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+
+ // Create the output images with new sizes
+ rgb = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 3);
+
+ gray = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+
+ }
+ // Here we make a copy of the pixel data from image to orig->imageData
+ // orig is a IplImage struct, the default image type in openCV, take a look on the IplImage data structure here
+ // http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
+ memcpy( orig->imageData, image.data, image.xsize*image.ysize*4 );
+
+ // Convert to grayscale
+ cvCvtColor(orig, gray, CV_RGBA2GRAY);
+ cvCvtColor(orig, rgb, CV_RGBA2RGB);
+
+ //CvSeq* seqhull;
+ CvSeq* defects;
+ CvSeq* contours;
+ int* hull;
+ int hullsize;
+ CvPoint* PointArray;
+ CvConvexityDefect* defectArray;
+ CvMemStorage* stor02;
+ CvMemStorage* stor03;
+ stor02 = cvCreateMemStorage(0);
+ stor03 = cvCreateMemStorage(0);
+
+
+ cvFindContours( gray, stor02, &contours, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ if (contours) contours = cvApproxPoly( contours, sizeof(CvContour), stor02, CV_POLY_APPROX_DP, 3, 1 );
+
+ int i = 0;
+ int area = 0;
+ int selected = -1;
+
+ //busquem el contorn mes gran
+ CvSeq* first_contour;
+ first_contour = contours;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ CvRect rect;
+ int count = contours->total;
+ rect = cvContourBoundingRect(contours, 1);
+ if ( (rect.width*rect.height) > area )
+ {
+ selected = i;
+ area = rect.width*rect.height;
+ }
+ i++;
+ }
+
+ contours = first_contour;
+
+ int k = 0;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ int i; // Indicator of cycles.
+ int count = contours->total; // This is number point in contour
+ CvPoint center;
+ CvSize size;
+ CvRect rect;
+
+ rect = cvContourBoundingRect( contours, 1);
+ if ( (k==selected) ) {
+
+
+ //fprintf(stderr,"malloc\n");
+ // Alloc memory for contour point set.
+ PointArray = (CvPoint*)malloc( count*sizeof(CvPoint) );
+
+ // Alloc memory for indices of convex hull vertices.
+ hull = (int*)malloc(sizeof(int)*count);
+
+ // Get contour point set.
+ //fprintf(stderr,"cvCvtSeqToArray\n");
+ cvCvtSeqToArray(contours, PointArray, CV_WHOLE_SEQ);
+
+
+ // Find convex hull for curent contour.
+ //fprintf(stderr,"cvConvexHull\n");
+ cvConvexHull( PointArray,
+ count,
+ NULL,
+ CV_COUNTER_CLOCKWISE,
+ hull,
+ &hullsize);
+
+ // Find convex hull for current contour.
+ // This required for cvConvexityDefects().
+ //fprintf(stderr,"cvConvexHull2\n");
+ //seqhull = cvConvexHull2( contours,0,
+ // CV_COUNTER_CLOCKWISE,
+ // 0);
+
+
+ // Draw current contour.
+ //cvDrawContours(x->cnt_img,contours,CV_RGB(255,255,255),CV_RGB(255,255,255),0,1, 8);
+ //cvDrawContours( rgb, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), 2, 2, CV_AA, cvPoint(0,0) );
+ outlet_float( m_nomdef, hullsize );
+
+ t_atom* rlist = new t_atom[hullsize*2];
+
+ int j=0;
+ // Draw convex hull for current contour.
+ for(i=0; i<hullsize-1; i++)
+ {
+#ifdef __APPLE__
+ cvLine(rgb, PointArray[hull[i]],
+ PointArray[hull[i+1]],CV_RGB(0,255,0),1, CV_AA, 0 );
+#else
+ cvLine(rgb, PointArray[hull[i]],
+ PointArray[hull[i+1]],CV_RGB(0,0,255),1, CV_AA, 0 );
+#endif
+ SETFLOAT(&rlist[j], PointArray[hull[i]].x);
+ SETFLOAT(&rlist[j+1], PointArray[hull[i]].y);
+ j = j + 2;
+ }
+#ifdef __APPLE__
+ cvLine(rgb, PointArray[hull[hullsize-1]],
+ PointArray[hull[0]],CV_RGB(0,255,0),1, CV_AA, 0 );
+#else
+ cvLine(rgb, PointArray[hull[hullsize-1]],
+ PointArray[hull[0]],CV_RGB(0,0,255),1, CV_AA, 0 );
+#endif
+
+ SETFLOAT(&rlist[j], PointArray[hull[i]].x);
+ SETFLOAT(&rlist[j+1], PointArray[hull[i]].y);
+ outlet_list( m_dataout, 0, hullsize*2, rlist );
+
+ if (rlist) delete rlist;
+ rlist = NULL;
+
+
+ // Free memory.
+ free(PointArray);
+ free(hull);
+ /* replace CV_FILLED with 1 to see the outlines */
+ //cvDrawContours( x->cnt_img, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), x->levels, 3, CV_AA, cvPoint(0,0) );
+ //cvConvexityDefects( contours, cvConvexHull2( contours, 0, CV_CLOCKWISE, 0 ), stor022 );
+ }
+ k++;
+ }
+
+ cvReleaseMemStorage( &stor03 );
+ cvReleaseMemStorage( &stor02 );
+ //if (defects) cvClearSeq(defects);
+ //if (seqhull) cvClearSeq(seqhull);
+
+ cvCvtColor(rgb, orig, CV_RGB2RGBA);
+ //copy back the processed frame to image
+ memcpy( image.data, orig->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_contours_convexhull :: processRGBImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+
+ // Create the output images with new sizes
+
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ // Convert to grayscale
+ cvCvtColor(rgb, gray, CV_RGB2GRAY);
+
+
+ //CvSeq* seqhull;
+ CvSeq* defects;
+ CvSeq* contours;
+ int* hull;
+ int hullsize;
+ CvPoint* PointArray;
+ CvConvexityDefect* defectArray;
+ CvMemStorage* stor02;
+ CvMemStorage* stor03;
+ stor02 = cvCreateMemStorage(0);
+ stor03 = cvCreateMemStorage(0);
+
+
+ cvFindContours( gray, stor02, &contours, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ if (contours) contours = cvApproxPoly( contours, sizeof(CvContour), stor02, CV_POLY_APPROX_DP, 3, 1 );
+
+ int i = 0;
+ int area = 0;
+ int selected = -1;
+
+ //busquem el contorn mes gran
+ CvSeq* first_contour;
+ first_contour = contours;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ CvRect rect;
+ int count = contours->total;
+ rect = cvContourBoundingRect(contours, 1);
+ if ( (rect.width*rect.height) > area )
+ {
+ selected = i;
+ area = rect.width*rect.height;
+ }
+ i++;
+ }
+
+ contours = first_contour;
+
+ int k = 0;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ int i; // Indicator of cycles.
+ int count = contours->total; // This is number point in contour
+ CvPoint center;
+ CvSize size;
+ CvRect rect;
+
+ rect = cvContourBoundingRect( contours, 1);
+ if ( (k==selected) ) {
+
+
+ //fprintf(stderr,"malloc\n");
+ // Alloc memory for contour point set.
+ PointArray = (CvPoint*)malloc( count*sizeof(CvPoint) );
+
+ // Alloc memory for indices of convex hull vertices.
+ hull = (int*)malloc(sizeof(int)*count);
+
+ // Get contour point set.
+ //fprintf(stderr,"cvCvtSeqToArray\n");
+ cvCvtSeqToArray(contours, PointArray, CV_WHOLE_SEQ);
+
+
+ // Find convex hull for curent contour.
+ //fprintf(stderr,"cvConvexHull\n");
+ cvConvexHull( PointArray,
+ count,
+ NULL,
+ CV_COUNTER_CLOCKWISE,
+ hull,
+ &hullsize);
+
+ // Find convex hull for current contour.
+ // This required for cvConvexityDefects().
+ //fprintf(stderr,"cvConvexHull2\n");
+ //seqhull = cvConvexHull2( contours,0,
+ // CV_COUNTER_CLOCKWISE,
+ // 0);
+
+
+ // Draw current contour.
+ //cvDrawContours(x->cnt_img,contours,CV_RGB(255,255,255),CV_RGB(255,255,255),0,1, 8);
+ //cvDrawContours( rgb, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), 2, 2, CV_AA, cvPoint(0,0) );
+ outlet_float( m_nomdef, hullsize );
+
+ t_atom* rlist = new t_atom[hullsize*2];
+
+ int j=0;
+ // Draw convex hull for current contour.
+ for(i=0; i<hullsize-1; i++)
+ {
+ cvLine(rgb, PointArray[hull[i]],
+ PointArray[hull[i+1]],CV_RGB(0,0,255),1, CV_AA, 0 );
+ SETFLOAT(rlist+j, PointArray[hull[i]].x);
+ SETFLOAT(rlist+j+1, PointArray[hull[i]].y);
+ j = j + 2;
+ }
+ cvLine(rgb, PointArray[hull[hullsize-1]],
+ PointArray[hull[0]],CV_RGB(0,0,255),1, CV_AA, 0 );
+
+ SETFLOAT(rlist+j, PointArray[hull[i]].x);
+ SETFLOAT(rlist+j+1, PointArray[hull[i]].y);
+ outlet_list( m_dataout, 0, hullsize*2, rlist );
+
+ if (rlist) delete rlist;
+ rlist = NULL;
+
+
+ // Free memory.
+ free(PointArray);
+ free(hull);
+ /* replace CV_FILLED with 1 to see the outlines */
+ //cvDrawContours( x->cnt_img, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), x->levels, 3, CV_AA, cvPoint(0,0) );
+ //cvConvexityDefects( contours, cvConvexHull2( contours, 0, CV_CLOCKWISE, 0 ), stor022 );
+ }
+ k++;
+ }
+
+ cvReleaseMemStorage( &stor03 );
+ cvReleaseMemStorage( &stor02 );
+ //if (defects) cvClearSeq(defects);
+ //if (seqhull) cvClearSeq(seqhull);
+
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+
+
+}
+
+void pix_opencv_contours_convexhull :: processYUVImage(imageStruct &image)
+{
+}
+
+void pix_opencv_contours_convexhull :: processGrayImage(imageStruct &image)
+{
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!orig)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+
+ // Create the output images with new sizes
+ rgb = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 3);
+
+ gray = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+
+ }
+ // Here we make a copy of the pixel data from image to orig->imageData
+ // orig is a IplImage struct, the default image type in openCV, take a look on the IplImage data structure here
+ // http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+ cvCvtColor(gray, rgb, CV_GRAY2RGB);
+
+ //CvSeq* seqhull;
+ CvSeq* defects;
+ CvSeq* contours;
+ int* hull;
+ int hullsize;
+ CvPoint* PointArray;
+ CvConvexityDefect* defectArray;
+ CvMemStorage* stor02;
+ CvMemStorage* stor03;
+ stor02 = cvCreateMemStorage(0);
+ stor03 = cvCreateMemStorage(0);
+
+
+ cvFindContours( gray, stor02, &contours, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ if (contours) contours = cvApproxPoly( contours, sizeof(CvContour), stor02, CV_POLY_APPROX_DP, 3, 1 );
+
+ int i = 0;
+ int area = 0;
+ int selected = -1;
+
+ //busquem el contorn mes gran
+ CvSeq* first_contour;
+ first_contour = contours;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ CvRect rect;
+ int count = contours->total;
+ rect = cvContourBoundingRect(contours, 1);
+ if ( (rect.width*rect.height) > area )
+ {
+ selected = i;
+ area = rect.width*rect.height;
+ }
+ i++;
+ }
+
+ contours = first_contour;
+
+ int k = 0;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ int i; // Indicator of cycles.
+ int count = contours->total; // This is number point in contour
+ CvPoint center;
+ CvSize size;
+ CvRect rect;
+
+ rect = cvContourBoundingRect( contours, 1);
+ if ( (k==selected) ) {
+
+
+ //fprintf(stderr,"malloc\n");
+ // Alloc memory for contour point set.
+ PointArray = (CvPoint*)malloc( count*sizeof(CvPoint) );
+
+ // Alloc memory for indices of convex hull vertices.
+ hull = (int*)malloc(sizeof(int)*count);
+
+ // Get contour point set.
+ //fprintf(stderr,"cvCvtSeqToArray\n");
+ cvCvtSeqToArray(contours, PointArray, CV_WHOLE_SEQ);
+
+
+ // Find convex hull for curent contour.
+ //fprintf(stderr,"cvConvexHull\n");
+ cvConvexHull( PointArray,
+ count,
+ NULL,
+ CV_COUNTER_CLOCKWISE,
+ hull,
+ &hullsize);
+
+ // Find convex hull for current contour.
+ // This required for cvConvexityDefects().
+ //fprintf(stderr,"cvConvexHull2\n");
+ //seqhull = cvConvexHull2( contours,0,
+ // CV_COUNTER_CLOCKWISE,
+ // 0);
+
+
+ // Draw current contour.
+ //cvDrawContours(x->cnt_img,contours,CV_RGB(255,255,255),CV_RGB(255,255,255),0,1, 8);
+ //cvDrawContours( rgb, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), 2, 2, CV_AA, cvPoint(0,0) );
+
+ outlet_float( m_nomdef, hullsize );
+
+ t_atom* rlist = new t_atom[hullsize*2];
+
+ int j=0;
+ // Draw convex hull for current contour.
+ for(i=0; i<hullsize-1; i++)
+ {
+ cvLine(rgb, PointArray[hull[i]],
+ PointArray[hull[i+1]],CV_RGB(0,0,255),1, CV_AA, 0 );
+ SETFLOAT(rlist+j, PointArray[hull[i]].x);
+ SETFLOAT(rlist+j+1, PointArray[hull[i]].y);
+ j = j + 2;
+ }
+ cvLine(rgb, PointArray[hull[hullsize-1]],
+ PointArray[hull[0]],CV_RGB(0,0,255),1, CV_AA, 0 );
+
+ SETFLOAT(rlist+j, PointArray[hull[i]].x);
+ SETFLOAT(rlist+j+1, PointArray[hull[i]].y);
+ outlet_list( m_dataout, 0, hullsize*2, rlist );
+ if(rlist) delete[] rlist;
+ rlist=NULL;
+
+
+ // Free memory.
+ free(PointArray);
+ free(hull);
+ /* replace CV_FILLED with 1 to see the outlines */
+ //cvDrawContours( x->cnt_img, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), x->levels, 3, CV_AA, cvPoint(0,0) );
+ //cvConvexityDefects( contours, cvConvexHull2( contours, 0, CV_CLOCKWISE, 0 ), stor022 );
+ }
+ k++;
+ }
+
+ cvReleaseMemStorage( &stor03 );
+ cvReleaseMemStorage( &stor02 );
+ //if (defects) cvClearSeq(defects);
+ //if (seqhull) cvClearSeq(seqhull);
+
+ cvCvtColor(rgb, gray, CV_RGB2GRAY);
+
+ //copy back the processed frame to image
+ memcpy( image.data, gray->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// floatThreshMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours_convexhull :: floatMinAreaMess (float minarea)
+{
+ if (minarea>0) this->minarea = (int)minarea;
+}
+void pix_opencv_contours_convexhull :: floatMaxAreaMess (float maxarea)
+{
+ if (maxarea>0) this->maxarea = (int)maxarea;
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours_convexhull :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_convexhull::floatMinAreaMessCallback,
+ gensym("minarea"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_convexhull::floatMaxAreaMessCallback,
+ gensym("maxarea"), A_FLOAT, A_NULL);
+}
+void pix_opencv_contours_convexhull :: floatMaxAreaMessCallback(void *data, t_floatarg maxarea)
+{
+ GetMyClass(data)->floatMaxAreaMess((float)maxarea);
+}
+void pix_opencv_contours_convexhull :: floatMinAreaMessCallback(void *data, t_floatarg minarea)
+{
+ GetMyClass(data)->floatMinAreaMess((float)minarea);
+}
diff --git a/src/pix_opencv_contours_convexhull.h b/src/pix_opencv_contours_convexhull.h
new file mode 100644
index 0000000..0da4fe6
--- /dev/null
+++ b/src/pix_opencv_contours_convexhull.h
@@ -0,0 +1,88 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Change pix to greyscale
+
+ 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_CONTOURS_CONVEXITY_H_
+#define INCLUDE_PIX_OPENCV_CONTOURS_CONVEXITY_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/legacy/compat.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_contours_convexhull
+
+ Change pix to greyscale
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_contours_convexhull : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_contours_convexhull, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_contours_convexhull();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_contours_convexhull();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void floatMinAreaMess(float minarea);
+ void floatMaxAreaMess(float maxarea);
+ // The new minimal/maximal area
+ int minarea;
+ int maxarea;
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ private:
+
+ t_outlet *m_nomdef;
+ t_outlet *m_dataout;
+ //////////
+ // Static member functions
+ static void floatMinAreaMessCallback(void *data, t_floatarg minarea);
+ static void floatMaxAreaMessCallback(void *data, t_floatarg maxarea);
+
+ /////////
+ // IplImage needed
+ IplImage *rgb, *orig, *gray;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_contours_convexity.cc b/src/pix_opencv_contours_convexity.cc
new file mode 100644
index 0000000..4c7260a
--- /dev/null
+++ b/src/pix_opencv_contours_convexity.cc
@@ -0,0 +1,685 @@
+////////////////////////////////////////////////////////
+//
+// 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_contours_convexity.h"
+
+CPPEXTERN_NEW(pix_opencv_contours_convexity)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_contours_convexity
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_contours_convexity :: pix_opencv_contours_convexity()
+{
+ //inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("minarea"));
+ //inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("maxarea"));
+ m_nomdef = outlet_new(this->x_obj, 0);
+ m_dataout = outlet_new(this->x_obj, 0);
+ minarea = 1;
+ maxarea = 320*240;
+ comp_xsize = 0;
+ comp_ysize = 0;
+ orig = NULL;
+ gray = NULL;
+ rgb = NULL;
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_contours_convexity :: ~pix_opencv_contours_convexity()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours_convexity :: processRGBAImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!orig)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+
+ // Create the output images with new sizes
+ rgb = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 3);
+
+ gray = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+
+ }
+ // Here we make a copy of the pixel data from image to orig->imageData
+ // orig is a IplImage struct, the default image type in openCV, take a look on the IplImage data structure here
+ // http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
+ memcpy( orig->imageData, image.data, image.xsize*image.ysize*4 );
+
+ // Convert to grayscale
+ cvCvtColor(orig, gray, CV_RGBA2GRAY);
+ cvCvtColor(orig, rgb, CV_RGBA2RGB);
+
+ CvSeq* seqhull;
+ CvSeq* defects;
+ CvSeq* contours;
+ int* hull;
+ int hullsize;
+ CvPoint* PointArray;
+ CvConvexityDefect* defectArray;
+ CvMemStorage* stor02;
+ CvMemStorage* stor03;
+ stor02 = cvCreateMemStorage(0);
+ stor03 = cvCreateMemStorage(0);
+
+
+ cvFindContours( gray, stor02, &contours, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ if (contours) contours = cvApproxPoly( contours, sizeof(CvContour), stor02, CV_POLY_APPROX_DP, 3, 1 );
+
+ int i = 0;
+ int area = 0;
+ int selected = -1;
+
+ //busquem el contorn mes gran
+ CvSeq* first_contour;
+ first_contour = contours;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ CvRect rect;
+ int count = contours->total;
+ rect = cvContourBoundingRect(contours, 1);
+ if ( (rect.width*rect.height) > area )
+ {
+ selected = i;
+ area = rect.width*rect.height;
+ }
+ i++;
+ }
+
+ contours = first_contour;
+
+ int k = 0;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ int i; // Indicator of cycles.
+ int count = contours->total; // This is number point in contour
+ CvPoint center;
+ CvSize size;
+ CvRect rect;
+
+ rect = cvContourBoundingRect( contours, 1);
+ if ( (k==selected) ) {
+
+
+ //fprintf(stderr,"malloc\n");
+ // Alloc memory for contour point set.
+ PointArray = (CvPoint*)malloc( count*sizeof(CvPoint) );
+
+ // Alloc memory for indices of convex hull vertices.
+ hull = (int*)malloc(sizeof(int)*count);
+
+ // Get contour point set.
+ //fprintf(stderr,"cvCvtSeqToArray\n");
+ cvCvtSeqToArray(contours, PointArray, CV_WHOLE_SEQ);
+
+
+ // Find convex hull for curent contour.
+ //fprintf(stderr,"cvConvexHull\n");
+ cvConvexHull( PointArray,
+ count,
+ NULL,
+ CV_COUNTER_CLOCKWISE,
+ hull,
+ &hullsize);
+
+ // Find convex hull for current contour.
+ // This required for cvConvexityDefects().
+ //fprintf(stderr,"cvConvexHull2\n");
+ seqhull = cvConvexHull2( contours,0,
+ CV_COUNTER_CLOCKWISE,
+ 0);
+
+ // This required for cvConvexityDefects().
+ // Otherwise cvConvexityDefects() falled.
+ if( hullsize < 4 )
+ continue;
+
+ // Find defects of convexity of current contours.
+ //fprintf(stderr,"cvConvexityDefects\n");
+ defects = cvConvexityDefects( contours,
+ seqhull,
+ stor03);
+ int j=0;
+ // This cycle marks all defects of convexity of current contours.
+ for(;defects;defects = defects->h_next)
+ {
+ int nomdef = defects->total; // defect amount
+ outlet_float( m_nomdef, nomdef );
+
+ if(nomdef == 0)
+ continue;
+
+ // Alloc memory for defect set.
+ //fprintf(stderr,"malloc\n");
+ defectArray = (CvConvexityDefect*)malloc(sizeof(CvConvexityDefect)*nomdef);
+
+ // Get defect set.
+ //fprintf(stderr,"cvCvtSeqToArray\n");
+ cvCvtSeqToArray(defects,defectArray, CV_WHOLE_SEQ);
+
+
+ // Draw marks for all defects.
+ for(i=0; i<nomdef; i++)
+ {
+ cvLine(rgb, *(defectArray[i].start), *(defectArray[i].depth_point),CV_RGB(0,0,255),1, CV_AA, 0 );
+ cvCircle( rgb, *(defectArray[i].depth_point), 5, CV_RGB(0,255,0), -1, 8,0);
+ cvCircle( rgb, *(defectArray[i].start), 5, CV_RGB(0,255,0), -1, 8,0);
+ cvLine(rgb, *(defectArray[i].depth_point), *(defectArray[i].end),CV_RGB(0,0,255),1, CV_AA, 0 );
+ t_atom rlist[7];
+ SETFLOAT(&rlist[0], i);
+ SETFLOAT(&rlist[1], defectArray[i].start->x);
+ SETFLOAT(&rlist[2], defectArray[i].start->y);
+ SETFLOAT(&rlist[3], defectArray[i].depth_point->x);
+ SETFLOAT(&rlist[4], defectArray[i].depth_point->y);
+ SETFLOAT(&rlist[5], defectArray[i].end->x);
+ SETFLOAT(&rlist[6], defectArray[i].end->y);
+ outlet_list( m_dataout, 0, 7, rlist );
+ }
+
+ j++;
+
+ // Free memory.
+ free(defectArray);
+ }
+
+ // Draw current contour.
+ //cvDrawContours(x->cnt_img,contours,CV_RGB(255,255,255),CV_RGB(255,255,255),0,1, 8);
+ cvDrawContours( rgb, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), 2, 2, CV_AA, cvPoint(0,0) );
+
+ // Draw convex hull for current contour.
+ for(i=0; i<hullsize-1; i++)
+ {
+ cvLine(rgb, PointArray[hull[i]],
+ PointArray[hull[i+1]],CV_RGB(255,255,255),1, CV_AA, 0 );
+ }
+ cvLine(rgb, PointArray[hull[hullsize-1]],
+ PointArray[hull[0]],CV_RGB(255,255,255),1, CV_AA, 0 );
+
+
+ // Free memory.
+ free(PointArray);
+ free(hull);
+ /* replace CV_FILLED with 1 to see the outlines */
+ //cvDrawContours( x->cnt_img, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), x->levels, 3, CV_AA, cvPoint(0,0) );
+ //cvConvexityDefects( contours, cvConvexHull2( contours, 0, CV_CLOCKWISE, 0 ), stor022 );
+ }
+ k++;
+ }
+
+ cvReleaseMemStorage( &stor03 );
+ cvReleaseMemStorage( &stor02 );
+ //if (defects) cvClearSeq(defects);
+ //if (seqhull) cvClearSeq(seqhull);
+
+ cvCvtColor(rgb, orig, CV_RGB2RGBA);
+ //copy back the processed frame to image
+ memcpy( image.data, orig->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_contours_convexity :: processRGBImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+
+ // Create the output images with new sizes
+
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ // Convert to grayscale
+ cvCvtColor(rgb, gray, CV_RGB2GRAY);
+
+
+ CvSeq* seqhull;
+ CvSeq* defects;
+ CvSeq* contours;
+ int* hull;
+ int hullsize;
+ CvPoint* PointArray;
+ CvConvexityDefect* defectArray;
+ CvMemStorage* stor02;
+ CvMemStorage* stor03;
+ stor02 = cvCreateMemStorage(0);
+ stor03 = cvCreateMemStorage(0);
+
+
+ cvFindContours( gray, stor02, &contours, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ if (contours) contours = cvApproxPoly( contours, sizeof(CvContour), stor02, CV_POLY_APPROX_DP, 3, 1 );
+
+ int i = 0;
+ int area = 0;
+ int selected = -1;
+
+ //busquem el contorn mes gran
+ CvSeq* first_contour;
+ first_contour = contours;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ CvRect rect;
+ int count = contours->total;
+ rect = cvContourBoundingRect(contours, 1);
+ if ( (rect.width*rect.height) > area )
+ {
+ selected = i;
+ area = rect.width*rect.height;
+ }
+ i++;
+ }
+
+ contours = first_contour;
+
+ int k = 0;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ int i; // Indicator of cycles.
+ int count = contours->total; // This is number point in contour
+ CvPoint center;
+ CvSize size;
+ CvRect rect;
+
+ rect = cvContourBoundingRect( contours, 1);
+ if ( (k==selected) ) {
+
+
+ //fprintf(stderr,"malloc\n");
+ // Alloc memory for contour point set.
+ PointArray = (CvPoint*)malloc( count*sizeof(CvPoint) );
+
+ // Alloc memory for indices of convex hull vertices.
+ hull = (int*)malloc(sizeof(int)*count);
+
+ // Get contour point set.
+ //fprintf(stderr,"cvCvtSeqToArray\n");
+ cvCvtSeqToArray(contours, PointArray, CV_WHOLE_SEQ);
+
+
+ // Find convex hull for curent contour.
+ //fprintf(stderr,"cvConvexHull\n");
+ cvConvexHull( PointArray,
+ count,
+ NULL,
+ CV_COUNTER_CLOCKWISE,
+ hull,
+ &hullsize);
+
+ // Find convex hull for current contour.
+ // This required for cvConvexityDefects().
+ //fprintf(stderr,"cvConvexHull2\n");
+ seqhull = cvConvexHull2( contours,0,
+ CV_COUNTER_CLOCKWISE,
+ 0);
+
+ // This required for cvConvexityDefects().
+ // Otherwise cvConvexityDefects() falled.
+ if( hullsize < 4 )
+ continue;
+
+ // Find defects of convexity of current contours.
+ //fprintf(stderr,"cvConvexityDefects\n");
+ defects = cvConvexityDefects( contours,
+ seqhull,
+ stor03);
+ int j=0;
+ // This cycle marks all defects of convexity of current contours.
+ for(;defects;defects = defects->h_next)
+ {
+ int nomdef = defects->total; // defect amount
+ outlet_float( m_nomdef, nomdef );
+
+ if(nomdef == 0)
+ continue;
+
+ // Alloc memory for defect set.
+ //fprintf(stderr,"malloc\n");
+ defectArray = (CvConvexityDefect*)malloc(sizeof(CvConvexityDefect)*nomdef);
+
+ // Get defect set.
+ //fprintf(stderr,"cvCvtSeqToArray\n");
+ cvCvtSeqToArray(defects,defectArray, CV_WHOLE_SEQ);
+
+
+ // Draw marks for all defects.
+ for(i=0; i<nomdef; i++)
+ {
+ cvLine(rgb, *(defectArray[i].start), *(defectArray[i].depth_point),CV_RGB(0,0,255),1, CV_AA, 0 );
+ cvCircle( rgb, *(defectArray[i].depth_point), 5, CV_RGB(0,255,0), -1, 8,0);
+ cvCircle( rgb, *(defectArray[i].start), 5, CV_RGB(0,255,0), -1, 8,0);
+ cvLine(rgb, *(defectArray[i].depth_point), *(defectArray[i].end),CV_RGB(0,0,255),1, CV_AA, 0 );
+ t_atom rlist[7];
+ SETFLOAT(&rlist[0], i);
+ SETFLOAT(&rlist[1], defectArray[i].start->x);
+ SETFLOAT(&rlist[2], defectArray[i].start->y);
+ SETFLOAT(&rlist[3], defectArray[i].depth_point->x);
+ SETFLOAT(&rlist[4], defectArray[i].depth_point->y);
+ SETFLOAT(&rlist[5], defectArray[i].end->x);
+ SETFLOAT(&rlist[6], defectArray[i].end->y);
+ outlet_list( m_dataout, 0, 7, rlist );
+ }
+
+ j++;
+
+ // Free memory.
+ free(defectArray);
+ }
+
+ // Draw current contour.
+ //cvDrawContours(x->cnt_img,contours,CV_RGB(255,255,255),CV_RGB(255,255,255),0,1, 8);
+ cvDrawContours( rgb, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), 2, 2, CV_AA, cvPoint(0,0) );
+
+ // Draw convex hull for current contour.
+ for(i=0; i<hullsize-1; i++)
+ {
+ cvLine(rgb, PointArray[hull[i]],
+ PointArray[hull[i+1]],CV_RGB(255,255,255),1, CV_AA, 0 );
+ }
+ cvLine(rgb, PointArray[hull[hullsize-1]],
+ PointArray[hull[0]],CV_RGB(255,255,255),1, CV_AA, 0 );
+
+
+ // Free memory.
+ free(PointArray);
+ free(hull);
+ /* replace CV_FILLED with 1 to see the outlines */
+ //cvDrawContours( x->cnt_img, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), x->levels, 3, CV_AA, cvPoint(0,0) );
+ //cvConvexityDefects( contours, cvConvexHull2( contours, 0, CV_CLOCKWISE, 0 ), stor022 );
+ }
+ k++;
+ }
+
+ cvReleaseMemStorage( &stor03 );
+ cvReleaseMemStorage( &stor02 );
+ //if (defects) cvClearSeq(defects);
+ //if (seqhull) cvClearSeq(seqhull);
+
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_contours_convexity :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_contours_convexity : yuv format not supported" );
+}
+
+void pix_opencv_contours_convexity :: processGrayImage(imageStruct &image)
+{
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!orig)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+
+ // Create the output images with new sizes
+ rgb = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 3);
+
+ gray = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+
+ }
+ // Here we make a copy of the pixel data from image to orig->imageData
+ // orig is a IplImage struct, the default image type in openCV, take a look on the IplImage data structure here
+ // http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+ cvCvtColor(gray, rgb, CV_GRAY2RGB);
+
+ CvSeq* seqhull;
+ CvSeq* defects;
+ CvSeq* contours;
+ int* hull;
+ int hullsize;
+ CvPoint* PointArray;
+ CvConvexityDefect* defectArray;
+ CvMemStorage* stor02;
+ CvMemStorage* stor03;
+ stor02 = cvCreateMemStorage(0);
+ stor03 = cvCreateMemStorage(0);
+
+
+ cvFindContours( gray, stor02, &contours, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ if (contours) contours = cvApproxPoly( contours, sizeof(CvContour), stor02, CV_POLY_APPROX_DP, 3, 1 );
+
+ int i = 0;
+ int area = 0;
+ int selected = -1;
+
+ //busquem el contorn mes gran
+ CvSeq* first_contour;
+ first_contour = contours;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ CvRect rect;
+ int count = contours->total;
+ rect = cvContourBoundingRect(contours, 1);
+ if ( (rect.width*rect.height) > area )
+ {
+ selected = i;
+ area = rect.width*rect.height;
+ }
+ i++;
+ }
+
+ contours = first_contour;
+
+ int k = 0;
+ for( ; contours != 0; contours = contours->h_next )
+ {
+ int i; // Indicator of cycles.
+ int count = contours->total; // This is number point in contour
+ CvPoint center;
+ CvSize size;
+ CvRect rect;
+
+ rect = cvContourBoundingRect( contours, 1);
+ if ( (k==selected) ) {
+
+
+ //fprintf(stderr,"malloc\n");
+ // Alloc memory for contour point set.
+ PointArray = (CvPoint*)malloc( count*sizeof(CvPoint) );
+
+ // Alloc memory for indices of convex hull vertices.
+ hull = (int*)malloc(sizeof(int)*count);
+
+ // Get contour point set.
+ //fprintf(stderr,"cvCvtSeqToArray\n");
+ cvCvtSeqToArray(contours, PointArray, CV_WHOLE_SEQ);
+
+
+ // Find convex hull for curent contour.
+ //fprintf(stderr,"cvConvexHull\n");
+ cvConvexHull( PointArray,
+ count,
+ NULL,
+ CV_COUNTER_CLOCKWISE,
+ hull,
+ &hullsize);
+
+ // Find convex hull for current contour.
+ // This required for cvConvexityDefects().
+ //fprintf(stderr,"cvConvexHull2\n");
+ seqhull = cvConvexHull2( contours,0,
+ CV_COUNTER_CLOCKWISE,
+ 0);
+
+ // This required for cvConvexityDefects().
+ // Otherwise cvConvexityDefects() falled.
+ if( hullsize < 4 )
+ continue;
+
+ // Find defects of convexity of current contours.
+ //fprintf(stderr,"cvConvexityDefects\n");
+ defects = cvConvexityDefects( contours,
+ seqhull,
+ stor03);
+ int j=0;
+ // This cycle marks all defects of convexity of current contours.
+ for(;defects;defects = defects->h_next)
+ {
+ int nomdef = defects->total; // defect amount
+ outlet_float( m_nomdef, nomdef );
+
+ if(nomdef == 0)
+ continue;
+
+ // Alloc memory for defect set.
+ //fprintf(stderr,"malloc\n");
+ defectArray = (CvConvexityDefect*)malloc(sizeof(CvConvexityDefect)*nomdef);
+
+ // Get defect set.
+ //fprintf(stderr,"cvCvtSeqToArray\n");
+ cvCvtSeqToArray(defects,defectArray, CV_WHOLE_SEQ);
+
+
+ // Draw marks for all defects.
+ for(i=0; i<nomdef; i++)
+ {
+ cvLine(rgb, *(defectArray[i].start), *(defectArray[i].depth_point),CV_RGB(0,0,255),1, CV_AA, 0 );
+ cvCircle( rgb, *(defectArray[i].depth_point), 5, CV_RGB(0,255,0), -1, 8,0);
+ cvCircle( rgb, *(defectArray[i].start), 5, CV_RGB(0,255,0), -1, 8,0);
+ cvLine(rgb, *(defectArray[i].depth_point), *(defectArray[i].end),CV_RGB(0,0,255),1, CV_AA, 0 );
+ t_atom rlist[7];
+ SETFLOAT(&rlist[0], i);
+ SETFLOAT(&rlist[1], defectArray[i].start->x);
+ SETFLOAT(&rlist[2], defectArray[i].start->y);
+ SETFLOAT(&rlist[3], defectArray[i].depth_point->x);
+ SETFLOAT(&rlist[4], defectArray[i].depth_point->y);
+ SETFLOAT(&rlist[5], defectArray[i].end->x);
+ SETFLOAT(&rlist[6], defectArray[i].end->y);
+ outlet_list( m_dataout, 0, 7, rlist );
+ }
+
+ j++;
+
+ // Free memory.
+ free(defectArray);
+ }
+
+ // Draw current contour.
+ //cvDrawContours(x->cnt_img,contours,CV_RGB(255,255,255),CV_RGB(255,255,255),0,1, 8);
+ cvDrawContours( rgb, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), 2, 2, CV_AA, cvPoint(0,0) );
+
+ // Draw convex hull for current contour.
+ for(i=0; i<hullsize-1; i++)
+ {
+ cvLine(rgb, PointArray[hull[i]],
+ PointArray[hull[i+1]],CV_RGB(255,255,255),1, CV_AA, 0 );
+ }
+ cvLine(rgb, PointArray[hull[hullsize-1]],
+ PointArray[hull[0]],CV_RGB(255,255,255),1, CV_AA, 0 );
+
+
+ // Free memory.
+ free(PointArray);
+ free(hull);
+ /* replace CV_FILLED with 1 to see the outlines */
+ //cvDrawContours( x->cnt_img, contours, CV_RGB(255,0,0), CV_RGB(0,255,0), x->levels, 3, CV_AA, cvPoint(0,0) );
+ //cvConvexityDefects( contours, cvConvexHull2( contours, 0, CV_CLOCKWISE, 0 ), stor022 );
+ }
+ k++;
+ }
+
+ cvReleaseMemStorage( &stor03 );
+ cvReleaseMemStorage( &stor02 );
+ //if (defects) cvClearSeq(defects);
+ //if (seqhull) cvClearSeq(seqhull);
+
+ cvCvtColor(rgb, gray, CV_RGB2GRAY);
+
+ //copy back the processed frame to image
+ memcpy( image.data, gray->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// floatThreshMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours_convexity :: floatMinAreaMess (float minarea)
+{
+ if (minarea>0) this->minarea = (int)minarea;
+}
+void pix_opencv_contours_convexity :: floatMaxAreaMess (float maxarea)
+{
+ if (maxarea>0) this->maxarea = (int)maxarea;
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_contours_convexity :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_convexity::floatMinAreaMessCallback,
+ gensym("minarea"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_contours_convexity::floatMaxAreaMessCallback,
+ gensym("maxarea"), A_FLOAT, A_NULL);
+}
+void pix_opencv_contours_convexity :: floatMaxAreaMessCallback(void *data, t_floatarg maxarea)
+{
+ GetMyClass(data)->floatMaxAreaMess((float)maxarea);
+}
+void pix_opencv_contours_convexity :: floatMinAreaMessCallback(void *data, t_floatarg minarea)
+{
+ GetMyClass(data)->floatMinAreaMess((float)minarea);
+}
diff --git a/src/pix_opencv_contours_convexity.h b/src/pix_opencv_contours_convexity.h
new file mode 100644
index 0000000..f7429c2
--- /dev/null
+++ b/src/pix_opencv_contours_convexity.h
@@ -0,0 +1,88 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Contours convexity detection
+
+ 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_CONTOURS_CONVEXITY_H_
+#define INCLUDE_PIX_OPENCV_CONTOURS_CONVEXITY_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/legacy/compat.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_contours_convexity
+
+ Contours convexity detection
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_contours_convexity : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_contours_convexity, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_contours_convexity();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_contours_convexity();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void floatMinAreaMess(float minarea);
+ void floatMaxAreaMess(float maxarea);
+ // The new minimal/maximal area
+ int minarea;
+ int maxarea;
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ private:
+
+ t_outlet *m_nomdef;
+ t_outlet *m_dataout;
+ //////////
+ // Static member functions
+ static void floatMinAreaMessCallback(void *data, t_floatarg minarea);
+ static void floatMaxAreaMessCallback(void *data, t_floatarg maxarea);
+
+ /////////
+ // IplImage needed
+ IplImage *rgb, *orig, *gray;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_dft.cc b/src/pix_opencv_dft.cc
new file mode 100644
index 0000000..b7a3ce5
--- /dev/null
+++ b/src/pix_opencv_dft.cc
@@ -0,0 +1,462 @@
+////////////////////////////////////////////////////////
+//
+// 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_dft.h"
+
+CPPEXTERN_NEW(pix_opencv_dft)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_dft
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_dft :: pix_opencv_dft()
+{
+ int i;
+
+ x_calculate = 1;
+ comp_xsize=0;
+ comp_ysize=0;
+
+ rgb = NULL;
+ rgba = NULL;
+ gray = NULL;
+ input_re = NULL;
+ input_im = NULL;
+ input_co = NULL;
+ dft_A = NULL;
+ image_re = NULL;
+ image_im = NULL;
+ image_mout = NULL;
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_dft :: ~pix_opencv_dft()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &rgba );
+ //cvReleaseImage( &gray );
+ cvReleaseImage( &input_re );
+ cvReleaseImage( &input_im );
+ cvReleaseImage( &input_co );
+ cvReleaseMat( &dft_A );
+ //cvReleaseImage( &mage_re );
+ //cvReleaseImage( &image_im );
+ //cvReleaseImage( &image_mout );
+
+}
+
+/////////////////////////////////////////////////////////
+// shiftDFT
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_dft :: shiftDFT(CvArr * src_arr, CvArr * dst_arr )
+{
+ CvMat *tmp=NULL;
+ CvMat q1stub, q2stub;
+ CvMat q3stub, q4stub;
+ CvMat d1stub, d2stub;
+ CvMat d3stub, d4stub;
+ CvMat * q1, * q2, * q3, * q4;
+ CvMat * d1, * d2, * d3, * d4;
+
+ CvSize size = cvGetSize(src_arr);
+ CvSize dst_size = cvGetSize(dst_arr);
+ int cx, cy;
+
+ if(dst_size.width != size.width ||
+ dst_size.height != size.height){
+ return;
+ }
+
+ if(src_arr==dst_arr){
+ tmp = cvCreateMat(size.height/2, size.width/2, cvGetElemType(src_arr));
+ }
+
+ cx = size.width/2;
+ cy = size.height/2; // image center
+
+ q1 = cvGetSubRect( src_arr, &q1stub, cvRect(0,0,cx, cy) );
+ q2 = cvGetSubRect( src_arr, &q2stub, cvRect(cx,0,cx,cy) );
+ q3 = cvGetSubRect( src_arr, &q3stub, cvRect(cx,cy,cx,cy) );
+ q4 = cvGetSubRect( src_arr, &q4stub, cvRect(0,cy,cx,cy) );
+ d1 = cvGetSubRect( src_arr, &d1stub, cvRect(0,0,cx,cy) );
+ d2 = cvGetSubRect( src_arr, &d2stub, cvRect(cx,0,cx,cy) );
+ d3 = cvGetSubRect( src_arr, &d3stub, cvRect(cx,cy,cx,cy) );
+ d4 = cvGetSubRect( src_arr, &d4stub, cvRect(0,cy,cx,cy) );
+
+ if(src_arr!=dst_arr)
+ {
+ if( !CV_ARE_TYPES_EQ( q1, d1 )){
+ return;
+ }
+ cvCopy(q3, d1, 0);
+ cvCopy(q4, d2, 0);
+ cvCopy(q1, d3, 0);
+ cvCopy(q2, d4, 0);
+ }
+ else
+ {
+ cvCopy(q3, tmp, 0);
+ cvCopy(q1, q3, 0);
+ cvCopy(tmp, q1, 0);
+ cvCopy(q4, tmp, 0);
+ cvCopy(q2, q4, 0);
+ cvCopy(tmp, q2, 0);
+ }
+ if(src_arr==dst_arr){
+ cvReleaseMat( &tmp );
+ }
+
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_dft :: processRGBAImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+ CvMat tmp;
+ double m,M;
+ int px,py;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize)) {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &rgba );
+ //cvReleaseImage( &gray );
+ cvReleaseImage( &input_re );
+ cvReleaseImage( &input_im );
+ cvReleaseImage( &input_co );
+ cvReleaseMat( &dft_A );
+ //cvReleaseImage( &image_re );
+ //cvReleaseImage( &image_im );
+ //cvReleaseImage( &image_mout );
+ //cvReleaseImage( &image_pout );
+
+ //Create cv_images
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ gray = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 1);
+ input_re = cvCreateImage( cvGetSize(rgb), IPL_DEPTH_64F, 1);
+ input_im = cvCreateImage( cvGetSize(rgb), IPL_DEPTH_64F, 1);
+ input_co = cvCreateImage( cvGetSize(rgb), IPL_DEPTH_64F, 2);
+ dft_M = cvGetOptimalDFTSize( image.ysize - 1 );
+ dft_N = cvGetOptimalDFTSize( image.xsize - 1 );
+ dft_A = cvCreateMat( dft_M, dft_N, CV_64FC2 );
+ image_re = cvCreateImage( cvSize(dft_N, dft_M), IPL_DEPTH_64F, 1);
+ image_im = cvCreateImage( cvSize(dft_N, dft_M), IPL_DEPTH_64F, 1);
+ image_mout = cvCreateImage( cvSize(dft_N, dft_M), IPL_DEPTH_8U, 1);
+
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ cvCvtColor(rgba, gray, CV_BGRA2GRAY);
+
+ if ( x_calculate )
+ {
+ // discrete fourier transform
+ cvScale(gray, input_re, 1.0, 0.0);
+ cvZero(input_im);
+ cvMerge(input_re, input_im, NULL, NULL, input_co);
+
+ // copy A to dft_A and pad dft_A with zeros
+ cvGetSubRect( dft_A, &tmp, cvRect(0,0, gray->width, gray->height));
+ cvCopy( input_co, &tmp, NULL );
+ if( dft_A->cols > gray->width )
+ {
+ cvGetSubRect( dft_A, &tmp, cvRect(gray->width,0, dft_A->cols - gray->width, gray->height));
+ cvZero( &tmp );
+ }
+
+ // no need to pad bottom part of dft_A with zeros because of
+ // use nonzero_rows parameter in cvDFT() call below
+ cvDFT( dft_A, dft_A, CV_DXT_FORWARD, input_co->height );
+
+ // Split Fourier in real and imaginary parts
+ cvSplit( dft_A, image_re, image_im, 0, 0 );
+
+ // Compute the magnitude of the spectrum Mag = sqrt(Re^2 + Im^2)
+ cvPow( image_re, image_re, 2.0);
+ cvPow( image_im, image_im, 2.0);
+ cvAdd( image_re, image_im, image_re, NULL);
+ cvPow( image_re, image_re, 0.5 );
+
+ // Compute log(1 + Mag)
+ cvAddS( image_re, cvScalarAll(1.0), image_re, NULL ); // 1 + Mag
+ cvLog( image_re, image_re ); // log(1 + Mag)
+
+ // Rearrange the quadrants of Fourier image so that the origin is at
+ // the image center
+ this->shiftDFT( image_re, image_re );
+
+ // normalize image
+ cvMinMaxLoc(image_re, &m, &M, NULL, NULL, NULL);
+ cvScale(image_re, image_re, 255.0/(M-m), 255.0*(-m)/(M-m));
+
+ for( py=0; py<image_re->height; py++ ) {
+ double* ptri = (double*) ( image_re->imageData + py * image_re->widthStep);
+ unsigned char* ptrp = (unsigned char*) ( image_mout->imageData + py * image_mout->widthStep);
+ for( px=0; px<image_re->width; px++ ) {
+ if ( *(ptrp+px) > 255.0 ) post( "pix_opencv_dft : error value over 255" );
+ (*(ptrp+px)) = (unsigned char)( (*(ptri+px)) );
+ }
+ }
+
+ x_calculate=0;
+ }
+
+ cvCvtColor(image_mout, rgba, CV_GRAY2RGBA);
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_dft :: processRGBImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+ CvMat tmp;
+ double m,M;
+ int px,py;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize)) {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &rgba );
+ //cvReleaseImage( &gray );
+ cvReleaseImage( &input_re );
+ cvReleaseImage( &input_im );
+ cvReleaseImage( &input_co );
+ cvReleaseMat( &dft_A );
+ //cvReleaseImage( &image_re );
+ //cvReleaseImage( &image_im );
+ //cvReleaseImage( &image_mout );
+ //cvReleaseImage( &image_pout );
+
+ //Create cv_images
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ gray = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 1);
+ input_re = cvCreateImage( cvGetSize(rgb), IPL_DEPTH_64F, 1);
+ input_im = cvCreateImage( cvGetSize(rgb), IPL_DEPTH_64F, 1);
+ input_co = cvCreateImage( cvGetSize(rgb), IPL_DEPTH_64F, 2);
+ dft_M = cvGetOptimalDFTSize( image.ysize - 1 );
+ dft_N = cvGetOptimalDFTSize( image.xsize - 1 );
+ dft_A = cvCreateMat( dft_M, dft_N, CV_64FC2 );
+ image_re = cvCreateImage( cvSize(dft_N, dft_M), IPL_DEPTH_64F, 1);
+ image_im = cvCreateImage( cvSize(dft_N, dft_M), IPL_DEPTH_64F, 1);
+ image_mout = cvCreateImage( cvSize(dft_N, dft_M), IPL_DEPTH_8U, 1);
+
+ }
+
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+ cvCvtColor(rgb, gray, CV_BGR2GRAY);
+
+ if ( x_calculate )
+ {
+ // discrete fourier transform
+ cvScale(gray, input_re, 1.0, 0.0);
+ cvZero(input_im);
+ cvMerge(input_re, input_im, NULL, NULL, input_co);
+
+ // copy A to dft_A and pad dft_A with zeros
+ cvGetSubRect( dft_A, &tmp, cvRect(0,0, gray->width, gray->height));
+ cvCopy( input_co, &tmp, NULL );
+ if( dft_A->cols > gray->width )
+ {
+ cvGetSubRect( dft_A, &tmp, cvRect(gray->width,0, dft_A->cols - gray->width, gray->height));
+ cvZero( &tmp );
+ }
+
+ // no need to pad bottom part of dft_A with zeros because of
+ // use nonzero_rows parameter in cvDFT() call below
+ cvDFT( dft_A, dft_A, CV_DXT_FORWARD, input_co->height );
+
+ // Split Fourier in real and imaginary parts
+ cvSplit( dft_A, image_re, image_im, 0, 0 );
+
+ // Compute the magnitude of the spectrum Mag = sqrt(Re^2 + Im^2)
+ cvPow( image_re, image_re, 2.0);
+ cvPow( image_im, image_im, 2.0);
+ cvAdd( image_re, image_im, image_re, NULL);
+ cvPow( image_re, image_re, 0.5 );
+
+ // Compute log(1 + Mag)
+ cvAddS( image_re, cvScalarAll(1.0), image_re, NULL ); // 1 + Mag
+ cvLog( image_re, image_re ); // log(1 + Mag)
+
+ // Rearrange the quadrants of Fourier image so that the origin is at
+ // the image center
+ this->shiftDFT( image_re, image_re );
+
+ // normalize image
+ cvMinMaxLoc(image_re, &m, &M, NULL, NULL, NULL);
+ cvScale(image_re, image_re, 255.0/(M-m), 255.0*(-m)/(M-m));
+
+ for( py=0; py<image_re->height; py++ ) {
+ double* ptri = (double*) ( image_re->imageData + py * image_re->widthStep);
+ unsigned char* ptrp = (unsigned char*) ( image_mout->imageData + py * image_mout->widthStep);
+ for( px=0; px<image_re->width; px++ ) {
+ if ( *(ptrp+px) > 255.0 ) post( "pix_opencv_dft : error value over 255" );
+ (*(ptrp+px)) = (unsigned char)( (*(ptri+px)) );
+ }
+ }
+
+ x_calculate=0;
+ }
+
+ cvCvtColor(image_mout, rgb, CV_GRAY2RGB);
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_dft :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_contours_convexity : yuv format not supported" );
+}
+
+void pix_opencv_dft :: processGrayImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+ CvMat tmp;
+ double m,M;
+ int px,py;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize)) {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &rgba );
+ //cvReleaseImage( &gray );
+ cvReleaseImage( &input_re );
+ cvReleaseImage( &input_im );
+ cvReleaseImage( &input_co );
+ cvReleaseMat( &dft_A );
+ //cvReleaseImage( &image_re );
+ //cvReleaseImage( &image_im );
+ //cvReleaseImage( &image_mout );
+ //cvReleaseImage( &image_pout );
+
+ //Create cv_images
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ gray = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 1);
+ input_re = cvCreateImage( cvGetSize(rgb), IPL_DEPTH_64F, 1);
+ input_im = cvCreateImage( cvGetSize(rgb), IPL_DEPTH_64F, 1);
+ input_co = cvCreateImage( cvGetSize(rgb), IPL_DEPTH_64F, 2);
+ dft_M = cvGetOptimalDFTSize( image.ysize - 1 );
+ dft_N = cvGetOptimalDFTSize( image.xsize - 1 );
+ dft_A = cvCreateMat( dft_M, dft_N, CV_64FC2 );
+ image_re = cvCreateImage( cvSize(dft_N, dft_M), IPL_DEPTH_64F, 1);
+ image_im = cvCreateImage( cvSize(dft_N, dft_M), IPL_DEPTH_64F, 1);
+ image_mout = cvCreateImage( cvSize(dft_N, dft_M), IPL_DEPTH_8U, 1);
+
+ }
+
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+
+ if ( x_calculate )
+ {
+ // discrete fourier transform
+ cvScale(gray, input_re, 1.0, 0.0);
+ cvZero(input_im);
+ cvMerge(input_re, input_im, NULL, NULL, input_co);
+
+ // copy A to dft_A and pad dft_A with zeros
+ cvGetSubRect( dft_A, &tmp, cvRect(0,0, gray->width, gray->height));
+ cvCopy( input_co, &tmp, NULL );
+ if( dft_A->cols > gray->width )
+ {
+ cvGetSubRect( dft_A, &tmp, cvRect(gray->width,0, dft_A->cols - gray->width, gray->height));
+ cvZero( &tmp );
+ }
+
+ // no need to pad bottom part of dft_A with zeros because of
+ // use nonzero_rows parameter in cvDFT() call below
+ cvDFT( dft_A, dft_A, CV_DXT_FORWARD, input_co->height );
+
+ // Split Fourier in real and imaginary parts
+ cvSplit( dft_A, image_re, image_im, 0, 0 );
+
+ // Compute the magnitude of the spectrum Mag = sqrt(Re^2 + Im^2)
+ cvPow( image_re, image_re, 2.0);
+ cvPow( image_im, image_im, 2.0);
+ cvAdd( image_re, image_im, image_re, NULL);
+ cvPow( image_re, image_re, 0.5 );
+
+ // Compute log(1 + Mag)
+ cvAddS( image_re, cvScalarAll(1.0), image_re, NULL ); // 1 + Mag
+ cvLog( image_re, image_re ); // log(1 + Mag)
+
+ // Rearrange the quadrants of Fourier image so that the origin is at
+ // the image center
+ this->shiftDFT( image_re, image_re );
+
+ // normalize image
+ cvMinMaxLoc(image_re, &m, &M, NULL, NULL, NULL);
+ cvScale(image_re, image_re, 255.0/(M-m), 255.0*(-m)/(M-m));
+
+ for( py=0; py<image_re->height; py++ ) {
+ double* ptri = (double*) ( image_re->imageData + py * image_re->widthStep);
+ unsigned char* ptrp = (unsigned char*) ( image_mout->imageData + py * image_mout->widthStep);
+ for( px=0; px<image_re->width; px++ ) {
+ if ( *(ptrp+px) > 255.0 ) post( "pix_opencv_dft : error value over 255" );
+ (*(ptrp+px)) = (unsigned char)( (*(ptri+px)) );
+ }
+ }
+
+ x_calculate=0;
+ }
+
+ memcpy( image.data, image_mout->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_dft :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_dft::calculateCallback,
+ gensym("bang"), A_NULL);
+}
+
+void pix_opencv_dft :: calculateCallback(void *data)
+{
+ GetMyClass(data)->x_calculate = 1.0;
+}
diff --git a/src/pix_opencv_dft.h b/src/pix_opencv_dft.h
new file mode 100644
index 0000000..7c96f6d
--- /dev/null
+++ b/src/pix_opencv_dft.h
@@ -0,0 +1,88 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Discrete Fourier Transform
+
+ 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_DFT_H_
+#define INCLUDE_PIX_OPENCV_DFT_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_dft
+
+ Discrete Fourier Transform
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_dft : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_dft, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_dft();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_dft();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ int comp_xsize;
+ int comp_ysize;
+ int x_calculate;
+ int dft_M;
+ int dft_N;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void calculateCallback(void *data);
+ static void shiftDFT(CvArr*, CvArr*);
+
+ // The output and temporary images
+ IplImage *rgb;
+ IplImage *rgba;
+ IplImage *gray;
+ IplImage *input_re;
+ IplImage *input_im;
+ IplImage *input_co;
+ CvMat *dft_A;
+ IplImage *image_re;
+ IplImage *image_im;
+ IplImage *image_mout;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_distrans.cc b/src/pix_opencv_distrans.cc
new file mode 100644
index 0000000..a87485d
--- /dev/null
+++ b/src/pix_opencv_distrans.cc
@@ -0,0 +1,420 @@
+////////////////////////////////////////////////////////
+//
+// 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_distrans.h"
+
+CPPEXTERN_NEW(pix_opencv_distrans)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_distrans
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_distrans :: pix_opencv_distrans()
+{
+ int i;
+
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("ft1"));
+
+ edge_thresh = 25;
+ build_voronoi = 0;
+ comp_xsize = 0;
+ comp_ysize = 0;
+
+ dist = NULL;
+ dist8u1 = NULL;
+ dist8u2 = NULL;
+ dist8u = NULL;
+ dist32s = NULL;
+ rgb = NULL;
+ gray = NULL;
+ edge = NULL;
+ labels = NULL;
+ rgba = NULL;
+ alpha = NULL;
+
+ mask_size = CV_DIST_MASK_PRECISE;
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_distrans :: ~pix_opencv_distrans()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &edge );
+ cvReleaseImage( &dist );
+ cvReleaseImage( &dist8u );
+ cvReleaseImage( &dist8u1 );
+ cvReleaseImage( &dist8u2 );
+ cvReleaseImage( &dist32s );
+ cvReleaseImage( &labels );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &alpha );
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_distrans :: processRGBAImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+ static const uchar colors[][3] =
+ {
+ {0,0,0},
+ {255,0,0},
+ {255,128,0},
+ {255,255,0},
+ {0,255,0},
+ {0,128,255},
+ {0,255,255},
+ {0,0,255},
+ {255,0,255}
+ };
+ int msize = mask_size;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &edge );
+ cvReleaseImage( &dist );
+ cvReleaseImage( &dist8u );
+ cvReleaseImage( &dist8u1 );
+ cvReleaseImage( &dist8u2 );
+ cvReleaseImage( &dist32s );
+ cvReleaseImage( &labels );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &alpha );
+
+ //Create cv_images
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+ dist = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32F, 1 );
+ dist8u1 = cvCloneImage( gray );
+ dist8u2 = cvCloneImage( gray );
+ dist8u = cvCreateImage( cvGetSize(gray), IPL_DEPTH_8U, 3 );
+ dist32s = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32S, 1 );
+ edge = cvCloneImage( gray );
+ labels = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32S, 1 );
+ rgba = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 4 );
+ alpha = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 1 );
+ }
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+
+ cvCvtColor(rgba, rgb, CV_RGBA2RGB);
+ cvCvtColor(rgb, gray, CV_RGB2GRAY);
+
+ cvThreshold( gray, edge, (float)edge_thresh, (float)edge_thresh, CV_THRESH_BINARY );
+
+ if( build_voronoi )
+ msize = CV_DIST_MASK_5;
+
+ cvDistTransform( edge, dist, CV_DIST_L2, msize, NULL, build_voronoi ? labels : NULL );
+
+ if( !build_voronoi )
+ {
+ // begin "painting" the distance transform result
+ cvConvertScale( dist, dist, 5000.0, 0 );
+ cvPow( dist, dist, 0.5 );
+
+ cvConvertScale( dist, dist32s, 1.0, 0.5 );
+ cvAndS( dist32s, cvScalarAll(255), dist32s, 0 );
+ cvConvertScale( dist32s, dist8u1, 1, 0 );
+ cvConvertScale( dist32s, dist32s, -1, 0 );
+ cvAddS( dist32s, cvScalarAll(255), dist32s, 0 );
+ cvConvertScale( dist32s, dist8u2, 1, 0 );
+ cvMerge( dist8u1, dist8u2, dist8u2, 0, dist8u );
+ // end "painting" the distance transform result
+ }
+ else
+ {
+ int i, j;
+ for( i = 0; i < labels->height; i++ )
+ {
+ int* ll = (int*)(labels->imageData + i*labels->widthStep);
+ float* dd = (float*)(dist->imageData + i*dist->widthStep);
+ uchar* d = (uchar*)(dist8u->imageData + i*dist8u->widthStep);
+ for( j = 0; j < labels->width; j++ )
+ {
+ int idx = ll[j] == 0 || dd[j] == 0 ? 0 : (ll[j]-1)%8 + 1;
+ int b = cvRound(colors[idx][0]);
+ int g = cvRound(colors[idx][1]);
+ int r = cvRound(colors[idx][2]);
+ d[j*3] = (uchar)b;
+ d[j*3+1] = (uchar)g;
+ d[j*3+2] = (uchar)r;
+ }
+ }
+ }
+
+
+ cvCvtColor(dist8u, rgba, CV_RGB2RGBA);
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_distrans :: processRGBImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+ static const uchar colors[][3] =
+ {
+ {0,0,0},
+ {255,0,0},
+ {255,128,0},
+ {255,255,0},
+ {0,255,0},
+ {0,128,255},
+ {0,255,255},
+ {0,0,255},
+ {255,0,255}
+ };
+ int msize = mask_size;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &edge );
+ cvReleaseImage( &dist );
+ cvReleaseImage( &dist8u );
+ cvReleaseImage( &dist8u1 );
+ cvReleaseImage( &dist8u2 );
+ cvReleaseImage( &dist32s );
+ cvReleaseImage( &labels );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &alpha );
+
+ //Create cv_images
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+ dist = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32F, 1 );
+ dist8u1 = cvCloneImage( gray );
+ dist8u2 = cvCloneImage( gray );
+ dist8u = cvCreateImage( cvGetSize(gray), IPL_DEPTH_8U, 3 );
+ dist32s = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32S, 1 );
+ edge = cvCloneImage( gray );
+ labels = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32S, 1 );
+ rgba = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 4 );
+ alpha = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 1 );
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ cvCvtColor(rgb, gray, CV_BGR2GRAY);
+
+ cvThreshold( gray, edge, (float)edge_thresh, (float)edge_thresh, CV_THRESH_BINARY );
+
+ if( build_voronoi )
+ msize = CV_DIST_MASK_5;
+
+ cvDistTransform( edge, dist, CV_DIST_L2, msize, NULL, build_voronoi ? labels : NULL );
+
+ if( !build_voronoi )
+ {
+ // begin "painting" the distance transform result
+ cvConvertScale( dist, dist, 5000.0, 0 );
+ cvPow( dist, dist, 0.5 );
+
+ cvConvertScale( dist, dist32s, 1.0, 0.5 );
+ cvAndS( dist32s, cvScalarAll(255), dist32s, 0 );
+ cvConvertScale( dist32s, dist8u1, 1, 0 );
+ cvConvertScale( dist32s, dist32s, -1, 0 );
+ cvAddS( dist32s, cvScalarAll(255), dist32s, 0 );
+ cvConvertScale( dist32s, dist8u2, 1, 0 );
+ cvMerge( dist8u1, dist8u2, dist8u2, 0, dist8u );
+ // end "painting" the distance transform result
+ }
+ else
+ {
+ int i, j;
+ for( i = 0; i < labels->height; i++ )
+ {
+ int* ll = (int*)(labels->imageData + i*labels->widthStep);
+ float* dd = (float*)(dist->imageData + i*dist->widthStep);
+ uchar* d = (uchar*)(dist8u->imageData + i*dist8u->widthStep);
+ for( j = 0; j < labels->width; j++ )
+ {
+ int idx = ll[j] == 0 || dd[j] == 0 ? 0 : (ll[j]-1)%8 + 1;
+ int b = cvRound(colors[idx][0]);
+ int g = cvRound(colors[idx][1]);
+ int r = cvRound(colors[idx][2]);
+ d[j*3] = (uchar)b;
+ d[j*3+1] = (uchar)g;
+ d[j*3+2] = (uchar)r;
+ }
+ }
+ }
+
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, dist8u->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_distrans :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_distrans : yuv format not supported" );
+}
+
+void pix_opencv_distrans :: processGrayImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+ static const uchar colors[][3] =
+ {
+ {0,0,0},
+ {255,0,0},
+ {255,128,0},
+ {255,255,0},
+ {0,255,0},
+ {0,128,255},
+ {0,255,255},
+ {0,0,255},
+ {255,0,255}
+ };
+ int msize = mask_size;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &edge );
+ cvReleaseImage( &dist );
+ cvReleaseImage( &dist8u );
+ cvReleaseImage( &dist8u1 );
+ cvReleaseImage( &dist8u2 );
+ cvReleaseImage( &dist32s );
+ cvReleaseImage( &labels );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &alpha );
+
+ //Create cv_images
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+ dist = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32F, 1 );
+ dist8u1 = cvCloneImage( gray );
+ dist8u2 = cvCloneImage( gray );
+ dist8u = cvCreateImage( cvGetSize(gray), IPL_DEPTH_8U, 3 );
+ dist32s = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32S, 1 );
+ edge = cvCloneImage( gray );
+ labels = cvCreateImage( cvGetSize(gray), IPL_DEPTH_32S, 1 );
+ rgba = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 4 );
+ alpha = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 1 );
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+
+
+ cvThreshold( gray, edge, (float)edge_thresh, (float)edge_thresh, CV_THRESH_BINARY );
+
+ if( build_voronoi )
+ msize = CV_DIST_MASK_5;
+
+ cvDistTransform( edge, dist, CV_DIST_L2, msize, NULL, build_voronoi ? labels : NULL );
+
+ if( !build_voronoi )
+ {
+ // begin "painting" the distance transform result
+ cvConvertScale( dist, dist, 5000.0, 0 );
+ cvPow( dist, dist, 0.5 );
+
+ cvConvertScale( dist, dist32s, 1.0, 0.5 );
+ cvAndS( dist32s, cvScalarAll(255), dist32s, 0 );
+ cvConvertScale( dist32s, dist8u1, 1, 0 );
+ cvConvertScale( dist32s, dist32s, -1, 0 );
+ cvAddS( dist32s, cvScalarAll(255), dist32s, 0 );
+ cvConvertScale( dist32s, dist8u2, 1, 0 );
+ cvMerge( dist8u1, dist8u2, dist8u2, 0, dist8u );
+ // end "painting" the distance transform result
+ }
+ else
+ {
+ int i, j;
+ for( i = 0; i < labels->height; i++ )
+ {
+ int* ll = (int*)(labels->imageData + i*labels->widthStep);
+ float* dd = (float*)(dist->imageData + i*dist->widthStep);
+ uchar* d = (uchar*)(dist8u->imageData + i*dist8u->widthStep);
+ for( j = 0; j < labels->width; j++ )
+ {
+ int idx = ll[j] == 0 || dd[j] == 0 ? 0 : (ll[j]-1)%8 + 1;
+ int b = cvRound(colors[idx][0]);
+ int g = cvRound(colors[idx][1]);
+ int r = cvRound(colors[idx][2]);
+ d[j*3] = (uchar)b;
+ d[j*3+1] = (uchar)g;
+ d[j*3+2] = (uchar)r;
+ }
+ }
+ }
+
+
+ cvCvtColor(dist8u, gray, CV_RGB2GRAY);
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, gray->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_distrans :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_distrans::thresholdMessCallback,
+ gensym("ft1"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_distrans::voronoiMessCallback,
+ gensym("voronoi"), A_DEFFLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_distrans::maskMessCallback,
+ gensym("mask"), A_DEFFLOAT, A_NULL);
+}
+void pix_opencv_distrans :: thresholdMessCallback(void *data, t_floatarg pos)
+{
+ if (pos>=0) GetMyClass(data)->edge_thresh = (int)pos;
+}
+void pix_opencv_distrans :: voronoiMessCallback(void *data, t_floatarg voronoi)
+{
+ GetMyClass(data)->build_voronoi=!(!(int)voronoi);
+}
+void pix_opencv_distrans :: maskMessCallback(void *data, t_floatarg f)
+{
+ if( (int)f == 3 )
+ GetMyClass(data)->mask_size = CV_DIST_MASK_3;
+ else if( (int)f == 5 )
+ GetMyClass(data)->mask_size = CV_DIST_MASK_5;
+ else if( (int)f == 0 )
+ GetMyClass(data)->mask_size = CV_DIST_MASK_PRECISE;
+}
diff --git a/src/pix_opencv_distrans.h b/src/pix_opencv_distrans.h
new file mode 100644
index 0000000..3f77466
--- /dev/null
+++ b/src/pix_opencv_distrans.h
@@ -0,0 +1,86 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Distrans 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_DISTRANS_H_
+#define INCLUDE_PIX_OPENCV_DISTRANS_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_distrans
+
+ Distrans algorithm
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_distrans : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_distrans, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_distrans();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_distrans();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ // Some varibales to control mophology voronoi
+ int edge_thresh;
+ int build_voronoi;
+ int mask_size;
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+
+ private:
+
+ //////////
+ // Static member functions
+ static void thresholdMessCallback(void *data, t_floatarg pos);
+ static void voronoiMessCallback(void *data, t_floatarg voronoi);
+ static void maskMessCallback(void *data, t_floatarg f);
+
+ // The output and temporary images
+ IplImage *dist, *dist8u1, *dist8u2, *dist8u, *dist32s;
+ IplImage *rgb, *gray, *edge, *labels;
+ IplImage *rgba, *alpha;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_edge.cc b/src/pix_opencv_edge.cc
new file mode 100644
index 0000000..270f233
--- /dev/null
+++ b/src/pix_opencv_edge.cc
@@ -0,0 +1,234 @@
+////////////////////////////////////////////////////////
+//
+// 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_edge.h"
+
+CPPEXTERN_NEW(pix_opencv_edge)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_edge
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_edge :: pix_opencv_edge()
+{
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("ft1"));
+ edge_thresh = 50;
+ comp_xsize = 0;
+ comp_ysize = 0;
+ orig = NULL;
+ gray = NULL;
+ edge = NULL;
+ cedge = NULL;
+ cedgergb = NULL;
+ rgb = NULL;
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_edge :: ~pix_opencv_edge()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&edge);
+ cvReleaseImage(&cedge);
+ cvReleaseImage(&cedgergb);
+ cvReleaseImage(&rgb);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_edge :: processRGBAImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!orig)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&edge);
+ cvReleaseImage(&cedge);
+ cvReleaseImage(&cedgergb);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+
+ // Create the output images with new sizes
+ cedge = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 4);
+
+ gray = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ edge = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+
+ }
+ // Here we make a copy of the pixel data from image to orig->imageData
+ // orig is a IplImage struct, the default image type in openCV, take a look on the IplImage data structure here
+ // http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
+ memcpy( orig->imageData, image.data, image.xsize*image.ysize*4 );
+
+ // Convert to grayscale
+ cvCvtColor(orig, gray, CV_BGRA2GRAY);
+
+ cvSmooth( gray, edge, CV_BLUR, 3, 3, 0, 0 );
+ cvNot( gray, edge );
+
+ // Run the edge detector on grayscale
+ cvCanny(gray, edge, (float)this->edge_thresh, (float)this->edge_thresh*3, 3);
+
+ cvZero( cedge );
+ // copy edge points
+ cvCopy( orig, cedge, edge );
+
+ //copy back the processed frame to image
+ memcpy( image.data, cedge->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_edge :: processRGBImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&edge);
+ cvReleaseImage(&cedge);
+ cvReleaseImage(&cedgergb);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+
+ // Create the output images with new sizes
+ cedgergb = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 3);
+
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+ edge = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ // Convert to grayscale
+ cvCvtColor(rgb, gray, CV_RGB2GRAY);
+
+ cvSmooth( gray, edge, CV_BLUR, 3, 3, 0, 0 );
+ cvNot( gray, edge );
+
+ // Run the edge detector on grayscale
+ cvCanny(gray, edge, (float)this->edge_thresh, (float)this->edge_thresh*3, 3);
+
+ cvZero( cedgergb );
+ // copy edge points
+ cvCopy( rgb, cedgergb, edge );
+
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, cedgergb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_edge :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_edge : yuv format not supported" );
+}
+
+void pix_opencv_edge :: processGrayImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&edge);
+ cvReleaseImage(&cedge);
+ cvReleaseImage(&cedgergb);
+ cvReleaseImage(&rgb);
+
+ //create the orig image with new size
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+
+ // Create the output images with new sizes
+ cedgergb = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 3);
+
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+ edge = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+
+ // Convert to RGB
+ cvCvtColor( gray, rgb, CV_GRAY2RGB);
+
+ cvSmooth( gray, edge, CV_BLUR, 3, 3, 0, 0 );
+ cvNot( gray, edge );
+
+ // Run the edge detector on grayscale
+ cvCanny(gray, edge, (float)this->edge_thresh, (float)this->edge_thresh*3, 3);
+
+ cvZero( cedgergb );
+ // copy edge points
+ cvCopy( rgb, cedgergb, edge );
+
+ cvCvtColor( cedgergb, gray, CV_RGB2GRAY);
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, gray->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// floatThreshMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_edge :: floatThreshMess (float edge_thresh)
+{
+ this->edge_thresh = (int)edge_thresh;
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_edge :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_edge::floatTreshMessCallback,
+ gensym("ft1"), A_FLOAT, A_NULL);
+}
+void pix_opencv_edge :: floatTreshMessCallback(void *data, t_floatarg edge_thresh)
+{
+ GetMyClass(data)->floatThreshMess((float)edge_thresh);
+}
diff --git a/src/pix_opencv_edge.h b/src/pix_opencv_edge.h
new file mode 100644
index 0000000..283811d
--- /dev/null
+++ b/src/pix_opencv_edge.h
@@ -0,0 +1,82 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Edge detection
+
+ 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_EDGE_H_
+#define INCLUDE_PIX_OPENCV_EDGE_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_edge
+
+ Edge detection
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_edge : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_edge, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_edge();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_edge();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void floatThreshMess(float edge_thresh);
+ // The new edge threshold
+ int edge_thresh;
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatTreshMessCallback(void *data, t_floatarg edge_thresh);
+
+ /////////
+ // IplImage needed
+ IplImage *rgb, *orig, *cedge, *cedgergb, *gray, *edge;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_facetracker.cc b/src/pix_opencv_facetracker.cc
new file mode 100644
index 0000000..e5021d0
--- /dev/null
+++ b/src/pix_opencv_facetracker.cc
@@ -0,0 +1,302 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org ) (pix_opencv)
+// FaceTracker for pix_opencv
+
+#ifdef HAVE_FACETRACKER
+#include "pix_opencv_facetracker.h"
+
+using namespace FACETRACKER;
+
+CPPEXTERN_NEW(pix_opencv_facetracker)
+
+static std::vector<int> consecutive(int start, int end) {
+ int n = end - start;
+ std::vector<int> result(n);
+ for(int i = 0; i < n; i++) {
+ result[i] = start + i;
+ }
+ return result;
+}
+
+std::vector<int> pix_opencv_facetracker::getFeatureIndices(int feature) {
+ switch(feature) {
+ case LEFT_JAW: return consecutive(0, 9);
+ case RIGHT_JAW: return consecutive(8, 17);
+ case JAW: return consecutive(0, 17);
+ case LEFT_EYEBROW: return consecutive(17, 22);
+ case RIGHT_EYEBROW: return consecutive(22, 27);
+ case LEFT_EYE: return consecutive(36, 42);
+ case RIGHT_EYE: return consecutive(42, 48);
+ case OUTER_MOUTH: return consecutive(48, 60);
+ case INNER_MOUTH: {
+ static int innerMouth[] = {48,60,61,62,54,63,64,65};
+ return std::vector<int>(innerMouth, innerMouth + 8);
+ }
+ case NOSE_BRIDGE: return consecutive(27, 31);
+ case NOSE_BASE: return consecutive(31, 36);
+ case FACE_OUTLINE: {
+ static int faceOutline[] = {17,18,19,20,21,22,23,24,25,26, 16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0};
+ return std::vector<int>(faceOutline, faceOutline + 27);
+ }
+ case ALL_FEATURES: return consecutive(0, 66);
+ }
+}
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_facetracker
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_facetracker :: pix_opencv_facetracker() : m_fcheck(false), \
+ m_scale(1), \
+ m_fpd(-1), \
+ m_show(true), \
+ m_nIter(5), \
+ m_clamp(3), \
+ m_fTol(0.01), \
+ m_failed(true), \
+ m_fps(-1), \
+ m_t1(0), \
+ m_t0(0), \
+ m_taboutput(0), \
+ m_autoresize(0)
+{
+ m_dataout = outlet_new(this->x_obj, 0);
+
+ //set other tracking parameters
+ m_wSize1.push_back(7);
+ m_wSize2.push_back(11); m_wSize2.push_back(9); m_wSize2.push_back(7);
+
+ for ( int i = 0; i<13 ; i++) m_arraysname[i]=NULL;
+
+ t_canvas* canvas=canvas_getcurrent();
+ char *basename=canvas_getdir(canvas)->s_name;
+
+ m_tracker.Load((std::string(basename) + "/model/face2.tracker").c_str());
+ m_tri = IO::LoadTri((std::string(basename) + "/model/face.tri").c_str());
+ m_con = IO::LoadCon((std::string(basename) + "/model/face.con").c_str());
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_facetracker :: ~pix_opencv_facetracker()
+{
+
+}
+
+void pix_opencv_facetracker :: OutputMesh(cv::Mat &image,cv::Mat &shape,cv::Mat &con,cv::Mat &tri,cv::Mat &visi)
+{
+ int n = shape.rows/2;
+ int vecsize(0);
+ t_garray *array;
+ t_word *vec;
+
+ for ( int i = 0; i<13 ; i++ ){
+
+ std::vector<int> indices = getFeatureIndices(i);
+ cv::Point pt;
+
+ if ( m_arraysname[i] == NULL ){
+ continue;
+ }
+ if (!(array = (t_garray *)pd_findbyclass(m_arraysname[i], garray_class))){
+ error("%s: no such array", m_arraysname[i]->s_name);
+ continue;
+ }
+
+ if (!garray_getfloatwords(array, &vecsize, &vec)){
+ error("%s: bad template for tabwrite", m_arraysname[i]->s_name);
+ continue;
+ }
+ //~if ( indices.size()*3 != vecsize ){
+ //~garray_resize_long(array,indices.size()*3);
+ //~if (!garray_getfloatwords(array, &vecsize, &vec)){
+ //~error("%s: can't resize correctly", m_arraysname[i]->s_name);
+ //~continue;
+ //~}
+ //~}
+
+ int idx(0);
+
+ for( size_t j = 0 ; j < indices.size(); j++ )
+ {
+ idx=3*j;
+ vec[idx].w_float = (float) shape.at<double>(indices[j],0)/image.cols;
+ vec[idx+1].w_float = (float) shape.at<double>(indices[j]+n,0)/image.rows;
+ vec[idx+2].w_float = 0.;
+ //~printf("update %s, indice %d : %.2f,%.2f,%.2f\n",m_arraysname[i]->s_name, idx, vec[idx].w_float, vec[idx+1].w_float, vec[idx+2].w_float);
+ }
+
+ garray_redraw(array);
+ }
+}
+
+void pix_opencv_facetracker :: Draw(cv::Mat &image,cv::Mat &shape,cv::Mat &con,cv::Mat &tri,cv::Mat &visi)
+{
+ int i,n = shape.rows/2; cv::Point p1,p2; cv::Scalar c;
+
+ //draw triangulation
+ c = CV_RGB(0,0,0);
+ for(i = 0; i < tri.rows; i++){
+ if(visi.at<int>(tri.at<int>(i,0),0) == 0 ||
+ visi.at<int>(tri.at<int>(i,1),0) == 0 ||
+ visi.at<int>(tri.at<int>(i,2),0) == 0)continue;
+ p1 = cv::Point(shape.at<double>(tri.at<int>(i,0),0),
+ shape.at<double>(tri.at<int>(i,0)+n,0));
+ p2 = cv::Point(shape.at<double>(tri.at<int>(i,1),0),
+ shape.at<double>(tri.at<int>(i,1)+n,0));
+ cv::line(image,p1,p2,c);
+ p1 = cv::Point(shape.at<double>(tri.at<int>(i,0),0),
+ shape.at<double>(tri.at<int>(i,0)+n,0));
+ p2 = cv::Point(shape.at<double>(tri.at<int>(i,2),0),
+ shape.at<double>(tri.at<int>(i,2)+n,0));
+ cv::line(image,p1,p2,c);
+ p1 = cv::Point(shape.at<double>(tri.at<int>(i,2),0),
+ shape.at<double>(tri.at<int>(i,2)+n,0));
+ p2 = cv::Point(shape.at<double>(tri.at<int>(i,1),0),
+ shape.at<double>(tri.at<int>(i,1)+n,0));
+ cv::line(image,p1,p2,c);
+ }
+ //draw connections
+ c = CV_RGB(0,0,255);
+ for(i = 0; i < con.cols; i++){
+ if(visi.at<int>(con.at<int>(0,i),0) == 0 ||
+ visi.at<int>(con.at<int>(1,i),0) == 0)continue;
+ p1 = cv::Point(shape.at<double>(con.at<int>(0,i),0),
+ shape.at<double>(con.at<int>(0,i)+n,0));
+ p2 = cv::Point(shape.at<double>(con.at<int>(1,i),0),
+ shape.at<double>(con.at<int>(1,i)+n,0));
+ cv::line(image,p1,p2,c,1);
+ }
+ //draw points
+ for(i = 0; i < n; i++){
+ if(visi.at<int>(i,0) == 0)continue;
+ p1 = cv::Point(shape.at<double>(i,0),shape.at<double>(i+n,0));
+ c = CV_RGB(255,0,0); cv::circle(image,p1,2,c);
+ }return;
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_facetracker :: processImage(imageStruct &image)
+{
+ cv::Mat gray, im;
+ if ( image.ysize < 1 || image.xsize < 1 ) return;
+ if ( image.csize == 1 ){
+ gray = cv::Mat( image.ysize, image.xsize, CV_8UC1, image.data, image.csize*image.xsize); // just transform imageStruct to IplImage without copying data
+ im = gray;
+ } else if ( image.csize == 3 ) {
+ im = cv::Mat( image.ysize, image.xsize, CV_8UC3, image.data, image.csize*image.xsize); // just transform imageStruct to IplImage without copying data
+ cv::cvtColor(im,gray,CV_RGB2GRAY);
+ } else if ( image.csize == 4 ) {
+ im = cv::Mat( image.ysize, image.xsize, CV_8UC4, image.data, image.csize*image.xsize); // just transform imageStruct to IplImage without copying data
+ cv::cvtColor(im,gray,CV_RGBA2GRAY);
+ } else {
+ error("pix_opencv_facetracker : unsupported video format %d",image.csize);
+ return;
+ }
+
+ //track this image
+ std::vector<int> wSize;
+ if(m_failed)wSize = m_wSize2;
+ else wSize = m_wSize1;
+
+ if(m_tracker.Track(gray,wSize,m_fpd,m_nIter,m_clamp,m_fTol,m_fcheck) == 0){
+ int idx = m_tracker._clm.GetViewIdx(); m_failed = false;
+ OutputMesh(im,m_tracker._shape,m_con,m_tri,m_tracker._clm._visi[idx]);
+ if (m_show) Draw(im,m_tracker._shape,m_con,m_tri,m_tracker._clm._visi[idx]);
+ }else{
+ m_tracker.FrameReset(); m_failed = true;
+ }
+ //draw framerate on display image
+ if(m_fnum >= 9){
+ m_t1 = cvGetTickCount();
+ m_fps = 10.0/((double(m_t1-m_t0)/cvGetTickFrequency())/1e+6);
+ m_t0 = m_t1; m_fnum = 0;
+ }else m_fnum += 1;
+ if(m_show){
+ char text[256];
+ sprintf(text,"%.2f frames/sec",(float)m_fps);
+ cv::putText(im,text,cv::Point(10,20), CV_FONT_HERSHEY_SIMPLEX,0.5,CV_RGB(255,255,255));
+ }
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_facetracker :: obj_setupCallback(t_class *classPtr)
+{
+ CPPEXTERN_MSG0(classPtr, "reset", resetMess);
+ CPPEXTERN_MSG1(classPtr, "show", showMess, int);
+ CPPEXTERN_MSG2(classPtr, "settab", tableMess, t_symbol*, t_symbol*);
+ CPPEXTERN_MSG1(classPtr, "tolerance", toleranceMess, float);
+ CPPEXTERN_MSG1(classPtr, "clamp", clampMess, float);
+ CPPEXTERN_MSG1(classPtr, "filter", filterMess, float);
+}
+
+void pix_opencv_facetracker :: resetMess(void){
+ m_tracker.FrameReset();
+}
+
+void pix_opencv_facetracker :: showMess(int flag){
+ m_show = flag>0;
+}
+
+void pix_opencv_facetracker :: tableMess(t_symbol*featurename, t_symbol*arrayname)
+{
+ std::string name(featurename->s_name);
+
+ int i=-1;
+ if ( name == "LEFT_EYEBROW" ) i = 0;
+ else if ( name == "RIGHT_EYEBROW" ) i = 1;
+ else if ( name == "LEFT_EYE" ) i = 2;
+ else if ( name == "RIGHT_EYE" ) i = 3;
+ else if ( name == "LEFT_JAW" ) i = 4;
+ else if ( name == "RIGHT_JAW" ) i = 5;
+ else if ( name == "JAW" ) i = 6;
+ else if ( name == "OUTER_MOUTH" ) i = 7;
+ else if ( name == "INNER_MOUTH" ) i = 8;
+ else if ( name == "NOSE_BRIDGE" ) i = 9;
+ else if ( name == "NOSE_BASE" ) i = 10;
+ else if ( name == "FACE_OUTLINE" ) i = 11;
+ else if ( name == "ALL_FEATURES" ) i = 12;
+ if ( i < 0 ) error("can't find feature %s",name.c_str());
+ else { m_arraysname[i] = arrayname; m_taboutput = 1; }
+}
+
+void pix_opencv_facetracker :: toleranceMess(float arg){
+ m_fTol=arg;
+}
+
+void pix_opencv_facetracker :: clampMess(float arg){
+ m_clamp=arg;
+}
+
+void pix_opencv_facetracker :: filterMess(float arg){
+ m_nIter=int(arg);
+}
+#endif /* HAVE_FACETRACKER */
diff --git a/src/pix_opencv_facetracker.h b/src/pix_opencv_facetracker.h
new file mode 100644
index 0000000..407e468
--- /dev/null
+++ b/src/pix_opencv_facetracker.h
@@ -0,0 +1,107 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Threshold filter
+
+ 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_FACETRACKER_H_
+#define INCLUDE_PIX_OPENCV_FACETRACKER_H_
+
+#ifndef _EiC
+#include "opencv2/opencv.hpp"
+#endif
+
+#include "FaceTracker/Tracker.h"
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_facetracker
+
+ FaceTracker implementation
+ * see https://github.com/kylemcdonald/FaceTracker
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_facetracker : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_facetracker, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_facetracker();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_facetracker();
+
+ //////////
+ // Do the processing
+ virtual void processImage(imageStruct &image);
+
+ void resetMess(void);
+ void showMess(int);
+ void tableMess(t_symbol*xarray, t_symbol*yarray);
+ void clampMess(float arg);
+ void toleranceMess(float arg);
+ void filterMess(float arg);
+
+ private:
+
+ enum Feature {
+ LEFT_EYEBROW, RIGHT_EYEBROW,
+ LEFT_EYE, RIGHT_EYE,
+ LEFT_JAW, RIGHT_JAW, JAW,
+ OUTER_MOUTH, INNER_MOUTH,
+ NOSE_BRIDGE, NOSE_BASE,
+ FACE_OUTLINE, ALL_FEATURES
+ };
+
+ void Draw(cv::Mat &image,cv::Mat &shape,cv::Mat &con,cv::Mat &tri,cv::Mat &visi);
+ void OutputMesh(cv::Mat &image,cv::Mat &shape,cv::Mat &con,cv::Mat &tri,cv::Mat &visi);
+ std::vector<int> getFeatureIndices(int feature);
+
+ bool m_fcheck;
+ double m_scale;
+ int m_fpd;
+ bool m_show;
+ int m_nIter;
+ double m_clamp, m_fTol;
+ bool m_failed;
+ int m_fps;
+ int64 m_t1,m_t0;
+ int m_fnum;
+ bool m_taboutput; // enable table output
+ bool m_autoresize; // flag to resize table according to mesh size
+
+ //set other tracking parameters
+ std::vector<int> m_wSize1;
+ std::vector<int> m_wSize2;
+
+ t_outlet *m_dataout; // info outlet
+ FACETRACKER::Tracker m_tracker;
+ cv::Mat m_tri, m_con;
+
+ t_symbol* m_arraysname[13];
+
+};
+#endif // for header file
diff --git a/src/pix_opencv_findchessboardcorners.cc b/src/pix_opencv_findchessboardcorners.cc
new file mode 100644
index 0000000..42fcd61
--- /dev/null
+++ b/src/pix_opencv_findchessboardcorners.cc
@@ -0,0 +1,220 @@
+////////////////////////////////////////////////////////
+//
+// 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_findchessboardcorners.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_findchessboardcorners)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_findchessboardcorners
+// by Antoine Villeret
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_findchessboardcorners :: pix_opencv_findchessboardcorners()
+{
+ //inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("minarea"));
+ //inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("maxarea"));
+ m_dataout = outlet_new(this->x_obj, 0);
+ //m_countout = outlet_new(this->x_obj, 0);
+ comp_xsize = 320;
+ comp_ysize = 240;
+ gray = NULL;
+ tmp_color = NULL;
+ tmp_gray = NULL;
+ rgb = NULL;
+
+ pattern_size = cvSize(6,7);
+ corners = new CvPoint2D32f[pattern_size.width * pattern_size.height];
+ cornerCount = 0;
+ win = cvSize(11, 11);
+ zero_zone = cvSize(-1,-1);
+ flags = CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FILTER_QUADS;
+ criteria = cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 30, 0.1);
+ coord_list = new t_atom[pattern_size.width * pattern_size.height * 2]; // all coordinates are packed in one list [x0 y0 x1 y1 .. xn yn(
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_findchessboardcorners :: ~pix_opencv_findchessboardcorners()
+{
+ //Destroy cv_images to clean memory
+ if (gray) cvReleaseImage(&gray);
+ if (rgb) cvReleaseImage(&rgb);
+ if (tmp_color) cvReleaseImage(&tmp_color);
+ if (tmp_gray) cvReleaseImage(&tmp_gray);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_findchessboardcorners :: processRGBAImage(imageStruct &image)
+{
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb))
+ {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if(gray) cvReleaseImage(&gray);
+ if(rgb) cvReleaseImage(&rgb);
+ if(tmp_color) cvReleaseImage(&tmp_color);
+
+ // Create images with new sizes
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ tmp_gray = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 1);
+ }
+
+ // no need to copy a lot of memory, just point to it...
+ rgb->imageData = (char*) image.data;
+ cvCvtColor( rgb, tmp_gray, CV_RGBA2GRAY);
+
+ //~ printf("find corner...*/\n");
+ int found = cvFindChessboardCorners( tmp_gray, pattern_size, corners, &cornerCount, flags);
+ //~ printf(" found : %d\n",found);
+
+
+ if ( found ) {
+ //~ printf("find corner sub pix...\n");
+ cvFindCornerSubPix( tmp_gray, corners, cornerCount, win, zero_zone, criteria);
+ }
+
+ // Draw corners on the image
+ //cvCvtColor( gray, tmp_color, CV_GRAY2RGBA);
+ cvDrawChessboardCorners( rgb, pattern_size, corners, cornerCount, found);
+ //cvCvtColor( tmp_color, gray, CV_RGBA2GRAY);
+
+ // send out corners screen coordinates if all corners have been found
+ if (found) {
+ int i;
+ CvPoint2D32f *pt;
+ pt = (CvPoint2D32f *) corners;
+ for ( i=0; i<pattern_size.width * pattern_size.height ; i++ )
+ {
+ SETFLOAT(&coord_list[i*2], pt->x);
+ SETFLOAT(&coord_list[i*2+1], pt->y);
+ pt++;
+ }
+ outlet_list( m_dataout, 0, pattern_size.width * pattern_size.height*2, coord_list );
+ }
+}
+
+void pix_opencv_findchessboardcorners :: processRGBImage(imageStruct &image)
+{
+ // TODO
+ error("cant't support RGB image for now");
+}
+
+void pix_opencv_findchessboardcorners :: processYUVImage(imageStruct &image)
+{
+ // TODO
+ error( "pix_opencv_findchessboardcorners : yuv format not supported" );
+}
+
+void pix_opencv_findchessboardcorners :: processGrayImage(imageStruct &image)
+{
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!gray))
+ {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if(gray) cvReleaseImage(&gray);
+ if(rgb) cvReleaseImage(&rgb);
+ if(tmp_color) cvReleaseImage(&tmp_color);
+ if(tmp_gray) cvReleaseImage(&tmp_gray);
+
+ // Create images with new sizes
+ gray = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 1);
+ tmp_color = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ }
+
+ // no need to copy a lot of memory, just point to it...
+ gray->imageData = (char*) image.data;
+ //~ printf("find corner...*/\n");
+ int found = cvFindChessboardCorners( gray, pattern_size, corners, &cornerCount, flags);
+ //~ printf(" found : %d\n",found);
+
+
+ if ( found ) {
+ //~ printf("find corner sub pix...\n");
+ cvFindCornerSubPix( gray, corners, cornerCount, win, zero_zone, criteria);
+ }
+
+ // Draw corners on the image
+ cvCvtColor( gray, tmp_color, CV_GRAY2RGBA);
+ cvDrawChessboardCorners( tmp_color, pattern_size, corners, cornerCount, found);
+ cvCvtColor( tmp_color, gray, CV_RGBA2GRAY);
+
+ // send out corners screen coordinates if all corners have been found
+ if (found) {
+ int i;
+ CvPoint2D32f *pt;
+ pt = (CvPoint2D32f *) corners;
+ for ( i=0; i<pattern_size.width * pattern_size.height ; i++ )
+ {
+ SETFLOAT(&coord_list[i*2], pt->x);
+ SETFLOAT(&coord_list[i*2+1], pt->y);
+ pt++;
+ }
+ outlet_list( m_dataout, 0, pattern_size.width * pattern_size.height*2, coord_list );
+ }
+}
+
+/////////////////////////////////////////////////////////
+// floatThreshMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_findchessboardcorners :: patternSizeMess (int xsize, int ysize)
+{
+ if ( xsize < 3 || ysize < 3 ) {
+ error("patternSize should be at least 3x3");
+ return;
+ }
+ pattern_size=cvSize(xsize,ysize);
+ // update corners array & output list size
+ delete coord_list;
+ coord_list = new t_atom[pattern_size.width * pattern_size.height * 2];
+ delete corners;
+ corners = new CvPoint2D32f[pattern_size.width * pattern_size.height];
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_findchessboardcorners :: obj_setupCallback(t_class *classPtr)
+{
+ // TODO add support for message : win, zero_zone, flags and criteria
+ class_addmethod(classPtr, (t_method)&pix_opencv_findchessboardcorners::patternSizeMessCallback,
+ gensym("patternSize"), A_FLOAT, A_FLOAT, A_NULL);
+}
+
+void pix_opencv_findchessboardcorners :: patternSizeMessCallback(void *data, t_floatarg xsize, t_floatarg ysize)
+{
+ GetMyClass(data)->patternSizeMess((int)xsize, (int)ysize);
+}
diff --git a/src/pix_opencv_findchessboardcorners.h b/src/pix_opencv_findchessboardcorners.h
new file mode 100644
index 0000000..a8bfcea
--- /dev/null
+++ b/src/pix_opencv_findchessboardcorners.h
@@ -0,0 +1,98 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Contours Bounding Rectangle detection
+
+ 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.
+
+-----------------------------------------------------------------*/
+// findchessboardcorners integration by Antoine Villeret - 2011
+
+#ifndef INCLUDE_PIX_OPENCV_FINDCHESSBOARDCORNERS_H_
+#define INCLUDE_PIX_OPENCV_FINDCHESSBOARDCORNERS_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+#define MAX_MARKERS 500
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_findchessboardcorners
+
+ Chessboard corners detection
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_findchessboardcorners : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_findchessboardcorners, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_findchessboardcorners();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_findchessboardcorners();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ /////////
+ // Setup
+ void patternSizeMess (int xsize, int ysize);
+
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+
+ CvSize pattern_size; // pattern size (inner corners count in 2D)
+ CvPoint2D32f *corners; // array to store corners coordinates
+ int cornerCount; // number of corners found
+ int flags; // flags for cvFindChessboardCorners
+ CvSize win; // half of the search window size for cvFindCornerSubPix
+ CvSize zero_zone; // for cvFindCornerSubPix
+ CvTermCriteria criteria; // for cvFindCornerSubPix
+ t_atom *coord_list;
+
+
+ private:
+
+ t_outlet *m_dataout;
+ //////////
+ // Static member functions
+ static void patternSizeMessCallback(void *data, t_floatarg xsize, t_floatarg ysize);
+
+
+ /////////
+ // IplImage needed
+ IplImage *rgb, *tmp_color, *gray, *tmp_gray;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_floodfill.cc b/src/pix_opencv_floodfill.cc
new file mode 100644
index 0000000..f41d3c0
--- /dev/null
+++ b/src/pix_opencv_floodfill.cc
@@ -0,0 +1,442 @@
+
+//
+// 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_floodfill.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_floodfill)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_floodfill
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_floodfill :: pix_opencv_floodfill()
+{
+ int i;
+
+ comp_xsize=320;
+ comp_ysize=240;
+
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("lo_diff"));
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("up_diff"));
+ m_dataout = outlet_new(this->x_obj, &s_anything);
+
+ x_lo = 20;
+ x_up = 20;
+ x_connectivity = 4;
+ x_color = 1;
+
+ for ( i=0; i<MAX_COMPONENTS; i++)
+ {
+ x_xcomp[i] = -1;
+ x_ycomp[i] = -1;
+ x_r[i] = rand() & 255;
+ x_g[i] = rand() & 255;
+ x_b[i] = rand() & 255;
+ }
+
+ 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 );
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_floodfill :: ~pix_opencv_floodfill()
+{
+ // Destroy cv_images
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_floodfill :: processRGBAImage(imageStruct &image)
+{
+ int i, k;
+ int im;
+ int marked;
+ CvConnectedComp comp;
+ int flags = x_connectivity + ( 255 << 8 ) + CV_FLOODFILL_FIXED_RANGE;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+
+ 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 );
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ cvCvtColor(rgba, grey, CV_BGRA2GRAY);
+ cvCvtColor(rgba, rgb, CV_BGRA2BGR);
+
+ // mark recognized components
+ for ( i=0; i<MAX_COMPONENTS; i++ )
+ {
+ if ( x_xcomp[i] != -1 )
+ {
+ if ( x_color )
+ {
+ CvPoint seed = cvPoint(x_xcomp[i],x_ycomp[i]);
+ CvScalar color = CV_RGB( x_r[i], x_g[i], x_b[i] );
+ cvFloodFill( rgb, seed, color, CV_RGB( x_lo, x_lo, x_lo ),
+ CV_RGB( x_up, x_up, x_up ), &comp, flags, NULL );
+ }
+ else
+ {
+ CvPoint seed = cvPoint(x_xcomp[i],x_ycomp[i]);
+ CvScalar brightness = cvRealScalar((x_r[i]*2 + x_g[i]*7 + x_b[i] + 5)/10);
+ cvFloodFill( grey, seed, brightness, cvRealScalar(x_lo),
+ cvRealScalar(x_up), &comp, flags, NULL );
+ }
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], comp.rect.x);
+ SETFLOAT(&x_list[2], comp.rect.y);
+ SETFLOAT(&x_list[3], comp.rect.width);
+ SETFLOAT(&x_list[4], comp.rect.height);
+ outlet_list( m_dataout, 0, 5, x_list );
+ }
+ }
+
+ if ( !x_color )
+ {
+ cvCvtColor(grey, rgba, CV_GRAY2BGRA);
+ }
+ else
+ {
+ cvCvtColor(rgb, rgba, CV_BGR2BGRA);
+ }
+
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_floodfill :: processRGBImage(imageStruct &image)
+{
+ int i, k;
+ int im;
+ int marked;
+ CvConnectedComp comp;
+ int flags = x_connectivity + ( 255 << 8 ) + CV_FLOODFILL_FIXED_RANGE;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+
+ 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 );
+ }
+
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ if ( !x_color )
+ {
+ cvCvtColor(rgb, grey, CV_BGR2GRAY);
+ }
+
+ // mark recognized components
+ for ( i=0; i<MAX_COMPONENTS; i++ )
+ {
+ if ( x_xcomp[i] != -1 )
+ {
+ if ( x_color )
+ {
+ CvPoint seed = cvPoint(x_xcomp[i],x_ycomp[i]);
+ CvScalar color = CV_RGB( x_r[i], x_g[i], x_b[i] );
+ cvFloodFill( rgb, seed, color, CV_RGB( x_lo, x_lo, x_lo ),
+ CV_RGB( x_up, x_up, x_up ), &comp, flags, NULL );
+ }
+ else
+ {
+ CvPoint seed = cvPoint(x_xcomp[i],x_ycomp[i]);
+ CvScalar brightness = cvRealScalar((x_r[i]*2 + x_g[i]*7 + x_b[i] + 5)/10);
+ cvFloodFill( grey, seed, brightness, cvRealScalar(x_lo),
+ cvRealScalar(x_up), &comp, flags, NULL );
+ }
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], comp.rect.x);
+ SETFLOAT(&x_list[2], comp.rect.y);
+ SETFLOAT(&x_list[3], comp.rect.width);
+ SETFLOAT(&x_list[4], comp.rect.height);
+ outlet_list( m_dataout, 0, 5, x_list );
+ }
+ }
+
+ if ( !x_color )
+ {
+ cvCvtColor(grey, rgb, CV_GRAY2BGR);
+ }
+
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_floodfill :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_floodfill : yuv format not supported" );
+}
+
+void pix_opencv_floodfill :: processGrayImage(imageStruct &image)
+{
+ int i, k;
+ int im;
+ int marked;
+ CvConnectedComp comp;
+ int flags = x_connectivity + ( 255 << 8 ) + CV_FLOODFILL_FIXED_RANGE;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+
+ 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 );
+ }
+
+ memcpy( grey->imageData, image.data, image.xsize*image.ysize );
+
+ // mark recognized components
+ for ( i=0; i<MAX_COMPONENTS; i++ )
+ {
+ if ( x_xcomp[i] != -1 )
+ {
+ CvPoint seed = cvPoint(x_xcomp[i],x_ycomp[i]);
+ CvScalar brightness = cvRealScalar((x_r[i]*2 + x_g[i]*7 + x_b[i] + 5)/10);
+ cvFloodFill( grey, seed, brightness, cvRealScalar(x_lo),
+ cvRealScalar(x_up), &comp, flags, NULL );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], comp.rect.x);
+ SETFLOAT(&x_list[2], comp.rect.y);
+ SETFLOAT(&x_list[3], comp.rect.width);
+ SETFLOAT(&x_list[4], comp.rect.height);
+ outlet_list( m_dataout, 0, 5, x_list );
+ }
+ }
+
+ memcpy( image.data, grey->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_floodfill :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_floodfill::colorMessCallback,
+ gensym("color"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_floodfill::fillcolorMessCallback,
+ gensym("fillcolor"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_floodfill::connectivityMessCallback,
+ gensym("connectivity"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_floodfill::markMessCallback,
+ gensym("mark"), A_FLOAT, A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_floodfill::deleteMessCallback,
+ gensym("delete"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_floodfill::clearMessCallback,
+ gensym("clear"), A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_floodfill::updiffMessCallback,
+ gensym("up_diff"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_floodfill::lodiffMessCallback,
+ gensym("lo_diff"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_floodfill :: colorMessCallback(void *data, t_floatarg color)
+{
+ GetMyClass(data)->colorMess((float)color);
+}
+
+void pix_opencv_floodfill :: fillcolorMessCallback(void *data, t_floatarg index, t_floatarg r, t_floatarg g, t_floatarg b)
+{
+ GetMyClass(data)->fillcolorMess(index, r, g, b);
+}
+
+void pix_opencv_floodfill :: connectivityMessCallback(void *data, t_floatarg connectivity)
+{
+ GetMyClass(data)->connectivityMess(connectivity);
+}
+
+void pix_opencv_floodfill :: markMessCallback(void *data, t_floatarg px, t_floatarg py)
+{
+ GetMyClass(data)->markMess(px, py);
+}
+
+void pix_opencv_floodfill :: deleteMessCallback(void *data, t_floatarg index)
+{
+ GetMyClass(data)->deleteMess((float)index);
+}
+
+void pix_opencv_floodfill :: clearMessCallback(void *data)
+{
+ GetMyClass(data)->clearMess();
+}
+
+void pix_opencv_floodfill :: updiffMessCallback(void *data, t_floatarg updiff)
+{
+ GetMyClass(data)->updiffMess((float)updiff);
+}
+
+void pix_opencv_floodfill :: lodiffMessCallback(void *data, t_floatarg lodiff)
+{
+ GetMyClass(data)->lodiffMess((float)lodiff);
+}
+
+void pix_opencv_floodfill :: colorMess(float color)
+{
+ if ( ( (int)color == 0 ) || ( (int)color == 1 ) )
+ {
+ x_color = (int)color;
+ }
+}
+
+void pix_opencv_floodfill :: fillcolorMess(float index, float r, float g, float b)
+{
+ if ( ( (int)index <= 0 ) || ( (int)index > MAX_COMPONENTS ) )
+ {
+ post( "pix_opencv_floodfill : wrong color index : %d", (int)index );
+ return;
+ }
+
+ if ( ( (int)r >= 0 ) || ( (int)r <= 255 ) )
+ {
+ x_r[(int)index-1] = (int)r;
+ }
+
+ if ( ( (int)g >= 0 ) || ( (int)g <= 255 ) )
+ {
+ x_g[(int)index-1] = (int)g;
+ }
+
+ if ( ( (int)b >= 0 ) || ( (int)b <= 255 ) )
+ {
+ x_b[(int)index-1] = (int)b;
+ }
+}
+
+void pix_opencv_floodfill :: connectivityMess(float connectivity)
+{
+ if ( ( connectivity != 4.0 ) && ( connectivity != 8.0 ) )
+ {
+ return;
+ }
+
+ x_connectivity = (int)connectivity;
+}
+
+void pix_opencv_floodfill :: markMess(float fpx, float fpy)
+{
+ int i;
+ int inserted;
+ int px, py;
+
+ if ( ( fpx < 0.0 ) || ( fpx > comp_xsize ) || ( fpy < 0.0 ) || ( fpy > comp_ysize ) )
+ {
+ return;
+ }
+
+ px = (int)fpx;
+ //py = comp_ysize-(int)fpy;
+ py = (int)fpy;
+ inserted = 0;
+ for ( i=0; i<MAX_COMPONENTS; i++)
+ {
+ if ( x_xcomp[i] == -1 )
+ {
+ x_xcomp[i] = px;
+ x_ycomp[i] = py;
+ // post( "pix_opencv_floodfill : inserted point (%d,%d)", px, py );
+ inserted = 1;
+ break;
+ }
+ }
+ if ( !inserted )
+ {
+ post( "pix_opencv_floodfill : max markers reached" );
+ }
+}
+
+void pix_opencv_floodfill :: deleteMess(float index)
+{
+ int i;
+
+ if ( ( index < 1.0 ) || ( index > MAX_COMPONENTS ) )
+ {
+ return;
+ }
+
+ x_xcomp[(int)index-1] = -1;
+ x_ycomp[(int)index-1] = -1;
+
+}
+
+void pix_opencv_floodfill :: clearMess(void)
+{
+ int i;
+
+ for ( i=0; i<MAX_COMPONENTS; i++)
+ {
+ x_xcomp[i] = -1;
+ x_ycomp[i] = -1;
+ }
+
+}
+
+void pix_opencv_floodfill :: updiffMess(float updiff)
+{
+ if ( ( (int)updiff >= 0 ) && ( (int)updiff <= 255 ) )
+ {
+ x_up = (int)updiff;
+ }
+}
+
+void pix_opencv_floodfill :: lodiffMess(float lodiff)
+{
+ if ( ( (int)lodiff >= 0 ) && ( (int)lodiff <= 255 ) )
+ {
+ x_lo = (int)lodiff;
+ }
+}
diff --git a/src/pix_opencv_floodfill.h b/src/pix_opencv_floodfill.h
new file mode 100644
index 0000000..8c2d296
--- /dev/null
+++ b/src/pix_opencv_floodfill.h
@@ -0,0 +1,111 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Color blob tracker
+
+ 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_FLOODFILL_H_
+#define INCLUDE_PIX_OPENCV_FLOODFILL_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+#define MAX_COMPONENTS 10
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_floodfill
+
+ Color blob tracker
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_floodfill : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_floodfill, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_floodfill();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_floodfill();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ void colorMess(float color);
+ void fillcolorMess(float index, float r, float g, float b);
+ void connectivityMess(float connectivity);
+ void markMess(float px, float py);
+ void deleteMess(float index);
+ void clearMess(void);
+ void updiffMess(float updiff);
+ void lodiffMess(float lodiff);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ t_outlet *m_dataout;
+ t_atom x_list[5];
+
+ int x_up;
+ int x_lo;
+ int x_connectivity;
+ int x_color;
+
+
+ private:
+
+ //////////
+ // Static member functions
+ static void colorMessCallback(void *data, float color);
+ static void fillcolorMessCallback(void *data, float index, float r, float g, float b);
+ static void connectivityMessCallback(void *data, float connectivity);
+ static void markMessCallback(void *data, float px, float py);
+ static void deleteMessCallback(void *data, float index);
+ static void clearMessCallback(void *data);
+ static void updiffMessCallback(void *data, float updiff);
+ static void lodiffMessCallback(void *data, float lodiff);
+
+ // Internal Open CV data
+ // tracked components
+ int x_xcomp[MAX_COMPONENTS];
+ int x_ycomp[MAX_COMPONENTS];
+
+ // fill color
+ int x_r[MAX_COMPONENTS];
+ int x_g[MAX_COMPONENTS];
+ int x_b[MAX_COMPONENTS];
+
+ IplImage *rgba, *rgb, *grey;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_haarcascade.cc b/src/pix_opencv_haarcascade.cc
new file mode 100644
index 0000000..59dce35
--- /dev/null
+++ b/src/pix_opencv_haarcascade.cc
@@ -0,0 +1,526 @@
+////////////////////////////////////////////////////////
+//
+// 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_haarcascade.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+CPPEXTERN_NEW(pix_opencv_haarcascade)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_haarcascade
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_haarcascade :: pix_opencv_haarcascade()
+{
+ int i;
+
+ m_numout = outlet_new(this->x_obj, 0);
+ m_dataout = outlet_new(this->x_obj, 0);
+
+ scale_factor = 1.1;
+ min_neighbors = 2;
+ mode = 0;
+ min_size = 30;
+
+ comp_xsize = 0;
+ comp_ysize = 0;
+ rgba = NULL;
+ grey = NULL;
+ frame = NULL;
+ x_ftolerance = 5;
+
+ for ( i=0; i<MAX_MARKERS; i++ )
+ {
+ x_xmark[i] = -1;
+ x_ymark[i] = -1;
+ }
+
+ // initialize font
+ cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 0, 1, 8 );
+
+ cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );
+ if( !cascade )
+ {
+ post( "ERROR: Could not load classifier cascade\n" );
+ }
+ else post( "Loaded classifier cascade from %s", cascade_name );
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_haarcascade :: ~pix_opencv_haarcascade()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&grey);
+ cvReleaseImage(&frame);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_haarcascade :: processRGBAImage(imageStruct &image)
+{
+ double scale = 1;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgba)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&grey);
+ cvReleaseImage(&frame);
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ frame = cvCreateImage(cvSize(rgba->width,rgba->height), IPL_DEPTH_8U, 3);
+ grey = cvCreateImage( cvSize(rgba->width,rgba->height), 8, 1 );
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ CvMemStorage* storage = cvCreateMemStorage(0);
+
+ static CvScalar colors[] =
+ {
+ {{0,0,255}},
+ {{0,128,255}},
+ {{0,255,255}},
+ {{0,255,0}},
+ {{255,128,0}},
+ {{255,255,0}},
+ {{255,0,0}},
+ {{255,0,255}}
+ };
+
+ int i, im;
+
+ if( cascade )
+ {
+ CvSeq* faces = cvHaarDetectObjects( rgba, cascade, storage,
+ scale_factor, min_neighbors, mode, cvSize(min_size, min_size) );
+
+ if ( faces && (faces->total > 0 ) )
+ outlet_float(this->m_numout, (float)faces->total);
+ else
+ outlet_float(this->m_numout, 0.0);
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( this->x_xmark[im] != -1.0 )
+ {
+ this->x_found[im]--;
+ }
+ }
+
+ for( i = 0; i < (faces ? faces->total : 0); i++ )
+ {
+ int oi, found;
+
+ CvRect* r = (CvRect*)cvGetSeqElem( faces, i );
+ CvPoint center;
+ int radius;
+ center.x = cvRound((r->x + r->width*0.5)*scale);
+ center.y = cvRound((r->y + r->height*0.5)*scale);
+ radius = cvRound((r->width + r->height)*0.25*scale);
+
+ found = 0;
+ oi = -1;
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ // check if the object is already known
+ if ( sqrt( pow((double)center.x - this->x_xmark[im], 2 ) + pow((double)center.y - this->x_ymark[im], 2 ) ) <= radius )
+ {
+ oi=im;
+ found=1;
+ this->x_found[im] = this->x_ftolerance;
+ this->x_xmark[im] = center.x;
+ this->x_ymark[im] = center.y;
+ break;
+ }
+ }
+ // new object detected
+ if ( !found )
+ {
+ oi = this->mark(center.x, center.y );
+ }
+
+ char tindex[4];
+ sprintf( tindex, "%d", oi );
+ cvCircle( rgba, center, radius, colors[oi%8], 3, 8, 0 );
+ cvPutText( rgba, tindex, center, &this->font, CV_RGB(255,255,255));
+
+ SETFLOAT(&this->rlist[0], oi);
+ SETFLOAT(&this->rlist[1], center.x);
+ SETFLOAT(&this->rlist[2], center.y);
+ SETFLOAT(&this->rlist[3], radius);
+ outlet_list( m_dataout, 0, 4, this->rlist );
+ }
+ // delete lost objects
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( this->x_found[im] < 0 )
+ {
+ this->x_xmark[im] = -1.0;
+ this->x_ymark[im] = -1,0;
+ this->x_found[im] = this->x_ftolerance;
+ }
+ }
+
+ }
+
+ cvReleaseMemStorage( &storage );
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_haarcascade :: processRGBImage(imageStruct &image)
+{
+ double scale = 1;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!frame)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&grey);
+ cvReleaseImage(&frame);
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ frame = cvCreateImage(cvSize(rgba->width,rgba->height), IPL_DEPTH_8U, 3);
+ grey = cvCreateImage( cvSize(rgba->width,rgba->height), 8, 1 );
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( frame->imageData, image.data, image.xsize*image.ysize*3 );
+ CvMemStorage* storage = cvCreateMemStorage(0);
+
+ static CvScalar colors[] =
+ {
+ {{0,0,255}},
+ {{0,128,255}},
+ {{0,255,255}},
+ {{0,255,0}},
+ {{255,128,0}},
+ {{255,255,0}},
+ {{255,0,0}},
+ {{255,0,255}}
+ };
+
+ int i, im;
+
+ if( cascade )
+ {
+ CvSeq* faces = cvHaarDetectObjects( frame, cascade, storage,
+ 1.1, 2, 0, cvSize(30, 30) );
+
+ if ( faces && (faces->total > 0 ) )
+ outlet_float(this->m_numout, (float)faces->total);
+ else
+ outlet_float(this->m_numout, 0.0);
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( this->x_xmark[im] != -1.0 )
+ {
+ this->x_found[im]--;
+ }
+ }
+
+ for( i = 0; i < (faces ? faces->total : 0); i++ )
+ {
+ int oi, found;
+
+ CvRect* r = (CvRect*)cvGetSeqElem( faces, i );
+ CvPoint center;
+ int radius;
+ center.x = cvRound((r->x + r->width*0.5)*scale);
+ center.y = cvRound((r->y + r->height*0.5)*scale);
+ radius = cvRound((r->width + r->height)*0.25*scale);
+
+ found = 0;
+ oi = -1;
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ // check if the object is already known
+ if ( sqrt( pow((double)center.x - this->x_xmark[im], 2 ) + pow((double)center.y - this->x_ymark[im], 2 ) ) <= radius )
+ {
+ oi=im;
+ found=1;
+ this->x_found[im] = this->x_ftolerance;
+ this->x_xmark[im] = center.x;
+ this->x_ymark[im] = center.y;
+ break;
+ }
+ }
+ // new object detected
+ if ( !found )
+ {
+ oi = this->mark(center.x, center.y );
+ }
+
+ char tindex[4];
+ sprintf( tindex, "%d", oi );
+ cvCircle( frame, center, radius, colors[oi%8], 3, 8, 0 );
+ cvPutText( frame, tindex, center, &this->font, CV_RGB(255,255,255));
+
+ SETFLOAT(&this->rlist[0], oi);
+ SETFLOAT(&this->rlist[1], center.x);
+ SETFLOAT(&this->rlist[2], center.y);
+ SETFLOAT(&this->rlist[3], radius);
+ outlet_list( m_dataout, 0, 4, this->rlist );
+ }
+ // delete lost objects
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( this->x_found[im] < 0 )
+ {
+ this->x_xmark[im] = -1.0;
+ this->x_ymark[im] = -1,0;
+ this->x_found[im] = this->x_ftolerance;
+ }
+ }
+ }
+
+ cvReleaseMemStorage( &storage );
+ memcpy( image.data, frame->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_haarcascade :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_haarcascade : yuv format not supported" );
+}
+
+void pix_opencv_haarcascade :: processGrayImage(imageStruct &image)
+{
+ double scale = 1;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!grey)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&grey);
+ cvReleaseImage(&frame);
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ frame = cvCreateImage(cvSize(rgba->width,rgba->height), IPL_DEPTH_8U, 3);
+ grey = cvCreateImage( cvSize(rgba->width,rgba->height), 8, 1 );
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( grey->imageData, image.data, image.xsize*image.ysize );
+ CvMemStorage* storage = cvCreateMemStorage(0);
+
+ static CvScalar colors[] =
+ {
+ {{0,0,255}},
+ {{0,128,255}},
+ {{0,255,255}},
+ {{0,255,0}},
+ {{255,128,0}},
+ {{255,255,0}},
+ {{255,0,0}},
+ {{255,0,255}}
+ };
+
+ int i;
+
+ if( cascade )
+ {
+ CvSeq* faces = cvHaarDetectObjects( grey, cascade, storage,
+ 1.1, 2, 0, cvSize(30, 30) );
+ for( i = 0; i < (faces ? faces->total : 0); i++ )
+ {
+ CvRect* r = (CvRect*)cvGetSeqElem( faces, i );
+ CvPoint center;
+ int radius;
+ center.x = cvRound((r->x + r->width*0.5)*scale);
+ center.y = cvRound((r->y + r->height*0.5)*scale);
+ radius = cvRound((r->width + r->height)*0.25*scale);
+ cvCircle( grey, center, radius, colors[i%8], 3, 8, 0 );
+
+ t_atom rlist[4];
+ SETFLOAT(&rlist[0], i);
+ SETFLOAT(&rlist[1], center.x);
+ SETFLOAT(&rlist[2], center.y);
+ SETFLOAT(&rlist[3], radius);
+ outlet_list( m_dataout, 0, 4, rlist );
+ }
+ }
+
+
+ cvReleaseMemStorage( &storage );
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, grey->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// scaleFactorMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_haarcascade :: scaleFactorMess (float scale_factor)
+{
+ this->scale_factor = scale_factor;
+}
+
+/////////////////////////////////////////////////////////
+// minNeighborsMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_haarcascade :: minNeighborsMess (float min_neighbors)
+{
+ this->min_neighbors = (int)min_neighbors;
+}
+
+/////////////////////////////////////////////////////////
+// modeMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_haarcascade :: modeMess (float mode)
+{
+ this->mode = !(!(int)mode);
+}
+
+/////////////////////////////////////////////////////////
+// minSizeMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_haarcascade :: minSizeMess (float min_size)
+{
+ this->min_size = (int)min_size;
+}
+
+/////////////////////////////////////////////////////////
+// fToleranceMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_haarcascade :: fToleranceMess (float ftolerance)
+{
+ this->x_ftolerance = (int)ftolerance;
+}
+
+/////////////////////////////////////////////////////////
+// clearMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_haarcascade :: clearMess (void)
+{
+ int i;
+
+ for ( i=0; i<MAX_MARKERS; i++)
+ {
+ this->x_xmark[i] = -1;
+ this->x_ymark[i] = -1;
+ this->x_found[i] = this->x_ftolerance;
+ }
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_haarcascade :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_haarcascade::scaleFactorMessCallback,
+ gensym("scale_factor"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_haarcascade::minNeighborsMessCallback,
+ gensym("min_neighbors"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_haarcascade::modeMessCallback,
+ gensym("mode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_haarcascade::minSizeMessCallback,
+ gensym("min_size"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_haarcascade::loadCascadeMessCallback,
+ gensym("load"), A_SYMBOL, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_haarcascade::fToleranceMessCallback,
+ gensym("ftolerance"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_haarcascade::clearMessCallback,
+ gensym("clear"), A_NULL);
+}
+void pix_opencv_haarcascade :: scaleFactorMessCallback(void *data, t_floatarg scale_factor)
+{
+ if (scale_factor>1) GetMyClass(data)->scaleFactorMess((float)scale_factor);
+}
+void pix_opencv_haarcascade :: minNeighborsMessCallback(void *data, t_floatarg min_neighbors)
+{
+ if (min_neighbors>=1) GetMyClass(data)->minNeighborsMess((float)min_neighbors);
+}
+void pix_opencv_haarcascade :: modeMessCallback(void *data, t_floatarg mode)
+{
+ if ((mode==0)||(mode==1)) GetMyClass(data)->modeMess((float)mode);
+}
+void pix_opencv_haarcascade :: minSizeMessCallback(void *data, t_floatarg min_size)
+{
+ if (min_size>1) GetMyClass(data)->minSizeMess((float)min_size);
+}
+void pix_opencv_haarcascade :: fToleranceMessCallback(void *data, t_floatarg ftolerance)
+{
+ if (ftolerance>1) GetMyClass(data)->fToleranceMess((float)ftolerance);
+}
+void pix_opencv_haarcascade :: clearMessCallback(void *data)
+{
+ GetMyClass(data)->clearMess();
+}
+void pix_opencv_haarcascade :: loadCascadeMessCallback(void *data, t_symbol* filename)
+{
+ GetMyClass(data)->loadCascadeMess(filename);
+}
+void pix_opencv_haarcascade :: loadCascadeMess(t_symbol *filename)
+{
+ cascade = (CvHaarClassifierCascade*)cvLoad( filename->s_name, 0, 0, 0 );
+ if( !cascade )
+ {
+ post( "ERROR: Could not load classifier cascade" );
+ }
+ else post( "Loaded classifier cascade from %s", filename->s_name );
+}
+int pix_opencv_haarcascade :: mark(float fx, float fy )
+{
+ int i;
+
+ if ( ( fx < 0.0 ) || ( fx > this->rgba->width ) || ( fy < 0 ) || ( fy > this->rgba->height ) )
+ {
+ return -1;
+ }
+
+ for ( i=0; i<MAX_MARKERS; i++)
+ {
+ if ( this->x_xmark[i] == -1 )
+ {
+ this->x_xmark[i] = (int)fx;
+ this->x_ymark[i] = (int)fy;
+ this->x_found[i] = this->x_ftolerance;
+ return i;
+ }
+ }
+
+ post( "pix_opencv_haarcascade : max markers reached" );
+ return -1;
+}
diff --git a/src/pix_opencv_haarcascade.h b/src/pix_opencv_haarcascade.h
new file mode 100644
index 0000000..8c8fbb4
--- /dev/null
+++ b/src/pix_opencv_haarcascade.h
@@ -0,0 +1,113 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Trained classifier using Haar's cascade
+
+ 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_HAARSCASCADE_H_
+#define INCLUDE_PIX_OPENCV_HAARSCASCADE_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/objdetect/objdetect.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+#define MAX_MARKERS 50
+
+const char* cascade_name ="./haarcascade_frontalface_alt.xml";
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_haarcascade
+
+ Trained classifier using Haar's cascade
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_haarcascade : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_haarcascade, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_haarcascade();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_haarcascade();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void scaleFactorMess(float scale_factor);
+ void minNeighborsMess(float min_neighbors);
+ void modeMess(float mode);
+ void minSizeMess(float min_size);
+ void loadCascadeMess(t_symbol *filename);
+ void fToleranceMess(float ftolerance);
+ void clearMess(void);
+ int mark(float fx, float fy );
+ // The parameters for cvHaarDetectObjects function
+ float scale_factor;
+ int min_neighbors;
+ int mode;
+ int min_size;
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+ t_atom rlist[4];
+ // marked objects history
+ int x_xmark[MAX_MARKERS];
+ int x_ymark[MAX_MARKERS];
+ int x_found[MAX_MARKERS];
+ int x_ftolerance;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void scaleFactorMessCallback(void *data, t_floatarg scale_factor);
+ static void minNeighborsMessCallback(void *data, float min_neighbors);
+ static void modeMessCallback(void *data, float mode);
+ static void minSizeMessCallback(void *data, float min_size);
+ static void loadCascadeMessCallback(void *data, t_symbol* filename);
+ static void fToleranceMessCallback(void *data, float ftolerance);
+ static void clearMessCallback(void *data);
+
+ CvHaarClassifierCascade* cascade;
+ CvFont font;
+ /////////
+ // IplImage needed
+ IplImage *rgba, *frame, *grey;
+
+ t_outlet *m_numout;
+ t_outlet *m_dataout;
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_hist_compare.cc b/src/pix_opencv_hist_compare.cc
new file mode 100644
index 0000000..b81d396
--- /dev/null
+++ b/src/pix_opencv_hist_compare.cc
@@ -0,0 +1,279 @@
+////////////////////////////////////////////////////////
+//
+// 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_hist_compare.h"
+
+CPPEXTERN_NEW(pix_opencv_hist_compare)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_hist_compare
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_hist_compare :: pix_opencv_hist_compare()
+{
+ comp_xsize=320;
+ comp_ysize=240;
+
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("save"));
+ m_dataout = outlet_new(this->x_obj, &s_anything);
+ m_measureout = outlet_new(this->x_obj, &s_anything);
+
+ save_now = 0;
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ grey = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ hsv = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3 );
+
+ h_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ s_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ v_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ planes[0] = h_plane;
+ planes[1] = s_plane;
+ h_saved_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ s_saved_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ v_saved_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ saved_planes[0] = h_saved_plane;
+ saved_planes[1] = s_saved_plane;
+
+ int h_bins = (int)(comp_xsize/10), s_bins = (int)(comp_ysize/10);
+ {
+ int hist_size[] = { h_bins, s_bins };
+ float h_ranges[] = { 0, 180 }; // hue is [0,180]
+ float s_ranges[] = { 0, 255 };
+ float* ranges[] = { h_ranges, s_ranges };
+ hist = cvCreateHist(
+ 2,
+ hist_size,
+ CV_HIST_ARRAY,
+ ranges,
+ 1
+ );
+ int n;
+ for (n=0; n<MAX_HISTOGRAMS_TO_COMPARE; n++) {
+ saved_hist[n] = cvCreateHist(
+ 2,
+ hist_size,
+ CV_HIST_ARRAY,
+ ranges,
+ 1
+ );
+ }
+ }
+ nbsaved=0;
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_hist_compare :: ~pix_opencv_hist_compare()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&grey);
+ cvReleaseImage(&h_plane);
+ cvReleaseImage(&s_plane);
+ cvReleaseImage(&v_plane);
+ cvReleaseImage(&h_saved_plane);
+ cvReleaseImage(&s_saved_plane);
+ cvReleaseImage(&v_saved_plane);
+
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_hist_compare :: processImage(imageStruct &image)
+{
+ int h_bins = (int)(comp_xsize/10), s_bins = (int)(comp_ysize/10);
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize)) {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&grey);
+ cvReleaseImage(&h_plane);
+ cvReleaseImage(&s_plane);
+ cvReleaseImage(&v_plane);
+ cvReleaseImage(&h_saved_plane);
+ cvReleaseImage(&s_saved_plane);
+ cvReleaseImage(&v_saved_plane);
+
+ //Create cv_images
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ grey = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ hsv = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3 );
+
+
+ h_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ s_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ v_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ planes[0] = h_plane;
+ planes[1] = s_plane;
+ cvCvtPixToPlane( hsv, h_plane, s_plane, v_plane, 0 );
+ h_saved_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ s_saved_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ v_saved_plane = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ saved_planes[0] = h_saved_plane;
+ saved_planes[1] = s_saved_plane;
+
+ h_bins = (int)(comp_xsize/10);
+ s_bins = (int)(comp_ysize/10);
+ {
+ int hist_size[] = { h_bins, s_bins };
+ float h_ranges[] = { 0, 180 }; // hue is [0,180]
+ float s_ranges[] = { 0, 255 };
+ float* ranges[] = { h_ranges, s_ranges };
+ if ( hist ) {
+ cvReleaseHist( &hist );
+ hist = NULL;
+ }
+
+ hist = cvCreateHist(
+ 2,
+ hist_size,
+ CV_HIST_ARRAY,
+ ranges,
+ 1
+ );
+ int n;
+ for (n=0; n<MAX_HISTOGRAMS_TO_COMPARE; n++) {
+ if ( saved_hist[n] ) {
+ cvReleaseHist( &saved_hist[n] );
+ saved_hist[n] = NULL;
+ }
+
+ saved_hist[n] = cvCreateHist(
+ 2,
+ hist_size,
+ CV_HIST_ARRAY,
+ ranges,
+ 1
+ );
+ }
+ }
+
+ }
+
+ // no need to copy the image, just create valid header and point to image.data...
+ IplImage* imgMat = cvCreateImageHeader( cv::Size(image.xsize, image.ysize), IPL_DEPTH_8U, image.csize);
+ imgMat->imageData = (char*) image.data;
+
+ if ( image.csize == 1 ){ // gray
+ cvCvtColor(imgMat, rgb, CV_GRAY2BGR);
+ } else if ( image.csize == 4 ) { //RGBA
+ cvCvtColor(imgMat, rgb, CV_BGRA2BGR);
+ } else {
+ error("support only RGBA or GRAY image");
+ return;
+ }
+
+ // Convert to hsv
+ cvCvtColor( rgb, hsv, CV_BGR2HSV );
+ cvCvtPixToPlane( hsv, h_plane, s_plane, v_plane, 0 );
+
+ // Build the histogram and compute its contents.
+ if (save_now>=0) {
+ post("saving histogram %d\n",save_now);
+ cvCvtPixToPlane( hsv, h_saved_plane, s_saved_plane, v_saved_plane, 0 );
+ cvCalcHist( saved_planes, saved_hist[save_now], 0, 0 ); //Compute histogram
+ cvNormalizeHist( saved_hist[save_now], 1.0 ); //Normalize it
+ save_now=-1;
+ nbsaved++;
+ }
+ cvCalcHist( planes, hist, 0, 0 ); //Compute histogram
+ cvNormalizeHist( hist, 1.0 ); //Normalize it
+
+ if ( nbsaved > 0 ) {
+ double* tato = new double[nbsaved];
+ t_atom* datalist = new t_atom[nbsaved];
+ int nearest = -1;
+ double max = 0;
+
+ for ( int n=0; n<nbsaved; n++) {
+ tato[n] = cvCompareHist(hist, saved_hist[n], CV_COMP_INTERSECT);
+ SETFLOAT(&datalist[n], tato[n]);
+ if (tato[n]>max) {
+ max = tato[n];
+ nearest = n;
+ }
+ }
+ outlet_float(m_dataout, (float)nearest);
+ outlet_list( m_measureout, 0, nbsaved , datalist );
+
+ if ( tato ) delete[] tato; tato=NULL;
+ if ( datalist ) delete[] datalist; datalist=NULL;
+ } else {
+ outlet_float(m_dataout, -1.0);
+ }
+
+ // Create an image to use to visualize our histogram.
+ int scale = 10;
+ // populate our visualization with little gray squares.
+ float max_value = 0;
+ cvGetMinMaxHistValue( hist, 0, &max_value, 0, 0 );
+
+ int h = 0;
+ int s = 0;
+
+ for( h = 0; h < h_bins; h++ ) {
+ for( s = 0; s < s_bins; s++ ) {
+ float bin_val = cvQueryHistValue_2D( hist, h, s );
+ int intensity = cvRound( bin_val * 255 / max_value );
+ cvRectangle(
+ imgMat,
+ cvPoint( h*scale, s*scale ),
+ cvPoint( (h+1)*scale - 1, (s+1)*scale - 1),
+ CV_RGB(intensity,intensity,intensity), CV_FILLED, 8 , 0 );
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_hist_compare :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_hist_compare::saveMessCallback,
+ gensym("save"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_hist_compare :: saveMess(float index)
+{
+ if (((int)index>=0)&&((int)index<MAX_HISTOGRAMS_TO_COMPARE)) save_now = (int)index;
+}
+
+void pix_opencv_hist_compare :: saveMessCallback(void *data, t_floatarg index)
+{
+ GetMyClass(data)->saveMess(index);
+}
diff --git a/src/pix_opencv_hist_compare.h b/src/pix_opencv_hist_compare.h
new file mode 100644
index 0000000..bdfedfd
--- /dev/null
+++ b/src/pix_opencv_hist_compare.h
@@ -0,0 +1,88 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Histogram reognition object using Open CV
+
+ 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_HIST_COMPARE_H_
+#define INCLUDE_PIX_OPENCV_HIST_COMPARE_H_
+
+#ifndef _EiC
+#include "opencv2/opencv.hpp"
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/legacy/compat.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+#define MAX_HISTOGRAMS_TO_COMPARE 80
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_hist_compare
+
+ Histogram reognition object using Open CV
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+
+class GEM_EXPORT pix_opencv_hist_compare : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_hist_compare, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_hist_compare();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_hist_compare();
+
+ //////////
+ // Do the processing
+ virtual void processImage(imageStruct &image);
+
+ void saveMess(float index);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ t_outlet *m_dataout;
+ t_outlet *m_measureout;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void saveMessCallback(void *data, t_floatarg index);
+
+ // The output and temporary images
+ int save_now;
+ int nbsaved;
+
+ CvHistogram *hist;
+ CvHistogram *saved_hist[MAX_HISTOGRAMS_TO_COMPARE];
+ IplImage *rgba, *rgb, *grey, *hsv, *h_plane, *s_plane, *v_plane, *h_saved_plane, *s_saved_plane, *v_saved_plane, *planes[2],*saved_planes[2];
+
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_hough_circles.cc b/src/pix_opencv_hough_circles.cc
new file mode 100644
index 0000000..2ebbdcf
--- /dev/null
+++ b/src/pix_opencv_hough_circles.cc
@@ -0,0 +1,315 @@
+////////////////////////////////////////////////////////
+//
+// 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_hough_circles.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_hough_circles)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_hough_circles
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_hough_circles :: pix_opencv_hough_circles()
+{
+ int i;
+
+ comp_xsize=320;
+ comp_ysize=240;
+
+ m_dataout = outlet_new(this->x_obj, &s_anything);
+
+ x_threshold = 100;
+ x_threshold2 = 10;
+ x_maxcircles = 10;
+ x_mindist = 30.0;
+ x_resolution = 1.0;
+ night_mode = 0;
+
+ // initialize font
+ cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 0, 1, 8 );
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_hough_circles :: ~pix_opencv_hough_circles()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_hough_circles :: processRGBAImage(imageStruct &image)
+{
+ int i, ucircles;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ cvCvtColor( rgba, gray, CV_BGRA2GRAY );
+
+ if( night_mode )
+ cvZero( rgba );
+
+ x_storage = cvCreateMemStorage(0);
+
+ cvSmooth( gray, gray, CV_GAUSSIAN, 9, 9 );
+ CvSeq* circles = cvHoughCircles( gray, x_storage, CV_HOUGH_GRADIENT, x_resolution, x_mindist, x_threshold, x_threshold2 );
+ ucircles = (circles->total>x_maxcircles)?x_maxcircles:circles->total;
+ for( i = 0; i < ucircles; i++ )
+ {
+ float* p = (float*)cvGetSeqElem( circles, i );
+ char tindex[10];
+
+ cvCircle( rgba, cvPoint(cvRound(p[0]),cvRound(p[1])), 3, CV_RGB(0,255,0), -1, 8, 0 );
+ cvCircle( rgba, cvPoint(cvRound(p[0]),cvRound(p[1])), cvRound(p[2]), CV_RGB(255,0,0), 3, 8, 0 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], cvRound(p[0]));
+ SETFLOAT(&x_list[2], cvRound(p[1]));
+ SETFLOAT(&x_list[3], cvRound(p[2]));
+ outlet_list( m_dataout, 0, 4, x_list );
+ sprintf( tindex, "%d", i );
+ cvPutText( rgba, tindex, cvPoint(cvRound(p[0]),cvRound(p[1])), &font, CV_RGB(255,255,255));
+ }
+
+ cvReleaseMemStorage( &x_storage );
+
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+
+}
+
+void pix_opencv_hough_circles :: processRGBImage(imageStruct &image)
+{
+ int i, ucircles;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+
+ }
+
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+ cvCvtColor( rgb, gray, CV_BGR2GRAY );
+
+ if( night_mode )
+ cvZero( rgb );
+
+ x_storage = cvCreateMemStorage(0);
+
+ cvSmooth( gray, gray, CV_GAUSSIAN, 9, 9 );
+ CvSeq* circles = cvHoughCircles( gray, x_storage, CV_HOUGH_GRADIENT, x_resolution, x_mindist, x_threshold, x_threshold2 );
+ ucircles = (circles->total>x_maxcircles)?x_maxcircles:circles->total;
+ for( i = 0; i < ucircles; i++ )
+ {
+ float* p = (float*)cvGetSeqElem( circles, i );
+ char tindex[10];
+
+ cvCircle( rgb, cvPoint(cvRound(p[0]),cvRound(p[1])), 3, CV_RGB(0,255,0), -1, 8, 0 );
+ cvCircle( rgb, cvPoint(cvRound(p[0]),cvRound(p[1])), cvRound(p[2]), CV_RGB(255,0,0), 3, 8, 0 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], cvRound(p[0]));
+ SETFLOAT(&x_list[2], cvRound(p[1]));
+ SETFLOAT(&x_list[3], cvRound(p[2]));
+ outlet_list( m_dataout, 0, 4, x_list );
+ sprintf( tindex, "%d", i );
+ cvPutText( rgb, tindex, cvPoint(cvRound(p[0]),cvRound(p[1])), &font, CV_RGB(255,255,255));
+ }
+
+ cvReleaseMemStorage( &x_storage );
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+
+}
+
+void pix_opencv_hough_circles :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_hough_circles : yuv format not supported" );
+}
+
+void pix_opencv_hough_circles :: processGrayImage(imageStruct &image)
+{
+ int i, ucircles;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+
+ }
+
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+
+ x_storage = cvCreateMemStorage(0);
+
+ cvSmooth( gray, gray, CV_GAUSSIAN, 9, 9 );
+ CvSeq* circles = cvHoughCircles( gray, x_storage, CV_HOUGH_GRADIENT, x_resolution, x_mindist, x_threshold, x_threshold2 );
+
+ if( night_mode )
+ cvZero( gray );
+
+ ucircles = (circles->total>x_maxcircles)?x_maxcircles:circles->total;
+ for( i = 0; i < ucircles; i++ )
+ {
+ float* p = (float*)cvGetSeqElem( circles, i );
+ char tindex[10];
+
+ cvCircle( gray, cvPoint(cvRound(p[0]),cvRound(p[1])), 3, CV_RGB(255,255,255), -1, 8, 0 );
+ cvCircle( gray, cvPoint(cvRound(p[0]),cvRound(p[1])), cvRound(p[2]), CV_RGB(255,255,255), 3, 8, 0 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], cvRound(p[0]));
+ SETFLOAT(&x_list[2], cvRound(p[1]));
+ SETFLOAT(&x_list[3], cvRound(p[2]));
+ outlet_list( m_dataout, 0, 4, x_list );
+ sprintf( tindex, "%d", i );
+ cvPutText( gray, tindex, cvPoint(cvRound(p[0]),cvRound(p[1])), &font, CV_RGB(255,255,255));
+ }
+
+ cvReleaseMemStorage( &x_storage );
+ memcpy( image.data, gray->imageData, image.xsize*image.ysize );
+
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_hough_circles :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_circles::floatNightModeMessCallback,
+ gensym("nightmode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_circles::floatThresholdMessCallback,
+ gensym("threshold"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_circles::floatThreshold2MessCallback,
+ gensym("threshold2"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_circles::floatMinDistMessCallback,
+ gensym("mindist"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_circles::floatResolutionMessCallback,
+ gensym("resolution"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_circles::floatMaxCirclesMessCallback,
+ gensym("maxcircles"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_hough_circles :: floatNightModeMessCallback(void *data, t_floatarg nightmode)
+{
+ GetMyClass(data)->floatNightModeMess((float)nightmode);
+}
+
+void pix_opencv_hough_circles :: floatThresholdMessCallback(void *data, t_floatarg threshold)
+{
+ GetMyClass(data)->floatThresholdMess((float)threshold);
+}
+
+void pix_opencv_hough_circles :: floatThreshold2MessCallback(void *data, t_floatarg threshold)
+{
+ GetMyClass(data)->floatThreshold2Mess((float)threshold);
+}
+
+void pix_opencv_hough_circles :: floatMinDistMessCallback(void *data, t_floatarg mindist)
+{
+ GetMyClass(data)->floatMinDistMess((float)mindist);
+}
+
+void pix_opencv_hough_circles :: floatResolutionMessCallback(void *data, t_floatarg resolution)
+{
+ GetMyClass(data)->floatResolutionMess((float)resolution);
+}
+
+void pix_opencv_hough_circles :: floatMaxCirclesMessCallback(void *data, t_floatarg maxcircles)
+{
+ GetMyClass(data)->floatMaxCirclesMess((float)maxcircles);
+}
+
+void pix_opencv_hough_circles :: floatNightModeMess(float nightmode)
+{
+ if ((nightmode==0.0)||(nightmode==1.0)) night_mode = (int)nightmode;
+}
+
+void pix_opencv_hough_circles :: floatThresholdMess(float threshold)
+{
+ if (threshold>0.0) x_threshold = (int)threshold;
+}
+
+void pix_opencv_hough_circles :: floatThreshold2Mess(float threshold)
+{
+ if (threshold>0.0) x_threshold2 = (int)threshold;
+}
+
+void pix_opencv_hough_circles :: floatMinDistMess(float mindist)
+{
+ if (mindist>0.0) x_mindist = (int)mindist;
+}
+
+void pix_opencv_hough_circles :: floatResolutionMess(float resolution)
+{
+ if (resolution>0.0) x_resolution = (int)resolution;
+}
+
+void pix_opencv_hough_circles :: floatMaxCirclesMess(float maxcircles)
+{
+ if (maxcircles>0.0) x_maxcircles = (int)maxcircles;
+}
+
diff --git a/src/pix_opencv_hough_circles.h b/src/pix_opencv_hough_circles.h
new file mode 100644
index 0000000..aaef0f5
--- /dev/null
+++ b/src/pix_opencv_hough_circles.h
@@ -0,0 +1,102 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Hough circles detection 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_HOUGH_CIRCLES_H_
+#define INCLUDE_PIX_OPENCV_HOUGH_CIRCLES_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+#define MAX_HISTOGRAMS_TO_COMPARE 80
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_hough_circles
+
+ Hough circles detection algorithm
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+
+class GEM_EXPORT pix_opencv_hough_circles : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_hough_circles, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_hough_circles();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_hough_circles();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ void floatNightModeMess(t_float nightmode);
+ void floatThresholdMess(t_floatarg threshold);
+ void floatThreshold2Mess(t_floatarg threshold);
+ void floatMinDistMess(t_floatarg mindist);
+ void floatResolutionMess(t_floatarg resolution);
+ void floatMaxCirclesMess(t_floatarg maxcircles);
+
+ t_outlet *m_dataout;
+
+ int x_threshold;
+ int x_threshold2;
+ int x_maxcircles;
+ double x_mindist;
+ double x_resolution;
+ int night_mode;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatNightModeMessCallback(void *data, t_floatarg nightmode);
+ static void floatThresholdMessCallback(void *data, t_floatarg threshold);
+ static void floatThreshold2MessCallback(void *data, t_floatarg threshold);
+ static void floatMinDistMessCallback(void *data, t_floatarg mindist);
+ static void floatResolutionMessCallback(void *data, t_floatarg resolution);
+ static void floatMaxCirclesMessCallback(void *data, t_floatarg maxcircles);
+
+ // The output and temporary images
+ IplImage *rgba, *rgb, *gray;
+ CvFont font;
+ CvMemStorage* x_storage;
+ CvSeq* x_circles;
+ t_atom x_list[4];
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_hough_lines.cc b/src/pix_opencv_hough_lines.cc
new file mode 100644
index 0000000..cf84305
--- /dev/null
+++ b/src/pix_opencv_hough_lines.cc
@@ -0,0 +1,600 @@
+////////////////////////////////////////////////////////
+//
+// 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_hough_lines.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_hough_lines)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_hough_lines
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_hough_lines :: pix_opencv_hough_lines()
+{
+ int i;
+
+ comp_xsize=320;
+ comp_ysize=240;
+
+ m_dataout = outlet_new(this->x_obj, &s_anything);
+
+ x_mode = CV_HOUGH_PROBABILISTIC;
+ x_threshold = 50;
+ x_maxlines = 10;
+ x_minlength = 30.0;
+ x_gap = 10.0;
+ x_aresolution = 10.0;
+ x_dresolution = 30.0;
+ night_mode = 0;
+
+ cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 0, 1, 8 );
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_hough_lines :: ~pix_opencv_hough_lines()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_hough_lines :: processRGBAImage(imageStruct &image)
+{
+ int i, ulines;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ cvCvtColor( rgba, gray, CV_BGRA2GRAY );
+
+ cvCanny( gray, gray, 50, 200, 3 );
+
+ if( night_mode )
+ cvZero( rgba );
+
+ x_storage = cvCreateMemStorage(0);
+
+ switch( x_mode )
+ {
+ case CV_HOUGH_STANDARD:
+
+ x_lines = cvHoughLines2( gray, x_storage, x_mode, 1, CV_PI/180, x_threshold, 0, 0 );
+
+ if ( x_lines )
+ {
+ ulines = ( x_lines->total >= x_maxlines ) ? x_maxlines:x_lines->total;
+ for( i=0; i<ulines; i++ )
+ {
+ float* line = (float*)cvGetSeqElem(x_lines,i);
+ float rho = line[0];
+ float theta = line[1];
+ CvPoint pt1, pt2;
+ char tindex[10];
+ double a = cos(theta), b = sin(theta);
+ double x0 = a*rho, y0 = b*rho;
+ pt1.x = cvRound(x0 + 1000*(-b));
+ pt1.y = cvRound(y0 + 1000*(a));
+ pt2.x = cvRound(x0 - 1000*(-b));
+ pt2.y = cvRound(y0 - 1000*(a));
+ cvLine( rgba, pt1, pt2, CV_RGB(255,0,0), 3, 8 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], pt1.x);
+ SETFLOAT(&x_list[2], pt1.y);
+ SETFLOAT(&x_list[3], pt2.x);
+ SETFLOAT(&x_list[4], pt2.y);
+ outlet_list( m_dataout, 0, 5, x_list );
+ pt1.x = (pt1.x+pt2.x)/2;
+ pt1.y = (pt1.y+pt2.y)/2;
+ sprintf( tindex, "%d", i );
+ cvPutText( rgba, tindex, pt1, &font, CV_RGB(255,255,255));
+ }
+ }
+ break;
+
+ case CV_HOUGH_PROBABILISTIC:
+
+ x_lines = cvHoughLines2( gray, x_storage, x_mode, 1, CV_PI/180, x_threshold, x_minlength, x_gap );
+
+ if ( x_lines )
+ {
+ ulines = ( x_lines->total >= x_maxlines ) ? x_maxlines:x_lines->total;
+ for( i=0; i<ulines; i++ )
+ {
+ CvPoint* line = (CvPoint*)cvGetSeqElem(x_lines,i);
+ char tindex[10];
+ cvLine( rgba, line[0], line[1], CV_RGB(255,0,0), 3, 8 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], line[0].x);
+ SETFLOAT(&x_list[2], line[0].y);
+ SETFLOAT(&x_list[3], line[1].x);
+ SETFLOAT(&x_list[4], line[1].y);
+ outlet_list( m_dataout, 0, 5, x_list );
+ line[0].x = (line[0].x+line[1].x)/2;
+ line[0].y = (line[0].y+line[1].y)/2;
+ sprintf( tindex, "%d", i );
+ cvPutText( rgba, tindex, line[0], &font, CV_RGB(255,255,255));
+ }
+ }
+ break;
+
+ case CV_HOUGH_MULTI_SCALE:
+
+ x_lines = cvHoughLines2( gray, x_storage, x_mode, 1, CV_PI/180, x_threshold, x_dresolution, x_aresolution );
+
+ if ( x_lines )
+ {
+ ulines = ( x_lines->total >= x_maxlines ) ? x_maxlines:x_lines->total;
+ for( i = 0; i < ulines; i++ )
+ {
+ float* line = (float*)cvGetSeqElem(x_lines,i);
+ float rho = line[0];
+ float theta = line[1];
+ char tindex[10];
+ CvPoint pt1, pt2;
+ double a = cos(theta), b = sin(theta);
+ double x0 = a*rho, y0 = b*rho;
+ pt1.x = cvRound(x0 + 1000*(-b));
+ pt1.y = cvRound(y0 + 1000*(a));
+ pt2.x = cvRound(x0 - 1000*(-b));
+ pt2.y = cvRound(y0 - 1000*(a));
+ cvLine( rgba, pt1, pt2, CV_RGB(255,0,0), 3, 8 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], pt1.x);
+ SETFLOAT(&x_list[2], pt1.y);
+ SETFLOAT(&x_list[3], pt2.x);
+ SETFLOAT(&x_list[4], pt2.y);
+ outlet_list( m_dataout, 0, 5, x_list );
+ pt1.x = (pt1.x+pt2.x)/2;
+ pt1.y = (pt1.y+pt2.y)/2;
+ sprintf( tindex, "%d", i );
+ cvPutText( rgba, tindex, pt1, &font, CV_RGB(255,255,255));
+ }
+ }
+ break;
+ }
+
+ cvReleaseMemStorage( &x_storage );
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+
+}
+
+void pix_opencv_hough_lines :: processRGBImage(imageStruct &image)
+{
+ int i, ulines;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+
+ }
+
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+ cvCvtColor( rgb, gray, CV_BGR2GRAY );
+
+ cvCanny( gray, gray, 50, 200, 3 );
+
+ if( night_mode )
+ cvZero( rgb );
+
+ x_storage = cvCreateMemStorage(0);
+
+ switch( x_mode )
+ {
+ case CV_HOUGH_STANDARD:
+
+ x_lines = cvHoughLines2( gray, x_storage, x_mode, 1, CV_PI/180, x_threshold, 0, 0 );
+
+ if ( x_lines )
+ {
+ ulines = ( x_lines->total >= x_maxlines ) ? x_maxlines:x_lines->total;
+ for( i=0; i<ulines; i++ )
+ {
+ float* line = (float*)cvGetSeqElem(x_lines,i);
+ float rho = line[0];
+ float theta = line[1];
+ CvPoint pt1, pt2;
+ char tindex[10];
+ double a = cos(theta), b = sin(theta);
+ double x0 = a*rho, y0 = b*rho;
+ pt1.x = cvRound(x0 + 1000*(-b));
+ pt1.y = cvRound(y0 + 1000*(a));
+ pt2.x = cvRound(x0 - 1000*(-b));
+ pt2.y = cvRound(y0 - 1000*(a));
+ cvLine( rgb, pt1, pt2, CV_RGB(255,0,0), 3, 8 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], pt1.x);
+ SETFLOAT(&x_list[2], pt1.y);
+ SETFLOAT(&x_list[3], pt2.x);
+ SETFLOAT(&x_list[4], pt2.y);
+ outlet_list( m_dataout, 0, 5, x_list );
+ pt1.x = (pt1.x+pt2.x)/2;
+ pt1.y = (pt1.y+pt2.y)/2;
+ sprintf( tindex, "%d", i );
+ cvPutText( rgb, tindex, pt1, &font, CV_RGB(255,255,255));
+ }
+ }
+ break;
+
+ case CV_HOUGH_PROBABILISTIC:
+
+ x_lines = cvHoughLines2( gray, x_storage, x_mode, 1, CV_PI/180, x_threshold, x_minlength, x_gap );
+
+ if ( x_lines )
+ {
+ ulines = ( x_lines->total >= x_maxlines ) ? x_maxlines:x_lines->total;
+ for( i=0; i<ulines; i++ )
+ {
+ CvPoint* line = (CvPoint*)cvGetSeqElem(x_lines,i);
+ char tindex[10];
+ cvLine( rgb, line[0], line[1], CV_RGB(255,0,0), 3, 8 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], line[0].x);
+ SETFLOAT(&x_list[2], line[0].y);
+ SETFLOAT(&x_list[3], line[1].x);
+ SETFLOAT(&x_list[4], line[1].y);
+ outlet_list( m_dataout, 0, 5, x_list );
+ line[0].x = (line[0].x+line[1].x)/2;
+ line[0].y = (line[0].y+line[1].y)/2;
+ sprintf( tindex, "%d", i );
+ cvPutText( rgb, tindex, line[0], &font, CV_RGB(255,255,255));
+ }
+ }
+ break;
+
+ case CV_HOUGH_MULTI_SCALE:
+
+ x_lines = cvHoughLines2( gray, x_storage, x_mode, 1, CV_PI/180, x_threshold, x_dresolution, x_aresolution );
+
+ if ( x_lines )
+ {
+ ulines = ( x_lines->total >= x_maxlines ) ? x_maxlines:x_lines->total;
+ for( i = 0; i < ulines; i++ )
+ {
+ float* line = (float*)cvGetSeqElem(x_lines,i);
+ float rho = line[0];
+ float theta = line[1];
+ char tindex[10];
+ CvPoint pt1, pt2;
+ double a = cos(theta), b = sin(theta);
+ double x0 = a*rho, y0 = b*rho;
+ pt1.x = cvRound(x0 + 1000*(-b));
+ pt1.y = cvRound(y0 + 1000*(a));
+ pt2.x = cvRound(x0 - 1000*(-b));
+ pt2.y = cvRound(y0 - 1000*(a));
+ cvLine( rgb, pt1, pt2, CV_RGB(255,0,0), 3, 8 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], pt1.x);
+ SETFLOAT(&x_list[2], pt1.y);
+ SETFLOAT(&x_list[3], pt2.x);
+ SETFLOAT(&x_list[4], pt2.y);
+ outlet_list( m_dataout, 0, 5, x_list );
+ pt1.x = (pt1.x+pt2.x)/2;
+ pt1.y = (pt1.y+pt2.y)/2;
+ sprintf( tindex, "%d", i );
+ cvPutText( rgb, tindex, pt1, &font, CV_RGB(255,255,255));
+ }
+ }
+ break;
+ }
+
+ cvReleaseMemStorage( &x_storage );
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+
+}
+
+void pix_opencv_hough_lines :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_hough_lines : yuv format not supported" );
+}
+
+void pix_opencv_hough_lines :: processGrayImage(imageStruct &image)
+{
+ int i, ulines;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+
+ }
+
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+ cvCanny( gray, gray, 50, 200, 3 );
+
+ x_storage = cvCreateMemStorage(0);
+
+ switch( x_mode )
+ {
+ case CV_HOUGH_STANDARD:
+
+ x_lines = cvHoughLines2( gray, x_storage, x_mode, 1, CV_PI/180, x_threshold, 0, 0 );
+
+ if( night_mode )
+ cvZero( gray );
+
+ if ( x_lines )
+ {
+ ulines = ( x_lines->total >= x_maxlines ) ? x_maxlines:x_lines->total;
+ for( i=0; i<ulines; i++ )
+ {
+ float* line = (float*)cvGetSeqElem(x_lines,i);
+ float rho = line[0];
+ float theta = line[1];
+ CvPoint pt1, pt2;
+ char tindex[10];
+ double a = cos(theta), b = sin(theta);
+ double x0 = a*rho, y0 = b*rho;
+ pt1.x = cvRound(x0 + 1000*(-b));
+ pt1.y = cvRound(y0 + 1000*(a));
+ pt2.x = cvRound(x0 - 1000*(-b));
+ pt2.y = cvRound(y0 - 1000*(a));
+ cvLine( gray, pt1, pt2, CV_RGB(255,255,255), 3, 8 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], pt1.x);
+ SETFLOAT(&x_list[2], pt1.y);
+ SETFLOAT(&x_list[3], pt2.x);
+ SETFLOAT(&x_list[4], pt2.y);
+ outlet_list( m_dataout, 0, 5, x_list );
+ pt1.x = (pt1.x+pt2.x)/2;
+ pt1.y = (pt1.y+pt2.y)/2;
+ sprintf( tindex, "%d", i );
+ cvPutText( gray, tindex, pt1, &font, CV_RGB(255,255,255));
+ }
+ }
+ break;
+
+ case CV_HOUGH_PROBABILISTIC:
+
+ x_lines = cvHoughLines2( gray, x_storage, x_mode, 1, CV_PI/180, x_threshold, x_minlength, x_gap );
+
+ if( night_mode )
+ cvZero( gray );
+
+ if ( x_lines )
+ {
+ ulines = ( x_lines->total >= x_maxlines ) ? x_maxlines:x_lines->total;
+ for( i=0; i<ulines; i++ )
+ {
+ CvPoint* line = (CvPoint*)cvGetSeqElem(x_lines,i);
+ char tindex[10];
+ cvLine( gray, line[0], line[1], CV_RGB(255,255,255), 3, 8 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], line[0].x);
+ SETFLOAT(&x_list[2], line[0].y);
+ SETFLOAT(&x_list[3], line[1].x);
+ SETFLOAT(&x_list[4], line[1].y);
+ outlet_list( m_dataout, 0, 5, x_list );
+ line[0].x = (line[0].x+line[1].x)/2;
+ line[0].y = (line[0].y+line[1].y)/2;
+ sprintf( tindex, "%d", i );
+ cvPutText( gray, tindex, line[0], &font, CV_RGB(255,255,255));
+ }
+ }
+ break;
+
+ case CV_HOUGH_MULTI_SCALE:
+
+ x_lines = cvHoughLines2( gray, x_storage, x_mode, 1, CV_PI/180, x_threshold, x_dresolution, x_aresolution );
+
+ if( night_mode )
+ cvZero( gray );
+
+ if ( x_lines )
+ {
+ ulines = ( x_lines->total >= x_maxlines ) ? x_maxlines:x_lines->total;
+ for( i = 0; i < ulines; i++ )
+ {
+ float* line = (float*)cvGetSeqElem(x_lines,i);
+ float rho = line[0];
+ float theta = line[1];
+ char tindex[10];
+ CvPoint pt1, pt2;
+ double a = cos(theta), b = sin(theta);
+ double x0 = a*rho, y0 = b*rho;
+ pt1.x = cvRound(x0 + 1000*(-b));
+ pt1.y = cvRound(y0 + 1000*(a));
+ pt2.x = cvRound(x0 - 1000*(-b));
+ pt2.y = cvRound(y0 - 1000*(a));
+ cvLine( gray, pt1, pt2, CV_RGB(255,255,255), 3, 8 );
+ SETFLOAT(&x_list[0], i);
+ SETFLOAT(&x_list[1], pt1.x);
+ SETFLOAT(&x_list[2], pt1.y);
+ SETFLOAT(&x_list[3], pt2.x);
+ SETFLOAT(&x_list[4], pt2.y);
+ outlet_list( m_dataout, 0, 5, x_list );
+ pt1.x = (pt1.x+pt2.x)/2;
+ pt1.y = (pt1.y+pt2.y)/2;
+ sprintf( tindex, "%d", i );
+ cvPutText( gray, tindex, pt1, &font, CV_RGB(255,255,255));
+ }
+ }
+ break;
+ }
+
+ cvReleaseMemStorage( &x_storage );
+ memcpy( image.data, gray->imageData, image.xsize*image.ysize );
+
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_hough_lines :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_lines::floatNightModeMessCallback,
+ gensym("nightmode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_lines::floatModeMessCallback,
+ gensym("mode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_lines::floatThresholdMessCallback,
+ gensym("threshold"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_lines::floatMinLengthMessCallback,
+ gensym("minlength"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_lines::floatGapMessCallback,
+ gensym("gap"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_lines::floatAResolutionMessCallback,
+ gensym("aresolution"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_lines::floatDResolutionMessCallback,
+ gensym("dresolution"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hough_lines::floatMaxLinesMessCallback,
+ gensym("maxlines"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_hough_lines :: floatNightModeMessCallback(void *data, t_floatarg nightmode)
+{
+ GetMyClass(data)->floatNightModeMess((float)nightmode);
+}
+
+void pix_opencv_hough_lines :: floatModeMessCallback(void *data, t_floatarg mode)
+{
+ GetMyClass(data)->floatModeMess((float)mode);
+}
+
+void pix_opencv_hough_lines :: floatThresholdMessCallback(void *data, t_floatarg threshold)
+{
+ GetMyClass(data)->floatThresholdMess((float)threshold);
+}
+
+void pix_opencv_hough_lines :: floatMinLengthMessCallback(void *data, t_floatarg minlength)
+{
+ GetMyClass(data)->floatMinLengthMess((float)minlength);
+}
+
+void pix_opencv_hough_lines :: floatGapMessCallback(void *data, t_floatarg gap)
+{
+ GetMyClass(data)->floatGapMess((float)gap);
+}
+
+void pix_opencv_hough_lines :: floatAResolutionMessCallback(void *data, t_floatarg aresolution)
+{
+ GetMyClass(data)->floatAResolutionMess((float)aresolution);
+}
+
+void pix_opencv_hough_lines :: floatDResolutionMessCallback(void *data, t_floatarg dresolution)
+{
+ GetMyClass(data)->floatDResolutionMess((float)dresolution);
+}
+
+void pix_opencv_hough_lines :: floatMaxLinesMessCallback(void *data, t_floatarg maxlines)
+{
+ GetMyClass(data)->floatMaxLinesMess((float)maxlines);
+}
+
+void pix_opencv_hough_lines :: floatNightModeMess(float nightmode)
+{
+ if ((nightmode==0.0)||(nightmode==1.0)) night_mode = (int)nightmode;
+}
+
+void pix_opencv_hough_lines :: floatModeMess(float mode)
+{
+ if ( mode == CV_HOUGH_STANDARD )
+ {
+ x_mode = CV_HOUGH_STANDARD;
+ }
+ if ( mode == CV_HOUGH_PROBABILISTIC )
+ {
+ x_mode = CV_HOUGH_PROBABILISTIC;
+ }
+ if ( mode == CV_HOUGH_MULTI_SCALE )
+ {
+ x_mode = CV_HOUGH_MULTI_SCALE;
+ }
+}
+
+void pix_opencv_hough_lines :: floatThresholdMess(float threshold)
+{
+ if (threshold>0.0) x_threshold = (int)threshold;
+}
+
+void pix_opencv_hough_lines :: floatMinLengthMess(float minlength)
+{
+ if (minlength>0.0) x_minlength = (double)minlength;
+}
+
+void pix_opencv_hough_lines :: floatGapMess(float gap)
+{
+ if (gap>0.0) x_gap = (double)gap;
+}
+
+void pix_opencv_hough_lines :: floatAResolutionMess(float aresolution)
+{
+ if (aresolution>0.0) x_aresolution = (double)aresolution;
+}
+
+void pix_opencv_hough_lines :: floatDResolutionMess(float dresolution)
+{
+ if (dresolution>0.0) x_dresolution = (double)dresolution;
+}
+
+void pix_opencv_hough_lines :: floatMaxLinesMess(float maxlines)
+{
+ if (maxlines>0.0) x_maxlines = (int)maxlines;
+}
+
diff --git a/src/pix_opencv_hough_lines.h b/src/pix_opencv_hough_lines.h
new file mode 100644
index 0000000..5b0d7bf
--- /dev/null
+++ b/src/pix_opencv_hough_lines.h
@@ -0,0 +1,110 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Hough lines detection 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_HOUGH_LINES_H_
+#define INCLUDE_PIX_OPENCV_HOUGH_LINES_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+#define MAX_HISTOGRAMS_TO_COMPARE 80
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_hough_lines
+
+ Hough lines detection algorithm
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+
+class GEM_EXPORT pix_opencv_hough_lines : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_hough_lines, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_hough_lines();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_hough_lines();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ void floatNightModeMess(t_float nightmode);
+ void floatModeMess(t_floatarg mode);
+ void floatThresholdMess(t_floatarg threshold);
+ void floatMinLengthMess(t_floatarg minlength);
+ void floatGapMess(t_floatarg gap);
+ void floatAResolutionMess(t_floatarg aresolution);
+ void floatDResolutionMess(t_floatarg dresolution);
+ void floatMaxLinesMess(t_floatarg maxlines);
+
+ t_outlet *m_dataout;
+
+ int x_mode;
+ int x_threshold;
+ int x_maxlines;
+ double x_minlength;
+ double x_gap;
+ double x_aresolution;
+ double x_dresolution;
+ int night_mode;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatNightModeMessCallback(void *data, t_float nightmode);
+ static void floatModeMessCallback(void *data, t_floatarg mode);
+ static void floatThresholdMessCallback(void *data, t_floatarg threshold);
+ static void floatMinLengthMessCallback(void *data, t_floatarg minlength);
+ static void floatGapMessCallback(void *data, t_floatarg gap);
+ static void floatAResolutionMessCallback(void *data, t_floatarg aresolution);
+ static void floatDResolutionMessCallback(void *data, t_floatarg dresolution);
+ static void floatMaxLinesMessCallback(void *data, t_floatarg maxlines);
+
+ // The output and temporary images
+ IplImage *rgba, *rgb, *gray;
+ t_atom x_list[5];
+
+ CvFont font;
+ CvMemStorage* x_storage;
+ CvSeq* x_lines;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_hu_compare.cc b/src/pix_opencv_hu_compare.cc
new file mode 100644
index 0000000..101c448
--- /dev/null
+++ b/src/pix_opencv_hu_compare.cc
@@ -0,0 +1,468 @@
+////////////////////////////////////////////////////////
+//
+// GEM - Graphics Environment for Multimedia
+//
+// zmoelnig@iem.kug.ac.at
+//
+// Implementation file
+//
+// Copyright (c) 1997-1998 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_hu_compare.h"
+
+CPPEXTERN_NEW_WITH_GIMME(pix_opencv_hu_compare)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_hu_compare
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_hu_compare :: pix_opencv_hu_compare(int argc, t_atom*argv)
+{
+ m_dataout = outlet_new(this->x_obj, &s_anything);
+ m_posout = outlet_new(this->x_obj, &s_anything);
+
+ comp_xsize=320;
+ comp_ysize=240;
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ rgbar = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgbr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ grayr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+
+ x_method = CV_CONTOURS_MATCH_I1;
+ x_storage = cvCreateMemStorage(0);
+
+ x_bcontourr = NULL;
+ x_minsize = 10*10;
+ x_cdistance = 0.1;
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_hu_compare :: ~pix_opencv_hu_compare()
+{
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgbar);
+ cvReleaseImage(&rgbr);
+ cvReleaseImage(&grayr);
+}
+
+/////////////////////////////////////////////////////////
+// processDualImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_hu_compare :: processRGBA_RGBA(imageStruct &left, imageStruct &right)
+{
+ double dist = 100.0, ndist;
+ int i = 0; // Indicator of cycles.
+ CvSeq *contourl=NULL, *contourlp;
+ CvRect rect;
+ CvMemStorage *mstorage;
+ CvSeq *contourr = NULL;
+ int size;
+
+ if ((left.xsize!=right.xsize) || (left.ysize!=right.ysize) )
+ {
+ post( "pix_opencv_hu_compare : left and right image are not of the same size" );
+ return;
+ }
+
+ if ((this->comp_xsize!=left.xsize)&&(this->comp_ysize!=left.ysize))
+ {
+ this->comp_xsize=left.xsize;
+ this->comp_ysize=left.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgbar);
+ cvReleaseImage(&rgbr);
+ cvReleaseImage(&grayr);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ rgbar = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgbr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ grayr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ }
+
+ memcpy( rgbar->imageData, right.data, right.xsize*right.ysize*4 );
+ cvCvtColor(rgbar, grayr, CV_BGRA2GRAY);
+
+ // calculate the biggest contour
+ try {
+ cvFindContours( grayr, x_storage, &contourr, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ }
+ catch(...) {
+ post( "pix_opencv_hu_compare : error calculating contours" );
+ return;
+ }
+
+ if ( contourr )
+ {
+ size=0;
+ for( ; contourr != 0; contourr = contourr->h_next )
+ {
+ rect = cvContourBoundingRect( contourr, 1);
+ if ( rect.width*rect.height > size && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ x_bcontourr = contourr;
+ size = rect.width*rect.height;
+ }
+ }
+ }
+
+ memcpy( rgba->imageData, left.data, left.xsize*left.ysize*4 );
+ cvCvtColor(rgba, gray, CV_BGRA2GRAY);
+
+ mstorage = cvCreateMemStorage(0);
+
+ cvFindContours( gray, mstorage, &contourl, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+
+ i=0;
+ if ( contourl && x_bcontourr )
+ {
+ contourlp=contourl;
+ for( ; contourlp != 0; contourlp = contourlp->h_next )
+ {
+ rect = cvContourBoundingRect( contourlp, 1);
+ if ( rect.width*rect.height > x_minsize && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ ndist = cvMatchShapes( x_bcontourr, contourlp, x_method, 0 );
+ if ( ndist < dist ) dist = ndist;
+ if ( ndist < x_cdistance )
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(255,255,255), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(255,255,255), CV_RGB(255,255,255), 0, 1, 8, cvPoint(0,0) );
+ SETFLOAT(&rlist[0], i++);
+ SETFLOAT(&rlist[1], rect.x);
+ SETFLOAT(&rlist[2], rect.y);
+ SETFLOAT(&rlist[3], rect.width);
+ SETFLOAT(&rlist[4], rect.height);
+ outlet_list( m_posout, 0, 5, rlist );
+ }
+ else
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(128,128,128), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(128,128,128), CV_RGB(128,128,128), 0, 1, 8, cvPoint(0,0) );
+ }
+ }
+ }
+ }
+
+ if ( dist < 100.00 ) outlet_float( m_dataout, dist );
+
+ cvReleaseMemStorage(&mstorage);
+
+ cvCvtColor(gray, rgba, CV_GRAY2BGR);
+ memcpy( left.data, rgba->imageData, left.xsize*left.ysize*4 );
+
+}
+
+/////////////////////////////////////////////////////////
+// processDualImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_hu_compare :: processRGB_RGB(imageStruct &left, imageStruct &right)
+{
+ double dist = 100.0, ndist;
+ int i = 0; // Indicator of cycles.
+ CvSeq *contourl=NULL, *contourlp;
+ CvRect rect;
+ CvMemStorage *mstorage;
+ CvSeq *contourr = NULL;
+ int size;
+
+ if ((left.xsize!=right.xsize) || (left.ysize!=right.ysize) )
+ {
+ post( "pix_opencv_hu_compare : left and right image are not of the same size" );
+ return;
+ }
+
+ if ((this->comp_xsize!=left.xsize)&&(this->comp_ysize!=left.ysize))
+ {
+ this->comp_xsize=left.xsize;
+ this->comp_ysize=left.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgbar);
+ cvReleaseImage(&rgbr);
+ cvReleaseImage(&grayr);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ rgbar = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgbr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ grayr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ }
+
+ memcpy( rgbr->imageData, right.data, right.xsize*right.ysize*3 );
+ cvCvtColor(rgbr, grayr, CV_BGRA2GRAY);
+
+ // calculate the biggest contour
+ try {
+ cvFindContours( grayr, x_storage, &contourr, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ }
+ catch(...) {
+ post( "pix_opencv_hu_compare : error calculating contours" );
+ return;
+ }
+
+ if ( contourr )
+ {
+ size=0;
+ for( ; contourr != 0; contourr = contourr->h_next )
+ {
+ rect = cvContourBoundingRect( contourr, 1);
+ if ( rect.width*rect.height > size && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ x_bcontourr = contourr;
+ size = rect.width*rect.height;
+ }
+ }
+ }
+
+ memcpy( rgb->imageData, left.data, left.xsize*left.ysize*3 );
+ cvCvtColor(rgb, gray, CV_BGRA2GRAY);
+
+ mstorage = cvCreateMemStorage(0);
+
+ cvFindContours( gray, mstorage, &contourl, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+
+ if ( contourl && x_bcontourr )
+ {
+ contourlp=contourl;
+ for( ; contourlp != 0; contourlp = contourlp->h_next )
+ {
+ rect = cvContourBoundingRect( contourlp, 1);
+ if ( rect.width*rect.height > x_minsize && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ ndist = cvMatchShapes( x_bcontourr, contourlp, x_method, 0 );
+ if ( ndist < dist ) dist = ndist;
+ if ( ndist < x_cdistance )
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(255,255,255), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(255,255,255), CV_RGB(255,255,255), 0, 1, 8, cvPoint(0,0) );
+ }
+ else
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(128,128,128), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(128,128,128), CV_RGB(128,128,128), 0, 1, 8, cvPoint(0,0) );
+ }
+ }
+ }
+ }
+
+ if ( dist < 100.00 ) outlet_float( m_dataout, dist );
+
+ cvReleaseMemStorage(&mstorage);
+
+ cvCvtColor(gray, rgb, CV_GRAY2BGR);
+ memcpy( left.data, rgb->imageData, left.xsize*left.ysize*3 );
+
+}
+
+/////////////////////////////////////////////////////////
+// processDualImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_hu_compare :: processGray_Gray(imageStruct &left, imageStruct &right)
+{
+ double dist = 100.0, ndist;
+ int i = 0; // Indicator of cycles.
+ CvSeq *contourl=NULL, *contourlp;
+ CvRect rect;
+ CvMemStorage *mstorage;
+ CvSeq *contourr = NULL;
+ int size;
+
+ if ((left.xsize!=right.xsize) || (left.ysize!=right.ysize) )
+ {
+ post( "pix_opencv_hu_compare : left and right image are not of the same size" );
+ return;
+ }
+
+ if ((this->comp_xsize!=left.xsize)&&(this->comp_ysize!=left.ysize))
+ {
+ this->comp_xsize=left.xsize;
+ this->comp_ysize=left.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgbar);
+ cvReleaseImage(&rgbr);
+ cvReleaseImage(&grayr);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ rgbar = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgbr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ grayr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ }
+
+ memcpy( grayr->imageData, right.data, right.xsize*right.ysize );
+
+ // calculate the biggest contour
+ try {
+ cvFindContours( grayr, x_storage, &contourr, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ }
+ catch(...) {
+ post( "pix_opencv_hu_compare : error calculating contours" );
+ return;
+ }
+
+ if ( contourr )
+ {
+ size=0;
+ for( ; contourr != 0; contourr = contourr->h_next )
+ {
+ rect = cvContourBoundingRect( contourr, 1);
+ if ( rect.width*rect.height > size && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ x_bcontourr = contourr;
+ size = rect.width*rect.height;
+ }
+ }
+ }
+
+ memcpy( gray->imageData, left.data, left.xsize*left.ysize );
+
+ mstorage = cvCreateMemStorage(0);
+
+ cvFindContours( gray, mstorage, &contourl, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+
+ if ( contourl && x_bcontourr )
+ {
+ contourlp=contourl;
+ for( ; contourlp != 0; contourlp = contourlp->h_next )
+ {
+ rect = cvContourBoundingRect( contourlp, 1);
+ if ( rect.width*rect.height > x_minsize && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ ndist = cvMatchShapes( x_bcontourr, contourlp, x_method, 0 );
+ if ( ndist < dist ) dist = ndist;
+ if ( ndist < x_cdistance )
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(255,255,255), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(255,255,255), CV_RGB(255,255,255), 0, 1, 8, cvPoint(0,0) );
+ }
+ else
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(128,128,128), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(128,128,128), CV_RGB(128,128,128), 0, 1, 8, cvPoint(0,0) );
+ }
+ }
+ }
+ }
+
+ if ( dist < 100.00 ) outlet_float( m_dataout, dist );
+
+ cvReleaseMemStorage(&mstorage);
+
+ memcpy( left.data, gray->imageData, left.xsize*left.ysize );
+
+}
+
+void pix_opencv_hu_compare :: processYUV_YUV(imageStruct &left, imageStruct &right)
+{
+ post( "pix_opencv_hu_compare : YUV colorspace not supported" );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_hu_compare :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_hu_compare::floatMethodMessCallback,
+ gensym("method"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hu_compare::floatMinSizeMessCallback,
+ gensym("minsize"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hu_compare::clearMessCallback,
+ gensym("clear"), A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_hu_compare::floatCriteriaMessCallback,
+ gensym("criteria"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_hu_compare :: floatMethodMessCallback(void *data, t_floatarg method)
+{
+ GetMyClass(data)->floatMethodMess((float)method);
+}
+
+void pix_opencv_hu_compare :: floatMinSizeMessCallback(void *data, t_floatarg minsize)
+{
+ GetMyClass(data)->floatMinSizeMess((float)minsize);
+}
+
+void pix_opencv_hu_compare :: clearMessCallback(void *data)
+{
+ GetMyClass(data)->clearMess();
+}
+
+void pix_opencv_hu_compare :: floatCriteriaMessCallback(void *data, t_floatarg criteria)
+{
+ GetMyClass(data)->floatCriteriaMess((float)criteria);
+}
+
+void pix_opencv_hu_compare :: floatMethodMess(float method)
+{
+ if ((int)method==CV_CONTOURS_MATCH_I1)
+ {
+ post( "pix_opencv_hu_compare : method set to CV_CONTOURS_MATCH_I1" );
+ x_method = (int)method;
+ }
+ if ((int)method==CV_CONTOURS_MATCH_I2)
+ {
+ post( "pix_opencv_hu_compare : method set to CV_CONTOURS_MATCH_I2" );
+ x_method = (int)method;
+ }
+ if ((int)method==CV_CONTOURS_MATCH_I3)
+ {
+ post( "pix_opencv_hu_compare : method set to CV_CONTOURS_MATCH_I3" );
+ x_method = (int)method;
+ }
+}
+
+void pix_opencv_hu_compare :: floatMinSizeMess(float minsize)
+{
+ if ( (int)minsize > 0 )
+ {
+ x_minsize = (int)minsize;
+ }
+}
+
+void pix_opencv_hu_compare :: clearMess(void)
+{
+ x_bcontourr = NULL;
+}
+
+void pix_opencv_hu_compare :: floatCriteriaMess(float criteria)
+{
+ if ( criteria > 0.0 )
+ {
+ x_cdistance = criteria;
+ }
+}
diff --git a/src/pix_opencv_hu_compare.h b/src/pix_opencv_hu_compare.h
new file mode 100644
index 0000000..bc70d58
--- /dev/null
+++ b/src/pix_opencv_hu_compare.h
@@ -0,0 +1,93 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Hu moments comparison used to compare contours
+
+ Copyright (c) 1997-1998 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_HU_COMPARE_H_
+#define INCLUDE_PIX_OPENCV_HU_COMPARE_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/legacy/compat.hpp"
+#endif
+
+#include "Base/GemPixDualObj.h"
+
+/*-----------------------------------------------------------------
+CLASS
+ pix_opencv_hu_compare
+
+ Hu moments comparison used to compare contours
+
+-----------------------------------------------------------------*/
+
+class GEM_EXPORT pix_opencv_hu_compare : public GemPixDualObj
+{
+ CPPEXTERN_HEADER(pix_opencv_hu_compare, GemPixDualObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_hu_compare(int,t_atom*);
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_hu_compare();
+
+ //////////
+ // Do the processing
+ virtual void processRGBA_RGBA(imageStruct &left, imageStruct &right);
+ virtual void processRGB_RGB(imageStruct &left, imageStruct &right);
+ virtual void processYUV_YUV(imageStruct &left, imageStruct &right);
+ virtual void processGray_Gray(imageStruct &left, imageStruct &right);
+
+ //////////
+ // change method used
+ void floatMethodMess(float method);
+ void floatMinSizeMess(float minsize);
+ void clearMess(void);
+ void floatCriteriaMess(float criteria);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ t_outlet *m_dataout;
+ t_outlet *m_posout;
+
+ int x_method;
+ int x_minsize;
+ float x_cdistance;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatMethodMessCallback(void *data, float method);
+ static void floatMinSizeMessCallback(void *data, float minsize);
+ static void clearMessCallback(void *data);
+ static void floatCriteriaMessCallback(void *data, float criteria);
+
+ IplImage *rgba, *rgb, *gray;
+ IplImage *rgbar, *rgbr, *grayr;
+
+ CvMemStorage *x_storage;
+ CvSeq *x_bcontourr;
+
+ t_atom rlist[5];
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_hu_moments.cc b/src/pix_opencv_hu_moments.cc
new file mode 100644
index 0000000..6665107
--- /dev/null
+++ b/src/pix_opencv_hu_moments.cc
@@ -0,0 +1,207 @@
+////////////////////////////////////////////////////////
+//
+// 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_hu_moments.h"
+
+CPPEXTERN_NEW(pix_opencv_hu_moments)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_hu_moments
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_hu_moments :: pix_opencv_hu_moments()
+{
+ m_dataout = outlet_new(this->x_obj, &s_anything);
+
+ comp_xsize = 320;
+ comp_ysize = 240;
+
+ x_binary = 0;
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_hu_moments :: ~pix_opencv_hu_moments()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_hu_moments :: processRGBAImage(imageStruct &image)
+{
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgba))
+ {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if ( rgba )
+ {
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ }
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 1);
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ cvCvtColor(rgba, gray, CV_BGRA2GRAY);
+
+ cvMoments( gray, &x_moments, x_binary );
+ cvGetHuMoments( &x_moments, &x_humoments );
+
+ SETFLOAT(&rlist[0], x_humoments.hu1);
+ SETFLOAT(&rlist[1], x_humoments.hu2);
+ SETFLOAT(&rlist[2], x_humoments.hu3);
+ SETFLOAT(&rlist[3], x_humoments.hu4);
+ SETFLOAT(&rlist[4], x_humoments.hu5);
+ SETFLOAT(&rlist[5], x_humoments.hu6);
+ SETFLOAT(&rlist[6], x_humoments.hu7);
+
+ outlet_list( m_dataout, 0, 7, rlist );
+}
+
+void pix_opencv_hu_moments :: processRGBImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if ( rgb )
+ {
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ }
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+
+ }
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+ cvCvtColor(rgb, gray, CV_RGB2GRAY);
+
+ cvMoments( gray, &x_moments, x_binary );
+ cvGetHuMoments( &x_moments, &x_humoments );
+
+ SETFLOAT(&rlist[0], x_humoments.hu1);
+ SETFLOAT(&rlist[1], x_humoments.hu2);
+ SETFLOAT(&rlist[2], x_humoments.hu3);
+ SETFLOAT(&rlist[3], x_humoments.hu4);
+ SETFLOAT(&rlist[4], x_humoments.hu5);
+ SETFLOAT(&rlist[5], x_humoments.hu6);
+ SETFLOAT(&rlist[6], x_humoments.hu7);
+
+ outlet_list( m_dataout, 0, 7, rlist );
+}
+
+void pix_opencv_hu_moments :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_hu_moments : yuv format not supported" );
+}
+
+void pix_opencv_hu_moments :: processGrayImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if ( rgb )
+ {
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ }
+
+ //create the orig image with new size
+ rgba = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+
+ }
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+
+ cvMoments( gray, &x_moments, x_binary );
+ cvGetHuMoments( &x_moments, &x_humoments );
+
+ SETFLOAT(&rlist[0], x_humoments.hu1);
+ SETFLOAT(&rlist[1], x_humoments.hu2);
+ SETFLOAT(&rlist[2], x_humoments.hu3);
+ SETFLOAT(&rlist[3], x_humoments.hu4);
+ SETFLOAT(&rlist[4], x_humoments.hu5);
+ SETFLOAT(&rlist[5], x_humoments.hu6);
+ SETFLOAT(&rlist[6], x_humoments.hu7);
+
+ outlet_list( m_dataout, 0, 7, rlist );
+}
+
+/////////////////////////////////////////////////////////
+// floatThreshMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_hu_moments :: floatBinaryMess (float binary)
+{
+ if ( ((int)binary==1) || ((int)binary==0) ) x_binary = (int)binary;
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_hu_moments :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_hu_moments::floatBinaryMessCallback,
+ gensym("binary"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_hu_moments :: floatBinaryMessCallback(void *data, t_floatarg binary)
+{
+ GetMyClass(data)->floatBinaryMess((float)binary);
+}
+
diff --git a/src/pix_opencv_hu_moments.h b/src/pix_opencv_hu_moments.h
new file mode 100644
index 0000000..01cd0f9
--- /dev/null
+++ b/src/pix_opencv_hu_moments.h
@@ -0,0 +1,86 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Hu moments calculator object
+
+ 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_HU_MOMENTS_H_
+#define INCLUDE_PIX_OPENCV_HU_MOMENTS_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_hu_moments
+
+ Hu moments calculator object
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_hu_moments : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_hu_moments, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_hu_moments();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_hu_moments();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void floatBinaryMess(float binary);
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ int x_binary;
+ t_outlet *m_dataout;
+ t_atom rlist[7];
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatBinaryMessCallback(void *data, t_floatarg binary);
+
+ /////////
+ // IplImage needed
+ IplImage *rgba, *rgb, *gray;
+ CvMoments x_moments;
+ CvHuMoments x_humoments;
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_knear.cc b/src/pix_opencv_knear.cc
new file mode 100644
index 0000000..0d8a98c
--- /dev/null
+++ b/src/pix_opencv_knear.cc
@@ -0,0 +1,502 @@
+////////////////////////////////////////////////////////
+//
+// 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_knear.h"
+
+
+CPPEXTERN_NEW_WITH_TWO_ARGS(pix_opencv_knear, t_symbol *, A_DEFSYM, t_floatarg, A_DEFFLOAT )
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_knear
+//
+/////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////
+//
+// Find the min box. The min box respect original aspect ratio image
+// The image is a binary data and background is white.
+//
+/////////////////////////////////////////////////////////
+
+
+void pix_opencv_knear :: findX(IplImage* imgSrc,int* min, int* max)
+{
+ int i;
+ int minFound=0;
+ CvMat data;
+ CvScalar maxVal=cvRealScalar(imgSrc->width * 255);
+ CvScalar val=cvRealScalar(0);
+
+ // for each col sum, if sum < width*255 then we find the min
+ // then continue to end to search the max, if sum< width*255 then is new max
+ for (i=0; i< imgSrc->width; i++)
+ {
+ cvGetCol(imgSrc, &data, i);
+ val= cvSum(&data);
+ if(val.val[0] < maxVal.val[0])
+ {
+ *max= i;
+ if(!minFound)
+ {
+ *min= i;
+ minFound= 1;
+ }
+ }
+
+ }
+}
+
+void pix_opencv_knear :: findY(IplImage* imgSrc,int* min, int* max)
+{
+ int i;
+ int minFound=0;
+ CvMat data;
+ CvScalar maxVal=cvRealScalar(imgSrc->width * 255);
+ CvScalar val=cvRealScalar(0);
+
+ // for each col sum, if sum < width*255 then we find the min
+ // then continue to end to search the max, if sum< width*255 then is new max
+ for (i=0; i< imgSrc->height; i++)
+ {
+ cvGetRow(imgSrc, &data, i);
+ val= cvSum(&data);
+ if(val.val[0] < maxVal.val[0])
+ {
+ *max=i;
+ if(!minFound)
+ {
+ *min= i;
+ minFound= 1;
+ }
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////
+//
+// Find the bounding box.
+//
+//
+/////////////////////////////////////////////////////////
+
+CvRect pix_opencv_knear :: findBB(IplImage* imgSrc)
+{
+ CvRect aux;
+ int xmin, xmax, ymin, ymax;
+ xmin=xmax=ymin=ymax=0;
+
+ this->findX(imgSrc, &xmin, &xmax);
+ this->findY(imgSrc, &ymin, &ymax);
+
+ aux=cvRect(xmin, ymin, xmax-xmin, ymax-ymin);
+
+ return aux;
+}
+
+IplImage pix_opencv_knear :: preprocessing(IplImage* imgSrc,int new_width, int new_height)
+{
+ IplImage* result;
+ IplImage* scaledResult;
+
+ CvMat data;
+ CvMat dataA;
+ CvRect bb;//bounding box
+
+ // find bounding box
+ bb=this->findBB(imgSrc);
+
+ if ( ( bb.width == 0 ) || ( bb.height == 0 ) )
+ {
+ bb.x = 0;
+ bb.y = 0;
+ bb.width = imgSrc->width;
+ bb.height = imgSrc->height;
+ }
+
+ // get bounding box data and no with aspect ratio, the x and y can be corrupted
+ cvGetSubRect(imgSrc, &data, cvRect(bb.x, bb.y, bb.width, bb.height));
+ // create image with this data with width and height with aspect ratio 1
+ // then we get highest size betwen width and height of our bounding box
+ int size=(bb.width>bb.height)?bb.width:bb.height;
+ result=cvCreateImage( cvSize( size, size ), 8, 1 );
+ cvSet(result,CV_RGB(255,255,255),NULL);
+ // copy de data in center of image
+ int x=(int)floor((float)(size-bb.width)/2.0f);
+ int y=(int)floor((float)(size-bb.height)/2.0f);
+ cvGetSubRect(result, &dataA, cvRect(x,y,bb.width, bb.height));
+ cvCopy(&data, &dataA, NULL);
+ // scale result
+ scaledResult=cvCreateImage( cvSize( new_width, new_height ), 8, 1 );
+ cvResize(result, scaledResult, CV_INTER_NN);
+
+ // return processed data
+ return *scaledResult;
+
+}
+
+void pix_opencv_knear :: load_patterns(void)
+{
+ IplImage* src_image;
+ IplImage prs_image;
+ CvMat row,data;
+ char file[255];
+ int i=0,j;
+ CvMat row_header, *row1;
+
+ this->x_rsamples = 0;
+
+ for( j = 0; j< this->x_nsamples; j++)
+ {
+
+ // load fileath
+
+ if ( x_filepath[0] == '/' ){ // absolute path
+ sprintf(file,"%s/%03d.png",this->x_filepath, j);
+ } else { // relative path
+ std::string absolutePath = localPath + x_filepath;
+ sprintf(file,"%s/%03d.png",absolutePath.c_str(), j);
+ }
+ src_image = cvLoadImage(file,0);
+ if(!src_image)
+ {
+ post("pix_opencv_knear : error: couldn't load image %s\n", file);
+ continue;
+ }
+ if ( ( this->x_pwidth == -1 ) || ( this->x_pheight == -1 ) )
+ {
+ this->x_pwidth = src_image->width;
+ this->x_pheight = src_image->height;
+ // post( "pix_opencv_knear : loaded : %s (%dx%d)", file, src_image->width, src_image->height);
+ this->x_rsamples++;
+ }
+ else if ( ( src_image->width != this->x_pwidth ) || ( src_image->height != this->x_pheight ) )
+ {
+ post( "pix_opencv_knear : error : %s (%dx%d) : wrong size ( should be %dx%d )", file, src_image->width, src_image->height, this->x_pwidth, this->x_pheight);
+ continue;
+ }
+ else
+ {
+ // post( "pix_opencv_knear : loaded : %s (%dx%d)", file, src_image->width, src_image->height);
+ this->x_rsamples++;
+ }
+
+ // process file
+ prs_image = this->preprocessing(src_image, this->x_pwidth, this->x_pheight);
+ // post( "pix_opencv_knear : preprocessed : %s (%dx%d)", file, this->x_pwidth, this->x_pheight);
+
+ if ( ( this->trainData == NULL ) || ( this->trainClasses == NULL ))
+ {
+ this->trainData = cvCreateMat(this->x_nsamples, this->x_pwidth*this->x_pheight, CV_32FC1);
+ this->trainClasses = cvCreateMat(this->x_nsamples, 1, CV_32FC1);
+ }
+
+ // set class label
+ cvGetRow(this->trainClasses, &row, j);
+ cvSet(&row, cvRealScalar(i), NULL);
+ // set data
+ cvGetRow(this->trainData, &row, j);
+
+ IplImage* img = cvCreateImage( cvSize( this->x_pwidth, this->x_pheight ), IPL_DEPTH_32F, 1 );
+ // convert 8 bits image to 32 float image
+ cvConvertScale(&prs_image, img, 0.0039215, 0);
+
+ cvGetSubRect(img, &data, cvRect( 0, 0, this->x_pwidth, this->x_pheight) );
+
+ // convert data matrix sizexsize to vecor
+ row1 = cvReshape( &data, &row_header, 0, 1 );
+ cvCopy(row1, &row, NULL);
+ cvReleaseImage( &img );
+ }
+
+ // create the classifier
+ post( "pix_opencv_knear : loaded : %d samples from %s", this->x_rsamples, this->x_filepath);
+ if ( this->x_rsamples == this->x_nsamples )
+ {
+ this->knn=new CvKNearest( this->trainData, this->trainClasses, 0, false, this->x_nsamples );
+ this->x_nearest=cvCreateMat(1,this->x_nsamples,CV_32FC1);
+ this->x_dist=cvCreateMat(1,this->x_nsamples,CV_32FC1);
+ }
+}
+
+
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_knear :: pix_opencv_knear(t_symbol *path, t_floatarg nsamples)
+{
+ m_dataout = outlet_new(this->x_obj, &s_anything);
+
+ comp_xsize = 320;
+ comp_ysize = 240;
+
+ 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 );
+
+ x_filepath = ( char * ) getbytes( 1024 );
+ sprintf( x_filepath, "%s", path->s_name );
+ x_nsamples = (int)nsamples;
+
+ x_classify = 0;
+ x_pwidth = -1;
+ x_pheight = -1;
+
+ trainData = NULL;
+ trainClasses = NULL;
+
+ t_canvas* canvas = canvas_getcurrent();
+ localPath = std::string(canvas_getdir(canvas)->s_name) + "/";
+
+ try {
+ this->load_patterns();
+ } catch(...) {
+ error( "can't load patterns" );
+ return;
+ }
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_knear :: ~pix_opencv_knear()
+{
+ // destroy cv structures
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+ cvReleaseMat( &trainData );
+ cvReleaseMat( &trainClasses );
+
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_knear :: processRGBAImage(imageStruct &image)
+{
+ int i;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ // destroy cv_images to clean memory
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+
+ // create cv_images
+ this->rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ this->rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ this->grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ cvCvtColor(rgba, grey, CV_BGRA2GRAY);
+
+ if ( this->x_classify )
+ {
+ IplImage prs_image;
+ CvMat row_header, *row1, odata;
+
+ // post( "pix_opencv_knear : size : (%dx%d)", this->x_pwidth, this->x_pheight);
+
+ // process file
+ prs_image = this->preprocessing(this->grey, this->x_pwidth, this->x_pheight);
+
+ //Set data
+ IplImage* img32 = cvCreateImage( cvSize( this->x_pwidth, this->x_pheight ), IPL_DEPTH_32F, 1 );
+ cvConvertScale(&prs_image, img32, 0.0039215, 0);
+ cvGetSubRect(img32, &odata, cvRect(0,0, this->x_pwidth, this->x_pheight));
+ row1 = cvReshape( &odata, &row_header, 0, 1 );
+
+ this->knn->find_nearest(row1,this->x_nsamples,0,0,this->x_nearest,this->x_dist);
+ for ( i=0; i<this->x_nsamples; i++ )
+ {
+ // post( "pix_opencv_knear : distance : %f", this->x_dist->data.fl[i] );
+ }
+ outlet_float(this->m_dataout, this->x_dist->data.fl[0]);
+
+ cvReleaseImage( &img32 );
+ this->x_classify = 0;
+ }
+
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_knear :: processRGBImage(imageStruct &image)
+{
+ int i;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ // destroy cv_images to clean memory
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+
+ // create cv_images
+ this->rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ this->rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ this->grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ }
+
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+ cvCvtColor(rgb, grey, CV_BGR2GRAY);
+
+ if ( this->x_classify )
+ {
+ IplImage prs_image;
+ CvMat row_header, *row1, odata;
+
+ // post( "pix_opencv_knear : size : (%dx%d)", this->x_pwidth, this->x_pheight);
+
+ // process file
+ prs_image = this->preprocessing(this->grey, this->x_pwidth, this->x_pheight);
+
+ //Set data
+ IplImage* img32 = cvCreateImage( cvSize( this->x_pwidth, this->x_pheight ), IPL_DEPTH_32F, 1 );
+ cvConvertScale(&prs_image, img32, 0.0039215, 0);
+ cvGetSubRect(img32, &odata, cvRect(0,0, this->x_pwidth, this->x_pheight));
+ row1 = cvReshape( &odata, &row_header, 0, 1 );
+
+ this->knn->find_nearest(row1,this->x_nsamples,0,0,this->x_nearest,this->x_dist);
+ for ( i=0; i<this->x_nsamples; i++ )
+ {
+ // post( "pix_opencv_knear : distance : %f", this->x_dist->data.fl[i] );
+ }
+ outlet_float(this->m_dataout, this->x_dist->data.fl[0]);
+
+ cvReleaseImage( &img32 );
+ this->x_classify = 0;
+ }
+
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_knear :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_knear : yuv format not supported" );
+}
+
+void pix_opencv_knear :: processGrayImage(imageStruct &image)
+{
+ int i;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ // destroy cv_images to clean memory
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &grey );
+
+ // create cv_images
+ this->rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ this->rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ this->grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ }
+
+ memcpy( grey->imageData, image.data, image.xsize*image.ysize*1 );
+
+ if ( this->x_classify )
+ {
+ IplImage prs_image;
+ CvMat row_header, *row1, odata;
+
+ // post( "pix_opencv_knear : size : (%dx%d)", this->x_pwidth, this->x_pheight);
+
+ // process file
+ prs_image = this->preprocessing(this->grey, this->x_pwidth, this->x_pheight);
+
+ //Set data
+ IplImage* img32 = cvCreateImage( cvSize( this->x_pwidth, this->x_pheight ), IPL_DEPTH_32F, 1 );
+ cvConvertScale(&prs_image, img32, 0.0039215, 0);
+ cvGetSubRect(img32, &odata, cvRect(0,0, this->x_pwidth, this->x_pheight));
+ row1 = cvReshape( &odata, &row_header, 0, 1 );
+
+ this->knn->find_nearest(row1,this->x_nsamples,0,0,this->x_nearest,this->x_dist);
+ for ( i=0; i<this->x_nsamples; i++ )
+ {
+ // post( "pix_opencv_knear : distance : %f", this->x_dist->data.fl[i] );
+ }
+ outlet_float(this->m_dataout, this->x_dist->data.fl[0]);
+
+ cvReleaseImage( &img32 );
+ this->x_classify = 0;
+ }
+
+ memcpy( image.data, grey->imageData, image.xsize*image.ysize*1 );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_knear :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_knear::bangMessCallback,
+ gensym("bang"), A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_knear::loadMessCallback,
+ gensym("load"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+}
+
+void pix_opencv_knear :: bangMessCallback(void *data)
+{
+ if ( GetMyClass(data)->trainData == NULL )
+ {
+ ::post( "pix_opencv_knear : no patterns loaded : cannot process" );
+ return;
+ }
+ GetMyClass(data)->x_classify=1;
+}
+
+void pix_opencv_knear :: loadMessCallback(void *data, t_symbol *path, t_floatarg nsamples)
+{
+ if ( (int) nsamples <= 0 )
+ {
+ ::post( "pix_opencv_knear : wrong number of samples : %d", nsamples );
+ return;
+ }
+ else
+ {
+ GetMyClass(data)->x_nsamples = (int)nsamples;
+ GetMyClass(data)->x_rsamples = 0;
+ cvReleaseMat( &GetMyClass(data)->trainData );
+ cvReleaseMat( &GetMyClass(data)->trainClasses );
+ GetMyClass(data)->trainData = NULL;
+ GetMyClass(data)->trainClasses = NULL;
+ }
+ strcpy( GetMyClass(data)->x_filepath, path->s_name );
+ GetMyClass(data)->load_patterns();
+}
diff --git a/src/pix_opencv_knear.h b/src/pix_opencv_knear.h
new file mode 100644
index 0000000..63ddcaa
--- /dev/null
+++ b/src/pix_opencv_knear.h
@@ -0,0 +1,108 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Simple distance classifier
+
+ 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_KNEAR_H_
+#define INCLUDE_PIX_OPENCV_KNEAR_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/highgui/highgui_c.h"
+#include "opencv2/ml/ml.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+#include <string>
+#include <stdio.h>
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+
+ pix_opencv_knear : OCR like pattern recognition
+ based on basic OCR with Open CV tutorial
+ by damiles : http://blog.damiles.com/?p=93
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_knear : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_knear, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_knear(t_symbol *path, t_floatarg nsamples);
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_knear();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ void findX(IplImage* imgSrc,int* min, int* max);
+ void findY(IplImage* imgSrc,int* min, int* max);
+ CvRect findBB(IplImage* imgSrc);
+ IplImage preprocessing(IplImage* imgSrc,int new_width, int new_height);
+ void load_patterns(void);
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+
+ private:
+
+ //////////
+ // Static member functions
+ static void bangMessCallback(void *data);
+ static void loadMessCallback(void *data, t_symbol *path, t_floatarg nsamples);
+
+ // internal data
+ t_outlet *m_dataout;
+ int x_classify;
+
+ // open cv classifier data
+ char *x_filepath;
+ int x_nsamples;
+ int x_rsamples;
+ CvMat *trainData;
+ CvMat *trainClasses;
+ CvMat *x_nearest;
+ CvMat *x_dist;
+ int x_pwidth;
+ int x_pheight;
+ CvKNearest *knn;
+
+ std::string localPath;
+
+ // The output and temporary images
+ IplImage *rgba, *rgb, *grey;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_laplace.cc b/src/pix_opencv_laplace.cc
new file mode 100644
index 0000000..163cd63
--- /dev/null
+++ b/src/pix_opencv_laplace.cc
@@ -0,0 +1,228 @@
+////////////////////////////////////////////////////////
+//
+// 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_laplace.h"
+
+CPPEXTERN_NEW(pix_opencv_laplace)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_laplace
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_laplace :: pix_opencv_laplace()
+{
+ int i;
+
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("ft1"));
+
+ aperture_size = 3;
+ comp_xsize = 0;
+ comp_ysize = 0;
+
+ rgb = NULL;
+ rgba = NULL;
+ grey = NULL;
+ laplace = NULL;
+ colorlaplace = NULL;
+ for (i=0; i<3; i++) planes[i] = NULL;
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_laplace :: ~pix_opencv_laplace()
+{
+ int i;
+ //Destroy cv_images to clean memory
+ for( i = 0; i < 3; i++ )
+ cvReleaseImage( &planes[i] );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &laplace );
+ cvReleaseImage( &colorlaplace );
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_laplace :: processRGBAImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgba)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ for( i = 0; i < 3; i++ )
+ cvReleaseImage( &planes[i] );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &grey );
+ cvReleaseImage( &laplace );
+ cvReleaseImage( &colorlaplace );
+
+ //Create cv_images
+ for( i = 0; i < 3; i++ )
+ planes[i] = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 1 );
+ laplace = cvCreateImage( cvSize(image.xsize, image.ysize), IPL_DEPTH_16S, 1 );
+ colorlaplace = cvCreateImage( cvSize(image.xsize,image.ysize), 8, 3 );
+ rgb = cvCreateImage( cvSize(image.xsize,image.ysize), 8, 3 );
+ rgba = cvCreateImage( cvSize(image.xsize,image.ysize), 8, 4 );
+ grey = cvCreateImage( cvSize(image.xsize,image.ysize), 8, 1 );
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+
+ cvCvtColor( rgba, rgb, CV_RGBA2RGB);
+
+ cvCvtPixToPlane( rgb, planes[0], planes[1], planes[2], 0 );
+ for( i = 0; i < 3; i++ )
+ {
+ cvLaplace( planes[i], laplace, aperture_size );
+ cvConvertScaleAbs( laplace, planes[i], 1, 0 );
+ }
+ cvCvtPlaneToPix( planes[0], planes[1], planes[2], 0, colorlaplace );
+ colorlaplace->origin = rgb->origin;
+
+ cvCvtColor( colorlaplace, rgba, CV_RGB2RGBA);
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_laplace :: processRGBImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ for( i = 0; i < 3; i++ )
+ cvReleaseImage( &planes[i] );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &laplace );
+ cvReleaseImage( &colorlaplace );
+
+ //Create cv_images
+ for( i = 0; i < 3; i++ )
+ planes[i] = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 1 );
+ laplace = cvCreateImage( cvSize(image.xsize, image.ysize), IPL_DEPTH_16S, 1 );
+ colorlaplace = cvCreateImage( cvSize(image.xsize,image.ysize), 8, 3 );
+ rgb = cvCreateImage( cvSize(image.xsize,image.ysize), 8, 3 );
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ cvCvtPixToPlane( rgb, planes[0], planes[1], planes[2], 0 );
+ for( i = 0; i < 3; i++ )
+ {
+ cvLaplace( planes[i], laplace, 3 );
+ cvConvertScaleAbs( laplace, planes[i], 1, 0 );
+ }
+ cvCvtPlaneToPix( planes[0], planes[1], planes[2], 0, colorlaplace );
+ colorlaplace->origin = rgb->origin;
+
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, colorlaplace->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_laplace :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_laplace : yuv format not supported" );
+}
+
+void pix_opencv_laplace :: processGrayImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!grey)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ for( i = 0; i < 3; i++ )
+ cvReleaseImage( &planes[i] );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &laplace );
+ cvReleaseImage( &colorlaplace );
+ cvReleaseImage( &grey );
+
+ //Create cv_images
+ for( i = 0; i < 3; i++ )
+ planes[i] = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 1 );
+ laplace = cvCreateImage( cvSize(image.xsize, image.ysize), IPL_DEPTH_16S, 1 );
+ colorlaplace = cvCreateImage( cvSize(image.xsize,image.ysize), 8, 3 );
+ rgb = cvCreateImage( cvSize(image.xsize,image.ysize), 8, 3 );
+ grey = cvCreateImage( cvSize(image.xsize,image.ysize), 8, 1 );
+ }
+ memcpy( grey->imageData, image.data, image.xsize*image.ysize );
+
+ cvCvtColor( grey, rgb, CV_GRAY2RGB);
+
+ cvCvtPixToPlane( rgb, planes[0], planes[1], planes[2], 0 );
+ for( i = 0; i < 3; i++ )
+ {
+ cvLaplace( planes[i], laplace, 3 );
+ cvConvertScaleAbs( laplace, planes[i], 1, 0 );
+ }
+ cvCvtPlaneToPix( planes[0], planes[1], planes[2], 0, colorlaplace );
+ colorlaplace->origin = rgb->origin;
+
+
+ cvCvtColor( colorlaplace, grey, CV_RGB2GRAY);
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, grey->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// floatApertureMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_laplace :: floatApertureMess (float aperture_size)
+{
+ if ((aperture_size==1)||(aperture_size==3)||(aperture_size==5)||(aperture_size==7)) this->aperture_size = (int)aperture_size;
+ else post("aperture size out of range ... must be 1,3,5 or 7");
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_laplace :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_laplace::floatApertureMessCallback,
+ gensym("ft1"), A_FLOAT, A_NULL);
+}
+void pix_opencv_laplace :: floatApertureMessCallback(void *data, t_floatarg aperture_size)
+{
+ GetMyClass(data)->floatApertureMess((float)aperture_size);
+}
diff --git a/src/pix_opencv_laplace.h b/src/pix_opencv_laplace.h
new file mode 100644
index 0000000..5d60275
--- /dev/null
+++ b/src/pix_opencv_laplace.h
@@ -0,0 +1,83 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Laplace transform / edge detection
+
+ 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_LAPLACE_H_
+#define INCLUDE_PIX_OPENCV_LAPLACE_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/legacy/compat.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_laplace
+
+ Laplace transform / edge detection
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_laplace : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_laplace, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_laplace();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_laplace();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new aperture_size
+ void floatApertureMess(float aperture_size);
+ // The new aperture size
+ int aperture_size;
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatApertureMessCallback(void *data, t_floatarg aperture_size);
+
+ /////////
+ // IplImage needed
+ IplImage *rgb, *rgba, *grey, *laplace, *colorlaplace, *planes[3];
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_lk.cc b/src/pix_opencv_lk.cc
new file mode 100644
index 0000000..8a9fe2e
--- /dev/null
+++ b/src/pix_opencv_lk.cc
@@ -0,0 +1,1170 @@
+
+//
+// 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_lk.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_lk)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_lk
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_lk :: pix_opencv_lk()
+{
+ int i;
+
+ comp_xsize=320;
+ comp_ysize=240;
+
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("winsize"));
+
+ m_dataout = outlet_new(this->x_obj, &s_anything);
+ win_size = 10;
+
+ points[0] = 0;
+ points[1] = 0;
+ status = 0;
+ count = 0;
+ need_to_init = 1;
+ night_mode = 0;
+ flags = 0;
+ add_remove_pt = 0;
+ quality = 0.1;
+ min_distance = 10;
+ maxmove = 20;
+ markall = 0;
+ ftolerance = 5;
+ delaunay = -1;
+ threshold = -1;
+
+ for ( i=0; i<MAX_MARKERS; i++ )
+ {
+ x_xmark[i] = -1;
+ x_ymark[i] = -1;
+ }
+
+ // initialize font
+ cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 0, 1, 8 );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ orgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ ogray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ pyramid = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_pyramid = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ points[0] = (CvPoint2D32f*)cvAlloc(MAX_COUNT*sizeof(points[0][0]));
+ points[1] = (CvPoint2D32f*)cvAlloc(MAX_COUNT*sizeof(points[0][0]));
+ status = (char*)cvAlloc(MAX_COUNT);
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_lk :: ~pix_opencv_lk()
+{
+ // Destroy cv_images
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &orgb );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &ogray );
+ cvReleaseImage( &prev_gray );
+ cvReleaseImage( &pyramid );
+ cvReleaseImage( &prev_pyramid );
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_lk :: processRGBAImage(imageStruct &image)
+{
+ int i, k;
+ int im, oi;
+ int marked;
+ float dist, odist;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &orgb );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &ogray );
+ cvReleaseImage( &prev_gray );
+ cvReleaseImage( &pyramid );
+ cvReleaseImage( &prev_pyramid );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ orgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ ogray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ pyramid = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_pyramid = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ points[0] = (CvPoint2D32f*)cvAlloc(MAX_COUNT*sizeof(points[0][0]));
+ points[1] = (CvPoint2D32f*)cvAlloc(MAX_COUNT*sizeof(points[0][0]));
+ status = (char*)cvAlloc(MAX_COUNT);
+
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ cvCvtColor(rgba, rgb, CV_BGRA2RGB);
+ cvCvtColor(rgba, orgb, CV_BGRA2RGB);
+ cvCvtColor(rgba, gray, CV_BGRA2GRAY);
+
+ if( night_mode )
+ cvZero( rgb );
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ x_found[im]--;
+ }
+
+ if ( delaunay >= 0 )
+ {
+ // init data structures for the delaunay
+ x_fullrect.x = -comp_xsize/2;
+ x_fullrect.y = -comp_ysize/2;
+ x_fullrect.width = 2*comp_xsize;
+ x_fullrect.height = 2*comp_ysize;
+
+ x_storage = cvCreateMemStorage(0);
+ x_subdiv = cvCreateSubdiv2D( CV_SEQ_KIND_SUBDIV2D, sizeof(*x_subdiv),
+ sizeof(CvSubdiv2DPoint),
+ sizeof(CvQuadEdge2D),
+ x_storage );
+ cvInitSubdivDelaunay2D( x_subdiv, x_fullrect );
+ }
+
+ if( need_to_init )
+ {
+ /* automatic initialization */
+ IplImage* eig = cvCreateImage( cvSize(gray->width,gray->height), 32, 1 );
+ IplImage* temp = cvCreateImage( cvSize(gray->width,gray->height), 32, 1 );
+
+ count = MAX_COUNT;
+ cvGoodFeaturesToTrack( gray, eig, temp, points[1], &count,
+ quality, min_distance, 0, 3, 0, 0.04 );
+ // cvFindCornerSubPix( gray, points[1], count,
+ // cvSize(win_size,win_size), cvSize(-1,-1),
+ // cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));
+ cvReleaseImage( &eig );
+ cvReleaseImage( &temp );
+
+ add_remove_pt = 0;
+ }
+ else if( count > 0 )
+ {
+ cvCalcOpticalFlowPyrLK( prev_gray, gray, prev_pyramid, pyramid,
+ points[0], points[1], count, cvSize(win_size,win_size), 3, status, 0,
+ cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03), flags );
+ flags |= CV_LKFLOW_PYR_A_READY;
+ for( i = k = 0; i < count; i++ )
+ {
+ if( add_remove_pt )
+ {
+ double dx = pt.x - points[1][i].x;
+ double dy = pt.y - points[1][i].y;
+
+ if( dx*dx + dy*dy <= 25 )
+ {
+ add_remove_pt = 0;
+ continue;
+ }
+ }
+
+ if( !status[i] )
+ continue;
+
+ points[1][k++] = points[1][i];
+ if ( delaunay == 0 ) // add all the points
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, points[1][i] );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+ // only add points included in (color-threshold)<p<(color+treshold)
+ if ( ( delaunay > 0 ) && ( x_xmark[delaunay-1] != -1 ) )
+ {
+ int px = cvPointFrom32f(points[1][i]).x;
+ int py = cvPointFrom32f(points[1][i]).y;
+ int ppx, ppy;
+
+ // eight connected pixels
+ for ( ppx=px-1; ppx<=px+1; ppx++ )
+ {
+ for ( ppy=py-1; ppy<=py+1; ppy++ )
+ {
+ if ( ( ppx < 0 ) || ( ppx >= comp_xsize ) ) continue;
+ if ( ( ppy < 0 ) || ( ppy >= comp_ysize ) ) continue;
+
+ uchar red = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3];
+ uchar green = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3+1];
+ uchar blue = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3+2];
+
+ uchar pred = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[delaunay-1]))[x_ymark[delaunay-1]*3];
+ uchar pgreen = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[delaunay-1]))[x_ymark[delaunay-1]*3+1];
+ uchar pblue = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[delaunay-1]))[x_ymark[delaunay-1]*3+2];
+
+ int diff = abs(red-pred) + abs(green-pgreen) + abs(blue-pblue);
+
+ // post( "pix_opencv_lk : point (%d,%d,%d) : diff : %d threshold : %d", blue, green, red, diff, threshold );
+
+ if ( diff < threshold )
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, points[1][i] );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+ }
+ }
+ }
+
+ cvCircle( rgb, cvPointFrom32f(points[1][i]), 3, CV_RGB(0,255,0), -1, 8,0);
+
+ marked=0;
+ oi=-1;
+ dist=(comp_xsize>comp_ysize)?comp_xsize:comp_ysize;
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+
+ if ( x_xmark[im] == -1 ) continue; // i don't see the point
+
+ odist=sqrt( pow( points[1][i].x - x_xmark[im], 2 ) + pow( points[1][i].y - x_ymark[im], 2 ) );
+
+ // search for the closest point
+ if ( odist <= maxmove )
+ {
+ if ( odist < dist )
+ {
+ dist = odist;
+ marked=1;
+ oi=im;
+ }
+ }
+ }
+
+ if ( oi != -1 )
+ {
+ char tindex[4];
+ sprintf( tindex, "%d", oi );
+ cvPutText( rgb, tindex, cvPointFrom32f(points[1][i]), &font, CV_RGB(255,255,255));
+ x_xmark[oi]=(int)points[1][i].x;
+ x_ymark[oi]=(int)points[1][i].y;
+ x_found[oi]=ftolerance;
+ SETFLOAT(&x_list[0], oi);
+ SETFLOAT(&x_list[1], x_xmark[oi]);
+ SETFLOAT(&x_list[2], x_ymark[oi]);
+ outlet_list( m_dataout, 0, 3, x_list );
+ }
+
+ if ( markall && !marked )
+ {
+ for ( im=0; im<MAX_MARKERS; im++)
+ {
+ if ( x_xmark[im] == -1 )
+ {
+ x_xmark[im]=points[1][i].x;
+ x_ymark[im]=points[1][i].y;
+ x_found[im]=ftolerance;
+ break;
+ }
+ }
+ }
+ }
+ count = k;
+ }
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( (x_xmark[im] != -1.0 ) && !x_found[im] )
+ {
+ x_xmark[im]=-1.0;
+ x_ymark[im]=-1.0;
+ SETFLOAT(&x_list[0], im+1);
+ SETFLOAT(&x_list[1], x_xmark[im]);
+ SETFLOAT(&x_list[2], x_ymark[im]);
+ // send a lost point message to the patch
+ outlet_list( m_dataout, 0, 3, x_list );
+ post( "pix_opencv_lk : lost point %d", im+1 );
+ }
+ }
+
+ if( add_remove_pt && count < MAX_COUNT )
+ {
+ points[1][count++] = cvPointTo32f(pt);
+ cvFindCornerSubPix( gray, points[1] + count - 1, 1,
+ cvSize(win_size,win_size), cvSize(-1,-1),
+ cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));
+ add_remove_pt = 0;
+ }
+
+ // draw the delaunay
+ if ( delaunay >= 0 )
+ {
+ CvSeqReader reader;
+ int i, total = x_subdiv->edges->total;
+ int elem_size = x_subdiv->edges->elem_size;
+
+ cvStartReadSeq( (CvSeq*)(x_subdiv->edges), &reader, 0 );
+
+ for( i = 0; i < total; i++ )
+ {
+ CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);
+ CvSubdiv2DPoint* org_pt;
+ CvSubdiv2DPoint* dst_pt;
+ CvPoint2D32f org;
+ CvPoint2D32f dst;
+ CvPoint iorg, idst;
+
+ if( CV_IS_SET_ELEM( edge ))
+ {
+ org_pt = cvSubdiv2DEdgeOrg((CvSubdiv2DEdge)edge);
+ dst_pt = cvSubdiv2DEdgeDst((CvSubdiv2DEdge)edge);
+
+ if( org_pt && dst_pt )
+ {
+ org = org_pt->pt;
+ dst = dst_pt->pt;
+
+ iorg = cvPoint( cvRound( org.x ), cvRound( org.y ));
+ idst = cvPoint( cvRound( dst.x ), cvRound( dst.y ));
+
+ if ( ( org.x > 0 ) && ( org.x < comp_xsize ) &&
+ ( dst.x > 0 ) && ( dst.x < comp_xsize ) &&
+ ( org.y > 0 ) && ( org.y < comp_ysize ) &&
+ ( dst.y > 0 ) && ( dst.y < comp_ysize ) )
+ cvLine( rgb, iorg, idst, CV_RGB(255,0,0), 1, CV_AA, 0 );
+ }
+ }
+
+ CV_NEXT_SEQ_ELEM( elem_size, reader );
+ }
+ }
+
+ CV_SWAP( prev_gray, gray, swap_temp );
+ CV_SWAP( prev_pyramid, pyramid, swap_temp );
+ CV_SWAP( points[0], points[1], swap_points );
+ need_to_init = 0;
+
+ cvCvtColor(rgb, rgba, CV_BGR2BGRA);
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_lk :: processRGBImage(imageStruct &image)
+{
+ int i, k;
+ int im, oi;
+ int marked;
+ float dist, odist;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &orgb );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &ogray );
+ cvReleaseImage( &prev_gray );
+ cvReleaseImage( &pyramid );
+ cvReleaseImage( &prev_pyramid );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ orgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ ogray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ pyramid = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_pyramid = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ points[0] = (CvPoint2D32f*)cvAlloc(MAX_COUNT*sizeof(points[0][0]));
+ points[1] = (CvPoint2D32f*)cvAlloc(MAX_COUNT*sizeof(points[0][0]));
+ status = (char*)cvAlloc(MAX_COUNT);
+
+ }
+
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+ memcpy( orgb->imageData, image.data, image.xsize*image.ysize*3 );
+ cvCvtColor(rgb, gray, CV_BGRA2GRAY);
+
+ if( night_mode )
+ cvZero( rgb );
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ x_found[im]--;
+ }
+
+ if ( delaunay >= 0 )
+ {
+ // init data structures for the delaunay
+ x_fullrect.x = -comp_xsize/2;
+ x_fullrect.y = -comp_ysize/2;
+ x_fullrect.width = 2*comp_xsize;
+ x_fullrect.height = 2*comp_ysize;
+
+ x_storage = cvCreateMemStorage(0);
+ x_subdiv = cvCreateSubdiv2D( CV_SEQ_KIND_SUBDIV2D, sizeof(*x_subdiv),
+ sizeof(CvSubdiv2DPoint),
+ sizeof(CvQuadEdge2D),
+ x_storage );
+ cvInitSubdivDelaunay2D( x_subdiv, x_fullrect );
+ }
+
+ if( need_to_init )
+ {
+ /* automatic initialization */
+ IplImage* eig = cvCreateImage( cvSize(gray->width,gray->height), 32, 1 );
+ IplImage* temp = cvCreateImage( cvSize(gray->width,gray->height), 32, 1 );
+
+ count = MAX_COUNT;
+ cvGoodFeaturesToTrack( gray, eig, temp, points[1], &count,
+ quality, min_distance, 0, 3, 0, 0.04 );
+ // cvFindCornerSubPix( gray, points[1], count,
+ // cvSize(win_size,win_size), cvSize(-1,-1),
+ // cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));
+ cvReleaseImage( &eig );
+ cvReleaseImage( &temp );
+
+ add_remove_pt = 0;
+ }
+ else if( count > 0 )
+ {
+ cvCalcOpticalFlowPyrLK( prev_gray, gray, prev_pyramid, pyramid,
+ points[0], points[1], count, cvSize(win_size,win_size), 3, status, 0,
+ cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03), flags );
+ flags |= CV_LKFLOW_PYR_A_READY;
+ for( i = k = 0; i < count; i++ )
+ {
+ if( add_remove_pt )
+ {
+ double dx = pt.x - points[1][i].x;
+ double dy = pt.y - points[1][i].y;
+
+ if( dx*dx + dy*dy <= 25 )
+ {
+ add_remove_pt = 0;
+ continue;
+ }
+ }
+
+ if( !status[i] )
+ continue;
+
+ points[1][k++] = points[1][i];
+ if ( delaunay == 0 ) // add all the points
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, points[1][i] );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+ // only add points included in (color-threshold)<p<(color+treshold)
+ if ( ( delaunay > 0 ) && ( x_xmark[delaunay-1] != -1 ) )
+ {
+ int px = cvPointFrom32f(points[1][i]).x;
+ int py = cvPointFrom32f(points[1][i]).y;
+ int ppx, ppy;
+
+ // eight connected pixels
+ for ( ppx=px-1; ppx<=px+1; ppx++ )
+ {
+ for ( ppy=py-1; ppy<=py+1; ppy++ )
+ {
+ if ( ( ppx < 0 ) || ( ppx >= comp_xsize ) ) continue;
+ if ( ( ppy < 0 ) || ( ppy >= comp_ysize ) ) continue;
+
+ uchar red = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3];
+ uchar green = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3+1];
+ uchar blue = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3+2];
+
+ uchar pred = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[delaunay-1]))[x_ymark[delaunay-1]*3];
+ uchar pgreen = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[delaunay-1]))[x_ymark[delaunay-1]*3+1];
+ uchar pblue = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[delaunay-1]))[x_ymark[delaunay-1]*3+2];
+
+ int diff = abs(red-pred) + abs(green-pgreen) + abs(blue-pblue);
+
+ // post( "pix_opencv_lk : point (%d,%d,%d) : diff : %d", blue, green, red, diff );
+
+ if ( diff < threshold )
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, points[1][i] );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+ }
+ }
+ }
+
+ cvCircle( rgb, cvPointFrom32f(points[1][i]), 3, CV_RGB(0,255,0), -1, 8,0);
+
+ marked=0;
+ oi=-1;
+ dist=(comp_xsize>comp_ysize)?comp_xsize:comp_ysize;
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( x_xmark[im] == -1 ) continue; // i don't see the point
+
+ odist=sqrt( pow( points[1][i].x - x_xmark[im], 2 ) + pow( points[1][i].y - x_ymark[im], 2 ) );
+
+ // search for this point
+ if ( odist <= maxmove )
+ {
+ if ( odist < dist )
+ {
+ dist = odist;
+ marked=1;
+ oi=im;
+ }
+ }
+ }
+
+ if ( oi !=-1 )
+ {
+ char tindex[4];
+ sprintf( tindex, "%d", oi );
+ cvPutText( rgb, tindex, cvPointFrom32f(points[1][i]), &font, CV_RGB(255,255,255));
+ x_xmark[oi]=points[1][i].x;
+ x_ymark[oi]=points[1][i].y;
+ x_found[oi]=ftolerance;
+ SETFLOAT(&x_list[0], oi);
+ SETFLOAT(&x_list[1], x_xmark[oi]);
+ SETFLOAT(&x_list[2], x_ymark[oi]);
+ outlet_list( m_dataout, 0, 3, x_list );
+ }
+
+ if ( markall && !marked )
+ {
+ for ( im=0; im<MAX_MARKERS; im++)
+ {
+ if ( x_xmark[im] == -1 )
+ {
+ x_xmark[im]=points[1][i].x;
+ x_ymark[im]=points[1][i].y;
+ x_found[im]=ftolerance;
+ break;
+ }
+ }
+ }
+ }
+ count = k;
+ }
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( (x_xmark[im] != -1.0 ) && !x_found[im] )
+ {
+ x_xmark[im]=-1.0;
+ x_ymark[im]=-1.0;
+ SETFLOAT(&x_list[0], im+1);
+ SETFLOAT(&x_list[1], x_xmark[im]);
+ SETFLOAT(&x_list[2], x_ymark[im]);
+ // send a lost point message to the patch
+ outlet_list( m_dataout, 0, 3, x_list );
+ post( "pix_opencv_lk : lost point %d", im+1 );
+ }
+ }
+
+ if( add_remove_pt && count < MAX_COUNT )
+ {
+ points[1][count++] = cvPointTo32f(pt);
+ cvFindCornerSubPix( gray, points[1] + count - 1, 1,
+ cvSize(win_size,win_size), cvSize(-1,-1),
+ cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));
+ add_remove_pt = 0;
+ }
+
+ // draw the delaunay
+ if ( delaunay >= 0 )
+ {
+ CvSeqReader reader;
+ int i, total = x_subdiv->edges->total;
+ int elem_size = x_subdiv->edges->elem_size;
+
+ cvStartReadSeq( (CvSeq*)(x_subdiv->edges), &reader, 0 );
+
+ for( i = 0; i < total; i++ )
+ {
+ CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);
+ CvSubdiv2DPoint* org_pt;
+ CvSubdiv2DPoint* dst_pt;
+ CvPoint2D32f org;
+ CvPoint2D32f dst;
+ CvPoint iorg, idst;
+
+ if( CV_IS_SET_ELEM( edge ))
+ {
+ org_pt = cvSubdiv2DEdgeOrg((CvSubdiv2DEdge)edge);
+ dst_pt = cvSubdiv2DEdgeDst((CvSubdiv2DEdge)edge);
+
+ if( org_pt && dst_pt )
+ {
+ org = org_pt->pt;
+ dst = dst_pt->pt;
+
+ iorg = cvPoint( cvRound( org.x ), cvRound( org.y ));
+ idst = cvPoint( cvRound( dst.x ), cvRound( dst.y ));
+
+ if ( ( org.x > 0 ) && ( org.x < comp_xsize ) &&
+ ( dst.x > 0 ) && ( dst.x < comp_xsize ) &&
+ ( org.y > 0 ) && ( org.y < comp_ysize ) &&
+ ( dst.y > 0 ) && ( dst.y < comp_ysize ) )
+ cvLine( rgb, iorg, idst, CV_RGB(255,0,0), 1, CV_AA, 0 );
+ }
+ }
+
+ CV_NEXT_SEQ_ELEM( elem_size, reader );
+ }
+ }
+
+ CV_SWAP( prev_gray, gray, swap_temp );
+ CV_SWAP( prev_pyramid, pyramid, swap_temp );
+ CV_SWAP( points[0], points[1], swap_points );
+ need_to_init = 0;
+
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_lk :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_lk : yuv format not supported" );
+}
+
+void pix_opencv_lk :: processGrayImage(imageStruct &image)
+{
+ int i, k;
+ int im, oi;
+ int marked;
+ float dist, odist;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &orgb );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &ogray );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &prev_gray );
+ cvReleaseImage( &pyramid );
+ cvReleaseImage( &prev_pyramid );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ orgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ pyramid = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ prev_pyramid = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ points[0] = (CvPoint2D32f*)cvAlloc(MAX_COUNT*sizeof(points[0][0]));
+ points[1] = (CvPoint2D32f*)cvAlloc(MAX_COUNT*sizeof(points[0][0]));
+ status = (char*)cvAlloc(MAX_COUNT);
+
+ }
+
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+ memcpy( ogray->imageData, image.data, image.xsize*image.ysize );
+
+ if( night_mode )
+ cvZero( gray );
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ x_found[im]--;
+ }
+
+ if ( delaunay >= 0 )
+ {
+ // init data structures for the delaunay
+ x_fullrect.x = -comp_xsize/2;
+ x_fullrect.y = -comp_ysize/2;
+ x_fullrect.width = 2*comp_xsize;
+ x_fullrect.height = 2*comp_ysize;
+
+ x_storage = cvCreateMemStorage(0);
+ x_subdiv = cvCreateSubdiv2D( CV_SEQ_KIND_SUBDIV2D, sizeof(*x_subdiv),
+ sizeof(CvSubdiv2DPoint),
+ sizeof(CvQuadEdge2D),
+ x_storage );
+ cvInitSubdivDelaunay2D( x_subdiv, x_fullrect );
+ }
+
+ if( need_to_init )
+ {
+ /* automatic initialization */
+ IplImage* eig = cvCreateImage( cvSize(gray->width,gray->height), 32, 1 );
+ IplImage* temp = cvCreateImage( cvSize(gray->width,gray->height), 32, 1 );
+
+ count = MAX_COUNT;
+ cvGoodFeaturesToTrack( gray, eig, temp, points[1], &count,
+ quality, min_distance, 0, 3, 0, 0.04 );
+ // cvFindCornerSubPix( gray, points[1], count,
+ // cvSize(win_size,win_size), cvSize(-1,-1),
+ // cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));
+ cvReleaseImage( &eig );
+ cvReleaseImage( &temp );
+
+ add_remove_pt = 0;
+ }
+ else if( count > 0 )
+ {
+ cvCalcOpticalFlowPyrLK( prev_gray, gray, prev_pyramid, pyramid,
+ points[0], points[1], count, cvSize(win_size,win_size), 3, status, 0,
+ cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03), flags );
+ flags |= CV_LKFLOW_PYR_A_READY;
+ for( i = k = 0; i < count; i++ )
+ {
+ if( add_remove_pt )
+ {
+ double dx = pt.x - points[1][i].x;
+ double dy = pt.y - points[1][i].y;
+
+ if( dx*dx + dy*dy <= 25 )
+ {
+ add_remove_pt = 0;
+ continue;
+ }
+ }
+
+ if( !status[i] )
+ continue;
+
+ points[1][k++] = points[1][i];
+ if ( delaunay == 0 ) // add all the points
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, points[1][i] );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+ // only add points included in (color-threshold)<p<(color+treshold)
+ if ( ( delaunay > 0 ) && ( x_xmark[delaunay-1] != -1 ) )
+ {
+ int px = cvPointFrom32f(points[1][i]).x;
+ int py = cvPointFrom32f(points[1][i]).y;
+ int ppx, ppy;
+
+ // eight connected pixels
+ for ( ppx=px-1; ppx<=px+1; ppx++ )
+ {
+ for ( ppy=py-1; ppy<=py+1; ppy++ )
+ {
+ if ( ( ppx < 0 ) || ( ppx >= comp_xsize ) ) continue;
+ if ( ( ppy < 0 ) || ( ppy >= comp_ysize ) ) continue;
+
+ uchar lum = ((uchar*)(ogray->imageData + ogray->widthStep*ppx))[ppy];
+ uchar plum = ((uchar*)(ogray->imageData + ogray->widthStep*x_xmark[delaunay-1]))[x_ymark[delaunay-1]];
+
+ int diff = abs(plum-lum);
+
+ // post( "pix_opencv_lk : point (%d,%d,%d) : diff : %d", blue, green, red, diff );
+
+ if ( diff < threshold )
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, points[1][i] );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+ }
+ }
+ }
+
+ cvCircle( gray, cvPointFrom32f(points[1][i]), 3, CV_RGB(0,255,0), -1, 8,0);
+
+ marked=0;
+ oi=-1;
+ dist=(comp_xsize>comp_ysize)?comp_xsize:comp_ysize;
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( x_xmark[im] == -1 ) continue; // i don't see the point
+
+ odist=sqrt( pow( points[1][i].x - x_xmark[im], 2 ) + pow( points[1][i].y - x_ymark[im], 2 ) );
+
+ // search for the closest point
+ if ( odist <= maxmove )
+ {
+ if ( odist < dist )
+ {
+ dist = odist;
+ marked=1;
+ oi=im;
+ }
+ }
+ }
+
+ if ( oi !=-1 )
+ {
+ char tindex[4];
+ sprintf( tindex, "%d", oi );
+ cvPutText( gray, tindex, cvPointFrom32f(points[1][i]), &font, CV_RGB(255,255,255));
+ x_xmark[oi]=points[1][i].x;
+ x_ymark[oi]=points[1][i].y;
+ x_found[oi]=ftolerance;
+ SETFLOAT(&x_list[0], oi);
+ SETFLOAT(&x_list[1], x_xmark[oi]);
+ SETFLOAT(&x_list[2], x_ymark[oi]);
+ outlet_list( m_dataout, 0, 3, x_list );
+ }
+
+ if ( markall && !marked )
+ {
+ for ( im=0; im<MAX_MARKERS; im++)
+ {
+ if ( x_xmark[im] == -1 )
+ {
+ x_xmark[im]=points[1][i].x;
+ x_ymark[im]=points[1][i].y;
+ x_found[im]=ftolerance;
+ break;
+ }
+ }
+ }
+ }
+ count = k;
+ }
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( (x_xmark[im] != -1.0 ) && !x_found[im] )
+ {
+ x_xmark[im]=-1.0;
+ x_ymark[im]=-1.0;
+ SETFLOAT(&x_list[0], im+1);
+ SETFLOAT(&x_list[1], x_xmark[im]);
+ SETFLOAT(&x_list[2], x_ymark[im]);
+ // send a lost point message to the patch
+ outlet_list( m_dataout, 0, 3, x_list );
+ post( "pix_opencv_lk : lost point %d", im+1 );
+ }
+ }
+
+ if( add_remove_pt && count < MAX_COUNT )
+ {
+ points[1][count++] = cvPointTo32f(pt);
+ cvFindCornerSubPix( gray, points[1] + count - 1, 1,
+ cvSize(win_size,win_size), cvSize(-1,-1),
+ cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03));
+ add_remove_pt = 0;
+ }
+
+ // draw the delaunay
+ if ( delaunay >= 0 )
+ {
+ CvSeqReader reader;
+ int i, total = x_subdiv->edges->total;
+ int elem_size = x_subdiv->edges->elem_size;
+
+ cvStartReadSeq( (CvSeq*)(x_subdiv->edges), &reader, 0 );
+
+ for( i = 0; i < total; i++ )
+ {
+ CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);
+ CvSubdiv2DPoint* org_pt;
+ CvSubdiv2DPoint* dst_pt;
+ CvPoint2D32f org;
+ CvPoint2D32f dst;
+ CvPoint iorg, idst;
+
+ if( CV_IS_SET_ELEM( edge ))
+ {
+ org_pt = cvSubdiv2DEdgeOrg((CvSubdiv2DEdge)edge);
+ dst_pt = cvSubdiv2DEdgeDst((CvSubdiv2DEdge)edge);
+
+ if( org_pt && dst_pt )
+ {
+ org = org_pt->pt;
+ dst = dst_pt->pt;
+
+ iorg = cvPoint( cvRound( org.x ), cvRound( org.y ));
+ idst = cvPoint( cvRound( dst.x ), cvRound( dst.y ));
+
+ if ( ( org.x > 0 ) && ( org.x < comp_xsize ) &&
+ ( dst.x > 0 ) && ( dst.x < comp_xsize ) &&
+ ( org.y > 0 ) && ( org.y < comp_ysize ) &&
+ ( dst.y > 0 ) && ( dst.y < comp_ysize ) )
+ cvLine( gray, iorg, idst, CV_RGB(255,0,0), 1, CV_AA, 0 );
+ }
+ }
+
+ CV_NEXT_SEQ_ELEM( elem_size, reader );
+ }
+ }
+
+ CV_SWAP( prev_gray, gray, swap_temp );
+ CV_SWAP( prev_pyramid, pyramid, swap_temp );
+ CV_SWAP( points[0], points[1], swap_points );
+ need_to_init = 0;
+
+ memcpy( image.data, gray->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_lk :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::winSizeMessCallback,
+ gensym("winsize"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::nightModeMessCallback,
+ gensym("nightmode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::qualityMessCallback,
+ gensym("quality"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::initMessCallback,
+ gensym("init"), A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::markMessCallback,
+ gensym("mark"), A_GIMME, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::deleteMessCallback,
+ gensym("delete"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::clearMessCallback,
+ gensym("clear"), A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::minDistanceMessCallback,
+ gensym("mindistance"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::maxMoveMessCallback,
+ gensym("maxmove"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::ftoleranceMessCallback,
+ gensym("ftolerance"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::delaunayMessCallback,
+ gensym("delaunay"), A_SYMBOL, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_lk::pdelaunayMessCallback,
+ gensym("pdelaunay"), A_FLOAT, A_FLOAT, A_NULL);
+}
+
+void pix_opencv_lk :: winSizeMessCallback(void *data, t_floatarg winsize)
+{
+ GetMyClass(data)->winSizeMess((float)winsize);
+}
+
+void pix_opencv_lk :: nightModeMessCallback(void *data, t_floatarg nightmode)
+{
+ GetMyClass(data)->nightModeMess((float)nightmode);
+}
+
+void pix_opencv_lk :: qualityMessCallback(void *data, t_floatarg quality)
+{
+ GetMyClass(data)->qualityMess((float)quality);
+}
+
+void pix_opencv_lk :: initMessCallback(void *data)
+{
+ GetMyClass(data)->initMess();
+}
+
+void pix_opencv_lk :: markMessCallback(void *data, t_symbol *s, int argc, t_atom *argv)
+{
+ GetMyClass(data)->markMess(argc, argv);
+}
+
+void pix_opencv_lk :: deleteMessCallback(void *data, t_floatarg index)
+{
+ GetMyClass(data)->deleteMess((float)index);
+}
+
+void pix_opencv_lk :: clearMessCallback(void *data)
+{
+ GetMyClass(data)->clearMess();
+}
+
+void pix_opencv_lk :: minDistanceMessCallback(void *data, t_floatarg mindistance)
+{
+ GetMyClass(data)->minDistanceMess((float)mindistance);
+}
+
+void pix_opencv_lk :: maxMoveMessCallback(void *data, t_floatarg maxmove)
+{
+ GetMyClass(data)->maxMoveMess((float)maxmove);
+}
+
+void pix_opencv_lk :: ftoleranceMessCallback(void *data, t_floatarg ftolerance)
+{
+ GetMyClass(data)->ftoleranceMess((float)ftolerance);
+}
+
+void pix_opencv_lk :: delaunayMessCallback(void *data, t_symbol *s)
+{
+ GetMyClass(data)->delaunayMess(s);
+}
+
+void pix_opencv_lk :: pdelaunayMessCallback(void *data, t_floatarg fpoint, t_floatarg fthreshold)
+{
+ GetMyClass(data)->pdelaunayMess((float)fpoint, (float)fthreshold);
+}
+
+void pix_opencv_lk :: winSizeMess(float fwinsize)
+{
+ if (fwinsize>1.0) win_size = (int)fwinsize;
+}
+
+void pix_opencv_lk :: nightModeMess(float fnightmode)
+{
+ if ((fnightmode==0.0)||(fnightmode==1.0)) night_mode = (int)fnightmode;
+}
+
+void pix_opencv_lk :: qualityMess(float fquality)
+{
+ if (fquality>0.0) quality = fquality;
+}
+
+void pix_opencv_lk :: initMess(void)
+{
+ need_to_init = 1;
+}
+
+void pix_opencv_lk :: markMess(int argc, t_atom *argv)
+{
+ int i;
+ int inserted;
+
+ if ( argc == 1 ) // mark all or none
+ {
+ if ( argv[0].a_type != A_SYMBOL )
+ {
+ error( "pix_opencv_lk : wrong argument (should be 'all')" );
+ return;
+ }
+ if ( !strcmp( argv[0].a_w.w_symbol->s_name, "all" ) )
+ {
+ markall = 1;
+ return;
+ }
+ if ( !strcmp( argv[0].a_w.w_symbol->s_name, "none" ) )
+ {
+ markall = 0;
+ clearMess();
+ return;
+ }
+ }
+ else
+ {
+ if ( ( argv[0].a_type != A_FLOAT ) || ( argv[1].a_type != A_FLOAT ) )
+ {
+ error( "pix_opencv_lk : wrong argument (should be mark px py)" );
+ return;
+ }
+ else
+ {
+ float fpx = argv[0].a_w.w_float;
+ float fpy = argv[1].a_w.w_float;
+ int px, py;
+
+ if ( ( fpx < 0.0 ) || ( fpx > comp_xsize ) || ( fpy < 0.0 ) || ( fpy > comp_ysize ) )
+ {
+ return;
+ }
+
+ px = (int)fpx;
+ py = (int)fpy;
+ inserted = 0;
+ for ( i=0; i<MAX_MARKERS; i++)
+ {
+ if ( x_xmark[i] == -1 )
+ {
+ x_xmark[i] = px;
+ x_ymark[i] = py;
+ x_found[i] = ftolerance;
+ inserted = 1;
+ // post( "pix_opencv_lk : inserted point (%d,%d)", px, py );
+ break;
+ }
+ }
+ if ( !inserted )
+ {
+ post( "pix_opencv_lk : max markers reached" );
+ }
+ }
+ }
+}
+
+void pix_opencv_lk :: deleteMess(float index)
+{
+ int i;
+
+ if ( ( index < 0.0 ) || ( index >= MAX_MARKERS ) )
+ {
+ return;
+ }
+
+ x_xmark[(int)index] = -1;
+ x_ymark[(int)index] = -1;
+
+}
+
+void pix_opencv_lk :: clearMess(void)
+{
+ int i;
+
+ for ( i=0; i<MAX_MARKERS; i++)
+ {
+ x_xmark[i] = -1;
+ x_ymark[i] = -1;
+ }
+
+}
+
+void pix_opencv_lk :: minDistanceMess(float fmindistance)
+{
+ if (fmindistance>1.0) min_distance = (int)fmindistance;
+}
+
+void pix_opencv_lk :: maxMoveMess(float fmaxmove)
+{
+ // has to be more than the size of a point
+ if (fmaxmove>=3.0) maxmove = (int)fmaxmove;
+}
+
+void pix_opencv_lk :: ftoleranceMess(float ftolerance)
+{
+ if (ftolerance>=0.0) ftolerance = (int)ftolerance;
+}
+
+void pix_opencv_lk :: delaunayMess(t_symbol *s)
+{
+ if (s == gensym("on"))
+ delaunay = 0;
+ if (s == gensym("off"))
+ delaunay = -1;
+}
+
+void pix_opencv_lk :: pdelaunayMess(float fpoint, float fthreshold)
+{
+ if (((int)fpoint>0) && ((int)fpoint<MAX_MARKERS))
+ {
+ delaunay = (int)fpoint;
+ threshold = (int)fthreshold;
+ // post( "pix_opencv_lk : setting threshold to : %d", threshold );
+ }
+}
+
diff --git a/src/pix_opencv_lk.h b/src/pix_opencv_lk.h
new file mode 100644
index 0000000..38f834c
--- /dev/null
+++ b/src/pix_opencv_lk.h
@@ -0,0 +1,132 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ LK point detection and tracking
+
+ 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_LK_H_
+#define INCLUDE_PIX_OPENCV_LK_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/video/tracking.hpp"
+
+#endif
+
+#include "Base/GemPixObj.h"
+
+#define MAX_MARKERS 500
+const int MAX_COUNT = 500;
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_lk
+
+ LK point detection and tracking
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_lk : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_lk, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_lk();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_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 winSizeMess(float winsize);
+ void nightModeMess(float nightmode);
+ void qualityMess(float quality);
+ void initMess(void);
+ void markMess(int, t_atom*);
+ void deleteMess(float index);
+ void clearMess(void);
+ void minDistanceMess(float mindistance);
+ void maxMoveMess(float maxmove);
+ void ftoleranceMess(float ftolerance);
+ void delaunayMess(t_symbol *s);
+ void pdelaunayMess(float fpoint, float fthreshold);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ t_outlet *m_dataout;
+ t_atom x_list[3];
+ int win_size;
+ double quality;
+ int min_distance;
+ int night_mode;
+ int maxmove;
+ int markall;
+ int ftolerance;
+ int delaunay;
+ int threshold;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void winSizeMessCallback(void *data, t_floatarg winsize);
+ static void nightModeMessCallback(void *data, t_floatarg nightmode);
+ static void qualityMessCallback(void *data, t_floatarg quality);
+ static void initMessCallback(void *data);
+ static void markMessCallback(void *data, t_symbol* name, int argc, t_atom* argv);
+ static void deleteMessCallback(void *data, t_floatarg index);
+ static void clearMessCallback(void *data);
+ static void minDistanceMessCallback(void *data, t_floatarg mindistance);
+ static void maxMoveMessCallback(void *data, t_floatarg maxmove);
+ static void ftoleranceMessCallback(void *data, t_floatarg ftolerance);
+ static void delaunayMessCallback(void *data, t_symbol *s);
+ static void pdelaunayMessCallback(void *data, t_floatarg fpoint, t_floatarg fthreshold);
+
+ // Internal Open CV data
+ IplImage *rgba, *rgb, *orgb, *gray, *ogray, *prev_gray, *pyramid, *prev_pyramid, *swap_temp;
+ int x_xmark[MAX_MARKERS];
+ int x_ymark[MAX_MARKERS];
+ int x_found[MAX_MARKERS];
+ CvPoint2D32f* points[2], *swap_points;
+ char* status;
+ int count;
+ int need_to_init;
+ int flags;
+ int add_remove_pt;
+ CvPoint pt;
+ CvFont font;
+
+ // structures needed for the delaunay
+ CvRect x_fullrect;
+ CvMemStorage* x_storage;
+ CvSubdiv2D* x_subdiv;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_matchshape.cc b/src/pix_opencv_matchshape.cc
new file mode 100644
index 0000000..737bfd3
--- /dev/null
+++ b/src/pix_opencv_matchshape.cc
@@ -0,0 +1,226 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org ) (pix_opencv)
+// Template for pix_opencv class
+
+#include "pix_opencv_matchshape.h"
+#include <stdio.h>
+#include <RTE/MessageCallbacks.h>
+
+#ifdef _MSC_VER
+#include <limits>
+#define INFINITY std::numeric_limits<float>::infinity()
+#endif
+
+using namespace std;
+
+
+CPPEXTERN_NEW(pix_opencv_matchshape)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_matchshape
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_matchshape :: pix_opencv_matchshape() : m_threshold(2), m_method(CV_CONTOURS_MATCH_I1)
+{
+ m_dataout = outlet_new(this->x_obj, 0);
+ m_template_vec_vec.clear();
+ m_template_vec_mat.clear();
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_matchshape :: ~pix_opencv_matchshape()
+{
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_matchshape :: processRGBAImage(imageStruct &image)
+{
+ error( "pix_opencv_matchshape : rgba format not supported" );
+}
+
+void pix_opencv_matchshape :: processRGBImage(imageStruct &image) {
+ error( "pix_opencv_matchshape : rgb format not supported");
+}
+
+void pix_opencv_matchshape :: processYUVImage(imageStruct &image)
+{
+ error( "pix_opencv_matchshape : yuv format not supported" );
+}
+
+void pix_opencv_matchshape :: processGrayImage(imageStruct &image)
+{
+ error( "pix_opencv_matchshape : gray format not supported" );
+ error( "pix_opencv_matchshape only works on pd lists" );
+
+ // TODO add support to compare grayscale images
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_matchshape :: obj_setupCallback(t_class *classPtr)
+{
+ CPPEXTERN_MSG0(classPtr, "clear", clearMess);
+ CPPEXTERN_MSG(classPtr, "template", templateMess);
+ CPPEXTERN_MSG(classPtr, "contour", contourMess);
+ CPPEXTERN_MSG1(classPtr, "threshold", thresholdMess, float);
+ CPPEXTERN_MSG1(classPtr, "method", methodMess, int);
+}
+
+void pix_opencv_matchshape :: clearMess(void)
+{
+ for ( vector<vector<cv::Point2f> >::iterator it=m_template_vec_vec.end(); it!=m_template_vec_vec.begin(); it--)
+ {
+ (*it).clear();
+ }
+ m_template_vec_vec.clear();
+ m_template_vec_mat.clear();
+
+ t_atom data;
+ SETFLOAT(&data, (float)m_template_vec_vec.size());
+ outlet_anything(m_dataout, gensym("template_vec"), 1, &data);
+
+ SETFLOAT(&data, (float)m_template_vec_mat.size());
+ outlet_anything(m_dataout, gensym("template_mat"), 1, &data);
+}
+
+
+void pix_opencv_matchshape :: contourMess(t_symbol*s, int argc, t_atom*argv)
+{
+ if ( m_template_vec_vec.empty() ){
+ error("no template to match, please load a template before matching");
+ return;
+ }
+
+ if ( (argc%2)!=0 ) {
+ error("template message argument should be multiple of 2");
+ return;
+ }
+
+ for (int i=0;i<argc;i++){
+ if(argv[i].a_type!=A_FLOAT){
+ error("template message should have only float arguments");
+ return;
+ }
+ }
+
+ vector<cv::Point2f> tmp_contour;
+ for (int i=2;i<argc;i+=2){ // start at index 2, first two elements are reserved (for compatibility)
+ cv::Point2f pt;
+ pt.x = atom_getfloat(argv+i);
+ pt.y = atom_getfloat(argv+i+1);
+ tmp_contour.push_back(pt);
+ }
+
+ double comp, min=INFINITY;
+ int id(0),i(0);
+ for ( vector<vector<cv::Point2f> >::iterator it=m_template_vec_vec.begin(); it!=m_template_vec_vec.end(); ++it){
+ comp = cv::matchShapes(tmp_contour, *it, m_method, 0.);
+ if ( comp < min ){
+ min=comp;
+ id=i;
+ }
+ i++;
+ }
+
+ if ( min < m_threshold )
+ {
+ t_atom data[2];
+ SETFLOAT(data,id);
+ SETFLOAT(data+1, min);
+ outlet_anything(m_dataout, gensym("match_vec"), 2, data);
+ }
+}
+
+void pix_opencv_matchshape :: templateMess(t_symbol*s, int argc, t_atom*argv)
+{
+ if ( (argc%2)!=0 ) {
+ error("template message argument should be multiple of 2");
+ return;
+ }
+ for (int i=0;i<argc;i++){
+ if(argv[i].a_type!=A_FLOAT){
+ error("template message should have only float arguments");
+ return;
+ }
+ }
+
+ vector<cv::Point2f> tmp_contour;
+ tmp_contour.clear();
+ for (int i=2;i<argc;i+=2){ // start at index 2, first two elements are reserved (for compatibility)
+ cv::Point2f pt;
+ pt.x = atom_getfloat(argv+i);
+ pt.y = atom_getfloat(argv+i+1);
+ tmp_contour.push_back(pt);
+ }
+ m_template_vec_vec.push_back(tmp_contour);
+
+ t_atom data;
+ SETFLOAT(&data, (int)m_template_vec_vec.size());
+ outlet_anything(m_dataout, gensym("template_vec"), 1, &data);
+}
+
+void pix_opencv_matchshape :: thresholdMess(float arg)
+{
+ m_threshold=(double)(arg>0.?arg:0.);
+ t_atom data;
+ SETFLOAT(&data, m_threshold);
+ outlet_anything(m_dataout, gensym("threshold"), 1, &data);
+}
+
+void pix_opencv_matchshape :: methodMess(int arg)
+{
+ switch (arg) {
+ case 1:
+ m_method=CV_CONTOURS_MATCH_I1;
+ break;
+ case 2:
+ m_method=CV_CONTOURS_MATCH_I2;
+ break;
+ case 3:
+ m_method=CV_CONTOURS_MATCH_I3;
+ break;
+ default:
+ error("method should be 1, 2 or 3");
+ m_method=CV_CONTOURS_MATCH_I1;
+ }
+ t_atom data;
+ switch (m_method) {
+ case CV_CONTOURS_MATCH_I1:
+ SETFLOAT(&data, 1);
+ break;
+ case CV_CONTOURS_MATCH_I2:
+ SETFLOAT(&data, 2);
+ break;
+ case CV_CONTOURS_MATCH_I3:
+ SETFLOAT(&data, 3);
+ break;
+ }
+ outlet_anything(m_dataout, gensym("method"), 1, &data);
+}
diff --git a/src/pix_opencv_matchshape.h b/src/pix_opencv_matchshape.h
new file mode 100644
index 0000000..c8063c3
--- /dev/null
+++ b/src/pix_opencv_matchshape.h
@@ -0,0 +1,74 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Threshold filter
+
+ 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_MATCHSHAPE_H_
+#define INCLUDE_PIX_OPENCV_MATCHSHAPE_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_matchshape
+
+ square pattern detector
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_matchshape : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_matchshape, GemPixObj)
+
+ public:
+
+ // Constructor
+ pix_opencv_matchshape();
+
+ protected:
+ // process messages
+ void clearMess(void);
+ void templateMess(t_symbol*s, int argc, t_atom*argv);
+ void contourMess(t_symbol*s, int argc, t_atom*argv);
+ void thresholdMess(float arg);
+ void methodMess(int arg);
+
+ // Destructor
+ virtual ~pix_opencv_matchshape();
+
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ private:
+
+ t_outlet *m_dataout; // info outlet
+ double m_threshold;
+ int m_method;
+ std::vector<std::vector<cv::Point2f> > m_template_vec_vec;
+ std::vector<cv::Mat> m_template_vec_mat;
+
+};
+#endif // for header file
diff --git a/src/pix_opencv_morphology.cc b/src/pix_opencv_morphology.cc
new file mode 100644
index 0000000..a241ac5
--- /dev/null
+++ b/src/pix_opencv_morphology.cc
@@ -0,0 +1,291 @@
+////////////////////////////////////////////////////////
+//
+// 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_morphology.h"
+
+CPPEXTERN_NEW(pix_opencv_morphology)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_morphology
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_morphology :: pix_opencv_morphology()
+{
+ int i;
+
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("ft1"));
+
+ pos = 0;
+ comp_xsize = 0;
+ comp_ysize = 0;
+
+ rgba = NULL;
+ grey = NULL;
+ rgb = NULL;
+ dst = NULL;
+
+ element_shape = CV_SHAPE_RECT;
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_morphology :: ~pix_opencv_morphology()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &dst );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &grey );
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_morphology :: processRGBAImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgba)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &dst );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &grey );
+
+ //Create cv_images
+ rgb = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 3 );
+ dst = cvCloneImage(rgb);
+ rgba = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 4 );
+ grey = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 1 );
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+
+ cvCvtColor(rgba, rgb, CV_RGBA2RGB);
+
+ if (this->mode == 1) { //open/close
+ int n = pos;
+ int an = n > 0 ? n : -n;
+ element = cvCreateStructuringElementEx( an*2+1, an*2+1, an, an, element_shape, 0 );
+ if( n < 0 )
+ {
+ cvErode(rgb,dst,element,1);
+ cvDilate(dst,dst,element,1);
+ }
+ else
+ {
+ cvDilate(rgb,dst,element,1);
+ cvErode(dst,dst,element,1);
+ }
+ cvReleaseStructuringElement(&element);
+
+ } else {
+ int n = pos;
+ int an = n > 0 ? n : -n;
+ element = cvCreateStructuringElementEx( an*2+1, an*2+1, an, an, element_shape, 0 );
+ if( n < 0 )
+ {
+ cvErode(rgb,dst,element,1);
+ }
+ else
+ {
+ cvDilate(rgb,dst,element,1);
+ }
+ cvReleaseStructuringElement(&element);
+
+ }
+
+ cvCvtColor(dst, rgba, CV_RGB2RGBA);
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_morphology :: processRGBImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &dst );
+
+ //Create cv_images
+ rgb = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 4 );
+ dst = cvCloneImage(rgb);
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ if (this->mode == 1) { //open/close
+ int n = pos;
+ int an = n > 0 ? n : -n;
+ element = cvCreateStructuringElementEx( an*2+1, an*2+1, an, an, element_shape, 0 );
+ if( n < 0 )
+ {
+ cvErode(rgb,dst,element,1);
+ cvDilate(dst,dst,element,1);
+ }
+ else
+ {
+ cvDilate(rgb,dst,element,1);
+ cvErode(dst,dst,element,1);
+ }
+ cvReleaseStructuringElement(&element);
+
+ } else {
+ int n = pos;
+ int an = n > 0 ? n : -n;
+ element = cvCreateStructuringElementEx( an*2+1, an*2+1, an, an, element_shape, 0 );
+ if( n < 0 )
+ {
+ cvErode(rgb,dst,element,1);
+ }
+ else
+ {
+ cvDilate(rgb,dst,element,1);
+ }
+ cvReleaseStructuringElement(&element);
+
+ }
+
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, dst->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_morphology :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_morphology : yuv format not supported" );
+}
+
+void pix_opencv_morphology :: processGrayImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+ int i;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!grey)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &dst );
+ cvReleaseImage( &grey );
+
+ //Create cv_images
+ rgb = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 3 );
+ dst = cvCloneImage(rgb);
+ grey = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 1 );
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( grey->imageData, image.data, image.xsize*image.ysize );
+
+ cvCvtColor(grey, rgb, CV_GRAY2RGB);
+
+ if (this->mode == 1) { //open/close
+ int n = pos;
+ int an = n > 0 ? n : -n;
+ element = cvCreateStructuringElementEx( an*2+1, an*2+1, an, an, element_shape, 0 );
+ if( n < 0 )
+ {
+ cvErode(rgb,dst,element,1);
+ cvDilate(dst,dst,element,1);
+ }
+ else
+ {
+ cvDilate(rgb,dst,element,1);
+ cvErode(dst,dst,element,1);
+ }
+ cvReleaseStructuringElement(&element);
+
+ } else {
+ int n = pos;
+ int an = n > 0 ? n : -n;
+ element = cvCreateStructuringElementEx( an*2+1, an*2+1, an, an, element_shape, 0 );
+ if( n < 0 )
+ {
+ cvErode(rgb,dst,element,1);
+ }
+ else
+ {
+ cvDilate(rgb,dst,element,1);
+ }
+ cvReleaseStructuringElement(&element);
+
+ }
+
+ cvCvtColor(dst, grey, CV_RGB2GRAY);
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, grey->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// floatPosMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_morphology :: floatPosMess (float pos)
+{
+ this->pos = (int)pos;
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_morphology :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_morphology::floatPosMessCallback,
+ gensym("ft1"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_morphology::modeMessCallback,
+ gensym("mode"), A_DEFFLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_morphology::shapeMessCallback,
+ gensym("shape"), A_DEFFLOAT, A_NULL);
+}
+void pix_opencv_morphology :: floatPosMessCallback(void *data, t_floatarg pos)
+{
+ GetMyClass(data)->floatPosMess((float)pos);
+}
+void pix_opencv_morphology :: modeMessCallback(void *data, t_floatarg mode)
+{
+ GetMyClass(data)->mode=!(!(int)mode);
+}
+void pix_opencv_morphology :: shapeMessCallback(void *data, t_floatarg f)
+{
+ if( (int)f == 1 )
+ GetMyClass(data)->element_shape = CV_SHAPE_RECT;
+ else if( (int)f == 2 )
+ GetMyClass(data)->element_shape = CV_SHAPE_ELLIPSE;
+ else if( (int)f == 3 )
+ GetMyClass(data)->element_shape = CV_SHAPE_CROSS;
+}
diff --git a/src/pix_opencv_morphology.h b/src/pix_opencv_morphology.h
new file mode 100644
index 0000000..3471a04
--- /dev/null
+++ b/src/pix_opencv_morphology.h
@@ -0,0 +1,87 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Morphology filter
+
+ 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_MORPHOLOGY_H_
+#define INCLUDE_PIX_OPENCV_MORPHOLOGY_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_morphology
+
+ Morphology filter
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_morphology : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_morphology, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_morphology();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_morphology();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new pos
+ void floatPosMess(float pos);
+ // Some varibales to control mophology mode
+ int pos;
+ int element_shape;
+ int mode; //to switch between openclose or dilateerode modes
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatPosMessCallback(void *data, t_floatarg pos);
+ static void modeMessCallback(void *data, t_floatarg mode);
+ static void shapeMessCallback(void *data, t_floatarg f);
+
+ // The output and temporary images
+ IplImage *rgba, *grey, *rgb, *dst;
+
+ IplConvKernel *element;
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_motempl.cc b/src/pix_opencv_motempl.cc
new file mode 100644
index 0000000..ec33a01
--- /dev/null
+++ b/src/pix_opencv_motempl.cc
@@ -0,0 +1,708 @@
+////////////////////////////////////////////////////////
+//
+// 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_motempl.h"
+
+CPPEXTERN_NEW(pix_opencv_motempl)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_motempl
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_motempl :: pix_opencv_motempl()
+{
+ int i;
+
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("ft1"));
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("min_size"));
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("max_size"));
+ m_dataout = outlet_new(this->x_obj, 0);
+
+ mhi_duration = 1.0;
+ aperture = 3;
+ diff_threshold = 30;
+ last = 0;
+ comp_xsize = 320;
+ comp_ysize = 240;
+
+ // various tracking parameters (in seconds)
+ max_time_delta = 0.5;
+ min_time_delta = 0.05;
+ // number of cyclic frame buffer used for motion detection
+ // (should, probably, depend on FPS)
+ frame_buffer_num = 4;
+
+ min_size=50;
+ max_size=500;
+
+ rgb = NULL;
+ motion = NULL;
+ rgba = NULL;
+ grey = NULL;
+ mhi = NULL;
+ orient = NULL;
+ mask = NULL;
+ segmask = NULL;
+ storage = NULL;
+
+ mask_size = CV_DIST_MASK_PRECISE;
+
+ cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 0, 1, 8 );
+
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ motion = cvCreateImage( cvSize(comp_xsize,comp_ysize), 8, 3 );
+ cvZero( motion );
+ motion->origin = rgb->origin;
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_motempl :: ~pix_opencv_motempl()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &motion );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &grey );
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_motempl :: processRGBAImage(imageStruct &image)
+{
+ double timestamp = (double)clock()/CLOCKS_PER_SEC; // get current time in seconds
+ CvSize size = cvSize(image.xsize,image.ysize); // get current frame size
+ int i, j, idx1 = last, idx2;
+ IplImage* silh;
+ CvSeq* seq;
+ CvRect comp_rect;
+ double count;
+ double angle;
+ CvPoint center;
+ double magnitude;
+ CvScalar color;
+ char tindex[10];
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &motion );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &grey );
+
+ //Create cv_images
+ rgb = cvCreateImage(cvSize(image.xsize, image.ysize), IPL_DEPTH_8U, 3);
+ motion = cvCreateImage( cvSize(rgb->width,rgb->height), 8, 3 );
+ cvZero( motion );
+ motion->origin = rgb->origin;
+ rgba = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 4 );
+ grey = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 1 );
+ }
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+
+ cvCvtColor( rgba, rgb, CV_RGBA2RGB);
+
+ // allocate images at the beginning or
+ // reallocate them if the frame size is changed
+ if( (!mhi) || (mhi->width != size.width) || (mhi->height != size.height) || (!buf)) {
+ if( buf == 0 ) {
+ buf = (IplImage**)malloc(frame_buffer_num*sizeof(buf[0]));
+ //memset( buf, 0, N*sizeof(buf[0]));
+ }
+
+ for( i = 0; i < frame_buffer_num; i++ ) {
+ // TODO if ( buf[i] != NULL ) cvReleaseImage( &buf[i] );
+ buf[i] = cvCreateImage( size, IPL_DEPTH_8U, 1 );
+ cvZero( buf[i] );
+ }
+ if ( mhi != NULL ) cvReleaseImage( &mhi );
+ if ( orient != NULL ) cvReleaseImage( &orient );
+ if ( segmask != NULL ) cvReleaseImage( &segmask );
+ if ( mask != NULL ) cvReleaseImage( &mask );
+
+ mhi = cvCreateImage( size, IPL_DEPTH_32F, 1 );
+ cvZero( mhi ); // clear MHI at the beginning
+ orient = cvCreateImage( size, IPL_DEPTH_32F, 1 );
+ segmask = cvCreateImage( size, IPL_DEPTH_32F, 1 );
+ mask = cvCreateImage( size, IPL_DEPTH_8U, 1 );
+ }
+
+ cvCvtColor( rgb, buf[last], CV_BGR2GRAY ); // convert frame to grayscale
+
+ idx2 = (last + 1) % frame_buffer_num; // index of (last - (N-1))th frame
+ last = idx2;
+
+ silh = buf[idx2];
+ cvAbsDiff( buf[idx1], buf[idx2], silh ); // get difference between frames
+
+ cvThreshold( silh, silh, diff_threshold, 1, CV_THRESH_BINARY ); // and threshold it
+ cvUpdateMotionHistory( silh, mhi, timestamp, mhi_duration ); // update MHI
+
+ // convert MHI to red 8u image
+ cvCvtScale( mhi, mask, 255./mhi_duration,
+ (mhi_duration - timestamp)*255./mhi_duration );
+ cvZero( motion );
+ cvCvtPlaneToPix( mask, 0, 0, 0, motion );
+
+ // calculate motion gradient orientation and valid orientation mask
+ cvCalcMotionGradient( mhi, mask, orient, max_time_delta, min_time_delta, aperture );
+
+ if( !storage )
+ storage = cvCreateMemStorage(0);
+ else
+ cvClearMemStorage(storage);
+
+ // segment motion: get sequence of motion components
+ // segmask is marked motion components map. It is not used further
+ seq = cvSegmentMotion( mhi, segmask, storage, timestamp, max_time_delta );
+
+ // iterate through the motion components,
+ // One more iteration (i == -1) corresponds to the whole image (global motion)
+ j=0;
+ if (seq->total) for( i = -1; i < seq->total; i++ ) {
+
+ if( i < 0 ) { // case of the whole image
+ comp_rect = cvRect( 0, 0, size.width, size.height );
+ color = CV_RGB(255,255,255);
+ magnitude = 100;
+ }
+ else { // i-th motion component
+ comp_rect = ((CvConnectedComp*)cvGetSeqElem( seq, i ))->rect;
+ if(( comp_rect.width + comp_rect.height < min_size )||( comp_rect.width + comp_rect.height > max_size )) // reject very small/big components
+ continue;
+#ifdef _DARWIN_
+ color = CV_RGB(0,255,0);
+#else
+ color = CV_RGB(255,0,0);
+#endif
+ magnitude = (comp_rect.width + comp_rect.height) /4;
+ }
+
+ // select component ROI
+ cvSetImageROI( silh, comp_rect );
+ cvSetImageROI( mhi, comp_rect );
+ cvSetImageROI( orient, comp_rect );
+ cvSetImageROI( mask, comp_rect );
+
+ // calculate orientation
+ angle = cvCalcGlobalOrientation( orient, mask, mhi, timestamp, mhi_duration);
+ angle = 360.0 - angle; // adjust for images with top-left origin
+
+ count = cvNorm( silh, 0, CV_L1, 0 ); // calculate number of points within silhouette ROI
+
+ cvResetImageROI( mhi );
+ cvResetImageROI( orient );
+ cvResetImageROI( mask );
+ cvResetImageROI( silh );
+
+
+ // draw a clock with arrow indicating the direction
+ center = cvPoint( (comp_rect.x + comp_rect.width/2),
+ (comp_rect.y + comp_rect.height/2) );
+
+ cvCircle( motion, center, cvRound(magnitude*1.2), color, 3, CV_AA, 0 );
+ cvLine( motion, center, cvPoint( cvRound( center.x + magnitude*cos(angle*CV_PI/180)),
+ cvRound( center.y - magnitude*sin(angle*CV_PI/180))), color, 3, CV_AA, 0 );
+
+ if( i < 0 ) // case of the whole image
+ {
+ sprintf( tindex, "%d", i );
+ cvPutText( motion, tindex, center, &font, CV_RGB(255,255,255));
+ SETFLOAT(&rlist[0], i);
+ SETFLOAT(&rlist[1], center.x);
+ SETFLOAT(&rlist[2], center.y);
+ SETFLOAT(&rlist[3], comp_rect.width);
+ SETFLOAT(&rlist[4], comp_rect.height);
+ SETFLOAT(&rlist[5], angle);
+ outlet_list( m_dataout, 0, 6, rlist );
+ }
+ else
+ {
+ sprintf( tindex, "%d", ++j );
+ cvPutText( motion, tindex, center, &font, CV_RGB(255,255,255));
+ SETFLOAT(&rlist[0], j);
+ SETFLOAT(&rlist[1], center.x);
+ SETFLOAT(&rlist[2], center.y);
+ SETFLOAT(&rlist[3], comp_rect.width);
+ SETFLOAT(&rlist[4], comp_rect.height);
+ SETFLOAT(&rlist[5], angle);
+ outlet_list( m_dataout, 0, 6, rlist );
+ }
+ }
+
+
+#ifdef _DARWIN_
+ cvCvtColor( motion, rgba, CV_RGB2BGRA);
+#else
+ cvCvtColor( motion, rgba, CV_RGB2RGBA);
+#endif
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_motempl :: processRGBImage(imageStruct &image)
+{
+ double timestamp = (double)clock()/CLOCKS_PER_SEC; // get current time in seconds
+ CvSize size = cvSize(image.xsize,image.ysize); // get current frame size
+ int i, j, idx1 = last, idx2;
+ IplImage* silh;
+ CvSeq* seq;
+ CvRect comp_rect;
+ double count;
+ double angle;
+ CvPoint center;
+ double magnitude;
+ CvScalar color;
+ char tindex[10];
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &motion );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &grey );
+
+ //Create cv_images
+ rgb = cvCreateImage(cvSize(image.xsize, image.ysize), IPL_DEPTH_8U, 3);
+ motion = cvCreateImage( cvSize(rgb->width,rgb->height), 8, 3 );
+ cvZero( motion );
+ motion->origin = rgb->origin;
+ rgba = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 4 );
+ grey = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 1 );
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ // allocate images at the beginning or
+ // reallocate them if the frame size is changed
+ if( (!mhi) || (mhi->width != size.width) || (mhi->height != size.height) || (!buf)) {
+ if( buf == 0 ) {
+ buf = (IplImage**)malloc(frame_buffer_num*sizeof(buf[0]));
+ //memset( buf, 0, N*sizeof(buf[0]));
+ }
+
+ for( i = 0; i < frame_buffer_num; i++ ) {
+ // TODO if ( buf[i] != NULL ) cvReleaseImage( &(buf[i]) );
+ buf[i] = cvCreateImage( size, IPL_DEPTH_8U, 1 );
+ cvZero( buf[i] );
+ }
+ if ( mhi != NULL ) cvReleaseImage( &mhi );
+ if ( orient != NULL ) cvReleaseImage( &orient );
+ if ( segmask != NULL ) cvReleaseImage( &segmask );
+ if ( mask != NULL ) cvReleaseImage( &mask );
+
+ mhi = cvCreateImage( size, IPL_DEPTH_32F, 1 );
+ cvZero( mhi ); // clear MHI at the beginning
+ orient = cvCreateImage( size, IPL_DEPTH_32F, 1 );
+ segmask = cvCreateImage( size, IPL_DEPTH_32F, 1 );
+ mask = cvCreateImage( size, IPL_DEPTH_8U, 1 );
+ }
+
+ cvCvtColor( rgb, buf[last], CV_BGR2GRAY ); // convert frame to grayscale
+
+ idx2 = (last + 1) % frame_buffer_num; // index of (last - (N-1))th frame
+ last = idx2;
+
+ silh = buf[idx2];
+ cvAbsDiff( buf[idx1], buf[idx2], silh ); // get difference between frames
+
+ cvThreshold( silh, silh, diff_threshold, 1, CV_THRESH_BINARY ); // and threshold it
+ cvUpdateMotionHistory( silh, mhi, timestamp, mhi_duration ); // update MHI
+
+ // convert MHI to blue 8u image
+ cvCvtScale( mhi, mask, 255./mhi_duration,
+ (mhi_duration - timestamp)*255./mhi_duration );
+ cvZero( motion );
+ cvCvtPlaneToPix( mask, 0, 0, 0, motion );
+
+ // calculate motion gradient orientation and valid orientation mask
+ cvCalcMotionGradient( mhi, mask, orient, max_time_delta, min_time_delta, aperture );
+
+ if( !storage )
+ storage = cvCreateMemStorage(0);
+ else
+ cvClearMemStorage(storage);
+
+ // segment motion: get sequence of motion components
+ // segmask is marked motion components map. It is not used further
+ seq = cvSegmentMotion( mhi, segmask, storage, timestamp, max_time_delta );
+
+ // iterate through the motion components,
+ // One more iteration (i == -1) corresponds to the whole image (global motion)
+ j=0;
+ if (seq->total) for( i = -1; i < seq->total; i++ ) {
+
+ if( i < 0 ) { // case of the whole image
+ comp_rect = cvRect( 0, 0, size.width, size.height );
+ color = CV_RGB(255,255,255);
+ magnitude = 100;
+ }
+ else { // i-th motion component
+ comp_rect = ((CvConnectedComp*)cvGetSeqElem( seq, i ))->rect;
+ if(( comp_rect.width + comp_rect.height < min_size )||( comp_rect.width + comp_rect.height > max_size )) // reject very small/big components
+ continue;
+#ifdef _DARWIN_
+ color = CV_RGB(0,255,0);
+#else
+ color = CV_RGB(255,0,0);
+#endif
+ magnitude = (comp_rect.width + comp_rect.height) / 4;
+ }
+
+ // select component ROI
+ cvSetImageROI( silh, comp_rect );
+ cvSetImageROI( mhi, comp_rect );
+ cvSetImageROI( orient, comp_rect );
+ cvSetImageROI( mask, comp_rect );
+
+ // calculate orientation
+ angle = cvCalcGlobalOrientation( orient, mask, mhi, timestamp, mhi_duration);
+ angle = 360.0 - angle; // adjust for images with top-left origin
+
+ count = cvNorm( silh, 0, CV_L1, 0 ); // calculate number of points within silhouette ROI
+
+ cvResetImageROI( mhi );
+ cvResetImageROI( orient );
+ cvResetImageROI( mask );
+ cvResetImageROI( silh );
+
+
+ // draw a clock with arrow indicating the direction
+ center = cvPoint( (comp_rect.x + comp_rect.width/2),
+ (comp_rect.y + comp_rect.height/2) );
+
+ cvCircle( motion, center, cvRound(magnitude*1.2), color, 3, CV_AA, 0 );
+ cvLine( motion, center, cvPoint( cvRound( center.x + magnitude*cos(angle*CV_PI/180)),
+ cvRound( center.y - magnitude*sin(angle*CV_PI/180))), color, 3, CV_AA, 0 );
+
+ if( i < 0 ) // case of the whole image
+ {
+ sprintf( tindex, "%d", i );
+ cvPutText( motion, tindex, center, &font, CV_RGB(255,255,255));
+ SETFLOAT(&rlist[0], i);
+ SETFLOAT(&rlist[1], center.x);
+ SETFLOAT(&rlist[2], center.y);
+ SETFLOAT(&rlist[3], comp_rect.width);
+ SETFLOAT(&rlist[4], comp_rect.height);
+ SETFLOAT(&rlist[5], angle);
+ outlet_list( m_dataout, 0, 6, rlist );
+ }
+ else
+ {
+ sprintf( tindex, "%d", ++j );
+ cvPutText( motion, tindex, center, &font, CV_RGB(255,255,255));
+ SETFLOAT(&rlist[0], j);
+ SETFLOAT(&rlist[1], center.x);
+ SETFLOAT(&rlist[2], center.y);
+ SETFLOAT(&rlist[3], comp_rect.width);
+ SETFLOAT(&rlist[4], comp_rect.height);
+ SETFLOAT(&rlist[5], angle);
+ outlet_list( m_dataout, 0, 6, rlist );
+ }
+ }
+
+
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, motion->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_motempl :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_motempl : yuv format not supported" );
+}
+
+void pix_opencv_motempl :: processGrayImage(imageStruct &image)
+{
+ double timestamp = (double)clock()/CLOCKS_PER_SEC; // get current time in seconds
+ CvSize size = cvSize(image.xsize,image.ysize); // get current frame size
+ int i, j, idx1 = last, idx2;
+ IplImage* silh;
+ CvSeq* seq;
+ CvRect comp_rect;
+ double count;
+ double angle;
+ CvPoint center;
+ double magnitude;
+ CvScalar color;
+ char tindex[10];
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &motion );
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &grey );
+
+ //Create cv_images
+ rgb = cvCreateImage(cvSize(image.xsize, image.ysize), IPL_DEPTH_8U, 3);
+ motion = cvCreateImage( cvSize(rgb->width,rgb->height), 8, 3 );
+ cvZero( motion );
+ motion->origin = rgb->origin;
+ rgba = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 4 );
+ grey = cvCreateImage( cvSize(image.xsize, image.ysize), 8, 1 );
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( grey->imageData, image.data, image.xsize*image.ysize );
+
+ // Convert to RGB
+ cvCvtColor( grey, rgb, CV_GRAY2RGB);
+
+ // allocate images at the beginning or
+ // reallocate them if the frame size is changed
+ if( (!mhi) || (mhi->width != size.width) || (mhi->height != size.height) || (!buf)) {
+ if( buf == 0 ) {
+ buf = (IplImage**)malloc(frame_buffer_num*sizeof(buf[0]));
+ //memset( buf, 0, N*sizeof(buf[0]));
+ }
+
+ for( i = 0; i < frame_buffer_num; i++ ) {
+ // TODO cvReleaseImage( &(buf[i]) );
+ buf[i] = cvCreateImage( size, IPL_DEPTH_8U, 1 );
+ cvZero( buf[i] );
+ }
+ if ( mhi != NULL ) cvReleaseImage( &mhi );
+ if ( orient != NULL ) cvReleaseImage( &orient );
+ if ( segmask != NULL ) cvReleaseImage( &segmask );
+ if ( mask != NULL ) cvReleaseImage( &mask );
+
+ mhi = cvCreateImage( size, IPL_DEPTH_32F, 1 );
+ cvZero( mhi ); // clear MHI at the beginning
+ orient = cvCreateImage( size, IPL_DEPTH_32F, 1 );
+ segmask = cvCreateImage( size, IPL_DEPTH_32F, 1 );
+ mask = cvCreateImage( size, IPL_DEPTH_8U, 1 );
+ }
+
+ cvCvtColor( rgb, buf[last], CV_BGR2GRAY ); // convert frame to grayscale
+
+ idx2 = (last + 1) % frame_buffer_num; // index of (last - (N-1))th frame
+ last = idx2;
+
+ silh = buf[idx2];
+ cvAbsDiff( buf[idx1], buf[idx2], silh ); // get difference between frames
+
+ cvThreshold( silh, silh, diff_threshold, 1, CV_THRESH_BINARY ); // and threshold it
+ cvUpdateMotionHistory( silh, mhi, timestamp, mhi_duration ); // update MHI
+
+ // convert MHI to blue 8u image
+ cvCvtScale( mhi, mask, 255./mhi_duration,
+ (mhi_duration - timestamp)*255./mhi_duration );
+ cvZero( motion );
+ cvCvtPlaneToPix( mask, 0, 0, 0, motion );
+
+ // calculate motion gradient orientation and valid orientation mask
+ cvCalcMotionGradient( mhi, mask, orient, max_time_delta, min_time_delta, aperture );
+
+ if( !storage )
+ storage = cvCreateMemStorage(0);
+ else
+ cvClearMemStorage(storage);
+
+ // segment motion: get sequence of motion components
+ // segmask is marked motion components map. It is not used further
+ seq = cvSegmentMotion( mhi, segmask, storage, timestamp, max_time_delta );
+
+ // iterate through the motion components,
+ // One more iteration (i == -1) corresponds to the whole image (global motion)
+ j=0;
+ if (seq->total) for( i = -1; i < seq->total; i++ ) {
+
+ if( i < 0 ) { // case of the whole image
+ comp_rect = cvRect( 0, 0, size.width, size.height );
+ color = CV_RGB(255,255,255);
+ magnitude = 100;
+ }
+ else { // i-th motion component
+ comp_rect = ((CvConnectedComp*)cvGetSeqElem( seq, i ))->rect;
+ if(( comp_rect.width + comp_rect.height < min_size )||( comp_rect.width + comp_rect.height > max_size )) // reject very small components
+ continue;
+#ifdef _DARWIN_
+ color = CV_RGB(0,255,0);
+#else
+ color = CV_RGB(255,0,0);
+#endif
+ magnitude = (comp_rect.width + comp_rect.height) / 4;
+ }
+
+ // select component ROI
+ cvSetImageROI( silh, comp_rect );
+ cvSetImageROI( mhi, comp_rect );
+ cvSetImageROI( orient, comp_rect );
+ cvSetImageROI( mask, comp_rect );
+
+ // calculate orientation
+ angle = cvCalcGlobalOrientation( orient, mask, mhi, timestamp, mhi_duration);
+ angle = 360.0 - angle; // adjust for images with top-left origin
+
+ count = cvNorm( silh, 0, CV_L1, 0 ); // calculate number of points within silhouette ROI
+
+ cvResetImageROI( mhi );
+ cvResetImageROI( orient );
+ cvResetImageROI( mask );
+ cvResetImageROI( silh );
+
+
+ // draw a clock with arrow indicating the direction
+ center = cvPoint( (comp_rect.x + comp_rect.width/2),
+ (comp_rect.y + comp_rect.height/2) );
+
+ cvCircle( motion, center, cvRound(magnitude*1.2), color, 3, CV_AA, 0 );
+ cvLine( motion, center, cvPoint( cvRound( center.x + magnitude*cos(angle*CV_PI/180)),
+ cvRound( center.y - magnitude*sin(angle*CV_PI/180))), color, 3, CV_AA, 0 );
+
+ if( i < 0 ) // case of the whole image
+ {
+ sprintf( tindex, "%d", i );
+ cvPutText( motion, tindex, center, &font, CV_RGB(255,255,255));
+ SETFLOAT(&rlist[0], i);
+ SETFLOAT(&rlist[1], center.x);
+ SETFLOAT(&rlist[2], center.y);
+ SETFLOAT(&rlist[3], comp_rect.width);
+ SETFLOAT(&rlist[4], comp_rect.height);
+ SETFLOAT(&rlist[5], angle);
+ outlet_list( m_dataout, 0, 6, rlist );
+ }
+ else
+ {
+ sprintf( tindex, "%d", ++j );
+ cvPutText( motion, tindex, center, &font, CV_RGB(255,255,255));
+ SETFLOAT(&rlist[0], j);
+ SETFLOAT(&rlist[1], center.x);
+ SETFLOAT(&rlist[2], center.y);
+ SETFLOAT(&rlist[3], comp_rect.width);
+ SETFLOAT(&rlist[4], comp_rect.height);
+ SETFLOAT(&rlist[5], angle);
+ outlet_list( m_dataout, 0, 6, rlist );
+ }
+ }
+
+
+ // Convert to grayscale
+ cvCvtColor( motion, grey, CV_RGB2GRAY);
+ //cvShowImage(wndname, cedge);
+ memcpy( image.data, grey->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_motempl :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_motempl::thresholdMessCallback,
+ gensym("ft1"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_motempl::mhi_durationMessCallback,
+ gensym("mhi_duration"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_motempl::max_time_deltaMessCallback, gensym("max_time_delta"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_motempl::min_time_deltaMessCallback, gensym("min_time_delta"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_motempl::frame_buffer_numMessCallback, gensym("frame_buffer_num"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_motempl::min_sizeMessCallback, gensym("min_size"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_motempl::max_sizeMessCallback, gensym("max_size"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_motempl::apertureMessCallback, gensym("aperture"), A_FLOAT, A_NULL);
+}
+void pix_opencv_motempl :: thresholdMessCallback(void *data, t_floatarg pos)
+{
+ GetMyClass(data)->floatThreshMess((float)pos);
+}
+void pix_opencv_motempl :: mhi_durationMessCallback(void *data, t_floatarg mhi_duration)
+{
+ GetMyClass(data)->floatMhiDuration((float)mhi_duration);
+}
+void pix_opencv_motempl :: min_sizeMessCallback(void *data, t_floatarg min_size)
+{
+ GetMyClass(data)->floatmin_size((float)min_size);
+}
+void pix_opencv_motempl :: max_sizeMessCallback(void *data, t_floatarg max_size)
+{
+ GetMyClass(data)->floatmax_size((float)max_size);
+}
+void pix_opencv_motempl :: max_time_deltaMessCallback(void *data, t_floatarg max_time_delta)
+{
+ GetMyClass(data)->floatmax_time_delta((float)max_time_delta);
+}
+void pix_opencv_motempl :: min_time_deltaMessCallback(void *data, t_floatarg min_time_delta)
+{
+ GetMyClass(data)->floatmin_time_delta((float)min_time_delta);
+}
+void pix_opencv_motempl :: frame_buffer_numMessCallback(void *data, t_floatarg frame_buffer_num)
+{
+ GetMyClass(data)->floatframe_buffer_num((float)frame_buffer_num);
+}
+void pix_opencv_motempl :: apertureMessCallback(void *data, t_floatarg aperture)
+{
+ GetMyClass(data)->apertureMess((float)aperture);
+}
+void pix_opencv_motempl :: floatThreshMess(float thresh)
+{
+ if (thresh>=0) diff_threshold = (int)thresh;
+}
+void pix_opencv_motempl :: floatMhiDuration(float duration)
+{
+ if ( duration < 1.0 ) mhi_duration = duration;
+}
+void pix_opencv_motempl :: apertureMess(float aperture)
+{
+ if ( ( aperture == 3.0 ) || ( aperture == 5.0 ) || ( aperture == 7.0 ) )
+ {
+ aperture = (int)aperture;
+ }
+}
+void pix_opencv_motempl :: floatmax_size(float max_size)
+{
+ if (max_size>=0) this->max_size = (int)max_size;
+}
+void pix_opencv_motempl :: floatmin_size(float min_size)
+{
+ if (min_size>=0) this->min_size = (int)min_size;
+}
+void pix_opencv_motempl :: floatframe_buffer_num(float frame_buffer_num)
+{
+ if (frame_buffer_num>=3)
+ {
+ this->frame_buffer_num = (int)frame_buffer_num;
+ this->buf = NULL;
+ }
+}
+void pix_opencv_motempl :: floatmax_time_delta(float max_time_delta)
+{
+ if (max_time_delta>=0) this->max_time_delta = max_time_delta;
+}
+void pix_opencv_motempl :: floatmin_time_delta(float min_time_delta)
+{
+ if (min_time_delta>=0) this->min_time_delta = min_time_delta;
+}
diff --git a/src/pix_opencv_motempl.h b/src/pix_opencv_motempl.h
new file mode 100644
index 0000000..cd14223
--- /dev/null
+++ b/src/pix_opencv_motempl.h
@@ -0,0 +1,132 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Motion detection based on motion history
+
+ 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_MOTEMPL_H_
+#define INCLUDE_PIX_OPENCV_MOTEMPL_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/legacy/compat.hpp"
+#include "opencv2/video/tracking.hpp"
+
+#include <time.h>
+#include <math.h>
+#include <ctype.h>
+#endif
+#include <stdio.h>
+
+#include "Base/GemPixObj.h"
+
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_motempl
+
+ Motion detection based on motion history
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_motempl : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_motempl, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_motempl();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_motempl();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void floatThreshMess(float thresh);
+ void floatMhiDuration(float duration);
+ void floatmin_time_delta(float min_time_delta);
+ void floatmax_time_delta(float max_time_delta);
+ void floatframe_buffer_num(float frame_buffer_num);
+ void floatmax_size(float max_size);
+ void floatmin_size(float min_size);
+ void apertureMess(float aperture);
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ // Some varibales to control mophology mhi_duration
+ double mhi_duration;
+ int diff_threshold;
+ int mask_size;
+ int last;
+ int aperture;
+
+ // various tracking parameters (in seconds)
+ double max_time_delta;
+ double min_time_delta;
+ // number of cyclic frame buffer used for motion detection
+ // (should, probably, depend on FPS)
+ int frame_buffer_num;
+
+ int max_size;
+ int min_size;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void thresholdMessCallback(void *data, t_floatarg pos);
+ static void mhi_durationMessCallback(void *data, t_floatarg mhi_duration);
+ static void max_time_deltaMessCallback(void *data, t_floatarg max_time_delta);
+ static void min_time_deltaMessCallback(void *data, t_floatarg min_time_delta);
+ static void frame_buffer_numMessCallback(void *data, t_floatarg frame_buffer_num);
+ static void min_sizeMessCallback(void *data, t_floatarg min_size);
+ static void max_sizeMessCallback(void *data, t_floatarg max_size);
+ static void apertureMessCallback(void *data, t_floatarg aperture);
+
+ // The output and temporary images
+ IplImage *rgb, *motion, *rgba, *grey;
+
+ // ring image buffer
+ IplImage **buf;
+
+ // temporary images
+ IplImage *mhi; // MHI
+ IplImage *orient; // orientation
+ IplImage *mask; // valid orientation mask
+ IplImage *segmask; // motion segmentation map
+ CvMemStorage* storage; // temporary storage
+ t_outlet *m_dataout;
+ CvFont font;
+ t_atom rlist[6];
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_of_bm.cc b/src/pix_opencv_of_bm.cc
new file mode 100644
index 0000000..90e0a08
--- /dev/null
+++ b/src/pix_opencv_of_bm.cc
@@ -0,0 +1,326 @@
+
+//
+// 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 );
+
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), IPL_DEPTH_8U, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), IPL_DEPTH_8U, 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( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_of_bm :: processImage(imageStruct &image)
+{
+ int px,py;
+ double globangle=0.0, globx=0.0, globy=0.0, maxamp=0.0, maxangle=0.0;
+ int nbblocks=0;
+ CvPoint orig, dest;
+ double angle=0.0;
+ double hypotenuse=0.0;
+
+
+ if ((comp_xsize!=image.xsize) || (comp_ysize!=image.ysize))
+ {
+ printf("resize buffer\n");
+
+ 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( &grey );
+ cvReleaseImage( &prev_grey );
+ cvReleaseImage( &x_velx );
+ cvReleaseImage( &x_vely );
+
+ grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), IPL_DEPTH_8U, 1 );
+ prev_grey = cvCreateImage( cvSize(comp_xsize, comp_ysize), IPL_DEPTH_8U, 1 );
+
+ x_velx = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ x_vely = cvCreateImage( x_velsize, IPL_DEPTH_32F, 1 );
+ }
+
+ // no need to copy the image, just create valid header and point to image.data...
+ IplImage* imgMat = cvCreateImageHeader( cv::Size(image.xsize, image.ysize), IPL_DEPTH_8U, image.csize);
+ imgMat->imageData = (char*) image.data;
+
+ if ( image.csize == 1 ){ // gray
+ grey = imgMat;
+ } else if ( image.csize == 4 ) { //RGBA
+ cvCvtColor(imgMat, grey, CV_BGRA2GRAY);
+ } else {
+ error("support only RGBA or GRAY image");
+ return;
+ }
+
+ if( x_nightmode )
+ cvZero( imgMat );
+
+ printf("grey size : %dx%d %d, prev_grey size %dx%d %d\n",grey->width,grey->height,grey->nChannels,prev_grey->width,prev_grey->height,prev_grey->nChannels);
+
+ try {
+ cvCalcOpticalFlowBM( prev_grey, grey,
+ x_blocksize, x_shiftsize,
+ x_maxrange, x_useprevious,
+ x_velx, x_vely );
+ } catch (...) {
+ error("can't comupute OpticalFlow");
+ return;
+ }
+
+ nbblocks = 0;
+ globangle = 0;
+ globx = 0;
+ globy = 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) (dest.y-orig.y), (double) (dest.x-orig.x) );
+ hypotenuse = sqrt( pow((double) dest.y-orig.y, 2) + pow((double)dest.x-orig.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( imgMat, 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( imgMat, 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( imgMat, orig, dest, CV_RGB(0,0,255), (int)hypotenuse/10, CV_AA, 0 );
+
+ globx = globx+cvGet2D(x_velx, py, px).val[0];
+ globy = globy+cvGet2D(x_vely, py, px).val[0];
+ 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++;
+ }
+
+ }
+ }
+
+ if ( nbblocks >= x_minblocks )
+ {
+ globangle=-atan2( globy, globx );
+ // post( "pdp_opencv_of_bm : globangle : %f", (globangle*180)/M_PI );
+
+ 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(globangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(globangle));
+ cvLine( imgMat, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (x_shiftsize.width/2) * cos(globangle + M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/2) * sin(globangle + M_PI / 4));
+ cvLine( imgMat, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (x_shiftsize.width/2) * cos(globangle - M_PI / 4));
+ orig.y = (int) (dest.y + (x_shiftsize.height/2) * sin(globangle - M_PI / 4));
+ cvLine( imgMat, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ globangle = (globangle*180)/M_PI;
+ SETFLOAT(&x_list[0], globangle);
+ 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 );
+ }
+}
+
+/////////////////////////////////////////////////////////
+// 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)&&(fwidth<=100.0)) x_blocksize.width = (int)fwidth;
+ if ((fheight>=5.0)&&(fheight<=100.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)&&(fwidth<=100.0)) x_shiftsize.width = (int)fwidth;
+ if ((fheight>=5.0)&&(fheight<=100.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)&&(fwidth<=100.0)) x_maxrange.width = (int)fwidth;
+ if ((fheight>=5.0)&&(fheight<=100.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/src/pix_opencv_of_bm.h b/src/pix_opencv_of_bm.h
new file mode 100644
index 0000000..a21950c
--- /dev/null
+++ b/src/pix_opencv_of_bm.h
@@ -0,0 +1,101 @@
+/*-----------------------------------------------------------------
+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
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/video/tracking.hpp"
+
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_of_bm
+
+ Optical Flow block Matching algorithm
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT 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 processImage(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 *grey, *prev_grey;
+ IplImage *x_velx, *x_vely;
+ CvFont font;
+
+ t_atom x_list[3];
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_of_hs.cc b/src/pix_opencv_of_hs.cc
new file mode 100644
index 0000000..b7e248e
--- /dev/null
+++ b/src/pix_opencv_of_hs.cc
@@ -0,0 +1,531 @@
+
+//
+// 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 globangle=0.0, globx=0.0, globy=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;
+ globangle = 0;
+ globx = 0;
+ globy = 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) (dest.y-orig.y), (double) (dest.x-orig.x) );
+ hypotenuse = sqrt( pow((double)dest.y-orig.y, 2) + pow((double)dest.x-orig.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 );
+
+ globx = globx+cvGet2D(x_velx, py, px).val[0];
+ globy = globy+cvGet2D(x_vely, py, px).val[0];
+ 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++;
+ }
+
+ }
+ }
+
+ if ( nbblocks >= x_minblocks )
+ {
+ globangle=-atan2( globy, globx );
+ // post( "pdp_opencv_of_bm : globangle : %f", (globangle*180)/M_PI );
+
+ 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(globangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(globangle));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ globangle = (globangle*180)/M_PI;
+ SETFLOAT(&x_list[0], globangle);
+ 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 globangle=0.0, globx=0.0, globy=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;
+ globangle = 0;
+ globx = 0;
+ globy = 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) (dest.y-orig.y), (double) (dest.x-orig.x) );
+ hypotenuse = sqrt( pow((double)dest.y-orig.y, 2) + pow((double)dest.x-orig.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 );
+
+ globx = globx+cvGet2D(x_velx, py, px).val[0];
+ globy = globy+cvGet2D(x_vely, py, px).val[0];
+ 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++;
+ }
+
+ }
+ }
+
+ if ( nbblocks >= x_minblocks )
+ {
+ globangle=-atan2( globy, globx );
+ // post( "pdp_opencv_of_bm : globangle : %f", (globangle*180)/M_PI );
+
+ 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(globangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(globangle));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ globangle = (globangle*180)/M_PI;
+ SETFLOAT(&x_list[0], globangle);
+ 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 globangle=0.0, globx=0.0, globy=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;
+ globangle = 0;
+ globx = 0;
+ globy = 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) (dest.y-orig.y), (double) (dest.x-orig.x) );
+ hypotenuse = sqrt( pow((double)dest.y-orig.y, 2) + pow((double)dest.x-orig.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 );
+
+ globx = globx+cvGet2D(x_velx, py, px).val[0];
+ globy = globx+cvGet2D(x_vely, py, px).val[0];
+ 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++;
+ }
+
+ }
+ }
+
+ if ( nbblocks >= x_minblocks )
+ {
+ globangle=-atan2( globy, globx );
+ // post( "pdp_opencv_of_bm : globangle : %f", (globangle*180)/M_PI );
+
+ 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(globangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(globangle));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle + M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle - M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ globangle = (globangle*180)/M_PI;
+ SETFLOAT(&x_list[0], globangle);
+ 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/src/pix_opencv_of_hs.h b/src/pix_opencv_of_hs.h
new file mode 100644
index 0000000..8069fbe
--- /dev/null
+++ b/src/pix_opencv_of_hs.h
@@ -0,0 +1,100 @@
+/*-----------------------------------------------------------------
+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
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/video/tracking.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_of_hs
+
+ Horn and Schunck Optical Flow algorithm
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT 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/src/pix_opencv_of_lk.cc b/src/pix_opencv_of_lk.cc
new file mode 100644
index 0000000..59d5115
--- /dev/null
+++ b/src/pix_opencv_of_lk.cc
@@ -0,0 +1,516 @@
+
+//
+// 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 = 9;
+ x_winsize.height = 9;
+ 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 globangle=0.0, globx=0.0, globy=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;
+ globangle = 0;
+ globx = 0;
+ globy = 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) (dest.y-orig.y), (double) (dest.x-orig.x) );
+ hypotenuse = sqrt( pow((double)dest.y-orig.y, 2) + pow((double)dest.x-orig.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 );
+
+ globx = globx+cvGet2D(x_velx, py, px).val[0];
+ globy = globy+cvGet2D(x_vely, py, px).val[0];
+ 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++;
+ }
+
+ }
+ }
+
+ if ( nbblocks >= x_minblocks )
+ {
+ globangle=-atan2( globy, globx );
+ // post( "pdp_opencv_of_bm : globangle : %f", (globangle*180)/M_PI );
+
+ 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(globangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(globangle));
+
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ globangle = (globangle*180)/M_PI;
+ SETFLOAT(&x_list[0], globangle);
+ 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 globangle=0.0, globx=0.0, globy=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;
+ globangle = 0;
+ globx = 0;
+ globy = 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) (dest.y-orig.y), (double) (dest.x-orig.x) );
+ hypotenuse = sqrt( pow((double)dest.y-orig.y, 2) + pow((double)dest.x-orig.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 );
+
+ globx = globx+cvGet2D(x_velx, py, px).val[0];
+ globy = globy+cvGet2D(x_vely, py, px).val[0];
+ 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++;
+ }
+
+ }
+ }
+
+ if ( nbblocks >= x_minblocks )
+ {
+ globangle=-atan2( globy, globx );
+ // post( "pdp_opencv_of_bm : globangle : %f", (globangle*180)/M_PI );
+
+ 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(globangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(globangle));
+
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle + M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle - M_PI / 4));
+ cvLine( rgb, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ globangle = (globangle*180)/M_PI;
+ SETFLOAT(&x_list[0], globangle);
+ 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 globangle=0.0, globx=0.0, globy=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;
+ globangle = 0;
+ globx = 0;
+ globy = 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) (dest.y-orig.y), (double) (dest.x-orig.x) );
+ hypotenuse = sqrt( pow((double)dest.y-orig.y, 2) + pow((double)dest.x-orig.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 );
+
+ globx = globx+cvGet2D(x_velx, py, px).val[0];
+ globy = globy+cvGet2D(x_vely, py, px).val[0];
+ 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++;
+ }
+
+ }
+ }
+
+ globangle=-atan2( globy, globx );
+ // post( "pdp_opencv_of_bm : globangle : %f", (globangle*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(globangle));
+ dest.y = (int) (orig.y-((comp_xsize>comp_ysize)?comp_ysize/2:comp_xsize/2)*sin(globangle));
+
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle + M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle + M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+ orig.x = (int) (dest.x - (6) * cos(globangle - M_PI / 4));
+ orig.y = (int) (dest.y + (6) * sin(globangle - M_PI / 4));
+ cvLine( grey, orig, dest, CV_RGB(255,255,255), 3, CV_AA, 0 );
+
+ // outputs the average angle of movement
+ globangle = (globangle*180)/M_PI;
+ SETFLOAT(&x_list[0], globangle);
+ 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==1.0 || fwidth==3.0 || fwidth==5.0 || fwidth==7.0 || fwidth==9.0 || fwidth==11.0 || fwidth==13.0 || fwidth==15.0 ) x_winsize.width = (int)fwidth;
+ else post( "pdp_opencv_of_lk : wrong winsize width : must be one of (1,3,5,7,9,11,13,15)" );
+ if (fheight==1.0 || fheight==3.0 || fheight==5.0 || fheight==7.0 || fheight==9.0 || fheight==11.0 || fheight==13.0 || fheight==15.0 ) x_winsize.height = (int)fheight;
+ else post( "pdp_opencv_of_lk : wrong winsize height : must be one of (1,3,5,7,9,11,13,15)" );
+}
+
+void pix_opencv_of_lk :: minBlocksMess(float minblocks)
+{
+ if (minblocks>=1.0) x_minblocks = (int)minblocks;
+}
+
diff --git a/src/pix_opencv_of_lk.h b/src/pix_opencv_of_lk.h
new file mode 100644
index 0000000..bad8297
--- /dev/null
+++ b/src/pix_opencv_of_lk.h
@@ -0,0 +1,96 @@
+/*-----------------------------------------------------------------
+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
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/video/tracking.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_of_lk
+
+ Lucas / Kanade Optical Flow algorithm
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT 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
diff --git a/src/pix_opencv_opticalflow.cc b/src/pix_opencv_opticalflow.cc
new file mode 100644
index 0000000..cfce8f6
--- /dev/null
+++ b/src/pix_opencv_opticalflow.cc
@@ -0,0 +1,229 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org ) (pix_opencv)
+// pix_opencv_opticalflow compute optical flow, several algorithms are available in one object
+// by Antoine Villeret - 2012
+
+#include "pix_opencv_opticalflow.h"
+
+CPPEXTERN_NEW(pix_opencv_opticalflow)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_opticalflow
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_opticalflow :: pix_opencv_opticalflow() : m_gain(1.)
+{
+ m_dataout_middle = outlet_new(this->x_obj, 0);
+ m_dataout_right = outlet_new(this->x_obj, 0);
+
+ //~ post("build on %s at %s", __DATE__, __TIME__);
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_opticalflow :: ~pix_opencv_opticalflow()
+{
+}
+
+/////////////////////////////////////////////////////////
+// render
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_opticalflow :: processRGBAImage(imageStruct &image)
+{
+ if ( image.xsize <= 0 || image.ysize <= 0 ) return;
+
+ cv::Mat rgbaImage( image.ysize, image.xsize, CV_8UC4, image.data, image.csize*image.xsize); // just transform imageStruct to IplImage without copying data
+ //~cvtColor(rgbaImage, m_curr, cv::COLOR_RGBA2RGB); //convert RGBA to RGB
+ cvtColor(rgbaImage, m_curr, cv::COLOR_RGBA2GRAY); //convert RGBA to RGB
+
+ if (m_prev.size() != m_curr.size()){
+ m_prev = m_curr.clone();
+ }
+ //~cv::calcOpticalFlowSF(m_curr, m_prev, m_flow, 3, 2, 4, 4.1, 25.5, 18, 55.0, 25.5, 0.35, 18, 55.0, 25.5, 10);
+
+ cv::calcOpticalFlowFarneback(m_curr, m_prev, flow, 0.5, 3, 15, 3, 5, 1.2, 0);
+ m_prev = m_curr.clone(); // copy data
+
+ cv::Size size = m_curr.size();
+ // here is the idiom: check the arrays for continuity and,
+ // if this is the case,
+ // treat the arrays as 1D vectors
+ if( m_curr.isContinuous() && m_prev.isContinuous() && m_flow.isContinuous() )
+ {
+ size.width *= size.height;
+ size.height = 1;
+ }
+
+ float gain=m_gain;
+ if ( m_normalize ){
+ float maxrad=1;
+
+ for (int y = 0; y < flow.rows; ++y)
+ {
+ for (int x = 0; x < flow.cols; ++x)
+ {
+ cv::Point2f u = flow.at<cv::Point2f>(x,y);
+
+ if (!isFlowCorrect(u))
+ continue;
+ float rad = sqrt(u.x * u.x + u.y * u.y);
+ maxrad = maxrad>rad?maxrad:rad;
+ }
+ }
+ gain=1/maxrad;
+ }
+
+ for( int i = 0; i < size.height; i++ )
+ {
+ // when the arrays are continuous,
+ // the outer loop is executed only once
+ const float* ptrFlow = flow.ptr<float>(i);
+ unsigned char* data=image.data+i*image.csize*image.xsize;
+
+
+ for( int j = 0; j < 2*size.width; j+=2 )
+ {
+ float fx = ptrFlow[j];
+ float fy = ptrFlow[j+1];
+
+ cv::Vec3b pix;
+
+ pix = computeColor(fx*gain, fy*gain);
+
+ for ( int k = 0; k < 3; k++ ){
+ data[k]=pix[k];
+ }
+ //~printf("pix %d : %d\t%d\t%d\n",j+i*size.width, pix[0],pix[1], pix[2]);
+ //~m_colorcode.computeColor(fx, fy, data);
+ data+=4;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_opticalflow :: obj_setupCallback(t_class *classPtr)
+{
+ CPPEXTERN_MSG1(classPtr, "gain", gainMess, double);
+ CPPEXTERN_MSG1(classPtr, "normalize", normalizeMess, double);
+}
+
+/////////////////////////////////////////////////////////
+// messages handling
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_opticalflow :: gainMess(double arg)
+{
+ m_gain = arg > 0 ? arg : 3.;
+}
+
+void pix_opencv_opticalflow :: normalizeMess(double arg)
+{
+ m_normalize = arg > 0;
+}
+
+
+
+///////////////////////////
+// static function for color coding
+///////////////////////////
+using namespace cv;
+
+static cv::Vec3b computeColor(float fx, float fy)
+{
+static bool first = true;
+
+ // relative lengths of color transitions:
+ // these are chosen based on perceptual similarity
+ // (e.g. one can distinguish more shades between red and yellow
+ // than between yellow and green)
+ const int RY = 15;
+ const int YG = 6;
+ const int GC = 4;
+ const int CB = 11;
+ const int BM = 13;
+ const int MR = 6;
+ const int NCOLS = RY + YG + GC + CB + BM + MR;
+ static Vec3i colorWheel[NCOLS];
+
+ if (first)
+ {
+ int k = 0;
+
+ for (int i = 0; i < RY; ++i, ++k)
+ colorWheel[k] = Vec3i(255, 255 * i / RY, 0);
+
+ for (int i = 0; i < YG; ++i, ++k)
+ colorWheel[k] = Vec3i(255 - 255 * i / YG, 255, 0);
+
+ for (int i = 0; i < GC; ++i, ++k)
+ colorWheel[k] = Vec3i(0, 255, 255 * i / GC);
+
+ for (int i = 0; i < CB; ++i, ++k)
+ colorWheel[k] = Vec3i(0, 255 - 255 * i / CB, 255);
+
+ for (int i = 0; i < BM; ++i, ++k)
+ colorWheel[k] = Vec3i(255 * i / BM, 0, 255);
+
+ for (int i = 0; i < MR; ++i, ++k)
+ colorWheel[k] = Vec3i(255, 0, 255 - 255 * i / MR);
+
+ first = false;
+ }
+
+ const float rad = sqrt(fx * fx + fy * fy);
+ const float a = atan2(-fy, -fx) / (float) CV_PI;
+
+ const float fk = (a + 1.0f) / 2.0f * (NCOLS - 1);
+ const int k0 = static_cast<int>(fk);
+ const int k1 = (k0 + 1) % NCOLS;
+ const float f = fk - k0;
+
+ Vec3b pix;
+
+ for (int b = 0; b < 3; b++)
+ {
+ const float col0 = colorWheel[k0][b] / 255.0f;
+ const float col1 = colorWheel[k1][b] / 255.0f;
+
+ float col = (1 - f) * col0 + f * col1;
+
+ if (rad <= 1)
+ col = 1 - rad * (1 - col); // increase saturation with radius
+ else
+ col *= .75; // out of range
+
+ pix[2 - b] = static_cast<uchar>(255.0 * col);
+ }
+
+ return pix;
+}
+
+inline bool isFlowCorrect(cv::Point2f u)
+{
+ return !cvIsNaN(u.x) && !cvIsNaN(u.y) && fabs(u.x) < 1e9 && fabs(u.y) < 1e9;
+}
diff --git a/src/pix_opencv_opticalflow.h b/src/pix_opencv_opticalflow.h
new file mode 100644
index 0000000..57a1583
--- /dev/null
+++ b/src/pix_opencv_opticalflow.h
@@ -0,0 +1,96 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ 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_OPTICALFLOW_H_
+#define INCLUDE_PIX_OPENCV_OPTICALFLOW_H_
+
+#include "opencv2/opencv.hpp"
+#include "Base/GemBase.h"
+#include "Gem/Exception.h"
+#include "Gem/State.h"
+#include "Base/GemPixObj.h"
+#include "RTE/MessageCallbacks.h"
+#include <stdio.h>
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_opticalflow
+ apply a simple flow method to compute optical flow
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+
+//~class Colorcode
+//~{
+//~// from colorcode.cpp by Daniel Scharstein, 4/2007
+//~// http://vision.middlebury.edu/flow/data/
+//~#define MAXCOLS 60
+//~
+//~public:
+ //~Colorcode();
+ //~static cv::Vec3b computeColor(float fx, float fy);
+ //~
+//~private:
+ //~int ncols;
+ //~int colorwheel[MAXCOLS][3];
+//~
+//~
+ //~void setcols(int r, int g, int b, int k);
+//~
+ //~void makecolorwheel();
+//~};
+
+static cv::Vec3b computeColor(float fx, float fy);
+inline bool isFlowCorrect(cv::Point2f u);
+
+class GEM_EXPORT pix_opencv_opticalflow : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_opticalflow, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_opticalflow();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_opticalflow();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+
+ // Messages handling
+ void gainMess(double arg);
+ void normalizeMess(double arg);
+
+ private:
+
+ cv::Mat prev, flow;
+ t_float m_gain;
+ bool m_normalize;
+
+ cv::Mat m_prev, m_curr, m_flow;
+
+ t_outlet *m_dataout_middle; // contour outlet
+ t_outlet *m_dataout_right; // info outlet
+};
+#endif // for header file
diff --git a/src/pix_opencv_patreco.h b/src/pix_opencv_patreco.h
new file mode 100644
index 0000000..0c4b62d
--- /dev/null
+++ b/src/pix_opencv_patreco.h
@@ -0,0 +1,95 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Threshold filter
+
+ 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_patreco_H_
+#define INCLUDE_pix_opencv_patreco_H_
+
+#ifndef _EiC
+#include "cv.h"
+#endif
+
+// ARma lib
+#include "pattern.hpp"
+#include "patterndetector.hpp"
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_patreco
+
+ square pattern detector
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+
+class GEM_EXPORT pix_opencv_patreco : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_patreco, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_patreco();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_patreco();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set parameters
+ void loadIntraMess(t_symbol *filename);
+ void loadDistMess(t_symbol *filename);
+ void loadMess(t_symbol *s, int argc, t_atom* argv);
+ void fixedThreshMess(float arg);
+ void adaptThreshMess(float arg);
+ void adaptBlockSizeMess(float arg);
+ void threshModeMess(float arg);
+ void patternSizeMess(float arg);
+ void monitorStageMess(t_float arg);
+ void ARTpatternMess(t_float arg);
+ void dilateMess(t_float arg);
+ void erodeMess(t_float arg);
+ void clearLibMess(void);
+
+ private:
+
+ t_outlet *m_dataout;
+
+ cv::Mat m_cameraMatrix, m_distortions;
+
+ ARma::PatternDetector *m_detector;
+ //~ std::vector<cv::Mat> m_patternLibrary; // pattern library
+ std::map<int, PatternLib> m_patternLibrary;
+ std::vector<ARma::Pattern> m_detectedPattern; // detected pattern
+ int m_pattern_size;
+
+};
+#endif // for header file
diff --git a/src/pix_opencv_pgh_compare.cc b/src/pix_opencv_pgh_compare.cc
new file mode 100644
index 0000000..4af6d8e
--- /dev/null
+++ b/src/pix_opencv_pgh_compare.cc
@@ -0,0 +1,481 @@
+////////////////////////////////////////////////////////
+//
+// GEM - Graphics Environment for Multimedia
+//
+// zmoelnig@iem.kug.ac.at
+//
+// Implementation file
+//
+// Copyright (c) 1997-1998 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_pgh_compare.h"
+
+CPPEXTERN_NEW_WITH_GIMME(pix_opencv_pgh_compare)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_pgh_compare
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_pgh_compare :: pix_opencv_pgh_compare(int argc, t_atom*argv)
+{
+ m_dataout = outlet_new(this->x_obj, &s_anything);
+ m_posout = outlet_new(this->x_obj, &s_anything);
+
+ comp_xsize=320;
+ comp_ysize=240;
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ rgbar = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgbr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ grayr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+
+ x_storage = cvCreateMemStorage(0);
+
+ x_bcontourr = NULL;
+ x_minsize = 10*10;
+ x_cdistance = 0.05;
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_pgh_compare :: ~pix_opencv_pgh_compare()
+{
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgbar);
+ cvReleaseImage(&rgbr);
+ cvReleaseImage(&grayr);
+}
+
+/////////////////////////////////////////////////////////
+// processDualImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_pgh_compare :: processRGBA_RGBA(imageStruct &left, imageStruct &right)
+{
+ double dist = 100.0, ndist;
+ int i = 0; // Indicator of cycles.
+ CvSeq *contourl=NULL, *contourlp;
+ CvRect rect;
+ CvMemStorage *mstorage;
+ CvSeq *contourr = NULL;
+ int size;
+ int dims[] = {8, 8};
+ float range[] = {-180, 180, -100, 100};
+ float *ranges[] = {&range[0], &range[2]};
+ CvHistogram *histl, *histr ;
+
+ if ((left.xsize!=right.xsize) || (left.ysize!=right.ysize) )
+ {
+ post( "pix_opencv_pgh_compare : left and right image are not of the same size" );
+ return;
+ }
+
+ if ((this->comp_xsize!=left.xsize)&&(this->comp_ysize!=left.ysize))
+ {
+ this->comp_xsize=left.xsize;
+ this->comp_ysize=left.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgbar);
+ cvReleaseImage(&rgbr);
+ cvReleaseImage(&grayr);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ rgbar = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgbr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ grayr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ }
+
+ memcpy( rgbar->imageData, right.data, right.xsize*right.ysize*4 );
+ cvCvtColor(rgbar, grayr, CV_BGRA2GRAY);
+
+ // calculate the biggest contour
+ try {
+ cvFindContours( grayr, x_storage, &contourr, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ }
+ catch(...) {
+ post( "pix_opencv_pgh_compare : error calculating contours" );
+ return;
+ }
+
+ if ( contourr )
+ {
+ size=0;
+ for( ; contourr != 0; contourr = contourr->h_next )
+ {
+ rect = cvContourBoundingRect( contourr, 1);
+ if ( rect.width*rect.height > size && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ x_bcontourr = contourr;
+ size = rect.width*rect.height;
+ }
+ }
+ }
+
+ memcpy( rgba->imageData, left.data, left.xsize*left.ysize*4 );
+ cvCvtColor(rgba, gray, CV_BGRA2GRAY);
+
+ mstorage = cvCreateMemStorage(0);
+
+ cvFindContours( gray, mstorage, &contourl, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+
+ i=0;
+ if ( contourl && x_bcontourr )
+ {
+ contourlp=contourl;
+ for( ; contourlp != 0; contourlp = contourlp->h_next )
+ {
+ rect = cvContourBoundingRect( contourlp, 1);
+ if ( rect.width*rect.height > x_minsize && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ histr = cvCreateHist(2, dims, CV_HIST_ARRAY, ranges, 1);
+ histl = cvCreateHist(2, dims, CV_HIST_ARRAY, ranges, 1);
+ cvCalcPGH(x_bcontourr, histr);
+ cvCalcPGH(contourlp, histl);
+ cvNormalizeHist(histr, 100.0f);
+ cvNormalizeHist(histl, 100.0f);
+ ndist = cvCompareHist(histr, histl, CV_COMP_BHATTACHARYYA);
+ cvReleaseHist(&histr);
+ cvReleaseHist(&histl);
+
+ if ( ndist < dist ) dist = ndist;
+ if ( ndist < x_cdistance )
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(255,255,255), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(255,255,255), CV_RGB(255,255,255), 0, 1, 8, cvPoint(0,0) );
+ SETFLOAT(&rlist[0], i++);
+ SETFLOAT(&rlist[1], rect.x);
+ SETFLOAT(&rlist[2], rect.y);
+ SETFLOAT(&rlist[3], rect.width);
+ SETFLOAT(&rlist[4], rect.height);
+ outlet_list( m_posout, 0, 5, rlist );
+ }
+ else
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(128,128,128), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(128,128,128), CV_RGB(128,128,128), 0, 1, 8, cvPoint(0,0) );
+ }
+ }
+ }
+ }
+
+ if ( dist < 100.00 ) outlet_float( m_dataout, dist );
+
+ cvReleaseMemStorage(&mstorage);
+
+ cvCvtColor(gray, rgba, CV_GRAY2BGR);
+ memcpy( left.data, rgba->imageData, left.xsize*left.ysize*4 );
+
+}
+
+/////////////////////////////////////////////////////////
+// processDualImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_pgh_compare :: processRGB_RGB(imageStruct &left, imageStruct &right)
+{
+ double dist = 100.0, ndist;
+ int i = 0; // Indicator of cycles.
+ CvSeq *contourl=NULL, *contourlp;
+ CvRect rect;
+ CvMemStorage *mstorage;
+ CvSeq *contourr = NULL;
+ int size;
+ int dims[] = {8, 8};
+ float range[] = {-180, 180, -100, 100};
+ float *ranges[] = {&range[0], &range[2]};
+ CvHistogram *histl, *histr ;
+
+ if ((left.xsize!=right.xsize) || (left.ysize!=right.ysize) )
+ {
+ post( "pix_opencv_pgh_compare : left and right image are not of the same size" );
+ return;
+ }
+
+ if ((this->comp_xsize!=left.xsize)&&(this->comp_ysize!=left.ysize))
+ {
+ this->comp_xsize=left.xsize;
+ this->comp_ysize=left.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgbar);
+ cvReleaseImage(&rgbr);
+ cvReleaseImage(&grayr);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ rgbar = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgbr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ grayr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ }
+
+ memcpy( rgbr->imageData, right.data, right.xsize*right.ysize*3 );
+ cvCvtColor(rgbr, grayr, CV_BGRA2GRAY);
+
+ // calculate the biggest contour
+ try {
+ cvFindContours( grayr, x_storage, &contourr, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ }
+ catch(...) {
+ post( "pix_opencv_pgh_compare : error calculating contours" );
+ return;
+ }
+
+ if ( contourr )
+ {
+ size=0;
+ for( ; contourr != 0; contourr = contourr->h_next )
+ {
+ rect = cvContourBoundingRect( contourr, 1);
+ if ( rect.width*rect.height > size && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ x_bcontourr = contourr;
+ size = rect.width*rect.height;
+ }
+ }
+ }
+
+ memcpy( rgb->imageData, left.data, left.xsize*left.ysize*3 );
+ cvCvtColor(rgb, gray, CV_BGRA2GRAY);
+
+ mstorage = cvCreateMemStorage(0);
+
+ cvFindContours( gray, mstorage, &contourl, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+
+ if ( contourl && x_bcontourr )
+ {
+ contourlp=contourl;
+ for( ; contourlp != 0; contourlp = contourlp->h_next )
+ {
+ rect = cvContourBoundingRect( contourlp, 1);
+ if ( rect.width*rect.height > x_minsize && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ histr = cvCreateHist(2, dims, CV_HIST_ARRAY, ranges, 1);
+ histl = cvCreateHist(2, dims, CV_HIST_ARRAY, ranges, 1);
+ cvCalcPGH(x_bcontourr, histr);
+ cvCalcPGH(contourlp, histl);
+ cvNormalizeHist(histr, 100.0f);
+ cvNormalizeHist(histl, 100.0f);
+ ndist = cvCompareHist(histr, histl, CV_COMP_BHATTACHARYYA);
+ cvReleaseHist(&histr);
+ cvReleaseHist(&histl);
+
+ if ( ndist < dist ) dist = ndist;
+ if ( ndist < x_cdistance )
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(255,255,255), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(255,255,255), CV_RGB(255,255,255), 0, 1, 8, cvPoint(0,0) );
+ }
+ else
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(128,128,128), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(128,128,128), CV_RGB(128,128,128), 0, 1, 8, cvPoint(0,0) );
+ }
+ }
+ }
+ }
+
+ if ( dist < 100.00 ) outlet_float( m_dataout, dist );
+
+ cvReleaseMemStorage(&mstorage);
+
+ cvCvtColor(gray, rgb, CV_GRAY2BGR);
+ memcpy( left.data, rgb->imageData, left.xsize*left.ysize*3 );
+
+}
+
+/////////////////////////////////////////////////////////
+// processDualImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_pgh_compare :: processGray_Gray(imageStruct &left, imageStruct &right)
+{
+ double dist = 100.0, ndist;
+ int i = 0; // Indicator of cycles.
+ CvSeq *contourl=NULL, *contourlp;
+ CvRect rect;
+ CvMemStorage *mstorage;
+ CvSeq *contourr = NULL;
+ int size;
+ int dims[] = {8, 8};
+ float range[] = {-180, 180, -100, 100};
+ float *ranges[] = {&range[0], &range[2]};
+ CvHistogram *histl, *histr ;
+
+
+ if ((left.xsize!=right.xsize) || (left.ysize!=right.ysize) )
+ {
+ post( "pix_opencv_pgh_compare : left and right image are not of the same size" );
+ return;
+ }
+
+ if ((this->comp_xsize!=left.xsize)&&(this->comp_ysize!=left.ysize))
+ {
+ this->comp_xsize=left.xsize;
+ this->comp_ysize=left.ysize;
+
+ cvReleaseImage(&rgba);
+ cvReleaseImage(&rgb);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgbar);
+ cvReleaseImage(&rgbr);
+ cvReleaseImage(&grayr);
+
+ rgba = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ rgbar = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 4);
+ rgbr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 3);
+ grayr = cvCreateImage(cvSize(comp_xsize,comp_ysize), IPL_DEPTH_8U, 1);
+ }
+
+ memcpy( grayr->imageData, right.data, right.xsize*right.ysize );
+
+ // calculate the biggest contour
+ try {
+ cvFindContours( grayr, x_storage, &contourr, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+ }
+ catch(...) {
+ post( "pix_opencv_pgh_compare : error calculating contours" );
+ return;
+ }
+
+ if ( contourr )
+ {
+ size=0;
+ for( ; contourr != 0; contourr = contourr->h_next )
+ {
+ rect = cvContourBoundingRect( contourr, 1);
+ if ( rect.width*rect.height > size && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ x_bcontourr = contourr;
+ size = rect.width*rect.height;
+ }
+ }
+ }
+
+ memcpy( gray->imageData, left.data, left.xsize*left.ysize );
+
+ mstorage = cvCreateMemStorage(0);
+
+ cvFindContours( gray, mstorage, &contourl, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0) );
+
+ if ( contourl && x_bcontourr )
+ {
+ contourlp=contourl;
+ for( ; contourlp != 0; contourlp = contourlp->h_next )
+ {
+ rect = cvContourBoundingRect( contourlp, 1);
+ if ( rect.width*rect.height > x_minsize && rect.width*rect.height < (comp_xsize-2)*(comp_ysize-2))
+ {
+ histr = cvCreateHist(2, dims, CV_HIST_ARRAY, ranges, 1);
+ histl = cvCreateHist(2, dims, CV_HIST_ARRAY, ranges, 1);
+ cvCalcPGH(x_bcontourr, histr);
+ cvCalcPGH(contourlp, histl);
+ cvNormalizeHist(histr, 100.0f);
+ cvNormalizeHist(histl, 100.0f);
+ ndist = cvCompareHist(histr, histl, CV_COMP_BHATTACHARYYA);
+ cvReleaseHist(&histr);
+ cvReleaseHist(&histl);
+
+ if ( ndist < dist ) dist = ndist;
+ if ( ndist < x_cdistance )
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(255,255,255), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(255,255,255), CV_RGB(255,255,255), 0, 1, 8, cvPoint(0,0) );
+ }
+ else
+ {
+ cvRectangle( gray, cvPoint(rect.x,rect.y), cvPoint(rect.x+rect.width,rect.y+rect.height), CV_RGB(128,128,128), 2, 8 , 0 );
+ cvDrawContours( gray, contourlp, CV_RGB(128,128,128), CV_RGB(128,128,128), 0, 1, 8, cvPoint(0,0) );
+ }
+ }
+ }
+ }
+
+ if ( dist < 100.00 ) outlet_float( m_dataout, dist );
+
+ cvReleaseMemStorage(&mstorage);
+
+ memcpy( left.data, gray->imageData, left.xsize*left.ysize );
+
+}
+
+void pix_opencv_pgh_compare :: processYUV_YUV(imageStruct &left, imageStruct &right)
+{
+ post( "pix_opencv_pgh_compare : YUV colorspace not supported" );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_pgh_compare :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_pgh_compare::floatMinSizeMessCallback,
+ gensym("minsize"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_pgh_compare::clearMessCallback,
+ gensym("clear"), A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_pgh_compare::floatCriteriaMessCallback,
+ gensym("criteria"), A_FLOAT, A_NULL);
+}
+
+void pix_opencv_pgh_compare :: floatMinSizeMessCallback(void *data, t_floatarg minsize)
+{
+ GetMyClass(data)->floatMinSizeMess((float)minsize);
+}
+
+void pix_opencv_pgh_compare :: clearMessCallback(void *data)
+{
+ GetMyClass(data)->clearMess();
+}
+
+void pix_opencv_pgh_compare :: floatCriteriaMessCallback(void *data, t_floatarg criteria)
+{
+ GetMyClass(data)->floatCriteriaMess((float)criteria);
+}
+
+void pix_opencv_pgh_compare :: floatMinSizeMess(float minsize)
+{
+ if ( (int)minsize > 0 )
+ {
+ x_minsize = (int)minsize;
+ }
+}
+
+void pix_opencv_pgh_compare :: clearMess(void)
+{
+ x_bcontourr = NULL;
+}
+
+void pix_opencv_pgh_compare :: floatCriteriaMess(float criteria)
+{
+ if ( criteria > 0.0 )
+ {
+ x_cdistance = criteria;
+ }
+}
diff --git a/src/pix_opencv_pgh_compare.h b/src/pix_opencv_pgh_compare.h
new file mode 100644
index 0000000..774ea0f
--- /dev/null
+++ b/src/pix_opencv_pgh_compare.h
@@ -0,0 +1,91 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ PGH histogram comparison used to compare contours
+
+ Copyright (c) 1997-1998 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_PGH_COMPARE_H_
+#define INCLUDE_PIX_OPENCV_PGH_COMPARE_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/legacy/compat.hpp"
+
+#endif
+
+#include "Base/GemPixDualObj.h"
+
+/*-----------------------------------------------------------------
+CLASS
+ pix_opencv_pgh_compare
+
+ PGH histogram comparison used to compare contours
+
+-----------------------------------------------------------------*/
+
+class GEM_EXPORT pix_opencv_pgh_compare : public GemPixDualObj
+{
+ CPPEXTERN_HEADER(pix_opencv_pgh_compare, GemPixDualObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_pgh_compare(int,t_atom*);
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_pgh_compare();
+
+ //////////
+ // Do the processing
+ virtual void processRGBA_RGBA(imageStruct &left, imageStruct &right);
+ virtual void processRGB_RGB(imageStruct &left, imageStruct &right);
+ virtual void processYUV_YUV(imageStruct &left, imageStruct &right);
+ virtual void processGray_Gray(imageStruct &left, imageStruct &right);
+
+ //////////
+ // change method used
+ void floatMinSizeMess(float minsize);
+ void clearMess(void);
+ void floatCriteriaMess(float criteria);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ t_outlet *m_dataout;
+ t_outlet *m_posout;
+
+ int x_minsize;
+ float x_cdistance;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatMinSizeMessCallback(void *data, float minsize);
+ static void clearMessCallback(void *data);
+ static void floatCriteriaMessCallback(void *data, float criteria);
+
+ IplImage *rgba, *rgb, *gray;
+ IplImage *rgbar, *rgbr, *grayr;
+
+ CvMemStorage *x_storage;
+ CvSeq *x_bcontourr;
+
+ t_atom rlist[5];
+
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_surf.cc b/src/pix_opencv_surf.cc
new file mode 100644
index 0000000..5a5b46d
--- /dev/null
+++ b/src/pix_opencv_surf.cc
@@ -0,0 +1,1054 @@
+
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+
+#if HAVE_LIBOPENCV_NONFREE
+#include "pix_opencv_surf.h"
+
+#include <stdio.h>
+
+
+CPPEXTERN_NEW(pix_opencv_surf)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_surf
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_surf :: pix_opencv_surf()
+{
+ int i;
+
+ comp_xsize=320;
+ comp_ysize=240;
+
+ m_dataout = outlet_new(this->x_obj, &s_anything);
+
+ night_mode = 0;
+ x_maxmove = 20;
+ x_delaunay = -1;
+ x_threshold = -1;
+
+ objectKeypoints = NULL;
+ objectDescriptors = NULL;
+ x_hessian = 1000;
+ x_ftolerance = 5;
+
+ x_markall = 0;
+ for ( i=0; i<MAX_MARKERS; i++ )
+ {
+ x_xmark[i] = -1;
+ x_ymark[i] = -1;
+ }
+
+ // initialize font
+ cvInitFont( &font, CV_FONT_HERSHEY_PLAIN, 1.0, 1.0, 0, 1, 8 );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ orgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ ogray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ cv::initModule_nonfree();
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_surf :: ~pix_opencv_surf()
+{
+ // Destroy cv_images
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &orgb );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &ogray );
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_surf :: processRGBAImage(imageStruct &image)
+{
+ int i, k;
+ int im, oi;
+ int marked;
+ int descsize;
+ char tindex[4];
+ float dist, odist;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &orgb );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &ogray );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ orgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ ogray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ }
+
+ memcpy( rgba->imageData, image.data, image.xsize*image.ysize*4 );
+ cvCvtColor(rgba, orgb, CV_BGRA2BGR);
+ cvCvtColor(rgba, rgb, CV_BGRA2BGR);
+ cvCvtColor(rgba, gray, CV_BGRA2GRAY);
+
+ x_storage = cvCreateMemStorage(0);
+
+ if( night_mode )
+ cvZero( rgb );
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ x_found[im]--;
+ }
+
+ if ( x_delaunay >= 0 )
+ {
+ // init data structures for the delaunay
+ x_fullrect.x = -comp_xsize/2;
+ x_fullrect.y = -comp_ysize/2;
+ x_fullrect.width = 2*comp_xsize;
+ x_fullrect.height = 2*comp_ysize;
+
+ x_subdiv = cvCreateSubdiv2D( CV_SEQ_KIND_SUBDIV2D, sizeof(*x_subdiv),
+ sizeof(CvSubdiv2DPoint),
+ sizeof(CvQuadEdge2D),
+ x_storage );
+ cvInitSubdivDelaunay2D( x_subdiv, x_fullrect );
+ }
+
+ cvExtractSURF( gray, 0, &objectKeypoints, &objectDescriptors, x_storage, cvSURFParams(x_hessian, 1) );
+ descsize = (int)(objectDescriptors->elem_size/sizeof(float));
+
+ for( i = 0; i < objectKeypoints->total; i++ )
+ {
+ CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, i );
+ const float* rdesc = (const float*)cvGetSeqElem( objectDescriptors, i );
+
+ if ( x_delaunay == 0 ) // add all the points
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, r1->pt );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+
+ // only add points included in (color-threshold)<p<(color+treshold)
+ if ( ( x_delaunay > 0 ) && ( x_xmark[x_delaunay-1] != -1 ) )
+ {
+ int px = cvPointFrom32f(r1->pt).x;
+ int py = cvPointFrom32f(r1->pt).y;
+ int ppx, ppy;
+
+ // eight connected pixels
+ for ( ppx=px-1; ppx<=px+1; ppx++ )
+ {
+ for ( ppy=py-1; ppy<=py+1; ppy++ )
+ {
+ if ( ( ppx < 0 ) || ( ppx >= comp_xsize ) ) continue;
+ if ( ( ppy < 0 ) || ( ppy >= comp_ysize ) ) continue;
+
+ uchar red = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3];
+ uchar green = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3+1];
+ uchar blue = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3+2];
+
+ uchar pred = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[x_delaunay-1]))[x_ymark[x_delaunay-1]*3];
+ uchar pgreen = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[x_delaunay-1]))[x_ymark[x_delaunay-1]*3+1];
+ uchar pblue = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[x_delaunay-1]))[x_ymark[x_delaunay-1]*3+2];
+
+ int diff = abs(red-pred) + abs(green-pgreen) + abs(blue-pblue);
+
+ // post( "pdp_opencv_surf : point (%d,%d,%d) : diff : %d", blue, green, red, diff );
+
+ if ( diff < x_threshold )
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, r1->pt );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+ }
+ }
+ }
+
+ cvCircle( rgb, cvPointFrom32f(r1->pt), 3, CV_RGB(0,255,0), -1, 8,0);
+
+ // mark the point if it is not already
+ if ( x_markall )
+ {
+ int marked = 0;
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( x_xmark[im] == -1 ) continue; // no points
+
+ odist=sqrt( pow( r1->pt.x-x_xmark[im], 2 ) + pow( r1->pt.y-x_ymark[im], 2 ) );
+
+ if ( odist <= x_maxmove )
+ {
+ marked = 1;
+ // post( "pdp_opencv_surf : point already marked" );
+ break;
+ }
+ }
+ if ( !marked )
+ {
+ for ( i=0; i<MAX_MARKERS; i++)
+ {
+ if ( x_xmark[i] == -1 )
+ {
+ x_xmark[i] = r1->pt.x;
+ x_ymark[i] = r1->pt.y;
+ x_found[i] = x_ftolerance;
+ memset( (float * )x_rdesc[i], 0x0, DSCSIZE*sizeof(float));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ int neighbour = -1;
+ double d, dist1 = 1000000, dist2 = 1000000;
+
+ if ( x_xmark[im] == -1 ) continue; // no points
+
+ oi=-1;
+ dist=(comp_xsize>comp_ysize)?comp_xsize:comp_ysize;
+
+ for( i = 0; i < objectKeypoints->total; i++ )
+ {
+ CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, i );
+ const float* rdesc = (const float*)cvGetSeqElem( objectDescriptors, i );
+ int descsize = (int)(objectDescriptors->elem_size/sizeof(float));
+
+ // manually marked points
+ // recognized on position
+ odist=sqrt( pow( r1->pt.x-x_xmark[im], 2 ) + pow( r1->pt.y-x_ymark[im], 2 ) );
+
+ if ( odist <= x_maxmove )
+ {
+ if ( odist < dist )
+ {
+ oi=im;
+ x_xmark[oi]=r1->pt.x;
+ x_ymark[oi]=r1->pt.y;
+ memcpy( (float * )x_rdesc[oi], rdesc, descsize*sizeof(float));
+ dist = odist;
+ }
+ }
+ }
+
+ if ( oi !=-1 )
+ {
+ sprintf( tindex, "%d", oi );
+ cvPutText( rgb, tindex, cvPoint(x_xmark[oi],x_ymark[oi]), &font, CV_RGB(255,255,255));
+ x_found[oi] = x_ftolerance;
+ SETFLOAT(&x_list[0], oi);
+ SETFLOAT(&x_list[1], x_xmark[oi]);
+ SETFLOAT(&x_list[2], x_ymark[oi]);
+ outlet_list( m_dataout, 0, 3, x_list );
+ }
+ }
+
+ // draw the delaunay
+ if ( x_delaunay >= 0 )
+ {
+ CvSeqReader reader;
+ int i, total = x_subdiv->edges->total;
+ int elem_size = x_subdiv->edges->elem_size;
+
+ cvStartReadSeq( (CvSeq*)(x_subdiv->edges), &reader, 0 );
+
+ for( i = 0; i < total; i++ )
+ {
+ CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);
+ CvSubdiv2DPoint* org_pt;
+ CvSubdiv2DPoint* dst_pt;
+ CvPoint2D32f org;
+ CvPoint2D32f dst;
+ CvPoint iorg, idst;
+
+ if( CV_IS_SET_ELEM( edge ))
+ {
+ org_pt = cvSubdiv2DEdgeOrg((CvSubdiv2DEdge)edge);
+ dst_pt = cvSubdiv2DEdgeDst((CvSubdiv2DEdge)edge);
+
+ if( org_pt && dst_pt )
+ {
+ org = org_pt->pt;
+ dst = dst_pt->pt;
+
+ iorg = cvPoint( cvRound( org.x ), cvRound( org.y ));
+ idst = cvPoint( cvRound( dst.x ), cvRound( dst.y ));
+
+ if ( ( org.x > 0 ) && ( org.x < comp_xsize ) &&
+ ( dst.x > 0 ) && ( dst.x < comp_xsize ) &&
+ ( org.y > 0 ) && ( org.y < comp_ysize ) &&
+ ( dst.y > 0 ) && ( dst.y < comp_ysize ) )
+ cvLine( rgb, iorg, idst, CV_RGB(255,0,0), 1, CV_AA, 0 );
+ }
+ }
+
+ CV_NEXT_SEQ_ELEM( elem_size, reader );
+ }
+ }
+
+ // suppress lost points
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( (x_xmark[im] != -1.0 ) && !x_found[im] )
+ {
+ x_xmark[im]=-1.0;
+ x_ymark[im]=-1.0;
+ SETFLOAT(&x_list[0], im+1);
+ SETFLOAT(&x_list[1], x_xmark[im]);
+ SETFLOAT(&x_list[2], x_ymark[im]);
+ // send a lost point message to the patch
+ outlet_list( m_dataout, 0, 3, x_list );
+ // post( "pdp_opencv_surf : lost point %d", im+1 );
+ }
+ }
+
+ cvReleaseMemStorage( &x_storage );
+
+ cvCvtColor(rgb, rgba, CV_BGR2BGRA);
+ memcpy( image.data, rgba->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_surf :: processRGBImage(imageStruct &image)
+{
+ int i, k;
+ int im, oi;
+ int marked;
+ int descsize;
+ char tindex[4];
+ float dist, odist;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &orgb );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &ogray );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ orgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ ogray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ }
+
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+ memcpy( orgb->imageData, image.data, image.xsize*image.ysize*3 );
+ cvCvtColor(rgb, gray, CV_BGRA2GRAY);
+
+ x_storage = cvCreateMemStorage(0);
+
+ if( night_mode )
+ cvZero( rgb );
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ x_found[im]--;
+ }
+
+ if ( x_delaunay >= 0 )
+ {
+ // init data structures for the delaunay
+ x_fullrect.x = -comp_xsize/2;
+ x_fullrect.y = -comp_ysize/2;
+ x_fullrect.width = 2*comp_xsize;
+ x_fullrect.height = 2*comp_ysize;
+
+ x_subdiv = cvCreateSubdiv2D( CV_SEQ_KIND_SUBDIV2D, sizeof(*x_subdiv),
+ sizeof(CvSubdiv2DPoint),
+ sizeof(CvQuadEdge2D),
+ x_storage );
+ cvInitSubdivDelaunay2D( x_subdiv, x_fullrect );
+ }
+
+ cvExtractSURF( gray, 0, &objectKeypoints, &objectDescriptors, x_storage, cvSURFParams(x_hessian, 1) );
+ descsize = (int)(objectDescriptors->elem_size/sizeof(float));
+
+ for( i = 0; i < objectKeypoints->total; i++ )
+ {
+ CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, i );
+ const float* rdesc = (const float*)cvGetSeqElem( objectDescriptors, i );
+
+ if ( x_delaunay == 0 ) // add all the points
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, r1->pt );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+
+ // only add points included in (color-threshold)<p<(color+treshold)
+ if ( ( x_delaunay > 0 ) && ( x_xmark[x_delaunay-1] != -1 ) )
+ {
+ int px = cvPointFrom32f(r1->pt).x;
+ int py = cvPointFrom32f(r1->pt).y;
+ int ppx, ppy;
+
+ // eight connected pixels
+ for ( ppx=px-1; ppx<=px+1; ppx++ )
+ {
+ for ( ppy=py-1; ppy<=py+1; ppy++ )
+ {
+ if ( ( ppx < 0 ) || ( ppx >= comp_xsize ) ) continue;
+ if ( ( ppy < 0 ) || ( ppy >= comp_ysize ) ) continue;
+
+ uchar red = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3];
+ uchar green = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3+1];
+ uchar blue = ((uchar*)(orgb->imageData + orgb->widthStep*ppx))[ppy*3+2];
+
+ uchar pred = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[x_delaunay-1]))[x_ymark[x_delaunay-1]*3];
+ uchar pgreen = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[x_delaunay-1]))[x_ymark[x_delaunay-1]*3+1];
+ uchar pblue = ((uchar*)(orgb->imageData + orgb->widthStep*x_xmark[x_delaunay-1]))[x_ymark[x_delaunay-1]*3+2];
+
+ int diff = abs(red-pred) + abs(green-pgreen) + abs(blue-pblue);
+
+ // post( "pdp_opencv_surf : point (%d,%d,%d) : diff : %d", blue, green, red, diff );
+
+ if ( diff < x_threshold )
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, r1->pt );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+ }
+ }
+ }
+
+ cvCircle( rgb, cvPointFrom32f(r1->pt), 3, CV_RGB(0,255,0), -1, 8,0);
+
+ // mark the point if it is not already
+ if ( x_markall )
+ {
+ int marked = 0;
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( x_xmark[im] == -1 ) continue; // no points
+
+ odist=sqrt( pow( r1->pt.x-x_xmark[im], 2 ) + pow( r1->pt.y-x_ymark[im], 2 ) );
+
+ if ( odist <= x_maxmove )
+ {
+ marked = 1;
+ // post( "pdp_opencv_surf : point already marked" );
+ break;
+ }
+ }
+ if ( !marked )
+ {
+ for ( i=0; i<MAX_MARKERS; i++)
+ {
+ if ( x_xmark[i] == -1 )
+ {
+ x_xmark[i] = r1->pt.x;
+ x_ymark[i] = r1->pt.y;
+ x_found[i] = x_ftolerance;
+ memset( (float * )x_rdesc[i], 0x0, DSCSIZE*sizeof(float));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ int neighbour = -1;
+ double d, dist1 = 1000000, dist2 = 1000000;
+
+ if ( x_xmark[im] == -1 ) continue; // no points
+
+ oi=-1;
+ dist=(comp_xsize>comp_ysize)?comp_xsize:comp_ysize;
+
+ for( i = 0; i < objectKeypoints->total; i++ )
+ {
+ CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, i );
+ const float* rdesc = (const float*)cvGetSeqElem( objectDescriptors, i );
+ int descsize = (int)(objectDescriptors->elem_size/sizeof(float));
+
+ // manually marked points
+ // recognized on position
+ odist=sqrt( pow( r1->pt.x-x_xmark[im], 2 ) + pow( r1->pt.y-x_ymark[im], 2 ) );
+
+ if ( odist <= x_maxmove )
+ {
+ if ( odist < dist )
+ {
+ oi=im;
+ x_xmark[oi]=r1->pt.x;
+ x_ymark[oi]=r1->pt.y;
+ memcpy( (float * )x_rdesc[oi], rdesc, descsize*sizeof(float));
+ dist = odist;
+ }
+ }
+ }
+
+ if ( oi !=-1 )
+ {
+ sprintf( tindex, "%d", oi );
+ cvPutText( rgb, tindex, cvPoint(x_xmark[oi],x_ymark[oi]), &font, CV_RGB(255,255,255));
+ x_found[oi] = x_ftolerance;
+ SETFLOAT(&x_list[0], oi);
+ SETFLOAT(&x_list[1], x_xmark[oi]);
+ SETFLOAT(&x_list[2], x_ymark[oi]);
+ outlet_list( m_dataout, 0, 3, x_list );
+ }
+ }
+
+ // draw the delaunay
+ if ( x_delaunay >= 0 )
+ {
+ CvSeqReader reader;
+ int i, total = x_subdiv->edges->total;
+ int elem_size = x_subdiv->edges->elem_size;
+
+ cvStartReadSeq( (CvSeq*)(x_subdiv->edges), &reader, 0 );
+
+ for( i = 0; i < total; i++ )
+ {
+ CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);
+ CvSubdiv2DPoint* org_pt;
+ CvSubdiv2DPoint* dst_pt;
+ CvPoint2D32f org;
+ CvPoint2D32f dst;
+ CvPoint iorg, idst;
+
+ if( CV_IS_SET_ELEM( edge ))
+ {
+ org_pt = cvSubdiv2DEdgeOrg((CvSubdiv2DEdge)edge);
+ dst_pt = cvSubdiv2DEdgeDst((CvSubdiv2DEdge)edge);
+
+ if( org_pt && dst_pt )
+ {
+ org = org_pt->pt;
+ dst = dst_pt->pt;
+
+ iorg = cvPoint( cvRound( org.x ), cvRound( org.y ));
+ idst = cvPoint( cvRound( dst.x ), cvRound( dst.y ));
+
+ if ( ( org.x > 0 ) && ( org.x < comp_xsize ) &&
+ ( dst.x > 0 ) && ( dst.x < comp_xsize ) &&
+ ( org.y > 0 ) && ( org.y < comp_ysize ) &&
+ ( dst.y > 0 ) && ( dst.y < comp_ysize ) )
+ cvLine( rgb, iorg, idst, CV_RGB(255,0,0), 1, CV_AA, 0 );
+ }
+ }
+
+ CV_NEXT_SEQ_ELEM( elem_size, reader );
+ }
+ }
+
+ // suppress lost points
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( (x_xmark[im] != -1.0 ) && !x_found[im] )
+ {
+ x_xmark[im]=-1.0;
+ x_ymark[im]=-1.0;
+ SETFLOAT(&x_list[0], im+1);
+ SETFLOAT(&x_list[1], x_xmark[im]);
+ SETFLOAT(&x_list[2], x_ymark[im]);
+ // send a lost point message to the patch
+ outlet_list( m_dataout, 0, 3, x_list );
+ // post( "pdp_opencv_surf : lost point %d", im+1 );
+ }
+ }
+
+ cvReleaseMemStorage( &x_storage );
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_surf :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_surf : yuv format not supported" );
+}
+
+void pix_opencv_surf :: processGrayImage(imageStruct &image)
+{
+ int i, k;
+ int im, oi;
+ int marked;
+ int descsize;
+ char tindex[4];
+ float dist, odist;
+
+ if ((this->comp_xsize!=image.xsize)&&(this->comp_ysize!=image.ysize))
+ {
+
+ this->comp_xsize=image.xsize;
+ this->comp_ysize=image.ysize;
+
+ cvReleaseImage( &rgba );
+ cvReleaseImage( &orgb );
+ cvReleaseImage( &rgb );
+ cvReleaseImage( &gray );
+ cvReleaseImage( &ogray );
+
+ rgba = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 4 );
+ orgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ rgb = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 3 );
+ gray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+ ogray = cvCreateImage( cvSize(comp_xsize, comp_ysize), 8, 1 );
+
+ }
+
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+ memcpy( ogray->imageData, image.data, image.xsize*image.ysize );
+ x_storage = cvCreateMemStorage(0);
+
+ if( night_mode )
+ cvZero( gray );
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ x_found[im]--;
+ }
+
+ if ( x_delaunay >= 0 )
+ {
+ // init data structures for the delaunay
+ x_fullrect.x = -comp_xsize/2;
+ x_fullrect.y = -comp_ysize/2;
+ x_fullrect.width = 2*comp_xsize;
+ x_fullrect.height = 2*comp_ysize;
+
+ x_subdiv = cvCreateSubdiv2D( CV_SEQ_KIND_SUBDIV2D, sizeof(*x_subdiv),
+ sizeof(CvSubdiv2DPoint),
+ sizeof(CvQuadEdge2D),
+ x_storage );
+ cvInitSubdivDelaunay2D( x_subdiv, x_fullrect );
+ }
+
+ cvExtractSURF( ogray, 0, &objectKeypoints, &objectDescriptors, x_storage, cvSURFParams(x_hessian, 1) );
+ descsize = (int)(objectDescriptors->elem_size/sizeof(float));
+
+ for( i = 0; i < objectKeypoints->total; i++ )
+ {
+ CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, i );
+ const float* rdesc = (const float*)cvGetSeqElem( objectDescriptors, i );
+
+ if ( x_delaunay == 0 ) // add all the points
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, r1->pt );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+
+ // only add points included in (color-threshold)<p<(color+treshold)
+ if ( ( x_delaunay > 0 ) && ( x_xmark[x_delaunay-1] != -1 ) )
+ {
+ int px = cvPointFrom32f(r1->pt).x;
+ int py = cvPointFrom32f(r1->pt).y;
+ int ppx, ppy;
+
+ // eight connected pixels
+ for ( ppx=px-1; ppx<=px+1; ppx++ )
+ {
+ for ( ppy=py-1; ppy<=py+1; ppy++ )
+ {
+ if ( ( ppx < 0 ) || ( ppx >= comp_xsize ) ) continue;
+ if ( ( ppy < 0 ) || ( ppy >= comp_ysize ) ) continue;
+
+ uchar lum = ((uchar*)(ogray->imageData + ogray->widthStep*ppx))[ppy];
+
+ uchar plum = ((uchar*)(ogray->imageData + ogray->widthStep*x_xmark[x_delaunay-1]))[x_ymark[x_delaunay-1]];
+
+ int diff = abs(lum-plum);
+
+ if ( diff < x_threshold )
+ {
+ cvSubdivDelaunay2DInsert( x_subdiv, r1->pt );
+ cvCalcSubdivVoronoi2D( x_subdiv );
+ }
+ }
+ }
+ }
+
+ cvCircle( gray, cvPointFrom32f(r1->pt), 3, CV_RGB(255,255,255), -1, 8,0);
+
+ // mark the point if it is not already
+ if ( x_markall )
+ {
+ int marked = 0;
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( x_xmark[im] == -1 ) continue; // no points
+
+ odist=sqrt( pow( r1->pt.x-x_xmark[im], 2 ) + pow( r1->pt.y-x_ymark[im], 2 ) );
+
+ if ( odist <= x_maxmove )
+ {
+ marked = 1;
+ // post( "pdp_opencv_surf : point already marked" );
+ break;
+ }
+ }
+
+ if ( !marked )
+ {
+ for ( i=0; i<MAX_MARKERS; i++)
+ {
+ if ( x_xmark[i] == -1 )
+ {
+ x_xmark[i] = r1->pt.x;
+ x_ymark[i] = r1->pt.y;
+ x_found[i] = x_ftolerance;
+ memset( (float * )x_rdesc[i], 0x0, DSCSIZE*sizeof(float));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ int neighbour = -1;
+ double d, dist1 = 1000000, dist2 = 1000000;
+
+ if ( x_xmark[im] == -1 ) continue; // no points
+
+ oi=-1;
+ dist=(comp_xsize>comp_ysize)?comp_xsize:comp_ysize;
+
+ for( i = 0; i < objectKeypoints->total; i++ )
+ {
+ CvSURFPoint* r1 = (CvSURFPoint*)cvGetSeqElem( objectKeypoints, i );
+ const float* rdesc = (const float*)cvGetSeqElem( objectDescriptors, i );
+ int descsize = (int)(objectDescriptors->elem_size/sizeof(float));
+
+ // manually marked points
+ // recognized on position
+ odist=sqrt( pow( r1->pt.x-x_xmark[im], 2 ) + pow( r1->pt.y-x_ymark[im], 2 ) );
+
+ if ( odist <= x_maxmove )
+ {
+ if ( odist < dist )
+ {
+ oi=im;
+ x_xmark[oi]=r1->pt.x;
+ x_ymark[oi]=r1->pt.y;
+ memcpy( (float * )x_rdesc[oi], rdesc, descsize*sizeof(float));
+ dist = odist;
+ }
+ }
+ }
+
+ if ( oi !=-1 )
+ {
+ sprintf( tindex, "%d", oi );
+ cvPutText( gray, tindex, cvPoint(x_xmark[oi],x_ymark[oi]), &font, CV_RGB(255,255,255));
+ x_found[oi] = x_ftolerance;
+ SETFLOAT(&x_list[0], oi);
+ SETFLOAT(&x_list[1], x_xmark[oi]);
+ SETFLOAT(&x_list[2], x_ymark[oi]);
+ outlet_list( m_dataout, 0, 3, x_list );
+ }
+ }
+
+ // draw the delaunay
+ if ( x_delaunay >= 0 )
+ {
+ CvSeqReader reader;
+ int i, total = x_subdiv->edges->total;
+ int elem_size = x_subdiv->edges->elem_size;
+
+ cvStartReadSeq( (CvSeq*)(x_subdiv->edges), &reader, 0 );
+
+ for( i = 0; i < total; i++ )
+ {
+ CvQuadEdge2D* edge = (CvQuadEdge2D*)(reader.ptr);
+ CvSubdiv2DPoint* org_pt;
+ CvSubdiv2DPoint* dst_pt;
+ CvPoint2D32f org;
+ CvPoint2D32f dst;
+ CvPoint iorg, idst;
+
+ if( CV_IS_SET_ELEM( edge ))
+ {
+ org_pt = cvSubdiv2DEdgeOrg((CvSubdiv2DEdge)edge);
+ dst_pt = cvSubdiv2DEdgeDst((CvSubdiv2DEdge)edge);
+
+ if( org_pt && dst_pt )
+ {
+ org = org_pt->pt;
+ dst = dst_pt->pt;
+
+ iorg = cvPoint( cvRound( org.x ), cvRound( org.y ));
+ idst = cvPoint( cvRound( dst.x ), cvRound( dst.y ));
+
+ if ( ( org.x > 0 ) && ( org.x < comp_xsize ) &&
+ ( dst.x > 0 ) && ( dst.x < comp_xsize ) &&
+ ( org.y > 0 ) && ( org.y < comp_ysize ) &&
+ ( dst.y > 0 ) && ( dst.y < comp_ysize ) )
+ cvLine( gray, iorg, idst, CV_RGB(255,255,255), 1, CV_AA, 0 );
+ }
+ }
+
+ CV_NEXT_SEQ_ELEM( elem_size, reader );
+ }
+ }
+
+ // suppress lost points
+ for ( im=0; im<MAX_MARKERS; im++ )
+ {
+ if ( (x_xmark[im] != -1.0 ) && !x_found[im] )
+ {
+ x_xmark[im]=-1.0;
+ x_ymark[im]=-1.0;
+ SETFLOAT(&x_list[0], im+1);
+ SETFLOAT(&x_list[1], x_xmark[im]);
+ SETFLOAT(&x_list[2], x_ymark[im]);
+ // send a lost point message to the patch
+ outlet_list( m_dataout, 0, 3, x_list );
+ // post( "pdp_opencv_surf : lost point %d", im+1 );
+ }
+ }
+
+ cvReleaseMemStorage( &x_storage );
+ memcpy( image.data, gray->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_surf :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_surf::nightModeMessCallback,
+ gensym("nightmode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_surf::hessianMessCallback,
+ gensym("hessian"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_surf::markMessCallback,
+ gensym("mark"), A_GIMME, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_surf::deleteMessCallback,
+ gensym("delete"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_surf::clearMessCallback,
+ gensym("clear"), A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_surf::maxMoveMessCallback,
+ gensym("maxmove"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_surf::ftoleranceMessCallback,
+ gensym("ftolerance"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_surf::delaunayMessCallback,
+ gensym("delaunay"), A_SYMBOL, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_surf::pdelaunayMessCallback,
+ gensym("pdelaunay"), A_FLOAT, A_FLOAT, A_NULL);
+}
+
+void pix_opencv_surf :: nightModeMessCallback(void *data, t_floatarg nightmode)
+{
+ GetMyClass(data)->nightModeMess((float)nightmode);
+}
+
+void pix_opencv_surf :: hessianMessCallback(void *data, t_floatarg hessian)
+{
+ GetMyClass(data)->hessianMess((float)hessian);
+}
+
+void pix_opencv_surf :: markMessCallback(void *data, t_symbol *s, int argc, t_atom *argv)
+{
+ GetMyClass(data)->markMess(argc, argv);
+}
+
+void pix_opencv_surf :: deleteMessCallback(void *data, t_floatarg index)
+{
+ GetMyClass(data)->deleteMess((float)index);
+}
+
+void pix_opencv_surf :: clearMessCallback(void *data)
+{
+ GetMyClass(data)->clearMess();
+}
+
+void pix_opencv_surf :: maxMoveMessCallback(void *data, t_floatarg maxmove)
+{
+ GetMyClass(data)->maxMoveMess((float)maxmove);
+}
+
+void pix_opencv_surf :: ftoleranceMessCallback(void *data, t_floatarg ftolerance)
+{
+ GetMyClass(data)->ftoleranceMess((float)ftolerance);
+}
+
+void pix_opencv_surf :: delaunayMessCallback(void *data, t_symbol *s)
+{
+ GetMyClass(data)->delaunayMess(s);
+}
+
+void pix_opencv_surf :: pdelaunayMessCallback(void *data, t_floatarg fpoint, t_floatarg fthreshold)
+{
+ GetMyClass(data)->pdelaunayMess((float)fpoint, (float)fthreshold);
+}
+
+void pix_opencv_surf :: nightModeMess(float nightmode)
+{
+ if ((nightmode==0.0)||(nightmode==1.0)) night_mode = (int)nightmode;
+}
+
+void pix_opencv_surf :: hessianMess(float hessian)
+{
+ if (hessian>0.0) x_hessian = (int)hessian;
+}
+
+void pix_opencv_surf :: markMess(int argc, t_atom *argv)
+{
+ int i;
+ int inserted;
+
+ if ( argc == 1 ) // mark all or none
+ {
+ if ( argv[0].a_type != A_SYMBOL )
+ {
+ error( "pix_opencv_surf : wrong argument (should be 'all')" );
+ return;
+ }
+ if ( !strcmp( argv[0].a_w.w_symbol->s_name, "all" ) )
+ {
+ x_markall = 1;
+ return;
+ }
+ if ( !strcmp( argv[0].a_w.w_symbol->s_name, "none" ) )
+ {
+ x_markall = 0;
+ clearMess();
+ return;
+ }
+ }
+ else
+ {
+ if ( ( argv[0].a_type != A_FLOAT ) || ( argv[1].a_type != A_FLOAT ) )
+ {
+ error( "pix_opencv_surf : wrong argument (should be mark px py)" );
+ return;
+ }
+ else
+ {
+ float fpx = argv[0].a_w.w_float;
+ float fpy = argv[1].a_w.w_float;
+ int px, py;
+
+ if ( ( fpx < 0.0 ) || ( fpx > comp_xsize ) || ( fpy < 0.0 ) || ( fpy > comp_ysize ) )
+ {
+ return;
+ }
+
+ px = (int)fpx;
+ py = (int)fpy;
+ inserted = 0;
+ for ( i=0; i<MAX_MARKERS; i++)
+ {
+ if ( x_xmark[i] == -1 )
+ {
+ x_xmark[i] = px;
+ x_ymark[i] = py;
+ x_found[i] = x_ftolerance;
+ inserted = 1;
+ // post( "pix_opencv_surf : inserted point (%d,%d)", px, py );
+ break;
+ }
+ }
+ if ( !inserted )
+ {
+ post( "pix_opencv_surf : max markers reached" );
+ }
+ }
+ }
+}
+
+void pix_opencv_surf :: deleteMess(float index)
+{
+ int i;
+
+ if ( ( index < 1.0 ) || ( index > MAX_MARKERS ) )
+ {
+ return;
+ }
+
+ x_xmark[(int)index-1] = -1;
+ x_ymark[(int)index-1] = -1;
+
+}
+
+void pix_opencv_surf :: clearMess(void)
+{
+ int i;
+
+ for ( i=0; i<MAX_MARKERS; i++)
+ {
+ x_xmark[i] = -1;
+ x_ymark[i] = -1;
+ }
+
+}
+
+void pix_opencv_surf :: maxMoveMess(float maxmove)
+{
+ // has to be more than the size of a point
+ if (maxmove>=3.0) maxmove = (int)maxmove;
+}
+
+void pix_opencv_surf :: ftoleranceMess(float ftolerance)
+{
+ if (ftolerance>=0.0) ftolerance = (int)ftolerance;
+}
+
+void pix_opencv_surf :: delaunayMess(t_symbol *s)
+{
+ if (s == gensym("on"))
+ x_delaunay = 0;
+ if (s == gensym("off"))
+ x_delaunay = -1;
+}
+
+void pix_opencv_surf :: pdelaunayMess(float point, float threshold)
+{
+ if (((int)point>0) && ((int)point<MAX_MARKERS))
+ {
+ x_delaunay = (int)point;
+ x_threshold = (int)threshold;
+ // post( "pix_opencv_surf : setting threshold to : %d", x_threshold );
+ }
+}
+#endif /* HAVE_LIBOPENCV_NONFREE */
+
+
diff --git a/src/pix_opencv_surf.h b/src/pix_opencv_surf.h
new file mode 100644
index 0000000..6d4b44a
--- /dev/null
+++ b/src/pix_opencv_surf.h
@@ -0,0 +1,122 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ SURF point detection and tracking
+
+ 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_SURF_H_
+#define INCLUDE_PIX_OPENCV_SURF_H_
+
+#ifndef _EiC
+#include "opencv2/nonfree/nonfree.hpp"
+#include "opencv2/legacy/legacy.hpp"
+#include "opencv2/legacy/compat.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+
+#define MAX_MARKERS 500
+#define DSCSIZE 128
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_surf
+
+ SURF point detection and tracking
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_surf : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_surf, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_surf();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_surf();
+
+ //////////
+ // 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 hessianMess(float hessian);
+ void markMess(int, t_atom*);
+ void deleteMess(float index);
+ void clearMess(void);
+ void maxMoveMess(float maxmove);
+ void ftoleranceMess(float ftolerance);
+ void delaunayMess(t_symbol *s);
+ void pdelaunayMess(float fpoint, float fthreshold);
+
+ int comp_xsize;
+ int comp_ysize;
+
+ t_outlet *m_dataout;
+ int x_hessian;
+ int x_criteria;
+ int night_mode;
+ int x_maxmove;
+ int x_markall;
+ int x_ftolerance;
+ int x_delaunay;
+ int x_threshold;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void nightModeMessCallback(void *data, t_floatarg nightmode);
+ static void hessianMessCallback(void *data, t_floatarg hessian);
+ static void markMessCallback(void *data, t_symbol* name, int argc, t_atom* argv);
+ static void deleteMessCallback(void *data, t_floatarg index);
+ static void clearMessCallback(void *data);
+ static void maxMoveMessCallback(void *data, t_floatarg maxmove);
+ static void ftoleranceMessCallback(void *data, t_floatarg ftolerance);
+ static void delaunayMessCallback(void *data, t_symbol *s);
+ static void pdelaunayMessCallback(void *data, t_floatarg fpoint, t_floatarg fthreshold);
+
+ // Internal Open CV data
+ IplImage *orgb, *rgba, *rgb, *gray, *ogray;
+ t_atom x_list[3];
+
+ int x_xmark[MAX_MARKERS];
+ int x_ymark[MAX_MARKERS];
+ float x_rdesc[MAX_MARKERS][DSCSIZE];
+ int x_found[MAX_MARKERS];
+
+ // internal OpenCV structures
+ CvSeq *objectKeypoints, *objectDescriptors;
+ CvFont font;
+
+ // structures needed for the delaunay
+ CvRect x_fullrect;
+ CvMemStorage* x_storage;
+ CvSubdiv2D* x_subdiv;
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_template.cc b/src/pix_opencv_template.cc
new file mode 100644
index 0000000..a42d252
--- /dev/null
+++ b/src/pix_opencv_template.cc
@@ -0,0 +1,63 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org ) (pix_opencv)
+// Template for pix_opencv class
+
+#include "pix_opencv_template.h"
+#include <stdio.h>
+#include <opencv/highgui.h>
+#include <RTE/MessageCallbacks.h>
+
+
+CPPEXTERN_NEW(pix_opencv_template)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_template
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_template :: pix_opencv_template()
+{
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_template :: ~pix_opencv_template()
+{
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_template :: processImage(imageStruct &image)
+{
+ cv::Mat imgMat( image.ysize, image.xsize, CV_8UC1, image.data, image.csize*image.xsize); // just transform imageStruct to IplImage without copying data
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_template :: obj_setupCallback(t_class *classPtr)
+{
+}
diff --git a/src/pix_opencv_template.h b/src/pix_opencv_template.h
new file mode 100644
index 0000000..bf6c818
--- /dev/null
+++ b/src/pix_opencv_template.h
@@ -0,0 +1,57 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Threshold filter
+
+ 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_template_H_
+#define INCLUDE_pix_opencv_template_H_
+
+#include "opencv2/opencv.hpp"
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_template
+
+ square pattern detector
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_template : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_template, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_template();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_template();
+
+ //////////
+ // Do the processing
+ virtual void processImage(imageStruct &image);
+
+};
+#endif // for header file
diff --git a/src/pix_opencv_threshold.cc b/src/pix_opencv_threshold.cc
new file mode 100644
index 0000000..28f1477
--- /dev/null
+++ b/src/pix_opencv_threshold.cc
@@ -0,0 +1,262 @@
+////////////////////////////////////////////////////////
+//
+// 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_threshold.h"
+
+CPPEXTERN_NEW(pix_opencv_threshold)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_threshold
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_threshold :: pix_opencv_threshold()
+{
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("max"));
+ inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("threshold"));
+ threshold_value = 50;
+ max_value = 255;
+ threshold_mode = 0;
+ comp_xsize = 0;
+ comp_ysize = 0;
+ orig = NULL;
+ rgb = NULL;
+ gray = NULL;
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_threshold :: ~pix_opencv_threshold()
+{
+ //Destroy cv_images to clean memory
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_threshold :: processRGBAImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!orig)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if ( orig )
+ {
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+ }
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(orig->width,orig->height), IPL_DEPTH_8U, 1);
+ }
+ // Here we make a copy of the pixel data from image to orig->imageData
+ // orig is a IplImage struct, the default image type in openCV, take a look on the IplImage data structure here
+ // http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html
+ memcpy( orig->imageData, image.data, image.xsize*image.ysize*4 );
+
+ // Convert to grayscale
+ cvCvtColor(orig, gray, CV_BGRA2GRAY);
+
+ // Applies fixed-level thresholding to single-channel array.
+ switch(this->threshold_mode) {
+ case 0:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_BINARY);
+ break;
+ case 1:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_BINARY_INV);
+ break;
+ case 2:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_TRUNC);
+ break;
+ case 3:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_TOZERO);
+ break;
+ case 4:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_TOZERO_INV);
+ break;
+ }
+
+ cvCvtColor(gray, orig, CV_GRAY2BGRA);
+
+ //copy back the processed frame to image
+ memcpy( image.data, orig->imageData, image.xsize*image.ysize*4 );
+}
+
+void pix_opencv_threshold :: processRGBImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if ( rgb )
+ {
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+ }
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( rgb->imageData, image.data, image.xsize*image.ysize*3 );
+
+ // Convert to grayscale
+ cvCvtColor(rgb, gray, CV_RGB2GRAY);
+
+ // Applies fixed-level thresholding to single-channel array.
+ switch(this->threshold_mode) {
+ case 0:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_BINARY);
+ break;
+ case 1:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_BINARY_INV);
+ break;
+ case 2:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_TRUNC);
+ break;
+ case 3:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_TOZERO);
+ break;
+ case 4:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_TOZERO_INV);
+ break;
+ }
+
+ cvCvtColor(gray, rgb, CV_GRAY2BGR);
+
+ memcpy( image.data, rgb->imageData, image.xsize*image.ysize*3 );
+}
+
+void pix_opencv_threshold :: processYUVImage(imageStruct &image)
+{
+ post( "pix_opencv_threshold : yuv format not supported" );
+}
+
+void pix_opencv_threshold :: processGrayImage(imageStruct &image)
+{
+ unsigned char *pixels = image.data;
+
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb)) {
+
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if ( rgb )
+ {
+ cvReleaseImage(&orig);
+ cvReleaseImage(&gray);
+ cvReleaseImage(&rgb);
+ }
+
+ //create the orig image with new size
+ orig = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 3);
+ gray = cvCreateImage(cvSize(rgb->width,rgb->height), IPL_DEPTH_8U, 1);
+
+ }
+ // FEM UNA COPIA DEL PACKET A image->imageData ... http://www.cs.iit.edu/~agam/cs512/lect-notes/opencv-intro/opencv-intro.html aqui veiem la estructura de IplImage
+ memcpy( gray->imageData, image.data, image.xsize*image.ysize );
+
+ // Applies fixed-level thresholding to single-channel array.
+ switch(this->threshold_mode) {
+ case 0:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_BINARY);
+ break;
+ case 1:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_BINARY_INV);
+ break;
+ case 2:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_TRUNC);
+ break;
+ case 3:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_TOZERO);
+ break;
+ case 4:
+ cvThreshold(gray, gray, (float)this->threshold_value, (float)this->max_value, CV_THRESH_TOZERO_INV);
+ break;
+ }
+
+ memcpy( image.data, gray->imageData, image.xsize*image.ysize );
+}
+
+/////////////////////////////////////////////////////////
+// floatThreshMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_threshold :: floatMaxMess (float maxvalue)
+{
+ if ( (int)maxvalue>0 ) this->max_value = maxvalue;
+}
+void pix_opencv_threshold :: floatThreshMess (float edge_thresh)
+{
+ this->threshold_value = edge_thresh;
+}
+void pix_opencv_threshold :: floatModeMess (float mode)
+{
+ if ((mode>=0)&&(mode<5)) this->threshold_mode = (int)mode;
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_threshold :: obj_setupCallback(t_class *classPtr)
+{
+ class_addmethod(classPtr, (t_method)&pix_opencv_threshold::floatModeMessCallback,
+ gensym("mode"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_threshold::floatMaxMessCallback,
+ gensym("max"), A_FLOAT, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_threshold::floatThreshMessCallback,
+ gensym("threshold"), A_FLOAT, A_NULL);
+}
+void pix_opencv_threshold :: floatMaxMessCallback(void *data, t_floatarg maxvalue)
+{
+ GetMyClass(data)->floatMaxMess((float)maxvalue);
+}
+void pix_opencv_threshold :: floatThreshMessCallback(void *data, t_floatarg edge_thresh)
+{
+ GetMyClass(data)->floatThreshMess((float)edge_thresh);
+}
+void pix_opencv_threshold :: floatModeMessCallback(void *data, t_floatarg mode)
+{
+ GetMyClass(data)->floatModeMess((float)mode);
+}
diff --git a/src/pix_opencv_threshold.h b/src/pix_opencv_threshold.h
new file mode 100644
index 0000000..cd82f90
--- /dev/null
+++ b/src/pix_opencv_threshold.h
@@ -0,0 +1,88 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Threshold filter
+
+ 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_THRESHOLD_H_
+#define INCLUDE_PIX_OPENCV_THRESHOLD_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_threshold
+
+ Threshold filter
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_threshold : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_threshold, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_threshold();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_threshold();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ //////////
+ // Set the new edge threshold
+ void floatMaxMess(float maxvalue);
+ void floatThreshMess(float edge_thresh);
+ void floatModeMess(float mode);
+
+ // The new edge threshold
+ float threshold_value;
+ float max_value;
+ int threshold_mode;
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ private:
+
+ //////////
+ // Static member functions
+ static void floatMaxMessCallback(void *data, t_floatarg maxvalue);
+ static void floatThreshMessCallback(void *data, t_floatarg thresh_value);
+ static void floatModeMessCallback(void *data, t_floatarg thresh_mode_value);
+
+ /////////
+ // IplImage needed
+ IplImage *orig, *rgb, *gray;
+};
+
+#endif // for header file
diff --git a/src/pix_opencv_trackKnn.cc b/src/pix_opencv_trackKnn.cc
new file mode 100644
index 0000000..6113b0c
--- /dev/null
+++ b/src/pix_opencv_trackKnn.cc
@@ -0,0 +1,602 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+// based on code written by Lluis Gomez i Bigorda ( lluisgomez _at_ hangar _dot_ org ) (pix_opencv)
+// and on some code from CCV : http://ccv.nuigroup.com/ (the tracker it self)
+// pix_opencv_trackKnn extract and simplify contours of incomming image
+// by Antoine Villeret - 2012
+
+#include "pix_opencv_trackKnn.h"
+
+using namespace cv;
+using namespace std;
+
+CPPEXTERN_NEW(pix_opencv_trackKnn)
+
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_trackKnn
+//
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_trackKnn :: pix_opencv_trackKnn() : \
+ m_taboutput(0), \
+ IdCounter(0), \
+ m_x_arrayname(NULL), \
+ m_y_arrayname(NULL), \
+ m_z_arrayname(NULL)
+{
+ m_dataout_right = outlet_new(this->x_obj, 0);
+
+ //~ post("build on %s at %s", __DATE__, __TIME__);
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_trackKnn :: ~pix_opencv_trackKnn()
+{
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+
+void pix_opencv_trackKnn :: processImage(imageStruct &image)
+{
+ // nothing to do for now...
+}
+
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_trackKnn :: obj_setupCallback(t_class *classPtr)
+{
+ CPPEXTERN_MSG(classPtr, "cvblob", cvblobMess);
+ CPPEXTERN_MSG1(classPtr, "drawBlob", drawBlobMess, int);
+ CPPEXTERN_MSG1(classPtr, "taboutput", taboutputMess, float);
+ CPPEXTERN_MSG1(classPtr, "tabinput", tabinputMess, float);
+ CPPEXTERN_MSG(classPtr, "settab", tableMess);
+ CPPEXTERN_MSG0(classPtr, "reset", resetMess);
+}
+
+/////////////////////////////////////////////////////////
+// messages handling
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_trackKnn :: resetMess(){
+ m_trackedBlobs.clear();
+ IdCounter=0;
+
+}
+
+void pix_opencv_trackKnn :: drawBlobMess(double arg)
+{
+ m_drawBlob = arg > 0 ? arg : 3.;
+}
+
+void pix_opencv_trackKnn :: cvblobMess(t_symbol *s, int argc, t_atom* argv){
+ // supposed to receive a cvblob message formatted like an iem matrix : the 2 first elements are the matrix cols and rows
+
+ // first check is matrix is valid...
+ if ( argc < 2 ){
+ error("this doesn't seems to be a valid cvblob matrix (too few elements !)");
+ return;
+ }
+
+ for ( int i = 0 ; i < argc ; i++){
+ if ( argv[i].a_type != A_FLOAT ){
+ error("cvblob takes only float arg");
+ return;
+ }
+ }
+
+ int blob_number = (int) atom_getfloatarg(0,argc,argv);
+ int blob_atom_size = (int) atom_getfloatarg(1, argc, argv);
+
+ if ( blob_number * blob_atom_size != argc - 2 ){
+ error("cvblob matrix have a wrong size %d != %d x %d", argc -2, blob_number, blob_atom_size);
+ return;
+ }
+
+ m_inputBlobs.clear();
+ for ( int i = 0; i < blob_number ; i++){
+ Blob blob;
+ blob.id=-1;
+ int j = i*blob_atom_size+2;
+ if ( blob_atom_size < 3 ) j--; // if there is more than 2 element per blob, assume, the first is Id
+ blob.centroid.x=atom_getfloatarg(j+1,argc,argv);
+ blob.centroid.y=atom_getfloatarg(j+2,argc,argv);
+
+ if ( blob_atom_size > 2 ){ // if we pass more than just x and y coordinates, assume the same data order than pix_opencv_contours outputs
+ blob.angleBoundingRect.size.width=atom_getfloatarg(j+3,argc,argv);
+ blob.angleBoundingRect.size.height=atom_getfloatarg(j+4,argc,argv);
+ blob.angle=atom_getfloatarg(j+5,argc,argv);
+ blob.area=atom_getfloatarg(j+6,argc,argv);
+
+ blob.nPts=atom_getfloatarg(j+15,argc,argv);
+ blob.length=atom_getfloatarg(j+16,argc,argv);
+ }
+
+ m_inputBlobs.push_back(blob);
+ }
+
+ doTracking();
+}
+
+void pix_opencv_trackKnn :: doTracking()
+{
+ // find an Id
+ for(unsigned int i=0; i<m_trackedBlobs.size(); i++)
+ {
+
+ /******************************************************************
+ * *****************TRACKING FUNCTION TO BE USED*******************
+ * Replace 'trackKnn(...)' with any function that will take the
+ * current track and find the corresponding track in the newBlobs
+ * 'winner' should contain the index of the found blob or '-1' if
+ * there was no corresponding blob
+ *****************************************************************/
+ int winner = trackKnn(i, 3, 0.);
+
+ if(winner==-1) //track has died, mark it for deletion
+ {
+ //erase calibrated blob from map
+ //calibratedBlobs.erase(m_trackedBlobs[i].id);
+
+ //mark the blob for deletion
+ m_trackedBlobs[i].id = -1;
+ }
+ else //still alive, have to update
+ {
+ //if winning new blob was labeled winner by another track
+ //then compare with this track to see which is closer
+ if(m_inputBlobs[winner].id!=-1)
+ {
+ //find the currently assigned blob
+ unsigned int j; //j will be the index of it
+ for(j=0; j<m_trackedBlobs.size(); j++)
+ {
+ if(m_trackedBlobs[j].id==m_inputBlobs[winner].id)
+ break;
+ }
+
+ if(j==m_trackedBlobs.size())//got to end without finding it
+ {
+ m_inputBlobs[winner].id = m_trackedBlobs[i].id;
+ m_inputBlobs[winner].age = m_trackedBlobs[i].age;
+ m_inputBlobs[winner].sitting = m_trackedBlobs[i].sitting;
+ m_inputBlobs[winner].downTime = m_trackedBlobs[i].downTime;
+ m_inputBlobs[winner].color = m_trackedBlobs[i].color;
+ m_inputBlobs[winner].lastTimeTimeWasChecked = m_trackedBlobs[i].lastTimeTimeWasChecked;
+
+ m_trackedBlobs[i] = m_inputBlobs[winner];
+ }
+ else //found it, compare with current blob
+ {
+ double _x = m_inputBlobs[winner].centroid.x;
+ double y = m_inputBlobs[winner].centroid.y;
+ double xOld = m_trackedBlobs[j].centroid.x;
+ double yOld = m_trackedBlobs[j].centroid.y;
+ double xNew = m_trackedBlobs[i].centroid.x;
+ double yNew = m_trackedBlobs[i].centroid.y;
+ double distOld = (_x-xOld)*(_x-xOld)+(y-yOld)*(y-yOld);
+ double distNew = (_x-xNew)*(_x-xNew)+(y-yNew)*(y-yNew);
+
+ //if this track is closer, update the ID of the blob
+ //otherwise delete this track.. it's dead
+ if(distNew<distOld) //update
+ {
+ m_inputBlobs[winner].id = m_trackedBlobs[i].id;
+ m_inputBlobs[winner].age = m_trackedBlobs[i].age;
+ m_inputBlobs[winner].sitting = m_trackedBlobs[i].sitting;
+ m_inputBlobs[winner].downTime = m_trackedBlobs[i].downTime;
+ m_inputBlobs[winner].color = m_trackedBlobs[i].color;
+ m_inputBlobs[winner].lastTimeTimeWasChecked = m_trackedBlobs[i].lastTimeTimeWasChecked;
+
+ //TODO--------------------------------------------------------------------------
+ //now the old winning blob has lost the win.
+ //I should also probably go through all the newBlobs
+ //at the end of this loop and if there are ones without
+ //any winning matches, check if they are close to this
+ //one. Right now I'm not doing that to prevent a
+ //recursive mess. It'll just be a new track.
+
+ //erase calibrated blob from map
+ //calibratedBlobs.erase(m_trackedBlobs[j].id);
+ //mark the blob for deletion
+ m_trackedBlobs[j].id = -1;
+ //------------------------------------------------------------------------------
+ }
+ else //delete
+ {
+ //erase calibrated blob from map
+ //calibratedBlobs.erase(m_trackedBlobs[i].id);
+ //mark the blob for deletion
+ m_trackedBlobs[i].id = -1;
+ }
+ }
+ }
+ else //no conflicts, so simply update
+ {
+ m_inputBlobs[winner].id = m_trackedBlobs[i].id;
+ m_inputBlobs[winner].age = m_trackedBlobs[i].age;
+ m_inputBlobs[winner].sitting = m_trackedBlobs[i].sitting;
+ m_inputBlobs[winner].downTime = m_trackedBlobs[i].downTime;
+ m_inputBlobs[winner].color = m_trackedBlobs[i].color;
+ m_inputBlobs[winner].lastTimeTimeWasChecked = m_trackedBlobs[i].lastTimeTimeWasChecked;
+ }
+ }
+ }
+
+ for(unsigned int i=0; i<m_trackedBlobs.size(); i++)
+ {
+ if(m_trackedBlobs[i].id==-1) //dead
+ {
+ //erase track
+ m_trackedBlobs.erase(m_trackedBlobs.begin()+i,
+ m_trackedBlobs.begin()+i+1);
+ //m_trackedBlobs.erase(m_trackedBlobs.begin()+i);
+
+ i--; //decrement one since we removed an element
+ }
+ else //living, so update it's data
+ {
+ for(unsigned int j=0; j<m_inputBlobs.size(); j++)
+ {
+ if(m_trackedBlobs[i].id==m_inputBlobs[j].id)
+ {
+ //update track
+ cv::Point tempLastCentroid = m_trackedBlobs[i].centroid; // assign the new centroid to the old
+ m_trackedBlobs[i]=m_inputBlobs[j];
+ m_trackedBlobs[i].lastCentroid = tempLastCentroid;
+
+ //get the Differences in position
+ //~m_trackedBlobs[i].D.set((m_trackedBlobs[i].centroid.x - m_trackedBlobs[i].lastCentroid.x) / (ofGetElapsedTimeMillis() - m_trackedBlobs[i].lastTimeTimeWasChecked),
+ //~(m_trackedBlobs[i].centroid.y - m_trackedBlobs[i].lastCentroid.y) / (ofGetElapsedTimeMillis() - m_trackedBlobs[i].lastTimeTimeWasChecked));
+
+ //printf("D(%f, %f)\n", m_trackedBlobs[i].D.x, m_trackedBlobs[i].D.y);
+
+ //if( abs((int)m_trackedBlobs[i].D.x) > 1 || abs((int)m_trackedBlobs[i].D.y) > 1) {
+ // printf("\nUNUSUAL BLOB @ %f\n-----------------------\nm_trackedBlobs[%i]\nD = (%f, %f)\nXY= (%f, %f)\nlastTimeTimeWasChecked = %f\nsitting = %f\n",
+ // ofGetElapsedTimeMillis(),
+ // i,
+ // m_trackedBlobs[i].D.x, m_trackedBlobs[i].D.y,
+ // m_trackedBlobs[i].centroid.x, m_trackedBlobs[i].centroid.y,
+ // m_trackedBlobs[i].lastTimeTimeWasChecked,
+ // m_trackedBlobs[i].downTime,
+ // m_trackedBlobs[i].sitting
+ // );
+ // }
+
+
+ //calculate the accelleration
+ //~ofPoint tD = m_trackedBlobs[i].D;
+ //~m_trackedBlobs[i].maccel = sqrtf((tD.x* tD.x)+(tD.y*tD.y)/(ofGetElapsedTimeMillis() - m_trackedBlobs[i].lastTimeTimeWasChecked));
+
+ //~m_trackedBlobs[i].lastTimeTimeWasChecked = ofGetElapsedTimeMillis();
+
+ //calculate the age
+ //~m_trackedBlobs[i].age = ofGetElapsedTimef() - m_trackedBlobs[i].downTime;
+
+ //if not moving more than min_movement_threshold then set to same position as last frame
+ // if(m_trackedBlobs[i].maccel < MIN_MOVEMENT_THRESHOLD)
+ // { //this helps avoid jittery blobs
+ // m_trackedBlobs[i].centroid.x = m_trackedBlobs[i].lastCentroid.x;
+ // m_trackedBlobs[i].centroid.y = m_trackedBlobs[i].lastCentroid.y;
+ // }
+
+ //set sitting (held length)
+ //~if(m_trackedBlobs[i].maccel < 7)
+ { //1 more frame of sitting
+ //~if(m_trackedBlobs[i].sitting != -1)
+ //~m_trackedBlobs[i].sitting = ofGetElapsedTimef() - m_trackedBlobs[i].downTime;
+ //~}
+ //~else {
+ //~m_trackedBlobs[i].sitting = -1;
+ //~}
+
+ //printf("time: %f\n", ofGetElapsedTimeMillis());
+ //printf("%i age: %f, downTimed at: %f\n", i, m_trackedBlobs[i].age, m_trackedBlobs[i].downTime);
+
+ //if blob has been 'holding/sitting' for 1 second send a held event
+ //~if(m_trackedBlobs[i].sitting > 1.0f){
+ //held event only happens once so set to -1
+ //~m_trackedBlobs[i].sitting = -1;
+ }
+ }
+ }
+ }
+ }
+ /*==================*/
+ //--Add New Living Tracks
+ //now every new blob should be either labeled with a tracked ID or
+ //have ID of -1... if the ID is -1... we need to make a new track
+ for(unsigned int i=0; i<m_inputBlobs.size(); i++)
+ {
+ if(m_inputBlobs[i].id==-1)
+ {
+
+ //add new track
+ m_inputBlobs[i].id=IdCounter;
+ IdCounter++;
+ //~IdCounter=IDcounter%6; // limit the id between 0 and 6...
+
+ //~m_inputBlobs[i].downTime = ofGetElapsedTimef();
+ //m_inputBlobs[i].lastTimeTimeWasChecked = ofGetElapsedTimeMillis();
+
+ //random color for blob. Could be useful?
+ Mat color(1,1,CV_8UC3);
+ color=Scalar(rand()%180,255,255);
+ cvtColor(color,color,CV_HSV2RGB);
+
+ const unsigned char* ptr=color.ptr<unsigned char>(0);
+ Scalar scolor(ptr[0],ptr[1],ptr[2]);
+
+ m_inputBlobs[i].color = scolor;
+ m_trackedBlobs.push_back(m_inputBlobs[i]);
+
+ //~numEnter++;
+ //~if (numEnter > 20)
+ //~{
+ //~printf("something wrong!\n");
+ //~}
+
+ }
+ }
+
+ outputBlob();
+}
+
+void pix_opencv_trackKnn :: outputBlob(){
+ if ( m_taboutput )
+ {
+ //~ put contours in 3 tables.
+ //~ contours are separated by 0 values
+
+ if ( m_x_arrayname == NULL || m_y_arrayname == NULL || m_id_arrayname == NULL){
+ error("please settab before trying to write into...");
+ return;
+ }
+
+ int size(m_trackedBlobs.size()), vecxsize(0), vecysize(0), vecidsize(0);
+ t_garray *ax, *ay, *aid;
+ t_word *vecx, *vecy, *vecid;
+
+ //~ check if array exist
+ if (!(ax = (t_garray *)pd_findbyclass(m_x_arrayname, garray_class))){
+ error("%s: no such array", m_x_arrayname->s_name);
+ return;
+ }
+ if (!(ay = (t_garray *)pd_findbyclass(m_y_arrayname, garray_class))){
+ error("%s: no such array", m_y_arrayname->s_name);
+ return;
+ }
+ if (!(aid = (t_garray *)pd_findbyclass(m_id_arrayname, garray_class))){
+ error("%s: no such array", m_id_arrayname->s_name);
+ return;
+ }
+
+ if (!garray_getfloatwords(ax, &vecxsize, &vecx)){
+ error("%s: bad template for tabwrite", m_x_arrayname->s_name);
+ return;
+ } else if ( vecxsize != size ){
+ garray_resize_long(ax,size);
+ if (!garray_getfloatwords(ax, &vecxsize, &vecx)){
+ error("%s: can't resize correctly", m_x_arrayname->s_name);
+ return;
+ }
+ }
+
+ if (!garray_getfloatwords(ay, &vecysize, &vecy)){
+ error("%s: bad template for tabwrite", m_y_arrayname->s_name);
+ return;
+ } else if ( vecysize != size ){
+ garray_resize_long(ay,size);
+ if (!garray_getfloatwords(ay, &vecysize, &vecy)){
+ error("%s: can't resize correctly", m_y_arrayname->s_name);
+ return;
+ }
+ }
+
+ if (!garray_getfloatwords(aid, &vecidsize, &vecid)){
+ error("%s: bad template for tabwrite", m_id_arrayname->s_name);
+ return;
+ } else if ( vecidsize != size ){
+ garray_resize_long(aid,size);
+ if (!garray_getfloatwords(aid, &vecidsize, &vecid)){
+ error("%s: can't resize correctly", m_id_arrayname->s_name);
+ return;
+ }
+ }
+
+ for( size_t i = 0 ; i < m_trackedBlobs.size(); i++ )
+ {
+ vecx[i].w_float = m_trackedBlobs[i].centroid.x;
+ vecy[i].w_float = m_trackedBlobs[i].centroid.y;
+ vecid[i].w_float = m_trackedBlobs[i].id;
+ }
+ //~ comment the redraw fnt if not needed
+ garray_redraw(ax);
+ garray_redraw(ay);
+ garray_redraw(aid);
+
+ } else {
+ unsigned int blob_num=m_trackedBlobs.size();
+ unsigned int blobMatrixWidth=17;
+ unsigned int blob_atom_size = 2+blob_num*blobMatrixWidth;
+
+ t_atom* blob_atom = new t_atom[blob_atom_size];
+ SETFLOAT(&blob_atom[0], blob_num);
+ SETFLOAT(&blob_atom[1], blobMatrixWidth);
+
+ for(unsigned int i=0; i<blob_num; i++){
+ t_atom *apt=blob_atom+2+i*blobMatrixWidth;
+ SETFLOAT(&apt[0], m_trackedBlobs[i].id);
+ SETFLOAT(&apt[1], m_trackedBlobs[i].centroid.x);
+ SETFLOAT(&apt[2], m_trackedBlobs[i].centroid.y);
+ SETFLOAT(&apt[3], m_trackedBlobs[i].angleBoundingRect.size.width);
+ SETFLOAT(&apt[4], m_trackedBlobs[i].angleBoundingRect.size.height);
+ SETFLOAT(&apt[5], 1); // state
+ SETFLOAT(&apt[6], m_trackedBlobs[i].area); // area in pixels
+
+ t_atom* apt2 = apt+7;
+ cv::Point2f corners[4];
+ m_trackedBlobs[i].angleBoundingRect.points(corners);
+
+ // blob rot rect 4 corners
+ for (int j=0;j<4;j++) {
+ SETFLOAT(apt2, corners[j].x);
+ SETFLOAT(apt2+1, corners[j].y);
+ apt2+=2;
+ }
+
+ SETFLOAT(apt+15, m_trackedBlobs[i].nPts); // number of points in segment
+ SETFLOAT(apt+16, (float) m_trackedBlobs[i].length);
+ }
+
+ outlet_anything(m_dataout_right, gensym("cvblob"), blob_atom_size, blob_atom);
+ if (blob_atom){
+ delete blob_atom;
+ blob_atom=NULL;
+ }
+ }
+}
+
+int pix_opencv_trackKnn :: trackKnn(unsigned int blob_index, unsigned int k, double thresh = 0)
+{
+ vector<Blob> newBlobs=m_inputBlobs;
+ Blob *track = &m_trackedBlobs[blob_index];
+
+ int winner = -1; //initially label track as '-1'=dead
+ if((k%2)==0) k++; //if k is not an odd number, add 1 to it
+
+ //if it exists, square the threshold to use as square distance
+ if(thresh>0)
+ thresh *= thresh;
+
+ //list of neighbor point index and respective distances
+ std::list<std::pair<int,double> > nbors;
+ std::list<std::pair<int,double> >::iterator iter;
+
+ //find 'k' closest neighbors of testpoint
+ double x, y, xT, yT, dist;
+ for(unsigned int i=0; i<newBlobs.size(); i++)
+ {
+ x = newBlobs[i].centroid.x;
+ y = newBlobs[i].centroid.y;
+
+ xT = track->centroid.x;
+ yT = track->centroid.y;
+ dist = (x-xT)*(x-xT)+(y-yT)*(y-yT);
+
+ if(dist<=thresh)//it's good, apply label if no label yet and return
+ {
+ winner = i;
+ return winner;
+ }
+
+ /****************************************************************
+ * check if this blob is closer to the point than what we've seen
+ *so far and add it to the index/distance list if positive
+ ****************************************************************/
+
+ //search the list for the first point with a longer distance
+ for(iter=nbors.begin(); iter!=nbors.end()
+ && dist>=iter->second; iter++);
+
+ if((iter!=nbors.end())||(nbors.size()<k)) //it's valid, insert it
+ {
+ nbors.insert(iter, 1, std::pair<int, double>(i, dist));
+ //too many items in list, get rid of farthest neighbor
+ if(nbors.size()>k)
+ nbors.pop_back();
+ }
+ }
+
+ /********************************************************************
+ * we now have k nearest neighbors who cast a vote, and the majority
+ * wins. we use each class average distance to the target to break any
+ * possible ties.
+ *********************************************************************/
+
+ // a mapping from labels (IDs) to count/distance
+ std::map<int, std::pair<int, double> > votes;
+
+ //remember:
+ //iter->first = index of newBlob
+ //iter->second = distance of newBlob to current tracked blob
+ for(iter=nbors.begin(); iter!=nbors.end(); iter++)
+ {
+ //add up how many counts each neighbor got
+ int count = ++(votes[iter->first].first);
+ double dist = (votes[iter->first].second+=iter->second);
+
+ /* check for a possible tie and break with distance */
+ if(count>votes[winner].first || ( count==votes[winner].first && dist<votes[winner].second ))
+ {
+ winner = iter->first;
+ }
+ }
+
+ return winner;
+}
+
+void pix_opencv_trackKnn :: taboutputMess(float arg)
+{
+ m_taboutput = arg > 0;
+}
+
+void pix_opencv_trackKnn :: tabinputMess(float arg)
+{
+ m_tabinput = arg > 0;
+}
+
+void pix_opencv_trackKnn :: tableMess(t_symbol *s, int argc, t_atom* argv)
+{
+ if ( argc < 3 ){
+ error("table message need at least 3 argumments");
+ return;
+ }
+
+ for ( int i = 0 ; i < argc ; i++ ){
+ if ( argv[i].a_type != A_SYMBOL ){
+ error("table message takes only symbol arguments, at most 4 at least 3");
+ return;
+ }
+ }
+
+ if ( argc == 3 && argv[0].a_type == A_SYMBOL && argv[1].a_type == A_SYMBOL && argv[2].a_type == A_SYMBOL ){
+
+ // check if arrays exist
+ m_x_arrayname = argv[0].a_w.w_symbol;
+ m_y_arrayname = argv[1].a_w.w_symbol;
+ m_z_arrayname = argv[2].a_w.w_symbol;
+ }
+
+ if ( argc > 3 ){
+ m_id_arrayname= argv[0].a_w.w_symbol;
+ m_x_arrayname = argv[1].a_w.w_symbol;
+ m_y_arrayname = argv[2].a_w.w_symbol;
+ m_z_arrayname = argv[3].a_w.w_symbol;
+ }
+ m_tabinput = 1;
+}
diff --git a/src/pix_opencv_trackKnn.h b/src/pix_opencv_trackKnn.h
new file mode 100644
index 0000000..2b0a006
--- /dev/null
+++ b/src/pix_opencv_trackKnn.h
@@ -0,0 +1,93 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ 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_TRACKKNN_H_
+#define INCLUDE_PIX_OPENCV_TRACKKNN_H_
+
+#ifndef _EiC
+#include "opencv2/legacy/legacy.hpp"
+#endif
+
+#include "Base/GemPixObj.h"
+#include <RTE/MessageCallbacks.h>
+
+#include <stdio.h>
+#include <list>
+
+#include "Blob.hpp"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_trackKnn
+
+detects contours and send them out
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_trackKnn : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_trackKnn, GemPixObj)
+
+public:
+
+ //////////
+ // Constructor
+ pix_opencv_trackKnn();
+
+protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_trackKnn();
+
+ //////////
+ // Do the processing
+ virtual void processImage(imageStruct &image);
+
+ // Messages handling
+ void drawBlobMess(double arg);
+ void cvblobMess(t_symbol *s, int argc, t_atom* argv);
+ void tabinputMess(float arg);
+ void taboutputMess(float arg);
+ void tableMess(t_symbol *s, int argc, t_atom* argv);
+
+private:
+
+ void doTracking(void);
+ int trackKnn(unsigned int blob_index, unsigned int k, double thresh);
+ void outputBlob();
+ void resetMess();
+
+ t_outlet *m_dataout_right; // cvblob outlet
+
+ unsigned int m_repeat_point;
+ double m_area_threshold; // min area for contour
+ double m_epsilon;
+ int m_drawBlob;
+
+ std::vector<Blob> m_trackedBlobs;
+ std::vector<Blob> m_inputBlobs;
+
+ int m_mode, m_taboutput, m_tabinput;
+
+ int IdCounter;
+
+ t_symbol *m_x_arrayname, *m_y_arrayname, *m_z_arrayname, *m_id_arrayname;
+
+};
+#endif // for header file
diff --git a/src/pix_opencv_warpperspective.cc b/src/pix_opencv_warpperspective.cc
new file mode 100644
index 0000000..b418ee4
--- /dev/null
+++ b/src/pix_opencv_warpperspective.cc
@@ -0,0 +1,313 @@
+////////////////////////////////////////////////////////
+//
+// 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.
+//
+/////////////////////////////////////////////////////////
+//
+// pix_opencv_warpperspective by Antoine Villeret - 2011/13
+
+
+
+
+#include "pix_opencv_warpperspective.h"
+#include <stdio.h>
+
+CPPEXTERN_NEW(pix_opencv_warpperspective)
+
+/////////////////////////////////////////////////////////
+// Constructor
+//
+/////////////////////////////////////////////////////////
+
+pix_opencv_warpperspective :: pix_opencv_warpperspective()
+{
+ //inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("minarea"));
+ //inlet_new(this->x_obj, &this->x_obj->ob_pd, gensym("float"), gensym("maxarea"));
+ m_dataout = outlet_new(this->x_obj, 0);
+ //m_countout = outlet_new(this->x_obj, 0);
+ comp_xsize = 320;
+ comp_ysize = 240;
+ gray = NULL;
+ tmp = NULL;
+ rgb = NULL;
+ mapMatrix = cvCreateMat(3,3,CV_32FC1);
+ srcMatrix = cvCreateMat(4,2,CV_32FC1);
+ dstMatrix = cvCreateMat(4,2,CV_32FC1);
+
+ cvSet(mapMatrix, cvScalar(0)); // set all to 0.
+ CV_MAT_ELEM( *mapMatrix, float, 0, 0 ) = 1.; // then make identity
+ CV_MAT_ELEM( *mapMatrix, float, 1, 1 ) = 1.;
+
+ // initialize srcMatrix & dstMatrix to the same...
+ CV_MAT_ELEM( *srcMatrix, float, 0, 0 ) = 0.;
+ CV_MAT_ELEM( *srcMatrix, float, 0, 1 ) = 0.;
+ CV_MAT_ELEM( *srcMatrix, float, 1, 0 ) = 1.;
+ CV_MAT_ELEM( *srcMatrix, float, 1, 1 ) = 0.;
+ CV_MAT_ELEM( *srcMatrix, float, 2, 0 ) = 1.;
+ CV_MAT_ELEM( *srcMatrix, float, 2, 1 ) = 1.;
+ CV_MAT_ELEM( *srcMatrix, float, 3, 0 ) = 0.;
+ CV_MAT_ELEM( *srcMatrix, float, 3, 1 ) = 1.;
+
+ CV_MAT_ELEM( *dstMatrix, float, 0, 0 ) = 0.;
+ CV_MAT_ELEM( *dstMatrix, float, 0, 1 ) = 0.;
+ CV_MAT_ELEM( *dstMatrix, float, 1, 0 ) = 1.;
+ CV_MAT_ELEM( *dstMatrix, float, 1, 1 ) = 0.;
+ CV_MAT_ELEM( *dstMatrix, float, 2, 0 ) = 1.;
+ CV_MAT_ELEM( *dstMatrix, float, 2, 1 ) = 1.;
+ CV_MAT_ELEM( *dstMatrix, float, 3, 0 ) = 0.;
+ CV_MAT_ELEM( *dstMatrix, float, 3, 1 ) = 1.;
+
+ mapMatrixList=new t_atom[9];
+
+ flags = CV_WARP_FILL_OUTLIERS; // TODO add a set method
+ findmethod = 0; // TODO add a set method
+
+}
+
+/////////////////////////////////////////////////////////
+// Destructor
+//
+/////////////////////////////////////////////////////////
+pix_opencv_warpperspective :: ~pix_opencv_warpperspective()
+{
+ //Destroy cv_images to clean memory
+ //if (gray) cvReleaseImage(&gray); // TODO free image header but not data because it points to Gem image...
+ if (rgb) cvReleaseImage(&rgb);
+ //if (tmp) cvReleaseImage(&tmp); // the same
+ if (mapMatrix) cvReleaseMat(&mapMatrix);
+ if (srcMatrix) cvReleaseMat(&srcMatrix);
+ if (dstMatrix) cvReleaseMat(&dstMatrix);
+}
+
+/////////////////////////////////////////////////////////
+// processImage
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_warpperspective :: processRGBAImage(imageStruct &image)
+{
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!rgb))
+ {
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if(gray) cvReleaseImage(&gray);
+ if(rgb) cvReleaseImage(&rgb);
+ if(tmp) cvReleaseImage(&tmp);
+
+ // Create images with new sizes
+ rgb = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ tmp = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 4);
+ }
+
+ // no need to copy a lot of memory, just point to it...
+ rgb->imageData = (char*) image.data;
+ cvWarpPerspective(rgb, tmp, mapMatrix, flags, cvScalar(0));
+ memcpy(image.data, tmp->imageData, image.xsize*image.ysize*image.csize);
+}
+
+void pix_opencv_warpperspective :: processRGBImage(imageStruct &image)
+{
+ // TODO
+ error("cant't support RGB image for now");
+}
+
+void pix_opencv_warpperspective :: processYUVImage(imageStruct &image)
+{
+ // TODO
+ error( "pix_opencv_warpperspective : yuv format not supported" );
+}
+
+void pix_opencv_warpperspective :: processGrayImage(imageStruct &image)
+{
+ if ((this->comp_xsize!=image.xsize)||(this->comp_ysize!=image.ysize)||(!gray))
+ {
+ this->comp_xsize = image.xsize;
+ this->comp_ysize = image.ysize;
+
+ //Destroy cv_images to clean memory
+ if(gray) cvReleaseImage(&gray);
+ if(rgb) cvReleaseImage(&rgb);
+ if(tmp) cvReleaseImage(&tmp);
+
+ // Create images with new sizes
+ gray = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 1);
+ tmp = cvCreateImage(cvSize(image.xsize,image.ysize), IPL_DEPTH_8U, 1);
+ }
+
+ // no need to copy a lot of memory, just point to it...
+ gray->imageData = (char*) image.data;
+ cvWarpPerspective(gray, tmp, mapMatrix, flags, cvScalar(0));
+ memcpy(image.data, tmp->imageData, image.xsize*image.ysize);
+
+}
+
+/////////////////////////////////////////////////////////
+// floatThreshMess
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_warpperspective :: mapMatrixMess (int argc, t_atom *argv)
+{
+ post("set mapMatrix");
+ int i;
+ if (argc != 9) {
+ error("map matrix should be 3x3");
+ return;
+ }
+ for ( i = 0; i < 9 ; i++) {
+ if (argv[i].a_type != A_FLOAT) {
+ error("map matrix should be float");
+ return;
+ }
+ }
+
+ // fillin the mapMatrix
+ CV_MAT_ELEM( *mapMatrix, float, 0, 0 ) = argv[0].a_w.w_float;
+ CV_MAT_ELEM( *mapMatrix, float, 1, 0 ) = argv[1].a_w.w_float;
+ CV_MAT_ELEM( *mapMatrix, float, 2, 0 ) = argv[2].a_w.w_float;
+ CV_MAT_ELEM( *mapMatrix, float, 0, 1 ) = argv[3].a_w.w_float;
+ CV_MAT_ELEM( *mapMatrix, float, 1, 1 ) = argv[4].a_w.w_float;
+ CV_MAT_ELEM( *mapMatrix, float, 2, 1 ) = argv[5].a_w.w_float;
+ CV_MAT_ELEM( *mapMatrix, float, 0, 2 ) = argv[6].a_w.w_float;
+ CV_MAT_ELEM( *mapMatrix, float, 1, 2 ) = argv[7].a_w.w_float;
+ CV_MAT_ELEM( *mapMatrix, float, 2, 2 ) = argv[8].a_w.w_float;
+
+}
+
+void pix_opencv_warpperspective :: srcMatrixMess (int argc, t_atom *argv)
+{
+ int i;
+ if ( argc % 2 ) {
+ error("src is should be a list of couple x/y values");
+ return;
+ }
+ if ( argc != dstMatrix->rows * dstMatrix->cols )
+ {
+ error("src matrix should have the same size as dst matrix (which is %d x %d)", dstMatrix->cols, dstMatrix->rows);
+ return;
+ }
+ for ( i = 0; i < argc ; i++) {
+ if (argv[i].a_type != A_FLOAT) {
+ error("src matrix should be float");
+ return;
+ }
+ }
+
+ // fillin the srcMatrix
+ for ( i = 0 ; i < dstMatrix->rows ; i++ )
+ {
+ CV_MAT_ELEM( *srcMatrix, float, i, 0 ) = argv[i*2].a_w.w_float;
+ CV_MAT_ELEM( *srcMatrix, float, i, 1 ) = argv[i*2+1].a_w.w_float; // does it work ?
+ }
+ findhomography();
+}
+
+void pix_opencv_warpperspective :: dstMatrixMess (int argc, t_atom *argv)
+{
+ int i;
+ if ( argc % 2 ){
+ error("dstMatrix is should be a list of x/y pairs");
+ return;
+ }
+ for ( i = 0; i < argc ; i++) {
+ if (argv[i].a_type != A_FLOAT) {
+ error("dstMatrix should be float");
+ return;
+ }
+ }
+ if ( dstMatrix->rows != argc/2 ) {
+ // delete and recreate matrix if needed
+ cvReleaseMat(&dstMatrix);
+ cvReleaseMat(&srcMatrix);
+ dstMatrix = cvCreateMat(argc/2,2,CV_32FC1);
+ srcMatrix = cvCreateMat(argc/2,2,CV_32FC1);
+ cvSet(srcMatrix, cvScalar(0)); // set all to 0.
+ }
+ // fillin the dstMatrix
+ for ( i = 0 ; i < dstMatrix->rows ; i++ )
+ {
+ CV_MAT_ELEM( *dstMatrix, float, i, 0 ) = argv[i*2].a_w.w_float;
+ CV_MAT_ELEM( *dstMatrix, float, i, 1 ) = argv[i*2+1].a_w.w_float; // does it work ?
+ }
+ findhomography();
+}
+
+void pix_opencv_warpperspective :: invertMess( int argc, t_atom *argv ){
+ if ( argc == 0 ){
+ error("invert need one argument (0|1)");
+ } else if ( argv[0].a_type != A_FLOAT ){
+ error("invert need one float argument (0|1)");
+ } else {
+ int invert = argv[0].a_w.w_float;
+ if (invert > 0){
+ flags |= cv::WARP_INVERSE_MAP;
+ } else {
+ flags ^= cv::WARP_INVERSE_MAP;
+ }
+ }
+}
+
+void pix_opencv_warpperspective :: findhomography( )
+{
+ int i,j;
+ if ( srcMatrix->cols != dstMatrix->cols || srcMatrix->rows != dstMatrix->rows ) {
+ error("srcMatrix and dstMatrix should have the same size to compute homography !");
+ return;
+ }
+ cvFindHomography(srcMatrix, dstMatrix, mapMatrix, findmethod, 0, NULL);
+ for ( j = 0 ; j < 3 ; j++ ){
+ for( i=0 ; i<3 ; i++){
+ SETFLOAT(&mapMatrixList[i+j*3], CV_MAT_ELEM( *mapMatrix, float, i, j));
+ }
+ }
+ // send out mapMatrix
+ outlet_list( m_dataout, 0, 9, mapMatrixList);
+}
+/////////////////////////////////////////////////////////
+// static member function
+//
+/////////////////////////////////////////////////////////
+void pix_opencv_warpperspective :: obj_setupCallback(t_class *classPtr)
+{
+ // TODO add method for message "flags"
+ // TODO treat list messages in a PD way ?
+ class_addmethod(classPtr, (t_method)&pix_opencv_warpperspective::mapMatrixMessCallback,
+ gensym("mapMatrix"), A_GIMME, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_warpperspective::srcMatrixMessCallback,
+ gensym("srcMatrix"), A_GIMME, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_warpperspective::dstMatrixMessCallback,
+ gensym("dstMatrix"), A_GIMME, A_NULL);
+ class_addmethod(classPtr, (t_method)&pix_opencv_warpperspective::invertMessCallback,
+ gensym("invert"), A_GIMME, A_NULL);
+}
+
+void pix_opencv_warpperspective :: mapMatrixMessCallback(void *data, t_symbol *s, int argc, t_atom *argv)
+{
+ GetMyClass(data)->mapMatrixMess(argc, argv);
+}
+
+void pix_opencv_warpperspective :: srcMatrixMessCallback(void *data, t_symbol *s, int argc, t_atom *argv)
+{
+ GetMyClass(data)->srcMatrixMess(argc, argv);
+}
+
+void pix_opencv_warpperspective :: dstMatrixMessCallback(void *data, t_symbol *s, int argc, t_atom *argv)
+{
+ GetMyClass(data)->dstMatrixMess(argc, argv);
+}
+
+void pix_opencv_warpperspective :: invertMessCallback(void *data, t_symbol *s, int argc, t_atom *argv)
+{
+ GetMyClass(data)->invertMess(argc, argv);
+}
diff --git a/src/pix_opencv_warpperspective.h b/src/pix_opencv_warpperspective.h
new file mode 100644
index 0000000..0f21ae1
--- /dev/null
+++ b/src/pix_opencv_warpperspective.h
@@ -0,0 +1,100 @@
+/*-----------------------------------------------------------------
+LOG
+ GEM - Graphics Environment for Multimedia
+
+ Compute homography between 2 sets on points and apply transformation to input image
+
+
+ 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.
+
+-----------------------------------------------------------------*/
+
+// pix_opencv_warpperspective by Antoine Villeret - 2011/13
+
+#ifndef INCLUDE_PIX_OPENCV_WARPPERSPECTIVE_H_
+#define INCLUDE_PIX_OPENCV_WARPPERSPECTIVE_H_
+
+#ifndef _EiC
+#include "opencv2/opencv.hpp"
+
+#endif
+
+#include "Base/GemPixObj.h"
+
+/*-----------------------------------------------------------------
+-------------------------------------------------------------------
+CLASS
+ pix_opencv_warpperspective
+
+KEYWORDS
+ pix
+
+DESCRIPTION
+
+-----------------------------------------------------------------*/
+class GEM_EXPORT pix_opencv_warpperspective : public GemPixObj
+{
+ CPPEXTERN_HEADER(pix_opencv_warpperspective, GemPixObj)
+
+ public:
+
+ //////////
+ // Constructor
+ pix_opencv_warpperspective();
+
+ protected:
+
+ //////////
+ // Destructor
+ virtual ~pix_opencv_warpperspective();
+
+ //////////
+ // Do the processing
+ virtual void processRGBAImage(imageStruct &image);
+ virtual void processRGBImage(imageStruct &image);
+ virtual void processYUVImage(imageStruct &image);
+ virtual void processGrayImage(imageStruct &image);
+
+ /////////
+ // Setup
+ void mapMatrixMess (int argc, t_atom *argv);
+ void srcMatrixMess (int argc, t_atom *argv);
+ void dstMatrixMess (int argc, t_atom *argv);
+ void invertMess (int argc, t_atom *argv);
+
+ void findhomography();
+
+ // to detect changes in the image size
+ int comp_xsize;
+ int comp_ysize;
+
+ CvMat *mapMatrix; // 3x3 transformation matrix
+ CvMat *srcMatrix, *dstMatrix; // nX2 points coordinates matrices, n>4
+ t_atom *mapMatrixList; // array to send out transformation Matrix
+ int flags;
+ int findmethod;
+
+
+ private:
+
+ t_outlet *m_dataout;
+ //////////
+ // Static member functions
+ static void mapMatrixMessCallback(void *data, t_symbol *s, int argc, t_atom *argv);
+ static void srcMatrixMessCallback(void *data, t_symbol *s, int argc, t_atom *argv);
+ static void dstMatrixMessCallback(void *data, t_symbol *s, int argc, t_atom *argv);
+ static void invertMessCallback(void *data, t_symbol *s, int argc, t_atom *argv);
+
+
+ /////////
+ // IplImage needed
+ IplImage *rgb, *tmp, *gray;
+
+};
+
+#endif // for header file