Skip to content

Commit

Permalink
Implemented support for transparent colormaps
Browse files Browse the repository at this point in the history
  • Loading branch information
smistad committed Sep 4, 2023
1 parent a0a1768 commit cbfe060
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 33 deletions.
38 changes: 32 additions & 6 deletions source/FAST/Algorithms/ApplyColormap/ApplyColormap.cl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ float4 vload4_at_pos(int position, __constant float* data) {
return (float4)(data[position], data[position+1], data[position+2], 1.0f);
}

float4 vload4_at_pos_opacity(int position, __constant float* data) {
return (float4)(data[position], data[position+1], data[position+2], data[position+3]);
}

float getIntensityFromColormap(float intensity, __constant float* colormap, int steps, char interpolate) {
float first = colormap[1];
float firstIntensity = colormap[0];
Expand Down Expand Up @@ -67,16 +71,30 @@ float getIntensityFromColormap(float intensity, __constant float* colormap, int
return first;
}

float4 getColorFromColormap(float intensity, __constant float* colormap, int steps, char interpolate) {
float4 first = vload4_at_pos(1, colormap);
float4 getColorFromColormap(float intensity, __constant float* colormap, int steps, char interpolate, char hasOpacity) {
float4 first;
if(hasOpacity == 1) {
first = vload4_at_pos_opacity(1, colormap);
} else {
first = vload4_at_pos(1, colormap);
}
float firstIntensity = colormap[0];

int values = 4;
if(hasOpacity == 1)
values = 5;

if(intensity <= firstIntensity)
return first;

for(int i = 1; i < steps; ++i) {
float4 second = vload4_at_pos(i*4 + 1, colormap);
float secondIntensity = colormap[i*4];
float4 second;
if(hasOpacity == 1) {
second = vload4_at_pos_opacity(i*values + 1, colormap);
} else {
second = vload4_at_pos(i*values + 1, colormap);
}
float secondIntensity = colormap[i*values];
if(intensity <= secondIntensity) {
if(interpolate == 1) {
return mix(first, second, (intensity - firstIntensity)/(secondIntensity - firstIntensity));
Expand All @@ -101,15 +119,23 @@ __kernel void applyColormapGrayscale(
__constant float* colormap,
__private int steps,
__private char interpolate,
__private char grayscale
__private char grayscale,
__private char hasOpacity,
__private char isIntensityInvariant,
__private float minValue,
__private float maxValue
) {
const int2 pos = {get_global_id(0), get_global_id(1)};

float4 value = readImageAsFloat2D(input, sampler, pos);

if(isIntensityInvariant == 1) {
value.x = clamp((value.x - minValue) / (maxValue - minValue), 0.0f, 1.0f);
}

if(grayscale == 1) {
writeImageAsFloat2D(output, pos, getIntensityFromColormap(value.x, colormap, steps, interpolate));
} else {
writeImageAsFloat42D(output, pos, getColorFromColormap(value.x, colormap, steps, interpolate));
writeImageAsFloat42D(output, pos, getColorFromColormap(value.x, colormap, steps, interpolate, hasOpacity));
}
}
85 changes: 76 additions & 9 deletions source/FAST/Algorithms/ApplyColormap/ApplyColormap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
#include <FAST/Data/Image.hpp>

namespace fast {
ApplyColormap::ApplyColormap(Colormap colormap) {
ApplyColormap::ApplyColormap(Colormap colormap, float minValue, float maxValue) {
createInputPort(0, "Image");
createOutputPort(0, "Image");
setColormap(colormap);
setMinValue(minValue);
setMaxValue(maxValue);
createOpenCLProgram(Config::getKernelSourcePath() + "/Algorithms/ApplyColormap/ApplyColormap.cl");
}

Expand All @@ -28,7 +30,11 @@ void ApplyColormap::execute() {
if(m_colormap.isGrayscale()) {
output = Image::create(input->getSize(), TYPE_UINT8, 1);
} else {
output = Image::create(input->getSize(), TYPE_UINT8, 3);
if(m_colormap.hasOpacity()) {
output = Image::create(input->getSize(), TYPE_UINT8, 4);
} else {
output = Image::create(input->getSize(), TYPE_UINT8, 3);
}
}
output->setSpacing(input->getSpacing());
SceneGraph::setParentNode(output, input);
Expand All @@ -44,12 +50,27 @@ void ApplyColormap::execute() {

cl::Kernel kernel(getOpenCLProgram(device), "applyColormapGrayscale");

float minValue = m_minValue;
float maxValue = m_maxValue;
if(m_colormap.isIntensityInvariant()) {
if(std::isnan(m_minValue)) {
minValue = input->calculateMinimumIntensity();
}
if(std::isnan(m_maxValue)) {
maxValue = input->calculateMaximumIntensity();
}
}

kernel.setArg(0, *inputAccess->get2DImage());
kernel.setArg(1, *outputAccess->get2DImage());
kernel.setArg(2, m_colormapBuffer);
kernel.setArg(3, m_colormap.getSteps());
kernel.setArg(4, (char)(m_colormap.isInterpolated() ? 1 : 0));
kernel.setArg(5, (char)(m_colormap.isGrayscale() ? 1 : 0));
kernel.setArg(6, (char)(m_colormap.hasOpacity() ? 1 : 0));
kernel.setArg(7, (char)(m_colormap.isIntensityInvariant() ? 1 : 0));
kernel.setArg(8, minValue);
kernel.setArg(9, maxValue);

device->getCommandQueue().enqueueNDRangeKernel(
kernel,
Expand All @@ -65,6 +86,16 @@ ApplyColormap::ApplyColormap() {

}

void ApplyColormap::setMinValue(float minValue) {
m_minValue = minValue;
setModified(true);
}

void ApplyColormap::setMaxValue(float maxValue) {
m_maxValue = maxValue;
setModified(true);
}

bool Colormap::isGrayscale() const {
return m_grayscale;
}
Expand All @@ -73,24 +104,31 @@ Colormap::Colormap() {

}

Colormap::Colormap(const std::map<float, Color>& colormap, bool interpolate) {
Colormap::Colormap(const std::map<float, Color>& colormap, bool interpolate, bool intensityInvariant) {
for(auto item : colormap) {
m_data.push_back(item.first);
m_data.push_back(item.second.getRedValue()*255.0f);
m_data.push_back(item.second.getGreenValue()*255.0f);
m_data.push_back(item.second.getBlueValue()*255.0f);
if(item.second.hasOpacity()) {
// FIXME: what if not all have defined opacity?
m_hasOpacity = true;
m_data.push_back(item.second.getOpacity()*255.0f);
}
}
m_grayscale = false;
m_interpolate = interpolate;
m_intensityInvariant = intensityInvariant;
}

Colormap::Colormap(const std::map<float, float>& colormap, bool interpolate) {
Colormap::Colormap(const std::map<float, float>& colormap, bool interpolate, bool intensityInvariant) {
for(auto item : colormap) {
m_data.push_back(item.first);
m_data.push_back(item.second);
}
m_grayscale = true;
m_interpolate = interpolate;
m_intensityInvariant = intensityInvariant;
}

bool Colormap::isInterpolated() const {
Expand All @@ -99,11 +137,15 @@ bool Colormap::isInterpolated() const {

void Colormap::checkData() const {
int elements = 4;
if(m_grayscale)
if(m_grayscale) {
elements = 2;
} else {
if(m_hasOpacity)
elements = 5;
}
if(m_data.size() % elements != 0)
throw Exception("Number of float data points given to Colormap must be dividable by 4 (color) or 2 (grayscale)."
"Each point in colormap must be 4 or 2 tuple: (intensity, red, green, blue) or (intensity, intensity)");
throw Exception("Number of float data points given to Colormap must be dividable by 4 (color), 2 (grayscale) or 5 (color + opacity)."
"Each point in colormap must be 4, 2 or 5 tuple: (intensity, red, green, blue), (intensity, intensity) or (intensity, red, green, blue, opacity)");

for(int i = elements; i < m_data.size(); i += elements) {
if(m_data[i-elements] > m_data[i]) {
Expand All @@ -128,15 +170,20 @@ int Colormap::getSteps() const {
if(m_grayscale) {
return m_data.size()/2;
} else {
return m_data.size()/4;
if(m_hasOpacity) {
return m_data.size()/5;
} else {
return m_data.size()/4;
}
}
}

Colormap::Colormap(std::vector<float> values, bool grayscale, bool interpolate) {
Colormap::Colormap(std::vector<float> values, bool grayscale, bool interpolate, bool intensityInvariant) {
m_data = values;
m_grayscale = grayscale;
checkData();
m_interpolate = interpolate;
m_intensityInvariant = intensityInvariant;
}

Colormap Colormap::Ultrasound(bool grayscale) {
Expand Down Expand Up @@ -170,4 +217,24 @@ std::vector<float> Colormap::getData() const {
return m_data;
}

bool Colormap::hasOpacity() const {
return m_hasOpacity;
}

bool Colormap::isIntensityInvariant() const {
return m_intensityInvariant;
}

Colormap Colormap::Inferno(bool withOpacity) {
float enableOpacity = withOpacity ? 1.0f : -1.0f;
return Colormap({
{0, Color(0, 0, 0, withOpacity ? 0.0f : -1.0f)},
{0.1, Color(40.0f/255, 25.0f/255, 0, 0.05f*enableOpacity)},
{0.3, Color(80.0f/255, 35.0f/255, 0, 0.1f*enableOpacity)},
{0.4, Color(140.0f/255, 30.0f/255, 0, 0.3f*enableOpacity)},
{0.6, Color(200.0f/255, 160.0f/255, 0, 0.5f*enableOpacity)},
{0.85, Color(255.0f/255, 255.0f/255, 255.0f/255, 0.8f*enableOpacity)},
}, true, true);
}

}
21 changes: 17 additions & 4 deletions source/FAST/Algorithms/ApplyColormap/ApplyColormap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ class FAST_EXPORT Colormap {
* @param colormap
* @param interpolate
*/
explicit Colormap(const std::map<float, float>& colormap, bool interpolate = true);
explicit Colormap(const std::map<float, float>& colormap, bool interpolate = true, bool intensityInvariant = false);
/**
* @brief Creates an RGB colormap
* @param colormap
* @param interpolate
*/
explicit Colormap(const std::map<float, Color>& colormap, bool interpolate = true);
explicit Colormap(const std::map<float, Color>& colormap, bool interpolate = true, bool intensityInvariant = false);
/**
* @brief Creates a colormap directly from a list of floats.
*
Expand All @@ -41,15 +41,17 @@ class FAST_EXPORT Colormap {
* @param grayscale
* @param interpolate
*/
Colormap(std::vector<float> values, bool grayscale, bool interpolate = true);
Colormap(std::vector<float> values, bool grayscale, bool interpolate = true, bool intensityInvariant = false);
/**
* @brief Create an OpenCL buffer from the colormap data.
* @param device OpenCL device to transfer data to
* @return OpenCL buffer
*/
cl::Buffer getAsOpenCLBuffer(OpenCLDevice::pointer device) const;
bool hasOpacity() const;
bool isGrayscale() const;
bool isInterpolated() const;
bool isIntensityInvariant() const;
int getSteps() const;
/**
* @brief Get data values of the colormap as a list of floats
Expand All @@ -58,10 +60,13 @@ class FAST_EXPORT Colormap {
std::vector<float> getData() const;

static Colormap Ultrasound(bool grayscale = false);
static Colormap Inferno(bool withOpacity = false);
private:
std::vector<float> m_data;
bool m_hasOpacity = false;
bool m_grayscale;
bool m_interpolate;
bool m_intensityInvariant = false;
void checkData() const;
};

Expand All @@ -86,15 +91,23 @@ class FAST_EXPORT ApplyColormap : public ProcessObject {
* @param colormap Colormap to apply
* @return instance
*/
FAST_CONSTRUCTOR(ApplyColormap, Colormap, colormap,)
FAST_CONSTRUCTOR(ApplyColormap,
Colormap, colormap,,
float, minValue, = std::nanf(""),
float, maxValue, = std::nanf("")
);
void setColormap(Colormap colormap);
Colormap getColormap() const;
void setMinValue(float minValue);
void setMaxValue(float maxValue);
protected:
ApplyColormap();
void execute() override;
Colormap m_colormap;
bool m_bufferUpToDate = false;
cl::Buffer m_colormapBuffer;
float m_minValue;
float m_maxValue;
};

} // end namespace
28 changes: 18 additions & 10 deletions source/FAST/Data/Color.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ class FAST_EXPORT Color {
Vector3f mColorVector;
bool m_null = false;
std::string m_name;
float m_alpha = -1.0f;
public:
Color() : mColorVector(Vector3f(0, 0, 0)), m_null(true) {};

Color(float red, float green, float blue, std::string name = "") : mColorVector(Vector3f(red, green, blue)), m_null(false), m_name(name) {};
Color(float red, float green, float blue, float opacity = -1.0f, std::string name = "") : mColorVector(Vector3f(red, green, blue)), m_null(false), m_name(name), m_alpha(opacity) {};

/**
* Whether the color is set or not
Expand All @@ -24,6 +25,10 @@ class FAST_EXPORT Color {
return m_null;
}

bool hasOpacity() const {
return m_alpha >= 0.0f;
}

static Color fromString(std::string str) {
if(str.find(';') != std::string::npos) { // CHeck if on the form RED;GREEN;BLUE, where these are integers from 0 to 255
auto parts = split(str, ";");
Expand Down Expand Up @@ -67,6 +72,9 @@ class FAST_EXPORT Color {
float getBlueValue() const {
return mColorVector.z();
}
float getOpacity() const {
return m_alpha;
}
std::string getName() const {
if(m_name.empty()) {
Vector3i intVector = (mColorVector*255).cast<int>();
Expand All @@ -84,31 +92,31 @@ class FAST_EXPORT Color {
return Color();
}
static Color Red() {
return Color(1,0,0, "red");
return Color(1,0,0, -1, "red");
}
static Color Green() {
return Color(0,1,0, "green");
return Color(0,1,0, -1, "green");
}
static Color Blue() {
return Color(0,0,1, "blue");
return Color(0,0,1, -1, "blue");
}
static Color White() {
return Color(1,1,1, "white");
return Color(1,1,1, -1, "white");
}
static Color Black() {
return Color(0,0,0, "black");
return Color(0,0,0, -1, "black");
}
static Color Yellow() {
return Color(1, 1, 0, "yellow");
return Color(1, 1, 0, -1, "yellow");
}
static Color Magenta() {
return Color(1, 0, 1, "magenta");
return Color(1, 0, 1, -1, "magenta");
}
static Color Cyan() {
return Color(0, 1, 1, "cyan");
return Color(0, 1, 1, -1, "cyan");
}
static Color Brown() {
return Color(1, 0.6, 0.2, "brown");
return Color(1, 0.6, 0.2, -1, "brown");
}

};
Expand Down
Loading

0 comments on commit cbfe060

Please sign in to comment.