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.
42 #pragma warning(disable:4786)
46 #include "autorelease.h"
48 #include "Tracker3dFilter.h"
49 #include "Tracker3dPropertyPage.h"
52 #define ARRAY_SIZEOF(a) (sizeof(a)/sizeof((a)[0]))
54 // locks a critical section, and unlocks it automatically
55 // when the lock goes out of scope. Also allows explicit unlocking.
60 AutoLock(CCritSec *lock)
87 void Trace(const char *fmt, ...)
92 vsprintf(buf, fmt, args);
94 OutputDebugString(buf) ;
101 // Provide the way for COM to create a Tracker3dFilter object
103 CUnknown *Tracker3dFilter::CreateInstance(IUnknown *unk, HRESULT *phr)
108 return new Tracker3dFilter(NAME("3d Tracker"), unk);
117 *phr = E_OUTOFMEMORY;
126 Tracker3dFilter::Tracker3dFilter(TCHAR *name, IUnknown *unk)
127 : CBaseFilter(name, unk, &m_cs, CLSID_Tracker3dFilter)
131 m_camera_configuration_loaded = false;
132 m_pCameraInfo = NULL;
133 m_tracker_clsid = GUID_NULL;
134 m_calibrate_cameras = 0;
135 m_camera_intrinsics = NULL;
136 m_viewing_stream = 0;
137 m_preferred_size = SIZE_Any;
138 m_end_of_stream_count = 0;
141 Tracker3dFilter::~Tracker3dFilter()
143 for (int i = 0; i < m_callbacks.size(); i++)
144 m_callbacks[i]->Release();
147 delete [] m_pCameraInfo;
148 delete [] m_camera_intrinsics;
153 // NonDelegatingQueryInterface
155 STDMETHODIMP Tracker3dFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv)
157 CheckPointer(ppv,E_POINTER);
159 if (riid == IID_ITracker3dFilter)
160 return GetInterface(static_cast<ITracker3dFilter *>(this), ppv);
161 else if (riid == IID_ITracker3dInternal)
162 return GetInterface(static_cast<ITracker3dInternal *>(this), ppv);
163 else if (riid == IID_ISpecifyPropertyPages)
164 return GetInterface(static_cast<ISpecifyPropertyPages *>(this), ppv);
165 else if (riid == IID_IPersist)
166 return GetInterface(static_cast<IPersist *>(static_cast<IPersistStream *>(this)), ppv);
167 else if (riid == IID_IPersistStream)
168 return GetInterface(static_cast<IPersistStream *>(this), ppv);
170 return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
173 int Tracker3dFilter::GetPinCount()
175 return 2 * m_num_streams;
178 CBasePin *Tracker3dFilter::GetPin(int n)
180 if (n > GetPinCount())
183 return m_streams[n/2].output_pin;
185 return m_streams[n/2].input_pin;
188 static HRESULT InitImageHeader(IplImage *image_header, const CMediaType *mt)
190 const GUID *subtype = mt->Subtype();
191 ASSERT(*mt->FormatType() == FORMAT_VideoInfo);
192 VIDEOINFO *fmt = (VIDEOINFO *)mt->Format();
197 // We assume that RGB images are inverted and YUV images are not...
198 if (*subtype == MEDIASUBTYPE_RGB24 || *subtype == MEDIASUBTYPE_RGB32)
200 channels = fmt->bmiHeader.biBitCount / 8;
201 origin = IPL_ORIGIN_BL;
203 else if (*subtype == MEDIASUBTYPE_YVU9
204 || *subtype == MEDIASUBTYPE_YV12
205 || *subtype == MEDIASUBTYPE_IYUV)
207 // Note: these are planar images, which for our purposes we treat as
208 // a single plane of Y8 and ignore the rest.
210 origin = IPL_ORIGIN_TL;
212 else // Other trackers could support other formats, which should be handled here
217 cvInitImageHeader(image_header, cvSize(fmt->bmiHeader.biWidth, fmt->bmiHeader.biHeight),
218 IPL_DEPTH_8U, channels, origin, 4);
222 HRESULT Tracker3dFilter::CheckMediaType(int pin, const CMediaType *mt)
224 Stream &stream = m_streams[pin];
226 if (stream.media_type.IsValid())
227 return *mt == stream.media_type ? S_OK : E_FAIL;
229 if (*mt->Type() != MEDIATYPE_Video || *mt->FormatType() != FORMAT_VideoInfo)
232 IplImage image_header;
233 HRESULT hr = InitImageHeader(&image_header, mt);
237 if (m_preferred_size == SIZE_320x240
238 && (image_header.width != 320 || image_header.height != 240))
243 if (m_preferred_size == SIZE_640x480
244 && (image_header.width != 640 || image_header.height != 480))
249 if (stream.tracker != NULL)
251 hr = stream.tracker->CheckFormat(&image_header);
260 HRESULT Tracker3dFilter::SetMediaType(int pin, const CMediaType *mt)
262 Stream &stream = m_streams[pin];
264 if (stream.media_type.IsValid())
265 return *mt == stream.media_type ? S_OK : E_FAIL;
267 HRESULT hr = CheckMediaType(pin, mt);
271 hr = InitImageHeader(&stream.image_header1, mt);
274 stream.image_header2 = stream.image_header1;
276 if (stream.tracker != NULL)
277 stream.tracker->SetFormat(&stream.image_header1);
279 stream.media_type = *mt;
283 HRESULT Tracker3dFilter::GetMediaType(int pin, int pos, CMediaType *mt)
288 #define IYUV 0x56555949
293 int width, height, depth;
295 { &MEDIASUBTYPE_IYUV, IYUV, 640, 480, 12 },
296 { &MEDIASUBTYPE_RGB24, BI_RGB, 640, 480, 24 },
297 { &MEDIASUBTYPE_RGB24, BI_RGB, 640, 480, 32 },
298 { &MEDIASUBTYPE_RGB32, BI_RGB, 640, 480, 32 },
299 { &MEDIASUBTYPE_IYUV, IYUV, 320, 240, 12 },
300 { &MEDIASUBTYPE_RGB24, BI_RGB, 320, 240, 24 },
301 { &MEDIASUBTYPE_RGB24, BI_RGB, 320, 240, 32 },
302 { &MEDIASUBTYPE_RGB32, BI_RGB, 320, 240, 32 },
305 // If our media type has been set, we respond only with that type;
306 // otherwise, we respond with each of the types we know about.
307 if (m_streams[pin].media_type.IsValid())
311 *mt = m_streams[pin].media_type;
315 else if (pos < ARRAY_SIZEOF(types))
317 const GUID *subtype = types[pos].subtype;
318 FOURCC fourcc = types[pos].fourcc;
319 int width = types[pos].width;
320 int height = types[pos].height;
321 int depth = types[pos].depth;
323 VIDEOINFOHEADER fmt = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, 30*width*height*depth, 0, 333333,
324 { sizeof(BITMAPINFOHEADER), width, height, 1, depth, fourcc, width*height*depth/8, 0, 0, 0, 0 } };
325 mt->SetType(&MEDIATYPE_Video);
326 mt->SetTemporalCompression(false);
327 mt->SetSubtype(subtype);
328 mt->SetSampleSize(width*height*depth/8);
329 mt->SetFormatType(&FORMAT_VideoInfo);
330 mt->SetFormat((BYTE *)&fmt, sizeof(fmt));
334 return VFW_S_NO_MORE_ITEMS;
337 STDMETHODIMP Tracker3dFilter::Run(REFERENCE_TIME t)
339 if (!m_camera_configuration_loaded && m_calibrate_cameras == 0)
340 LoadCameraConfiguration();
341 return CBaseFilter::Run(t);
344 STDMETHODIMP Tracker3dFilter::Pause()
346 return CBaseFilter::Pause();
349 STDMETHODIMP Tracker3dFilter::Stop()
351 AutoLock lock1(&m_cs);
352 AutoLock lock2(&m_recv_cs);
354 HRESULT hr = CBaseFilter::Stop();
358 for (int i = 0; i < m_num_streams; i++)
360 m_end_of_stream_count = 0;
365 HRESULT Tracker3dFilter::Flush(int pin)
367 Stream::SampleQueue &q = m_streams[pin].queue;
373 Tracker3dFilter::Stream::Stream()
382 Tracker3dFilter::Stream::~Stream()
384 SAFE_RELEASE(input_pin);
385 SAFE_RELEASE(output_pin);
386 SAFE_RELEASE(tracker);
389 void Tracker3dFilter::Stream::operator =(Stream &s)
391 SAFE_RELEASE(input_pin);
392 SAFE_RELEASE(output_pin);
393 SAFE_RELEASE(tracker);
395 input_pin = s.input_pin; s.input_pin = NULL;
396 output_pin = s.output_pin; s.output_pin = NULL;
397 tracker = s.tracker; s.tracker = NULL;
398 image_header1 = image_header2 = s.image_header1;
399 media_type = s.media_type;
402 inline Tracker3dFilter::Stream::Sample::Sample(IMediaSample *s, const ITracker::TrackingInfo &t)
407 sample->GetTime(&ts, &te);
408 sample->GetPointer(&data);
411 inline Tracker3dFilter::Stream::Sample::Sample(const Sample &s)
416 tracking_info(s.tracking_info)
421 inline void Tracker3dFilter::Stream::Sample::operator=(const Sample &s)
429 tracking_info = s.tracking_info;
433 inline Tracker3dFilter::Stream::Sample::~Sample()
438 //-----------------------
439 // This is an attempt to match the frames up across the streams which are connected to the tracker.
440 // The goal is to minimize the time differences between the frames in one set. This is necessary since
441 // there is no available syncronization mechanism for the independant USB cameras or other video sources.
442 //-----------------------
443 bool Tracker3dFilter::QueuesReady()
447 REFERENCE_TIME greatest = 0;
448 REFERENCE_TIME least = 0;
449 for (i = 0; i < m_num_streams; i++)
451 Stream::SampleQueue &q = m_streams[i].queue;
454 REFERENCE_TIME ts = q.front().ts;
457 if (ts < least || least == 0)
461 // If the difference between the greatest timestamp and the least is less than
462 // the specified difference (1/60 sec or 1/2 nominal frame time), all queues
464 #define MAX_DIFF (10000000/60)
465 if (greatest - least <= MAX_DIFF)
468 // Otherwise, check in each queue whether the 'greatest' frame is a closer time
469 // match to the second frame in the queue than to the first frame in the queue.
470 // If so, discard the first frame in the queue.
471 // This algorithm requires a second frame in each queue, so it introduces one
473 for (i = 0; i < m_num_streams; i++)
475 Stream::SampleQueue &q = m_streams[i].queue;
476 while (q.size() > 1 && abs(q.begin()->ts - greatest) > abs((q.begin() + 1)->ts - greatest))
478 m_streams[i].discarded_frames++;
480 Trace("Discard: %*s %5lu\n", 6*i, "", (unsigned long)q.front().ts/10000);
487 if (abs(greatest - q.front().ts) <= MAX_DIFF)
495 //-----------------------
496 // Performs the frame to frame work...
497 //-----------------------
498 HRESULT Tracker3dFilter::Receive(int pin, IMediaSample *sample)
502 Stream &stream = m_streams[pin];
503 ITracker::TrackingInfo tracking_info;
505 if (stream.tracker != NULL && m_calibrate_cameras == 0)
508 sample->GetPointer(&data);
509 stream.image_header1.imageData = (char *)data;
512 stream.tracker->Process(&stream.image_header1);
513 stream.tracker->GetTrackedObjects(tracking_info);
518 stream.image_header1.imageData = NULL;
521 AutoLock lock(&m_recv_cs);
523 stream.queue.push_back(Stream::Sample(sample, tracking_info));
526 Trace("Receive(%d):%*s %5lu\n", pin, 6*pin, "", (unsigned long)stream.queue.back().ts/10000);
533 char buf[100] = "Process: ";
534 char *p = buf + strlen(buf);
535 REFERENCE_TIME greatest = 0;
536 REFERENCE_TIME least = 0;
537 for (i = 0; i < m_num_streams; i++)
539 REFERENCE_TIME ts = m_streams[i].queue.front().ts;
542 if (ts < least || least == 0)
544 sprintf(p, " %5lu", (unsigned long)ts/10000);
547 sprintf(p, " %4ld", (long)((greatest - least)/10000));
549 static unsigned long processed_frames;
550 sprintf(p, "; %4lu", ++processed_frames);
552 for (i = 0; i < m_num_streams; i++)
554 Stream &s = m_streams[i];
555 sprintf(p, " %4lu (%2lu%%)", s.discarded_frames, (100*s.discarded_frames/(processed_frames+s.discarded_frames)));
562 if (m_calibrate_cameras != 0)
564 std::vector<IplImage *> samples(m_num_streams);
565 for (i = 0; i < m_num_streams; i++)
567 m_streams[i].image_header2.imageData = (char *)m_streams[i].queue.front().data;
568 samples[i] = &m_streams[i].image_header2;
571 // This is the second most significant call in the filter, this is how the relative locations of
572 // all of the cameras are determined. This information is vital to the later process of determining
573 // the location of the targets in 3D from the set of 2D results provided by the trackers.
575 if (cv3dTrackerCalibrateCameras(m_num_streams, m_camera_intrinsics, m_etalon_size,
576 m_square_size, &samples[0], m_pCameraInfo))
578 if (m_calibrate_cameras == 1)
580 m_calibrate_cameras = 0;
581 SaveCameraConfiguration();
589 for (i = 0; i < m_num_streams; i++)
591 int n = m_streams[i].queue.front().tracking_info.size();
596 if (num_objects != 0)
598 std::vector<Cv3dTracker2dTrackedObject> tracking_info(m_num_streams * num_objects,
599 cv3dTracker2dTrackedObject(-1, CvPoint()));
600 for (i = 0; i < m_num_streams; i++)
602 ITracker::TrackingInfo &t = m_streams[i].queue.front().tracking_info;
604 if (m_streams[i].image_header2.origin == IPL_ORIGIN_BL)
605 for (int j = 0; j < t.size(); j++)
606 t[j].p.y = m_streams[i].image_header2.height - 1 - t[j].p.y;
608 for (int j = 0; j < t.size(); j++)
609 tracking_info[i*num_objects+j] = t[j];
612 m_tracked_objects.resize(num_objects);
614 // This is it. The whole point of this is this call.
615 // For each matched set of results from the trackers this call will compute the 3D coordinates of the targets.
617 int n = cv3dTrackerLocateObjects(m_num_streams, num_objects, m_pCameraInfo, &tracking_info[0], &m_tracked_objects[0]);
618 m_tracked_objects.resize(n);
621 m_tracked_objects.clear();
624 // Copy viewing sample out of queue before releasing lock.
625 Stream::Sample viewing_sample = m_streams[m_viewing_stream].queue.front();
627 for (i = 0; i < m_num_streams; i++)
629 m_streams[i].output_pin->Deliver(m_streams[i].queue.front().sample);
630 m_streams[i].queue.pop_front();
635 for (i = 0; i < m_callbacks.size(); i++)
636 m_callbacks[i]->Callback(m_tracked_objects, viewing_sample.data, (IUnknown *)viewing_sample.sample);
641 HRESULT Tracker3dFilter::EndOfStream(int pin)
643 AutoLock lock(&m_recv_cs);
645 m_end_of_stream_count++;
647 if (m_end_of_stream_count == m_num_streams)
649 for (int i = 0; i < m_num_streams; i++)
652 m_streams[i].output_pin->DeliverEndOfStream();
659 void Tracker3dFilter::SetNumberOfStreams(int num_streams)
661 if (num_streams == m_num_streams)
664 delete [] m_camera_intrinsics;
665 m_camera_intrinsics = NULL;
667 Stream *streams = NULL;
668 Cv3dTrackerCameraInfo *camera_info = NULL;
674 camera_info = new Cv3dTrackerCameraInfo [ num_streams ];
675 streams = new Stream [ num_streams ];
676 for (i = 0; i < num_streams; i++)
678 if (i < m_num_streams)
680 streams[i] = m_streams[i];
681 camera_info[i] = m_pCameraInfo[i];
685 streams[i].input_pin = new Tracker3dInputPin(i, this, &m_cs);
686 streams[i].output_pin = new Tracker3dOutputPin(i, this, &m_cs);
687 if (m_tracker_clsid != GUID_NULL)
689 // If CoCreateInstance fails, m_streams[i].tracker is left as NULL.
690 CoCreateInstance(m_tracker_clsid, NULL, CLSCTX_INPROC_SERVER, IID_ITracker, (void **)&streams[i].tracker);
692 camera_info[i].valid = false;
700 delete [] m_pCameraInfo;
701 m_pCameraInfo = camera_info;
703 m_num_streams = num_streams;
704 IncrementPinVersion();
711 // Returns the clsid's of the property pages we support
713 STDMETHODIMP Tracker3dFilter::GetPages(CAUUID *pPages)
715 std::vector<GUID> pages;
716 pages.push_back(CLSID_Tracker3dPropertyPage);
718 // In addition to our property page, return a separate property page for each type of tracker.
719 // Normally all the trackers will be the same, so only one property page will be returned,
720 // but we call each tracker to see if it wants a different property page.
721 // It is up to each property page to call GetTrackers and connect to all the trackers
722 // that are of the appropriate type.
723 if (m_streams != NULL)
725 for (int i = 0; i < m_num_streams; i++)
728 if (m_streams[i].tracker != NULL
729 && SUCCEEDED(m_streams[i].tracker->GetPropertyPage(&page)))
731 if (std::find(pages.begin(), pages.end(), page) == pages.end())
732 pages.push_back(page);
737 // Now that we've determined how many pages we need, allocate the memory
739 pPages->cElems = pages.size();
740 pPages->pElems = (GUID *) CoTaskMemAlloc(pages.size() * sizeof(GUID));
741 if (pPages->pElems == NULL)
742 return E_OUTOFMEMORY;
744 for (int i = 0; i < pages.size(); i++)
745 pPages->pElems[i] = pages[i];
752 STDMETHODIMP Tracker3dFilter::GetClassID(CLSID *clsid)
754 *clsid = CLSID_Tracker3dFilter;
758 STDMETHODIMP Tracker3dFilter::IsDirty()
763 static const int PERSIST_STREAM_VERSION0 = 0;
764 static const int PERSIST_STREAM_VERSION0_SIZE = 3;
765 static const int PERSIST_STREAM_VERSION1 = 1;
766 static const int PERSIST_STREAM_VERSION1_SIZE = 4;
767 static const int PERSIST_STREAM_VERSION2 = 2;
768 static const int PERSIST_STREAM_VERSION2_SIZE = PERSIST_STREAM_VERSION1_SIZE;
769 static const int PERSIST_STREAM_VERSION3 = 3;
770 static const int PERSIST_STREAM_VERSION3_SIZE = 3 + sizeof(GUID);
772 STDMETHODIMP Tracker3dFilter::Load(IStream *stream)
775 unsigned char buf[3];
777 HRESULT hr = stream->Read(buf, 3, &nread);
786 if (buf[0] == PERSIST_STREAM_VERSION0 && buf[1] == PERSIST_STREAM_VERSION0_SIZE)
788 num_streams = buf[2];
790 else if (buf[0] == PERSIST_STREAM_VERSION1 && buf[1] == PERSIST_STREAM_VERSION1_SIZE)
792 num_streams = buf[2];
794 else if (buf[0] == PERSIST_STREAM_VERSION2 && buf[1] == PERSIST_STREAM_VERSION2_SIZE)
796 num_streams = buf[2] & 0x3f;
797 m_preferred_size = (InputSize)((buf[2] >> 6) & 0x03);
799 else if (buf[0] == PERSIST_STREAM_VERSION3)
801 num_streams = buf[2] & 0x3f;
802 m_preferred_size = (InputSize)((buf[2] >> 6) & 0x03);
803 if (buf[1] == PERSIST_STREAM_VERSION3_SIZE)
806 hr = stream->Read(&guid, sizeof(GUID), &nread);
809 if (nread != sizeof(GUID))
811 m_tracker_clsid = guid;
819 if (buf[0] == PERSIST_STREAM_VERSION1 || buf[0] == PERSIST_STREAM_VERSION2)
821 unsigned char lib_name_len;
823 hr = stream->Read(&lib_name_len, 1, &nread);
829 if (lib_name_len > 0)
832 hr = stream->Read(lib_name, lib_name_len, &nread);
833 // We don't support this format any more; ignore the
834 // default tracker dll name, but allow the graph
836 //if (SUCCEEDED(hr) && nread == lib_name_len)
838 // lib_name[lib_name_len] = '\0';
839 // SetDefaultTrackerDLL(lib_name);
844 SetTrackers(num_streams, NULL);
849 STDMETHODIMP Tracker3dFilter::Save(IStream *stream, BOOL clear_dirty)
853 unsigned char buf[PERSIST_STREAM_VERSION3_SIZE] = { PERSIST_STREAM_VERSION3,
854 PERSIST_STREAM_VERSION3_SIZE,
855 (unsigned char)((m_num_streams & 0x7f) | (m_preferred_size << 6))
858 if (m_tracker_clsid != GUID_NULL)
859 memcpy(buf+3, &m_tracker_clsid, sizeof(GUID));
863 hr = stream->Write(&buf, buf[1], &dummy);
870 STDMETHODIMP Tracker3dFilter::GetSizeMax(ULARGE_INTEGER *size)
872 size->QuadPart = PERSIST_STREAM_VERSION3_SIZE;
879 STDMETHODIMP Tracker3dFilter::GetNumberOfCameras(int &num_streams)
881 num_streams = m_num_streams;
890 AutoClose(FILE ** ppfile) : m_ppfile(ppfile) { };
893 if (*m_ppfile != NULL)
901 #define AUTO_CLOSE(f) AutoClose AutoClose##f(&f)
903 STDMETHODIMP Tracker3dFilter::LoadCameraConfiguration(const char *filename)
905 FILE *f = fopen(filename, "r"); AUTO_CLOSE(f);
911 r = fscanf(f, "%ld\n", &num_streams);
912 if (r != 1 || num_streams <= 0)
915 SetNumberOfStreams(num_streams);
917 for (int c = 0; c < m_num_streams; c++)
919 Cv3dTrackerCameraInfo &camera = m_pCameraInfo[c];
921 char camera_name[80];
923 if (fscanf(f, "%[^:]:", camera_name) != 1)
925 if (fscanf(f, "%g, %g;", &camera.principal_point.x, &camera.principal_point.y) != 2)
927 for (int i = 0; i < 4; i++)
928 for (int j = 0; j < 4; j++)
929 if (fscanf(f, "%g", &camera.mat[i][j]) != 1)
931 //camera.camera_name = camera_name;
935 m_camera_configuration_loaded = true;
940 STDMETHODIMP Tracker3dFilter::SaveCameraConfiguration(const char *filename)
942 if (m_pCameraInfo == NULL)
945 FILE *f = fopen(filename, "w");
949 fprintf(f, "%lu\n", m_num_streams);
951 for (int c = 0; c < m_num_streams; c++)
953 Cv3dTrackerCameraInfo &camera = m_pCameraInfo[c];
955 fprintf(f, "Camera %d: ", c);
956 //fprintf(f, "%s: ", camera.camera_name.c_str());
957 fprintf(f, " %g, %g;", camera.principal_point.x, camera.principal_point.y);
958 for (int i = 0; i < 4; i++)
959 for (int j = 0; j < 4; j++)
960 fprintf(f, " %g", camera.mat[i][j]);
969 static std::string subst(std::string s, char i, char o)
971 std::string::size_type pos = 0;
972 while ((pos = s.find(i, pos)) != std::string::npos)
973 s.replace(pos, 1, 1, o);
977 std::string Tracker3dFilter::CalibrationName(int i)
979 std::string camera_name;
980 GetCameraName(i, camera_name);
981 return subst(camera_name, '\\', '#');
989 AutoCloseKey(HKEY *pkey) : m_pkey(pkey) { };
994 RegCloseKey(*m_pkey);
1000 #define AUTO_CLOSE_KEY(f) AutoCloseKey AutoCloseKey##f(&f)
1002 static const char key_name[] = "Software\\Intel\\VAI\\3d Tracker\\";
1004 //------------------
1005 // Once camera extrinsic parameters are determined they are stored in the registry in the key
1006 // Software\\Intel\\VAI\\3d Tracker\\
1007 // Read them back from here when the app starts.
1008 //------------------
1009 void Tracker3dFilter::LoadCameraConfiguration()
1013 HKEY key = 0; AUTO_CLOSE_KEY(key);
1014 r = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key_name, 0,KEY_READ, &key);
1015 if (r != ERROR_SUCCESS)
1018 int valid_count = 0;
1019 for (int c = 0; c < m_num_streams; c++)
1021 Cv3dTrackerCameraInfo &camera = m_pCameraInfo[c];
1023 DWORD size = sizeof(buf);
1024 r = RegQueryValueEx(key, CalibrationName(c).c_str(), 0, NULL, (BYTE *)buf, &size);
1025 if (r != ERROR_SUCCESS)
1028 if (sscanf(buf, "%g %g; %g %g %g %g, %g %g %g %g, %g %g %g %g, %g %g %g %g",
1029 &camera.principal_point.x, &camera.principal_point.y,
1030 &camera.mat[0][0], &camera.mat[0][1], &camera.mat[0][2], &camera.mat[0][3],
1031 &camera.mat[1][0], &camera.mat[1][1], &camera.mat[1][2], &camera.mat[1][3],
1032 &camera.mat[2][0], &camera.mat[2][1], &camera.mat[2][2], &camera.mat[2][3],
1033 &camera.mat[3][0], &camera.mat[3][1], &camera.mat[3][2], &camera.mat[3][3]) != 18)
1036 camera.valid = true;
1040 m_camera_configuration_loaded = (valid_count == m_num_streams);
1043 void Tracker3dFilter::SaveCameraConfiguration()
1047 HKEY key = 0; AUTO_CLOSE_KEY(key);
1048 r = RegCreateKeyEx(HKEY_LOCAL_MACHINE, key_name, 0, NULL, 0, KEY_WRITE, NULL, &key, NULL);
1049 if (r != ERROR_SUCCESS)
1052 for (int c = 0; c < m_num_streams; c++)
1054 Cv3dTrackerCameraInfo &camera = m_pCameraInfo[c];
1057 int len = sprintf(p, "%g %g;", camera.principal_point.x, camera.principal_point.y);
1059 for (int i = 0; i < 4; i++)
1061 for (int j = 0; j < 4; j++)
1063 len = sprintf(p, " %g", camera.mat[i][j]);
1068 RegSetValueEx(key, CalibrationName(c).c_str(), 0, REG_SZ, (BYTE *)buf, p - buf + 1);
1073 STDMETHODIMP Tracker3dFilter::GetDefaultTracker(GUID &tracker_clsid)
1075 tracker_clsid = m_tracker_clsid;
1079 STDMETHODIMP Tracker3dFilter::SetDefaultTracker(const GUID &tracker_clsid)
1081 m_tracker_clsid = tracker_clsid;
1085 STDMETHODIMP Tracker3dFilter::SetTrackers(int num_streams, ITracker * const *trackers)
1087 SetNumberOfStreams(num_streams);
1089 if (trackers == NULL)
1091 for (int i = 0; i < m_num_streams; i++)
1093 SAFE_RELEASE(m_streams[i].tracker);
1095 if (m_tracker_clsid != GUID_NULL)
1097 // If CoCreateInstance fails, m_streams[i].tracker is left as NULL.
1098 CoCreateInstance(m_tracker_clsid, NULL, CLSCTX_INPROC_SERVER, IID_ITracker, (void **)&m_streams[i].tracker);
1099 if (m_streams[i].media_type.IsValid())
1100 m_streams[i].tracker->SetFormat(&m_streams[i].image_header1);
1106 for (int i = 0; i < m_num_streams; i++)
1108 SAFE_RELEASE(m_streams[i].tracker);
1110 // Store and addref new tracker
1111 m_streams[i].tracker = trackers[i];
1112 if (m_streams[i].tracker != NULL)
1114 m_streams[i].tracker->AddRef();
1115 if (m_streams[i].media_type.IsValid())
1116 m_streams[i].tracker->SetFormat(&m_streams[i].image_header1);
1125 STDMETHODIMP Tracker3dFilter::GetTrackers(std::vector<ITracker *> &trackers)
1127 trackers.resize(m_num_streams);
1128 for (int i = 0; i < m_num_streams; i++)
1130 trackers[i] = m_streams[i].tracker;
1131 if (trackers[i] != NULL)
1132 trackers[i]->AddRef();
1138 //----------------------
1139 // Read the intrinsics information for all of the from a list of files.
1140 // The following is an example of the format that the file is reading.
1141 // The top left 2 diagonal elements of the matrix are the horizontal and
1142 // vertical focal lengths and the top 2 elements in the last column
1143 // indicate the image center coordinates, x in the top row, y in the second.
1145 // The Distortion parameters indicate the radial and barrel distortion of the lens.
1147 // All of these results are further discussed in the documentation concerning
1149 //----------------------
1153 M[0.0]= 401.3238525M[0.1]= 0.0000000M[0.2]= 135.9540710
1154 M[1.0]= 0.0000000M[1.1]= 403.9630737M[1.2]= 116.6698456
1155 M[2.0]= 0.0000000M[2.1]= 0.0000000M[2.2]= 1.0000000
1165 HRESULT Tracker3dFilter::ReadCameraIntrinsics(const char *filenames[])
1167 // read camera intrinsics for all cameras: focal length[2], principal_point.x&y, distortion[4]
1168 if (m_camera_intrinsics == NULL)
1169 m_camera_intrinsics = new Cv3dTrackerCameraIntrinsics [ m_num_streams ];
1171 for (int c = 0; c < m_num_streams; c++)
1173 Cv3dTrackerCameraIntrinsics &camera = m_camera_intrinsics[c];
1175 FILE *file = fopen(filenames[c], "r"); AUTO_CLOSE(file);
1179 #define BUF_SIZE 500
1180 char buffer[BUF_SIZE+1];
1181 int sz = fread( buffer, 1, BUF_SIZE, file );
1185 float camera_matrix[3][3];
1188 for( k = 0; k < 9; k++ )
1190 ptr = strstr( ptr, "M[" );
1195 if( sscanf( ptr, "%d%*[.,]%d%n", &i, &j, &s ) == 2 && i == k/3 && j == k%3 )
1198 ptr = strstr( ptr, "=" );
1203 if( sscanf( ptr, "%f%n", &camera_matrix[i][j], &s ) == 1 )
1215 camera.focal_length[0] = camera_matrix[0][0];
1216 camera.focal_length[1] = camera_matrix[1][1];
1218 camera.principal_point.x = camera_matrix[0][2];
1219 camera.principal_point.y = camera_matrix[1][2];
1221 /* read distortion */
1222 for( k = 0; k < 4; k++ )
1224 ptr = strstr( ptr, "D[" );
1229 if( sscanf( ptr, "%d%n", &i, &s ) == 1 && i == k )
1232 ptr = strstr( ptr, "=" );
1237 if( sscanf( ptr, "%f%n", &camera.distortion[k], &s ) == 1 )
1254 //---------------------
1255 // Initiate the camera calibration process. Actual calibration call is made in Receive()
1256 //---------------------
1257 STDMETHODIMP Tracker3dFilter::CalibrateCameras(int checkerboard_width, int checkerboard_height,
1258 const char *camera_intrinsics_filenames[],
1262 AutoLock lock(&m_recv_cs);
1263 HRESULT hr = ReadCameraIntrinsics(camera_intrinsics_filenames);
1266 m_tracked_objects.clear();
1267 m_etalon_size = cvSize(checkerboard_width-1, checkerboard_height-1);
1268 m_calibrate_cameras = continuous ? 2 : 1;
1269 m_square_size = square_size;
1270 for (int c = 0; c < m_num_streams; c++)
1271 m_pCameraInfo[c].valid = false;
1276 STDMETHODIMP Tracker3dFilter::GetTrackedObjects(std::vector<Cv3dTrackerTrackedObject> &tracked_objects)
1278 AutoLock lock(&m_recv_cs);
1279 tracked_objects = m_tracked_objects;
1283 STDMETHODIMP Tracker3dFilter::AddCallback(ITracker3dCallback *new_callback)
1285 if (new_callback == NULL)
1286 return E_INVALIDARG;
1288 new_callback->AddRef();
1289 m_callbacks.push_back(new_callback);
1295 STDMETHODIMP Tracker3dFilter::RemoveCallback(ITracker3dCallback *callback)
1297 if (callback == NULL)
1298 return E_INVALIDARG;
1300 std::vector<ITracker3dCallback *>::iterator i = std::find(m_callbacks.begin(), m_callbacks.end(), callback);
1301 if (i != m_callbacks.end())
1303 m_callbacks.erase(i);
1304 callback->Release();
1312 STDMETHODIMP Tracker3dFilter::SetViewingStream(int stream)
1314 if (stream < 0 || stream >= m_num_streams)
1315 return E_INVALIDARG;
1317 m_viewing_stream = stream;
1321 STDMETHODIMP Tracker3dFilter::GetViewingStream(int &stream)
1323 stream = m_viewing_stream;
1327 //--------------------
1328 // Begin at input pin 'i' and work upstream in the graph to find the source filter. Return
1329 // the name of the source in the string name.
1330 //--------------------
1331 STDMETHODIMP Tracker3dFilter::GetCameraName(int i, std::string &name)
1333 // Set up a default, in case we can't generate something better
1334 // (This could be improved.)
1335 name = (char)('0'+i);
1337 // Work upstream from input pin 'i' to find the capture filter
1339 IPin *input_pin = m_streams[i].input_pin; input_pin->AddRef(); AUTO_RELEASE(input_pin);
1340 IBaseFilter *capture_filter = NULL; AUTO_RELEASE(capture_filter);
1344 IPin *output_pin = NULL; AUTO_RELEASE(output_pin);
1345 hr = input_pin->ConnectedTo(&output_pin);
1350 hr = output_pin->QueryPinInfo(&pin_info);
1353 IBaseFilter *filter = pin_info.pFilter; AUTO_RELEASE(filter);
1355 FILTER_INFO filter_info;
1356 hr = filter->QueryFilterInfo(&filter_info);
1357 filter_info.pGraph->Release(); filter_info.pGraph = NULL;
1359 IEnumPins *e = NULL; AUTO_RELEASE(e);
1360 hr = filter->EnumPins(&e);
1364 input_pin->Release(); input_pin = NULL;
1365 while (e->Next(1, &input_pin, NULL) == S_OK)
1367 hr = input_pin->QueryPinInfo(&pin_info);
1370 pin_info.pFilter->Release();
1371 if (pin_info.dir == PINDIR_INPUT)
1373 input_pin->Release(); input_pin = NULL;
1376 if (input_pin == NULL) // No input pin found; this must be the capture filter
1378 capture_filter = filter;
1379 capture_filter->AddRef();
1384 // Use IPersistStream to save the filter data, which includes a text representation of the device id.
1385 IPersistStream *persist_stream = NULL; AUTO_RELEASE(persist_stream);
1386 hr = capture_filter->QueryInterface(IID_IPersistStream, (void **)&persist_stream);
1390 HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, 0);
1392 return E_OUTOFMEMORY;
1394 IStream *stream = NULL; AUTO_RELEASE(stream);
1395 CreateStreamOnHGlobal(mem, true, &stream);
1396 hr = persist_stream->Save(stream, false);
1401 hr = stream->Stat(&stat, STATFLAG_NONAME);
1405 int size = stat.cbSize.LowPart;
1407 wchar_t *p = (wchar_t *)GlobalLock(mem);
1408 wchar_t *end = p + size;
1412 if (wcsncmp(p, L"@device:", 8) == 0)
1420 if (id == NULL)// Didn't find something we recognize
1424 while (p < end && *p != L'\0')
1430 for (i = 0; i < len; i++)
1431 name[i] = (char)id[i];
1436 STDMETHODIMP Tracker3dFilter::SetPreferredInputSize(InputSize size)
1438 m_preferred_size = size;
1442 STDMETHODIMP Tracker3dFilter::GetPreferredInputSize(InputSize &size)
1444 size = m_preferred_size;
1448 STDMETHODIMP Tracker3dFilter::IsConnected(bool &any_connected, bool &all_connected)
1450 any_connected = false;
1451 all_connected = true;
1452 for (int i = 0; i < m_num_streams; i++)
1454 if (m_streams[i].input_pin->IsConnected())
1455 any_connected = true;
1457 all_connected = false;
1459 if (m_streams[i].output_pin->IsConnected())
1460 any_connected = true;
1462 all_connected = false;
1467 // ITracker3dInternal
1468 STDMETHODIMP Tracker3dFilter::GetCameraInfo(std::vector<Cv3dTrackerCameraInfo> &info)
1470 info.resize(m_num_streams);
1471 for (int i = 0; i < m_num_streams; i++)
1472 info[i] = m_pCameraInfo[i];
1477 // Implementation of pins for Tracker 3d Filter
1479 static const wchar_t *PinName(PIN_DIRECTION pin_dir, int pin_number)
1481 static wchar_t buf[14];
1482 swprintf(buf, L"%s %d", pin_dir == PINDIR_INPUT ? L"Input" : L"Output", pin_number);
1487 Tracker3dInputPin::Tracker3dInputPin(int pin_number, Tracker3dFilter *filter, CCritSec *cs)
1488 : CBaseInputPin("Tracker3dInputPin", filter, cs, NULL, PinName(PINDIR_INPUT, pin_number)),
1489 m_pin_number(pin_number),
1495 Tracker3dInputPin::~Tracker3dInputPin()
1499 STDMETHODIMP_(ULONG) Tracker3dInputPin::NonDelegatingAddRef()
1501 InterlockedIncrement(&m_refcnt);
1502 return CBaseInputPin::NonDelegatingAddRef();
1505 STDMETHODIMP_(ULONG) Tracker3dInputPin::NonDelegatingRelease()
1507 if (InterlockedDecrement(&m_refcnt) == 0)
1512 return CBaseInputPin::NonDelegatingRelease();
1515 STDMETHODIMP Tracker3dInputPin::GetAllocator(IMemAllocator **allocator)
1517 if (m_pAllocator == NULL)
1518 return VFW_E_NO_ALLOCATOR;
1519 *allocator = m_pAllocator;
1520 (*allocator)->AddRef();
1524 STDMETHODIMP Tracker3dInputPin::NotifyAllocator(IMemAllocator *allocator, BOOL read_only)
1526 ALLOCATOR_PROPERTIES props, actual;
1527 allocator->GetProperties(&props);
1529 allocator->SetProperties(&props, &actual);
1530 return CBaseInputPin::NotifyAllocator(allocator, read_only);
1533 HRESULT Tracker3dInputPin::CheckMediaType(const CMediaType *mt)
1535 return m_pFilter->CheckMediaType(m_pin_number, mt);
1538 HRESULT Tracker3dInputPin::SetMediaType(const CMediaType *mt)
1540 CBaseInputPin::SetMediaType(mt);
1541 return m_pFilter->SetMediaType(m_pin_number, mt);
1544 HRESULT Tracker3dInputPin::GetMediaType(int pos, CMediaType *mt)
1546 return m_pFilter->GetMediaType(m_pin_number, pos, mt);
1550 HRESULT Tracker3dInputPin::Receive(IMediaSample *sample)
1552 HRESULT hr = CBaseInputPin::Receive(sample);
1556 return m_pFilter->Receive(m_pin_number, sample);
1559 HRESULT Tracker3dInputPin::EndOfStream()
1561 m_pFilter->EndOfStream(m_pin_number);
1565 HRESULT Tracker3dInputPin::BeginFlush()
1567 HRESULT hr = CBaseInputPin::BeginFlush();
1571 m_pFilter->Flush(m_pin_number);
1573 return static_cast<Tracker3dOutputPin *>(m_pFilter->GetPin(m_pin_number*2+1))->DeliverBeginFlush();
1576 HRESULT Tracker3dInputPin::EndFlush()
1578 HRESULT hr = static_cast<Tracker3dOutputPin *>(m_pFilter->GetPin(m_pin_number*2+1))->DeliverEndFlush();
1582 return CBaseInputPin::EndFlush();
1585 HRESULT Tracker3dInputPin::NewSegment(REFERENCE_TIME start, REFERENCE_TIME stop, double rate)
1587 CBasePin::NewSegment(start, stop, rate);
1588 return static_cast<Tracker3dOutputPin *>(m_pFilter->GetPin(m_pin_number*2+1))->DeliverNewSegment(start, stop, rate);
1592 Tracker3dOutputPin::Tracker3dOutputPin(int pin_number, Tracker3dFilter *filter, CCritSec *cs)
1593 : CBaseOutputPin("Tracker3dOutputPin", filter, cs, NULL, PinName(PINDIR_OUTPUT, pin_number)),
1594 m_pin_number(pin_number),
1600 Tracker3dOutputPin::~Tracker3dOutputPin()
1604 STDMETHODIMP_(ULONG) Tracker3dOutputPin::NonDelegatingAddRef()
1606 InterlockedIncrement(&m_refcnt);
1607 return CBaseOutputPin::NonDelegatingAddRef();
1610 STDMETHODIMP_(ULONG) Tracker3dOutputPin::NonDelegatingRelease()
1612 if (InterlockedDecrement(&m_refcnt) == 0)
1617 return CBaseOutputPin::NonDelegatingRelease();
1621 HRESULT Tracker3dOutputPin::CheckMediaType(const CMediaType *mt)
1623 return m_pFilter->CheckMediaType(m_pin_number, mt);
1626 HRESULT Tracker3dOutputPin::SetMediaType(const CMediaType *mt)
1628 CBaseOutputPin::SetMediaType(mt);
1629 return m_pFilter->SetMediaType(m_pin_number, mt);
1632 HRESULT Tracker3dOutputPin::GetMediaType(int pos, CMediaType *mt)
1634 return m_pFilter->GetMediaType(m_pin_number, pos, mt);
1637 HRESULT Tracker3dOutputPin::DecideAllocator(IMemInputPin *pPin, IMemAllocator **ppAlloc)
1641 Tracker3dInputPin *my_input_pin = static_cast<Tracker3dInputPin *>(m_pFilter->GetPin(m_pin_number*2));
1642 IMemAllocator *alloc;
1643 HRESULT hr = my_input_pin->GetAllocator(&alloc);
1647 // get downstream prop request
1648 ALLOCATOR_PROPERTIES props, request;
1649 alloc->GetProperties(&props);
1650 if (SUCCEEDED(pPin->GetAllocatorRequirements(&request)))
1652 bool changed = false;
1653 if (request.cbAlign > props.cbAlign)
1654 props.cbAlign = request.cbAlign, changed = true;
1655 if (request.cBuffers > props.cBuffers)
1656 props.cBuffers = request.cBuffers, changed = true;
1657 if (request.cbBuffer > props.cbBuffer)
1658 props.cbBuffer = request.cbBuffer, changed = true;
1659 if (request.cbPrefix > props.cbPrefix)
1660 props.cbPrefix = request.cbPrefix, changed = true;
1663 ALLOCATOR_PROPERTIES actual;
1664 alloc->SetProperties(&props, &actual);
1668 hr = pPin->NotifyAllocator(alloc, my_input_pin->IsReadOnly());
1680 // Setup information
1682 const AMOVIESETUP_MEDIATYPE sudPinTypes =
1684 &MEDIATYPE_Video, // Major type
1685 &MEDIASUBTYPE_NULL // Minor type
1688 const AMOVIESETUP_PIN sudpPins[] =
1690 { L"Input", // Pins string name
1691 FALSE, // Is it rendered
1692 FALSE, // Is it an output
1693 TRUE, // Are we allowed none
1694 TRUE, // And allowed many
1695 &CLSID_NULL, // Connects to filter
1696 NULL, // Connects to pin
1697 1, // Number of types
1698 &sudPinTypes // Pin information
1700 { L"Output", // Pins string name
1701 FALSE, // Is it rendered
1702 TRUE, // Is it an output
1703 TRUE, // Are we allowed none
1704 TRUE, // And allowed many
1705 &CLSID_NULL, // Connects to filter
1706 NULL, // Connects to pin
1707 1, // Number of types
1708 &sudPinTypes // Pin information
1712 const AMOVIESETUP_FILTER sudFilter =
1714 &CLSID_Tracker3dFilter, // Filter CLSID
1715 L"3d Tracker", // String name
1716 MERIT_DO_NOT_USE, // Filter merit
1717 2, // Number of pins
1718 sudpPins // Pin information
1722 // List of class IDs and creator functions for the class factory. This
1723 // provides the link between the OLE entry point in the DLL and an object
1724 // being created. The class factory will call the static CreateInstance
1726 CFactoryTemplate g_Templates[] = {
1728 , &CLSID_Tracker3dFilter
1729 , Tracker3dFilter::CreateInstance
1733 { L"3d Tracker Property Page"
1734 , &CLSID_Tracker3dPropertyPage
1735 , Tracker3dPropertyPage::CreateInstance }
1737 int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
1741 // DllRegisterServer
1743 // Handles sample registry and unregistry
1745 STDAPI DllRegisterServer()
1747 return AMovieDllRegisterServer2( TRUE );
1749 } // DllRegisterServer
1753 // DllUnregisterServer
1755 STDAPI DllUnregisterServer()
1757 return AMovieDllRegisterServer2( FALSE );
1759 } // DllUnregisterServer