<commit_before>#include "CNNBallDetector.h"
#include "Tools/CameraGeometry.h"

#include "Tools/PatchWork.h"
#include "Tools/BlackSpotExtractor.h"

#include "Classifier/Fy1500_Conf.h"
#include "Classifier/FrugallyDeep.h"

using namespace std;

CNNBallDetector::CNNBallDetector()
{
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:keyPoints", "draw key points extracted from integral image", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:drawCandidates", "draw ball candidates", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:drawCandidatesResizes", "draw ball candidates (resized)", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:refinePatches", "draw refined ball key points", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:drawPercepts", "draw ball percepts", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:drawPatchContrast", "draw patch contrast (only when contrast-check is in use!", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:draw_projected_ball","", false);

  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:extractPatches", "generate YUVC patches", false);

  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:keyPointsBlack", "draw black key points extracted from integral image", false);

  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:drawPatchInImage", "draw the gray-scale patch like it is passed to the CNN in the image", false);

  theBallKeyPointExtractor = registerModule<BallKeyPointExtractor>("BallKeyPointExtractor", true);
  getDebugParameterList().add(&params);

  cnnMap = createCNNMap();

  setClassifier("fy1500_conf", "fy1500_conf");
}

CNNBallDetector::~CNNBallDetector()
{
  getDebugParameterList().remove(&params);
}

void CNNBallDetector::execute(CameraInfo::CameraID id)
{
  cameraID = id;
  getBallCandidates().reset();

  best.clear();
  // update parameter
  theBallKeyPointExtractor->getModuleT()->setParameter(params.keyDetector);
  theBallKeyPointExtractor->getModuleT()->setCameraId(cameraID);
  theBallKeyPointExtractor->getModuleT()->calculateKeyPoints(best);

  addPatchByLastBall();

  // update selected classifier from parameters
  // TODO: make it more efficient
  setClassifier(params.classifier, params.classifierClose);

  if (best.size() > 0)
  {
    calculateCandidates();
  }

  DEBUG_REQUEST("Vision:CNNBallDetector:refinePatches",
                for (BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i) {
                  //BestPatchList::Patch p = theBallKeyPointExtractor->getModuleT()->refineKeyPoint(*i);
                  RECT_PX(ColorClasses::red, (*i).min.x, (*i).min.y, (*i).max.x, (*i).max.y);
                });

  DEBUG_REQUEST("Vision:CNNBallDetector:drawPercepts",
                for (MultiBallPercept::ConstABPIterator iter = getMultiBallPercept().begin(); iter != getMultiBallPercept().end(); iter++) {
                  if ((*iter).cameraId == cameraID)
                  {
                    CIRCLE_PX(ColorClasses::orange, (int)((*iter).centerInImage.x + 0.5), (int)((*iter).centerInImage.y + 0.5), (int)((*iter).radiusInImage + 0.5));
                  }
                });

  DEBUG_REQUEST("Vision:CNNBallDetector:extractPatches",
                extractPatches(););

  if (params.numberOfExportBestPatches > 0)
  {
    extractPatches();
  }

  DEBUG_REQUEST("Vision:CNNBallDetector:keyPointsBlack",
                BestPatchList bbest;
                for (BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i) {
                  bbest.clear();
                  BlackSpotExtractor::calculateKeyPointsBlackBetter(getBallDetectorIntegralImage(), bbest, (*i).min.x, (*i).min.y, (*i).max.x, (*i).max.y);
                  int idx = 0;
                  for (BestPatchList::reverse_iterator j = bbest.rbegin(); j != bbest.rend(); ++j)
                  {
                    RECT_PX(ColorClasses::red, (*j).min.x, (*j).min.y, (*j).max.x, (*j).max.y);
                    if (++idx > 3)
                    {
                      break;
                    }
                  }
                });
}

std::map<string, std::shared_ptr<AbstractCNNFinder>> CNNBallDetector::createCNNMap()
{
  std::map<string, std::shared_ptr<AbstractCNNFinder>> result;

  // register classifiers
  result.insert({"fy1500_conf", std::make_shared<Fy1500_Conf>()});

#ifndef WIN32
  result.insert({"fdeep_fy1300", std::make_shared<FrugallyDeep>("fy1300.json")});
  result.insert({"fdeep_fy1500", std::make_shared<FrugallyDeep>("fy1500.json")});
#endif

  return result;
}

void CNNBallDetector::setClassifier(const std::string &name, const std::string &nameClose)
{
  auto location = cnnMap.find(name);
  if (location != cnnMap.end())
  {
    currentCNN = location->second;
  }

  location = cnnMap.find(nameClose);
  if (location != cnnMap.end())
  {
    currentCNNClose = location->second;
  }
}

void CNNBallDetector::calculateCandidates()
{
  // the used patch size
  const int patch_size = 16;

  // NOTE: patches are sorted in the ascending order, so start from the end to get the best patches
  int index = 0;
  for (BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i)
  {
    if (getFieldPercept().getValidField().isInside((*i).min) && getFieldPercept().getValidField().isInside((*i).max))
    {
      // limit the max amount of evaluated keys
      if (index > params.maxNumberOfKeys)
      {
        break;
      }

      static BallCandidates::PatchYUVClassified patch((*i).min, (*i).max, patch_size);
      patch.min = (*i).min;
      patch.max = (*i).max;
      if (!getImage().isInside(patch.min) || !getImage().isInside(patch.max))
      {
        continue;
      }

      //
      // add an additional border as post-processing
      int postBorder = (int)(patch.radius() * params.postBorderFactorFar);
      double selectedCNNThreshold = params.cnn.threshold;
      if (patch.width() >= params.postMaxCloseSize) // HACK: use patch size as estimate if close or far away
      {
        postBorder = (int)(patch.radius() * params.postBorderFactorClose);
        selectedCNNThreshold = params.cnn.thresholdClose;
      }

      // resize the patch if possible
      if (getImage().isInside(patch.min - postBorder) && getImage().isInside(patch.max + postBorder))
      {
        patch.min -= postBorder;
        patch.max += postBorder;
      }

      // extract the pixels
      PatchWork::subsampling(getImage(), getFieldColorPercept(), patch);

      // (5) check contrast
      if (params.checkContrast)
      {
        //double stddev = PatchWork::calculateContrastIterative2nd(getImage(),getFieldColorPercept(),min.x,min.y,max.x,max.y,patch_size);
        double stddev = PatchWork::calculateContrastIterative2nd(patch);

        DEBUG_REQUEST("Vision:CNNBallDetector:drawPatchContrast",
                      CANVAS(((cameraID == CameraInfo::Top) ? "ImageTop" : "ImageBottom"));
                      PEN("FF0000", 1); // red
                      Vector2i c = patch.center();
                      CIRCLE(c.x, c.y, stddev / 5.0););

        // skip this patch, if contrast doesn't fullfill minimum
        double selectedContrastMinimum = params.contrastMinimum;
        if (patch.width() >= params.postMaxCloseSize)
        {
          selectedContrastMinimum = params.contrastMinimumClose;
        }
        if (stddev <= selectedContrastMinimum)
        {
          continue;
        }
      }

      PatchWork::multiplyBrightness((cameraID == CameraInfo::Top) ? params.brightnessMultiplierTop : params.brightnessMultiplierBottom, patch);

      DEBUG_REQUEST("Vision:CNNBallDetector:drawPatchInImage",
                    unsigned int offsetX = patch.min.x;
                    unsigned int offsetY = patch.min.y;
                    unsigned int pixelWidth = (unsigned int)((double)(patch.max.x - patch.min.x) / (double)patch.size() + 0.5);

                    for (unsigned int x = 0; x < patch.size(); x++) {
                      for (unsigned int y = 0; y < patch.size(); y++)
                      {
                        unsigned char pixelY = patch.data[x * patch_size + y].pixel.y;

                        // draw each image pixel this patch pixel occupies
                        for (unsigned int px = 0; px < pixelWidth; px++)
                        {
                          for (unsigned int py = 0; py < pixelWidth; py++)
                          {
                            getDebugImageDrawings().drawPointToImage(pixelY, 128, 128,
                                                                     static_cast<int>(offsetX + (x * pixelWidth) + px), offsetY + (y * pixelWidth) + py);
                          }
                        }
                      }
                    });

      // run CNN
      stopwatch.start();

      std::shared_ptr<AbstractCNNFinder> cnn = currentCNN;
      if (patch.width() >= params.postMaxCloseSize)
      {
        cnn = currentCNNClose;
      }

      STOPWATCH_START("CNNBallDetector:predict");
      cnn->find(patch, params.cnn.meanBrightness);
      STOPWATCH_STOP("CNNBallDetector:predict");

      bool found = false;
      double radius = cnn->getRadius();
      Vector2d pos = cnn->getCenter();
      if (cnn->getBallConfidence() >= selectedCNNThreshold && pos.x >= 0.0 && pos.y >= 0.0)
      {
        found = true;
      }

      stopwatch.stop();
      stopwatch_values.push_back(static_cast<double>(stopwatch.lastValue) * 0.001);

      if (found)
      {
        // adjust the center and radius of the patch
        Vector2i ballCenterInPatch(static_cast<int>(pos.x * patch.width()), static_cast<int>(pos.y * patch.width()));

        addBallPercept(patch.min + ballCenterInPatch, radius * patch.width());
      }

      DEBUG_REQUEST("Vision:CNNBallDetector:drawCandidates",
                    // original patch
                    RECT_PX(ColorClasses::skyblue, (*i).min.x, (*i).min.y, (*i).max.x, (*i).max.y);
                    // possibly recised patch
                    RECT_PX(ColorClasses::orange, patch.min.x, patch.min.y, patch.max.x, patch.max.y););

      index++;
    } // end if in field
  }   // end for

} // end calculateCandidates

void CNNBallDetector::extractPatches()
{
  int idx = 0;
  for (BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i)
  {
    if (idx >= params.numberOfExportBestPatches)
    {
      break;
    }
    int offset = ((*i).max.x - (*i).min.x) / 4;
    Vector2i min = (*i).min - offset;
    Vector2i max = (*i).max + offset;

    if (getFieldPercept().getValidField().isInside(min) && getFieldPercept().getValidField().isInside(max))
    {
      BallCandidates::PatchYUVClassified &q = getBallCandidates().nextFreePatchYUVClassified();
      q.min = min;
      q.max = max;
      q.setSize(24);
      q.value = (*i).value;
      PatchWork::subsampling(getImage(), getFieldColorPercept(), q);

      idx++;
    }
  }
}

void CNNBallDetector::addBallPercept(const Vector2i &center, double radius)
{
  const double ballRadius = 50.0;
  MultiBallPercept::BallPercept ballPercept;

  if (CameraGeometry::imagePixelToFieldCoord(
          getCameraMatrix(),
          getImage().cameraInfo,
          center.x,
          center.y,
          ballRadius,
          ballPercept.positionOnField))
  {
    ballPercept.cameraId = cameraID;
    ballPercept.centerInImage = center;
    ballPercept.radiusInImage = radius;

    getMultiBallPercept().add(ballPercept);
    getMultiBallPercept().frameInfoWhenBallWasSeen = getFrameInfo();
  }
}

void CNNBallDetector::addPatchByLastBall()
{
  if (getBallModel().valid)
  {
    Vector3d ballInField;
    ballInField.x = getBallModel().positionPreview.x;
    ballInField.y = getBallModel().positionPreview.y;
    ballInField.z = getFieldInfo().ballRadius;

    Vector2i ballInImage;
    if (CameraGeometry::relativePointToImage(getCameraMatrix(), getImage().cameraInfo, ballInField, ballInImage))
    {

      double estimatedRadius = CameraGeometry::estimatedBallRadius(
          getCameraMatrix(), getImage().cameraInfo, getFieldInfo().ballRadius,
          ballInImage.x, ballInImage.y);

      int border = static_cast<int>((estimatedRadius * 1.2) + 0.5);

      Vector2i start = ballInImage - border;
      Vector2i end = ballInImage + border;

      if (start.y >= 0 && end.y < static_cast<int>(getImage().height()) && start.x >= 0 && end.x < static_cast<int>(getImage().width()))
      {
        DEBUG_REQUEST("Vision:CNNBallDetector:draw_projected_ball",
                      RECT_PX(ColorClasses::red, start.x, start.y, end.x, end.y););
        best.add(
            start.x,
            start.y,
            end.x,
            end.y,
            256.0);
      }
    }
  }
}
<commit_msg>Make the projected ball always loose for overlapped patches.<commit_after>#include "CNNBallDetector.h"
#include "Tools/CameraGeometry.h"

#include "Tools/PatchWork.h"
#include "Tools/BlackSpotExtractor.h"

#include "Classifier/Fy1500_Conf.h"
#include "Classifier/FrugallyDeep.h"

using namespace std;

CNNBallDetector::CNNBallDetector()
{
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:keyPoints", "draw key points extracted from integral image", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:drawCandidates", "draw ball candidates", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:drawCandidatesResizes", "draw ball candidates (resized)", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:refinePatches", "draw refined ball key points", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:drawPercepts", "draw ball percepts", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:drawPatchContrast", "draw patch contrast (only when contrast-check is in use!", false);
  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:draw_projected_ball","", false);

  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:extractPatches", "generate YUVC patches", false);

  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:keyPointsBlack", "draw black key points extracted from integral image", false);

  DEBUG_REQUEST_REGISTER("Vision:CNNBallDetector:drawPatchInImage", "draw the gray-scale patch like it is passed to the CNN in the image", false);

  theBallKeyPointExtractor = registerModule<BallKeyPointExtractor>("BallKeyPointExtractor", true);
  getDebugParameterList().add(&params);

  cnnMap = createCNNMap();

  setClassifier("fy1500_conf", "fy1500_conf");
}

CNNBallDetector::~CNNBallDetector()
{
  getDebugParameterList().remove(&params);
}

void CNNBallDetector::execute(CameraInfo::CameraID id)
{
  cameraID = id;
  getBallCandidates().reset();

  best.clear();
  // update parameter
  theBallKeyPointExtractor->getModuleT()->setParameter(params.keyDetector);
  theBallKeyPointExtractor->getModuleT()->setCameraId(cameraID);
  theBallKeyPointExtractor->getModuleT()->calculateKeyPoints(best);

  addPatchByLastBall();

  // update selected classifier from parameters
  // TODO: make it more efficient
  setClassifier(params.classifier, params.classifierClose);

  if (best.size() > 0)
  {
    calculateCandidates();
  }

  DEBUG_REQUEST("Vision:CNNBallDetector:refinePatches",
                for (BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i) {
                  //BestPatchList::Patch p = theBallKeyPointExtractor->getModuleT()->refineKeyPoint(*i);
                  RECT_PX(ColorClasses::red, (*i).min.x, (*i).min.y, (*i).max.x, (*i).max.y);
                });

  DEBUG_REQUEST("Vision:CNNBallDetector:drawPercepts",
                for (MultiBallPercept::ConstABPIterator iter = getMultiBallPercept().begin(); iter != getMultiBallPercept().end(); iter++) {
                  if ((*iter).cameraId == cameraID)
                  {
                    CIRCLE_PX(ColorClasses::orange, (int)((*iter).centerInImage.x + 0.5), (int)((*iter).centerInImage.y + 0.5), (int)((*iter).radiusInImage + 0.5));
                  }
                });

  DEBUG_REQUEST("Vision:CNNBallDetector:extractPatches",
                extractPatches(););

  if (params.numberOfExportBestPatches > 0)
  {
    extractPatches();
  }

  DEBUG_REQUEST("Vision:CNNBallDetector:keyPointsBlack",
                BestPatchList bbest;
                for (BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i) {
                  bbest.clear();
                  BlackSpotExtractor::calculateKeyPointsBlackBetter(getBallDetectorIntegralImage(), bbest, (*i).min.x, (*i).min.y, (*i).max.x, (*i).max.y);
                  int idx = 0;
                  for (BestPatchList::reverse_iterator j = bbest.rbegin(); j != bbest.rend(); ++j)
                  {
                    RECT_PX(ColorClasses::red, (*j).min.x, (*j).min.y, (*j).max.x, (*j).max.y);
                    if (++idx > 3)
                    {
                      break;
                    }
                  }
                });
}

std::map<string, std::shared_ptr<AbstractCNNFinder>> CNNBallDetector::createCNNMap()
{
  std::map<string, std::shared_ptr<AbstractCNNFinder>> result;

  // register classifiers
  result.insert({"fy1500_conf", std::make_shared<Fy1500_Conf>()});

#ifndef WIN32
  result.insert({"fdeep_fy1300", std::make_shared<FrugallyDeep>("fy1300.json")});
  result.insert({"fdeep_fy1500", std::make_shared<FrugallyDeep>("fy1500.json")});
#endif

  return result;
}

void CNNBallDetector::setClassifier(const std::string &name, const std::string &nameClose)
{
  auto location = cnnMap.find(name);
  if (location != cnnMap.end())
  {
    currentCNN = location->second;
  }

  location = cnnMap.find(nameClose);
  if (location != cnnMap.end())
  {
    currentCNNClose = location->second;
  }
}

void CNNBallDetector::calculateCandidates()
{
  // the used patch size
  const int patch_size = 16;

  // NOTE: patches are sorted in the ascending order, so start from the end to get the best patches
  int index = 0;
  for (BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i)
  {
    if (getFieldPercept().getValidField().isInside((*i).min) && getFieldPercept().getValidField().isInside((*i).max))
    {
      // limit the max amount of evaluated keys
      if (index > params.maxNumberOfKeys)
      {
        break;
      }

      static BallCandidates::PatchYUVClassified patch((*i).min, (*i).max, patch_size);
      patch.min = (*i).min;
      patch.max = (*i).max;
      if (!getImage().isInside(patch.min) || !getImage().isInside(patch.max))
      {
        continue;
      }

      //
      // add an additional border as post-processing
      int postBorder = (int)(patch.radius() * params.postBorderFactorFar);
      double selectedCNNThreshold = params.cnn.threshold;
      if (patch.width() >= params.postMaxCloseSize) // HACK: use patch size as estimate if close or far away
      {
        postBorder = (int)(patch.radius() * params.postBorderFactorClose);
        selectedCNNThreshold = params.cnn.thresholdClose;
      }

      // resize the patch if possible
      if (getImage().isInside(patch.min - postBorder) && getImage().isInside(patch.max + postBorder))
      {
        patch.min -= postBorder;
        patch.max += postBorder;
      }

      // extract the pixels
      PatchWork::subsampling(getImage(), getFieldColorPercept(), patch);

      // (5) check contrast
      if (params.checkContrast)
      {
        //double stddev = PatchWork::calculateContrastIterative2nd(getImage(),getFieldColorPercept(),min.x,min.y,max.x,max.y,patch_size);
        double stddev = PatchWork::calculateContrastIterative2nd(patch);

        DEBUG_REQUEST("Vision:CNNBallDetector:drawPatchContrast",
                      CANVAS(((cameraID == CameraInfo::Top) ? "ImageTop" : "ImageBottom"));
                      PEN("FF0000", 1); // red
                      Vector2i c = patch.center();
                      CIRCLE(c.x, c.y, stddev / 5.0););

        // skip this patch, if contrast doesn't fullfill minimum
        double selectedContrastMinimum = params.contrastMinimum;
        if (patch.width() >= params.postMaxCloseSize)
        {
          selectedContrastMinimum = params.contrastMinimumClose;
        }
        if (stddev <= selectedContrastMinimum)
        {
          continue;
        }
      }

      PatchWork::multiplyBrightness((cameraID == CameraInfo::Top) ? params.brightnessMultiplierTop : params.brightnessMultiplierBottom, patch);

      DEBUG_REQUEST("Vision:CNNBallDetector:drawPatchInImage",
                    unsigned int offsetX = patch.min.x;
                    unsigned int offsetY = patch.min.y;
                    unsigned int pixelWidth = (unsigned int)((double)(patch.max.x - patch.min.x) / (double)patch.size() + 0.5);

                    for (unsigned int x = 0; x < patch.size(); x++) {
                      for (unsigned int y = 0; y < patch.size(); y++)
                      {
                        unsigned char pixelY = patch.data[x * patch_size + y].pixel.y;

                        // draw each image pixel this patch pixel occupies
                        for (unsigned int px = 0; px < pixelWidth; px++)
                        {
                          for (unsigned int py = 0; py < pixelWidth; py++)
                          {
                            getDebugImageDrawings().drawPointToImage(pixelY, 128, 128,
                                                                     static_cast<int>(offsetX + (x * pixelWidth) + px), offsetY + (y * pixelWidth) + py);
                          }
                        }
                      }
                    });

      // run CNN
      stopwatch.start();

      std::shared_ptr<AbstractCNNFinder> cnn = currentCNN;
      if (patch.width() >= params.postMaxCloseSize)
      {
        cnn = currentCNNClose;
      }

      STOPWATCH_START("CNNBallDetector:predict");
      cnn->find(patch, params.cnn.meanBrightness);
      STOPWATCH_STOP("CNNBallDetector:predict");

      bool found = false;
      double radius = cnn->getRadius();
      Vector2d pos = cnn->getCenter();
      if (cnn->getBallConfidence() >= selectedCNNThreshold && pos.x >= 0.0 && pos.y >= 0.0)
      {
        found = true;
      }

      stopwatch.stop();
      stopwatch_values.push_back(static_cast<double>(stopwatch.lastValue) * 0.001);

      if (found)
      {
        // adjust the center and radius of the patch
        Vector2i ballCenterInPatch(static_cast<int>(pos.x * patch.width()), static_cast<int>(pos.y * patch.width()));

        addBallPercept(patch.min + ballCenterInPatch, radius * patch.width());
      }

      DEBUG_REQUEST("Vision:CNNBallDetector:drawCandidates",
                    // original patch
                    RECT_PX(ColorClasses::skyblue, (*i).min.x, (*i).min.y, (*i).max.x, (*i).max.y);
                    // possibly recised patch
                    RECT_PX(ColorClasses::orange, patch.min.x, patch.min.y, patch.max.x, patch.max.y););

      index++;
    } // end if in field
  }   // end for

} // end calculateCandidates

void CNNBallDetector::extractPatches()
{
  int idx = 0;
  for (BestPatchList::reverse_iterator i = best.rbegin(); i != best.rend(); ++i)
  {
    if (idx >= params.numberOfExportBestPatches)
    {
      break;
    }
    int offset = ((*i).max.x - (*i).min.x) / 4;
    Vector2i min = (*i).min - offset;
    Vector2i max = (*i).max + offset;

    if (getFieldPercept().getValidField().isInside(min) && getFieldPercept().getValidField().isInside(max))
    {
      BallCandidates::PatchYUVClassified &q = getBallCandidates().nextFreePatchYUVClassified();
      q.min = min;
      q.max = max;
      q.setSize(24);
      q.value = (*i).value;
      PatchWork::subsampling(getImage(), getFieldColorPercept(), q);

      idx++;
    }
  }
}

void CNNBallDetector::addBallPercept(const Vector2i &center, double radius)
{
  const double ballRadius = 50.0;
  MultiBallPercept::BallPercept ballPercept;

  if (CameraGeometry::imagePixelToFieldCoord(
          getCameraMatrix(),
          getImage().cameraInfo,
          center.x,
          center.y,
          ballRadius,
          ballPercept.positionOnField))
  {
    ballPercept.cameraId = cameraID;
    ballPercept.centerInImage = center;
    ballPercept.radiusInImage = radius;

    getMultiBallPercept().add(ballPercept);
    getMultiBallPercept().frameInfoWhenBallWasSeen = getFrameInfo();
  }
}

void CNNBallDetector::addPatchByLastBall()
{
  if (getBallModel().valid)
  {
    Vector3d ballInField;
    ballInField.x = getBallModel().positionPreview.x;
    ballInField.y = getBallModel().positionPreview.y;
    ballInField.z = getFieldInfo().ballRadius;

    Vector2i ballInImage;
    if (CameraGeometry::relativePointToImage(getCameraMatrix(), getImage().cameraInfo, ballInField, ballInImage))
    {

      double estimatedRadius = CameraGeometry::estimatedBallRadius(
          getCameraMatrix(), getImage().cameraInfo, getFieldInfo().ballRadius,
          ballInImage.x, ballInImage.y);

      int border = static_cast<int>((estimatedRadius) + 0.5);

      Vector2i start = ballInImage - border;
      Vector2i end = ballInImage + border;

      if (start.y >= 0 && end.y < static_cast<int>(getImage().height()) && start.x >= 0 && end.x < static_cast<int>(getImage().width()))
      {
        DEBUG_REQUEST("Vision:CNNBallDetector:draw_projected_ball",
                      RECT_PX(ColorClasses::red, start.x, start.y, end.x, end.y);
                      CIRCLE_PX(ColorClasses::red, ballInImage.x, ballInImage.y, border);
                      );
        best.add(
            start.x,
            start.y,
            end.x,
            end.y,
            -1.0);
      }
    }
  }
}
<|endoftext|>


<commit_before>/*
    Copyright 2011 Brain Research Institute, Melbourne, Australia

    Written by Robert E. Smith, 2012.

    This file is part of MRtrix.

    MRtrix is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MRtrix is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.

 */

#include "dwi/tractography/properties.h"
#include "dwi/tractography/seeding/seeding.h"

namespace MR
{
  namespace DWI
  {
    namespace Tractography
    {
      namespace Seeding
      {

      using namespace App;

      const OptionGroup SeedOption = OptionGroup ("Tractography seeding options")

      + Option ("seed_sphere", "spherical seed as four comma-separated values (XYZ position and radius)").allow_multiple()
        + Argument ("spec").type_sequence_float()

      + Option ("seed_image", "seed streamlines entirely at random within a mask image "
                              "(this is the same behaviour as the streamline seeding in MRtrix 0.2)").allow_multiple()
        + Argument ("image").type_image_in()

      + Option ("seed_random_per_voxel", "seed a fixed number of streamlines per voxel in a mask image; random placement of seeds in each voxel").allow_multiple()
        + Argument ("image").type_image_in()
        + Argument ("num_per_voxel").type_integer (1, 1, std::numeric_limits<int>::max())

      + Option ("seed_grid_per_voxel", "seed a fixed number of streamlines per voxel in a mask image; place seeds on a 3D mesh grid "
                                       "(grid_size argument is per axis; so a grid_size of 3 results in 27 seeds per voxel)").allow_multiple()
        + Argument ("image").type_image_in()
        + Argument ("grid_size").type_integer (1, 1, std::numeric_limits<int>::max())

      + Option ("seed_rejection", "seed from an image using rejection sampling (higher values = more probable to seed from)").allow_multiple()
        + Argument ("image").type_image_in()

      + Option ("seed_gmwmi", "seed from the grey matter - white matter interface (only valid if using ACT framework)").allow_multiple()
        + Argument ("seed_image").type_image_in()

      + Option ("seed_dynamic", "determine seed points dynamically using the SIFT model (must NOT provide any other seeding mechanism)") // Don't allow multiple
        + Argument ("fod_image").type_image_in()

      + Option ("max_seed_attempts", "set the maximum number of times that the tracking algorithm should "
                                     "attempt to find an appropriate tracking direction from a given seed point")
        + Argument ("count").type_integer (1, 1, 1e6)

      + Option ("output_seeds", "output the seed location of all successful streamlines to a file")
        + Argument ("path").type_file_out();

      void load_tracking_seeds (Properties& properties)
      {

        List& list (properties.seeds);

        App::Options opt = get_options ("seed_sphere");
        for (size_t i = 0; i < opt.size(); ++i) {
          Sphere* seed = new Sphere (opt[i][0], list.get_rng());
          list.add (seed);
        }

        opt = get_options ("seed_image");
        for (size_t i = 0; i < opt.size(); ++i) {
          SeedMask* seed = new SeedMask (opt[i][0], list.get_rng());
          list.add (seed);
        }

        opt = get_options ("seed_random_per_voxel");
        for (size_t i = 0; i < opt.size(); ++i) {
          Random_per_voxel* seed = new Random_per_voxel (opt[i][0], list.get_rng(), opt[i][1]);
          list.add (seed);
        }

        opt = get_options ("seed_grid_per_voxel");
        for (size_t i = 0; i < opt.size(); ++i) {
          Grid_per_voxel* seed = new Grid_per_voxel (opt[i][0], list.get_rng(), opt[i][1]);
          list.add (seed);
        }

        opt = get_options ("seed_rejection");
        for (size_t i = 0; i < opt.size(); ++i) {
          Rejection* seed = new Rejection (opt[i][0], list.get_rng());
          list.add (seed);
        }

        opt = get_options ("seed_gmwmi");
        if (opt.size()) {
          App::Options opt_act = get_options ("act");
          if (!opt_act.size())
            throw Exception ("Cannot perform GM-WM Interface seeding without ACT segmented tissue image");
          for (size_t i = 0; i < opt.size(); ++i) {
            GMWMI* seed = new GMWMI (opt[i][0], list.get_rng(), str(opt_act[0][0]));
            list.add (seed);
          }
        }

        // Can't instantiate the dynamic seeder here; internal FMLS segmenter has to use the same Directions::Set as TrackMapperDixel
        opt = get_options ("seed_dynamic");
        if (opt.size()) {
          if (list.num_seeds())
            throw Exception ("If performing dynamic streamline seeding, cannot specify any other type of seed!");
          properties["seed_dynamic"] = str(opt[0][0]);
        } else if (!list.num_seeds()) {
          throw Exception ("Must provide at least one source of streamline seeds!");
        }

        opt = get_options ("max_seed_attempts");
        if (opt.size()) properties["max_seed_attempts"] = std::string (opt[0][0]);

        opt = get_options ("output_seeds");
        if (opt.size()) properties["seed_output"] = std::string (opt[0][0]);

      }

      }
    }
  }
}

<commit_msg>tckgen: Update description of -seed_dynamic option<commit_after>/*
    Copyright 2011 Brain Research Institute, Melbourne, Australia

    Written by Robert E. Smith, 2012.

    This file is part of MRtrix.

    MRtrix is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MRtrix is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.

 */

#include "dwi/tractography/properties.h"
#include "dwi/tractography/seeding/seeding.h"

namespace MR
{
  namespace DWI
  {
    namespace Tractography
    {
      namespace Seeding
      {

      using namespace App;

      const OptionGroup SeedOption = OptionGroup ("Tractography seeding options")

      + Option ("seed_sphere", "spherical seed as four comma-separated values (XYZ position and radius)").allow_multiple()
        + Argument ("spec").type_sequence_float()

      + Option ("seed_image", "seed streamlines entirely at random within a mask image "
                              "(this is the same behaviour as the streamline seeding in MRtrix 0.2)").allow_multiple()
        + Argument ("image").type_image_in()

      + Option ("seed_random_per_voxel", "seed a fixed number of streamlines per voxel in a mask image; random placement of seeds in each voxel").allow_multiple()
        + Argument ("image").type_image_in()
        + Argument ("num_per_voxel").type_integer (1, 1, std::numeric_limits<int>::max())

      + Option ("seed_grid_per_voxel", "seed a fixed number of streamlines per voxel in a mask image; place seeds on a 3D mesh grid "
                                       "(grid_size argument is per axis; so a grid_size of 3 results in 27 seeds per voxel)").allow_multiple()
        + Argument ("image").type_image_in()
        + Argument ("grid_size").type_integer (1, 1, std::numeric_limits<int>::max())

      + Option ("seed_rejection", "seed from an image using rejection sampling (higher values = more probable to seed from)").allow_multiple()
        + Argument ("image").type_image_in()

      + Option ("seed_gmwmi", "seed from the grey matter - white matter interface (only valid if using ACT framework)").allow_multiple()
        + Argument ("seed_image").type_image_in()

      + Option ("seed_dynamic", "determine seed points dynamically using the SIFT model (must not provide any other seeding mechanism). "
                                "Note that while this seeding mechanism improves the distribution of reconstructed streamlines density, "
                                "it should NOT be used as a substitute for the SIFT method itself.") // Don't allow multiple
        + Argument ("fod_image").type_image_in()

      + Option ("max_seed_attempts", "set the maximum number of times that the tracking algorithm should "
                                     "attempt to find an appropriate tracking direction from a given seed point")
        + Argument ("count").type_integer (1, 1, 1e6)

      + Option ("output_seeds", "output the seed location of all successful streamlines to a file")
        + Argument ("path").type_file_out();

      void load_tracking_seeds (Properties& properties)
      {

        List& list (properties.seeds);

        App::Options opt = get_options ("seed_sphere");
        for (size_t i = 0; i < opt.size(); ++i) {
          Sphere* seed = new Sphere (opt[i][0], list.get_rng());
          list.add (seed);
        }

        opt = get_options ("seed_image");
        for (size_t i = 0; i < opt.size(); ++i) {
          SeedMask* seed = new SeedMask (opt[i][0], list.get_rng());
          list.add (seed);
        }

        opt = get_options ("seed_random_per_voxel");
        for (size_t i = 0; i < opt.size(); ++i) {
          Random_per_voxel* seed = new Random_per_voxel (opt[i][0], list.get_rng(), opt[i][1]);
          list.add (seed);
        }

        opt = get_options ("seed_grid_per_voxel");
        for (size_t i = 0; i < opt.size(); ++i) {
          Grid_per_voxel* seed = new Grid_per_voxel (opt[i][0], list.get_rng(), opt[i][1]);
          list.add (seed);
        }

        opt = get_options ("seed_rejection");
        for (size_t i = 0; i < opt.size(); ++i) {
          Rejection* seed = new Rejection (opt[i][0], list.get_rng());
          list.add (seed);
        }

        opt = get_options ("seed_gmwmi");
        if (opt.size()) {
          App::Options opt_act = get_options ("act");
          if (!opt_act.size())
            throw Exception ("Cannot perform GM-WM Interface seeding without ACT segmented tissue image");
          for (size_t i = 0; i < opt.size(); ++i) {
            GMWMI* seed = new GMWMI (opt[i][0], list.get_rng(), str(opt_act[0][0]));
            list.add (seed);
          }
        }

        // Can't instantiate the dynamic seeder here; internal FMLS segmenter has to use the same Directions::Set as TrackMapperDixel
        opt = get_options ("seed_dynamic");
        if (opt.size()) {
          if (list.num_seeds())
            throw Exception ("If performing dynamic streamline seeding, cannot specify any other type of seed!");
          properties["seed_dynamic"] = str(opt[0][0]);
        } else if (!list.num_seeds()) {
          throw Exception ("Must provide at least one source of streamline seeds!");
        }

        opt = get_options ("max_seed_attempts");
        if (opt.size()) properties["max_seed_attempts"] = std::string (opt[0][0]);

        opt = get_options ("output_seeds");
        if (opt.size()) properties["seed_output"] = std::string (opt[0][0]);

      }

      }
    }
  }
}

<|endoftext|>


<commit_before>#ifndef ALEPH_CONTAINERS_DIMENSIONALITY_ESTIMATORS_HH__
#define ALEPH_CONTAINERS_DIMENSIONALITY_ESTIMATORS_HH__

#include <stdexcept>
#include <vector>

#include <cmath>

#include <aleph/math/KahanSummation.hh>

namespace aleph
{

namespace containers
{

/**
  Estimates local intrinsic dimensionality of a container using its
  nearest neighbours. The underlying assumption of the estimator is
  that points are locally uniformly distributed uniformly. Use this
  estimator with care when analysing unknown data.

  @param container Container to use for dimensionality estimation
  @param k         Number of nearest neighbours
  @param distance  Distance measure

  @returns Vector of local intrinsic dimensionality estimates. Note
           that the numbers are reported *without* rounding.
*/

template <
  class Distance,
  class Container,
  class Wrapper
> std::vector<double> estimateLocalDimensionalityNearestNeighbours( const Container& container,
                                                                    unsigned k,
                                                                    Distance /* distance */ = Distance() )
{
  using IndexType = typename Wrapper::IndexType;

  std::vector< std::vector<IndexType> > indices;
  std::vector< std::vector<double> > distances;

  Wrapper nnWrapper( container );
  nnWrapper.neighbourSearch( k+1, indices, distances );

  auto n = container.size();

  std::vector<double> estimates;
  estimates.reserve( n );

  for( decltype(n) i = 0; i < n; i++ )
  {
    auto&& nnDistances = distances.at(i);
    auto r1            = aleph::math::accumulate_kahan( nnDistances.begin(), nnDistances.begin() + k,     0.0 ) / static_cast<double>(k  );
    auto r2            = aleph::math::accumulate_kahan( nnDistances.begin(), nnDistances.begin() + k + 1, 0.0 ) / static_cast<double>(k+1);

    estimates.push_back( r1 / ( (r2-r1)*k ) );
  }

  return estimates;
}

/**
  Estimates local intrinsic dimensionality of a container using its
  nearest neighbours. No assumptions about the distribution of data
  points are made. The function uses an iteration over a *range* of
  nearest neighbours and solves a regression problem.

  Please see the publication

    > An evaluation of intrinsic dimensionality estimators\n
    > Peter J. Verveer and Robert P. W. Duin\n
    > IEEE Transactions on Pattern Analysis and Machine Intelligence 17.1, pp. 81-86, 1985

  for more details.

  @param container Container to use for dimensionality estimation

  @param kMin      Minimum number of nearest neighbours to use in
                   computing local dimensionality estimates.

  @param kMax      Maximum number of nearest neighbours to use in
                   computing local dimensionality estimates. This
                   parameter influences performance.

  @param distance  Distance measure

  @returns Vector of local intrinsic dimensionality estimates. Note that
           the numbers are reported *without* rounding.
*/

template <
  class Distance,
  class Container,
  class Wrapper
> std::vector<double> estimateLocalDimensionalityNearestNeighbours( const Container& container,
                                                                    unsigned kMin,
                                                                    unsigned kMax,
                                                                    Distance /* distance */ = Distance() )

{
  if( kMin > kMax )
    std::swap( kMin, kMax );

  if( kMax == 0 || kMin == 0 )
    throw std::runtime_error( "Expecting non-zero number of nearest neighbours" );

  using IndexType   = typename Wrapper::IndexType;
  using ElementType = typename Wrapper::ElementType;

  std::vector< std::vector<IndexType> > indices;
  std::vector< std::vector<ElementType> > distances;

  Wrapper nnWrapper( container );
  nnWrapper.neighbourSearch( kMax, indices, distances );

  auto n = container.size();

  std::vector<double> estimates;
  estimates.reserve( n );

  for( decltype(n) i = 0; i < n; i++ )
  {
    auto&& nnDistances = distances.at(i);

    std::vector<double> localEstimates;
    localEstimates.reserve( kMax );

    for( unsigned k = kMin; k < kMax; k++ )
    {
      auto r = aleph::math::accumulate_kahan( nnDistances.begin(), nnDistances.begin() + k, 0.0 ) / static_cast<double>(k);
      localEstimates.emplace_back( r );
    }

    // The dimensionality estimates consist of two terms. The first term
    // is similar to the local biased dimensionality estimate.

    std::vector<double> firstTerms;
    firstTerms.reserve( kMax );

    std::vector<double> secondTerms;
    secondTerms.reserve( kMax );

    for( unsigned k = kMin; k < kMax - 1; k++ )
    {
      auto index = k - kMin;
      auto r1    = localEstimates.at(index);
      auto r2    = localEstimates.at(index+1);

      firstTerms.emplace_back ( ( (r2-r1) * r1 ) / k );
      secondTerms.emplace_back( ( (r2-r1) * (r2-r1) ) );
    }

    auto s = aleph::math::accumulate_kahan( firstTerms.begin() , firstTerms.end() , 0.0 );
    auto t = aleph::math::accumulate_kahan( secondTerms.begin(), secondTerms.end(), 0.0 );

    estimates.push_back( s / t );
  }

  return estimates;

}

/**
  Estimates local intrinsic dimensionality of a container using its
  nearest neighbours. No assumptions about the distribution of data
  points are made. The function uses *maximum likelihood estimates*
  for the dimensionality estimates.

  Please see the publication

    > Maximum Likelihood Estimation of Intrinsic Dimension\n
    > Elizaveta Levina and Peter J. Bickel\n
    > Advances in Neural Information Processing Systems, 2005

  for more details.

  @param container Container to use for dimensionality estimation

  @param kMin      Minimum number of nearest neighbours to use in
                   computing local dimensionality estimates.

  @param kMax      Maximum number of nearest neighbours to use in
                   computing local dimensionality estimates. This
                   parameter influences performance.

  @param distance  Distance measure

  @returns Vector of local intrinsic dimensionality estimates. Note that
           the numbers are reported *without* rounding.
*/

template <
  class Distance,
  class Container,
  class Wrapper
> std::vector<double> estimateLocalDimensionalityNearestNeighboursMLE( const Container& container,
                                                                       unsigned kMin,
                                                                       unsigned kMax,
                                                                       Distance /* distance */ = Distance() )
{
  if( kMin > kMax )
    std::swap( kMin, kMax );

  if( kMax == 0 || kMin == 0 )
    throw std::runtime_error( "Expecting non-zero number of nearest neighbours" );

  using IndexType = typename Wrapper::IndexType;

  std::vector< std::vector<IndexType> > indices;
  std::vector< std::vector<double> > distances;

  Wrapper nnWrapper( container );
  nnWrapper.neighbourSearch( kMax, indices, distances );

  auto n = container.size();

  std::vector<double> estimates;
  estimates.reserve( n );

  for( decltype(n) i = 0; i < n; i++ )
  {
    auto&& nnDistances = distances.at(i);

    std::vector<double> localEstimates;
    localEstimates.reserve( kMax );

    // This follows the notation in the original paper. I dislike using
    // $T_k$ to denote distances, though.
    for( unsigned k = kMin - 1; k < kMax; k++ )
    {
      // Nothing to do here...
      if( k == 0 )
        continue;

      std::vector<double> logEstimates;
      logEstimates.reserve( k-1 );

      for( auto it = nnDistances.begin(); it != nnDistances.begin() + k; ++it )
      {
        if( *it > 0.0 && nnDistances.at(k) > 0.0 )
          logEstimates.push_back( std::log( nnDistances.at(k) / *it ) );

        // This defines log(0) = 0, as usually done in information
        // theory. The original paper does not handle this.
        else
          logEstimates.push_back( 0.0 );
      }

      auto mk = k > 1 ? 1.0 / (k-1) * aleph::math::accumulate_kahan( logEstimates.begin(), logEstimates.end(), 0.0 )
                      : 0.0;

      if( mk > 0.0 )
        mk = 1.0 / mk;
      else
        mk = 0.0;

      localEstimates.push_back( mk );
    }

    estimates.push_back( aleph::math::accumulate_kahan( localEstimates.begin(), localEstimates.end(), 0.0 ) / (kMax - kMin + 1) );
  }

  return estimates;
}

} // namespace containers

} // namespace aleph

#endif
<commit_msg>Fixed element types for dimensionality estimators<commit_after>#ifndef ALEPH_CONTAINERS_DIMENSIONALITY_ESTIMATORS_HH__
#define ALEPH_CONTAINERS_DIMENSIONALITY_ESTIMATORS_HH__

#include <stdexcept>
#include <vector>

#include <cmath>

#include <aleph/math/KahanSummation.hh>

namespace aleph
{

namespace containers
{

/**
  Estimates local intrinsic dimensionality of a container using its
  nearest neighbours. The underlying assumption of the estimator is
  that points are locally uniformly distributed uniformly. Use this
  estimator with care when analysing unknown data.

  @param container Container to use for dimensionality estimation
  @param k         Number of nearest neighbours
  @param distance  Distance measure

  @returns Vector of local intrinsic dimensionality estimates. Note
           that the numbers are reported *without* rounding.
*/

template <
  class Distance,
  class Container,
  class Wrapper
> std::vector<double> estimateLocalDimensionalityNearestNeighbours( const Container& container,
                                                                    unsigned k,
                                                                    Distance /* distance */ = Distance() )
{
  using IndexType   = typename Wrapper::IndexType;
  using ElementType = typename Wrapper::ElementType;

  std::vector< std::vector<IndexType> > indices;
  std::vector< std::vector<ElementType> > distances;

  Wrapper nnWrapper( container );
  nnWrapper.neighbourSearch( k+1, indices, distances );

  auto n = container.size();

  std::vector<double> estimates;
  estimates.reserve( n );

  for( decltype(n) i = 0; i < n; i++ )
  {
    auto&& nnDistances = distances.at(i);
    auto r1            = aleph::math::accumulate_kahan( nnDistances.begin(), nnDistances.begin() + k,     0.0 ) / static_cast<double>(k  );
    auto r2            = aleph::math::accumulate_kahan( nnDistances.begin(), nnDistances.begin() + k + 1, 0.0 ) / static_cast<double>(k+1);

    estimates.push_back( r1 / ( (r2-r1)*k ) );
  }

  return estimates;
}

/**
  Estimates local intrinsic dimensionality of a container using its
  nearest neighbours. No assumptions about the distribution of data
  points are made. The function uses an iteration over a *range* of
  nearest neighbours and solves a regression problem.

  Please see the publication

    > An evaluation of intrinsic dimensionality estimators\n
    > Peter J. Verveer and Robert P. W. Duin\n
    > IEEE Transactions on Pattern Analysis and Machine Intelligence 17.1, pp. 81-86, 1985

  for more details.

  @param container Container to use for dimensionality estimation

  @param kMin      Minimum number of nearest neighbours to use in
                   computing local dimensionality estimates.

  @param kMax      Maximum number of nearest neighbours to use in
                   computing local dimensionality estimates. This
                   parameter influences performance.

  @param distance  Distance measure

  @returns Vector of local intrinsic dimensionality estimates. Note that
           the numbers are reported *without* rounding.
*/

template <
  class Distance,
  class Container,
  class Wrapper
> std::vector<double> estimateLocalDimensionalityNearestNeighbours( const Container& container,
                                                                    unsigned kMin,
                                                                    unsigned kMax,
                                                                    Distance /* distance */ = Distance() )

{
  if( kMin > kMax )
    std::swap( kMin, kMax );

  if( kMax == 0 || kMin == 0 )
    throw std::runtime_error( "Expecting non-zero number of nearest neighbours" );

  using IndexType   = typename Wrapper::IndexType;
  using ElementType = typename Wrapper::ElementType;

  std::vector< std::vector<IndexType> > indices;
  std::vector< std::vector<ElementType> > distances;

  Wrapper nnWrapper( container );
  nnWrapper.neighbourSearch( kMax, indices, distances );

  auto n = container.size();

  std::vector<double> estimates;
  estimates.reserve( n );

  for( decltype(n) i = 0; i < n; i++ )
  {
    auto&& nnDistances = distances.at(i);

    std::vector<double> localEstimates;
    localEstimates.reserve( kMax );

    for( unsigned k = kMin; k < kMax; k++ )
    {
      auto r = aleph::math::accumulate_kahan( nnDistances.begin(), nnDistances.begin() + k, 0.0 ) / static_cast<double>(k);
      localEstimates.emplace_back( r );
    }

    // The dimensionality estimates consist of two terms. The first term
    // is similar to the local biased dimensionality estimate.

    std::vector<double> firstTerms;
    firstTerms.reserve( kMax );

    std::vector<double> secondTerms;
    secondTerms.reserve( kMax );

    for( unsigned k = kMin; k < kMax - 1; k++ )
    {
      auto index = k - kMin;
      auto r1    = localEstimates.at(index);
      auto r2    = localEstimates.at(index+1);

      firstTerms.emplace_back ( ( (r2-r1) * r1 ) / k );
      secondTerms.emplace_back( ( (r2-r1) * (r2-r1) ) );
    }

    auto s = aleph::math::accumulate_kahan( firstTerms.begin() , firstTerms.end() , 0.0 );
    auto t = aleph::math::accumulate_kahan( secondTerms.begin(), secondTerms.end(), 0.0 );

    estimates.push_back( s / t );
  }

  return estimates;

}

/**
  Estimates local intrinsic dimensionality of a container using its
  nearest neighbours. No assumptions about the distribution of data
  points are made. The function uses *maximum likelihood estimates*
  for the dimensionality estimates.

  Please see the publication

    > Maximum Likelihood Estimation of Intrinsic Dimension\n
    > Elizaveta Levina and Peter J. Bickel\n
    > Advances in Neural Information Processing Systems, 2005

  for more details.

  @param container Container to use for dimensionality estimation

  @param kMin      Minimum number of nearest neighbours to use in
                   computing local dimensionality estimates.

  @param kMax      Maximum number of nearest neighbours to use in
                   computing local dimensionality estimates. This
                   parameter influences performance.

  @param distance  Distance measure

  @returns Vector of local intrinsic dimensionality estimates. Note that
           the numbers are reported *without* rounding.
*/

template <
  class Distance,
  class Container,
  class Wrapper
> std::vector<double> estimateLocalDimensionalityNearestNeighboursMLE( const Container& container,
                                                                       unsigned kMin,
                                                                       unsigned kMax,
                                                                       Distance /* distance */ = Distance() )
{
  if( kMin > kMax )
    std::swap( kMin, kMax );

  if( kMax == 0 || kMin == 0 )
    throw std::runtime_error( "Expecting non-zero number of nearest neighbours" );

  using IndexType   = typename Wrapper::IndexType;
  using ElementType = typename Wrapper::ElementType;

  std::vector< std::vector<IndexType> > indices;
  std::vector< std::vector<ElementType> > distances;

  Wrapper nnWrapper( container );
  nnWrapper.neighbourSearch( kMax, indices, distances );

  auto n = container.size();

  std::vector<double> estimates;
  estimates.reserve( n );

  for( decltype(n) i = 0; i < n; i++ )
  {
    auto&& nnDistances = distances.at(i);

    std::vector<double> localEstimates;
    localEstimates.reserve( kMax );

    // This follows the notation in the original paper. I dislike using
    // $T_k$ to denote distances, though.
    for( unsigned k = kMin - 1; k < kMax; k++ )
    {
      // Nothing to do here...
      if( k == 0 )
        continue;

      std::vector<double> logEstimates;
      logEstimates.reserve( k-1 );

      for( auto it = nnDistances.begin(); it != nnDistances.begin() + k; ++it )
      {
        if( *it > 0.0 && nnDistances.at(k) > 0.0 )
          logEstimates.push_back( std::log( nnDistances.at(k) / *it ) );

        // This defines log(0) = 0, as usually done in information
        // theory. The original paper does not handle this.
        else
          logEstimates.push_back( 0.0 );
      }

      auto mk = k > 1 ? 1.0 / (k-1) * aleph::math::accumulate_kahan( logEstimates.begin(), logEstimates.end(), 0.0 )
                      : 0.0;

      if( mk > 0.0 )
        mk = 1.0 / mk;
      else
        mk = 0.0;

      localEstimates.push_back( mk );
    }

    estimates.push_back( aleph::math::accumulate_kahan( localEstimates.begin(), localEstimates.end(), 0.0 ) / (kMax - kMin + 1) );
  }

  return estimates;
}

} // namespace containers

} // namespace aleph

#endif
<|endoftext|>


<commit_before>//	Copyright 2017 Adam Smith
//	Licensed under the Apache License, Version 2.0 (the "License");
//	you may not use this file except in compliance with the License.
//	You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
//	Unless required by applicable law or agreed to in writing, software
//	distributed under the License is distributed on an "AS IS" BASIS,
//	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//	See the License for the specific language governing permissions and
//	limitations under the License.

#ifndef ASMITH_REFLECTION_CONSTRUCTOR_HPP
#define ASMITH_REFLECTION_CONSTRUCTOR_HPP

namespace asmith {

	class reflection_class;

	class reflection_constructor {
	protected:
		virtual void call_(void*, const void*) const = 0;
	public:
		virtual ~reflection_constructor() {}

		virtual size_t get_parameter_count() const = 0;
		virtual const reflection_class& get_parameter(size_t) const = 0;
		virtual size_t get_modifiers() const = 0;

		void call(void* aObject) const {
			call_(aObject, nullptr);
		}

		template<class P1>
		void call(void* aObject, P1 p1) const {
			call_(&aObject, &p1);
		}

		template<class P1, class P2>
		void call(void* aObject, P1 p1, P2 p2) const {
			call_(aObject, &p1);
		}

		template<class P1, class P2, class P3>
		void call(void* aObject, P1 p1, P2 p2, P3 p3) const {
			call_(aObject, &p1);
		}

		template<class P1, class P2, class P3, class P4>
		void call(void* aObject, P1 p1, P2 p2, P3 p3, P4 p4) const {
			call_(aObject, &p1);
		}

		template<class P1, class P2, class P3, class P4, class P5>
		void call(void* aObject, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) const {
			call_(&aObject, &p1);
		}
	};

		template<class CLASS, class... PARAMS>
	class auto_reflection_constructor : public reflection_constructor {
	private:
		const size_t mModifiers;

		template<class T>
		void call__(void* aObject, const void* aParams) const {
			new(aObject) T();
		}

		template<class T, class P1 >
		void call__(void* aObject, const void* aParams) const {
			const P1* const p1 = reinterpret_cast<const P1*>(aParams);
			new(aObject) T(*p1);
		}

		template<class T, class P1, class P2>
		void call__(void* aObject, const void* aParams) const {
			const P1* const p1 = reinterpret_cast<const P1*>(aParams);
			const P2* const p2 = reinterpret_cast<const P2*>(reinterpret_cast<const uint8_t*>(p1) + sizeof(P1));
			new(aObject) T(*p1, *p2);
		}

		template<class T, class P1, class P2, class P3>
		void call__(void* aObject, const void* aParams) const {
			const P1* const p1 = reinterpret_cast<const P1*>(aParams);
			const P2* const p2 = reinterpret_cast<const P2*>(reinterpret_cast<const uint8_t*>(p1) + sizeof(P1));
			const P3* const p3 = reinterpret_cast<const P3*>(reinterpret_cast<const uint8_t*>(p2) + sizeof(P2));
			new(aObject) T(*p1, *p2, *p3);
		}

		template<class T, class P1, class P2, class P3, class P4>
		void call__(void* aObject, const void* aParams) const {
			const P1* const p1 = reinterpret_cast<const P1*>(aParams);
			const P2* const p2 = reinterpret_cast<const P2*>(reinterpret_cast<const uint8_t*>(p1) + sizeof(P1));
			const P3* const p3 = reinterpret_cast<const P3*>(reinterpret_cast<const uint8_t*>(p2) + sizeof(P2));
			const P4* const p3 = reinterpret_cast<const P4*>(reinterpret_cast<const uint8_t*>(p3) + sizeof(P3));
			new(aObject) T(*p1, *p2, *p3, *p4);
		}

		template<class T, class P1, class P2, class P3, class P4, class P5>
		void call__(void* aObject, const void* aParams) const {
			const P1* const p1 = reinterpret_cast<const P1*>(aParams);
			const P2* const p2 = reinterpret_cast<const P2*>(reinterpret_cast<const uint8_t*>(p1) + sizeof(P1));
			const P3* const p3 = reinterpret_cast<const P3*>(reinterpret_cast<const uint8_t*>(p2) + sizeof(P2));
			const P4* const p3 = reinterpret_cast<const P4*>(reinterpret_cast<const uint8_t*>(p3) + sizeof(P3));
			const P5* const p3 = reinterpret_cast<const P5*>(reinterpret_cast<const uint8_t*>(p4) + sizeof(P4));
			new(aObject) T(*p1, *p2, *p3, *p4, *p5);
		}
	protected:
		// Inherited from reflection_constructor

		void call_(void* aObject, const void* aParams) const override {
			call__<CLASS, PARAMS...>(aObject, aParams);
		}
	public:
		auto_reflection_constructor(const size_t aModifiers) :
			mModifiers(aModifiers)
		{}

		// Inherited from reflection_constructor

		size_t get_parameter_count() const override {
			return sizeof...(PARAMS);
		}

		const reflection_class& get_parameter(size_t aIndex) const override {
			switch(aIndex) {
			case 0: return reflect<type_at_index<0, PARAMS...>::type>();
			case 1: return reflect<type_at_index<1, PARAMS...>::type>();
			case 2: return reflect<type_at_index<2, PARAMS...>::type>();
			case 3: return reflect<type_at_index<3, PARAMS...>::type>();
			case 4: return reflect<type_at_index<4, PARAMS...>::type>();
			case 5: return reflect<type_at_index<5, PARAMS...>::type>();
			default: throw std::runtime_error("asmith::reflection_constructor::get_parameter : Index out of bounds");
			}
		}

		size_t get_modifiers() const override {
			return mModifiers;
		}
	};
}

#endif<commit_msg>Renamed reflection_constructor::call_<commit_after>//	Copyright 2017 Adam Smith
//	Licensed under the Apache License, Version 2.0 (the "License");
//	you may not use this file except in compliance with the License.
//	You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
//	Unless required by applicable law or agreed to in writing, software
//	distributed under the License is distributed on an "AS IS" BASIS,
//	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//	See the License for the specific language governing permissions and
//	limitations under the License.

#ifndef ASMITH_REFLECTION_CONSTRUCTOR_HPP
#define ASMITH_REFLECTION_CONSTRUCTOR_HPP

namespace asmith {

	class reflection_class;

	class reflection_constructor {
	public:
		virtual ~reflection_constructor() {}

		virtual size_t get_parameter_count() const = 0;
		virtual const reflection_class& get_parameter(size_t) const = 0;
		virtual size_t get_modifiers() const = 0;

		virtual void call_unsafe(void*, const void*) const = 0;

		void call(void* aObject) const {
			call_unsafe(aObject, nullptr);
		}

		template<class P1>
		void call(void* aObject, P1 p1) const {
			call_unsafe(&aObject, &p1);
		}

		template<class P1, class P2>
		void call(void* aObject, P1 p1, P2 p2) const {
			call_unsafe(aObject, &p1);
		}

		template<class P1, class P2, class P3>
		void call(void* aObject, P1 p1, P2 p2, P3 p3) const {
			call_unsafe(aObject, &p1);
		}

		template<class P1, class P2, class P3, class P4>
		void call(void* aObject, P1 p1, P2 p2, P3 p3, P4 p4) const {
			call_unsafe(aObject, &p1);
		}

		template<class P1, class P2, class P3, class P4, class P5>
		void call(void* aObject, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) const {
			call_unsafe(&aObject, &p1);
		}
	};

		template<class CLASS, class... PARAMS>
	class auto_reflection_constructor : public reflection_constructor {
	private:
		const size_t mModifiers;

		template<class T>
		void call__(void* aObject, const void* aParams) const {
			new(aObject) T();
		}

		template<class T, class P1 >
		void call__(void* aObject, const void* aParams) const {
			const P1* const p1 = reinterpret_cast<const P1*>(aParams);
			new(aObject) T(*p1);
		}

		template<class T, class P1, class P2>
		void call__(void* aObject, const void* aParams) const {
			const P1* const p1 = reinterpret_cast<const P1*>(aParams);
			const P2* const p2 = reinterpret_cast<const P2*>(reinterpret_cast<const uint8_t*>(p1) + sizeof(P1));
			new(aObject) T(*p1, *p2);
		}

		template<class T, class P1, class P2, class P3>
		void call__(void* aObject, const void* aParams) const {
			const P1* const p1 = reinterpret_cast<const P1*>(aParams);
			const P2* const p2 = reinterpret_cast<const P2*>(reinterpret_cast<const uint8_t*>(p1) + sizeof(P1));
			const P3* const p3 = reinterpret_cast<const P3*>(reinterpret_cast<const uint8_t*>(p2) + sizeof(P2));
			new(aObject) T(*p1, *p2, *p3);
		}

		template<class T, class P1, class P2, class P3, class P4>
		void call__(void* aObject, const void* aParams) const {
			const P1* const p1 = reinterpret_cast<const P1*>(aParams);
			const P2* const p2 = reinterpret_cast<const P2*>(reinterpret_cast<const uint8_t*>(p1) + sizeof(P1));
			const P3* const p3 = reinterpret_cast<const P3*>(reinterpret_cast<const uint8_t*>(p2) + sizeof(P2));
			const P4* const p3 = reinterpret_cast<const P4*>(reinterpret_cast<const uint8_t*>(p3) + sizeof(P3));
			new(aObject) T(*p1, *p2, *p3, *p4);
		}

		template<class T, class P1, class P2, class P3, class P4, class P5>
		void call__(void* aObject, const void* aParams) const {
			const P1* const p1 = reinterpret_cast<const P1*>(aParams);
			const P2* const p2 = reinterpret_cast<const P2*>(reinterpret_cast<const uint8_t*>(p1) + sizeof(P1));
			const P3* const p3 = reinterpret_cast<const P3*>(reinterpret_cast<const uint8_t*>(p2) + sizeof(P2));
			const P4* const p3 = reinterpret_cast<const P4*>(reinterpret_cast<const uint8_t*>(p3) + sizeof(P3));
			const P5* const p3 = reinterpret_cast<const P5*>(reinterpret_cast<const uint8_t*>(p4) + sizeof(P4));
			new(aObject) T(*p1, *p2, *p3, *p4, *p5);
		}
	public:
		auto_reflection_constructor(const size_t aModifiers) :
			mModifiers(aModifiers)
		{}

		// Inherited from reflection_constructor

		size_t get_parameter_count() const override {
			return sizeof...(PARAMS);
		}

		const reflection_class& get_parameter(size_t aIndex) const override {
			switch(aIndex) {
			case 0: return reflect<type_at_index<0, PARAMS...>::type>();
			case 1: return reflect<type_at_index<1, PARAMS...>::type>();
			case 2: return reflect<type_at_index<2, PARAMS...>::type>();
			case 3: return reflect<type_at_index<3, PARAMS...>::type>();
			case 4: return reflect<type_at_index<4, PARAMS...>::type>();
			case 5: return reflect<type_at_index<5, PARAMS...>::type>();
			default: throw std::runtime_error("asmith::reflection_constructor::get_parameter : Index out of bounds");
			}
		}

		size_t get_modifiers() const override {
			return mModifiers;
		}

		void call_unsafe(void* aObject, const void* aParams) const override {
			call__<CLASS, PARAMS...>(aObject, aParams);
		}
	};
}

#endif<|endoftext|>


<commit_before>/*!
    \file async_wait_free_queue.inl
    \brief Asynchronous wait-free logging ring queue inline implementation
    \author Ivan Shynkarenka
    \date 04.08.2016
    \copyright MIT License
*/

namespace CppLogging {

template<typename T>
inline AsyncWaitFreeQueue<T>::AsyncWaitFreeQueue(size_t capacity) : _capacity(capacity), _mask(capacity - 1), _buffer(new Node[capacity]), _head(0), _tail(0)
{
    assert((capacity > 1) && "Ring queue capacity must be greater than one!");
    assert(((capacity & (capacity - 1)) == 0) && "Ring queue capacity must be a power of two!");

    memset(_pad0, 0, sizeof(cache_line_pad));
    memset(_pad1, 0, sizeof(cache_line_pad));
    memset(_pad2, 0, sizeof(cache_line_pad));
    memset(_pad3, 0, sizeof(cache_line_pad));

    // Populate the sequence initial values
    for (size_t i = 0; i < capacity; ++i)
        _buffer[i].sequence.store(i, std::memory_order_relaxed);
}

template<typename T>
inline size_t AsyncWaitFreeQueue<T>::size() const noexcept
{
    const size_t head = _head.load(std::memory_order_acquire);
    const size_t tail = _tail.load(std::memory_order_acquire);

    return head - tail;
}

template<typename T>
inline bool AsyncWaitFreeQueue<T>::Enqueue(Record& record)
{
    size_t head_sequence = _head.load(std::memory_order_relaxed);

    for (;;)
    {
        Node* node = &_buffer[head_sequence & _mask];
        size_t node_sequence = node->sequence.load(std::memory_order_acquire);

        // If node sequence and head sequence are the same then it means this slot is empty
        int64_t diff = (int64_t)node_sequence - (int64_t)head_sequence;
        if (diff == 0)
        {
            // Claim our spot by moving head. If head isn't the same
            // as we last checked then that means someone beat us to
            // the punch weak compare is faster, but can return spurious
            // results which in this instance is OK, because it's in the loop
            if (_head.compare_exchange_weak(head_sequence, head_sequence + 1, std::memory_order_relaxed))
            {
                // Store and swap the item value
                swap(node->value, record);

                // Increment the sequence so that the tail knows it's accessible
                node->sequence.store(head_sequence + 1, std::memory_order_release);
                return true;
            }
        }
        else if (diff < 0)
        {
            // If node sequence is less than head sequence then it means this slot is full
            // and therefore buffer is full
            return false;
        }
        else
        {
            // Under normal circumstances this branch should never be taken
            head_sequence = _head.load(std::memory_order_relaxed);
        }
    }

    // Never happens...
    return false;
}

template<typename T>
inline bool AsyncWaitFreeQueue<T>::Dequeue(Record& record)
{
    size_t tail_sequence = _tail.load(std::memory_order_relaxed);

    for (;;)
    {
        Node* node = &_buffer[tail_sequence & _mask];
        size_t node_sequence = node->sequence.load(std::memory_order_acquire);

        // If node sequence and head sequence are the same then it means this slot is empty
        int64_t diff = (int64_t)node_sequence - (int64_t)(tail_sequence + 1);
        if (diff == 0)
        {
            // Claim our spot by moving head. If head isn't the same
            // as we last checked then that means someone beat us to
            // the punch weak compare is faster, but can return spurious
            // results which in this instance is OK, because it's in the loop
            if (_tail.compare_exchange_weak(tail_sequence, tail_sequence + 1, std::memory_order_relaxed))
            {
                // Swap and get the item value
                swap(record, node->value);

                // Set the sequence to what the head sequence should be next time around
                node->sequence.store(tail_sequence + _mask + 1, std::memory_order_release);
                return true;
            }
        }
        else if (diff < 0)
        {
            // If seq is less than head seq then it means this slot is full and therefore the buffer is full
            return false;
        }
        else
        {
            // Under normal circumstances this branch should never be taken
            tail_sequence = _tail.load(std::memory_order_relaxed);
        }
    }

    // Never happens...
    return false;
}

} // namespace CppLogging
<commit_msg>Update<commit_after>/*!
    \file async_wait_free_queue.inl
    \brief Asynchronous wait-free logging ring queue inline implementation
    \author Ivan Shynkarenka
    \date 04.08.2016
    \copyright MIT License
*/

#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4702) // C4702: unreachable code
#endif

namespace CppLogging {

template<typename T>
inline AsyncWaitFreeQueue<T>::AsyncWaitFreeQueue(size_t capacity) : _capacity(capacity), _mask(capacity - 1), _buffer(new Node[capacity]), _head(0), _tail(0)
{
    assert((capacity > 1) && "Ring queue capacity must be greater than one!");
    assert(((capacity & (capacity - 1)) == 0) && "Ring queue capacity must be a power of two!");

    memset(_pad0, 0, sizeof(cache_line_pad));
    memset(_pad1, 0, sizeof(cache_line_pad));
    memset(_pad2, 0, sizeof(cache_line_pad));
    memset(_pad3, 0, sizeof(cache_line_pad));

    // Populate the sequence initial values
    for (size_t i = 0; i < capacity; ++i)
        _buffer[i].sequence.store(i, std::memory_order_relaxed);
}

template<typename T>
inline size_t AsyncWaitFreeQueue<T>::size() const noexcept
{
    const size_t head = _head.load(std::memory_order_acquire);
    const size_t tail = _tail.load(std::memory_order_acquire);

    return head - tail;
}

template<typename T>
inline bool AsyncWaitFreeQueue<T>::Enqueue(Record& record)
{
    size_t head_sequence = _head.load(std::memory_order_relaxed);

    for (;;)
    {
        Node* node = &_buffer[head_sequence & _mask];
        size_t node_sequence = node->sequence.load(std::memory_order_acquire);

        // If node sequence and head sequence are the same then it means this slot is empty
        int64_t diff = (int64_t)node_sequence - (int64_t)head_sequence;
        if (diff == 0)
        {
            // Claim our spot by moving head. If head isn't the same
            // as we last checked then that means someone beat us to
            // the punch weak compare is faster, but can return spurious
            // results which in this instance is OK, because it's in the loop
            if (_head.compare_exchange_weak(head_sequence, head_sequence + 1, std::memory_order_relaxed))
            {
                // Store and swap the item value
                swap(node->value, record);

                // Increment the sequence so that the tail knows it's accessible
                node->sequence.store(head_sequence + 1, std::memory_order_release);
                return true;
            }
        }
        else if (diff < 0)
        {
            // If node sequence is less than head sequence then it means this slot is full
            // and therefore buffer is full
            return false;
        }
        else
        {
            // Under normal circumstances this branch should never be taken
            head_sequence = _head.load(std::memory_order_relaxed);
        }
    }

    // Never happens...
    return false;
}

template<typename T>
inline bool AsyncWaitFreeQueue<T>::Dequeue(Record& record)
{
    size_t tail_sequence = _tail.load(std::memory_order_relaxed);

    for (;;)
    {
        Node* node = &_buffer[tail_sequence & _mask];
        size_t node_sequence = node->sequence.load(std::memory_order_acquire);

        // If node sequence and head sequence are the same then it means this slot is empty
        int64_t diff = (int64_t)node_sequence - (int64_t)(tail_sequence + 1);
        if (diff == 0)
        {
            // Claim our spot by moving head. If head isn't the same
            // as we last checked then that means someone beat us to
            // the punch weak compare is faster, but can return spurious
            // results which in this instance is OK, because it's in the loop
            if (_tail.compare_exchange_weak(tail_sequence, tail_sequence + 1, std::memory_order_relaxed))
            {
                // Swap and get the item value
                swap(record, node->value);

                // Set the sequence to what the head sequence should be next time around
                node->sequence.store(tail_sequence + _mask + 1, std::memory_order_release);
                return true;
            }
        }
        else if (diff < 0)
        {
            // If seq is less than head seq then it means this slot is full and therefore the buffer is full
            return false;
        }
        else
        {
            // Under normal circumstances this branch should never be taken
            tail_sequence = _tail.load(std::memory_order_relaxed);
        }
    }

    // Never happens...
    return false;
}

} // namespace CppLogging

#if defined(_MSC_VER)
#pragma warning(pop)
#endif
<|endoftext|>


<commit_before>#include "DescriptorSet.h"

namespace Engine
{
    namespace Descriptors
    {
        DescriptorSet::~DescriptorSet()
        {
            auto device = ApplicationData::data->device;

            device.destroyPipelineLayout(pipeline_layout_);
            for (auto &desc_layout : desc_layout_)
                device.destroyDescriptorSetLayout(desc_layout);

            delete uniform_buffer_;
        }

        void DescriptorSet::create()
        {
            auto device = ApplicationData::data->device;

            // Set Layout Bindings
            layout_bindings_.resize(1 + texture_count_);

            layout_bindings_[0].binding 					 = 0;
            layout_bindings_[0].descriptorType 				 = vk::DescriptorType::eUniformBuffer;
            layout_bindings_[0].descriptorCount 			 = 1;
            layout_bindings_[0].stageFlags 					 = vk::ShaderStageFlagBits::eVertex;
            layout_bindings_[0].pImmutableSamplers			 = nullptr;

            if (texture_count_ > 0) {
                layout_bindings_[1].binding 					 = 1;
                layout_bindings_[1].descriptorType 				 = vk::DescriptorType::eCombinedImageSampler;
                layout_bindings_[1].descriptorCount 			 = 1;
                layout_bindings_[1].stageFlags 					 = vk::ShaderStageFlagBits::eFragment;
                layout_bindings_[1].pImmutableSamplers 			 = nullptr;
            }

            // Set Descriptor Layouts
            vk::Result res;

            desc_layout_.resize(1);

            vk::DescriptorSetLayoutCreateInfo descriptor_layout_ = {};
            descriptor_layout_.pNext 						 = nullptr;
            descriptor_layout_.bindingCount 				 = static_cast<uint32_t>(layout_bindings_.size());
            descriptor_layout_.pBindings 					 = layout_bindings_.data();

            res = device.createDescriptorSetLayout(&descriptor_layout_, nullptr, desc_layout_.data());
            assert(res == vk::Result::eSuccess);

            // Set Pipeline Layout
            vk::PipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {};
            pPipelineLayoutCreateInfo.pNext                  = nullptr;
            pPipelineLayoutCreateInfo.pushConstantRangeCount = 0;
            pPipelineLayoutCreateInfo.pPushConstantRanges    = nullptr;
            pPipelineLayoutCreateInfo.setLayoutCount         = 1;
            pPipelineLayoutCreateInfo.pSetLayouts            = desc_layout_.data();

            res = device.createPipelineLayout(&pPipelineLayoutCreateInfo, nullptr, &pipeline_layout_);
            assert(res == vk::Result::eSuccess);

            //  Create Uniform Buffer
            auto app_data = ApplicationData::data;
            struct BufferData uniformBufferData = {};
            uniformBufferData.usage      = vk::BufferUsageFlagBits::eUniformBuffer;
            uniformBufferData.properties = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent;
            uniformBufferData.size       = sizeof(glm::mat4) * 3;

            uniform_buffer_ = new UniformBuffer(uniformBufferData);
            uniform_buffer_->initModelView(app_data->view_width, app_data->view_height);
        }

        Texture DescriptorSet::getTextelBuffer(const std::string& texture_path, vk::Queue queue)
        {
            struct ImageProps img_props = {};
            img_props.format     = vk::Format::eR8G8B8A8Unorm;
            img_props.aspectMask = vk::ImageAspectFlagBits::eColor;

            vk::Image texture_image = nullptr;
            if(!texture_path.empty()) {
                texture_image = Textures::createTextureImage(texture_path, queue);

                if(nullptr != texture_image) {

                    vk::SamplerCreateInfo sampler_ = {};
                    sampler_.maxAnisotropy 		= 1.0f;
                    sampler_.magFilter 			= vk::Filter::eLinear;
                    sampler_.minFilter 			= vk::Filter::eLinear;
                    sampler_.mipmapMode 		= vk::SamplerMipmapMode::eLinear;
                    sampler_.addressModeU 		= vk::SamplerAddressMode::eRepeat;
                    sampler_.addressModeV 		= vk::SamplerAddressMode::eRepeat;
                    sampler_.addressModeW 		= vk::SamplerAddressMode::eRepeat;
                    sampler_.mipLodBias 		= 0.0f;
                    sampler_.compareOp 			= vk::CompareOp::eNever;
                    sampler_.minLod 			= 0.0f;
                    sampler_.maxLod 			= 0.0f;
                    sampler_.maxAnisotropy 		= 1.0;
                    sampler_.anisotropyEnable 	= VK_FALSE;
                    sampler_.borderColor 		= vk::BorderColor::eFloatOpaqueWhite;

                    vk::Sampler sampler_obj_ = ApplicationData::data->device.createSampler(sampler_, nullptr);

                    return Texture{
                        .buffer  = new Memory::BufferImage(img_props, texture_image),
                        .sampler = sampler_obj_
                    };
                }
            }

            assert(false);
        }

        void DescriptorSet::updateDescriptorSet(Texture texture, vk::DescriptorSet desc_set)
        {
            std::vector<vk::WriteDescriptorSet, mem::StdAllocator<vk::WriteDescriptorSet>> writes = {};

            vk::DescriptorBufferInfo buffer_info;
            buffer_info.range  = uniform_buffer_->size;
            buffer_info.offset = 0;
            buffer_info.buffer = uniform_buffer_->buf;

            vk::WriteDescriptorSet write = {};
            write.pNext 			= nullptr;
            write.dstSet 			= desc_set;
            write.descriptorCount 	= 1;
            write.descriptorType 	= vk::DescriptorType::eUniformBuffer;
            write.pBufferInfo 		= &buffer_info;
            write.dstArrayElement 	= 0;
            write.dstBinding 		= 0;
            writes.push_back(write);

            if (texture_count_ > 0) {
                vk::DescriptorImageInfo texture_info = {};
                texture_info.imageLayout    = vk::ImageLayout::eShaderReadOnlyOptimal;
                texture_info.imageView 	    = texture.buffer->view;
                texture_info.sampler 	    = texture.sampler;

                write.dstSet 			    = desc_set;
                write.descriptorCount 	    = 1;
                write.descriptorType 	    = vk::DescriptorType::eCombinedImageSampler;
                write.pImageInfo 		    = &texture_info;
                write.dstArrayElement 	    = 0;
                write.dstBinding 		    = 1;
                writes.push_back(write);
            }

            ApplicationData::data->device.updateDescriptorSets(static_cast<u_int32_t>(writes.size()), writes.data(), 0, nullptr);
        }

        vk::DescriptorPool DescriptorSet::createDescriptorPool()
        {
            vk::DescriptorPool desc_pool;

            std::vector<vk::DescriptorPoolSize> poolSizes = {};

            poolSizes.resize(1 + texture_count_);
            poolSizes[0].type 			 = vk::DescriptorType::eUniformBuffer;
            poolSizes[0].descriptorCount = 1;

            if(texture_count_ > 0){
                poolSizes[1].type 			 = vk::DescriptorType::eCombinedImageSampler;
                poolSizes[1].descriptorCount = 1;
            }

            vk::DescriptorPoolCreateInfo descriptor_pool_info = {};
            descriptor_pool_info.pNext 			= nullptr;
            descriptor_pool_info.maxSets 		= 1;
            descriptor_pool_info.poolSizeCount 	= static_cast<uint32_t>(poolSizes.size());
            descriptor_pool_info.pPoolSizes 	= poolSizes.data();

            vk::Result res = ApplicationData::data->device.createDescriptorPool(&descriptor_pool_info, nullptr, &desc_pool);
            assert(res == vk::Result::eSuccess);

            return desc_pool;
        }

        vk::DescriptorSet DescriptorSet::createDescriptorSet(vk::DescriptorPool desc_pool)
        {
            vk::DescriptorSet desc_set_;

            vk::DescriptorSetAllocateInfo alloc_info_[1];
            alloc_info_[0].pNext 							  = nullptr;
            alloc_info_[0].descriptorPool 					  = desc_pool;
            alloc_info_[0].descriptorSetCount 				  = 1;
            alloc_info_[0].pSetLayouts 						  = desc_layout_.data();

            vk::Result res = ApplicationData::data->device.allocateDescriptorSets(alloc_info_, &desc_set_);
            assert(res == vk::Result::eSuccess);

            return desc_set_;
        }

        vk::PipelineLayout DescriptorSet::getPipelineLayout() const
        {
            return pipeline_layout_;
        }

        UniformBuffer* DescriptorSet::getUniformBuffer() const
        {
            return uniform_buffer_;
        }
    }
}
<commit_msg>refactor DescriptorSet DEBUG_CALL macro<commit_after>#include "DescriptorSet.h"

namespace Engine
{
    namespace Descriptors
    {
        DescriptorSet::~DescriptorSet()
        {
            auto device = ApplicationData::data->device;

            device.destroyPipelineLayout(pipeline_layout_);
            for (auto &desc_layout : desc_layout_)
                device.destroyDescriptorSetLayout(desc_layout);

            delete uniform_buffer_;
        }

        void DescriptorSet::create()
        {
            auto device = ApplicationData::data->device;

            // Set Layout Bindings
            layout_bindings_.resize(1 + texture_count_);

            layout_bindings_[0].binding 					 = 0;
            layout_bindings_[0].descriptorType 				 = vk::DescriptorType::eUniformBuffer;
            layout_bindings_[0].descriptorCount 			 = 1;
            layout_bindings_[0].stageFlags 					 = vk::ShaderStageFlagBits::eVertex;
            layout_bindings_[0].pImmutableSamplers			 = nullptr;

            if (texture_count_ > 0) {
                layout_bindings_[1].binding 					 = 1;
                layout_bindings_[1].descriptorType 				 = vk::DescriptorType::eCombinedImageSampler;
                layout_bindings_[1].descriptorCount 			 = 1;
                layout_bindings_[1].stageFlags 					 = vk::ShaderStageFlagBits::eFragment;
                layout_bindings_[1].pImmutableSamplers 			 = nullptr;
            }

            // Set Descriptor Layouts
            vk::Result res;

            desc_layout_.resize(1);

            vk::DescriptorSetLayoutCreateInfo descriptor_layout_ = {};
            descriptor_layout_.pNext 						 = nullptr;
            descriptor_layout_.bindingCount 				 = static_cast<uint32_t>(layout_bindings_.size());
            descriptor_layout_.pBindings 					 = layout_bindings_.data();

            res = device.createDescriptorSetLayout(&descriptor_layout_, nullptr, desc_layout_.data());
            assert(res == vk::Result::eSuccess);

            // Set Pipeline Layout
            vk::PipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {};
            pPipelineLayoutCreateInfo.pNext                  = nullptr;
            pPipelineLayoutCreateInfo.pushConstantRangeCount = 0;
            pPipelineLayoutCreateInfo.pPushConstantRanges    = nullptr;
            pPipelineLayoutCreateInfo.setLayoutCount         = 1;
            pPipelineLayoutCreateInfo.pSetLayouts            = desc_layout_.data();

            res = device.createPipelineLayout(&pPipelineLayoutCreateInfo, nullptr, &pipeline_layout_);
            assert(res == vk::Result::eSuccess);

            //  Create Uniform Buffer
            auto app_data = ApplicationData::data;
            struct BufferData uniformBufferData = {};
            uniformBufferData.usage      = vk::BufferUsageFlagBits::eUniformBuffer;
            uniformBufferData.properties = vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent;
            uniformBufferData.size       = sizeof(glm::mat4) * 3;

            uniform_buffer_ = new UniformBuffer(uniformBufferData);
            uniform_buffer_->initModelView(app_data->view_width, app_data->view_height);
        }

        Texture DescriptorSet::getTextelBuffer(const std::string& texture_path, vk::Queue queue)
        {
            struct ImageProps img_props = {};
            img_props.format     = vk::Format::eR8G8B8A8Unorm;
            img_props.aspectMask = vk::ImageAspectFlagBits::eColor;

            vk::Image texture_image = nullptr;
            if(!texture_path.empty()) {
                texture_image = Textures::createTextureImage(texture_path, queue);

                if(nullptr != texture_image) {

                    vk::SamplerCreateInfo sampler_ = {};
                    sampler_.maxAnisotropy 		= 1.0f;
                    sampler_.magFilter 			= vk::Filter::eLinear;
                    sampler_.minFilter 			= vk::Filter::eLinear;
                    sampler_.mipmapMode 		= vk::SamplerMipmapMode::eLinear;
                    sampler_.addressModeU 		= vk::SamplerAddressMode::eRepeat;
                    sampler_.addressModeV 		= vk::SamplerAddressMode::eRepeat;
                    sampler_.addressModeW 		= vk::SamplerAddressMode::eRepeat;
                    sampler_.mipLodBias 		= 0.0f;
                    sampler_.compareOp 			= vk::CompareOp::eNever;
                    sampler_.minLod 			= 0.0f;
                    sampler_.maxLod 			= 0.0f;
                    sampler_.maxAnisotropy 		= 1.0;
                    sampler_.anisotropyEnable 	= VK_FALSE;
                    sampler_.borderColor 		= vk::BorderColor::eFloatOpaqueWhite;

                    return Texture{
                        .buffer  = new Memory::BufferImage(img_props, texture_image),
                        .sampler = ApplicationData::data->device.createSampler(sampler_)
                    };
                }
            }

            assert(false);
        }

        void DescriptorSet::updateDescriptorSet(Texture texture, vk::DescriptorSet desc_set)
        {
            std::vector<vk::WriteDescriptorSet, mem::StdAllocator<vk::WriteDescriptorSet>> writes = {};

            vk::DescriptorBufferInfo buffer_info;
            buffer_info.range  = uniform_buffer_->size;
            buffer_info.offset = 0;
            buffer_info.buffer = uniform_buffer_->buf;

            vk::WriteDescriptorSet write = {};
            write.pNext 			= nullptr;
            write.dstSet 			= desc_set;
            write.descriptorCount 	= 1;
            write.descriptorType 	= vk::DescriptorType::eUniformBuffer;
            write.pBufferInfo 		= &buffer_info;
            write.dstArrayElement 	= 0;
            write.dstBinding 		= 0;
            writes.push_back(write);

            if (texture_count_ > 0) {
                vk::DescriptorImageInfo texture_info = {};
                texture_info.imageLayout    = vk::ImageLayout::eShaderReadOnlyOptimal;
                texture_info.imageView 	    = texture.buffer->view;
                texture_info.sampler 	    = texture.sampler;

                write.dstSet 			    = desc_set;
                write.descriptorCount 	    = 1;
                write.descriptorType 	    = vk::DescriptorType::eCombinedImageSampler;
                write.pImageInfo 		    = &texture_info;
                write.dstArrayElement 	    = 0;
                write.dstBinding 		    = 1;
                writes.push_back(write);
            }

            DEBUG_CALL(ApplicationData::data->device.updateDescriptorSets(writes, {}));
        }

        vk::DescriptorPool DescriptorSet::createDescriptorPool()
        {
            vk::DescriptorPool desc_pool;

            std::vector<vk::DescriptorPoolSize> poolSizes = {};

            poolSizes.resize(1 + texture_count_);
            poolSizes[0].type 			 = vk::DescriptorType::eUniformBuffer;
            poolSizes[0].descriptorCount = 1;

            if(texture_count_ > 0){
                poolSizes[1].type 			 = vk::DescriptorType::eCombinedImageSampler;
                poolSizes[1].descriptorCount = 1;
            }

            vk::DescriptorPoolCreateInfo descriptor_pool_info = {};
            descriptor_pool_info.pNext 			= nullptr;
            descriptor_pool_info.maxSets 		= 1;
            descriptor_pool_info.poolSizeCount 	= static_cast<uint32_t>(poolSizes.size());
            descriptor_pool_info.pPoolSizes 	= poolSizes.data();

            DEBUG_CALL(desc_pool = ApplicationData::data->device.createDescriptorPool(descriptor_pool_info));

            return desc_pool;
        }

        vk::DescriptorSet DescriptorSet::createDescriptorSet(vk::DescriptorPool desc_pool)
        {
            std::vector<vk::DescriptorSet> desc_sets_;

            vk::DescriptorSetAllocateInfo alloc_info_;
            alloc_info_.pNext 				= nullptr;
            alloc_info_.descriptorPool 		= desc_pool;
            alloc_info_.descriptorSetCount 	= 1;
            alloc_info_.pSetLayouts 		= desc_layout_.data();

            DEBUG_CALL(desc_sets_ = ApplicationData::data->device.allocateDescriptorSets(alloc_info_));

            return desc_sets_[0];
        }

        vk::PipelineLayout DescriptorSet::getPipelineLayout() const
        {
            return pipeline_layout_;
        }

        UniformBuffer* DescriptorSet::getUniformBuffer() const
        {
            return uniform_buffer_;
        }
    }
}
<|endoftext|>