跳转至

OpenCV SurfaceMatching模块的相关工具

计算法向量

OpenCV Compute Normals API

/**
 * @brief: 使用平面拟合的方法,计算一个3D点云中任意点的法向量。
 * @param PC: 输入的3D点云。必须为 (Nx3) 或 (Nx6)
 * @param PCNormals: 输出点云。(Nx6)
 * @param NumNeighbors: 平面拟合时考虑的点的数量
 * @param FlipViewpoint: 如果为 'true',则计算得到的法向量会被翻转到指向 'viewpoint' 的
 方向。为 'fasle' 则不进行任何操作
 * @param viewpoint: 视点位置
 */
int cv::ppf_match_3d::computeNormalsPC3d ( 
    const Mat &     PC,
    Mat &   PCNormals,
    const int   NumNeighbors,
    const bool  FlipViewpoint,
    const Vec3f &   viewpoint )
retval, PCNormals = cv.ppf_match_3d.computeNormalsPC3d(
    PC, 
    NumNeighbors, 
    FlipViewpoint, 
    viewpoint
    [, PCNormals] 
)

示例

  cv::Mat points, pointsAndNormals;

  cout << "Loading points\n";
  cv::ppf_match_3d::loadPLYSimple(modelFileName.c_str(), 1).copyTo(points);

  cout << "Computing normals\n";
  cv::Vec3d viewpoint(0, 0, 0);
  cv::ppf_match_3d::computeNormalsPC3d(points, pointsAndNormals, 6, false, viewpoint);

读取Ply数据(loadPLYSimple)

可参考loadPLYSimple函数,将自己的数据结构转成Mat

Mat loadPLYSimple(const char* fileName, int withNormals)
{
  Mat cloud;
  int numVertices = 0;
  int numCols = 3;
  int has_normals = 0;

  std::ifstream ifs(fileName);

  if (!ifs.is_open())
    CV_Error(Error::StsError, String("Error opening input file: ") + String(fileName) + "\n");

  std::string str;
  while (str.substr(0, 10) != "end_header")
  {
    std::vector<std::string> tokens = split(str,' ');
    if (tokens.size() == 3)
    {
      if (tokens[0] == "element" && tokens[1] == "vertex")
      {
        numVertices = atoi(tokens[2].c_str());
      }
      else if (tokens[0] == "property")
      {
        if (tokens[2] == "nx" || tokens[2] == "normal_x")
        {
          has_normals = -1;
          numCols += 3;
        }
        else if (tokens[2] == "r" || tokens[2] == "red")
        {
          //has_color = true;
          numCols += 3;
        }
        else if (tokens[2] == "a" || tokens[2] == "alpha")
        {
          //has_alpha = true;
          numCols += 1;
        }
      }
    }
    else if (tokens.size() > 1 && tokens[0] == "format" && tokens[1] != "ascii")
      CV_Error(Error::StsBadArg, String("Cannot read file, only ascii ply format is currently supported..."));
    std::getline(ifs, str);
  }
  withNormals &= has_normals;

  cloud = Mat(numVertices, withNormals ? 6 : 3, CV_32FC1);

  for (int i = 0; i < numVertices; i++)
  {
    float* data = cloud.ptr<float>(i);
    int col = 0;
    for (; col < (withNormals ? 6 : 3); ++col)
    {
      ifs >> data[col];
    }
    for (; col < numCols; ++col)
    {
      float tmp;
      ifs >> tmp;
    }
    if (withNormals)
    {
      // normalize to unit norm
      double norm = sqrt(data[3]*data[3] + data[4]*data[4] + data[5]*data[5]);
      if (norm>0.00001)
      {
        data[3]/=static_cast<float>(norm);
        data[4]/=static_cast<float>(norm);
        data[5]/=static_cast<float>(norm);
      }
    }
  }

  //cloud *= 5.0f;
  return cloud;
}