I want to visualize PNG files in a directory using volume rendering, but the vtkColorTransferFunction and vtkPiecewiseFunction classes did not work with vtkVolumeProperty. As far as I understand, this issue arises because the voxel values are in RGB format. However, I know that it’s possible to convert the RGB format to grayscale, but in this case, the PNG files are already in grayscale.
Hello,
As far as I understand, you need to store scalar values in the data set. Whatever RGB values in there will be interpreted as scalars. So, since you didn’t post the code you’re using, I guess you need to do some conversion after PNG read and while setting the values in vtkImageData
object. Since your PNGs are already grayscale, R==G==B, so I guess you only need to read one of these channels and load into your vtkImageData
and set the color scale to 0 (min) and 255 (max). Again, all this is guessing. I believe you could share the part of the code involved into loading the PNGs and volume rendering so we can reach a more accurate diagnosis.
best,
PC
Thank you, Paulo. Yes, as you mentioned, the PNGs are already grayscale. Below, I am sharing the code where I read the PNGs and transfer them to vtkImageData
.
vtkSmartPointer<vtkImageAppend> imageStack = vtkSmartPointer<vtkImageAppend>::New();
imageStack->SetAppendAxis(2);
for (const string& filePath : pngFiles) {
vtkSmartPointer<vtkPNGReader> reader = vtkSmartPointer<vtkPNGReader>::New();
reader->SetNumberOfScalarComponents(1);
reader->SetFileName(filePath.c_str());
reader->Update();
imageStack->AddInputConnection(reader->GetOutputPort());
}
imageStack->Update();
imageStack->GetOutput()->SetSpacing(0.5, 0.5, 0.5);
Hello,
Thank you, but I think we need everything after that and up to setting up a vtkVolume
object.
best,
PC
Of course, I do. But vtkColorTransferFunction and vtkPiecewiseFunction not working. I think this is because three channels are being used instead of a single channel.
int main()
{
string path = "C:\\Users\\fatil\\OneDrive\\Belgeler\\Dicoms\\snow_png";
vector<string> pngFiles = getBMPFiles(path);
if (pngFiles.empty()) {
std::cerr << "Error" << std::endl;
return -1;
}
vtkSmartPointer<vtkImageAppend> imageStack = vtkSmartPointer<vtkImageAppend>::New();
imageStack->SetAppendAxis(2);
for (const string& filePath : pngFiles) {
vtkSmartPointer<vtkPNGReader> reader = vtkSmartPointer<vtkPNGReader>::New();
reader->SetNumberOfScalarComponents(1);
reader->SetFileName(filePath.c_str());
reader->Update();
imageStack->AddInputConnection(reader->GetOutputPort());
}
imageStack->Update();
imageStack->GetOutput()->SetSpacing(0.5, 0.5, 0.5);
vtkSmartPointer<vtkSmartVolumeMapper> mapper = vtkSmartPointer<vtkSmartVolumeMapper>::New();
mapper->SetInputConnection(imageStack->GetOutputPort());
vtkSmartPointer<vtkColorTransferFunction> color = vtkSmartPointer<vtkColorTransferFunction>::New();
color->AddRGBPoint(0, 1, 0, 0);
color->AddRGBPoint(255, 1, 1, 1);
vtkSmartPointer<vtkPiecewiseFunction> opacity = vtkSmartPointer<vtkPiecewiseFunction>::New();
opacity->AddPoint(0, 0);
opacity->AddPoint(255, 0.1);
vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
volume->SetMapper(mapper);
volume->GetProperty()->SetColor(color);
volume->GetProperty()->SetScalarOpacity(opacity);
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->AddVolume(volume);
renderer->SetBackground(0.1, 0.2, 0.4);
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindow->Render();
renderWindowInteractor->Start();
return 0;
}
Try to modify your code to this:
int main()
{
string path = "C:\\Users\\fatil\\OneDrive\\Belgeler\\Dicoms\\snow_png";
vector<string> pngFiles = getBMPFiles(path);
if (pngFiles.empty()) {
std::cerr << "Error" << std::endl;
return -1;
}
vtkSmartPointer<vtkImageAppend> imageStack = vtkSmartPointer<vtkImageAppend>::New();
imageStack->SetAppendAxis(2);
for (const string& filePath : pngFiles) {
vtkSmartPointer<vtkPNGReader> reader = vtkSmartPointer<vtkPNGReader>::New();
reader->SetNumberOfScalarComponents(1);
reader->SetFileName(filePath.c_str());
reader->Update();
imageStack->AddInputConnection(reader->GetOutputPort());
}
imageStack->Update();
imageStack->GetOutput()->SetSpacing(0.5, 0.5, 0.5);
vtkSmartPointer<vtkSmartVolumeMapper> mapper = vtkSmartPointer<vtkSmartVolumeMapper>::New();
mapper->SetInputConnection(imageStack->GetOutputPort());
/*add*/ mapper->SetBlendModeToComposite(); // composite first
/*add*/ mapper->Update();
vtkSmartPointer<vtkColorTransferFunction> color = vtkSmartPointer<vtkColorTransferFunction>::New();
/*add*/ color->SetColorSpaceToRGB();
color->AddRGBPoint(0, 1, 0, 0);
color->AddRGBPoint(255, 1, 1, 1);
vtkSmartPointer<vtkPiecewiseFunction> opacity = vtkSmartPointer<vtkPiecewiseFunction>::New();
/* Set both to 1.0 to make sure it works before trying to set translucency. */
opacity->AddPoint(0, 0);
opacity->AddPoint(255, 0.1);
/** new block of code to add **/
//this object instructs VTK on how to render the volume.
vtkSmartPointer<vtkVolumeProperty> volumeProperty = vtkSmartPointer<vtkVolumeProperty>::New();
volumeProperty->ShadeOff();
volumeProperty->SetColor(color);
volumeProperty->SetScalarOpacity(opacity);
//volumeProperty->SetInterpolationType(VTK_LINEAR_INTERPOLATION);
volumeProperty->SetInterpolationType(VTK_NEAREST_INTERPOLATION);
vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New();
volume->SetMapper(mapper);
/*remove*/volume->GetProperty()->SetColor(color);
/*remove*/volume->GetProperty()->SetScalarOpacity(opacity);
/*add*/volume->SetProperty( volumeProperty );
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderer->AddVolume(volume); // if it still doesn't work, try AddActor(volume) instead.
renderer->SetBackground(0.1, 0.2, 0.4);
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->AddRenderer(renderer);
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindow->Render();
renderWindowInteractor->Start();
return 0;
}
Thanks, Paulo. It works for vtkColorTransferFunction
, but not for vtkPiecewiseFunction
. I’ve decided to add an alpha channel to vtkImageData
for opacity. This way, I won’t need both vtkColorTransferFunction
and vtkPiecewiseFunction
. Thanks again. Your last message was really helpful.
That’s how I successfully render volumes in my project, including opacity/translucense. If you manage to make it work as desired without those two, please, share your solution here.