From ba994f4404b6eadcab4e0ead46ef4d3ffeceb024 Mon Sep 17 00:00:00 2001 From: Antoine Villeret Date: Thu, 10 Jul 2014 14:39:22 +0000 Subject: lots of changes ! 1. switch to a new build system based on automake (because we need to check for some lib on ./configure before make) 2. sort files in different directory 3. add some new features (some of them need OpenCV >= 2.4.5) svn path=/trunk/externals/pix_opencv/; revision=17324 --- src/Blob.hpp | 56 ++ src/Makefile.am | 129 ++++ src/blobtrack.h | 121 ++++ src/pix_opencv.cc | 38 + src/pix_opencv_athreshold.cc | 261 +++++++ src/pix_opencv_athreshold.h | 95 +++ src/pix_opencv_backgroundsubtractor.cc | 374 ++++++++++ src/pix_opencv_backgroundsubtractor.h | 96 +++ src/pix_opencv_bgstats.cc | 251 +++++++ src/pix_opencv_bgstats.h | 100 +++ src/pix_opencv_bgsubstract.cc | 258 +++++++ src/pix_opencv_bgsubstract.h | 86 +++ src/pix_opencv_blobtrack.cc | 693 ++++++++++++++++++ src/pix_opencv_blobtrack.h | 106 +++ src/pix_opencv_calibration.cc | 547 +++++++++++++++ src/pix_opencv_calibration.h | 120 ++++ src/pix_opencv_camshift.cc | 450 ++++++++++++ src/pix_opencv_camshift.h | 110 +++ src/pix_opencv_clahe.cc | 171 +++++ src/pix_opencv_clahe.h | 84 +++ src/pix_opencv_colorfilt.cc | 299 ++++++++ src/pix_opencv_colorfilt.h | 103 +++ src/pix_opencv_contours.cc | 591 ++++++++++++++++ src/pix_opencv_contours.h | 89 +++ src/pix_opencv_contours_boundingrect.cc | 772 ++++++++++++++++++++ src/pix_opencv_contours_boundingrect.h | 124 ++++ src/pix_opencv_contours_convexhull.cc | 582 +++++++++++++++ src/pix_opencv_contours_convexhull.h | 88 +++ src/pix_opencv_contours_convexity.cc | 685 ++++++++++++++++++ src/pix_opencv_contours_convexity.h | 88 +++ src/pix_opencv_dft.cc | 462 ++++++++++++ src/pix_opencv_dft.h | 88 +++ src/pix_opencv_distrans.cc | 420 +++++++++++ src/pix_opencv_distrans.h | 86 +++ src/pix_opencv_edge.cc | 234 +++++++ src/pix_opencv_edge.h | 82 +++ src/pix_opencv_facetracker.cc | 302 ++++++++ src/pix_opencv_facetracker.h | 107 +++ src/pix_opencv_findchessboardcorners.cc | 220 ++++++ src/pix_opencv_findchessboardcorners.h | 98 +++ src/pix_opencv_floodfill.cc | 442 ++++++++++++ src/pix_opencv_floodfill.h | 111 +++ src/pix_opencv_haarcascade.cc | 526 ++++++++++++++ src/pix_opencv_haarcascade.h | 113 +++ src/pix_opencv_hist_compare.cc | 279 ++++++++ src/pix_opencv_hist_compare.h | 88 +++ src/pix_opencv_hough_circles.cc | 315 +++++++++ src/pix_opencv_hough_circles.h | 102 +++ src/pix_opencv_hough_lines.cc | 600 ++++++++++++++++ src/pix_opencv_hough_lines.h | 110 +++ src/pix_opencv_hu_compare.cc | 468 +++++++++++++ src/pix_opencv_hu_compare.h | 93 +++ src/pix_opencv_hu_moments.cc | 207 ++++++ src/pix_opencv_hu_moments.h | 86 +++ src/pix_opencv_knear.cc | 502 +++++++++++++ src/pix_opencv_knear.h | 108 +++ src/pix_opencv_laplace.cc | 228 ++++++ src/pix_opencv_laplace.h | 83 +++ src/pix_opencv_lk.cc | 1170 +++++++++++++++++++++++++++++++ src/pix_opencv_lk.h | 132 ++++ src/pix_opencv_matchshape.cc | 226 ++++++ src/pix_opencv_matchshape.h | 74 ++ src/pix_opencv_morphology.cc | 291 ++++++++ src/pix_opencv_morphology.h | 87 +++ src/pix_opencv_motempl.cc | 708 +++++++++++++++++++ src/pix_opencv_motempl.h | 132 ++++ src/pix_opencv_of_bm.cc | 326 +++++++++ src/pix_opencv_of_bm.h | 101 +++ src/pix_opencv_of_hs.cc | 531 ++++++++++++++ src/pix_opencv_of_hs.h | 100 +++ src/pix_opencv_of_lk.cc | 516 ++++++++++++++ src/pix_opencv_of_lk.h | 96 +++ src/pix_opencv_opticalflow.cc | 229 ++++++ src/pix_opencv_opticalflow.h | 96 +++ src/pix_opencv_patreco.h | 95 +++ src/pix_opencv_pgh_compare.cc | 481 +++++++++++++ src/pix_opencv_pgh_compare.h | 91 +++ src/pix_opencv_surf.cc | 1054 ++++++++++++++++++++++++++++ src/pix_opencv_surf.h | 122 ++++ src/pix_opencv_template.cc | 63 ++ src/pix_opencv_template.h | 57 ++ src/pix_opencv_threshold.cc | 262 +++++++ src/pix_opencv_threshold.h | 88 +++ src/pix_opencv_trackKnn.cc | 602 ++++++++++++++++ src/pix_opencv_trackKnn.h | 93 +++ src/pix_opencv_warpperspective.cc | 313 +++++++++ src/pix_opencv_warpperspective.h | 100 +++ 87 files changed, 22363 insertions(+) create mode 100644 src/Blob.hpp create mode 100644 src/Makefile.am create mode 100644 src/blobtrack.h create mode 100644 src/pix_opencv.cc create mode 100644 src/pix_opencv_athreshold.cc create mode 100644 src/pix_opencv_athreshold.h create mode 100644 src/pix_opencv_backgroundsubtractor.cc create mode 100644 src/pix_opencv_backgroundsubtractor.h create mode 100644 src/pix_opencv_bgstats.cc create mode 100644 src/pix_opencv_bgstats.h create mode 100644 src/pix_opencv_bgsubstract.cc create mode 100644 src/pix_opencv_bgsubstract.h create mode 100644 src/pix_opencv_blobtrack.cc create mode 100644 src/pix_opencv_blobtrack.h create mode 100644 src/pix_opencv_calibration.cc create mode 100644 src/pix_opencv_calibration.h create mode 100644 src/pix_opencv_camshift.cc create mode 100644 src/pix_opencv_camshift.h create mode 100644 src/pix_opencv_clahe.cc create mode 100644 src/pix_opencv_clahe.h create mode 100644 src/pix_opencv_colorfilt.cc create mode 100644 src/pix_opencv_colorfilt.h create mode 100644 src/pix_opencv_contours.cc create mode 100644 src/pix_opencv_contours.h create mode 100644 src/pix_opencv_contours_boundingrect.cc create mode 100644 src/pix_opencv_contours_boundingrect.h create mode 100644 src/pix_opencv_contours_convexhull.cc create mode 100644 src/pix_opencv_contours_convexhull.h create mode 100644 src/pix_opencv_contours_convexity.cc create mode 100644 src/pix_opencv_contours_convexity.h create mode 100644 src/pix_opencv_dft.cc create mode 100644 src/pix_opencv_dft.h create mode 100644 src/pix_opencv_distrans.cc create mode 100644 src/pix_opencv_distrans.h create mode 100644 src/pix_opencv_edge.cc create mode 100644 src/pix_opencv_edge.h create mode 100644 src/pix_opencv_facetracker.cc create mode 100644 src/pix_opencv_facetracker.h create mode 100644 src/pix_opencv_findchessboardcorners.cc create mode 100644 src/pix_opencv_findchessboardcorners.h create mode 100644 src/pix_opencv_floodfill.cc create mode 100644 src/pix_opencv_floodfill.h create mode 100644 src/pix_opencv_haarcascade.cc create mode 100644 src/pix_opencv_haarcascade.h create mode 100644 src/pix_opencv_hist_compare.cc create mode 100644 src/pix_opencv_hist_compare.h create mode 100644 src/pix_opencv_hough_circles.cc create mode 100644 src/pix_opencv_hough_circles.h create mode 100644 src/pix_opencv_hough_lines.cc create mode 100644 src/pix_opencv_hough_lines.h create mode 100644 src/pix_opencv_hu_compare.cc create mode 100644 src/pix_opencv_hu_compare.h create mode 100644 src/pix_opencv_hu_moments.cc create mode 100644 src/pix_opencv_hu_moments.h create mode 100644 src/pix_opencv_knear.cc create mode 100644 src/pix_opencv_knear.h create mode 100644 src/pix_opencv_laplace.cc create mode 100644 src/pix_opencv_laplace.h create mode 100644 src/pix_opencv_lk.cc create mode 100644 src/pix_opencv_lk.h create mode 100644 src/pix_opencv_matchshape.cc create mode 100644 src/pix_opencv_matchshape.h create mode 100644 src/pix_opencv_morphology.cc create mode 100644 src/pix_opencv_morphology.h create mode 100644 src/pix_opencv_motempl.cc create mode 100644 src/pix_opencv_motempl.h create mode 100644 src/pix_opencv_of_bm.cc create mode 100644 src/pix_opencv_of_bm.h create mode 100644 src/pix_opencv_of_hs.cc create mode 100644 src/pix_opencv_of_hs.h create mode 100644 src/pix_opencv_of_lk.cc create mode 100644 src/pix_opencv_of_lk.h create mode 100644 src/pix_opencv_opticalflow.cc create mode 100644 src/pix_opencv_opticalflow.h create mode 100644 src/pix_opencv_patreco.h create mode 100644 src/pix_opencv_pgh_compare.cc create mode 100644 src/pix_opencv_pgh_compare.h create mode 100644 src/pix_opencv_surf.cc create mode 100644 src/pix_opencv_surf.h create mode 100644 src/pix_opencv_template.cc create mode 100644 src/pix_opencv_template.h create mode 100644 src/pix_opencv_threshold.cc create mode 100644 src/pix_opencv_threshold.h create mode 100644 src/pix_opencv_trackKnn.cc create mode 100644 src/pix_opencv_trackKnn.h create mode 100644 src/pix_opencv_warpperspective.cc create mode 100644 src/pix_opencv_warpperspective.h (limited to 'src') 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 + +class Blob { + +public: + + std::vector 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 +#include + +/* 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 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(m_bgsub_algos[0]); + } else { + m_fgbgMOG = Algorithm::create(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 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 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 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 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 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(paramList[i]); + } else if ( !m_fgbgGMG.empty() ){ + val = m_fgbgGMG->get(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(m_bgsub_algos[id]); + } else { + m_fgbgMOG = Algorithm::create(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(algo); + } else { + m_fgbgMOG = Algorithm::create(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 +#include "Gem/Exception.h" + +#include + +/*----------------------------------------------------------------- +------------------------------------------------------------------- +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 m_fgbgMOG; + cv::Ptr 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 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; iGetBlob(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 : "); + 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 []"); + } else error("getModules [] : need 1 or 2 args"); +} + +void pix_opencv_blobtrack :: setModuleMess(t_symbol*s, int argc, t_atom*argv) +{ + if ( argc != 2 ){ + error("use : setModule "); + 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 +#endif + +#include "blobtrack.h" + +#include "Base/GemPixObj.h" +#include +#include +/*----------------------------------------------------------------- +------------------------------------------------------------------- +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 + +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; jimage_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 + +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 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 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; pyheight; py++ ) { + for( px=0; pxwidth; 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; pyheight; py++ ) { + for( px=0; pxwidth; 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 + +#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 +#include + +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 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 > contours; + std::vector 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 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 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 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 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 defects(m_convexhulls[i].size()); + + cv::Ptr 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 it = cv::Seq(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(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 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 > m_contours; + std::vector > m_convexhulls; + std::vector 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 + +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; icomp_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; imh_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; immark(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; imimageData, 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; imh_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; immark(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; imimageData, 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; imh_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; immark(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; imimageData, 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; ifloatMaxAreaMess((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; icnt_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; icnt_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; icnt_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; ix); + 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; icnt_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; ix); + 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; icnt_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; ix); + 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; icnt_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; pyheight; 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; pxwidth; 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; pyheight; 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; pxwidth; 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; pyheight; 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; pxwidth; 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 consecutive(int start, int end) { + int n = end - start; + std::vector result(n); + for(int i = 0; i < n; i++) { + result[i] = start + i; + } + return result; +} + +std::vector 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(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(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 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(indices[j],0)/image.cols; + vec[idx+1].w_float = (float) shape.at(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(tri.at(i,0),0) == 0 || + visi.at(tri.at(i,1),0) == 0 || + visi.at(tri.at(i,2),0) == 0)continue; + p1 = cv::Point(shape.at(tri.at(i,0),0), + shape.at(tri.at(i,0)+n,0)); + p2 = cv::Point(shape.at(tri.at(i,1),0), + shape.at(tri.at(i,1)+n,0)); + cv::line(image,p1,p2,c); + p1 = cv::Point(shape.at(tri.at(i,0),0), + shape.at(tri.at(i,0)+n,0)); + p2 = cv::Point(shape.at(tri.at(i,2),0), + shape.at(tri.at(i,2)+n,0)); + cv::line(image,p1,p2,c); + p1 = cv::Point(shape.at(tri.at(i,2),0), + shape.at(tri.at(i,2)+n,0)); + p2 = cv::Point(shape.at(tri.at(i,1),0), + shape.at(tri.at(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(con.at(0,i),0) == 0 || + visi.at(con.at(1,i),0) == 0)continue; + p1 = cv::Point(shape.at(con.at(0,i),0), + shape.at(con.at(0,i)+n,0)); + p2 = cv::Point(shape.at(con.at(1,i),0), + shape.at(con.at(1,i)+n,0)); + cv::line(image,p1,p2,c,1); + } + //draw points + for(i = 0; i < n; i++){ + if(visi.at(i,0) == 0)continue; + p1 = cv::Point(shape.at(i,0),shape.at(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 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 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 m_wSize1; + std::vector 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 + +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; ix); + 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; ix); + 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 + +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; icomp_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; iimageData, 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; iimageData, 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; iimageData, 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 ) ) + { + 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= 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 +#include + +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; icomp_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; imx_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; imx_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; imx_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; imx_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; imx_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; imx_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; ix_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; ix_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; ncomp_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; nimageData = (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; nmax) { + 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)indexsaveMess(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 + +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 + +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; itotal >= x_maxlines ) ? x_maxlines:x_lines->total; + for( i=0; itotal >= 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; itotal >= x_maxlines ) ? x_maxlines:x_lines->total; + for( i=0; itotal >= 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; itotal >= x_maxlines ) ? x_maxlines:x_lines->total; + for( i=0; itotal >= 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; ix_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; ix_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; ix_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 +#include + +/*----------------------------------------------------------------- +------------------------------------------------------------------- +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 + +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; icomp_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= 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) 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= 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= 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) 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= 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= 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) 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= 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 ) ) + { + return; + } + + x_xmark[(int)index] = -1; + x_ymark[(int)index] = -1; + +} + +void pix_opencv_lk :: clearMess(void) +{ + int i; + + for ( i=0; i1.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 +#include + +#ifdef _MSC_VER +#include +#define INFINITY std::numeric_limits::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 >::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 tmp_contour; + for (int i=2;i >::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 tmp_contour; + tmp_contour.clear(); + for (int i=2;i0.?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 > m_template_vec_vec; + std::vector 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 +#include +#include +#endif +#include + +#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 + +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_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 + +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_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_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_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 + +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_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_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_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(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(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(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(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 + +/*----------------------------------------------------------------- +------------------------------------------------------------------- +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 m_patternLibrary; // pattern library + std::map m_patternLibrary; + std::vector 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 + + +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; icomp_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= 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) 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; impt.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; ipt.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; imcomp_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; imimageData, 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= 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) 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; impt.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; ipt.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; imcomp_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; imimageData, 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= 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) 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; impt.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; ipt.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; imcomp_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; imimageData, 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 ) ) + { + 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=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 +#include +#include + + +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 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(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 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 > nbors; + std::list >::iterator iter; + + //find 'k' closest neighbors of testpoint + double x, y, xT, yT, dist; + for(unsigned int i=0; icentroid.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()(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 > 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 && distfirst; + } + } + + 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 + +#include +#include + +#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 m_trackedBlobs; + std::vector 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 + +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 -- cgit v1.2.1