Reputation: 408
How can I convert efficiently from a cv::Mat
to a vtkImageData
in C++?
I search for a method that works for BW and COLOR images.
Upvotes: 6
Views: 1796
Reputation: 66
Below is a simplified version of the answer above by @lucasahli. It uses an efficient memcpy instead of a double for loop.
NOTE: For color images, you may need to flip the image channels from bgr to rgb ordering, which i used mixChannels to achieve.
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;
}
Upvotes: 4
Reputation: 408
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;
}
Upvotes: 2