Basic Image AlgorithmS Library  2.8.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
TriangleMesh.cpp
1 /*
2  This file is part of the BIAS library (Basic ImageAlgorithmS).
3 
4  Copyright (C) 2003-2009 (see file CONTACT for details)
5  Multimediale Systeme der Informationsverarbeitung
6  Institut fuer Informatik
7  Christian-Albrechts-Universitaet Kiel
8 
9 
10  BIAS is free software; you can redistribute it and/or modify
11  it under the terms of the GNU Lesser General Public License as published by
12  the Free Software Foundation; either version 2.1 of the License, or
13  (at your option) any later version.
14 
15  BIAS is distributed in the hope that it will be usefulen,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  GNU Lesser General Public License for more details.
19 
20  You should have received a copy of the GNU Lesser General Public License
21  along with BIAS; if not, write to the Free Software
22  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23  */
24 
25 #include <Utils/TriangleMesh.hh>
26 #include <Base/Math/Vector.hh>
27 #include <Base/Math/Random.hh>
28 #include <Geometry/RMatrix.hh>
29 #include <MathAlgo/Lapack.hh>
30 #include <Base/Common/CompareFloatingPoint.hh>
31 #include <Filter/Gauss.hh>
32 #include <Base/Common/FileHandling.hh>
33 #include <Base/Image/ImageIO.hh>
34 #include <Base/Geometry/HomgPlane3D.hh>
35 #include <Filter/Rescale.hh>
36 #include <Base/Math/Quadric3.hh>
37 
38 #define MESH_OPTIMZER
39 #define USE_OPTIMIZED
40 #define USE_CONTRACTION_TEST
41 #define USE_BOUNDARY_TEST
42 
43 using namespace BIAS;
44 using namespace std;
45 
46 TriangleMesh::TriangleMesh(const double& minCornerAngle, const double& maxAngle,
47  const double depthScale) {
48  SetParams(minCornerAngle, maxAngle, depthScale);
49 }
50 
52  if (!texture.SamePixelAndChannelCount(texture_)) {
53  BIASERR("invalid texture size, not changing existing texture");
54  return -1;
55  }
56  texture_ = texture;
57  return 0;
58 }
59 
60 void TriangleMesh::SetParams(const double& minCornerAngle, const double& maxAngle,
61  const double depthScale) {
62  minCornerAngle_ = minCornerAngle;
63  maxAngle_ = maxAngle;
64  depthScale_ = depthScale;
65 }
66 
67 /* @return plane unit 1 normal
68  assuming RHS order of p1,p2,p3, e.g.
69  p1 p3
70  p2
71  @author Jan Woetzel 06/2003
72  */
73 inline BIAS::Vector<double> GetPlaneNormalEuclidean(const BIAS::Vector3<double> & p1,
74  const BIAS::Vector3<double> & p2,
75  const BIAS::Vector3<double> & p3) {
76  BIAS::Vector3<double> nvec; // normal unit 1 result vector
77  nvec = (p2 - p1).CrossProduct(p3 - p1);
78  if (Equal(nvec.NormL2(), 0.0)) {
79  //BIASERR("normalvector could not be calculated");
80  } else
81  nvec.Normalize(); // to length 1;
82  return nvec;
83 }
84 
85 /* @return radian angle between the two direction vectors
86  @author JW 06/2003
87  */
88 inline double GetAngle(const BIAS::Vector3<double> & vec1, const BIAS::Vector3<double> & vec2) {
89  double cosAlpha = (vec1.GetNormalized()).ScalarProduct(vec2.GetNormalized());
90  // we want an angle between 0 and Pi/2 rad (0..90 deg):
91  if (cosAlpha < 0) {
92  return (M_PI - acos(cosAlpha));
93  } else {
94  return acos(cosAlpha);
95  }
96 
97 }
98 
99 /** @return true if triangle(p1,p2,p3) corner angles are at least minPolyAngle
100 @author JW 06/2003
101 assume a RHS of p1,p2,p3, e.g.:
102 p1 p3
103 p2
104 @param minPolyAngle min. angle in rad of each polygion corner.
105 sorts out 'stretched' triangles with acute angles (spitze Winkel)
106 */
107 inline bool AcceptPolyCornerAngles(const BIAS::Vector3<double> & p1,
108  const BIAS::Vector3<double> & p2,
109  const BIAS::Vector3<double> & p3, const double minPolyAngle = 5.0
110  / 180.0 * M_PI) {
111  // check min. poly angles of corner p1, p2, p3 in RHS order:
112  if (GetAngle(p2 - p1, p3 - p1) < minPolyAngle)
113  return false; // angle at p1 too small
114  else if (GetAngle(p3 - p2, p1 - p2) < minPolyAngle)
115  return false; // angle at p2 too small
116  else if (GetAngle(p1 - p3, p2 - p3) < minPolyAngle)
117  return false; // angle at p3 too small
118  else
119  return true;
120 }
121 
122 /** @return true if the angel between plane view ray
123 and plane normal is not too big.
124 sorts out closing of object discontinuities/occlusions.
125 @param p1,p2,p3 triangle corners in RHS order
126 @param C virtual view camera center
127 @param maxAngle in rad between plane normal and ray of view, e.g. 89(deg).*(M_PI/180.0)
128 @author JW 06/2003 */
129 inline bool AcceptPolyPlanViewAngle(const BIAS::Vector3<double> & p1,
130  const BIAS::Vector3<double> & p2,
131  const BIAS::Vector3<double> & p3,
132  const BIAS::Vector3<double> & C, const double maxAngle = 89.0
133  / 180.0 * M_PI) {
134  // check angle between plane normal and ray of view from C to nw:
135  // view on p1 (may be any vertex)
136  double angle = GetAngle(p1 - C, GetPlaneNormalEuclidean(p1, p2, p3));
137  return (angle <= maxAngle);
138 }
139 
141  const BIAS::Image<unsigned char>& Texture) {
142  #ifdef BIAS_DEBUG
143  if( (DenseDepthMap.GetWidth()!=Texture.GetWidth()) ||
144  (DenseDepthMap.GetHeight()!=Texture.GetHeight()) ) {
145  BIASERR("Texturesize != DepthMapsize \n");
146  return -1;
147  }
148  #endif
149  //init datastructs
150  unsigned int w = DenseDepthMap.GetWidth();
151  unsigned int h = DenseDepthMap.GetHeight();
152  vector<int> pointIndices;
153  pointIndices.resize(w * h);
154 
155  meshVertices_.clear();
156  vertexNormals_.clear();
157  textureCoords_.clear();
158  triangleIndices_.clear();
159 
160  meshVertices_.reserve(w * h);
161  vertexNormals_.reserve(w * h);
162  textureCoords_.reserve(w * h);
163  triangleIndices_.reserve(w * h * 2);
164 
165  texture_ = Texture;
166 
167  //build list of indices and list of Vertices
168  float texCoordOffsetX = 1.0f / float(w - 1);
169  float texCoordOffsetY = 1.0f / float(h - 1);
170  Vector2<float> texCoord;
171  texCoord.Set(0.0, 1.0);
172 
173  Vector3<double> vertex;
174  HomgPoint3D vertexHom;
175 
176  const float** depthValues = DenseDepthMap.GetImageDataArray();
177  unsigned int currentIndex = 0;
178  unsigned int x, y;
179  for (y = 0; y < h; y++) {
180  for (x = 0; x < w; x++) {
181  if (depthValues[y][x] <= 0.0) {
182  pointIndices[y * w + x] = -1;
183  } else {
184  pointIndices[y * w + x] = currentIndex;
185  vertexHom = HomgPoint3D((double) x, (double) y, depthScale_ * (double) depthValues[y][x]);
186  vertexHom.GetEuclidean(vertex);
187  meshVertices_.push_back(vertex);
188  textureCoords_.push_back(texCoord);
189  currentIndex++;
190  }
191  texCoord[0] += texCoordOffsetX;
192  }
193  texCoord[0] = 0.0;
194  texCoord[1] -= texCoordOffsetY;
195  }
196 
197  //meshing
198  Vector3<int> triangle;
199  int index0, index1, index2;
200  for (y = 0; y < h; y++) {
201  for (x = 0; x < w - 1; x++) {
202  /* v1
203  * i0---i1
204  * | /
205  * | / v0
206  * i2
207  */
208  if (y < h - 1) {
209  index0 = pointIndices[y * w + x];
210  index1 = pointIndices[y * w + x + 1];
211  index2 = pointIndices[(y + 1) * w + x];
212  if (index0 > (-1) && index1 > (-1) && index2 > (-1)) {
213  triangle.Set(index2, index1, index0);
214  if (AcceptPolyCornerAngles(meshVertices_[index0], meshVertices_[index1],
215  meshVertices_[index2], minCornerAngle_))
216  triangleIndices_.push_back(triangle);
217  }
218  }
219  /*
220  * i0
221  * / |
222  * / |
223  * i2--i1
224  */
225  if (y > 0) {
226  index2 = pointIndices[y * w + x];
227  index1 = pointIndices[y * w + x + 1];
228  index0 = pointIndices[(y - 1) * w + x + 1];
229  if (index0 > (-1) && index1 > (-1) && index2 > (-1)) {
230  triangle.Set(index2, index1, index0);
231  if (AcceptPolyCornerAngles(meshVertices_[index0], meshVertices_[index1],
232  meshVertices_[index2], minCornerAngle_))
233  triangleIndices_.push_back(triangle);
234  }
235  }
236  }
237 
238  // cout<<"vertices "<<meshVertices_.size()<<endl;
239  // cout<<"texCoords "<<textureCoords_.size()<<endl;
240  // cout<<"triangles "<<triangleIndices_.size()<<endl;
241 
242  }
243 
244  if (triangleIndices_.empty())
245  return -1;
246  return 0;
247 
248 }
249 
251  BIAS::Projection& P,
252  const BIAS::Image<unsigned char>& Texture,
253  BIAS::Projection& TexP,
254  bool reduce,
255  double reduceValue){
256 
257  GenerateDenseMesh(DenseDepthMap,P,Texture,P);
258  if(reduce){
259 #ifdef MESH_OPTIMZER
260  TriangleMesh::SimplifyMeshSurface_(Texture, TexP, reduceValue);
261 #endif
262  }
263  return 0;
264 }
265 
267  BIAS::PMatrix& P,
268  const BIAS::Image<unsigned char>& Texture) {
270  ppp.SetImageSize(Texture.GetWidth(), Texture.GetHeight());
271  ppp.SetP(P);
272  Projection pP(ppp);
273  return GenerateDenseMesh(DenseDepthMap, pP, Texture, pP);
274 }
275 
277  BIAS::Projection& P,
278  const BIAS::Image<unsigned char>& Texture,
279  BIAS::Projection& TexP,
280  const unsigned int distBetweenPoints) {
281  unsigned int w = DenseDepthMap.GetWidth();
282  unsigned int h = DenseDepthMap.GetHeight();
283  return GenerateDenseMesh(DenseDepthMap, P, Texture, TexP, 0, 0, w, h, distBetweenPoints);
284 }
285 
286 Vector2<float> ImageToTexCoordinates(float x0, float y0, unsigned int w, unsigned int h) {
287  float texCoordOffsetX = 1.0f / float(w - 1);
288  float texCoordOffsetY = 1.0f / float(h - 1);
289  Vector2<float> texCoord;
290  texCoord.Set(x0 * texCoordOffsetX, 1.0f - y0 * texCoordOffsetY);
291  return texCoord;
292 }
293 
295  BIAS::Projection& P,
296  const BIAS::Image<unsigned char>& Texture,
297  BIAS::Projection& TexP, const unsigned int x0,
298  const unsigned int y0, const unsigned int width,
299  const unsigned int height,
300  const unsigned int distBetweenPoints) {
301  Vector3<double> C = P.GetC();
302 
303  if (width <= 0 || height <= 0) {
304  BIASERR("tile width is too small!");
305  return -1;
306  }
307 
308  //init datastructs
309  //unsigned int dw=DenseDepthMap.GetWidth();
310  //unsigned int dh=DenseDepthMap.GetHeight();
311  unsigned int tw = Texture.GetWidth();
312  unsigned int th = Texture.GetHeight();
313  vector<int> pointIndices;
314  pointIndices.resize(width * height);
315 
316  meshVertices_.clear();
317  vertexNormals_.clear();
318  textureCoords_.clear();
319  triangleIndices_.clear();
320 
321  meshVertices_.reserve(width * height);
322  vertexNormals_.reserve(width * height);
323  textureCoords_.reserve(width * height);
324  triangleIndices_.reserve(width * height * 2);
325 
326  texture_ = Texture;
327 
328  //build list of indices and list of Vertices
329  // float texCoordOffsetX = 1.0f / float(tw-1);
330  // float texCoordOffsetY = 1.0f / float(th-1);
331  Vector2<float> texCoord;
332  // texCoord.Set(x0 * texCoordOffsetX, 1.0f - y0 * texCoordOffsetY);
333 
334  Vector3<double> vertex;
335 
336  const float** depthValues = DenseDepthMap.GetImageDataArray();
337  unsigned int currentIndex = 0;
338  unsigned int x, y;
339 
340  for (y = 0; y < height; y++) {
341  for (x = 0; x < width; x++) {
342  if (depthValues[y0 + y][x0 + x] < 1e-5) {
343  pointIndices[y * width + x] = -1;
344  } else {
345  // BIASASSERT(depthValues[y][x]>1e-5);
346  vertex = P.UnProjectToPoint(HomgPoint2D(x + x0, y + y0), depthScale_
347  * depthValues[y + y0][x0 + x]);
348  // BIASASSERT((vertex-P.GetC()).NormL2()>1e-10)
349  if (vertex.NormL2() > 1e-10) {
350  //generate texture coordinates
351  HomgPoint2D uv;
352  HomgPoint3D p3dh(vertex);
353  if (TexP.DoesPointProjectIntoImage(p3dh, uv)) {
354  double u = uv[0] / float(tw - 1);
355  double v = 1 - uv[1] / float(th - 1);
356  texCoord.Set(float(u), float(v));
357  } //... or else?
358 
359  meshVertices_.push_back(vertex);
360  textureCoords_.push_back(texCoord);
361  pointIndices[y * width + x] = currentIndex;
362  currentIndex++;
363  } else {
364  pointIndices[y * width + x] = -1;
365  //cout<<"***vertex invalid***"<<currentIndex<<" "<<endl;
366  }
367  }
368  BIASASSERT(meshVertices_.size()==currentIndex);
369  // texCoord[0] += texCoordOffsetX;
370  if (!meshVertices_.empty()) {
371  BIASASSERT(meshVertices_.back().NormL2()>1e-10);
372  }
373 
374  }
375  }
376  //cout<<"size of vertex array is "<<meshVertices_.size()<<endl;
377  //meshing
378  Vector3<int> triangle;
379  int index0, index1, index2;
380  int rejected = 0;
381  for (y = 0; y < height; y += distBetweenPoints) {
382  for (x = 0; x < width - 1; x += distBetweenPoints) {
383  /* v1
384  * i0---i1
385  * | /
386  * | / v0
387  * i2
388  */
389  if (y < height - 1) {
390  index0 = pointIndices[y * width + x];
391  index1 = pointIndices[y * width + x + 1];
392  index2 = pointIndices[(y + 1) * width + x];
393  if (index0 > (-1) && index1 > (-1) && index2 > (-1)) {
394 
395  //BIASASSERT(index1<meshVertices_.size());
396  //BIASASSERT(index2<meshVertices_.size());
397  //BIASASSERT(index0<meshVertices_.size());
398 
399  //BIASASSERT((meshVertices_[index0]-meshVertices_[index1]).NormL2()>1e-10);
400  //BIASASSERT((meshVertices_[index2]-meshVertices_[index1]).NormL2()>1e-10);
401  //BIASASSERT((meshVertices_[index0]-meshVertices_[index2]).NormL2()>1e-10);
402  //BIASASSERT(meshVertices_[index0].NormL2()>1e-10);
403  //BIASASSERT(meshVertices_[index1].NormL2()>1e-10);
404  //BIASASSERT(meshVertices_[index2].NormL2()>1e-10);
405 
406  triangle.Set(index2, index1, index0);
407  // triangleIndices_.push_back(Vector<unsigned int>(triangle));
408  if (AcceptPolyCornerAngles(meshVertices_[index0], meshVertices_[index1],
409  meshVertices_[index2], minCornerAngle_)
410  && AcceptPolyPlanViewAngle(meshVertices_[index0], meshVertices_[index1],
411  meshVertices_[index2], C, maxAngle_)) {
412  triangleIndices_.push_back(triangle);
413  } else {
414  rejected++;
415  }
416  }
417  }
418  /*
419  * i0
420  * / |
421  * / |
422  * i2--i1
423  */
424  if (y > 0) {
425  index2 = pointIndices[y * width + x];
426  index1 = pointIndices[y * width + x + 1];
427  index0 = pointIndices[(y - 1) * width + x + 1];
428  if (index0 > (-1) && index1 > (-1) && index2 > (-1)) {
429  triangle.Set(index2, index1, index0);
430  // triangleIndices_.push_back(Vector<unsigned int>(triangle));
431  if (AcceptPolyCornerAngles(meshVertices_[index0], meshVertices_[index1],
432  meshVertices_[index2], minCornerAngle_)
433  && AcceptPolyPlanViewAngle(meshVertices_[index0], meshVertices_[index1],
434  meshVertices_[index2], C, maxAngle_)) {
435  triangleIndices_.push_back(triangle);
436  } else {
437  rejected++;
438  }
439  }
440  }
441  }
442  }
443 
444  // cout<<"vertices "<<meshVertices_.size()<<endl;
445  // cout<<"texCoords "<<textureCoords_.size()<<endl;
446  // cout<<"triangles "<<triangleIndices_.size()<<endl;
447  // cout<<"rejected "<<rejected<<" triangles due to invalid angles\n";
448  if (triangleIndices_.empty()) {
449  //BIASERR("no triangles generated");
450  return -1;
451  }
452 
453  return 0;
454 }
455 
456 
458  PMatrix& P,
459  const Image<unsigned char>& TextureC,
460  const ROI& CutOut) {
461  // dirty de-const cast to conveniently access ROI, which is restored later
462  Image<float>& DenseDepthMap(const_cast<Image<float>&> (DenseDepthMapC));
463  Image<unsigned char>& Texture(const_cast<Image<unsigned char>&> (TextureC));
464 
465  int UpperLeftX, UpperLeftY, LowerRightX, LowerRightY;
466  CutOut.GetCorners(UpperLeftX, UpperLeftY, LowerRightX, LowerRightY);
467 
468  // adapt pmatrix principal point
469  PMatrix PCutOut(P);
470  if (PCutOut.IsMetric()) {
471  // more exact to use saved K,R,C and recompose than multiplying
472  KMatrix K = PCutOut.GetK();
473  K[0][2] -= double(UpperLeftX);
474  K[1][2] -= double(UpperLeftY);
475  RMatrix R = PCutOut.GetR();
476  Vector3<double> C(PCutOut.GetC());
477  PCutOut.Compose(K, R, C);
478  } else {
479  // non-metrix case, just multiply with correction matrix
481  K[0][2] -= double(UpperLeftX);
482  K[1][2] -= double(UpperLeftY);
483  PCutOut = K * PCutOut;
484 
485  // tell PMatrix that a basic matrix operation has been invoked
486  // and chache is invalid
487  PCutOut.InvalidateDecomposition();
488  }
489  // PCutout is now the new projection matrix for the cutout
490 
491  // get CutOut from texture
492  ROI backupROI = *Texture.GetROI();
493  Texture.SetROICorners(UpperLeftX, UpperLeftY, LowerRightX, LowerRightY);
494  Image<unsigned char> CutOutTexture;
495  Texture.GetCopyOfROI(CutOutTexture);
496  *Texture.GetROI() = backupROI;
497 
498  // get CutOut from depth map
499  backupROI = *DenseDepthMap.GetROI();
500  DenseDepthMap.SetROICorners(UpperLeftX, UpperLeftY, LowerRightX, LowerRightY);
501  Image<float> CutOutDenseDepthMap;
502  DenseDepthMap.GetCopyOfROI(CutOutDenseDepthMap);
503  *DenseDepthMap.GetROI() = backupROI;
504 
505  // call generic interface
506  return GenerateDenseMesh(CutOutDenseDepthMap, PCutOut, CutOutTexture);
507 }
508 
510  const BIAS::Image<unsigned char>& Texture,
511  const double& w) {
512 
513  if (typeid(*P.GetParameters()) != typeid(ProjectionParametersPerspective)) {
514  BIASWARN("Interpolation of texture coordinates not yet implemented !");
515  }
516  HomgPoint2D x(0.0, 0.0);
517  Vector2<float> texcoord(0.0, 1.0);
518  for (unsigned int i = 0; i < 4; i++) {
519  switch (i) {
520  case 1: {
521  x = HomgPoint2D(Texture.GetWidth() - 1, 0);
522  texcoord = Vector2<float> (1.0, 1.0);
523  break;
524  }
525  case 2: {
526  x = HomgPoint2D(0, Texture.GetHeight() - 1);
527  texcoord = Vector2<float> (0.0, 0.0);
528  break;
529  }
530  case 3: {
531  x = HomgPoint2D(Texture.GetWidth() - 1, Texture.GetHeight() - 1);
532  texcoord = Vector2<float> (1.0, 0.0);
533  break;
534  }
535  }
536  Vector3<double> eucl;
538  meshVertices_.push_back(eucl);
539  textureCoords_.push_back(texcoord);
540  }
541  Vector3<int> triangle;
542  // two triangles
543  triangle.Set(0, 1, 2);
544  triangleIndices_.push_back(triangle);
545  triangle.Set(1, 2, 3);
546  triangleIndices_.push_back(triangle);
547  texture_ = Texture;
548 
549  return 0;
550 }
551 
552 
554  Image<unsigned char>& rectB,
555  const double& scale,
556  const double& opacity,
557  const double& resolution) {
558 
559  ProjectionParametersBase* newparams = rectPPB->Clone();
560  if (resolution != 1.0)
561  newparams->Rescale(float(resolution));
562 
563  BIASASSERT(opacity<=1.0)
564  unsigned int width = 0, height = 0;
565  newparams->GetImageSize(width, height);
566  Image<float> depthmap(width, height, 1);
567  depthmap.SetZero();
568  for (unsigned int y = 0; y < height; y++) {
569  for (unsigned int x = 0; x < width; x++) {
570  HomgPoint2D p2(x, y);
571  HomgPoint3D p3 = newparams->UnProjectToImagePlane(p2, scale);
572  if (p3.NormL2() < 0.1)
573  continue;
574  depthmap.GetImageDataArray()[y][x] = (float) (p3 - HomgPoint3D(rectPPB->GetC())).NormL2();
575 
576  }
577  }
578 
579  BIASASSERT(rectB.GetChannelCount()==3);
581  Image<unsigned char> rect(width, height, rectB.GetChannelCount());
582  R.SetFactor(resolution);
583  R.Filter(rectB, rect);
584 
585  Projection Proj(*newparams);
586 
587  Image<unsigned char> rectAlpha(rect.GetWidth(), rect.GetHeight(), 4, true);
588 
590  for (unsigned int y = 0; y < rectAlpha.GetHeight(); y++) {
591  for (unsigned int x = 0; x < rectAlpha.GetWidth(); x++) {
592  unsigned char *pD = &rect.GetImageDataArray()[y][3 * x];
593  unsigned char *pDA = &rectAlpha.GetImageDataArray()[y][4 * x];
594  *pDA++ = *pD++;
595  *pDA++ = *pD++;
596  *pDA++ = *pD++;
597  *pDA++ = (unsigned char) (255.0 * opacity);
598  }
599  }
600 
601  GenerateDenseMesh(depthmap, Proj, rectAlpha, Proj);
602  delete newparams;
603 }
604 
605 /* take 5 points: 4 corners + center, calculate normals for 4 triangles,
606  compare normals and if one angle is above threshold, refine recursivly.
607  If depth values of one point is missing, refine, too
608  */
610  const BIAS::Image<unsigned char>& Texture,
611  std::vector<Image<float> > & depthmaps, float x, float y,
612  float offset, float minoffset, double minCosAngle,
613  double maxDiagRatio, bool simulateOnly) {
614 
615  const int maxlayer = depthmaps.size() - 1;
616  int step = int(rint(log(offset) / log(2.0))) - 2;
617  if (step > maxlayer)
618  step = maxlayer;
619  if (step < 0)
620  step = 0;
621 
622  Image<float> &depthmap = depthmaps[step];
623  HomgPoint2D S[5];
624  Vector3<double> qp[5];
625  Vector3<double> normal[4];
626  bool vetoed = true;
627  unsigned int FoundDepthValues = 0;
628  unsigned int c;
629  float** depthValues = depthmap.GetImageDataArray();
630  for (c = 0; c < 5; c++) {
631  S[c][2] = 1.0;
632  switch (c) {
633  case 0:
634  S[c][0] = x;
635  S[c][1] = y;
636  break;
637  case 1:
638  S[c][0] = x - offset;
639  S[c][1] = y - offset;
640  break;
641  case 2:
642  S[c][0] = x - offset;
643  S[c][1] = y + offset;
644  break;
645  case 3:
646  S[c][0] = x + offset;
647  S[c][1] = y + offset;
648  break;
649  case 4:
650  S[c][0] = x + offset;
651  S[c][1] = y - offset;
652  break;
653  }
654  double depth = depthValues[int(S[c][1])][int(S[c][0])];
655  if (depth < 1e-10) {
656  // invalid depth
657  break;
658  }
659 
660  HomgPoint3D backProjected;
661  backProjected = P.GetParameters()->UnProjectToPoint(S[c], depthScale_
662  * depthValues[int(S[c][1])][int(S[c][0])]);
663  if (backProjected.NormL2() < 1e-10) {
664  // point is not backprojectable, e.g. out of sphere circle
665  break;
666  //cout<<"***vertex invalid***"<<currentIndex<<" "<<endl;
667  }
668  backProjected.GetEuclidean(qp[c]);
669  FoundDepthValues++;
670  } // for c=1 to 5
671 
672  // check if loop terminated normaly or break was called
673  double cosang[6];
674  double diag1;
675  double diag2, diagratio;
676 
677  bool passedMinAngleTest = true;
678 
679  Vector3<double> meannormal(0, 0, 0);
680  Vector3<double> LoS;
681  // add all single-pixel quads
682  if (FoundDepthValues == 5) {
683  vetoed = false;
684  // calculate 4 normals
685  (qp[1] - qp[0]).CrossProduct((qp[2] - qp[0]), normal[0]);
686  (qp[2] - qp[0]).CrossProduct((qp[3] - qp[0]), normal[1]);
687  (qp[3] - qp[0]).CrossProduct((qp[4] - qp[0]), normal[2]);
688  (qp[4] - qp[0]).CrossProduct((qp[1] - qp[0]), normal[3]);
689  for (unsigned int i = 0; i < 4; i++) {
690  double mynorm = normal[i].NormL2();
691  if (mynorm < 1e-4)
692  mynorm = 1e-4;
693  normal[i] /= mynorm;
694  meannormal += normal[i];
695  }
696  meannormal /= 4.0;
697 
698  // compare normals
699 
700  normal[0].ScalarProduct(normal[1], cosang[0]);
701  normal[0].ScalarProduct(normal[2], cosang[1]);
702  normal[0].ScalarProduct(normal[3], cosang[2]);
703  normal[1].ScalarProduct(normal[2], cosang[3]);
704  normal[1].ScalarProduct(normal[3], cosang[4]);
705  normal[2].ScalarProduct(normal[3], cosang[5]);
706  for (unsigned int c = 0; c < 6; c++) {
707  if (cosang[c] < minCosAngle)
708  vetoed = true;
709  }
710  // 25 deg: 0.9, 18 deg: 0.95, 8 deg: 0.96 5 deg: 0.996
711  diag1 = (qp[1] - qp[3]).NormL2();
712  diag2 = (qp[2] - qp[4]).NormL2();
713  diagratio = diag1 > diag2 ? diag1 / diag2 : diag2 / diag1;
714  if (diagratio > maxDiagRatio) {
715  vetoed = true;
716  }
717  LoS = qp[0] - P.GetC();
718  LoS.Normalize();
719  if (GetAngle(meannormal, LoS) > maxAngle_) {
720  // too bad viewing angle
721  FoundDepthValues = 0; // reject even if smallest size !
722  }
723  if((!AcceptPolyCornerAngles(qp[0], qp[1], qp[2], minCornerAngle_)) ||
724  (!AcceptPolyCornerAngles(qp[0], qp[2], qp[3], minCornerAngle_)) ||
725  (!AcceptPolyCornerAngles(qp[0], qp[3], qp[4], minCornerAngle_)) ||
726  (!AcceptPolyCornerAngles(qp[0], qp[4], qp[1], minCornerAngle_))){
727  passedMinAngleTest = false;
728  }
729  }
730  if(passedMinAngleTest){
731  if ((FoundDepthValues == 5) && ((!vetoed) || offset <= minoffset)) {
732 
733  float newx, newy, newoff;
734  newoff = offset / 2;
735  newx = x - newoff;
736  newy = y - newoff;
737 
738  if ((newoff <= minoffset) && simulateOnly)
739  return true;
740 
741  bool result = true;
742 
743  MakeTriangles_(P, TexP, Texture, depthmaps, newx, newy, newoff, minoffset, minCosAngle,
744  maxDiagRatio, true);
745  newy = y + newoff;
746  result &= MakeTriangles_(P, TexP, Texture, depthmaps, newx, newy, newoff, minoffset,
747  minCosAngle, maxDiagRatio, true);
748  newx = x + newoff;
749  result &= MakeTriangles_(P, TexP, Texture, depthmaps, newx, newy, newoff, minoffset,
750  minCosAngle, maxDiagRatio, true);
751  newy = y - newoff;
752  result &= MakeTriangles_(P, TexP, Texture, depthmaps, newx, newy, newoff, minoffset,
753  minCosAngle, maxDiagRatio, true);
754 
755  if (simulateOnly)
756  return result;
757 
758  if (result) {
759 
760  //cout<<"allow at "<<newoff<<endl;
761  }
762 
763  if (result || (newoff <= minoffset)) {
764  // we are allowed to place the quad !
765  // add this quad to vertarray
766  vector<int> indices;
767  for (unsigned int c = 1; c < 5; c++) {
768  int found = -1;
769  // Vector2<float> thecoords(ImageToTexCoordinates(float(S[c][0]),
770  // float(S[c][1]),
771  // depthmap.GetWidth(),
772  // depthmap.GetHeight()));
773 
774  //generate texture coordinates
775  HomgPoint2D uv;
776  HomgPoint3D p3dh(qp[c]);
777  Vector2<float> thecoords;
778  unsigned int tw = Texture.GetWidth();
779  unsigned int th = Texture.GetHeight();
780  if (TexP.DoesPointProjectIntoImage(p3dh, uv)) {
781  thecoords[0] = float(uv[0]) / float(tw - 1);
782  thecoords[1] = 1.0f - float(uv[1]) / float(th - 1);
783  } else {
784  BIASWARNONCE("point does not project into texture!!!!");
785  }
786 
787  //create texture coordinates
788  for (unsigned int f = 0; f < textureCoords_.size(); f++) {
789  if (textureCoords_[f][0] == thecoords[0] && textureCoords_[f][1] == thecoords[1]) {
790  found = f;
791  break;
792  }
793  }
794  if (found > -1) {
795  // this vertex already exists ...
796  indices.push_back(found);
797  } else {
798  meshVertices_.push_back(qp[c]);
799  textureCoords_.push_back(thecoords);
800  indices.push_back(currentIndex_++);
801  }
802  }
803 
804  Vector3<int> triangle;
805  //triangle.Set(currentIndex_-4, currentIndex_-3, currentIndex_-2);
806  triangle.Set(indices[0], indices[1], indices[2]);
807  triangleIndices_.push_back(triangle);
808  //triangle.Set(currentIndex_-2, currentIndex_-1, currentIndex_-4);
809  triangle.Set(indices[2], indices[3], indices[0]);
810  triangleIndices_.push_back(triangle);
811 
812  HomgPlane3D thePlaneA;
813  thePlaneA.SetFromPoints(HomgPoint3D(qp[1]), HomgPoint3D(qp[2]), HomgPoint3D(qp[3]));
814  int x = int(S[0][0] - offset);
815  int y = 0;
816  Vector3<double> p, dir;
817  for (y = int(S[0][1] - offset); y <= int(S[0][1] + offset); y++) {
818  P.UnProjectToRay(HomgPoint2D(x, y), p, dir);
819  HomgPoint3D Ray(dir);
820  Ray[3] = 0;
821  HomgPoint3D Intersection(0, 0, 0, 0);
822  thePlaneA.GetLineIntersection(HomgPoint3D(P.GetC()), Ray, Intersection);
823  if (depthmaps[0].IsPositionInImage(x, y))
824  for (unsigned int i = 0; i < depthmaps.size(); i++) {
825  depthmaps[i].GetImageDataArray()[y][x] = float((HomgPoint3D(P.GetC())
826  - Intersection).NormL2());
827  }
828  }
829  y--;
830  for (x = int(S[0][0] - offset); x <= int(S[0][0] + offset); x++) {
831  P.UnProjectToRay(HomgPoint2D(x, y), p, dir);
832  HomgPoint3D Ray(dir);
833  Ray[3] = 0;
834  HomgPoint3D Intersection(0, 0, 0, 0);
835  thePlaneA.GetLineIntersection(HomgPoint3D(P.GetC()), Ray, Intersection);
836  if (depthmaps[0].IsPositionInImage(x, y))
837  for (unsigned int i = 0; i < depthmaps.size(); i++) {
838  depthmaps[i].GetImageDataArray()[y][x] = float((HomgPoint3D(P.GetC())
839  - Intersection).NormL2());
840  }
841  }
842  x--;
843  thePlaneA.SetFromPoints(HomgPoint3D(qp[3]), HomgPoint3D(qp[4]), HomgPoint3D(qp[1]));
844  for (; y >= int(S[0][1] - offset); y--) {
845  P.UnProjectToRay(HomgPoint2D(x, y), p, dir);
846  HomgPoint3D Ray(dir);
847  Ray[3] = 0;
848  HomgPoint3D Intersection(0, 0, 0, 0);
849  thePlaneA.GetLineIntersection(HomgPoint3D(P.GetC()), Ray, Intersection);
850  if (depthmaps[0].IsPositionInImage(x, y))
851  for (unsigned int i = 0; i < depthmaps.size(); i++) {
852  depthmaps[i].GetImageDataArray()[y][x] = float((HomgPoint3D(P.GetC())
853  - Intersection).NormL2());
854  }
855  }
856  y++;
857  for (; x >= int(S[0][0] - offset); x--) {
858  P.UnProjectToRay(HomgPoint2D(x, y), p, dir);
859  HomgPoint3D Ray(dir);
860  Ray[3] = 0;
861  HomgPoint3D Intersection(0, 0, 0, 0);
862  thePlaneA.GetLineIntersection(HomgPoint3D(P.GetC()), Ray, Intersection);
863  if (depthmaps[0].IsPositionInImage(x, y))
864  for (unsigned int i = 0; i < depthmaps.size(); i++) {
865  depthmaps[i].GetImageDataArray()[y][x] = float((HomgPoint3D(P.GetC())
866  - Intersection).NormL2());
867  }
868  }
869  return true;
870  }
871  }
872  if (offset > (minoffset)) {
873  if (simulateOnly)
874  return false;
875  float newx, newy, newoff;
876  newoff = offset / 2;
877  newx = x - newoff;
878  newy = y - newoff;
879 
880  MakeTriangles_(P, TexP, Texture, depthmaps, newx, newy, newoff, minoffset, minCosAngle,
881  maxDiagRatio, false);
882  newy = y + newoff;
883  MakeTriangles_(P, TexP, Texture, depthmaps, newx, newy, newoff, minoffset, minCosAngle,
884  maxDiagRatio, false);
885  newx = x + newoff;
886  MakeTriangles_(P, TexP, Texture, depthmaps, newx, newy, newoff, minoffset, minCosAngle,
887  maxDiagRatio, false);
888  newy = y - newoff;
889  MakeTriangles_(P, TexP, Texture, depthmaps, newx, newy, newoff, minoffset, minCosAngle,
890  maxDiagRatio, false);
891 
892  return false;
893  }
894  }
895  return true;
896 }
897 
898 
899 int TriangleMesh::
901  const BIAS::Projection& P,
902  const BIAS::Image<unsigned char>& Texture,
903  const Projection& TexP,
904  const unsigned int x0, const unsigned int y0,
905  const unsigned int width, const unsigned int height,
906  double minCosAngle,
907  double maxDiagRatio)
908 {
909  // some sanity checks
910  unsigned mwidth, mheight;
911  BIASASSERT(P.GetParameters());
912  P.GetParameters()->GetImageSize(mwidth, mheight);
913  BIASASSERT(DenseDepthMap.GetWidth()==mwidth &&
914  DenseDepthMap.GetHeight()==mheight);
915  BIASASSERT(width==mwidth && height==mheight);
916  BIASASSERT(TexP.GetParameters());
917  TexP.GetParameters()->GetImageSize(mwidth, mheight);
918  BIASASSERT(Texture.GetWidth()==mwidth &&
919  Texture.GetHeight()==mheight);
920 
921  // check whether straight lines in space map to straight lines in the image
922  // if not, we may get holes
924  dynamic_cast<const ProjectionParametersPerspective *> (P.GetParameters());
925 
926  // from all our cam models, only the perspectives preserve straight lines
927  bool straightlinesPreserved = (ppp != NULL);
928  if (ppp != NULL) {
929  // and only if they dont contain distortion
930  straightlinesPreserved &= (!ppp->IsDistorted());
931  }
932  if (!straightlinesPreserved) {
933  BIASWARN("Your projection does not relate straight lines in the image to "
934  "straight lines in space, which leads to the problem that "
935  "the 3D triangle tesselation may contain holes, since it is done "
936  "in the image. ");
937  // TODO !!!
938  // here it would be best to cube-map the depth (in case of wide-angle
939  // fisheye) or if fov<120 to map the depth onto an ideal perspective
940 
941  // for (cubeface=1 to 5 do)
942  // P.GetIdealK();
943  // ProjectionMapping.map(origdepth, idealdepth);
944 
945  // then, in the code below, simply make distinction between texture and
946  // depth when computing texture coordinates and generate trianglemesh
947  // for each cubeface
948 
949  }
950 
951  currentIndex_ = 0;
952  Vector3<double> C = P.GetC();
953  // if( ( (DenseDepthMap.GetWidth()!=Texture.GetWidth()) ||
954  // (DenseDepthMap.GetHeight()!=Texture.GetHeight()) )
955  // && ( !(Texture.IsEmpty()) )
956  // ) {
957  // BIASWARN("Texturesize != DepthMapsize \n");
958  // }
959  if (width <= 0 || height <= 0) {
960  BIASERR("tile width is too small!");
961  return -1;
962  }
963 
964  vector<Image<float> > depthimages;
965 
966  int maxlevel = int(log(double(DenseDepthMap.GetWidth())) / log(2.0) - 3);
967  int spacing = int(pow(2.0, maxlevel));
968  cout << "maximum level is " << maxlevel << " spacing is " << spacing << endl;
969  depthimages.reserve(maxlevel + 1);
970  depthimages.push_back(DenseDepthMap);
971 
972  Gauss<float, float> DepthSmooth;
973  bool smoothOriginal = true;
974  if (smoothOriginal) {
975  DepthSmooth.SetSigma(1.0);
976  depthimages.back().SetZero();
977  DepthSmooth.Filter7x7GreyIgnoreBelowThreshold(DenseDepthMap, depthimages.back(), 0.0f);
978  }
979 
980  double curSigma = 0.5;
981  for (int curLevel = 0; curLevel < maxlevel; curLevel++) {
982  double neededSigma = sqrt(pow(2.0, double(2 * curLevel)) - curSigma * curSigma);
983  cout << "Filtering with sigma " << neededSigma << endl;
984  curSigma = pow(2.0, double(curLevel));
985  DepthSmooth.SetSigma(neededSigma);
986  depthimages.push_back(depthimages.back());
987  depthimages.back().SetZero();
988  DepthSmooth.Filter7x7GreyIgnoreBelowThreshold(*(depthimages.rbegin() + 1),
989  depthimages.back(), 0.0f);
990  }
991  // for (int curLevel = 0; curLevel<maxlevel; curLevel++) {
992  // ImageIO::Write("depth"+toString(curLevel),depthimages[curLevel]);
993  // }
994 
995  meshVertices_.clear();
996  vertexNormals_.clear();
997  textureCoords_.clear();
998  triangleIndices_.clear();
999 
1000  meshVertices_.reserve(width * height);
1001  vertexNormals_.reserve(width * height);
1002  textureCoords_.reserve(width * height);
1003  triangleIndices_.reserve(width * height * 2);
1004 
1005  texture_ = Texture;
1006 
1007  unsigned int x, y;
1008  int minoffset = 2;
1009  for (y = spacing + 1; y < height - spacing; y += spacing) {
1010  for (x = spacing + 1; x < width - spacing; x += spacing) {
1011  MakeTriangles_(P, TexP, Texture, depthimages, float(x), float(y), float(spacing),
1012  float(minoffset), minCosAngle, maxDiagRatio, false);
1013  }
1014  }
1015 
1016  bool multiPass = true;
1017  if (multiPass) {
1018  for (int curLevel = 2; curLevel < maxlevel; curLevel++) {
1019  cout << "pass " << curLevel << " of " << maxlevel - 1 << " ..." << flush << endl;
1020  meshVertices_.clear();
1021  vertexNormals_.clear();
1022  textureCoords_.clear();
1023  triangleIndices_.clear();
1024  currentIndex_ = 0;
1025  // second path with consistent depth maps
1026  for (y = spacing + 1; y < height - spacing; y += spacing) {
1027  for (x = spacing + 1; x < width - spacing; x += spacing) {
1028  MakeTriangles_(P, TexP, Texture, depthimages, float(x), float(y), float(spacing),
1029  float(minoffset), minCosAngle, maxDiagRatio, false);
1030 
1031  }
1032  }
1033  }
1034  }
1035 
1036  // cout<<"vertices "<<meshVertices_.size()<<endl;
1037  // cout<<"texCoords "<<textureCoords_.size()<<endl;
1038  // cout<<"triangles "<<triangleIndices_.size()<<endl;
1039  // cout<<"rejected "<<rejected<<" triangles due to invalid angles\n";
1040  if (triangleIndices_.empty()) {
1041  BIASERR("no triangles generated");
1042  return -1;
1043  }
1044  return 0;
1045 }
1046 
1047 
1048 int TriangleMesh::
1050  const Vector3<double>& UL, const Vector3<double>& UR,
1051  const Vector3<double>& LL, const Vector3<double>& LR,
1052  const Vector2<double>& ul, const Vector2<double>& ur,
1053  const Vector2<double>& ll, const Vector2<double>& lr,
1054  const Vector3<double>& normal)
1055 {
1056  const bool doTexture = Texture.GetWidth() > 0;
1057  const float w = Texture.GetWidth() - 1.0f;
1058  const float h = Texture.GetHeight() - 1.0f;
1059 
1060  meshVertices_.clear();
1061  vertexNormals_.clear();
1062  textureCoords_.clear();
1063  triangleIndices_.clear();
1064 
1065  meshVertices_.push_back(UL);
1066  meshVertices_.push_back(UR);
1067  meshVertices_.push_back(LL);
1068  meshVertices_.push_back(LR);
1069 
1070  const double normalLength = normal.NormL2();
1071  if (BIAS::Equal(normalLength, 1.0)) {
1072  for (unsigned int i = 0; i < meshVertices_.size(); i++)
1073  vertexNormals_.push_back(normal);
1074  }
1075 
1076  if (doTexture)
1077  {
1078  textureCoords_.push_back(Vector2<float> ((float) ul[0] / w, 1.0f - (float) ul[1] / h));
1079  textureCoords_.push_back(Vector2<float> ((float) ur[0] / w, 1.0f - (float) ur[1] / h));
1080  textureCoords_.push_back(Vector2<float> ((float) ll[0] / w, 1.0f - (float) ll[1] / h));
1081  textureCoords_.push_back(Vector2<float> ((float) lr[0] / w, 1.0f - (float) lr[1] / h));
1082  texture_ = Texture;
1083  }
1084 
1085  Vector<int> triangle(3);
1086  triangle[0] = 0;
1087  triangle[1] = 1;
1088  triangle[2] = 2;
1089  triangleIndices_.push_back(triangle);
1090  triangle[0] = 1;
1091  triangle[1] = 3;
1092  triangle[2] = 2;
1093  triangleIndices_.push_back(triangle);
1094 
1095  return 0;
1096 }
1097 
1098 
1099 int TriangleMesh::
1101  const Vector3<double>& UL, const Vector3<double>& UR,
1102  const Vector3<double>& LL, const Vector3<double>& LR,
1103  const Vector3<double>& normal)
1104 {
1105  const float w = Texture.GetWidth() - 1.0f;
1106  const float h = Texture.GetHeight() - 1.0f;
1107 
1108  HomgPoint2D ul(P * HomgPoint3D(UL));
1109  HomgPoint2D ur(P * HomgPoint3D(UR));
1110  HomgPoint2D ll(P * HomgPoint3D(LL));
1111  HomgPoint2D lr(P * HomgPoint3D(LR));
1112 
1113  if (P.NormFrobenius() < 1e-9) {
1114  ul.Set(0, 0);
1115  ur.Set(w, 0);
1116  ll.Set(0, h);
1117  lr.Set(w, h);
1118  } else {
1119  ul.Set(P * HomgPoint3D(UL));
1120  ur.Set(P * HomgPoint3D(UR));
1121  ll.Set(P * HomgPoint3D(LL));
1122  lr.Set(P * HomgPoint3D(LR));
1123  ul.Homogenize();
1124  ur.Homogenize();
1125  ll.Homogenize();
1126  lr.Homogenize();
1127  }
1128 
1129  return GenerateTexturedQuad(Texture, UL, UR, LL, LR,
1130  ul.GetEuclidean(), ur.GetEuclidean(),
1131  ll.GetEuclidean(), lr.GetEuclidean(), normal);
1132 }
1133 
1134 
1135 int TriangleMesh::
1137  const std::vector< Vector3<double> > &X,
1138  const std::vector< Vector2<double> > &x)
1139 {
1140  const bool doTexture = Texture.GetWidth() > 0;
1141  const float w = Texture.GetWidth() - 1.0f;
1142  const float h = Texture.GetHeight() - 1.0f;
1143 
1144  // clear mesh data
1145  meshVertices_.clear();
1146  vertexNormals_.clear();
1147  textureCoords_.clear();
1148  triangleIndices_.clear();
1149 
1150  // add vertices
1151  std::vector< Vector3<double> >::const_iterator x3d;
1152  for (x3d = X.begin(); x3d != X.end(); x3d++) {
1153  meshVertices_.push_back(*x3d);
1154  }
1155 
1156  // add texture coordinates
1157  if (doTexture && !x.empty())
1158  {
1159  BIASASSERT(X.size() == x.size());
1160  texture_ = Texture;
1161  std::vector< Vector2<double> >::const_iterator x2d;
1162  for (x2d = x.begin(); x2d != x.end(); x2d++) {
1163  textureCoords_.push_back(
1164  Vector2<float>((float)((*x2d)[0] / w), 1.0f - (float)((*x2d)[1] / h)));
1165  }
1166  }
1167 
1168  // add triangles
1169  const unsigned int num = X.size();
1170  Vector<int> triangle(3);
1171  for (unsigned int idx = 0; idx+3 < num; idx += 2) {
1172  triangle[0] = idx;
1173  triangle[1] = idx + 1;
1174  triangle[2] = idx + 2;
1175  triangleIndices_.push_back(triangle);
1176  triangle[0] = idx + 1;
1177  triangle[1] = idx + 3;
1178  triangle[2] = idx + 2;
1179  triangleIndices_.push_back(triangle);
1180  }
1181 
1182  return 0;
1183 }
1184 
1185 
1186 int TriangleMesh::
1188  const Image<unsigned char>& Texture,
1189  const std::vector< Vector3<double> > &X)
1190 {
1191  std::vector< Vector2<double> > x;
1192  std::vector< Vector3<double> >::const_iterator x3d;
1193  x.reserve(X.size());
1194  HomgPoint2D x2d;
1195  for (x3d = X.begin(); x3d != X.end(); x3d++) {
1196  x2d = P.Project(HomgPoint3D(*x3d));
1197  if (!Equal(x2d[2], 0.0)) {
1198  // 3d point is not at infinity and projects into texture image
1199  x2d.Homogenize();
1200  x.push_back(x2d.GetEuclidean());
1201  } else {
1202  // 3d point is at infinity, add dummy texture coordinates
1203  x.push_back(Vector2<double>(0.0, 0.0));
1204  }
1205  }
1206 
1207  return GenerateTexturedQuadStrip(Texture, X, x);
1208 }
1209 
1210 
1211 /**
1212  * @brief Simplifies a TriangleMesh to a desired percentage of triangles of the original mesh.
1213  * The method uses the algorithm described by Garland and Heckbert in "Surface Simplification Using Quadric Error Metrics"
1214  *
1215  * @author bangerer, 09/2008
1216  * @param Texture the texture of the mesh
1217  * @param TexP the
1218  * @param reduce reduces the triangles in the generated mesh to "reduce" percent of the original mesh
1219  * @return true if mesh could be reduced to the given reduce value
1220  */
1222  BIAS::Projection& TexP, double reduce) {
1223 
1224  vector<int> deletedVertices_;
1225  Vector2<float> texCoord;
1226 
1227  // Initialize Quadrics.
1228 
1229  cout << "Mesh has " << meshVertices_.size() << " vertices and " << triangleIndices_.size()
1230  << " triangles" << endl;
1231 
1232  std::vector<Quadric3> quadrics;
1233  std::vector<int> triangleIndicesRemove;
1234 
1235  // initialize datastructures with values
1236  for (unsigned int i = 0; i < meshVertices_.size(); i++) {
1237  Quadric3 quad;
1238  quadrics.push_back(quad);
1239  set<int> neighbours;
1240  neighboursOfVertex_.push_back(neighbours);
1241  set<int> tris;
1242  vertexIsInTriangles_.push_back(tris);
1243  set<int> pair;
1244  vertexIsInThisPairs_.push_back(pair);
1245  }
1246 
1247  // calculate the inital Quadrics for each vertex
1248  for (unsigned int i = 0; i < triangleIndices_.size(); i++) {
1249  // make vectors for fast indexing
1250 
1251  triangleIndicesRemove.push_back(0);
1252 
1253  //neighbours of vector 1
1254  neighboursOfVertex_[triangleIndices_[i][0]].insert(triangleIndices_[i][1]);
1255  neighboursOfVertex_[triangleIndices_[i][0]].insert(triangleIndices_[i][2]);
1256 
1257  // neighbours of vector 2
1258  neighboursOfVertex_[triangleIndices_[i][1]].insert(triangleIndices_[i][0]);
1259  neighboursOfVertex_[triangleIndices_[i][1]].insert(triangleIndices_[i][2]);
1260 
1261  // neighbours of vector 3
1262  neighboursOfVertex_[triangleIndices_[i][2]].insert(triangleIndices_[i][0]);
1263  neighboursOfVertex_[triangleIndices_[i][2]].insert(triangleIndices_[i][1]);
1264 
1265  // triangle, vector is in
1266  vertexIsInTriangles_[triangleIndices_[i][0]].insert(i);
1267  vertexIsInTriangles_[triangleIndices_[i][1]].insert(i);
1268  vertexIsInTriangles_[triangleIndices_[i][2]].insert(i);
1269 
1270  // get corners of triangle
1271  Vector3<double> v1 = meshVertices_[triangleIndices_[i][0]];
1272  Vector3<double> v2 = meshVertices_[triangleIndices_[i][1]];
1273  Vector3<double> v3 = meshVertices_[triangleIndices_[i][2]];
1274 
1275  Vector4<double> test;
1276  double area = 0.0;
1277  area = ThreePointsToPlane_(v1, v2, v3, test); // calculate plane and area for triangle
1278 
1279  Quadric3 Q(test[0], test[1], test[2], test[3], area);
1280  Q *= Q.GetArea();
1281  // add quadric to every vertex
1282  quadrics[triangleIndices_[i][0]] += Q;
1283  quadrics[triangleIndices_[i][1]] += Q;
1284  quadrics[triangleIndices_[i][2]] += Q;
1285  }
1286 
1287  // add all valid edges to a set
1288  set<int>::iterator externIter, internIter;
1289  for (unsigned int i = 0; i < neighboursOfVertex_.size(); i++) {
1290  for (externIter = neighboursOfVertex_[i].begin(); externIter
1291  != neighboursOfVertex_[i].end(); ++externIter) {
1292  //cout << "Kante von " << i << " nach " << *externIter << endl;
1293  if ((unsigned int)*externIter > i) {
1294  Edge e;
1295  e.IDv1 = i;
1296  e.IDv2 = *externIter;
1297 
1298  validPairs_.push_back(e);
1299  vertexIsInThisPairs_[e.IDv1].insert(validPairs_.size() - 1);
1300  vertexIsInThisPairs_[e.IDv2].insert(validPairs_.size() - 1);
1301 
1302  // we have to test if one of the edges is a boundary edge. if it is, then give it
1303  // a penalty value because it should not be contracted.
1304 #ifdef USE_BOUNDARY_TEST
1305 
1306  // get the triangles v1 is in
1307  int countTriangles = 0;
1308  int triangleID = 0;
1309  for (internIter = vertexIsInTriangles_[e.IDv1].begin(); internIter
1310  != vertexIsInTriangles_[e.IDv1].end(); ++internIter) {
1311  if ((triangleIndices_[*internIter][0] == e.IDv2) || (triangleIndices_[*internIter][1]
1312  == e.IDv2) || (triangleIndices_[*internIter][2] == e.IDv2)) {
1313  countTriangles++;
1314  triangleID = *internIter;
1315  }
1316  }
1317 
1318 
1319  // construct plane through e1 and plane
1320  if (countTriangles == 1) {
1321 
1322  Vector4<double> test;
1323  ThreePointsToPlane_(meshVertices_[triangleIndices_[triangleID][0]],
1324  meshVertices_[triangleIndices_[triangleID][1]],
1325  meshVertices_[triangleIndices_[triangleID][2]], test);
1326  Vector4<double> senkrechteEbene;
1327  Vector3<double> normale(test[0], test[1], test[2]);
1328  normale.AddIP(meshVertices_[e.IDv1]);
1329  double area2 = 0.0;
1330  area2 = ThreePointsToPlane_(meshVertices_[e.IDv1], meshVertices_[e.IDv2], normale,
1331  senkrechteEbene);
1332 
1333  // calc quadric of orthogonal plane and add to v1 and v2
1334 
1335  Quadric3 Q2(senkrechteEbene[0], senkrechteEbene[1], senkrechteEbene[2],
1336  senkrechteEbene[3], area2);
1337  Q2 *= Q2.GetArea();
1338  Q2 *= 1000; // penalty
1339  quadrics[e.IDv1] += Q2;
1340  quadrics[e.IDv2] += Q2;
1341  }
1342 #endif
1343  }
1344  }
1345  }
1346 
1347  // double T = 0.05; // distance between two non connected points
1348 
1349 
1350 // if (T > 0) {
1351 // for (unsigned int i = 0; i < meshVertices_.size(); i++) {
1352 // for (unsigned int j = i + 1; j < meshVertices_.size(); j++) {
1353 // // cout << (meshVertices_[i].Normalize() - meshVertices_[j].Normalize()).NormL2() << endl;
1354 // Vector3<double> vt1;
1355 // Vector3<double> vt2;
1356 // vt1 = meshVertices_[i];
1357 // vt1.Normalize();
1358 // vt2 = meshVertices_[j];
1359 // vt2.Normalize();
1360 // if ((vt1 - vt2).NormL2() < T) {
1361 // Edge e;
1362 // e.IDv1 = i;
1363 // e.IDv2 = j;
1364 // validPairs_.push_back(e);
1365 // vertexIsInThisPairs_[e.IDv1].insert(validPairs_.size() - 1);
1366 // vertexIsInThisPairs_[e.IDv2].insert(validPairs_.size() - 1);
1367 // cout << "Found additional valid pair" << endl;
1368 // }
1369 // }
1370 // }
1371 // }
1372 
1373  // now compute the contraction for every pair/edge with the costs and put in a sorted list
1374  for (unsigned int i = 0; i < validPairs_.size(); i++) {
1375  Quadric3 Q;
1376  Q = quadrics[validPairs_[i].IDv1];
1377  Q += quadrics[validPairs_[i].IDv2];
1378 
1379 
1380 #ifdef USE_OPTIMIZED
1381  // compute optimized point
1382  if (Q.Optimize(validPairs_[i].newV)) {
1383  validPairs_[i].err = Q.Evaluate(validPairs_[i].newV[0], validPairs_[i].newV[1],
1384  validPairs_[i].newV[2]);
1385  //cout << "found optimal new point" << endl;
1386  } else {
1387 #endif
1388  // move vertex v2 at position of vertex v1
1389  Vector3<double> vi = meshVertices_[validPairs_[i].IDv1];
1390  Vector3<double> vj = meshVertices_[validPairs_[i].IDv2];
1391 
1392  double ei = Q.Evaluate(vi[0], vi[1], vi[2]);
1393  double ej = Q.Evaluate(vj[0], vj[1], vj[2]);
1394 
1395  if (ei < ej) {
1396  validPairs_[i].err = ei;
1397  validPairs_[i].newV = vi;
1398  } else {
1399  validPairs_[i].err = ej;
1400  validPairs_[i].newV = vj;
1401  }
1402 #ifdef USE_OPTIMIZED
1403  }
1404 #endif
1405 
1406  // test if one of the triangles after contraction changes direction. if yes, then the points
1407  // v1 and v2 should not be contracted
1408 #ifdef USE_CONTRACTION_TEST
1409  TestContraction(validPairs_[i].IDv1, validPairs_[i].IDv2, i, true);
1410 #endif
1411 
1412  // add to sorted set
1413  sorted.insert(validPairs_[i]);
1414  }
1415 
1416  // give me the sorted edges
1417  std::multiset<Edge,Edge>::iterator iter;
1418 
1419  // now we can start with the decimation of the vertices
1420  int valid_triangles = triangleIndices_.size();
1421  int target = int(valid_triangles * (reduce/100.0)); // percentage of original mesh
1422  Edge ed;
1423  // compute contraction
1424  Vector3<double> dv1, dv2;
1425 
1426  std::vector<int> dead_triangles; // here we put triangles that degenerate
1427  std::vector<int> moved_triangles; // here we put triangle indices of triangles that reshape
1428  std::vector<BIAS::Vector<int> > trianglesToKill;
1429  std::vector<int> IntegerToKill;
1430 
1431  int moved_pivot;
1432 
1433  cout << "starting mesh reduction... " << endl;
1434 
1435  int difference = valid_triangles - target;
1436  int progress=0;
1437  int lastPerCent = 0;
1438  while (valid_triangles > target && sorted.size() > 0) {
1439  // calculate progress and print status
1440  progress = (int)rint((double) 1 - ((double) (valid_triangles - target)
1441  / (double) difference)) * 100;
1442 
1443  if (progress % 10 == 0) {
1444  if (progress != lastPerCent) {
1445  cout << progress << " %" << endl;
1446  lastPerCent = progress;
1447  }
1448  }
1449 
1450  // fetch the first element out of the sorted set an delete it from the set.
1451  iter = sorted.begin();
1452  ed = *iter;
1453  sorted.erase(ed);
1454 
1455  // clean vectors
1456  dead_triangles.clear();
1457  moved_triangles.clear();
1458 
1459  // the two vectors that should be contracted
1460  Vector3<double> v1 = meshVertices_[ed.IDv1];
1461  Vector3<double> v2 = meshVertices_[ed.IDv2];
1462 
1463  ed.newV.Sub(v1, dv1);
1464  ed.newV.Sub(v2, dv2);
1465 
1466  // get the triangles that degenerate and the triangles that are only moved
1467  GetNeighbours_(ed.IDv1, ed.IDv2, dead_triangles, moved_triangles, moved_pivot);
1468 
1469  // accumulate quadrics of the two vertices
1470  quadrics[ed.IDv1] += quadrics[ed.IDv2];
1471 
1472  vector<Edge> maybeDoubleEdges;
1473 
1474  // concat the edges with v2 to v1
1475 
1476  //first search for potential double edges and delete the actually used edge from pairs
1477  IntegerToKill.clear();
1478  set<int>::iterator pairIter;
1479  for (pairIter = vertexIsInThisPairs_[ed.IDv1].begin(); pairIter
1480  != vertexIsInThisPairs_[ed.IDv1].end(); ++pairIter) {
1481 
1482  sorted.erase(validPairs_[*pairIter]);
1483 
1484  if (validPairs_[*pairIter].IDv1 == ed.IDv2 || validPairs_[*pairIter].IDv2 == ed.IDv2) {
1485  // delete edge from pairs
1486  IntegerToKill.push_back(*pairIter);
1487  } else {
1488  // add double edge
1489  maybeDoubleEdges.push_back(validPairs_[*pairIter]);
1490  }
1491  }
1492  // delete the selected pairs
1493  for(unsigned int j = 0; j < IntegerToKill.size(); j++){
1494  vertexIsInThisPairs_[ed.IDv1].erase(IntegerToKill[j]);
1495  vertexIsInThisPairs_[ed.IDv2].erase(IntegerToKill[j]);
1496  }
1497  IntegerToKill.clear();
1498 
1499  // relink pairs with v2 into pairs with v1 and check for double entrys
1500  for (pairIter = vertexIsInThisPairs_[ed.IDv2].begin(); pairIter
1501  != vertexIsInThisPairs_[ed.IDv2].end(); ++pairIter) {
1502 
1503  Edge edg = validPairs_[*pairIter];
1504  bool relink = true;
1505  if (validPairs_[*pairIter].IDv1 == ed.IDv2) {
1506  sorted.erase(validPairs_[*pairIter]);
1507  for (unsigned int j = 0; j < maybeDoubleEdges.size(); j++) {
1508 
1509  if(maybeDoubleEdges[j].IDv1 == validPairs_[*pairIter].IDv2)
1510  relink = false;
1511  else if(maybeDoubleEdges[j].IDv2 == validPairs_[*pairIter].IDv2)
1512  relink = false;
1513  else if(validPairs_[*pairIter].IDv2 == ed.IDv1)
1514  relink = false;
1515 
1516  }
1517  if (relink) {
1518 
1519  if(validPairs_[*pairIter].IDv2 == ed.IDv1){
1520  IntegerToKill.push_back(*pairIter);
1521  }else{
1522  validPairs_[*pairIter].IDv1 = ed.IDv1;
1523  vertexIsInThisPairs_[ed.IDv1].insert(*pairIter);
1524  }
1525  }else{
1526  IntegerToKill.push_back(*pairIter);
1527  }
1528 
1529  }
1530 
1531  if (validPairs_[*pairIter].IDv2 == ed.IDv2) {
1532  sorted.erase(validPairs_[*pairIter]);
1533  for (unsigned int j = 0; j < maybeDoubleEdges.size(); j++) {
1534 
1535  if(maybeDoubleEdges[j].IDv1 == validPairs_[*pairIter].IDv1)
1536  relink = false;
1537  else if(maybeDoubleEdges[j].IDv2 == validPairs_[*pairIter].IDv1)
1538  relink = false;
1539  else if(validPairs_[*pairIter].IDv1 == ed.IDv1)
1540  relink = false;
1541 
1542  }
1543  if (relink) {
1544  if(validPairs_[*pairIter].IDv1 == ed.IDv1){
1545  IntegerToKill.push_back(*pairIter);
1546  }else{
1547  validPairs_[*pairIter].IDv2 = ed.IDv1;
1548  vertexIsInThisPairs_[ed.IDv1].insert(*pairIter);
1549  }
1550  }else{
1551  IntegerToKill.push_back(*pairIter);
1552  }
1553  }
1554 
1555  }
1556 
1557  for(unsigned int j = 0; j < IntegerToKill.size(); j++){
1558  vertexIsInThisPairs_[validPairs_[IntegerToKill[j]].IDv1].erase(IntegerToKill[j]);
1559  vertexIsInThisPairs_[validPairs_[IntegerToKill[j]].IDv2].erase(IntegerToKill[j]);
1560  }
1561 
1562 
1563  // move v1 to vNew
1564  meshVertices_[ed.IDv1] += dv1;
1565  // meshVertices_[ed.IDv2] += dv2;
1566 
1567  // calc new texture
1568  //generate texture coordinates
1569  HomgPoint2D uv;
1570  HomgPoint3D p3dh(meshVertices_[ed.IDv1]);
1571 
1572  unsigned int tw = Texture.GetWidth();
1573  unsigned int th = Texture.GetHeight();
1574 
1575  if (TexP.DoesPointProjectIntoImage(p3dh, uv)) {
1576  double u = uv[0] / float(tw - 1);
1577  double v = 1 - uv[1] / float(th - 1);
1578  texCoord.Set(float(u), float(v));
1579 
1580  textureCoords_[ed.IDv1] = texCoord;
1581  }
1582 
1583 
1584  // all v2 become v1
1585  set<int>::iterator listIter;
1586 
1587  for (unsigned int i = 0; i < moved_triangles.size(); i++) {
1588  if (triangleIndices_[moved_triangles[i]][0] == ed.IDv2) {
1589  triangleIndices_[moved_triangles[i]][0] = ed.IDv1;
1590  vertexIsInTriangles_[ed.IDv1].insert(moved_triangles[i]);
1591  }
1592 
1593  if (triangleIndices_[moved_triangles[i]][1] == ed.IDv2) {
1594  triangleIndices_[moved_triangles[i]][1] = ed.IDv1;
1595  vertexIsInTriangles_[ed.IDv1].insert(moved_triangles[i]);
1596  }
1597  if (triangleIndices_[moved_triangles[i]][2] == ed.IDv2) {
1598  triangleIndices_[moved_triangles[i]][2] = ed.IDv1;
1599  vertexIsInTriangles_[ed.IDv1].insert(moved_triangles[i]);
1600  }
1601  }
1602 
1603  // remove degenerated triangles from vertices
1604  for (unsigned int i = 0; i < dead_triangles.size(); i++) {
1605 
1606  vertexIsInTriangles_[triangleIndices_[dead_triangles[i]][0]].erase(dead_triangles[i]);
1607  vertexIsInTriangles_[triangleIndices_[dead_triangles[i]][1]].erase(dead_triangles[i]);
1608  vertexIsInTriangles_[triangleIndices_[dead_triangles[i]][2]].erase(dead_triangles[i]);
1609 
1610  triangleIndicesRemove[dead_triangles[i]] = 1;
1611  }
1612 
1613  // delete all of v2
1614  vertexIsInTriangles_[ed.IDv2].clear();
1615  vertexIsInThisPairs_[ed.IDv2].clear();
1616 
1617  // update number of valid triangles
1618  valid_triangles -= dead_triangles.size();
1619 
1620  // update list with error quadrics
1621  for (pairIter = vertexIsInThisPairs_[ed.IDv1].begin(); pairIter
1622  != vertexIsInThisPairs_[ed.IDv1].end(); ++pairIter) {
1623 
1624  sorted.erase(validPairs_[*pairIter]);
1625 
1626  int doubleV1p = validPairs_[*pairIter].IDv1;
1627  int doubleV2p = validPairs_[*pairIter].IDv2;
1628 
1629  if(doubleV1p == doubleV2p)
1630  cout << "SOMETHING WENT WRONG" << endl;
1631 
1632  Quadric3 Q;
1633  Q = quadrics[validPairs_[*pairIter].IDv1];
1634  Q += quadrics[validPairs_[*pairIter].IDv2];
1635 #ifdef USE_OPTIMIZED
1636  // compute optimized point
1637  if (Q.Optimize(validPairs_[*pairIter].newV)) {
1638  validPairs_[*pairIter].err = Q.Evaluate(validPairs_[*pairIter].newV[0],
1639  validPairs_[*pairIter].newV[1],
1640  validPairs_[*pairIter].newV[2]);
1641  } else {
1642 #endif
1643  Vector3<double> vi = meshVertices_[validPairs_[*pairIter].IDv1];
1644  Vector3<double> vj = meshVertices_[validPairs_[*pairIter].IDv2];
1645 
1646  double ei = Q.Evaluate(vi[0], vi[1], vi[2]);
1647  double ej = Q.Evaluate(vj[0], vj[1], vj[2]);
1648 
1649  if (ei < ej) {
1650  validPairs_[*pairIter].err = ei;
1651  validPairs_[*pairIter].newV = vi;
1652  } else {
1653  validPairs_[*pairIter].err = ej;
1654  validPairs_[*pairIter].newV = vj;
1655  }
1656 #ifdef USE_OPTIMIZED
1657  }
1658 
1659 #endif
1660  // evaluate contraction
1661 #ifdef USE_CONTRACTION_TEST
1662  TestContraction(ed.IDv1, ed.IDv2, *pairIter, true);
1663 #endif
1664 
1665  sorted.insert(validPairs_[*pairIter]);
1666  }
1667  }
1668 
1669  cout << "100 %" << endl;
1670 
1671  // remove the degenerated triangles from the original mesh
1672  std::vector<BIAS::Vector<int> > tmp;
1673 
1674  for(unsigned int i = 0; i < triangleIndices_.size(); i++){
1675  if(triangleIndicesRemove[i] == 0){
1676  tmp.push_back(triangleIndices_[i]);
1677  }
1678  }
1679 
1680  triangleIndices_.clear();
1681  triangleIndices_ = tmp;
1682 
1683  cout << "Triangles after mesh reduction: " << triangleIndices_.size() << endl;
1684 
1685  return true;
1686 }
1687 
1688 /**
1689  * @brief calculates a plan of three points and returns it area
1690  *
1691  * @param v1 first vector of plane
1692  * @param v2 second vector of plane
1693  * @param v3 third vector of plane
1694  * @param erg vector in which the plane is stored
1695  * @return the area of the plan
1696  */
1698  Vector3<double> v3, Vector4<double>& erg) {
1699  // plane in parameterform
1700  // E = A + s* AB + t* AC
1701  Vector3<double> AB;
1702  Vector3<double> AC;
1703 
1704  AB = v2 - v1;
1705  AC = v3 - v1;
1706 
1707  // calc normal vector N
1708  Vector3<double> N;
1709  AB.CrossProduct(AC, N);
1710 
1711  // generate cooordinate form
1712  // ax + by + cz + d = 0;
1713  N.Normalize();
1714 
1715  erg[0] = N[0]; // a
1716  erg[1] = N[1]; // b
1717  erg[2] = N[2]; // c
1718  erg[3] = -(int) (N.ScalarProduct(v1)); // d
1719 
1720  // return the area of the triangle
1721  return (N.NormL2() * 0.5);
1722 }
1723 
1724 /**
1725  * @brief get the neighbours of the contracted points and returns two list that contains
1726  * the triangles to remove from the model and triangles that have to be moved to one of the vertices
1727  */
1728 void TriangleMesh::GetNeighbours_(int v1, int v2, std::vector<int>& dead_triangles,
1729  std::vector<int>& moved_triangles, int &moved_pivot) {
1730  // first check for triangles that contain one or two of the vectors.
1731  multiset<int> neighbours;
1732  set<int>::iterator iter;
1733 
1734  // get neighbours
1735  for (iter = vertexIsInTriangles_[v1].begin(); iter != vertexIsInTriangles_[v1].end(); ++iter) {
1736  neighbours.insert(*iter);
1737  //cout << "Triangle i= " << vertexIsInTriangles_[v1][i] <<" enthaelt " << neighbours.count(vertexIsInTriangles_[v1][i])<< endl;
1738  }
1739 
1740  // if theres the neighbour only once, then move the triangle with the one neighbour, else kill the triangle
1741  for (iter = vertexIsInTriangles_[v2].begin(); iter != vertexIsInTriangles_[v2].end(); ++iter) {
1742  neighbours.insert(*iter);
1743  // cout << "Count: " << neighbours.count(*iter) << endl;
1744  if (neighbours.count(*iter) == 1) {
1745  moved_triangles.push_back(*iter);
1746  } else {
1747  // cout << "Triangle to kill: " << *iter << endl;
1748  dead_triangles.push_back(*iter);
1749  }
1750  }
1751  ////////////////////////////////
1752  //if(dead_triangles.size() == 0 || dead_triangles.size() > 2){
1753  //cout << "Number of triangles to kill: " << dead_triangles.size() << endl;
1754  //cout << "V1: " << v1 << " V2: " << v2 << endl;
1755  //}
1756 }
1757 
1758 /**
1759  * @brief Tests if a triangle that is involved in the contraction changes
1760  * direction or overlapps
1761  */
1762 void TriangleMesh::TestContraction(int v1, int v2, int edge, bool init) {
1763  // test for planes connected to v1
1764  Vector<double> normalOld;
1765  Vector<double> normalNew;
1766  normalNew.SetZero();
1767 
1768  if (init) {
1769  set<int>::iterator iter;
1770 
1771  for (iter = vertexIsInTriangles_[v1].begin();
1772  iter != vertexIsInTriangles_[v1].end();
1773  ++iter) {
1774  //cout << *iter << endl;
1775  if (triangleIndices_[*iter][0] != v2 && triangleIndices_[*iter][1] != v2
1776  && triangleIndices_[*iter][2] != v2) {
1777  // compute normale before contraction
1778  // cout << "V1: " << v1 << " V2: " << v2 << endl;
1779 
1780  normalOld = GetPlaneNormalEuclidean(meshVertices_[triangleIndices_[*iter][0]],
1781  meshVertices_[triangleIndices_[*iter][1]],
1782  meshVertices_[triangleIndices_[*iter][2]]);
1783  // compute normale after contraction
1784  if (triangleIndices_[*iter][0] == v1)
1785  normalNew = GetPlaneNormalEuclidean(validPairs_[edge].newV,
1786  meshVertices_[triangleIndices_[*iter][1]],
1787  meshVertices_[triangleIndices_[*iter][2]]);
1788  if (triangleIndices_[*iter][1] == v1)
1789  normalNew = GetPlaneNormalEuclidean(meshVertices_[triangleIndices_[*iter][0]],
1790  validPairs_[edge].newV,
1791  meshVertices_[triangleIndices_[*iter][2]]);
1792  if (triangleIndices_[*iter][2] == v1)
1793  normalNew = GetPlaneNormalEuclidean(meshVertices_[triangleIndices_[*iter][0]],
1794  meshVertices_[triangleIndices_[*iter][1]],
1795  validPairs_[edge].newV);
1796 
1797  if (!normalNew.IsZero() && !normalOld.IsZero()) {
1798  if (normalNew.NormL2() > std::numeric_limits<double>::epsilon()
1799  && normalOld.NormL2() > std::numeric_limits<double>::epsilon()) {
1800  const double angle = GetAngle(normalOld, normalNew);
1801  if (angle > 0.15 || angle < -0.15) {
1802  validPairs_[edge].err *= 1000;
1803  return;
1804  }
1805  }
1806  }
1807  }
1808  }
1809 
1810  for (iter = vertexIsInTriangles_[v2].begin();
1811  iter != vertexIsInTriangles_[v2].end();
1812  ++iter) {
1813  //cout << *iter << endl;
1814  if (triangleIndices_[*iter][0] != v1 && triangleIndices_[*iter][1] != v1
1815  && triangleIndices_[*iter][2] != v1) {
1816  // compute normale before contraction
1817  // cout << "V1: " << v1 << " V2: " << v2 << endl;
1818 
1819  normalOld = GetPlaneNormalEuclidean(meshVertices_[triangleIndices_[*iter][0]],
1820  meshVertices_[triangleIndices_[*iter][1]],
1821  meshVertices_[triangleIndices_[*iter][2]]);
1822  // compute normale after contraction
1823  if (triangleIndices_[*iter][0] == v2)
1824  normalNew = GetPlaneNormalEuclidean(validPairs_[edge].newV,
1825  meshVertices_[triangleIndices_[*iter][1]],
1826  meshVertices_[triangleIndices_[*iter][2]]);
1827  if (triangleIndices_[*iter][1] == v2)
1828  normalNew = GetPlaneNormalEuclidean(meshVertices_[triangleIndices_[*iter][0]],
1829  validPairs_[edge].newV,
1830  meshVertices_[triangleIndices_[*iter][2]]);
1831  if (triangleIndices_[*iter][2] == v2)
1832  normalNew = GetPlaneNormalEuclidean(meshVertices_[triangleIndices_[*iter][0]],
1833  meshVertices_[triangleIndices_[*iter][1]],
1834  validPairs_[edge].newV);
1835 
1836  if (!normalNew.IsZero() && !normalOld.IsZero()) {
1837  if (normalNew.NormL2() > std::numeric_limits<double>::epsilon()
1838  && normalOld.NormL2() > std::numeric_limits<double>::epsilon()) {
1839  const double angle = GetAngle(normalOld, normalNew);
1840 
1841  if (angle > 0.15 || angle < -0.15) {
1842  validPairs_[edge].err *= 1000;
1843  return;
1844  }
1845  }
1846  }
1847  }
1848  }
1849  }
1850 }
1851 
1853 {
1854  /////
1855  // to speed up searching, create a list of triangles for each 3D point that
1856  // touch it
1857  /////
1858  vector<vector<size_t> > pointsToTris(meshVertices_.size());
1859 
1860  for (size_t triCnt = 0; triCnt < triangleIndices_.size(); ++triCnt) {
1861  for (size_t cornerCnt = 0;
1862  cornerCnt < size_t(triangleIndices_[triCnt].size());
1863  ++cornerCnt) {
1864  pointsToTris[triangleIndices_[triCnt][cornerCnt]].push_back(triCnt);
1865  }
1866  }
1867 
1868  /////
1869  // find conntected segments
1870  /////
1871  vector<vector<size_t> > segments(triangleIndices_.size()); // indices of triangles belonging to a certain segment
1872  size_t emptySegmentIndex = 0; // always points to an empty segment
1873  vector<int> trisToSegments(triangleIndices_.size(), -1); // for each tri store corresponding segment
1874 
1875  // iterate over the previously created list of points and add all triangles
1876  // touching the same point to the same segment
1877  for (size_t pointCnt = 0; pointCnt < pointsToTris.size(); ++pointCnt) {
1878  // skip if no triangles touch this point
1879  if (pointsToTris[pointCnt].empty()) {
1880  BIASWARN("mesh contains a vertex that is not used by any triangle")
1881  continue;
1882  }
1883 
1884  // add all triangles (and their corresponding segments) to the segment of
1885  // the first point in the list
1886 
1887  // get segment of first triangle for current point or create new one for it
1888  const size_t firstTriIndex = pointsToTris[pointCnt][0];
1889  int segmentForCurPoint = trisToSegments[firstTriIndex];
1890 
1891  if (segmentForCurPoint < 0) { // create new segment
1892  segments[emptySegmentIndex].push_back(firstTriIndex);
1893  trisToSegments[firstTriIndex] = emptySegmentIndex;
1894  segmentForCurPoint = (int)emptySegmentIndex;
1895  ++emptySegmentIndex;
1896  }
1897 
1898  // now add remaining triangles (and their segments) meeting in cur point
1899  for (size_t triCnt = 1; triCnt < pointsToTris[pointCnt].size(); ++triCnt) {
1900  const size_t curTriIndex = pointsToTris[pointCnt][triCnt];
1901  const int curTriSegment = trisToSegments[curTriIndex];
1902 
1903  // check if current tri already belongs to a segment
1904  if (curTriSegment < 0) { // only need to add this
1905  segments[segmentForCurPoint].push_back(curTriIndex);
1906  trisToSegments[curTriIndex] = segmentForCurPoint;
1907  }
1908  else if (curTriSegment == segmentForCurPoint) { // already in the segment
1909  // nothing to do
1910  }
1911  else { // triangle belongs to a segment -> add all triangles from segment
1912  for (size_t triSegmCnt = 0;
1913  triSegmCnt < segments[size_t(curTriSegment)].size();
1914  ++triSegmCnt) {
1915  segments[segmentForCurPoint].push_back(segments[size_t(curTriSegment)][triSegmCnt]);
1916  trisToSegments[segments[size_t(curTriSegment)][triSegmCnt]]
1917  = segmentForCurPoint;
1918  }
1919 
1920  segments[size_t(curTriSegment)].clear();
1921  }
1922  }
1923  }
1924 
1925  /////
1926  // search for biggest segment
1927  /////
1928  size_t maxNumTris = 0;
1929  int biggestSegmentIndex = -1;
1930 
1931  for (size_t segmCnt = 0; segmCnt < segments.size(); ++segmCnt) {
1932  if (segments[segmCnt].size() > maxNumTris) {
1933  maxNumTris = segments[segmCnt].size();
1934  biggestSegmentIndex = int(segmCnt);
1935  }
1936  }
1937 
1938  if (biggestSegmentIndex == -1) {
1939  BIASWARN("did not find any connected segments at all")
1940  return;
1941  }
1942 
1943  /////
1944  // remove unused vertices and produce final TriangleMesh
1945  /////
1946  vector<Vector3<double> > vertices;
1947  vector<Vector3<double> > normals;
1948  vector<Vector4<unsigned char> > colors;
1949  vector<int> newVertexIndices(meshVertices_.size(), -1); // new indices for orig vertices
1950 
1951  for (size_t triCnt = 0;
1952  triCnt < segments[biggestSegmentIndex].size();
1953  ++triCnt) {
1954  const size_t curTriIndex = segments[biggestSegmentIndex][triCnt];
1955 
1956  for (size_t pointCnt = 0;
1957  pointCnt < size_t(triangleIndices_[curTriIndex].size());
1958  ++pointCnt) {
1959  const size_t curPointIndex = triangleIndices_[curTriIndex][pointCnt];
1960 
1961  if (newVertexIndices[curPointIndex] == -1) {
1962  newVertexIndices[curPointIndex] = vertices.size();
1963  vertices.push_back(meshVertices_[curPointIndex]);
1964  if (!vertexNormals_.empty()) {
1965  normals.push_back(vertexNormals_[curPointIndex]);
1966  }
1967  colors.push_back(vertexColors_[curPointIndex]);
1968  }
1969  }
1970  }
1971 
1972  vector<Vector<int> > triangleIndices;
1973  triangleIndices.reserve(segments[biggestSegmentIndex].size());
1974 
1975  for (size_t triCnt = 0;
1976  triCnt < segments[biggestSegmentIndex].size();
1977  ++triCnt) {
1978  const Vector<int>& origTri
1979  = triangleIndices_[segments[biggestSegmentIndex][triCnt]];
1980  Vector<int> newTri(triangleIndices_[segments[biggestSegmentIndex][triCnt]].size());
1981 
1982  for (size_t pointCnt = 0; pointCnt < size_t(origTri.size()); ++pointCnt) {
1983  const size_t origIndex = origTri[pointCnt];
1984  const size_t newIndex = newVertexIndices[origIndex];
1985  newTri[pointCnt] = newIndex;
1986  }
1987 
1988  triangleIndices.push_back(newTri);
1989  }
1990 
1991  biggestSegment.SetMesh(vertices, normals, colors, triangleIndices);
1992 }
1993 
1995  Vector3<double>& maxPoint) const
1996 {
1997  // if this mesh is empty, set both points to zero
1998  if (meshVertices_.empty()) {
1999  minPoint.Set(0.0, 0.0, 0.0);
2000  maxPoint.Set(0.0, 0.0, 0.0);
2001  return;
2002  }
2003 
2004  // at this point we have vertices in the mesh, so search for minimal and
2005  // maximal components
2006  minPoint.Set(numeric_limits<double>::max(),
2007  numeric_limits<double>::max(),
2008  numeric_limits<double>::max());
2009  maxPoint.Set(-numeric_limits<double>::max(),
2010  -numeric_limits<double>::max(),
2011  -numeric_limits<double>::max());
2012 
2013  for (size_t vertexCnt = 0; vertexCnt < meshVertices_.size(); ++vertexCnt) {
2014  const Vector3<double>& curVertex = meshVertices_[vertexCnt]; // readability
2015 
2016  if (curVertex[0] < minPoint[0]) minPoint[0] = curVertex[0];
2017  if (curVertex[1] < minPoint[1]) minPoint[1] = curVertex[1];
2018  if (curVertex[2] < minPoint[2]) minPoint[2] = curVertex[2];
2019 
2020  if (curVertex[0] > maxPoint[0]) maxPoint[0] = curVertex[0];
2021  if (curVertex[1] > maxPoint[1]) maxPoint[1] = curVertex[1];
2022  if (curVertex[2] > maxPoint[2]) maxPoint[2] = curVertex[2];
2023  }
2024 }
2025 
2027  Vector3<double> normalForUnusedVertices)
2028 {
2029  BIASASSERT(normalForUnusedVertices.NormL2() == 1.0)
2030 
2031  // prepare normals vector
2032  vertexNormals_.clear();
2033  vertexNormals_.resize(meshVertices_.size(), normalForUnusedVertices);
2034 
2035  // compute normal for each triangle
2036  vector<Vector3<double> > triNormals;
2037  triNormals.reserve(triangleIndices_.size());
2038 
2039  for (size_t triCnt = 0; triCnt < triangleIndices_.size(); ++triCnt) {
2040  // get corner points and compute difference vectors
2041  const Vector3<double>& p1 = meshVertices_[triangleIndices_[triCnt][0]];
2042  const Vector3<double>& p2 = meshVertices_[triangleIndices_[triCnt][1]];
2043  const Vector3<double>& p3 = meshVertices_[triangleIndices_[triCnt][2]];
2044 
2045  const Vector3<double>& diffP1P2 = p1 - p2;
2046  const Vector3<double>& diffP1P3 = p1 - p3;
2047 
2048  if (leftHanded) {
2049  // cross product for perpendicular vector
2050  Vector3<double> triNormal = diffP1P2.CrossProduct(diffP1P3);
2051 
2052  // normalise and save
2053  triNormal.Normalize();
2054  triNormals.push_back(triNormal);
2055  }
2056  else { // left-handed
2057  // cross product for perpendicular vector
2058  Vector3<double> triNormal = diffP1P3.CrossProduct(diffP1P2);
2059 
2060  // normalise and save
2061  triNormal.Normalize();
2062  triNormals.push_back(triNormal);
2063  }
2064  }
2065 
2066  // for each vertice determine which triangles it touches
2067  // vertices not used in any triangle get index -1
2068  vector<vector<int> > trianglesForVertices(meshVertices_.size());
2069 
2070  for (size_t triCnt = 0; triCnt < triangleIndices_.size(); ++triCnt) {
2071  trianglesForVertices[triangleIndices_[triCnt][0]].push_back(triCnt);
2072  trianglesForVertices[triangleIndices_[triCnt][1]].push_back(triCnt);
2073  trianglesForVertices[triangleIndices_[triCnt][2]].push_back(triCnt);
2074  }
2075 
2076  // compute normal for each vertex by averaging the normals of the surrounding
2077  // triangles
2078  for (size_t vertexCnt = 0;
2079  vertexCnt < trianglesForVertices.size();
2080  ++vertexCnt) {
2081  if (!trianglesForVertices[vertexCnt].empty()) {
2082  vertexNormals_[vertexCnt].Set(0.0, 0.0, 0.0);
2083 
2084  for (size_t triCnt = 0;
2085  triCnt < trianglesForVertices[vertexCnt].size();
2086  ++triCnt) {
2087  vertexNormals_[vertexCnt]
2088  += triNormals[trianglesForVertices[vertexCnt][triCnt]];
2089  }
2090 
2091  vertexNormals_[vertexCnt]
2092  /= double(trianglesForVertices[vertexCnt].size());
2093  vertexNormals_[vertexCnt].Normalize();
2094  }
2095  }
2096 }
int GetCopyOfROI(ImageBase &copy) const
returns a copy of ROI (a new image) lower right point excluded, only interleaved images ! ...
Definition: ImageBase.cpp:568
virtual BIAS::Vector3< double > GetC() const
Get projection center.
void SetFromPoints(const HomgPoint3D &p1, const HomgPoint3D &p2, const HomgPoint3D &p3)
constructs a homogeneous plane from three points
Definition: HomgPlane3D.hh:195
void GetEuclidean(Vector3< HOMGPOINT3D_TYPE > &dest) const
calculate affine coordinates of this and write them to dest affine coordinates are projective coordin...
Definition: HomgPoint3D.hh:336
double GetArea()
Definition: Quadric3.hh:36
class for handling different region of interest (ROI) representations...
Definition: ROI.hh:118
void Set(const T *pv)
copy the array of vectorsize beginning at *T to this-&gt;data_
Definition: Vector3.hh:532
int GenerateDenseMesh(const BIAS::Image< float > &DenseDepthMap, const BIAS::Image< unsigned char > &Texture)
Calculate a triangle mesh from dense depth map without PMatrix.
TriangleMesh(const double &minCornerAngle=M_PI *5.0/180.0, const double &maxAngle=M_PI *60.0/180.0, const double depthScale=1.0)
class HomgPoint2D describes a point with 2 degrees of freedom in projective coordinates.
Definition: HomgPoint2D.hh:67
int GetLineIntersection(const HomgPoint3D &PointOnLine, const HomgPoint3D &LineDir, HomgPoint3D &Intersection) const
compute the intersection point of the plane with a line (defined by point and direction) ...
Definition: HomgPlane3D.hh:238
void GenerateTexturedCamera(const ProjectionParametersBase *PPB, Image< unsigned char > &rgbtexture, const double &scale=1.0, const double &opacity=1.0, const double &resolution=1.0)
generates the sensor plane / cylinder / sphere in 3D space
void GetCorners(unsigned &UpperLeftX, unsigned &UpperLeftY, unsigned &LowerRightX, unsigned &LowerRightY) const
Return the region of interest, by saving the coordinates within the variables defined by the paramete...
Definition: ROI.hh:443
int ChangeTexture(const BIAS::Image< unsigned char > &texture)
Replace the texture of this mesh.
int GenerateImagePlane(const BIAS::Projection &P, const BIAS::Image< unsigned char > &Texture, const double &w=1.0)
backproject image onto a plane in space so far only implemented for perspective cameras ...
double NormL2() const
Return the L2 norm: sqrt(a^1 + a^2 + ...)
Definition: Vector.hh:416
bool Optimize(Vector3< double > &v)
Definition: Quadric3.cpp:47
camera parameters which define the mapping between rays in the camera coordinate system and pixels in...
const unsigned int size() const
Definition: Vector2.hh:225
virtual HomgPoint3D UnProjectToPoint(const HomgPoint2D &pos, double depth, bool IgnoreDistortion=false) const
calculates a 3D point in the global (not the rig) coordinate system, which belongs to the image posit...
int Filter7x7GreyIgnoreBelowThreshold(const Image< InputStorageType > &src, Image< OutputStorageType > &dst, const InputStorageType &thresh)
7x7 gauss filtering, values below threshold are ignored useful for depth map filtering ...
Definition: Gauss.cpp:1135
void ScalarProduct(const Vector3< T > &argvec, T &result) const
scalar product (=inner product) of two vectors, storing the result in result
Definition: Vector3.hh:603
virtual void SetImageSize(const unsigned int w, const unsigned int h)
Set image dimensions (in pixels).
virtual ProjectionParametersBase * Clone() const =0
Covariant virtual copy constructor used in BIAS::Projection.
virtual void Rescale(double ratio, const double offset=0.0)
Adapt internal parameters to resampled image.
void SetColorModel(EColorModel Model)
Definition: ImageBase.hh:561
void GetEuclidean(Vector2< HOMGPOINT2D_TYPE > &dest) const
calculate affine coordinates of this and write them to dest affine coordinates are projective coordin...
Definition: HomgPoint2D.hh:244
HomgPoint2D & Homogenize()
homogenize class data member elements to W==1 by divison by W
Definition: HomgPoint2D.hh:215
void GetBiggestConnectedSegment(TriangleMesh &biggestSegment) const
Returns the biggest connected segment of this triangle mesh.
bool MakeTriangles_(const Projection &P, const Projection &TexP, const BIAS::Image< unsigned char > &Texture, std::vector< Image< float > > &depthmaps, float x, float y, float offset, float minoffset, double minCosAngle, double maxDiagRatio, bool simulateOnly)
BIAS::Vector3< T > GetNormalized() const
return a normalized vector of this
Definition: Vector3.cpp:47
virtual void SetP(const PMatrix &P)
set from P
int GetR(Matrix3x3< double > &R)
Definition: PMatrix.cpp:204
double Evaluate(double x, double y, double z)
Definition: Quadric3.cpp:40
unsigned int GetWidth() const
Definition: ImageBase.hh:312
void SetZero()
equivalent to matrix call
Definition: Vector.hh:156
double NormL2() const
Return the L2 norm: sqrt(a^2 + b^2 + c^2 + d^2)
Definition: Vector4.hh:510
void TestContraction(int v1, int v2, int edge, bool init=false)
Tests if a triangle that is involved in the contraction changes direction or overlapps.
Struct for easier handling of edges.
Definition: TriangleMesh.hh:42
void GenerateVertexNormals(bool leftHanded=true, Vector3< double > normalForUnusedVertices=Vector3< double >(-1.0, 0.0, 0.0))
Computes the normals for the vertices by averaging the normals of adjacent triangles.
3D rotation matrix
Definition: RMatrix.hh:49
class Vector4 contains a Vector of dim.
Definition: Vector4.hh:65
void SetMesh(BIAS::Image< unsigned char > texture, std::vector< BIAS::Vector3< double > > meshVertices, std::vector< BIAS::Vector3< double > > vertexNormals, std::vector< BIAS::Vector2< float > > textureCoords, std::vector< BIAS::Vector< int > > triangleIndices)
Definition: TriangleMesh.hh:97
void Set(const T &scalar)
set all elements to a scalar value
Definition: Vector2.hh:190
void CrossProduct(const Vector3< T > &argvec, Vector3< T > &destvec) const
cross product of two vectors destvec = this x argvec
Definition: Vector3.hh:594
const ProjectionParametersBase * GetParameters(unsigned int cam=0) const
const parameter access function
Definition: Projection.hh:194
ROI * GetROI()
Returns a pointer to the roi object.
Definition: ImageBase.hh:615
void UnProjectToRay(const HomgPoint2D &pos, Vector3< double > &origin, Vector3< double > &rayDir, unsigned int cam=0, bool IgnoreDistortion=false) const
calculates the viewing ray in the global coordinate frame (not the rig) from the camera center which ...
Definition: Projection.cpp:323
This class hides the underlying projection model, like projection matrix, spherical camera...
Definition: Projection.hh:70
void AddIP(const T &scalar)
Addition (in place) of an scalar.
Definition: Vector3.hh:310
unsigned int GetChannelCount() const
returns the number of Color channels, e.g.
Definition: ImageBase.hh:382
Create and represent a 3D triangle mesh.
Definition: TriangleMesh.hh:84
void SetParams(const double &minCornerAngle=M_PI *5.0/180.0, const double &maxAngle=M_PI *60.0/180.0, const double depthScale=1.0)
void InvalidateDecomposition()
to re-Decompose_() after filling with data use this.
Definition: PMatrix.hh:499
unsigned int GetHeight() const
Definition: ImageBase.hh:319
void SetSigma(const double si)
Definition: Gauss.hh:162
int GenerateTexturedQuad(const Image< unsigned char > &Texture, const Vector3< double > &UL, const Vector3< double > &UR, const Vector3< double > &LL, const Vector3< double > &LR, const Vector2< double > &ul, const Vector2< double > &ur, const Vector2< double > &ll, const Vector2< double > &lr, const Vector3< double > &normal=Vector3< double >(0, 0, 0))
Generate planar quad in 3D space with given texture coordinates.
bool SamePixelAndChannelCount(const ImageBase &Image) const
checks if data area has same &quot;size&quot; as Image of other type
Definition: ImageBase.hh:73
class HomgPoint3D describes a point with 3 degrees of freedom in projective coordinates.
Definition: HomgPoint3D.hh:61
virtual bool DoesPointProjectIntoImage(const BIAS::HomgPoint3D &X, BIAS::HomgPoint2D &x, unsigned int cam=0, bool IgnoreDistortion=false) const
Checks if 3D point projects into specified image and returns belonging 2D image point.
Definition: Projection.cpp:300
virtual int GetImageSize(unsigned int &Width, unsigned int &Height) const
Obtain image dimensions.
bool Equal(const T left, const T right, const T eps)
comparison function for floating point values See http://www.boost.org/libs/test/doc/components/test_...
RGBA, 4 channels, order: red,green,blue,alpha.
Definition: ImageBase.hh:141
bool IsMetric()
Definition: PMatrix.hh:134
void Sub(const T &scalar, Vector3< T > &dest) const
Substraction with a scalar, storing results in destination vector.
Definition: Vector3.hh:705
int GetC(Vector3< double > &C)
computes translation vector origin world coo -&gt; origin camera coo (center), uses decomposition, which is cached
Definition: PMatrix.cpp:165
Vector3< double > UnProjectToPoint(const HomgPoint2D &pos, double depth, unsigned int cam=0, bool IgnoreDistortion=false) const
calculates a 3D point in the global (not the rig) coordinate system, which belongs to the image posit...
Definition: Projection.cpp:349
bool SimplifyMeshSurface_(const BIAS::Image< unsigned char > &Texture, BIAS::Projection &TexP, double reduceValue)
Simplifies a TriangleMesh to a desired percentage of triangles of the original mesh.
K describes the mapping from world coordinates (wcs) to pixel coordinates (pcs).
Definition: KMatrix.hh:48
void GetNeighbours_(int v1, int v2, std::vector< int > &dead_triangles, std::vector< int > &moved_triangles, int &moved_pivot)
get the neighbours of the contracted points and returns two list that contains the triangles to remov...
int GenerateSimplifiedMesh(const Image< float > &DenseDepthMap, const Projection &P, const Image< unsigned char > &Texture, const unsigned int x0, const unsigned int y0, const unsigned int width, const unsigned int height, double minCosAngle=0.8, double maxDiagRatio=3.0)
Implements a 3D quadric and quadric operations.
Definition: Quadric3.hh:25
Vector3< double > newV
Definition: TriangleMesh.hh:46
describes a projective 3D -&gt; 2D mapping in homogenous coordinates
Definition: PMatrix.hh:88
double NormFrobenius() const
Return Frobenius norm = sqrt(trace(A^t * A)).
Definition: Matrix.hh:897
virtual HomgPoint2D Project(const HomgPoint3D &X, unsigned int cam=0, bool IgnoreDistortion=false) const
returns the 2d projection of X in camera cam, where X is given in the global coordinate frame (not th...
Definition: Projection.cpp:250
Camera parameters which define the mapping between rays in the camera coordinate system and pixels in...
int GenerateTexturedQuadStrip(const Image< unsigned char > &Texture, const std::vector< Vector3< double > > &X, const std::vector< Vector2< double > > &x)
Generate a strip of planar quads in 3D space using the given texture coordinates. ...
int SetROICorners(unsigned int UpperLeftX, unsigned int UpperLeftY, unsigned int LowerRightX, unsigned int LowerRightY)
Definition: ImageBase.cpp:1048
void GetBoundingBox(Vector3< double > &minPoint, Vector3< double > &maxPoint) const
Returns the minimal and maximal point of the axis-aligned bounding box of this triangle mesh...
void Set(const HOMGPOINT2D_TYPE &x, const HOMGPOINT2D_TYPE &y)
set elementwise with given 2 euclidean scalar values.
Definition: HomgPoint2D.hh:174
Vector3< double > GetC(unsigned int cam=0) const
return Center of camera with index cam.
Definition: Projection.hh:236
bool IsZero() const
Definition: Vector.cpp:127
double ThreePointsToPlane_(Vector3< double > v1, Vector3< double > v2, Vector3< double > v3, Vector4< double > &erg)
calculates a plan of three points and returns it area
Vector3< T > & Normalize()
normalize this vector to length 1
Definition: Vector3.hh:663
void SetZero()
zeroes the image
Definition: ImageBase.hh:83
Subscript size() const
Definition: vec.h:262
double NormL2() const
the L2 norm sqrt(a^2 + b^2 + c^2)
Definition: Vector3.hh:633
class BIASGeometryBase_EXPORT HomgPoint2D
void Compose(const Matrix3x3< double > &K, const Matrix3x3< double > &R, const Vector3< double > &C)
composes this from K, R and C using P = [ K R&#39; | -K R&#39; C ] with R&#39; = transpose(R) ...
Definition: PMatrix.cpp:581
virtual HomgPoint3D UnProjectToImagePlane(const HomgPoint2D &pos, const double &depth=1.0, bool IgnoreDistortion=false) const =0
map points from image onto unit diameter image plane in 3D.
class BIASGeometryBase_EXPORT HomgPoint3D
int GetK(KMatrix &K)
calibration matrix
Definition: PMatrix.cpp:220
const StorageType ** GetImageDataArray() const
overloaded GetImageDataArray() from ImageBase
Definition: Image.hh:153
A homogeneous plane (in P^3) All points X on the plane p fulfill p &#39; * X = 0.
Definition: HomgPlane3D.hh:46