RescaleReverseLUT
vtk-examples/Cxx/Utilities/RescaleReverseLUT
Description¶
This example shows how to adjust a colormap so that the colormap scalar range matches the scalar range on the object. This is done by adjusting the colormap so that the colormap scalar range matches the scalar range of the object by rescaling the control points and, optionally, reversing the order of the colors.
Of course, if you are generating the scalars, it may be easier to just change the scalar range of your filter. However, this may not be possible in some cases.
Here, we generate the original Color Transfer Function (CTF) corresponding to the seven colors that Isaac Newton labeled when dividing the spectrum of visible light in 1672. There are seven colors and the scalar range is [-1, 1].
The cylinder has a vtkElevationFilter applied to it with a scalar range of [0, 1].
There are four images:
- Original - The cylinder is colored by only the top four colors from the CTF. This is because the elevation scalar range on the cylinder is [0, 1] and the CTF scalar range is [-1, 1]. So the coloring is green->violet.
- Reversed - We have reversed the colors from the original CTF and the lower four colors in the original CTF are now the top four colors used to color the cylinder. The coloring is now green->red.
- Rescaled - The original CTF is rescaled to the range [0, 1] to match the scalar range of the elevation filter. The coloring is red->violet.
- Rescaled and Reversed - The original CTF is rescaled to the range [0, 1] and the colors reversed. The coloring is violet->red.
Other languages
See (Python)
Question
If you have a question about this example, please use the VTK Discourse Forum
Code¶
RescaleReverseLUT.cxx
#include <vtkActor.h>
#include <vtkActor2D.h>
#include <vtkCylinderSource.h>
#include <vtkDiscretizableColorTransferFunction.h>
#include <vtkElevationFilter.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkScalarBarActor.h>
#include <vtkSmartPointer.h>
#include <vtkTextMapper.h>
#include <vtkTextProperty.h>
#include <array>
#include <string>
#include <vector>
namespace {
/**
* Generate the color transfer function.
*
* The seven colors corresponding to the colors that Isaac Newton labelled
* when dividing the spectrum of visible light in 1672 are used.
*
* The modern variant of these colors can be selected and used instead.
*
* See: [Rainbow](https://en.wikipedia.org/wiki/Rainbow)
*
* @param modern: Selects either Newton's original seven colors or the modern
* version.
*
* @return The color transfer function.
*/
vtkNew<vtkDiscretizableColorTransferFunction>
GetCTF(bool const& modern = false);
/**
* Generate a new color transfer function from the old one,
* adding in the new x and rgb values.
*
* @param oldCTF: The old color transfer function.
* @param newX: The new color x-values.
* @param newRGB: The color RGB values.
* @param reverse: If true, reverse the colors.
*
* @return The new color transfer function.
*/
vtkNew<vtkDiscretizableColorTransferFunction>
GenerateNewCTF(vtkNew<vtkDiscretizableColorTransferFunction> const& oldCTF,
std::vector<double> const& newX,
std::vector<std::array<double, 3>> const& newRGB,
bool const& reverse = false);
/**
* Rescale the values.
*
* See:
* https://stats.stackexchange.com/questions/25894/changing-the-scale-of-a-variable-to-0-100
*
* @param xv: The values to be rescaled.
* @param newMin: The new minimum value.
* @param newMax: The new maximum value.
*
* @return The rescaled values.
*/
std::vector<double> Rescale(std::vector<double> const& xv,
double const& newMin = 0, double const& newMax = 1);
/**
* Rescale and, optionally, reverse the colors in the color transfer function.
*
* @param ctf: The color transfer function to rescale.
* @param newMin: The new minimum value.
* @param newMax: The new maximum value.
* @param reverse: If true, reverse the colors.
*
* @return The rescaled color transfer function.
*/
vtkNew<vtkDiscretizableColorTransferFunction>
RescaleCTF(vtkNew<vtkDiscretizableColorTransferFunction> const& ctf,
double const& newMin = 0, double const& newMax = 1,
bool const& reverse = false);
} // namespace
int main(int, char*[])
{
std::array<unsigned char, 4> bkg{82, 87, 110, 255};
vtkNew<vtkNamedColors> colors;
colors->SetColor("ParaViewBkg", bkg.data());
vtkNew<vtkRenderWindow> renWin;
renWin->SetSize(640 * 2, 480 * 2);
renWin->SetWindowName("RescaleReverseLUT");
vtkNew<vtkRenderWindowInteractor> iRen;
iRen->SetRenderWindow(renWin);
vtkNew<vtkInteractorStyleTrackballCamera> style;
iRen->SetInteractorStyle(style);
std::vector<vtkNew<vtkDiscretizableColorTransferFunction>> ctf;
ctf.push_back(GetCTF(false));
const double* tmp = ctf[0]->GetRange();
ctf.push_back(RescaleCTF(ctf[0], 0, 1, false));
ctf.push_back(RescaleCTF(ctf[0], tmp[0], tmp[1], true));
ctf.push_back(RescaleCTF(ctf[0], 0, 1, true));
// Define viewport ranges.
std::array<double, 4> xMins{0.0, 0.0, 0.5, 0.5};
std::array<double, 4> xMaxs{0.5, 0.5, 1.0, 1.0};
std::array<double, 4> yMins{0.5, 0.0, 0.5, 0.0};
std::array<double, 4> yMaxs{1.0, 0.5, 1.0, 0.5};
// Define titles.
std::vector<std::string> titles{"Orginal", "Rescaled", "Reversed",
"Rescaled and Reversed"};
// Create a common text property->
vtkNew<vtkTextProperty> textProperty;
textProperty->SetFontSize(36);
textProperty->SetJustificationToCentered();
textProperty->SetColor(colors->GetColor3d("LightGoldenrodYellow").GetData());
std::vector<vtkSmartPointer<vtkCylinderSource>> sources;
std::vector<vtkSmartPointer<vtkElevationFilter>> elevation_filters;
std::vector<vtkSmartPointer<vtkPolyDataMapper>> mappers;
std::vector<vtkSmartPointer<vtkActor>> actors;
std::vector<vtkSmartPointer<vtkScalarBarActor>> scalarBars;
std::vector<vtkSmartPointer<vtkRenderer>> renderers;
std::vector<vtkSmartPointer<vtkTextMapper>> textMappers;
std::vector<vtkSmartPointer<vtkActor2D>> textActors;
for (auto i = 0; i < 4; ++i)
{
vtkNew<vtkCylinderSource> cylinder;
cylinder->SetCenter(0.0, 0.0, 0.0);
cylinder->SetResolution(6);
cylinder->Update();
double* bounds = cylinder->GetOutput()->GetBounds();
sources.push_back(cylinder);
vtkNew<vtkElevationFilter> elevation_filter;
elevation_filter->SetScalarRange(0, 1);
elevation_filter->SetLowPoint(0, bounds[2], 0);
elevation_filter->SetHighPoint(0, bounds[3], 0);
elevation_filter->SetInputConnection(sources[i]->GetOutputPort());
elevation_filters.push_back(elevation_filter);
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(elevation_filters[i]->GetOutputPort());
mapper->SetLookupTable(ctf[i]);
mapper->SetColorModeToMapScalars();
mapper->InterpolateScalarsBeforeMappingOn();
mappers.push_back(mapper);
vtkNew<vtkActor> actor;
actor->SetMapper(mappers[i]);
actors.push_back(actor);
// Add a scalar bar.
vtkNew<vtkScalarBarActor> scalarBar;
scalarBar->SetLookupTable(ctf[i]);
scalarBars.push_back(scalarBar);
vtkNew<vtkTextMapper> textMapper;
textMappers.push_back(textMapper);
textMappers[i]->SetInput(titles[i].c_str());
textMappers[i]->SetTextProperty(textProperty);
vtkNew<vtkActor2D> textActor;
textActors.push_back(textActor);
textActors[i]->SetMapper(textMappers[i]);
// Note: The position of an Actor2D is specified in display coordinates.
textActors[i]->SetPosition(300, 16);
vtkNew<vtkRenderer> ren;
ren->SetBackground(colors->GetColor3d("ParaViewBkg").GetData());
ren->AddActor(actors[i]);
ren->AddActor(scalarBars[i]);
ren->AddActor(textActors[i]);
ren->SetViewport(xMins[i], yMins[i], xMaxs[i], yMaxs[i]);
renderers.push_back(ren);
renWin->AddRenderer(renderers[i]);
}
renWin->Render();
iRen->Start();
return EXIT_SUCCESS;
}
namespace {
vtkNew<vtkDiscretizableColorTransferFunction> GetCTF(bool const& modern)
{
// name: Rainbow, creator: Andrew Maclean
// interpolationspace: RGB, space: rgb
// file name:
vtkNew<vtkDiscretizableColorTransferFunction> ctf;
ctf->SetColorSpaceToRGB();
ctf->SetScaleToLinear();
ctf->SetNanColor(0.5, 0.5, 0.5);
ctf->SetBelowRangeColor(0.0, 0.0, 0.0);
ctf->UseBelowRangeColorOn();
ctf->SetAboveRangeColor(1.0, 1.0, 1.0);
ctf->UseAboveRangeColorOn();
if (modern)
{
ctf->AddRGBPoint(-1.0, 1.0, 0.0, 0.0); // Red
ctf->AddRGBPoint(-2.0 / 3.0, 1.0, 128.0 / 255.0, 0.0); // Orange #ff8000
ctf->AddRGBPoint(-1.0 / 3.0, 1.0, 1.0, 0.0); // Yellow
ctf->AddRGBPoint(0.0, 0.0, 1.0, 0.0); // Green #00ff00
ctf->AddRGBPoint(1.0 / 3.0, 0.0, 1.0, 1.0); // Cyan
ctf->AddRGBPoint(2.0 / 3.0, 0.0, 0.0, 1.0); // Blue
ctf->AddRGBPoint(1.0, 128.0 / 255.0, 0.0, 1.0); // Violet #8000ff
}
else
{
ctf->AddRGBPoint(-1.0, 1.0, 0.0, 0.0); // Red
ctf->AddRGBPoint(-2.0 / 3.0, 1.0, 165.0 / 255.0, 0.0); // Orange #00a500
ctf->AddRGBPoint(-1.0 / 3.0, 1.0, 1.0, 0.0); // Yellow
ctf->AddRGBPoint(0.0, 0.0, 125.0 / 255.0, 0.0); // Green #008000
ctf->AddRGBPoint(1.0 / 3.0, 0.0, 153.0 / 255.0, 1.0); // Blue #0099ff
ctf->AddRGBPoint(2.0 / 3.0, 68.0 / 255.0, 0,
153.0 / 255.0); // Indigo #4400ff
ctf->AddRGBPoint(1.0, 153.0 / 255.0, 0.0, 1.0); // Violet #9900ff
}
ctf->SetNumberOfValues(7);
ctf->DiscretizeOn();
return ctf;
}
vtkNew<vtkDiscretizableColorTransferFunction>
GenerateNewCTF(vtkNew<vtkDiscretizableColorTransferFunction> const& oldCTF,
std::vector<double> const& newX,
std::vector<std::array<double, 3>> const& newRGB,
bool const& reverse)
{
vtkNew<vtkDiscretizableColorTransferFunction> newCTF;
newCTF->SetScale(oldCTF->GetScale());
newCTF->SetColorSpace(oldCTF->GetColorSpace());
newCTF->SetNanColor(oldCTF->GetNanColor());
if (!reverse)
{
newCTF->SetBelowRangeColor(oldCTF->GetBelowRangeColor());
newCTF->SetUseBelowRangeColor(oldCTF->GetUseBelowRangeColor());
newCTF->SetAboveRangeColor(oldCTF->GetAboveRangeColor());
newCTF->SetUseAboveRangeColor(oldCTF->GetUseAboveRangeColor());
}
else
{
newCTF->SetBelowRangeColor(oldCTF->GetAboveRangeColor());
newCTF->SetUseBelowRangeColor(oldCTF->GetUseAboveRangeColor());
newCTF->SetAboveRangeColor(oldCTF->GetBelowRangeColor());
newCTF->SetUseAboveRangeColor(oldCTF->GetUseBelowRangeColor());
}
newCTF->SetNumberOfValues(newX.size());
newCTF->SetDiscretize(oldCTF->GetDiscretize());
if (!reverse)
{
for (size_t i = 0; i < newX.size(); ++i)
{
newCTF->AddRGBPoint(newX[i], newRGB[i][0], newRGB[i][1],
newRGB[i][2]);
}
}
else
{
auto sz = newX.size();
for (size_t i = 0; i < sz; i++)
{
auto j = sz - (i + 1);
newCTF->AddRGBPoint(newX[i], newRGB[j][0], newRGB[j][1],
newRGB[j][2]);
}
}
newCTF->Build();
return newCTF;
}
std::vector<double> Rescale(std::vector<double> const& values,
double const& newMin, double const& newMax)
{
std::vector<double> res;
double oldMin = *std::min_element(values.begin(), values.end());
double oldMax = *std::max_element(values.begin(), values.end());
for (size_t i = 0; i < values.size(); ++i)
{
double newV =
(newMax - newMin) / (oldMax - oldMin) * (values[i] - oldMin) + newMin;
// double newV1 =
// (newMax - newMin) / (oldMax - oldMin) * (values[i] - oldMax) +
// newMax;
res.push_back(newV);
}
return res;
}
vtkNew<vtkDiscretizableColorTransferFunction>
RescaleCTF(vtkNew<vtkDiscretizableColorTransferFunction> const& ctf,
double const& newMin, double const& newMax, bool const& reverse)
{
double r0;
double r1;
if (newMin > newMax)
{
r0 = newMax;
r1 = newMin;
}
else
{
r0 = newMin;
r1 = newMax;
}
std::vector<double> xv;
std::vector<std::array<double, 3>> rgbv;
double nv[6] = {0, 0, 0, 0, 0, 0};
for (auto i = 0; i < ctf->GetNumberOfValues(); ++i)
{
ctf->GetNodeValue(i, nv);
double x = nv[0];
std::array<double, 3> rgb;
for (auto j = 1; j < 4; ++j)
{
rgb[j - 1] = nv[j];
}
xv.push_back(x);
rgbv.push_back(rgb);
}
std::vector<double> xvr = Rescale(xv, r0, r1);
return GenerateNewCTF(ctf, xvr, rgbv, reverse);
}
} // namespace
CMakeLists.txt¶
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(RescaleReverseLUT)
find_package(VTK COMPONENTS
CommonColor
CommonCore
FiltersCore
FiltersSources
InteractionStyle
RenderingAnnotation
RenderingContextOpenGL2
RenderingCore
RenderingFreeType
RenderingGL2PSOpenGL2
RenderingOpenGL2
)
if (NOT VTK_FOUND)
message(FATAL_ERROR "RescaleReverseLUT: Unable to find the VTK build folder.")
endif()
# Prevent a "command line is too long" failure in Windows.
set(CMAKE_NINJA_FORCE_RESPONSE_FILE "ON" CACHE BOOL "Force Ninja to use response files.")
add_executable(RescaleReverseLUT MACOSX_BUNDLE RescaleReverseLUT.cxx )
target_link_libraries(RescaleReverseLUT PRIVATE ${VTK_LIBRARIES}
)
# vtk_module_autoinit is needed
vtk_module_autoinit(
TARGETS RescaleReverseLUT
MODULES ${VTK_LIBRARIES}
)
Download and Build RescaleReverseLUT¶
Click here to download RescaleReverseLUT and its CMakeLists.txt file. Once the tarball RescaleReverseLUT.tar has been downloaded and extracted,
cd RescaleReverseLUT/build
If VTK is installed:
cmake ..
If VTK is not installed but compiled on your system, you will need to specify the path to your VTK build:
cmake -DVTK_DIR:PATH=/home/me/vtk_build ..
Build the project:
make
and run it:
./RescaleReverseLUT
WINDOWS USERS
Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.