SegmentableTemplateRecognition.cpp
Go to the documentation of this file.
1 /*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package VisionX::Component
17 * @author Kai Welke <welke at kit dot edu>
18 * @copyright 2013 Humanoids Group, HIS, KIT
19 * @license http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22 
23 #include <fstream>
24 
26 
27 // Core
29 
30 // RobotState
33 
34 // IVT
35 #include <Image/ByteImage.h>
36 #include <Image/ImageProcessor.h>
37 #include <Image/PrimitivesDrawer.h>
38 #include <Helpers/helpers.h>
39 #include <Math/FloatMatrix.h>
40 
41 // MemoryX
45 
46 // This file must be included after all Ice-related headers when Ice 3.5.x is used (e.g. with Ubuntu 14.04 or Debian 7.0)!
47 // GLContext.h indirectly includes X.h which does "#define None", colliding with IceUtil::None.
48 #include <gui/GLContext.h>
49 
50 #include <SimoxUtility/algorithm/string/string_tools.h>
51 
52 #include <boost/scope_exit.hpp>
53 
54 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
55 
56 using namespace armarx;
57 using namespace visionx;
58 using namespace memoryx::EntityWrappers;
59 
60 namespace
61 {
62 
63  struct HSVSegmentationParams
64  {
65  int hueValue;
66  int hueTolerance;
67  int saturationMin;
68  int saturationMax;
69  int valueMin;
70  int valueMax;
71 
72  float minRatio = 0.0f;
73  };
74 
75  HSVSegmentationParams getSegmentationParamsFromColor(CColorParameterSet const& colorParameters, ObjectColor color)
76  {
77  HSVSegmentationParams result = {};
78  int const* colorArray = colorParameters.GetColorParameters(color);
79  if (colorArray)
80  {
81  result.hueValue = colorArray[0];
82  result.hueTolerance = colorArray[1];
83  result.saturationMin = colorArray[2];
84  result.saturationMax = colorArray[3];
85  result.valueMin = colorArray[4];
86  result.valueMax = colorArray[5];
87  }
88  else
89  {
90  ARMARX_WARNING << "Unknown color: " << color;
91  }
92  return result;
93  }
94 
95  struct BlobDetector
96  {
97  BlobDetector(int width, int height)
98  : segmented(width, height, CByteImage::eGrayScale)
99  , temp(width, height, CByteImage::eGrayScale)
100  , hsv(width, height, CByteImage::eRGB24)
101  { }
102 
103  BlobDetector(BlobDetector const&) = delete;
104  BlobDetector& operator = (BlobDetector const&) = delete;
105 
106  Object2DList findColoredRegions(CByteImage* inputImage, int minPixelsPerRegion, std::vector<HSVSegmentationParams>& segmentationParams, int dilationCount = 2)
107  {
108  ::ImageProcessor::CalculateHSVImage(inputImage, &hsv);
109 
110  ::ImageProcessor::Zero(&segmented);
111  for (HSVSegmentationParams const& segParam : segmentationParams)
112  {
113  ::ImageProcessor::FilterHSV(&hsv, &temp,
114  segParam.hueValue, segParam.hueTolerance,
115  segParam.saturationMin, segParam.saturationMax,
116  segParam.valueMin, segParam.valueMax);
117  ::ImageProcessor::Or(&segmented, &temp, &segmented);
118  }
119 
120  for (int i = 0; i < dilationCount / 2; ++i)
121  {
122  ::ImageProcessor::Erode(&segmented, &temp);
123  ::ImageProcessor::Dilate(&temp, &segmented);
124  }
125  for (int i = 0; i < dilationCount / 2; ++i)
126  {
127  ::ImageProcessor::Dilate(&segmented, &temp);
128  ::ImageProcessor::Erode(&temp, &segmented);
129  }
130  if (dilationCount == 0)
131  {
132  ::ImageProcessor::Dilate(&segmented, &temp);
133  ::ImageProcessor::Dilate(&temp, &segmented);
134  ::ImageProcessor::Erode(&segmented, &temp);
135  ::ImageProcessor::CopyImage(&temp, &segmented);
136  }
137 
138  RegionList regions;
139  ::ImageProcessor::FindRegions(&segmented, regions, minPixelsPerRegion);
140 
141  Object2DList objects;
142  objects.reserve(regions.size());
143  int objectId = 0;
144  for (MyRegion const& region : regions)
145  {
146  Object2DEntry object;
147  //object.color = color; // FIXME: We have changed to HSV seg parameters
148  object.region = region;
149 
150  for (HSVSegmentationParams& seg : segmentationParams)
151  {
152  if (seg.minRatio > 0.0f)
153  {
154  int count = 0;
155 
156  int min_hue = (int) seg.hueValue - (int) seg.hueTolerance;
157  int max_hue = (int) seg.hueValue + (int) seg.hueTolerance;
158 
159  if (min_hue < 0)
160  {
161  min_hue += 180;
162  }
163 
164  if (max_hue >= 180)
165  {
166  max_hue -= 180;
167  }
168 
169  for (int y = region.min_y; y < region.max_y; ++y)
170  {
171  for (int x = region.min_x; x < region.max_x; ++x)
172  {
173  int pixelIndex = y * hsv.width + x;
174  int h = hsv.pixels[pixelIndex * 3 + 0];
175  int s = hsv.pixels[pixelIndex * 3 + 1];
176  int v = hsv.pixels[pixelIndex * 3 + 2];
177  if (seg.saturationMin <= s && s <= seg.saturationMax &&
178  seg.valueMin <= v && v <= seg.valueMax)
179  {
180  if (max_hue >= min_hue)
181  {
182  if (min_hue <= h && h <= max_hue)
183  {
184  ++count;
185  }
186  }
187  else
188  {
189  if (h <= max_hue || h >= min_hue)
190  {
191  ++count;
192  }
193  }
194  }
195 
196  }
197  }
198 
199  float ratio = float(count) / region.nPixels;
200  if (ratio < seg.minRatio)
201  {
202  ARMARX_VERBOSE << "Discarding region because ratio too small: " << ratio;
203  continue;
204  }
205  }
206  }
207 
208  object.type = eCompactObject;
209  object.sName = "CompactObject";
210  object.id = objectId++;
211 
212  objects.push_back(object);
213  }
214  return objects;
215  }
216 
217  CByteImage segmented;
218  CByteImage temp;
219  CByteImage hsv;
220  };
221 
222  void CropImageToTemplate(CByteImage* inputImage, MyRegion const& region, CByteImage* outputImage)
223  {
224  int width = region.max_x - region.min_x + 1;
225  int height = region.max_y - region.min_y + 1;
226  int k = outputImage->width;
227 
228 
229  if (width >= height)
230  {
231  int new_height = int((k * height) / float(width) + 0.5f);
232  CByteImage temp_image(k, new_height, CByteImage::eGrayScale);
233 
234  ::ImageProcessor::Resize(inputImage, &temp_image, &region);
235 
236  const int nPixels = k * new_height;
237  unsigned char* output = outputImage->pixels;
238 
239  memcpy(output, temp_image.pixels, nPixels);
240 
241  const int nTotalPixels = k * k;
242 
243  for (int i = nPixels; i < nTotalPixels; i++)
244  {
245  output[i] = 0;
246  }
247  }
248  else
249  {
250  int new_width = int((k * width) / float(height) + 0.5f);
251  CByteImage temp_image(new_width, k, CByteImage::eGrayScale);
252 
253  ::ImageProcessor::Resize(inputImage, &temp_image, &region);
254 
255  const unsigned char* input = temp_image.pixels;
256  unsigned char* output = outputImage->pixels;
257 
258  for (int i = 0, offset = 0, offset2 = 0; i < k; i++)
259  {
260  int j;
261 
262  for (j = 0; j < new_width; j++, offset++, offset2++)
263  {
264  output[offset] = input[offset2];
265  }
266 
267  for (j = new_width; j < k; j++, offset++)
268  {
269  output[offset] = 0;
270  }
271  }
272  }
273  }
274 
275 }
276 
277 
278 SegmentableTemplateRecognition::SegmentableTemplateRecognition()
279 {
280 }
281 
282 SegmentableTemplateRecognition::~SegmentableTemplateRecognition()
283 {
284 
285 }
286 
287 void SegmentableTemplateRecognition::onInitObjectLocalizerProcessor()
288 {
289  offeringTopic(getProperty<std::string>("DebugObserverName").getValue());
290 
291  paramMinPixelsPerRegion = getProperty<int>("MinPixelsPerRegion").getValue();
292 }
293 
294 void SegmentableTemplateRecognition::onExitObjectLocalizerProcessor()
295 {
296 
297 }
298 
299 std::vector<std::string> getFileList(const std::string& path)
300 {
301  std::vector<std::string> result;
302  if (!path.empty())
303  {
304  namespace fs = std::filesystem;
305 
306  fs::path apk_path(path);
307  fs::recursive_directory_iterator end;
308 
309  for (fs::recursive_directory_iterator i(apk_path); i != end; ++i)
310  {
311  const fs::path cp = (*i);
312  result.push_back(cp.string());
313  }
314  }
315  return result;
316 }
317 
318 bool SegmentableTemplateRecognition::initRecognizer()
319 {
320  ARMARX_VERBOSE << "Initializing SegmentableTemplateRecognition";
321 
322  Eigen::Vector3f minPoint = getProperty<Eigen::Vector3f>("MinPoint").getValue();
323  Eigen::Vector3f maxPoint = getProperty<Eigen::Vector3f>("MaxPoint").getValue();
324 
325  Math3d::SetVec(validResultBoundingBoxMin, minPoint(0), minPoint(1), minPoint(2));
326  Math3d::SetVec(validResultBoundingBoxMax, maxPoint(0), maxPoint(1), maxPoint(2));
327 
328  maxEpipolarDistance = getProperty<float>("MaxEpipolarDistance").getValue();
329  std::string colorParemeterFilename = getProperty<std::string>("ColorParameterFile").getValue();
330 
331  paramTemplateMatchThreshold = getProperty<float>("TemplateMatchThreshold");
332  paramSizeRatioThreshold = getProperty<float>("SizeRatioThreshold");
333  paramCorrelationThreshold = getProperty<float>("CorrelationThreshold");
334  paramSingleInstance = getProperty<bool>("SingleInstance");
335 
336  if (!ArmarXDataPath::getAbsolutePath(colorParemeterFilename, colorParemeterFilename))
337  {
338  ARMARX_ERROR << "Could not find color parameter file in ArmarXDataPath: " << colorParemeterFilename;
339  }
340 
341  templatePath = getProperty<std::string>("TemplatePath").getValue();
342  if (!ArmarXDataPath::getAbsolutePath(templatePath, templatePath))
343  {
344  ARMARX_ERROR << "Could not find template path file in ArmarXDataPath: " << templatePath;
345  }
346 
347  ARMARX_IMPORTANT << "Looking in template path: " << templatePath;
348  auto templateFiles = getFileList(templatePath);
349  for (std::string const& templateFile : templateFiles)
350  {
351  if (!simox::alg::ends_with(templateFile, ".seg"))
352  {
353  continue;
354  }
355 
357 
358  ARMARX_INFO << "Loading template: " << templateFile;
359 
360  FILE* file = fopen(templateFile.c_str(), "rb");
361  BOOST_SCOPE_EXIT((file))
362  {
363  fclose(file);
364  }
365  BOOST_SCOPE_EXIT_END;
366 
367  fseek(file, 0L, SEEK_END);
368  long fileSize = ftell(file);
369  fseek(file, 0L, SEEK_SET);
370 
371  void* memory = operator new (fileSize);
372  entry.data.reset(new (memory) SegmentableTemplateHeader);
373  if (fileSize < (long)sizeof(SegmentableTemplateHeader))
374  {
375  ARMARX_WARNING << "File is too short for template header: " << templateFile
376  << " (expected size: " << sizeof(SegmentableTemplateHeader)
377  << ", but got size: " << fileSize << ")";
378  continue;
379  }
380 
381  long read = fread(memory, 1, fileSize, file);
382  if (read != fileSize)
383  {
384  ARMARX_WARNING << "Could not read template file completely (expected size: "
385  << fileSize << ", but read: " << read << ")";
386  continue;
387  }
388 
389  std::string objectName = entry.data->name;
390  {
391  // Load model
392  std::string modelFilename = templatePath + "/" + objectName + ".mat";
393  entry.model.reset(new CFloatMatrix());
394  if (!entry.model->LoadFromFile(modelFilename.c_str()))
395  {
396  ARMARX_WARNING << "Could not load model file: " << modelFilename;
397  continue;
398  }
399  }
400  std::string colorString;
401  {
402  // Load color
403  std::string colorFilename = templatePath + "/" + objectName + ".color";
404  std::ifstream input(colorFilename.c_str());
405  if (!(input >> colorString))
406  {
407  ARMARX_WARNING << "Could not load color file: " << colorFilename;
408  continue;
409  }
410  ObjectColor color = CColorParameterSet::Translate(colorString.c_str());
411  if (color == eNone)
412  {
413  ARMARX_WARNING << "Unknown color string: " << colorString;
414  continue;
415  }
416  entry.color = color;
417  }
418 
419  ARMARX_INFO << "Inserting template into database: " << objectName
420  << ", template count: " << entry.data->templateCount
421  << ", color: " << colorString << " (" << entry.color << ")";
422  database.emplace(objectName, std::move(entry));
423  }
424 
425  if (!colorParameters.LoadFromFile(colorParemeterFilename.c_str()))
426  {
427  throw armarx::LocalException("Could not read color parameter file.");
428  }
429 
430  // create context
431  ImageFormatInfo imageFormat = getImageFormat();
432  contextGL.reset(new CGLContext());
433  contextGL->CreateContext(imageFormat.dimension.width, imageFormat.dimension.height);
434  contextGL->MakeCurrent();
435 
436  m_pOpenGLVisualizer.reset(new COpenGLVisualizer());
437  m_pOpenGLVisualizer->InitByCalibration(getStereoCalibration()->GetRightCalibration());
438 
439  m_pObjectFinderStereo.reset(new CObjectFinderStereo());
440  m_pObjectFinderStereo->SetColorParameterSet(&colorParameters);
441  m_pObjectFinderStereo->Init(getStereoCalibration());
442 
443  ARMARX_INFO << "SegmentableTemplateRecognition is initialized";
444 
445  return true;
446 }
447 
448 bool SegmentableTemplateRecognition::addObjectClass(const memoryx::EntityPtr& objectClassEntity, const memoryx::GridFileManagerPtr& fileManager)
449 {
450  std::string entityName = objectClassEntity->getName();
451  ARMARX_VERBOSE << "Adding object class " << objectClassEntity->getName() << armarx::flush;
452 
453  auto iter = database.find(entityName);
454  return iter != database.end();
455 }
456 
457 memoryx::ObjectLocalizationResultList SegmentableTemplateRecognition::localizeObjectClasses(const std::vector<std::string>& objectClassNames, CByteImage** cameraImages, armarx::MetaInfoSizeBasePtr imageMetaInfo, CByteImage** resultImages)
458 {
459 
460  if (objectClassNames.size() < 1)
461  {
462  ARMARX_WARNING << "objectClassNames.size() = " << objectClassNames.size() << ", something is wrong here! ";
463 
464  memoryx::ObjectLocalizationResultList resultList;
465  return resultList;
466  }
467 
468  ARMARX_VERBOSE << "Localizing " << objectClassNames;
469 
470  contextGL->MakeCurrent();
471 
472  Object3DList objectList;
473 
474  for (std::string const& className : objectClassNames)
475  {
476  Object3DList classObjectList;
477 
478  int dilationCount = 2;
479 
480  auto classEntryIter = database.find(className);
481  if (classEntryIter == database.end())
482  {
483  ARMARX_WARNING << "Could not find database entry for: " << className;
484  continue;
485  }
486  SegmentableTemplateEntry const& classEntry = classEntryIter->second;
487  ObjectColor color = classEntry.color;
488 
489  std::vector<HSVSegmentationParams> hsvSegmentationParams;
490  // FIXME: Hack to allow two colored objects
491  if (color == eYellow3)
492  {
493  hsvSegmentationParams.push_back(getSegmentationParamsFromColor(colorParameters, eRed));
494  hsvSegmentationParams.push_back(getSegmentationParamsFromColor(colorParameters, eYellow));
495  hsvSegmentationParams.back().minRatio = 0.2f;
496  }
497  else
498  {
499  HSVSegmentationParams hsvSegmentationParam = getSegmentationParamsFromColor(colorParameters, color);
500  hsvSegmentationParams.push_back(hsvSegmentationParam);
501  }
502 
503  {
504  CByteImage* leftInput = cameraImages[0];
505  CByteImage* rightInput = cameraImages[1];
506  int width = leftInput->width;
507  int height = leftInput->height;
508  ARMARX_CHECK_EXPRESSION(rightInput->width == width && rightInput->height == height);
509 
510  CByteImage grayImageBig(width, height, CByteImage::eGrayScale);
511  ::ImageProcessor::ConvertImage(leftInput, &grayImageBig, true);
512 
513  BlobDetector leftBlobDetector(width, height);
514  BlobDetector rightBlobDetector(width, height);
515 
516  Object2DList leftObjects = leftBlobDetector.findColoredRegions(leftInput, paramMinPixelsPerRegion, hsvSegmentationParams, dilationCount);
517  Object2DList rightObjects = rightBlobDetector.findColoredRegions(rightInput, paramMinPixelsPerRegion, hsvSegmentationParams, dilationCount);
518 
519  for (Object2DEntry const& entry : leftObjects)
520  {
521  ::PrimitivesDrawer::DrawRegion(resultImages[0], entry.region, 255, 0, 0, 2);
522  }
523 
524  // Calculate blobs with 3D pose from stereo calibration
525  Object3DList blobList;
526 
527  float maxEpipolarDistance = this->maxEpipolarDistance;
528  CStereoCalibration* stereoCalibration = getStereoCalibration();
529  bool inputImagesAreRectified = false; // FIXME: Should this be true?
530  bool useDistortionParameters = !getImagesAreUndistorted();
531  float minZDistance = 500;
532  float maxZDistance = 3000;
533  {
534  for (Object2DEntry& leftObject : leftObjects)
535  {
536  Object2DEntry const* bestMatch = nullptr;
537  float bestDiff = maxEpipolarDistance;
538  for (Object2DEntry& rightObject : rightObjects)
539  {
540  float pixelRatio = leftObject.region.nPixels < rightObject.region.nPixels
541  ? (float) leftObject.region.nPixels / rightObject.region.nPixels
542  : (float) rightObject.region.nPixels / leftObject.region.nPixels;
543  float aspectRatioRatio = leftObject.region.ratio < rightObject.region.ratio
544  ? (float) leftObject.region.ratio / rightObject.region.ratio
545  : (float) rightObject.region.ratio / leftObject.region.ratio;
546 
547  Vec2d leftCentroid = leftObject.region.centroid;
548  Vec2d rightCentroid = rightObject.region.centroid;
549 
550  float yDiff;
551  if (inputImagesAreRectified)
552  {
553  yDiff = fabsf(leftCentroid.y - rightCentroid.y);
554  }
555  else
556  {
557  yDiff = stereoCalibration->CalculateEpipolarLineInLeftImageDistance(leftCentroid, rightCentroid);
558  yDiff = fabs(yDiff);
559  }
560 
561  Vec3d position;
562  stereoCalibration->Calculate3DPoint(leftCentroid, rightCentroid, position,
563  inputImagesAreRectified, useDistortionParameters);
564 
565 
566  if (pixelRatio > 0.5f && aspectRatioRatio > 0.5f && yDiff <= maxEpipolarDistance &&
567  position.z >= minZDistance && position.z <= maxZDistance && yDiff <= bestDiff)
568  {
569  bestDiff = yDiff;
570  bestMatch = &rightObject;
571  }
572  }
573 
574  // We now have the best matching right region in bestMatch
575  if (bestMatch)
576  {
577  Object2DEntry const& rightObject = *bestMatch;
578  Object3DEntry entry;
579 
580  entry.region_left = leftObject.region;
581  entry.region_right = rightObject.region;
582  entry.region_id_left = leftObject.id;
583  entry.region_id_right = rightObject.id;
584 
585  stereoCalibration->Calculate3DPoint(entry.region_left.centroid, entry.region_right.centroid,
586  entry.pose.translation, inputImagesAreRectified, useDistortionParameters);
587 
588  entry.world_point = entry.pose.translation;
589 
590  blobList.push_back(entry);
591  }
592  }
593  }
594 
595  // Visualize results
596  CByteImage* segmented = &leftBlobDetector.segmented;
597  CByteImage* resultImage = resultImages[0];
598  {
599  const unsigned char* input = segmented->pixels;
600  unsigned char* output = resultImage->pixels;
601  int nPixels = segmented->width * segmented->height;
602 
603  // FIXME: Make the highlight color configurable (depending on query color?)
604  int r, g, b;
605  hsv2rgb(0, 255, 100, r, g, b);
606 
607  for (int i = 0; i < nPixels; i++)
608  {
609  if (input[i])
610  {
611  const int offset = 3 * i;
612  output[offset + 0] = r;
613  output[offset + 1] = g;
614  output[offset + 2] = b;
615  }
616  }
617  }
618 
619 
620  // Cut out segmented part of the image
621  // segmentedObject contains the grayscale image of the blob (every other pixel is black)
622  CByteImage segmentedObject(width, height, CByteImage::eGrayScale);
623  ::ImageProcessor::ConvertImage(leftInput, &segmentedObject);
624  ::ImageProcessor::And(segmented, &segmentedObject, &segmentedObject);
625 
626  // Try to match templates into the blobs
627  ARMARX_VERBOSE << "Blob count: " << blobList.size();
628  for (Object3DEntry& blob : blobList)
629  {
630  const MyRegion& region = blob.region_left;
631 
632  int length = SegmentableBitmapWidth;
633  CByteImage objectGray(length, length, CByteImage::eGrayScale);
634 
635  //DEBUG_SAVE_IMAGE(*pGrayImage, "00");
636 
637  // Crop the region of the blob
638  CropImageToTemplate(&segmentedObject, region, &objectGray);
639 
640  //DEBUG_SAVE_IMAGE(objectGray, "01");
641 
642  ::ImageProcessor::ThresholdBinarize(&objectGray, &objectGray, 1);
643  //DEBUG_SAVE_IMAGE(objectGray, "03_binarize");
644 
645  // objectGray is now a black/white image (64x64) of the blob
646  CByteImage* input = &objectGray;
647  SegmentableTemplateHeader const& templateHeader = *classEntry.data;
648 
649  int bestOverlap = 0;
650  int total = 0;
651  SegmentableTemplate const* bestTemplate = nullptr;
652  for (std::uint32_t i = 0; i < templateHeader.templateCount; ++i)
653  {
654  SegmentableTemplate const& template_ = templateHeader.templates[i];
655 
656  // Calculate the overlap between input and template_.bitmap
657  int overlap = 0;
658  int stride = input->width;
659  total = 0;
660  for (int y = 0; y < (int)SegmentableBitmapWidth; ++y)
661  {
662  std::uint32_t const* templateRow = template_.bitmap + y * 2;
663  unsigned char const* inputRow = input->pixels + y * stride;
664  for (int x = 0; x < (int)(SegmentableBitmapWidth / 32); ++x)
665  {
666  std::uint32_t compressed = templateRow[x];
667  // Decompress
668  for (int i = 0; i < 32; ++i)
669  {
670  bool inputPixel = inputRow[x * 32 + i] > 0;
671  bool templatePixel = (compressed & (1 << i)) > 0;
672  if ((inputPixel && templatePixel) || (!inputPixel && !templatePixel))
673  {
674  ++overlap;
675  }
676  ++total;
677  }
678  }
679  }
680 
681  if (overlap > bestOverlap)
682  {
683  bestOverlap = overlap;
684  bestTemplate = &template_;
685  }
686  }
687 
688  float overlapRatio = 1.0f * bestOverlap / (SegmentableBitmapWidth * SegmentableBitmapWidth);
689 
690  if (!bestTemplate || overlapRatio < paramTemplateMatchThreshold)
691  {
692  ARMARX_VERBOSE << "Could not find a matching template (overlap ratio: " << overlapRatio << ")";
693  }
694  else
695  {
696  // Calculate pose from angles
697  {
698  Transformation3d& poseData = blob.pose;
699  Vec3d convention;
700  convention.x = bestTemplate->rotationX;
701  convention.y = bestTemplate->rotationY;
702  convention.z = bestTemplate->rotationZ;
703 
704  // calculate rotation matrix
705  Mat3d temp;
706  Math3d::SetRotationMatX(poseData.rotation, convention.x);
707  Math3d::SetRotationMatZ(temp, convention.z);
708  Math3d::MulMatMat(poseData.rotation, temp, poseData.rotation);
709  Math3d::SetRotationMatY(temp, convention.y);
710  Math3d::MulMatMat(poseData.rotation, temp, poseData.rotation);
711  }
712 
713  // Calculate corrected orientation
714  Vec3d position = blob.pose.translation;
715  Mat3d orientation = blob.pose.rotation;
716  Mat3d& resultOrientation = blob.pose.rotation;
717  {
718  Vec3d u0 = { 0, 0, 1 };
719  Vec3d u = position;
720  Math3d::NormalizeVec(u);
721 
722  // corrective rotation matrix
723  Mat3d correctiveRotation;
724  Vec3d axis;
725  Math3d::CrossProduct(u0, u, axis);
726  float angle = Math3d::Angle(u0, u, axis);
727  Math3d::SetRotationMatAxis(correctiveRotation, axis, angle);
728 
729  // final rotation matrix
730  Math3d::MulMatMat(correctiveRotation, orientation, resultOrientation);
731  }
732 
733  // Calculate corrected position
734  orientation = resultOrientation;
735  float resultCorrelation = 0.0f;
736  float resultSizeRatio = 0.0f;
737  CByteImage* initialMask = &objectGray;
738  int nSize = region.nPixels;
739  int nSimulatedSize = 0;
740  {
741  Vec3d initialPosition = position;
742  // Calculate position in rendered images
743  // Add difference pos_real - pos_sim to pos_real
744  // This results in the simulation producing the expected image
745 
746  CStereoCalibration* stereoCalibration = getStereoCalibration();
747  const int width = stereoCalibration->width;
748  const int height = stereoCalibration->height;
749 
750  Transformation3d pose;
751  Math3d::SetVec(pose.translation, position);
752  Math3d::SetMat(pose.rotation, orientation);
753 
754  CByteImage image_left(width, height, CByteImage::eGrayScale);
755  CByteImage image_right(width, height, CByteImage::eGrayScale);
756  CByteImage* ppImages[2] = { &image_left, &image_right };
757 
758  // render left image
759  m_pOpenGLVisualizer->Clear();
760  m_pOpenGLVisualizer->ActivateShading(false);
761  m_pOpenGLVisualizer->SetProjectionMatrix(stereoCalibration->GetLeftCalibration());
762  //DrawObjectFromFile(m_pOpenGLVisualizer, entry->sOivPath, pose, entry->sName);
763  CFloatMatrix* model = database[className].model.get();
764  m_pOpenGLVisualizer->DrawObject(model, pose);
765  m_pOpenGLVisualizer->GetImage(&image_left);
766  ::ImageProcessor::FlipY(&image_left, &image_left);
767 
768  // render right image
769  m_pOpenGLVisualizer->Clear();
770  m_pOpenGLVisualizer->SetProjectionMatrix(stereoCalibration->GetRightCalibration());
771  //DrawObjectFromFile(m_pOpenGLVisualizer, entry->sOivPath, pose, entry->sName);
772  m_pOpenGLVisualizer->DrawObject(model, pose);
773  m_pOpenGLVisualizer->GetImage(&image_right);
774  ::ImageProcessor::FlipY(&image_right, &image_right);
775 
776  // DEBUG_SAVE_IMAGE(image_left, "04_simulated");
777  // DEBUG_SAVE_IMAGE(image_right, "05_simulated");
778 
779  // calculate stereo
780  m_pObjectFinderStereo->ClearObjectList();
781  m_pObjectFinderStereo->FindObjectsInSegmentedImage(ppImages, 0, eRed, 100, false);
782  m_pObjectFinderStereo->Finalize(0, 10000, false, eRed, 10, false);
783  const Object3DList& objectList = m_pObjectFinderStereo->GetObject3DList();
784 
785  if (objectList.size() == 1)
786  {
787  Object3DEntry const& simEntry = objectList[0];
788 
789  Vec3d positionCorrection;
790  Math3d::SubtractVecVec(position, simEntry.pose.translation, positionCorrection);
791  Math3d::AddVecVec(initialPosition, positionCorrection, position);
792 
793  // calculate quality measure
794  // get simulated size
795  nSimulatedSize = objectList.at(0).region_left.nPixels;
796 
797  // calculate size normalized image
798  const int k = SegmentableBitmapWidth;
799  CByteImage simulatedObject(k, k, CByteImage::eGrayScale);
800  CropImageToTemplate(&image_left, simEntry.region_left, &simulatedObject);
801 
802  int sum = 0;
803  for (std::size_t j = 0; j < SegmentableBitmapSize; j++)
804  {
805  sum += abs(simulatedObject.pixels[j] - initialMask->pixels[j]);
806  }
807 
808  // calculate final measures
809  float divisor = 255.0f * SegmentableBitmapSize;
810  resultCorrelation = 1.0f - sum / divisor;
811  resultSizeRatio = nSimulatedSize < nSize ? float(nSimulatedSize) / nSize : float(nSize) / nSimulatedSize;
812  }
813  else
814  {
815  ARMARX_VERBOSE << "Could not find object '" << className << "' in simulated image";
816  }
817  }
818 
819  if (resultSizeRatio < paramSizeRatioThreshold)
820  {
821  ARMARX_VERBOSE << "Detected blob size is too different in real image compared to simulated image ("
822  << "real size: " << nSize << ", simulated size: " << nSimulatedSize << ", ratio: " << resultSizeRatio
823  << ")";
824  }
825  else if (resultCorrelation < paramCorrelationThreshold)
826  {
827  ARMARX_VERBOSE << "Detected blob does not correlate with the simulated blob ("
828  << "ratio: " << resultCorrelation << ")";
829  }
830  else
831  {
832  blob.sName = className;
833  blob.class_id = 0;
834  blob.quality = resultSizeRatio;//fResultRatio;
835  blob.quality2 = resultCorrelation;//fResultCorrelation;
836  blob.localizationValid = true;
837 
838  ARMARX_VERBOSE << "Found a match (overlap ratio: " << overlapRatio
839  << ", size ratio: " << resultSizeRatio
840  << ", correlation: " << resultCorrelation
841  << ", blob size: " << nSize
842  << ")";
843 
844  classObjectList.push_back(blob);
845  }
846  }
847  }
848  if (classObjectList.size() > 1 && paramSingleInstance)
849  {
850  std::sort(classObjectList.begin(), classObjectList.end(), [](Object3DEntry const & left, Object3DEntry const & right)
851  {
852  return left.quality > right.quality;
853  });
854  Object3DEntry& bestMatch = classObjectList.front();
855  objectList.push_back(bestMatch);
856  }
857  else
858  {
859  objectList.insert(objectList.end(), classObjectList.begin(), classObjectList.end());
860  }
861  }
862  }
863 
864  ARMARX_VERBOSE << "Found " << objectList.size() << " object(s)";
865 
866  // help for problem analysis
867  if (objectList.size() == 0)
868  {
869  ARMARX_INFO << "Looked unsuccessfully for:";
870  std::string color;
871 
872  for (std::string const& className : objectClassNames)
873  {
874  CColorParameterSet::Translate(database[className].color, color);
875  ARMARX_INFO << className << " color: " << color << flush;
876  }
877  }
878  else
879  {
880  visualizeResults(objectList, resultImages);
881  }
882 
883  const auto agentName = getProperty<std::string>("AgentName").getValue();
884 
885  memoryx::ObjectLocalizationResultList resultList;
886 
887  for (Object3DList::iterator iter = objectList.begin() ; iter != objectList.end() ; iter++)
888  {
889  float x = iter->pose.translation.x;
890  float y = iter->pose.translation.y;
891  float z = iter->pose.translation.z;
892 
893  if (seq.count(iter->sName))
894  {
895  seq[iter->sName]++;
896  }
897  else
898  {
899  seq[iter->sName] = 0;
900  }
901 
902  StringVariantBaseMap mapValues;
903  mapValues["x"] = new Variant(x);
904  mapValues["y"] = new Variant(y);
905  mapValues["z"] = new Variant(z);
906  mapValues["name"] = new Variant(iter->sName);
907  mapValues["sequence"] = new Variant(seq[iter->sName]);
908  mapValues["timestamp"] = new Variant(imageMetaInfo->timeProvided / 1000.0 / 1000.0);
909  debugObserver->setDebugChannel("ObjectRecognition", mapValues);
910 
911  // only accept realistic positions
912  if (x > validResultBoundingBoxMin.x && y > validResultBoundingBoxMin.y && z > validResultBoundingBoxMin.z &&
913  x < validResultBoundingBoxMax.x && y < validResultBoundingBoxMax.y && z < validResultBoundingBoxMax.z)
914  {
915  // assemble result
916  memoryx::ObjectLocalizationResult result;
917 
918  // position and orientation
919  Eigen::Vector3f position(iter->pose.translation.x, iter->pose.translation.y, iter->pose.translation.z);
920  Eigen::Matrix3f orientation;
921  orientation << iter->pose.rotation.r1, iter->pose.rotation.r2, iter->pose.rotation.r3,
922  iter->pose.rotation.r4, iter->pose.rotation.r5, iter->pose.rotation.r6,
923  iter->pose.rotation.r7, iter->pose.rotation.r8, iter->pose.rotation.r9;
924 
925  ARMARX_VERBOSE << "Reporting in frame: " << referenceFrameName;
926  result.position = new armarx::FramedPosition(position, referenceFrameName, agentName);
927  result.orientation = new armarx::FramedOrientation(orientation, referenceFrameName, agentName);
928 
929  // calculate noise
930  result.positionNoise = calculateLocalizationUncertainty(iter->region_left.centroid, iter->region_right.centroid);
931 
932  // calculate recognition certainty
933  result.recognitionCertainty = 0.5f + 0.5f * calculateRecognitionCertainty(iter->sName, *iter);
934  result.objectClassName = iter->sName;
935  result.timeStamp = new TimestampVariant(imageMetaInfo->timeProvided);
936 
937  resultList.push_back(result);
938  }
939  else
940  {
941  ARMARX_VERBOSE << "Refused unrealistic localization at position: " << x << " " << y << " " << z;
942  }
943  }
944 
945  ARMARX_VERBOSE << "Finished localizing " << objectClassNames.at(0);
946 
947  return resultList;
948 }
949 
950 
951 
952 float SegmentableTemplateRecognition::calculateRecognitionCertainty(const std::string& objectClassName, const Object3DEntry& entry)
953 {
954  float foundProb = entry.quality * entry.quality2;
955  float notFoundProb = (1 - entry.quality) * (1 - entry.quality2);
956 
957  if (foundProb <= 0)
958  {
959  return 0.0f;
960  }
961 
962  return foundProb / (foundProb + notFoundProb);
963 }
964 
965 
966 
967 void SegmentableTemplateRecognition::visualizeResults(const Object3DList& objectList, CByteImage** resultImages)
968 {
969  m_pOpenGLVisualizer->ActivateShading(true);
970  glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
971 
972 
973  m_pOpenGLVisualizer->SetProjectionMatrix(getStereoCalibration()->GetRightCalibration());
974 
975  m_pOpenGLVisualizer->Clear();
976 
977 
978  for (int i = 0; i < (int) objectList.size(); i++)
979  {
980  const Object3DEntry& entry = objectList.at(i);
981  CFloatMatrix* model = database[entry.sName].model.get();
982  m_pOpenGLVisualizer->DrawObject(model, entry.pose);
983  }
984 
985  const int nImageIndex = 1;
986 
987  if (resultImages && resultImages[nImageIndex])
988  {
989  CByteImage tempImage(resultImages[nImageIndex]);
990  m_pOpenGLVisualizer->GetImage(&tempImage);
991  ::ImageProcessor::FlipY(&tempImage, &tempImage);
992  const int nBytes = 3 * tempImage.width * tempImage.height;
993  const unsigned char* pixels = tempImage.pixels;
994  unsigned char* output = resultImages[nImageIndex]->pixels;
995 
996  for (int i = 0; i < nBytes; i += 3)
997  {
998  if (pixels[i])
999  {
1000  const unsigned char g = pixels[i];
1001  output[i] = g;
1002  output[i + 1] = g;
1003  output[i + 2] = g;
1004  }
1005  }
1006  }
1007 }
1008 
getFileList
std::vector< std::string > getFileList(const std::string &path)
Definition: SegmentableTemplateRecognition.cpp:299
armarx::Variant
The Variant class is described here: Variants.
Definition: Variant.h:224
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:180
armarx::StringVariantBaseMap
std::map< std::string, VariantBasePtr > StringVariantBaseMap
Definition: ManagedIceObject.h:111
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:183
visionx
ArmarX headers.
Definition: OpenPoseStressTest.h:38
visionx::SegmentableTemplateEntry::data
std::unique_ptr< SegmentableTemplateHeader > data
Definition: SegmentableTemplateRecognition.h:68
Pose.h
armarx::TimestampVariant
Definition: TimestampVariant.h:54
GfxTL::Vec2d
VectorXD< 2, double > Vec2d
Definition: VectorXD.h:694
ObjectRecognitionWrapper.h
visionx::SegmentableTemplateHeader
Definition: SegmentableTemplateRecognition.h:58
armarx::memory
Brief description of class memory.
Definition: memory.h:39
visionx::SegmentableTemplateHeader::templates
SegmentableTemplate templates[1]
Definition: SegmentableTemplateRecognition.h:63
visionx::SegmentableTemplate::rotationZ
float rotationZ
Definition: SegmentableTemplateRecognition.h:54
IceInternal::Handle
Definition: forward_declarations.h:8
visionx::SegmentableTemplateEntry::model
std::unique_ptr< CFloatMatrix > model
Definition: SegmentableTemplateRecognition.h:69
armarx::abs
std::vector< T > abs(const std::vector< T > &v)
Definition: VectorHelpers.h:253
SegmentableTemplateRecognition.h
FramedPose.h
armarx::flush
const LogSender::manipulator flush
Definition: LogSender.h:251
GfxTL::Vec3d
VectorXD< 3, double > Vec3d
Definition: VectorXD.h:695
armarx::aron::input
ReaderT::InputType & input
Definition: rw.h:19
TimestampVariant.h
visionx::SegmentableTemplate::rotationX
float rotationX
Definition: SegmentableTemplateRecognition.h:52
armarx::read
void read(auto &eigen, auto *table)
Definition: FTSensorCalibrationGuiWidgetController.cpp:462
MemoryXCoreObjectFactories.h
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:189
armarx::ctrlutil::v
double v(double t, double v0, double a0, double j)
Definition: CtrlUtil.h:39
GfxTL::Matrix3f
MatrixXX< 3, 3, float > Matrix3f
Definition: MatrixXX.h:600
ARMARX_CHECK_EXPRESSION
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
Definition: ExpressionException.h:73
memoryx::EntityWrappers
Definition: AbstractEntityWrapper.cpp:28
armarx::VariantType::FramedOrientation
const VariantTypeId FramedOrientation
Definition: FramedPose.h:40
float
#define float
Definition: 16_Level.h:22
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
memoryx::GridFileManagerPtr
std::shared_ptr< GridFileManager > GridFileManagerPtr
Definition: AbstractEntityWrapper.h:32
visionx::SegmentableTemplateHeader::templateCount
std::uint32_t templateCount
Definition: SegmentableTemplateRecognition.h:62
armarx::ends_with
bool ends_with(const std::string &haystack, const std::string &needle)
Definition: StringHelpers.cpp:50
visionx::SegmentableTemplate
Definition: SegmentableTemplateRecognition.h:50
angle
double angle(const Point &a, const Point &b, const Point &c)
Definition: point.hpp:100
armarx::ArmarXDataPath::getAbsolutePath
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
Definition: ArmarXDataPath.cpp:111
visionx::SegmentableTemplateEntry
Definition: SegmentableTemplateRecognition.h:66
armarx::VariantType::FramedPosition
const VariantTypeId FramedPosition
Definition: FramedPose.h:39
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:186
ArmarXDataPath.h
visionx::SegmentableTemplateEntry::color
ObjectColor color
Definition: SegmentableTemplateRecognition.h:70
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33
visionx::SegmentableTemplate::rotationY
float rotationY
Definition: SegmentableTemplateRecognition.h:53
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28