00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 #include <stdio.h>
00034 #include <fstream>
00035 #include <iostream>
00036 #include <ctype.h>
00037 #include <ctime>
00038 #include "camera_laser_calibration/scene_recog/scene_recog.h"
00039 
00040 #include <ros/ros.h>
00041 
00042 CSceneRecog *p_plane_display = NULL;
00043 
00044 CSceneRecog::CSceneRecog ()
00045 {
00046   L_width_ = 0;
00047   L_height_ = 0;
00048   p_plane_display = this;
00049   plane_parameters_[0] = 0.0;
00050   plane_parameters_[1] = 0.0;
00051   plane_parameters_[2] = 0.0;
00052   plane_parameters_[3] = 0.0;
00053 }
00054 
00055 CSceneRecog::~CSceneRecog (void)
00056 {
00057 }
00058 
00059 void CSceneRecog::srProcess (std::vector < std::vector < CPoint3d > >&data)
00060 {
00061   laser_data_.swap (data);      
00062   srPlaneExtract ();            
00063   srStructureExtract ();
00064 }
00065 
00066 void CSceneRecog::srPlaneExtract ()
00067 {
00068   CPoint3d PointInSquare[DIMEN][DIMEN];
00069   CPoint3d Mv;
00070   CPoint3d Nvec;
00071   int flagbuf;
00072   int pnum = -1;
00073   if (laser_data_.size () > 0)
00074   {
00075     L_width_ = (int) laser_data_.size ();
00076     L_height_ = (int) laser_data_[0].size ();
00077 
00078     std::vector < CPointTag > TagVector (L_height_);
00079     std::vector < std::vector < CPointTag > >PTV (L_width_, TagVector);
00080     point_tag_vector_.swap (PTV);
00081 
00082     for (int i = 0; i < L_width_ - DIMEN; i = i + 1)
00083     {
00084       for (int j = 0; j < L_height_ - DIMEN; j = j + 1)
00085       {
00086         Mv.x = Mv.y = Mv.z = 0.0;
00087         for (int k = 0; k < DIMEN; k++) 
00088           for (int l = 0; l < DIMEN; l++)
00089           {
00090             PointInSquare[k][l] = laser_data_[i + k][j + l];
00091             Mv.x += PointInSquare[k][l].x;
00092             Mv.y += PointInSquare[k][l].y;
00093             Mv.z += PointInSquare[k][l].z;
00094           }
00095         Mv.x = Mv.x / (DIMEN * DIMEN);  
00096         Mv.y = Mv.y / (DIMEN * DIMEN);
00097         Mv.z = Mv.z / (DIMEN * DIMEN);
00098 
00099         if (srIsAPlane (PointInSquare, Mv))
00100         {
00101           Nvec =srNormalVec (laser_data_[i + DIMEN - 1][j + DIMEN - 1], laser_data_[i + DIMEN - 1][j],
00102                              laser_data_[i][j + DIMEN - 1], laser_data_[i][j]);
00103 
00104           flagbuf = -1;
00105           for (int f = 0; f <= DIMEN - 1; f++)
00106             for (int g = 0; g <= DIMEN - 1; g++)
00107             {
00108               if (point_tag_vector_[i + f][j + g].pflag != -1)
00109               {
00110                 flagbuf = point_tag_vector_[i + f][j + g].pflag;
00111                 break;
00112               }
00113             }
00114 
00115           if (flagbuf == -1)
00116           {
00117             pnum++;
00118             for (int m = 0; m < DIMEN; m++)
00119             {
00120               for (int n = 0; n < DIMEN; n++)
00121               {
00122                 point_tag_vector_[i + m][j + n].pflag = pnum;
00123               }
00124             }
00125             Plane pTag;
00126             pTag.VecNum = 1;
00127             pTag.VecSum = Nvec;
00128             pTag.VecAve = Nvec;
00129             pTag.MeanVal = Mv;
00130             pTag.MeanSum = Mv;
00131             plane_vector_.push_back (pTag);
00132           }
00133           
00134           else
00135           {
00136             if (srAngle (plane_vector_[flagbuf].VecAve, Nvec) < 0.2  )
00137             {
00138               for (int m2 = 0; m2 < DIMEN; m2++)
00139                 for (int n2 = 0; n2 < DIMEN; n2++)
00140                 {
00141                   point_tag_vector_[i + m2][j + n2].pflag = flagbuf;
00142                 }
00143 
00144               plane_vector_[flagbuf].VecNum++;
00145               
00146               plane_vector_[flagbuf].VecSum.x += Nvec.x;
00147               plane_vector_[flagbuf].VecSum.y += Nvec.y;
00148               plane_vector_[flagbuf].VecSum.z += Nvec.z;
00149               
00150               plane_vector_[flagbuf].VecAve.x = plane_vector_[flagbuf].VecSum.x / plane_vector_[flagbuf].VecNum;
00151               plane_vector_[flagbuf].VecAve.y = plane_vector_[flagbuf].VecSum.y / plane_vector_[flagbuf].VecNum;
00152               plane_vector_[flagbuf].VecAve.z = plane_vector_[flagbuf].VecSum.z / plane_vector_[flagbuf].VecNum;
00153               
00154               plane_vector_[flagbuf].MeanSum.x += Mv.x;
00155               plane_vector_[flagbuf].MeanSum.y += Mv.y;
00156               plane_vector_[flagbuf].MeanSum.z += Mv.z;
00157               
00158               plane_vector_[flagbuf].MeanVal.x = plane_vector_[flagbuf].MeanSum.x / plane_vector_[flagbuf].VecNum;
00159               plane_vector_[flagbuf].MeanVal.y = plane_vector_[flagbuf].MeanSum.y / plane_vector_[flagbuf].VecNum;
00160               plane_vector_[flagbuf].MeanVal.z = plane_vector_[flagbuf].MeanSum.z / plane_vector_[flagbuf].VecNum;
00161             }
00162           }
00163         }
00164       }
00165     }
00166   }
00167 }
00168 
00169 void CSceneRecog::srStructureExtract () 
00170 {
00171 
00172   using namespace std;
00173 
00174   for (int i = 0; i < L_height_; i++)
00175   {
00176     for (int j = 0; j < L_width_; j++)
00177     {
00178       int flag = point_tag_vector_[j][i].pflag;
00179       if (flag >= 0)
00180       {
00181         CPointIndex index (i, j);
00182         plane_vector_[flag].InnerPoints.push_back (index);
00183       }
00184     }
00185   }
00186 
00187 
00188   
00189   int PlaneNum = (int) plane_vector_.size ();
00190   for (int i = 0; i < PlaneNum - 1; i++)
00191   {
00192     int pmax = i;
00193     for (int j = i + 1; j < PlaneNum; j++)
00194     {
00195       if (plane_vector_[j].VecNum > plane_vector_[pmax].VecNum)
00196       {
00197         pmax = j;
00198       }
00199     }
00200     if (pmax != i)
00201       std::swap (plane_vector_[i], plane_vector_[pmax]);
00202   }
00203 
00204 
00205 
00206 
00207 
00208 
00209 
00210 
00211   int iterate = 2;
00212   while (iterate-- > 1)
00213   {
00214     
00215     int cnt = 0;
00216     vector < Plane > Planes;    
00217     vector < Plane > ExcludePlane;
00218     for (int i = 0; i < (int) plane_vector_.size (); i++)
00219     {
00220       cnt++;
00221       if (plane_vector_[i].VecNum < 4)
00222         break;
00223       
00224       plane_vector_[i].A = plane_vector_[i].VecAve.x;
00225       plane_vector_[i].B = plane_vector_[i].VecAve.y;
00226       plane_vector_[i].C = plane_vector_[i].VecAve.z;
00227       plane_vector_[i].D = -(plane_vector_[i].A * plane_vector_[i].MeanVal.x 
00228                            + plane_vector_[i].B * plane_vector_[i].MeanVal.y 
00229                            + plane_vector_[i].C * plane_vector_[i].MeanVal.z);
00230       
00231       bool flag = false;
00232       float angle;
00233       for (int j = 0; j < (int) Planes.size (); j++)
00234       {
00235         angle = srAngle (plane_vector_[i].VecAve, Planes[j].VecAve);
00236         angle = angle < (PI - angle) ? angle : (PI - angle);
00237         if (angle < 20 * PI / 180)
00238         {
00239           if (srP2Pl (plane_vector_[i].MeanVal, Planes[j]) < 0.1)
00240           {
00241             Planes[j].VecNum += plane_vector_[i].VecNum;
00242 
00243             
00244             Planes[j].VecSum += plane_vector_[i].VecSum;
00245 
00246             
00247             Planes[j].VecAve.x = Planes[j].VecSum.x / Planes[j].VecNum;
00248             Planes[j].VecAve.y = Planes[j].VecSum.y / Planes[j].VecNum;
00249             Planes[j].VecAve.z = Planes[j].VecSum.z / Planes[j].VecNum;
00250 
00251             
00252             Planes[j].MeanSum += plane_vector_[i].MeanSum;
00253 
00254             
00255             Planes[j].MeanVal.x = Planes[j].MeanSum.x / Planes[j].VecNum;
00256             Planes[j].MeanVal.y = Planes[j].MeanSum.y / Planes[j].VecNum;
00257             Planes[j].MeanVal.z = Planes[j].MeanSum.z / Planes[j].VecNum;
00258 
00259             Planes[j].A = Planes[j].VecAve.x;
00260             Planes[j].B = Planes[j].VecAve.y;
00261             Planes[j].C = Planes[j].VecAve.z;
00262             Planes[j].D = -(Planes[j].A * Planes[j].MeanVal.x 
00263                           + Planes[j].B * Planes[j].MeanVal.y 
00264                           + Planes[j].C * Planes[j].MeanVal.z);
00265 
00266             int I = (int) plane_vector_[i].InnerPoints.size ();
00267             for (int k = 0; k < I; k++)
00268             {
00269               Planes[j].InnerPoints.push_back (plane_vector_[i].InnerPoints[k]);
00270             }
00271             flag = true;
00272             break;
00273           }
00274 
00275         }
00276       }
00277       
00278       if (!flag)
00279       {
00280         if ((int) Planes.size () < iterate * iterate * MAX_PLANE_NUM)
00281         {
00282           Plane ptemp;
00283           Planes.push_back (ptemp);
00284           std::swap (Planes[Planes.size () - 1], plane_vector_[i]);
00285         }
00286         else
00287         {
00288           Plane ptemp;
00289           ExcludePlane.push_back (ptemp);
00290           std::swap (ExcludePlane[ExcludePlane.size () - 1], plane_vector_[i]);
00291         }
00292       }
00293     }
00294     plane_vector_.swap (Planes);        
00295   }
00296   
00297   boundary_vec_.resize (plane_vector_.size ());
00298   for (int i = 0; i < (int) plane_vector_.size (); i++)
00299   {
00300     int flag = 0;
00301     boundary_vec_[i].push_back (plane_vector_[i].InnerPoints[0]);
00302     for (int j = 1; j < (int) plane_vector_[i].InnerPoints.size (); j++)
00303     {
00304       flag = 1;
00305       if (plane_vector_[i].InnerPoints[j - 1].i == plane_vector_[i].InnerPoints[j].i
00306           && plane_vector_[i].InnerPoints[j - 1].j + 1 == plane_vector_[i].InnerPoints[j].j)
00307         continue;
00308       else
00309       {
00310         boundary_vec_[i].push_back (plane_vector_[i].InnerPoints[j - 1]);
00311         boundary_vec_[i].push_back (plane_vector_[i].InnerPoints[j]);
00312         flag = 0;
00313       }
00314     }
00315     if (flag == 1)
00316       boundary_vec_[i].push_back (plane_vector_[i].InnerPoints.back ());
00317   }
00318 
00319   
00320 
00321 
00322 
00323 
00324   
00325   convex_vec_.resize (boundary_vec_.size ());
00326   for (int i = 0; i < (int) boundary_vec_.size (); i++)
00327   {
00328     int minidx = 0;
00329     for (int j = 1; j < (int) boundary_vec_[i].size (); j++)
00330     {
00331       if (boundary_vec_[i][j].i < boundary_vec_[i][minidx].i
00332           || (boundary_vec_[i][j].i == boundary_vec_[i][minidx].i
00333           && boundary_vec_[i][j].j < boundary_vec_[i][minidx].j))
00334         minidx = j;
00335     }
00336     if (minidx != 0)
00337       std::swap (boundary_vec_[i][0], boundary_vec_[i][minidx]);
00338 
00339     for (int j = 1; j < (int) boundary_vec_[i].size () - 1; j++)
00340     {
00341       int r = j;
00342       for (int k = j + 1; k < (int) boundary_vec_[i].size (); k++)
00343       {
00344         bool res = srCompL1R0bool (boundary_vec_[i][0], boundary_vec_[i][r], boundary_vec_[i][k]);
00345         if (res == 0)
00346           r = k;
00347       }
00348       if (r != j)
00349         std::swap (boundary_vec_[i][j], boundary_vec_[i][r]);
00350     }
00351 
00352     std::vector < CPointIndex > stack;
00353     stack.clear ();
00354     stack.push_back (boundary_vec_[i][0]);
00355     stack.push_back (boundary_vec_[i][1]);
00356     stack.push_back (boundary_vec_[i][2]);
00357 
00358     int top = 2;
00359     for (int j = 3; j < (int) boundary_vec_[i].size (); j++)
00360     {
00361       while (top >= 2 && srCross_Product (stack[top - 1], stack[top], boundary_vec_[i][j]) <= 0)
00362       {
00363         stack.pop_back ();
00364         top--;
00365       }
00366       stack.push_back (boundary_vec_[i][j]);
00367       top++;
00368     }
00369     convex_vec_[i].swap (stack);
00370   }
00371   
00372 
00373 
00374 
00375 
00376 
00377   plane_parameters_[0] = plane_vector_[0].A;
00378   plane_parameters_[1] = plane_vector_[0].B;
00379   plane_parameters_[2] = plane_vector_[0].C;
00380   plane_parameters_[3] = plane_vector_[0].D;
00381   
00382 
00383   
00384 
00385 
00386 
00387 
00388 
00389 
00390 
00391 
00392 
00393 
00394 
00395 
00396 
00397 
00398 
00399 
00400 
00401   
00402 
00403 }
00404 
00405 float CSceneRecog::srPtopDistan (CPoint3d p1, CPoint3d p2)
00406 {
00407   return ((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) + (p1.z - p2.z) * (p1.z - p2.z));
00408 }
00409 
00410 bool CSceneRecog::srIsAPlane (CPoint3d p[][DIMEN], CPoint3d m)
00411 {
00412   CPoint3d Pzero (0, 0, 0);
00413   int n = 3, jt = 100;
00414   float cov[9] = { 0.0 };
00415   float s1, s2, s3, min;
00416   float eps = 0.01f;
00417   CPoint3d sum, ptemp;
00418   int zeronum = 0;
00419   bool booldis = true;
00420   float distemp, maxdis = 0.0;
00421 
00422   ptemp = p[0][0];
00423   for (int i = 0; i < DIMEN; i++)
00424   {
00425     for (int j = 1; j < DIMEN; j++)
00426     {
00427       if (Pzero == p[i][j])
00428       {
00429         zeronum++;
00430       }
00431       else
00432       {
00433         distemp = srPtopDistan (ptemp, p[i][j]);
00434         if (distemp > maxdis)
00435         {
00436           maxdis = distemp;
00437         }
00438         ptemp = p[i][j];
00439       }
00440     }
00441   }
00442 
00443   if (maxdis > MAXDISTH1 * MAXDISTH1)   
00444   {
00445     booldis = false;
00446     return false;
00447   }
00448   for (int i = 0; i < DIMEN; i++)
00449   {
00450     for (int j = 0; j < DIMEN; j++)
00451     {
00452       float buf[3];
00453       buf[0] = p[i][j].x - m.x;
00454       buf[1] = p[i][j].y - m.y;
00455       buf[2] = p[i][j].z - m.z;
00456       for (int k = 0; k < 3; k++)
00457         for (int l = 0; l < 3; l++)
00458           if (!(p[i][j] == Pzero))
00459           {
00460             cov[k * 3 + l] += buf[k] * buf[l];
00461           }
00462     }
00463   }
00464   
00465   srEigenValue (cov, n, eps, jt);
00466   s1 = fabs (cov[0]);
00467   s2 = fabs (cov[4]);
00468   s3 = fabs (cov[8]);
00469   min = s1 < s2 ? s1 : s2;
00470   min = s3 < min ? s3 : min;
00471 
00472   
00473   if (min < THRESH && zeronum < DIMEN && booldis)
00474     return true;
00475   return false;
00476 }
00477 
00478 int CSceneRecog::srEigenValue (float a[], int n, double eps, int jt)
00479 {
00480   int i, j, u, w, t, s, l;
00481   int p = 0;
00482   int q = 0;
00483   float fm, cn, sn, omega, x, y, d, v[30];
00484   l = 1;
00485   for (i = 0; i <= n - 1; i++)
00486   {
00487     v[i * n + i] = 1.0;
00488     for (j = 0; j <= n - 1; j++)
00489     {
00490       if (i != j)
00491       {
00492         v[i * n + j] = 0.0;
00493       }
00494     }
00495   }
00496   while (l == 1)
00497   {
00498     fm = 0.0;
00499     for (i = 0; i <= n - 1; i++)
00500     {
00501       for (j = 0; j <= n - 1; j++)
00502       {
00503         d = fabs (a[i * n + j]);
00504         if ((i != j) && (d > fm))
00505         {
00506           fm = d;
00507           p = i;
00508           q = j;
00509         }
00510       }
00511     }
00512     if (fm < eps)
00513     {
00514       return (1);
00515     }
00516     if (l > jt)
00517     {
00518       return (-1);
00519     }
00520     l = l + 1;
00521     u = p * n + q;
00522     w = p * n + p;
00523     t = q * n + p;
00524     s = q * n + q;
00525     x = -a[u];
00526     y = (a[s] - a[w]) / 2.0;
00527     omega = x / sqrt (x * x + y * y);
00528     if (y < 0.0)
00529     {
00530       omega = -omega;
00531     }
00532     sn = 1.0 + sqrt (1.0 - omega * omega);
00533     sn = omega / sqrt (2.0 * sn);
00534     cn = sqrt (1.0 - sn * sn);
00535     fm = a[w];
00536     a[w] = fm * cn * cn + a[s] * sn * sn + a[u] * omega;
00537     a[s] = fm * sn * sn + a[s] * cn * cn - a[u] * omega;
00538     a[u] = 0.0;
00539     a[t] = 0.0;
00540     for (j = 0; j <= n - 1; j++)
00541     {
00542       if ((j != p) && (j != q))
00543       {
00544         u = p * n + j;
00545         w = q * n + j;
00546         fm = a[u];
00547         a[u] = fm * cn + a[w] * sn;
00548         a[w] = -fm * sn + a[w] * cn;
00549       }
00550     }
00551     for (i = 0; i <= n - 1; i++)
00552     {
00553       if ((i != p) && (i != q))
00554       {
00555         u = i * n + p;
00556         w = i * n + q;
00557         fm = a[u];
00558         a[u] = fm * cn + a[w] * sn;
00559         a[w] = -fm * sn + a[w] * cn;
00560       }
00561     }
00562     for (i = 0; i <= n - 1; i++)
00563     {
00564       u = i * n + p;
00565       w = i * n + q;
00566       fm = v[u];
00567       v[u] = fm * cn + v[w] * sn;
00568       v[w] = -fm * sn + v[w] * cn;
00569     }
00570   }
00571   return 0;
00572 }
00573 
00574 CPoint3d CSceneRecog::srNormalVec (CPoint3d start1, CPoint3d end1, CPoint3d start2, CPoint3d end2)
00575 {
00576   float r1, r2;
00577   CPoint3d point1, point2, point;
00578   point1 = srVecMuti (start1, end1, start1, end2);
00579   point2 = srVecMuti (start2, end1, start2, end2);
00580   r1 = sqrt (point1.x * point1.x + point1.y * point1.y + point1.z * point1.z);
00581   r2 = sqrt (point2.x * point2.x + point2.y * point2.y + point2.z * point2.z);
00582   point.x = (point1.x / r1 + point2.x / r2) / 2;
00583   point.y = (point1.y / r1 + point2.y / r2) / 2;
00584   point.z = (point1.z / r1 + point2.z / r2) / 2;
00585   return point;
00586 }
00587 
00588 CPoint3d CSceneRecog::srVecMuti (CPoint3d start1, CPoint3d end1, CPoint3d start2, CPoint3d end2)
00589 {
00590   float x1, y1, z1;
00591   float x2, y2, z2;
00592   x1 = end1.x - start1.x;
00593   y1 = end1.y - start1.y;
00594   z1 = end1.z - start1.z;
00595   x2 = end2.x - start2.x;
00596   y2 = end2.y - start2.y;
00597   z2 = end2.z - start2.z;
00598   CPoint3d point;
00599   point.x = y1 * z2 - y2 * z1;
00600   point.y = x2 * z1 - x1 * z2;
00601   point.z = x1 * y2 - x2 * y1;
00602   
00603   return point;
00604 }
00605 
00606 float CSceneRecog::srAngle (CPoint3d vec1, CPoint3d vec2)
00607 {
00608   float x1, y1, z1, p1, p2,  result, theta;
00609   x1 = vec1.x * vec2.x;
00610   y1 = vec1.y * vec2.y;
00611   z1 = vec1.z * vec2.z;
00612   p1 = vec1.x * vec1.x + vec1.y * vec1.y + vec1.z * vec1.z;
00613   p2 = vec2.x * vec2.x + vec2.y * vec2.y + vec2.z * vec2.z;
00614   result = (x1 + y1 + z1) / sqrt (p1 * p2);
00615   theta = acos (result);
00616   
00617   return theta;
00618 }
00619 
00620 float CSceneRecog::srP2Pl (CPoint3d m, const Plane & Plane)
00621 {
00622   float dist;
00623   dist = fabs (Plane.A * m.x + Plane.B * m.y + Plane.C * m.z + Plane.D);
00624   dist = dist / sqrt (Plane.A * Plane.A + Plane.B * Plane.B + Plane.C * Plane.C);
00625   
00626   return dist;
00627 }
00628 
00629 void CSceneRecog::srCalColor (int idx, std::vector < float >&rgb)
00630 {
00631   static int preidx = 1000;
00632   float step = 1;
00633   if (idx < 0)
00634   {
00635     rgb[0] = 0.5;
00636     rgb[1] = 0.5;
00637     rgb[2] = 0.5;
00638   }
00639   else if (idx != preidx)
00640   {
00641     float ratio = idx % 8;
00642     ratio = (1 - ratio / 8);
00643     switch (idx % 6)
00644     {
00645       case 0:
00646         rgb[0] = ratio * step;
00647         rgb[1] = 0;
00648         rgb[2] = 0;
00649         break;
00650       case 1:
00651         rgb[0] = 0;
00652         rgb[1] = ratio * step;
00653         rgb[2] = 0;
00654         break;
00655       case 2:
00656         rgb[0] = 0;
00657         rgb[1] = 0;
00658         rgb[2] = ratio * step;
00659         break;
00660       case 3:
00661         rgb[0] = ratio * step;
00662         rgb[1] = ratio * step;
00663         rgb[2] = 0;
00664         break;
00665       case 4:
00666         rgb[0] = ratio * step;
00667         rgb[1] = 0;
00668         rgb[2] = ratio * step;
00669         break;
00670       case 5:
00671         rgb[0] = 0;
00672         rgb[1] = ratio * step;
00673         rgb[2] = ratio * step;
00674         break;
00675     }
00676   }
00677   preidx = idx;
00678 }
00679 
00680 bool CSceneRecog::srCompL1R0bool (const CPointIndex & p1, const CPointIndex & p2, const CPointIndex & p3)
00681 {
00682   bool res = 1;
00683   int temp = (p2.j - p1.j) * (p3.i - p1.i) - (p2.i - p1.i) * (p3.j - p1.j);
00684   if (temp < 0)
00685   {
00686     res = 0;
00687   }
00688   else if (temp == 0)
00689   {
00690     int dist1 = (p2.i - p1.i) * (p2.i - p1.i) + (p2.j - p1.j) * (p2.j - p1.j);
00691     int dist2 = (p3.i - p1.i) * (p3.i - p1.i) + (p3.j - p1.j) * (p3.j - p1.j);
00692     if (dist1 > dist2)
00693       res = 0;
00694   }
00695   
00696   return res;
00697 }
00698 
00699 inline int CSceneRecog::srCross_Product (const CPointIndex & p1, const CPointIndex & p2, const CPointIndex & p3)
00700 {
00701   return (p2.j - p1.j) * (p3.i - p1.i) - (p2.i - p1.i) * (p3.j - p1.j);
00702 }