如何将cv :: Mat转换为vtkImageData?

时间:2018-08-20 13:04:34

标签: c++ opencv vtk

如何在C ++中有效地从cv::Mat转换为vtkImageData? 我寻找一种适用于黑白和彩色图像的方法。

2 个答案:

答案 0 :(得分:1)

vtkSmartPointer<vtkImageData> OpenCVToVtkConverter::convertCVMatToVtkImageData(const cv::Mat &sourceCVImage, bool flipOverXAxis) {
  vtkSmartPointer<vtkImageData> outputVtkImage = vtkSmartPointer<vtkImageData>::New();
  double spacing[3] = {1, 1, 1};
  double origin[3] = {0, 0, 0};
  int extent[6] = {0, sourceCVImage.cols - 1, 0, sourceCVImage.rows - 1, 0, 0};
  auto numOfChannels = sourceCVImage.channels();
  outputVtkImage->SetSpacing(spacing);
  outputVtkImage->SetOrigin(origin);
  outputVtkImage->SetExtent(extent);
  outputVtkImage->SetDimensions(sourceCVImage.cols, sourceCVImage.rows, 1);
  outputVtkImage->AllocateScalars(VTK_UNSIGNED_CHAR, numOfChannels);

  cv::Mat tempCVImage;
  if(flipOverXAxis){ // Normaly you should flip the image!
    cv::flip(sourceCVImage, tempCVImage, 0);
  }
  else {
    tempCVImage = sourceCVImage;
  }
  for (int imgHeightPos = 0; imgHeightPos < sourceCVImage.rows; ++imgHeightPos) {
    for (int imgWidthPos = 0; imgWidthPos < sourceCVImage.cols; ++imgWidthPos){
      switch(numOfChannels){
        case 1:{
          auto pixel = tempCVImage.at<unsigned char>(imgHeightPos, imgWidthPos);
          outputVtkImage->SetScalarComponentFromDouble(imgWidthPos, imgHeightPos, 0, 0, pixel); //red
          break;
        }
        case 3: {
          auto pixel2 = tempCVImage.at<cv::Vec3b>(imgHeightPos, imgWidthPos);
          outputVtkImage->SetScalarComponentFromDouble(imgWidthPos, imgHeightPos, 0, 0, pixel2[2]); //red
          outputVtkImage->SetScalarComponentFromDouble(imgWidthPos, imgHeightPos, 0, 1, pixel2[1]); //green
          outputVtkImage->SetScalarComponentFromDouble(imgWidthPos, imgHeightPos, 0, 2, pixel2[0]); //blue
          break;
        }
        case 4:{
          auto pixel3 = tempCVImage.at<cv::Vec4b>(imgHeightPos, imgWidthPos);
          outputVtkImage->SetScalarComponentFromDouble(imgWidthPos, imgHeightPos, 0, 0, pixel3[2]); //red
          outputVtkImage->SetScalarComponentFromDouble(imgWidthPos, imgHeightPos, 0, 1, pixel3[1]); //green
          outputVtkImage->SetScalarComponentFromDouble(imgWidthPos, imgHeightPos, 0, 2, pixel3[0]); //blue
          outputVtkImage->SetScalarComponentFromDouble(imgWidthPos, imgHeightPos, 0, 3, pixel3[3]); //alpha
          break;
        }
        default:
          std::cout << "unknown number of channels" << std::endl;
      }
    }
  }
  outputVtkImage->Modified();
  return outputVtkImage;
}

答案 1 :(得分:0)

下面是@lucasahli给出的答案的简化版本。它使用高效的memcpy而不是double for循环。

注意:对于彩色图像,您可能需要将图像通道从bgr切换为rgb顺序,这是我使用mixChannels来实现的。

vtkSmartPointer<vtkImageData> OpenCVToVtkConverter::convertCVMatToVtkImageData(const cv::Mat &sourceCVImage, bool flipOverXAxis) {
  vtkSmartPointer<vtkImageData> outputVtkImage = vtkSmartPointer<vtkImageData>::New();

  int numOfChannels = sourceCVImage.channels();

  // dimension set to 1 for z since it's 2D
  outputVtkImage->SetDimensions(sourceCVImage.cols, sourceCVImage.rows, 1);

  // NOTE: if your image isn't uchar for some reason you'll need to change this type
  outputVtkImage->AllocateScalars(VTK_UNSIGNED_CHAR, numOfChannels);

  // the flipped image data gets put into tempCVImage
  cv::Mat tempCVImage;
  if(flipOverXAxis){ // Normally you should flip the image!
    cv::flip(sourceCVImage, tempCVImage, 0);
  }
  else {
    tempCVImage = sourceCVImage;
  }

  // the number of byes in the cv::Mat, assuming the data type is uchar
  size_t byte_count = sourceCVImage.cols * sourceCVImage.rows * numOfChannels * sizeof(unsigned char);

  // copy the internal cv::Mat data into the vtkImageData pointer
  memcpy(outputVtkImage->GetScalarPointer(), tempCVImage.data, byte_count);

  outputVtkImage->Modified();
  return outputVtkImage;
}