Move the sources to trunk
[opencv] / cvaux / src / cv3dtracker.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) 2002, 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 //   * Redistributions of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
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.
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
42 #include "_cvaux.h"
43
44 #if _MSC_VER >= 1200
45 #pragma warning(disable:4786) // Disable MSVC warnings in the standard library.
46 #pragma warning(disable:4100)
47 #pragma warning(disable:4512)
48 #endif
49 #include <stdio.h>
50 #include <map>
51 #include <algorithm>
52 #if _MSC_VER >= 1200
53 #pragma warning(default:4100)
54 #pragma warning(default:4512)
55 #endif
56
57 #define ARRAY_SIZEOF(a) (sizeof(a)/sizeof((a)[0]))
58
59 static void FillObjectPoints(CvPoint3D32f *obj_points, CvSize etalon_size, float square_size);
60 static void DrawEtalon(IplImage *img, CvPoint2D32f *corners,
61                        int corner_count, CvSize etalon_size, int draw_ordered);
62 static void MultMatrix(float rm[4][4], const float m1[4][4], const float m2[4][4]);
63 static void MultVectorMatrix(float rv[4], const float v[4], const float m[4][4]);
64 static CvPoint3D32f ImageCStoWorldCS(const Cv3dTrackerCameraInfo &camera_info, CvPoint2D32f p);
65 static bool intersection(CvPoint3D32f o1, CvPoint3D32f p1,
66                          CvPoint3D32f o2, CvPoint3D32f p2,
67                          CvPoint3D32f &r1, CvPoint3D32f &r2);
68
69 /////////////////////////////////
70 // cv3dTrackerCalibrateCameras //
71 /////////////////////////////////
72 CV_IMPL CvBool cv3dTrackerCalibrateCameras(int num_cameras,
73                    const Cv3dTrackerCameraIntrinsics camera_intrinsics[], // size is num_cameras
74                    CvSize etalon_size,
75                    float square_size,
76                    IplImage *samples[],                                   // size is num_cameras
77                    Cv3dTrackerCameraInfo camera_info[])                   // size is num_cameras
78 {
79     CV_FUNCNAME("cv3dTrackerCalibrateCameras");
80     const int num_points = etalon_size.width * etalon_size.height;
81     int cameras_done = 0;        // the number of cameras whose positions have been determined
82     CvPoint3D32f *object_points = NULL; // real-world coordinates of checkerboard points
83     CvPoint2D32f *points = NULL; // 2d coordinates of checkerboard points as seen by a camera
84     IplImage *gray_img = NULL;   // temporary image for color conversion
85     IplImage *tmp_img = NULL;    // temporary image used by FindChessboardCornerGuesses
86     int c, i, j;
87
88     if (etalon_size.width < 3 || etalon_size.height < 3)
89         CV_ERROR(CV_StsBadArg, "Chess board size is invalid");
90
91     for (c = 0; c < num_cameras; c++)
92     {
93         // CV_CHECK_IMAGE is not available in the cvaux library
94         // so perform the checks inline.
95
96         //CV_CALL(CV_CHECK_IMAGE(samples[c]));
97
98         if( samples[c] == NULL )
99             CV_ERROR( CV_HeaderIsNull, "Null image" );
100
101         if( samples[c]->dataOrder != IPL_DATA_ORDER_PIXEL && samples[c]->nChannels > 1 )
102             CV_ERROR( CV_BadOrder, "Unsupported image format" );
103
104         if( samples[c]->maskROI != 0 || samples[c]->tileInfo != 0 )
105             CV_ERROR( CV_StsBadArg, "Unsupported image format" );
106
107         if( samples[c]->imageData == 0 )
108             CV_ERROR( CV_BadDataPtr, "Null image data" );
109
110         if( samples[c]->roi &&
111             ((samples[c]->roi->xOffset | samples[c]->roi->yOffset
112               | samples[c]->roi->width | samples[c]->roi->height) < 0 ||
113              samples[c]->roi->xOffset + samples[c]->roi->width > samples[c]->width ||
114              samples[c]->roi->yOffset + samples[c]->roi->height > samples[c]->height ||
115              (unsigned) (samples[c]->roi->coi) > (unsigned) (samples[c]->nChannels)))
116             CV_ERROR( CV_BadROISize, "Invalid ROI" );
117
118         // End of CV_CHECK_IMAGE inline expansion
119
120         if (samples[c]->depth != IPL_DEPTH_8U)
121             CV_ERROR(CV_BadDepth, "Channel depth of source image must be 8");
122
123         if (samples[c]->nChannels != 3 && samples[c]->nChannels != 1)
124             CV_ERROR(CV_BadNumChannels, "Source image must have 1 or 3 channels");
125     }
126
127     CV_CALL(object_points = (CvPoint3D32f *)cvAlloc(num_points * sizeof(CvPoint3D32f)));
128     CV_CALL(points = (CvPoint2D32f *)cvAlloc(num_points * sizeof(CvPoint2D32f)));
129
130     // fill in the real-world coordinates of the checkerboard points
131     FillObjectPoints(object_points, etalon_size, square_size);
132
133     for (c = 0; c < num_cameras; c++)
134     {
135         CvSize image_size = cvSize(samples[c]->width, samples[c]->height);
136         IplImage *img;
137
138         // The input samples are not required to all have the same size or color
139         // format. If they have different sizes, the temporary images are
140         // reallocated as necessary.
141         if (samples[c]->nChannels == 3)
142         {
143             // convert to gray
144             if (gray_img == NULL || !CV_ARE_SIZES_EQ(gray_img, samples[c]))
145             {
146                 if (gray_img != NULL)
147                     cvReleaseImage(&gray_img);
148                 CV_CALL(gray_img = cvCreateImage(image_size, IPL_DEPTH_8U, 1));
149             }
150             
151             CV_CALL(cvCvtColor(samples[c], gray_img, CV_BGR2GRAY));
152
153             img = gray_img;
154         }
155         else
156         {
157             // no color conversion required
158             img = samples[c];
159         }
160
161         if (tmp_img == NULL || !CV_ARE_SIZES_EQ(tmp_img, samples[c]))
162         {
163             if (tmp_img != NULL)
164                 cvReleaseImage(&tmp_img);
165             CV_CALL(tmp_img = cvCreateImage(image_size, IPL_DEPTH_8U, 1));
166         }
167
168         int count = num_points;
169         bool found = cvFindChessBoardCornerGuesses(img, tmp_img, 0,
170                                                    etalon_size, points, &count) != 0;
171         if (count == 0)
172             continue;
173         
174         // If found is true, it means all the points were found (count = num_points).
175         // If found is false but count is non-zero, it means that not all points were found.
176
177         cvFindCornerSubPix(img, points, count, cvSize(5,5), cvSize(-1,-1),
178                     cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10, 0.01f));
179
180         // If the image origin is BL (bottom-left), fix the y coordinates
181         // so they are relative to the true top of the image.
182         if (samples[c]->origin == IPL_ORIGIN_BL)
183         {
184             for (i = 0; i < count; i++)
185                 points[i].y = samples[c]->height - 1 - points[i].y;
186         }
187
188         if (found)
189         {
190             // Make sure x coordinates are increasing and y coordinates are decreasing.
191             // (The y coordinate of point (0,0) should be the greatest, because the point
192             // on the checkerboard that is the origin is nearest the bottom of the image.)
193             // This is done after adjusting the y coordinates according to the image origin.
194             if (points[0].x > points[1].x)
195             {
196                 // reverse points in each row
197                 for (j = 0; j < etalon_size.height; j++)
198                 {
199                     CvPoint2D32f *row = &points[j*etalon_size.width];
200                     for (i = 0; i < etalon_size.width/2; i++)
201                         std::swap(row[i], row[etalon_size.width-i-1]);
202                 }
203             }
204
205             if (points[0].y < points[etalon_size.width].y)
206             {
207                 // reverse points in each column
208                 for (i = 0; i < etalon_size.width; i++)
209                 {
210                     for (j = 0; j < etalon_size.height/2; j++)
211                         std::swap(points[i+j*etalon_size.width],
212                                   points[i+(etalon_size.height-j-1)*etalon_size.width]);
213                 }
214             }
215         }
216
217         DrawEtalon(samples[c], points, count, etalon_size, found);
218
219         if (!found)
220             continue;
221
222         float rotVect[3];
223         float rotMatr[9];
224         float transVect[3];
225
226         cvFindExtrinsicCameraParams(count,
227                                     image_size,
228                                     points,
229                                     object_points,
230                                     const_cast<float *>(camera_intrinsics[c].focal_length),
231                                     camera_intrinsics[c].principal_point,
232                                     const_cast<float *>(camera_intrinsics[c].distortion),
233                                     rotVect,
234                                     transVect);
235
236         // Check result against an arbitrary limit to eliminate impossible values.
237         // (If the chess board were truly that far away, the camera wouldn't be able to
238         // see the squares.)
239         if (transVect[0] > 1000*square_size
240             || transVect[1] > 1000*square_size
241             || transVect[2] > 1000*square_size)
242         {
243             // ignore impossible results
244             continue;
245         }
246
247         CvMat rotMatrDescr = cvMat(3, 3, CV_32FC1, rotMatr);
248         CvMat rotVectDescr = cvMat(3, 1, CV_32FC1, rotVect);
249
250         /* Calc rotation matrix by Rodrigues Transform */
251         cvRodrigues2( &rotVectDescr, &rotMatrDescr );
252
253         //combine the two transformations into one matrix
254         //order is important! rotations are not commutative
255         float tmat[4][4] = { { 1.f, 0.f, 0.f, 0.f },
256                              { 0.f, 1.f, 0.f, 0.f },
257                              { 0.f, 0.f, 1.f, 0.f },
258                              { transVect[0], transVect[1], transVect[2], 1.f } };
259         
260         float rmat[4][4] = { { rotMatr[0], rotMatr[1], rotMatr[2], 0.f },
261                              { rotMatr[3], rotMatr[4], rotMatr[5], 0.f },
262                              { rotMatr[6], rotMatr[7], rotMatr[8], 0.f },
263                              { 0.f, 0.f, 0.f, 1.f } };
264
265
266         MultMatrix(camera_info[c].mat, tmat, rmat);
267
268         // change the transformation of the cameras to put them in the world coordinate 
269         // system we want to work with.
270
271         // Start with an identity matrix; then fill in the values to accomplish
272         // the desired transformation.
273         float smat[4][4] = { { 1.f, 0.f, 0.f, 0.f },
274                              { 0.f, 1.f, 0.f, 0.f },
275                              { 0.f, 0.f, 1.f, 0.f },
276                              { 0.f, 0.f, 0.f, 1.f } };
277
278         // First, reflect through the origin by inverting all three axes.
279         smat[0][0] = -1.f;
280         smat[1][1] = -1.f;
281         smat[2][2] = -1.f;
282         MultMatrix(tmat, camera_info[c].mat, smat);
283
284         // Scale x and y coordinates by the focal length (allowing for non-square pixels
285         // and/or non-symmetrical lenses).
286         smat[0][0] = 1.0f / camera_intrinsics[c].focal_length[0];
287         smat[1][1] = 1.0f / camera_intrinsics[c].focal_length[1];
288         smat[2][2] = 1.0f;
289         MultMatrix(camera_info[c].mat, smat, tmat);
290
291         camera_info[c].principal_point = camera_intrinsics[c].principal_point;
292         camera_info[c].valid = true;
293
294         cameras_done++;
295     }
296
297 exit:
298     cvReleaseImage(&gray_img);
299     cvReleaseImage(&tmp_img);
300     cvFree(&object_points);
301     cvFree(&points);
302
303     return cameras_done == num_cameras;
304 }
305
306 // fill in the real-world coordinates of the checkerboard points
307 static void FillObjectPoints(CvPoint3D32f *obj_points, CvSize etalon_size, float square_size)
308 {
309     int x, y, i;
310
311     for (y = 0, i = 0; y < etalon_size.height; y++)
312     {
313         for (x = 0; x < etalon_size.width; x++, i++)
314         {
315             obj_points[i].x = square_size * x;
316             obj_points[i].y = square_size * y;
317             obj_points[i].z = 0;
318         }
319     }
320 }
321
322
323 // Mark the points found on the input image
324 // The marks are drawn multi-colored if all the points were found.
325 static void DrawEtalon(IplImage *img, CvPoint2D32f *corners,
326                        int corner_count, CvSize etalon_size, int draw_ordered)
327 {
328     const int r = 4;
329     int i;
330     int x, y;
331     CvPoint prev_pt = { 0, 0 };
332     static const CvScalar rgb_colors[] = {
333         {{0,0,255}},
334         {{0,128,255}},
335         {{0,200,200}},
336         {{0,255,0}},
337         {{200,200,0}},
338         {{255,0,0}},
339         {{255,0,255}} };
340     static const CvScalar gray_colors[] = {
341         {{80}}, {{120}}, {{160}}, {{200}}, {{100}}, {{140}}, {{180}}
342     };
343     const CvScalar* colors = img->nChannels == 3 ? rgb_colors : gray_colors;
344
345     CvScalar color = colors[0];
346     for (y = 0, i = 0; y < etalon_size.height; y++)
347     {
348         if (draw_ordered)
349             color = colors[y % ARRAY_SIZEOF(rgb_colors)];
350
351         for (x = 0; x < etalon_size.width && i < corner_count; x++, i++)
352         {
353             CvPoint pt;
354             pt.x = cvRound(corners[i].x);
355             pt.y = cvRound(corners[i].y);
356             if (img->origin == IPL_ORIGIN_BL)
357                 pt.y = img->height - 1 - pt.y;
358
359             if (draw_ordered)
360             {
361                 if (i != 0)
362                    cvLine(img, prev_pt, pt, color, 1, CV_AA);
363                 prev_pt = pt;
364             }
365
366             cvLine( img, cvPoint(pt.x - r, pt.y - r),
367                     cvPoint(pt.x + r, pt.y + r), color, 1, CV_AA );
368             cvLine( img, cvPoint(pt.x - r, pt.y + r),
369                     cvPoint(pt.x + r, pt.y - r), color, 1, CV_AA );
370             cvCircle( img, pt, r+1, color, 1, CV_AA );
371         }
372     }
373 }
374
375 // Find the midpoint of the line segment between two points.
376 static CvPoint3D32f midpoint(const CvPoint3D32f &p1, const CvPoint3D32f &p2)
377 {
378     return cvPoint3D32f((p1.x+p2.x)/2, (p1.y+p2.y)/2, (p1.z+p2.z)/2);
379 }
380
381 static void operator +=(CvPoint3D32f &p1, const CvPoint3D32f &p2)
382 {
383     p1.x += p2.x;
384     p1.y += p2.y;
385     p1.z += p2.z;
386 }
387
388 static CvPoint3D32f operator /(const CvPoint3D32f &p, int d)
389 {
390     return cvPoint3D32f(p.x/d, p.y/d, p.z/d);
391 }
392
393 static const Cv3dTracker2dTrackedObject *find(const Cv3dTracker2dTrackedObject v[], int num_objects, int id)
394 {
395     for (int i = 0; i < num_objects; i++)
396     {
397         if (v[i].id == id)
398             return &v[i];
399     }
400     return NULL;
401 }
402
403 #define CAMERA_POS(c) (cvPoint3D32f((c).mat[3][0], (c).mat[3][1], (c).mat[3][2]))
404
405 //////////////////////////////
406 // cv3dTrackerLocateObjects //
407 //////////////////////////////
408 CV_IMPL int  cv3dTrackerLocateObjects(int num_cameras, int num_objects,
409                  const Cv3dTrackerCameraInfo camera_info[],      // size is num_cameras
410                  const Cv3dTracker2dTrackedObject tracking_info[], // size is num_objects*num_cameras
411                  Cv3dTrackerTrackedObject tracked_objects[])     // size is num_objects
412 {
413     /*CV_FUNCNAME("cv3dTrackerLocateObjects");*/
414     int found_objects = 0;
415
416     // count how many cameras could see each object
417     std::map<int, int> count;
418     for (int c = 0; c < num_cameras; c++)
419     {
420         if (!camera_info[c].valid)
421             continue;
422
423         for (int i = 0; i < num_objects; i++)
424         {
425             const Cv3dTracker2dTrackedObject *o = &tracking_info[c*num_objects+i];
426             if (o->id != -1)
427                 count[o->id]++;
428         }
429     }
430
431     // process each object that was seen by at least two cameras
432     for (std::map<int, int>::iterator i = count.begin(); i != count.end(); i++)
433     {
434         if (i->second < 2)
435             continue; // ignore object seen by only one camera
436         int id = i->first;
437
438         // find an approximation of the objects location for each pair of cameras that
439         // could see this object, and average them
440         CvPoint3D32f total = cvPoint3D32f(0, 0, 0);
441         int weight = 0;
442
443         for (int c1 = 0; c1 < num_cameras-1; c1++)
444         {
445             if (!camera_info[c1].valid)
446                 continue;
447
448             const Cv3dTracker2dTrackedObject *o1 = find(&tracking_info[c1*num_objects],
449                                                         num_objects, id);
450             if (o1 == NULL)
451                 continue; // this camera didn't see this object
452
453             CvPoint3D32f p1a = CAMERA_POS(camera_info[c1]);
454             CvPoint3D32f p1b = ImageCStoWorldCS(camera_info[c1], o1->p);
455
456             for (int c2 = c1 + 1; c2 < num_cameras; c2++)
457             {
458                 if (!camera_info[c2].valid)
459                     continue;
460
461                 const Cv3dTracker2dTrackedObject *o2 = find(&tracking_info[c2*num_objects],
462                                                             num_objects, id);
463                 if (o2 == NULL)
464                     continue; // this camera didn't see this object
465
466                 CvPoint3D32f p2a = CAMERA_POS(camera_info[c2]);
467                 CvPoint3D32f p2b = ImageCStoWorldCS(camera_info[c2], o2->p);
468
469                 // these variables are initialized simply to avoid erroneous error messages
470                 // from the compiler
471                 CvPoint3D32f r1 = cvPoint3D32f(0, 0, 0);
472                 CvPoint3D32f r2 = cvPoint3D32f(0, 0, 0);
473
474                 // find the intersection of the two lines (or the points of closest
475                 // approach, if they don't intersect)
476                 if (!intersection(p1a, p1b, p2a, p2b, r1, r2))
477                     continue;
478
479                 total += midpoint(r1, r2);
480                 weight++;
481             }
482         }
483
484         CvPoint3D32f center = total/weight;
485         tracked_objects[found_objects++] = cv3dTrackerTrackedObject(id, center);
486     }
487
488     return found_objects;
489 }
490
491 #define EPS 1e-9
492
493 // Compute the determinant of the 3x3 matrix represented by 3 row vectors.
494 static inline double det(CvPoint3D32f v1, CvPoint3D32f v2, CvPoint3D32f v3)
495 {
496     return v1.x*v2.y*v3.z + v1.z*v2.x*v3.y + v1.y*v2.z*v3.x
497            - v1.z*v2.y*v3.x - v1.x*v2.z*v3.y - v1.y*v2.x*v3.z;
498 }
499
500 static CvPoint3D32f operator +(CvPoint3D32f a, CvPoint3D32f b)
501 {
502     return cvPoint3D32f(a.x + b.x, a.y + b.y, a.z + b.z);
503 }
504
505 static CvPoint3D32f operator -(CvPoint3D32f a, CvPoint3D32f b)
506 {
507     return cvPoint3D32f(a.x - b.x, a.y - b.y, a.z - b.z);
508 }
509
510 static CvPoint3D32f operator *(CvPoint3D32f v, double f)
511 {
512     return cvPoint3D32f(f*v.x, f*v.y, f*v.z);
513 }
514
515
516 // Find the intersection of two lines, or if they don't intersect,
517 // the points of closest approach.
518 // The lines are defined by (o1,p1) and (o2, p2).
519 // If they intersect, r1 and r2 will be the same.
520 // Returns false on error.
521 static bool intersection(CvPoint3D32f o1, CvPoint3D32f p1,
522                          CvPoint3D32f o2, CvPoint3D32f p2,
523                          CvPoint3D32f &r1, CvPoint3D32f &r2)
524 {
525     CvPoint3D32f x = o2 - o1;
526     CvPoint3D32f d1 = p1 - o1;
527     CvPoint3D32f d2 = p2 - o2;
528
529     CvPoint3D32f cross = cvPoint3D32f(d1.y*d2.z - d1.z*d2.y,
530                                       d1.z*d2.x - d1.x*d2.z,
531                                       d1.x*d2.y - d1.y*d2.x);
532     double den = cross.x*cross.x + cross.y*cross.y + cross.z*cross.z;
533
534     if (den < EPS)
535         return false;
536
537     double t1 = det(x, d2, cross) / den;
538     double t2 = det(x, d1, cross) / den;
539
540     r1 = o1 + d1 * t1;
541     r2 = o2 + d2 * t2;
542
543     return true;
544 }
545
546 // Convert from image to camera space by transforming point p in
547 // the image plane by the camera matrix.
548 static CvPoint3D32f ImageCStoWorldCS(const Cv3dTrackerCameraInfo &camera_info, CvPoint2D32f p)
549 {
550     float tp[4];
551     tp[0] = (float)p.x - camera_info.principal_point.x;
552     tp[1] = (float)p.y - camera_info.principal_point.y;
553     tp[2] = 1.f;
554     tp[3] = 1.f;
555
556     float tr[4];
557     //multiply tp by mat to get tr
558     MultVectorMatrix(tr, tp, camera_info.mat);
559
560     return cvPoint3D32f(tr[0]/tr[3], tr[1]/tr[3], tr[2]/tr[3]);
561 }
562
563 // Multiply affine transformation m1 by the affine transformation m2 and
564 // return the result in rm.
565 static void MultMatrix(float rm[4][4], const float m1[4][4], const float m2[4][4])
566 {
567     for (int i=0; i<=3; i++)
568         for (int j=0; j<=3; j++)
569         {
570             rm[i][j]= 0.0;
571             for (int k=0; k <= 3; k++)
572                 rm[i][j] += m1[i][k]*m2[k][j];
573         }
574 }
575
576 // Multiply the vector v by the affine transformation matrix m and return the
577 // result in rv.
578 void MultVectorMatrix(float rv[4], const float v[4], const float m[4][4])
579 {
580     for (int i=0; i<=3; i++)
581     {
582         rv[i] = 0.f;
583         for (int j=0;j<=3;j++)
584             rv[i] += v[j] * m[j][i];
585     }
586 }