1 /*M///////////////////////////////////////////////////////////////////////////////////////
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
5 // By downloading, copying, installing or using the software you agree to this license.
6 // If you do not agree to this license, do not download, install,
7 // copy or use the software.
10 // Intel License Agreement
11 // For Open Source Computer Vision Library
13 // Copyright (C) 2002, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
19 // * Redistributions of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
22 // * Redistributions in binary form must reproduce the above copyright notice,
23 // this list of conditions and the following disclaimer in the documentation
24 // and/or other materials provided with the distribution.
26 // * The name of Intel Corporation may not be used to endorse or promote products
27 // derived from this software without specific prior written permission.
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
43 // ////////////////////////////////////////////////////////////////////////////
46 // This file contains the bulk of the implementation of the
49 // ////////////////////////////////////////////////////////////////////////////
54 #include "cvstreams.h"
55 #include "BlobTracker.h"
56 #include "BlobTrkObject.h"
57 #include "BlobTrackerPropertyPage.h"
59 static void FloodFill(unsigned char *image, int width, int height, int start_x, int start_y, int new_color, CvRect &rect, int &area);
65 // Used by the DirectShow base classes to create instances
67 CUnknown *BlobTracker::CreateInstance(IUnknown *outer, HRESULT *phr)
69 CUnknown *punk = new BlobTracker(outer, phr);
78 // ////////////////////////////////////////////////////////////////////////////
79 // BlobTracker::BlobTracker()
83 // ////////////////////////////////////////////////////////////////////////////
84 BlobTracker::BlobTracker(IUnknown *outer, HRESULT *phr)
85 : CUnknown(NAME("Blob Tracker"), outer)
87 // Set member variables to defaults
88 m_output_options = IBlobTracker::OUTPUT_CROSSHAIRS;
90 m_pixel_threshold = 99;
92 m_distance_threshold = 80;
95 m_objects = new BlobTrackerObjectList;
98 // ////////////////////////////////////////////////////////////////////////////
99 // BlobTracker::~BlobTracker()
103 // ////////////////////////////////////////////////////////////////////////////
104 BlobTracker::~BlobTracker()
106 cvReleaseImage( &m_image );
107 cvReleaseImage( &m_tmp_image );
113 HRESULT BlobTracker::NonDelegatingQueryInterface(REFIID iid, void **ppv)
115 if (iid == IID_IBlobTracker)
116 return GetInterface((IUnknown *)(void *)static_cast<IBlobTracker *>(this), ppv);
118 if (iid == IID_ITracker)
119 return GetInterface((IUnknown *)(void *)static_cast<ITracker *>(this), ppv);
121 return CUnknown::NonDelegatingQueryInterface(iid, ppv);
127 STDMETHODIMP BlobTracker::CheckFormat(IplImage *image_header)
129 if (image_header->depth != IPL_DEPTH_8U
130 || image_header->width % 4 != 0)
135 if (image_header->nChannels == 1
136 && strncmp(image_header->colorModel, "GRAY", 4) == 0)
141 if (image_header->nChannels == 3
142 && strncmp(image_header->colorModel, "RGB", 3) == 0)
150 STDMETHODIMP BlobTracker::SetFormat(IplImage *image_header)
152 HRESULT hr = CheckFormat(image_header);
156 m_image_format = *image_header;
158 m_image = cvCreateImage( cvSize(image_header->width,image_header->height), IPL_DEPTH_8U, 1 );
159 m_image->origin = image_header->origin;
161 if (image_header->nChannels != 1)
163 m_tmp_image = cvCreateImage( cvSize(image_header->width,image_header->height), IPL_DEPTH_8U, 1 );
164 m_image->origin = image_header->origin;
171 STDMETHODIMP BlobTracker::SetNumberOfObjects(int num_objects)
176 m_num_objects = num_objects;
181 STDMETHODIMP BlobTracker::GetPropertyPage(GUID *page)
183 *page = CLSID_BlobTrackerPropertyPage;
187 // ////////////////////////////////////////////////////////////////////////////
188 // BlobTracker::Process()
190 // Performs the processing of one frame.
193 // Input frame data (src)
195 // ////////////////////////////////////////////////////////////////////////////
197 static inline bool MatchFormat(IplImage *image, IplImage *format)
199 return (image->nChannels == format->nChannels
200 && image->width == format->width
201 && image->height == format->height
202 && image->depth == format->depth
203 && image->dataOrder == format->dataOrder
204 && image->origin == format->origin
205 && strncmp(image->channelSeq, format->channelSeq, 4) == 0);
209 // Used for sorting in descending order by size
210 static inline bool size_cmp(const BlobTrackerObject &o1, const BlobTrackerObject &o2)
212 return o1.GetSize() > o2.GetSize();
215 // compute the square of the distance between two points.
216 static inline int distance(const CvPoint &p1, const CvPoint &p2)
218 int dx = p1.x - p2.x;
219 int dy = p1.y - p2.y;
220 return dx*dx + dy*dy;
223 static inline bool InRect(const CvRect &rect, const CvPoint &p)
225 return (p.x >= rect.x && p.x < rect.x + rect.width
226 && p.y >= rect.y && p.y < rect.y + rect.height);
229 STDMETHODIMP BlobTracker::Process(IplImage *image)
231 // Make sure we've gone through our initialization stage
232 if (m_image == NULL || m_num_objects == 0)
235 // Make sure the image format matches what is expected.
236 if (!MatchFormat(image, &m_image_format))
239 // Convert the image to gray if required.
240 IplImage *gray_image = image;
241 if (image->nChannels != 1)
243 cvCvtColor(image, m_tmp_image, CV_BGR2GRAY );
244 gray_image = m_tmp_image;
247 // Dilation or blurring of the image enlarges small points to make them more visible
248 // and connects any points with gaps so they become a single point.
249 // Blurring has the advantage that it helps filter out noise, whereas dilation
250 // exacerbates noise. However, blurring reduces the intensity of the bright spots,
251 // so the threshold must be set lower.
252 // Since background noise isn't a big problem with the cameras we use, on the
253 // whole we determined that dilation improves tracking.
254 cvDilate(gray_image, m_image, 0, 2);
255 //iplBlur(gray_image, m_image, 3, 3, 1, 1);
258 // Copy the modified image back into the input image, so the
259 // image seen in the video window is the one that is actually processed.
260 // This is fairly expensive, especially if color conversion is required
261 // (i.e, if the input image is RGB); this step may be eliminated with no
262 // loss of tracking functionality, in which case the video window will
263 // show the original image rather than the processed image. (Crosshairs
264 // are drawn on the input image in any case.)
265 if (image->nChannels == 1)
266 cvCopy(m_image, image);
268 cvCvtColor(m_image, image, CV_GRAY2BGR );
271 // Search the frame for bright spots.
272 BlobTrackerObjectList new_objects;
273 FindObjects(new_objects);
276 // Match up the objects just found with the ones found previously,
277 // to try to achieve continuity of object ids across frames.
278 BlobTrackerObjectList old_objects = *m_objects;
280 unsigned long matched = 0;
281 for (BlobTrackerObjectList::iterator old = old_objects.begin(); old != old_objects.end(); )
283 int min_d = m_distance_threshold;
284 BlobTrackerObjectList::iterator min_o = new_objects.end();
285 for (BlobTrackerObjectList::iterator o = new_objects.begin(); o != new_objects.end(); o++)
287 if (InRect(old->GetRect(), o->GetCenter()))
293 int d = distance(o->GetCenter(), old->GetCenter());
301 if (min_o != new_objects.end())
303 int id = old->GetId();
307 m_objects->push_back(*min_o);
308 new_objects.erase(min_o);
309 old_objects.erase(old);
318 // For any objects that weren't matched up, assign ids in
319 // order of decreasing size.
320 if (m_objects->size() < m_num_objects && new_objects.size() > 0)
322 std::sort(new_objects.begin(), new_objects.end(), size_cmp);
325 for (BlobTrackerObjectList::iterator o = new_objects.begin();
326 o != new_objects.end() && m_objects->size() < m_num_objects;
329 while (matched & (1 << id))
333 m_objects->push_back(*o);
337 // Generate output frame
338 FillDestinationData(image);
344 // (These functions are not very efficient, but they are called rarely enough
345 // that it is not a big impact on performance.)
346 static inline int GetPixel(const unsigned char *image, int width, int x, int y)
348 return image[y*width + x];
351 static inline void SetPixel(unsigned char *image, int width, int x, int y, int color)
353 image[y*width + x] = color;
357 // Search the image for spots that meet the brightness and size thresholds.
358 void BlobTracker::FindObjects(BlobTrackerObjectList &objects)
360 for (int y = 0; y < m_image_format.height; y++)
362 for (int x = 0; x < m_image_format.width; x++)
364 // Check each pixel to see if it meets the brightness threshold.
365 // (This call to GetPixel is the only one that is executed with enough frequency to have
366 // any impact on performance, but optimizing it gave minimal performance improvement.)
367 if (GetPixel((unsigned char *)m_image->imageData, m_image_format.width, x, y) >= m_pixel_threshold)
369 // Find all connected bright pixels. Set them all to black while keeping track
370 // of the bounding rectangle and the number of pixels found.
373 FloodFill((unsigned char *)m_image->imageData, m_image_format.width, m_image_format.height, x, y, m_pixel_threshold, rect, area);
375 // Ignore any spots that don't meet the size threshold.
376 if (area >= m_size_threshold)
377 objects.push_back(BlobTrackerObject(rect, area));
384 // Filled horizontal segment of scanline y for xl$<=$x$<=$xr.
385 // Parent segment was on line y-dy. dy=l or -1
389 Segment(int a, int b, int c, int d) : y(a), xl(b), xr(c), dy(d) { };
392 static inline void push(std::stack<Segment> &stack, int height, int y, int xl, int xr, int dy)
395 if (y >= 0 && y < height)
396 stack.push(Segment(y, xl, xr, dy));
400 static inline void SetPixel(unsigned char *image, int width, int x, int y, int color, CvRect &rect, int &area)
404 else if (x >= rect.x+rect.width)
405 rect.width = x+1-rect.x;
408 else if (y >= rect.y+rect.height)
409 rect.height = y+1-rect.y;
410 SetPixel(image, width, x, y, color);
414 #define GET_PIXEL(x, y) GetPixel(image, width, x, y)
415 #define SET_PIXEL(x, y) SetPixel(image, width, x, y, 0, rect, area)
416 #define PUSH(y, xl, xr, dy) push(stack, height, y, xl, xr, dy)
419 // FloodFill: set the pixel at (x,y) and all of its 4-connected neighbors
420 // that meet the brightness threshold to zero while keeping track
421 // of the bounding rectangle and the number of pixels found.
422 // A 4-connected neighbor is a pixel above, below, left, or right of
424 // Algorithm adapted from one by Paul Heckbert 13 Sept 1982, 28 Jan 1987.
426 static void FloodFill(unsigned char *image, int width, int height, int start_x, int start_y, int threshold, CvRect &rect, int &area)
428 CvRect init_rect = { start_x, start_y, 1, 1 };
432 std::stack<Segment> stack;
433 PUSH(start_y, start_x, start_x, 1); /* needed in some cases */
434 PUSH(start_y+1, start_x, start_x, -1); /* seed segment (popped 1st) */
436 while (!stack.empty())
438 /* pop segment off stack and fill a neighboring scan line */
439 const Segment seg = stack.top();
442 // segment of scan line y-dy for xl<=x<=x2 was
443 // previously filled,
444 // now explore adjacent pixels in scan line y
448 for (x = seg.xl; x >= 0 && GET_PIXEL(x, seg.y) >= threshold; x--)
455 PUSH(seg.y, l, seg.xl-1, -seg.dy); /* leak on left? */
458 for (; x < width && GET_PIXEL(x, seg.y) >= threshold; x++)
460 PUSH(seg.y, l, x-1, seg.dy);
462 PUSH(seg.y, seg.xr+1, x-1, -seg.dy); /* leak on right? */
464 for (x++; x <= seg.xr && GET_PIXEL(x, seg.y) < threshold; x++)
467 } while (x <= seg.xr);
472 // Registration information
474 // List of class IDs and creator functions for the class factory. This
475 // provides the link between the COM entry point in the DLL and an object
476 // being created. The class factory will call the static CreateInstance.
478 CFactoryTemplate g_Templates[] = {
479 { L"Blob Tracker", &CLSID_BlobTracker, BlobTracker::CreateInstance },
480 { L"Blob Tracker Property Page", &CLSID_BlobTrackerPropertyPage, BlobTrackerPropertyPage::CreateInstance }
482 int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
488 // Register the COM objects (the tracker and the property page).
489 // Also add the tracker to the "Video Trackers" component category.
490 STDAPI DllRegisterServer()
492 HRESULT hr = AMovieDllRegisterServer2( TRUE );
497 hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_ALL, IID_ICatRegister, (void **)®);
501 CATEGORYINFO catinfo;
502 catinfo.catid = CATID_Trackers;
503 catinfo.lcid = 0x409;
504 wcscpy(catinfo.szDescription, L"Video Trackers");
505 reg->RegisterCategories(1, &catinfo);
506 reg->RegisterClassImplCategories(CLSID_BlobTracker, 1, const_cast<GUID *>(&CATID_Trackers));
511 } // DllRegisterServer
515 // DllUnregisterServer
517 // Unregister the COM objects (the tracker and the property page).
518 // Also remove the tracker from the "Video Trackers" component category.
519 STDAPI DllUnregisterServer()
521 HRESULT hr = AMovieDllRegisterServer2( FALSE );
526 hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_ALL, IID_ICatRegister, (void **)®);
530 reg->UnRegisterClassImplCategories(CLSID_BlobTracker, 1, const_cast<GUID *>(&CATID_Trackers));
535 } // DllUnregisterServer