LUTUtilities
Repository source: LUTUtilities
Description¶
A class called LUTUtilities is demonstrated along with a test harness that shows you how to use the class.
This class allows you to:
- Print the contents of the lookup table
- Compare two lookup tables to see if they are the same.
The test harness is a function called: TestLookupTables that tests pairs of lookup tables against each other.
The program will not display any output if all tests are successful.
Other languages
See (Python)
Question
If you have a question about this example, please use the VTK Discourse Forum
Code¶
LUTUtilities.cxx
#include <vtkColorSeries.h>
#include <vtkLookupTable.h>
#include <vtkNew.h>
#include <vtkVariantArray.h>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
//! Utilities for displaying and comparing lookup tables.
class LUTUtilities
{
public:
//-----------------------------------------------------------------------------
//! Constructor.
LUTUtilities(){};
//-----------------------------------------------------------------------------
//! Destructor.
~LUTUtilities(){};
//-----------------------------------------------------------------------------
//! Display the contents of the lookup table.
/*!
* @param lut - the lookup table.
* @return a string containing the table data.
*/
std::string DisplayLUTAsString(vtkLookupTable* lut)
{
vtkIdType tv = lut->GetNumberOfTableValues();
double dR[2];
lut->GetTableRange(dR);
std::ostringstream os;
if (lut->GetIndexedLookup())
{
vtkIdType av = lut->GetNumberOfAnnotatedValues();
os << "Categorical Lookup Table\nNumber of annotated values: " << av
<< " Number of table values: " << tv << "\nTable Range: " << std::fixed
<< std::setw(8) << std::setprecision(6) << dR[0] << " to " << dR[1]
<< std::endl;
if (av > 0)
{
for (vtkIdType i = 0; i < av; ++i)
{
double rgba[4];
lut->GetAnnotationColor(lut->GetAnnotatedValue(i), rgba);
os << std::setw(5) << lut->GetAnnotation(i) << ": ";
os << this->AssembleRGBAString(rgba);
os << std::endl;
}
}
else
{
for (vtkIdType i = 0; i < tv; ++i)
{
double rgba[4];
lut->GetTableValue(i, rgba);
os << std::setw(5) << i << ": ";
os << this->AssembleRGBAString(rgba);
os << std::endl;
}
}
}
else
{
os << "Ordinal Lookup Table\nNumber of table values : " << tv
<< "\nTable Range: " << std::fixed << std::setw(8)
<< std::setprecision(6) << dR[0] << " to " << dR[1] << std::endl;
std::vector<double> indices;
for (int i = 0; i < tv; ++i)
{
indices.push_back((dR[1] - dR[0]) * i / tv + dR[0]);
}
for (std::vector<double>::const_iterator p = indices.begin();
p != indices.end(); ++p)
{
double rgba[4];
lut->GetColor(*p, rgba);
rgba[3] = lut->GetOpacity(*p);
os << std::fixed << std::setw(5) << std::setprecision(2) << *p << ": ";
os << this->AssembleRGBAString(rgba);
os << std::endl;
}
}
return os.str();
}
//-----------------------------------------------------------------------------
//! Compare two lookup tables.
/*!
* @param lut1 - the lookup table.
* @param lut2 - the lookup table.
* @return true if the tables are the same.
*/
std::pair<bool, std::string> CompareLUTs(vtkLookupTable* lut1,
vtkLookupTable* lut2)
{
std::pair<bool, std::string> res(true, "");
if (lut1->GetIndexedLookup() != lut2->GetIndexedLookup())
{
res.first = false;
res.second = "One table is ordinal and the other is categorical.";
return res;
}
if (lut1->GetIndexedLookup() &&
lut1->GetNumberOfAnnotatedValues() !=
lut2->GetNumberOfAnnotatedValues())
{
res.first = false;
res.second = "The number of annotated values do not match.";
return res;
}
if (lut1->GetNumberOfTableValues() != lut2->GetNumberOfTableValues())
{
res.first = false;
res.second = "Table values do not match.";
return res;
}
double dR1[2];
lut1->GetTableRange(dR1);
double dR2[2];
lut2->GetTableRange(dR2);
if (dR1[0] != dR2[0] && dR2[1] != dR1[1])
{
res.first = false;
res.second = "Table ranges do not match.";
}
if (lut1->GetIndexedLookup())
{
vtkIdType av = lut1->GetNumberOfAnnotatedValues();
if (av > 0)
{
for (vtkIdType i = 0; i < av; ++i)
{
if (lut1->GetAnnotation(i) != lut1->GetAnnotation(i))
{
res.first = false;
res.second = "Annotations do not match.";
return res;
}
}
for (vtkIdType i = 0; i < av; ++i)
{
double rgba1[4];
lut1->GetAnnotationColor(lut1->GetAnnotatedValue(i), rgba1);
double rgba2[4];
lut2->GetAnnotationColor(lut2->GetAnnotatedValue(i), rgba2);
if (!this->CompareRGBA(rgba1, rgba2))
{
res.first = false;
res.second = "Colors do not match.";
return res;
}
}
}
else
{
for (vtkIdType i = 0; i < av; ++i)
{
double rgba1[4];
lut1->GetTableValue(i, rgba1);
double rgba2[4];
lut2->GetTableValue(i, rgba2);
if (!this->CompareRGBA(rgba1, rgba2))
{
res.first = false;
res.second = "Colors do not match.";
return res;
}
}
}
}
else
{
vtkIdType tv = lut1->GetNumberOfTableValues();
std::vector<double> indices;
for (int i = 0; i < tv; ++i)
{
indices.push_back((dR1[1] - dR1[0]) * i / tv + dR1[0]);
}
for (std::vector<double>::const_iterator p = indices.begin();
p != indices.end(); ++p)
{
double rgba1[4];
lut1->GetColor(*p, rgba1);
rgba1[3] = lut1->GetOpacity(*p);
double rgba2[4];
lut2->GetColor(*p, rgba2);
rgba2[3] = lut2->GetOpacity(*p);
if (!this->CompareRGBA(rgba1, rgba2))
{
res.first = false;
res.second = "Colors do not match.";
return res;
}
}
}
return res;
}
private:
//-----------------------------------------------------------------------------
//! Get a string of [R, G, B, A] as double.
std::string RGBAToDoubleString(double const* rgba)
{
std::ostringstream os;
os << "[";
for (int i = 0; i < 4; ++i)
{
if (i == 0)
{
os << std::fixed << std::setw(8) << std::setprecision(6) << rgba[i];
}
else
{
os << std::fixed << std::setw(9) << std::setprecision(6) << rgba[i];
}
if (i < 3)
{
os << ",";
}
}
os << "]";
return os.str();
}
//-----------------------------------------------------------------------------
//! Get a string of [R, G, B, A] as unsigned char.
std::string RGBAToCharString(double const* rgba)
{
std::ostringstream os;
os << "[";
for (int i = 0; i < 4; ++i)
{
if (i == 0)
{
os << std::setw(3) << static_cast<int>(rgba[i] * 255);
}
else
{
os << std::setw(4) << static_cast<int>(rgba[i] * 255);
}
if (i < 3)
{
os << ",";
}
}
os << "]";
return os.str();
}
//-----------------------------------------------------------------------------
//! Get a hexadecimal string of the RGB colors.
std::string RGBToHexString(double const* rgba)
{
std::ostringstream os;
for (int i = 0; i < 3; ++i)
{
os << std::setw(2) << std::setfill('0') << std::hex
<< static_cast<int>(rgba[i] * 255);
}
return os.str();
}
//-----------------------------------------------------------------------------
//! Get a string of [R, G, B, A] as double, unsigned char and hex.
std::string AssembleRGBAString(double const* rgba)
{
std::ostringstream os;
os << this->RGBAToDoubleString(rgba);
os << " ";
os << this->RGBAToCharString(rgba);
os << " 0x";
os << this->RGBToHexString(rgba);
return os.str();
}
//-----------------------------------------------------------------------------
//! Compare two rgba colors.
template <typename T> bool CompareRGBA(T const* rgba1, T const* rgba2)
{
bool areEquivalent = true;
for (int i = 0; i < 4; ++i)
{
areEquivalent &= rgba1[i] == rgba2[i];
if (!areEquivalent)
{
return false;
}
}
return true;
}
};
//-----------------------------------------------------------------------------
//! Get all the color scheme names.
/*!
* @return a map of the names keyed on their index.
*/
std::map<int, std::string> GetAllColorSchemes()
{
std::map<int, std::string> colorSchemes;
vtkNew<vtkColorSeries> colorSeries;
for (int i = 0; i < colorSeries->GetNumberOfColorSchemes(); ++i)
{
colorSeries->SetColorScheme(i);
colorSchemes[i] = colorSeries->GetColorSchemeName();
}
return colorSchemes;
}
//-----------------------------------------------------------------------------
//! The available color scheme indexes and names.
/*!
* @param colorSchemes - a map of the names keyed on their index.
* @return a string if the indexes and names.
*/
std::string AvailableColorSchemes(std::map<int, std::string> const& colorSchemes)
{
std::ostringstream os;
for (std::map<int, std::string>::const_iterator p = colorSchemes.begin();
p != colorSchemes.end(); ++p)
{
os << std::setw(3) << p->first << "\t" << p->second << std::endl;
}
return os.str();
}
//-----------------------------------------------------------------------------
//! Display the available color schemes.
void DisplayAvailableColorSchemes()
{
std::string line("-----------------------------------------------------------"
"------------------\n");
std::map<int, std::string> colorSchemes;
colorSchemes = GetAllColorSchemes();
std::cout << line << AvailableColorSchemes(colorSchemes) << line << std::endl;
}
//-----------------------------------------------------------------------------
//! Display the lookup tables and reason for failure.
/*!
* @param reason - the reason.
* @param lut1 - the first lookup table.
* @param lut2 - the second lookup table.
*/
void DisplayResults(std::string const& reason, vtkLookupTable* lut1,
vtkLookupTable* lut2)
{
LUTUtilities lutUtilities;
std::string line("-----------------------------------------------------------"
"------------------\n");
std::cout << line;
std::cout << reason << std::endl;
std::cout << lutUtilities.DisplayLUTAsString(lut1) << std::endl;
std::cout << lutUtilities.DisplayLUTAsString(lut2) << std::endl;
std::cout << line;
}
//-----------------------------------------------------------------------------
//! Test pairs of lookup tables.
/*!
* @param lut1 - the first lookup table.
* @param lut2 - the second lookup table.
* @param expected - if false a fail is expected.
* @return true/false.
*/
bool TestTables(vtkLookupTable* lut1, vtkLookupTable* lut2,
bool const expected = true)
{
LUTUtilities lutUtilities;
std::pair<bool, std::string> comparison =
lutUtilities.CompareLUTs(lut1, lut2);
if (comparison.first != expected)
{
DisplayResults(comparison.second, lut1, lut2);
}
return (expected) ? comparison.first : !comparison.first;
}
//-----------------------------------------------------------------------------
//! Test various combinations of lookup tables.
/*!
* @param lutMode - if true the tables are ordinal, categorical otherwise.
* @return true if all tests passed.
*/
bool TestLookupTables(bool const& lutMode)
{
// LUTUtilities lutUtilities;
vtkNew<vtkLookupTable> lut1;
vtkNew<vtkLookupTable> lut2;
vtkNew<vtkColorSeries> colorSeries;
int colorSeriesEnum = colorSeries->SPECTRUM;
colorSeries->SetColorScheme(colorSeriesEnum);
colorSeries->BuildLookupTable(lut1);
colorSeries->BuildLookupTable(lut2);
if (lutMode)
{
lut1->IndexedLookupOff();
lut2->IndexedLookupOff();
}
lut1->SetNanColor(1, 0, 0, 1);
lut2->SetNanColor(1, 0, 0, 1);
if (!lutMode)
{
// For the annotation just use a letter of the alphabet.
vtkNew<vtkVariantArray> values1;
vtkNew<vtkVariantArray> values2;
std::string str = "abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i < lut1->GetNumberOfTableValues(); ++i)
{
values1->InsertNextValue(vtkVariant(str.substr(i, 1)));
}
for (int i = 0; i < lut2->GetNumberOfTableValues(); ++i)
{
values2->InsertNextValue(vtkVariant(str.substr(i, 1)));
}
for (int i = 0; i < values1->GetNumberOfTuples(); ++i)
{
lut1->SetAnnotation(i, values1->GetValue(i).ToString());
}
for (int i = 0; i < values2->GetNumberOfTuples(); ++i)
{
lut2->SetAnnotation(i, values2->GetValue(i).ToString());
}
}
// Are they the same?
bool res = true;
res &= TestTables(lut1, lut2);
// Different size
lut2->SetNumberOfTableValues(5);
res &= TestTables(lut1, lut2, false);
lut2->SetNumberOfTableValues(lut1->GetNumberOfTableValues());
res &= TestTables(lut1, lut2);
if (lutMode)
{
// Different range
lut2->SetTableRange(1, 2);
res &= TestTables(lut1, lut2, false);
double tr[2];
lut1->GetTableRange(tr);
lut2->SetTableRange(tr);
res &= TestTables(lut1, lut2);
// Different color
colorSeriesEnum = colorSeries->COOL;
colorSeries->SetColorScheme(colorSeriesEnum);
vtkNew<vtkLookupTable> lut3;
colorSeries->BuildLookupTable(lut3);
lut3->IndexedLookupOff();
res &= TestTables(lut1, lut3, false);
// One indexed, the other ordinal.
lut1->IndexedLookupOn();
res &= TestTables(lut1, lut2, false);
}
else
{
// Different color
colorSeriesEnum = colorSeries->COOL;
colorSeries->SetColorScheme(colorSeriesEnum);
vtkNew<vtkLookupTable> lut3;
// For the annotation just use a letter of the alphabet.
vtkNew<vtkVariantArray> values;
std::string str = "abcdefghijklmnopqrstuvwxyz";
for (int i = 0; i < lut1->GetNumberOfTableValues(); ++i)
{
values->InsertNextValue(vtkVariant(str.substr(i, 1)));
}
for (int i = 0; i < values->GetNumberOfTuples(); ++i)
{
lut3->SetAnnotation(i, values->GetValue(i).ToString());
}
colorSeries->BuildLookupTable(lut3);
res &= TestTables(lut1, lut3, false);
// Different annotations.
lut2->ResetAnnotations();
for (int i = 0; i < values->GetNumberOfTuples(); ++i)
{
if (i % 3 == 0)
continue;
lut2->SetAnnotation(i, values->GetValue(i).ToString());
}
res &= TestTables(lut1, lut2, false);
// No annotations.
lut1->ResetAnnotations();
lut2->ResetAnnotations();
res &= TestTables(lut1, lut2);
// One indexed, the other ordinal.
lut1->IndexedLookupOff();
res &= TestTables(lut1, lut2, false);
}
return res;
}
//-----------------------------------------------------------------------------
int main(int, char*[])
{
// DisplayAvailableColorSchemes();
// Test ordinal LUTS.
bool res = TestLookupTables(true);
// Test categorical LUTs.
res &= TestLookupTables(false);
return (res) ? EXIT_SUCCESS : EXIT_FAILURE;
}
CMakeLists.txt¶
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
project(LUTUtilities)
find_package(VTK COMPONENTS
CommonColor
CommonCore
)
if (NOT VTK_FOUND)
message(FATAL_ERROR "LUTUtilities: 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(LUTUtilities MACOSX_BUNDLE LUTUtilities.cxx )
target_link_libraries(LUTUtilities PRIVATE ${VTK_LIBRARIES}
)
# vtk_module_autoinit is needed
vtk_module_autoinit(
TARGETS LUTUtilities
MODULES ${VTK_LIBRARIES}
)
Download and Build LUTUtilities¶
Click here to download LUTUtilities and its CMakeLists.txt file. Once the tarball LUTUtilities.tar has been downloaded and extracted,
cd LUTUtilities/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:
./LUTUtilities
WINDOWS USERS
Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.