Move the sources to trunk
[opencv] / filters / CamShift / CamShiftF.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
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.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's 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.
25 //
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.
28 //
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.
39 //
40 //M*/
41 #include <windows.h>
42 #include <cvstreams.h>
43 #include <initguid.h>
44 #include <olectl.h>
45 #include <math.h>
46 #if (1100 > _MSC_VER)
47 #include <olectlid.h>
48 #endif
49 #include <assert.h>
50 #include "CamShiftUIDs.h"
51 #include "CV.hpp"
52 #include "iCamShift.h"
53 #include "CamShiftProp.h"
54 #include "CamShiftF.h"
55 #include "resource.h"
56
57 // setup data
58
59 const AMOVIESETUP_MEDIATYPE sudPinTypes =
60 {
61     &MEDIATYPE_Video,       // Major type
62     &MEDIASUBTYPE_NULL      // Minor type
63 };
64
65 const AMOVIESETUP_PIN psudPins[] =
66 {
67     {
68         L"Input",           // String pin name
69         FALSE,              // Is it rendered
70         FALSE,              // Is it an output
71         FALSE,              // Allowed none
72         FALSE,              // Allowed many
73         &CLSID_NULL,        // Connects to filter
74         L"Output",          // Connects to pin
75         1,                  // Number of types
76         &sudPinTypes },     // The pin details
77       { L"Output",          // String pin name
78         FALSE,              // Is it rendered
79         TRUE,               // Is it an output
80         FALSE,              // Allowed none
81         FALSE,              // Allowed many
82         &CLSID_NULL,        // Connects to filter
83         L"Input",           // Connects to pin
84         1,                  // Number of types
85         &sudPinTypes        // The pin details
86     }
87 };
88
89
90 const AMOVIESETUP_FILTER sudCamShift =
91 {
92     &CLSID_CamShift,        // Filter CLSID
93     L"CamShift",            // Filter name
94     MERIT_DO_NOT_USE,       // Its merit
95     2,                      // Number of pins
96     psudPins                // Pin details
97 };
98
99
100 // List of class IDs and creator functions for the class factory. This
101 // provides the link between the OLE entry point in the DLL and an object
102 // being created. The class factory will call the static CreateInstance
103
104 CFactoryTemplate g_Templates[2] = {
105
106     { L"CamShift"
107     , &CLSID_CamShift
108     , CCamShiftF::CreateInstance
109     , NULL
110     , &sudCamShift }
111   ,
112     { L"CamShift Property Page"
113     , &CLSID_CamShiftPropertyPage
114     , CCamShiftProperties::CreateInstance }
115 };
116 int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
117
118
119 //
120 // Constructor
121 //
122 CCamShiftF::CCamShiftF(TCHAR *tszName,LPUNKNOWN punk,HRESULT *phr) :
123     CTransformFilter(tszName, punk, CLSID_CamShift)
124 {
125     m_params.x = 0.4f; 
126     m_params.y = 0.3f;
127     m_params.width = 0.2f;
128     m_params.height = 0.3f;
129
130     m_params.Smin = 20;
131     m_params.Vmin = 40;
132     m_params.Vmax = 255;
133     m_params.bins = 20;
134     m_params.view = 0;
135     m_params.threshold = 0;
136
137     IsTracking = false;
138     IsInit = false;
139 } // CamShift
140
141
142 //
143 // CreateInstance
144 //
145 // Provide the way for COM to create a CCamShift object
146 //
147 CUnknown * WINAPI CCamShiftF::CreateInstance(LPUNKNOWN punk, HRESULT *phr) {
148
149     CCamShiftF *pNewObject = new CCamShiftF(NAME("CamShift"), punk, phr);
150     if (pNewObject == NULL) {
151         *phr = E_OUTOFMEMORY;
152     }
153     return pNewObject;
154
155 } // CreateInstance
156
157
158 //
159 // NonDelegatingQueryInterface
160 //
161 // Reveals ICamShift and ISpecifyPropertyPages
162 //
163 STDMETHODIMP CCamShiftF::NonDelegatingQueryInterface(REFIID riid, void **ppv)
164 {
165     CheckPointer(ppv,E_POINTER);
166
167     if (riid == IID_ICamShift) {
168         return GetInterface((ICamShift *) this, ppv);
169     } else if (riid == IID_ISpecifyPropertyPages) {
170         return GetInterface((ISpecifyPropertyPages *) this, ppv);
171     } else {
172         return CTransformFilter::NonDelegatingQueryInterface(riid, ppv);
173     }
174
175 } // NonDelegatingQueryInterface
176
177
178 //
179 // Transform
180 //
181 // Copy the input sample into the output sample
182 // Then transform the output sample 'in place'
183 //
184 HRESULT CCamShiftF::Transform(IMediaSample *pIn, IMediaSample *pOut)
185 {
186     HRESULT hr = Copy(pIn, pOut);
187     if (FAILED(hr)) {
188         return hr;
189     }
190     return Transform(pOut);
191
192 } // Transform
193
194
195 //
196 // Copy
197 //
198 // Make destination an identical copy of source
199 //
200 HRESULT CCamShiftF::Copy(IMediaSample *pSource, IMediaSample *pDest) const
201 {
202     // Copy the sample data
203
204     BYTE *pSourceBuffer, *pDestBuffer;
205     long lSourceSize = pSource->GetActualDataLength();
206     long lDestSize  = pDest->GetSize();
207
208     ASSERT(lDestSize >= lSourceSize);
209
210     pSource->GetPointer(&pSourceBuffer);
211     pDest->GetPointer(&pDestBuffer);
212
213     CopyMemory( (PVOID) pDestBuffer,(PVOID) pSourceBuffer,lSourceSize);
214
215     // Copy the sample times
216
217     REFERENCE_TIME TimeStart, TimeEnd;
218     if (NOERROR == pSource->GetTime(&TimeStart, &TimeEnd)) {
219         pDest->SetTime(&TimeStart, &TimeEnd);
220     }
221
222     LONGLONG MediaStart, MediaEnd;
223     if (pSource->GetMediaTime(&MediaStart,&MediaEnd) == NOERROR) {
224         pDest->SetMediaTime(&MediaStart,&MediaEnd);
225     }
226
227     // Copy the Sync point property
228
229     HRESULT hr = pSource->IsSyncPoint();
230     if (hr == S_OK) {
231         pDest->SetSyncPoint(TRUE);
232     }
233     else if (hr == S_FALSE) {
234         pDest->SetSyncPoint(FALSE);
235     }
236     else {  // an unexpected error has occured...
237         return E_UNEXPECTED;
238     }
239
240     // Copy the media type
241
242     AM_MEDIA_TYPE *pMediaType;
243     pSource->GetMediaType(&pMediaType);
244     pDest->SetMediaType(pMediaType);
245     DeleteMediaType(pMediaType);
246
247     // Copy the preroll property
248
249     hr = pSource->IsPreroll();
250     if (hr == S_OK) {
251         pDest->SetPreroll(TRUE);
252     }
253     else if (hr == S_FALSE) {
254         pDest->SetPreroll(FALSE);
255     }
256     else {  // an unexpected error has occured...
257         return E_UNEXPECTED;
258     }
259
260     // Copy the discontinuity property
261
262     hr = pSource->IsDiscontinuity();
263     if (hr == S_OK) {
264     pDest->SetDiscontinuity(TRUE);
265     }
266     else if (hr == S_FALSE) {
267         pDest->SetDiscontinuity(FALSE);
268     }
269     else {  // an unexpected error has occured...
270         return E_UNEXPECTED;
271     }
272
273     // Copy the actual data length
274
275     long lDataLength = pSource->GetActualDataLength();
276     pDest->SetActualDataLength(lDataLength);
277     return NOERROR;
278
279 } // Copy
280
281
282 void  CCamShiftF::ApplyCamShift( IplImage* image, bool initialize )
283 {
284     CvSize size;
285     int bins = m_params.bins;
286
287     m_cCamShift.set_hist_dims( 1, &bins );
288     m_cCamShift.set_hist_bin_range( 0, 0, 180 );
289     m_cCamShift.set_threshold( 0 );
290     m_cCamShift.set_min_ch_val( 1, m_params.Smin );
291     m_cCamShift.set_max_ch_val( 1, 255 );
292     m_cCamShift.set_min_ch_val( 2, m_params.Vmin );
293     m_cCamShift.set_max_ch_val( 2, m_params.Vmax );
294     
295     cvGetImageRawData( image, 0, 0, &size );
296
297     if( m_object.x < 0 ) m_object.x = 0;
298     if( m_object.x > size.width - m_object.width - 1 )
299         m_object.x = MAX(0, size.width - m_object.width - 1);
300
301     if( m_object.y < 0 ) m_object.y = 0;
302     if( m_object.y > size.height - m_object.height - 1 )
303         m_object.y = MAX(0, size.height - m_object.height - 1);
304
305     if( m_object.width > size.width - m_object.x )
306         m_object.width = MIN(size.width, size.width - m_object.x);
307
308     if( m_object.height > size.height - m_object.y )
309         m_object.height = MIN(size.height, size.height - m_object.y);
310
311     m_cCamShift.set_window(m_object);
312     
313     if( initialize )
314     {
315         m_cCamShift.reset_histogram();
316         m_cCamShift.update_histogram( image );
317     }
318
319     m_cCamShift.track_object( image );
320     m_object = m_cCamShift.get_window();
321 }
322
323
324 void  CCamShiftF::CheckBackProject( IplImage* image )
325 {
326     if( m_params.view == 1 )
327     {
328         IplImage* src = m_cCamShift.get_back_project();
329         if( src && src->imageData && image )
330         {
331             cvCvtColor( src, image, CV_GRAY2BGR );
332         }
333     }
334     else if( m_params.view == 2 && IsTracking )
335     {
336         int i, dims;
337         CvSize size;
338
339         m_cCamShift.get_hist_dims( &dims );
340         cvGetImageRawData( image, 0, 0, &size );
341
342         for( i = 0; i < dims; i++ )
343         {
344             int val = cvRound(m_cCamShift.query(&i));
345             CvPoint p[4];
346
347             p[0].x = p[1].x = i*size.width/(2*dims);
348             p[2].x = p[3].x = (i+1)*size.width/(2*dims);
349
350             p[1].y = p[2].y = 0;
351             p[0].y = p[3].y = (val*size.height)/(3*255);
352
353             cvFillConvexPoly( image, p, 4, CV_RGB(255,0,0));
354         }
355     }
356 }
357
358
359 void  CCamShiftF::DrawCross( IplImage* image )
360 {
361     float cs = (float)cos( m_cCamShift.get_orientation() );
362     float sn = (float)sin( m_cCamShift.get_orientation() );
363     
364     int x = m_object.x + m_object.width / 2;
365     int y = m_object.y + m_object.height / 2;
366     
367     CvPoint p1 = {(int)(x + m_cCamShift.get_length() * cs / 2),
368         (int)(y + m_cCamShift.get_length() * sn / 2)};
369     CvPoint p2 = {(int)(x - m_cCamShift.get_length() * cs / 2),
370         (int)(y - m_cCamShift.get_length() * sn / 2)};
371     CvPoint p3 = {(int)(x + m_cCamShift.get_width() * sn / 2),
372         (int)(y - m_cCamShift.get_width() * cs / 2)};
373     CvPoint p4 = {(int)(x - m_cCamShift.get_width() * sn / 2),
374         (int)(y + m_cCamShift.get_width() * cs / 2)};
375     cvLine( image, p1, p2, CV_RGB(255,255,255) );
376     cvLine( image, p4, p3, CV_RGB(255,255,255) );
377 }
378
379
380 //
381 // Transform
382 //
383 // 'In place' adjust the CamShift of this sample
384 //
385 HRESULT CCamShiftF::Transform(IMediaSample *pMediaSample)
386 {
387     BYTE*   pData;
388     IplImage image;
389     
390     pMediaSample->GetPointer(&pData);
391     
392     AM_MEDIA_TYPE* pType = &m_pInput->CurrentMediaType();
393     VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *) pType->pbFormat;
394     
395     // Get the image properties from the BITMAPINFOHEADER
396     CvSize size = cvSize( pvi->bmiHeader.biWidth, pvi->bmiHeader.biHeight );
397     int stride = (size.width * 3 + 3) & -4;
398
399     cvInitImageHeader( &image, size, IPL_DEPTH_8U, 3, IPL_ORIGIN_TL, 4 );
400     cvSetImageData( &image, pData, stride );
401
402     if(IsTracking == false)
403     {
404         if(IsInit == false)
405         {
406             CvPoint p1, p2;
407             // Draw box
408             p1.x = cvRound( size.width * m_params.x );
409             p1.y = cvRound( size.height * m_params.y );
410
411             p2.x = cvRound( size.width * (m_params.x + m_params.width));
412             p2.y = cvRound( size.height * (m_params.y + m_params.height));
413
414             CheckBackProject( &image );
415
416             cvRectangle( &image, p1, p2, -1, 1 );
417         }
418         else 
419         {
420             m_object.x = cvRound( size.width * m_params.x );
421             m_object.y = cvRound( size.height * m_params.y );
422             m_object.width = cvRound( size.width * m_params.width );
423             m_object.height = cvRound( size.height * m_params.height );
424
425             assert( image.roi == 0 );
426             cvSetImageROI( &image, cvRect( m_object.x, m_object.y, m_object.width, m_object.height )); 
427             double norm = cvNorm( &image, 0, CV_L1 );
428             cvResetImageROI( &image );
429
430             ApplyCamShift( &image, true );
431             CheckBackProject( &image );
432             
433             IsTracking = true;
434         }
435     }
436     else
437     {
438         ApplyCamShift( &image, false );
439         CheckBackProject( &image );
440
441         DrawCross( &image );
442     }
443
444     return NOERROR;
445 } // Transform
446
447
448 //
449 // CheckInputType
450 //
451 // Check the input type is OK, return an error otherwise
452 //
453 HRESULT CCamShiftF::CheckInputType(const CMediaType *mtIn)
454 {
455     // Check this is a VIDEOINFO type
456
457     if (*mtIn->FormatType() != FORMAT_VideoInfo) {
458         return E_INVALIDARG;
459     }
460
461     // Is this a palettised format
462
463     if (CanChangeCamShiftLevel(mtIn)) {
464         return NOERROR;
465     }
466     return E_FAIL;
467
468 } // CheckInputType
469
470
471 //
472 // CheckTransform
473 //
474 // To be able to transform the formats must be identical
475 //
476 HRESULT CCamShiftF::CheckTransform(const CMediaType *mtIn,const CMediaType *mtOut)
477 {
478     HRESULT hr;
479     if (FAILED(hr = CheckInputType(mtIn))) {
480     return hr;
481     }
482
483     // format must be a VIDEOINFOHEADER
484     if (*mtOut->FormatType() != FORMAT_VideoInfo) {
485     return E_INVALIDARG;
486     }
487
488     // formats must be big enough
489     if (mtIn->FormatLength() < sizeof(VIDEOINFOHEADER) ||
490     mtOut->FormatLength() < sizeof(VIDEOINFOHEADER))
491     return E_INVALIDARG;
492
493     VIDEOINFO *pInput = (VIDEOINFO *) mtIn->Format();
494     VIDEOINFO *pOutput = (VIDEOINFO *) mtOut->Format();
495     if (memcmp(&pInput->bmiHeader,&pOutput->bmiHeader,sizeof(BITMAPINFOHEADER)) == 0) {
496     return NOERROR;
497     }
498
499     return E_INVALIDARG;
500 } // CheckTransform
501
502
503 //
504 // DecideBufferSize
505 //
506 // Tell the output pin's allocator what size buffers we
507 // require. Can only do this when the input is connected
508 //
509 HRESULT CCamShiftF::DecideBufferSize(IMemAllocator *pAlloc,ALLOCATOR_PROPERTIES *pProperties)
510 {
511     // Is the input pin connected
512
513     if (m_pInput->IsConnected() == FALSE) {
514         return E_UNEXPECTED;
515     }
516
517     ASSERT(pAlloc);
518     ASSERT(pProperties);
519     HRESULT hr = NOERROR;
520
521     pProperties->cBuffers = 1;
522     pProperties->cbBuffer = m_pInput->CurrentMediaType().GetSampleSize();
523
524     ASSERT(pProperties->cbBuffer);
525
526     // If we don't have fixed sized samples we must guess some size
527
528     if (!m_pInput->CurrentMediaType().bFixedSizeSamples) {
529         if (pProperties->cbBuffer < 100000) {
530             // nothing more than a guess!!
531             pProperties->cbBuffer = 100000;
532         }
533     }
534
535     // Ask the allocator to reserve us some sample memory, NOTE the function
536     // can succeed (that is return NOERROR) but still not have allocated the
537     // memory that we requested, so we must check we got whatever we wanted
538
539     ALLOCATOR_PROPERTIES Actual;
540     hr = pAlloc->SetProperties(pProperties,&Actual);
541     if (FAILED(hr)) {
542         return hr;
543     }
544
545     ASSERT( Actual.cBuffers == 1 );
546
547     if (pProperties->cBuffers > Actual.cBuffers ||
548             pProperties->cbBuffer > Actual.cbBuffer) {
549                 return E_FAIL;
550     }
551     return NOERROR;
552
553 } // DecideBufferSize
554
555
556 //
557 // GetMediaType
558 //
559 // I support one type, namely the type of the input pin
560 // We must be connected to support the single output type
561 //
562 HRESULT CCamShiftF::GetMediaType(int iPosition, CMediaType *pMediaType)
563 {
564     // Is the input pin connected
565
566     if (m_pInput->IsConnected() == FALSE) {
567         return E_UNEXPECTED;
568     }
569
570     // This should never happen
571
572     if (iPosition < 0) {
573         return E_INVALIDARG;
574     }
575
576     // Do we have more items to offer
577
578     if (iPosition > 0) {
579         return VFW_S_NO_MORE_ITEMS;
580     }
581
582
583     *pMediaType = m_pInput->CurrentMediaType();
584     return NOERROR;
585
586 } // GetMediaType
587
588
589
590 //
591 // GetPages
592 //
593 // This is the sole member of ISpecifyPropertyPages
594 // Returns the clsid's of the property pages we support
595 //
596 STDMETHODIMP CCamShiftF::GetPages(CAUUID *pPages)
597 {
598     pPages->cElems = 1;
599     pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID));
600     if (pPages->pElems == NULL) {
601         return E_OUTOFMEMORY;
602     }
603     *(pPages->pElems) = CLSID_CamShiftPropertyPage;
604     return NOERROR;
605
606 } // GetPages
607
608
609 STDMETHODIMP CCamShiftF::GetParams( CvCamShiftParams* params )
610 {
611     if( params )
612     {
613         *params = m_params;
614     }
615     return NOERROR;
616 }
617
618
619 STDMETHODIMP CCamShiftF::SetParams( CvCamShiftParams* params )
620 {
621     CAutoLock cAutoLock(&m_CamShiftLock);
622     if( params )
623     {
624         m_params = *params;
625     }
626     return NOERROR;
627 }
628
629
630
631 STDMETHODIMP CCamShiftF::StartTracking()
632 {
633     CAutoLock cAutoLock(&m_CamShiftLock);
634     IsInit = true;
635     IsTracking = false;
636     return NOERROR;
637 }
638
639 STDMETHODIMP CCamShiftF::StopTracking()
640 {
641     CAutoLock cAutoLock(&m_CamShiftLock);
642     IsInit = IsTracking = false;
643     return NOERROR;
644 }
645
646 //
647 // CanChangeCamShiftLevel
648 //
649 // Check if this is a paletised format
650 //
651 BOOL CCamShiftF::CanChangeCamShiftLevel(const CMediaType *pMediaType) const
652 {
653     if ((IsEqualGUID(*pMediaType->Type(), MEDIATYPE_Video))
654         && (IsEqualGUID(*pMediaType->Subtype(), MEDIASUBTYPE_RGB24))) {
655
656         // I think I can process this format (8 bit palettised)
657         // So do a quick sanity check on the palette information
658
659         VIDEOINFO *pvi = (VIDEOINFO *) pMediaType->Format();
660         return (pvi->bmiHeader.biBitCount == 24);
661
662     } else {
663         return FALSE;
664     }
665 } // CanChangeCamShiftLevel
666
667
668
669 //
670 // DllRegisterServer
671 //
672 // Handle registration of this filter
673 //
674 STDAPI DllRegisterServer()
675 {
676     return AMovieDllRegisterServer2( TRUE );
677
678 } // DllRegisterServer
679
680
681 //
682 // DllUnregisterServer
683 //
684 STDAPI DllUnregisterServer()
685 {
686     return AMovieDllRegisterServer2( FALSE );
687
688 } // DllUnregisterServer
689
690 /* End of file. */