Need help understanding a color-mapping algorithm - c

So I'm working on a project that involves taking pre-existing skeleton code of an application that simulates "fluid flow and visualization" and applying different visualization techniques on it.
The first step of the project is to apply different color-mapping techniques on three different data-sets which are as follows: fluid density (rho), fluid velocity magnitude (||v||) and the force field magnitude ||f||.
The skeleton code provided already has an example that I can study to be able to determine how best to design and implement different color-maps such as red-to-white or blue-to-yellow or what have you.
The snippet of code I'm trying to understand is the following:
//rainbow: Implements a color palette, mapping the scalar 'value' to a rainbow color RGB
void rainbow(float value,float* R,float* G,float* B)
{
const float dx=0.8;
if (value<0) value=0; if (value>1) value=1;
value = (6-2*dx)*value+dx;
*R = max(0.0,(3-fabs(value-4)-fabs(value-5))/2);
*G = max(0.0,(4-fabs(value-2)-fabs(value-4))/2);
*B = max(0.0,(3-fabs(value-1)-fabs(value-2))/2);
}
The float valuebeing passed by the first parameter is, as far as I can tell, the fluid density. I've determined this by studying these two snippets.
//set_colormap: Sets three different types of colormaps
void set_colormap(float vy)
{
float R,G,B;
if (scalar_col==COLOR_BLACKWHITE)
R = G = B = vy;
else if (scalar_col==COLOR_RAINBOW)
rainbow(vy,&R,&G,&B);
else if (scalar_col==COLOR_BANDS)
{
const int NLEVELS = 7;
vy *= NLEVELS; vy = (int)(vy); vy/= NLEVELS;
rainbow(vy,&R,&G,&B);
}
glColor3f(R,G,B);
}
and
set_colormap(rho[idx0]);
glVertex2f(px0, py0);
set_colormap(rho[idx1]);
glVertex2f(px1, py1);
set_colormap(rho[idx2]);
glVertex2f(px2, py2);
set_colormap(rho[idx0]);
glVertex2f(px0, py0);
set_colormap(rho[idx2]);
glVertex2f(px2, py2);
set_colormap(rho[idx3]);
glVertex2f(px3, py3);
With all of this said, could somebody please explain to me how the first method works?
Here's the output when the method is invoked by the user and matter is injected into the window by means of using the cursor:
Whereas otherwise it would look like this (gray-scale):

I suspect that this is a variation of HSV to RGB.
The idea is that you can map your fluid density (on a linear scale) to the Hue parameter of a color in HSV format. Saturation and Value can just maintain constant value of 1. Normally Hue starts and ends at red, so you also want to shift your Hue values into the [red, blue] range. This will give you a "heatmap" of colors in HSV format depending on the fluid density, which you then have to map to RGB in the shader.
Because some of your values can be kept constant and because you don't care about any of the intermediate results, the algorithm that transforms fluid density to RGB can be simplified to the snippet above.

I'm not sure which part of the function you don't understand, so let me explain this line by line:
void rainbow(float value,float* R,float* G,float* B){
}
This part is probably clear to you - the function takes in a single density/color value and outputs a rainbow color in rgb space.
const float dx=0.8;
Next, the constant dx is initialised. I'm not sure what the name "dx" stands for, but looks like it's later used to determine which part of the color spectrum is used.
if (value<0) value=0; if (value>1) value=1;
This clamps the input to a value between 0 and 1.
value = (6-2*dx)*value+dx;
This maps the input to a value between dx and 6-dx.
*R = max(0.0,(3-fabs(value-4)-fabs(value-5))/2);
This is probably the most complicated part. If value is smaller than 4, this simplifies to max(0.0,(2*value-6)/2) or max(0.0,value-3). This means that if value is less than 3, the red output will be 0, and if it is between 3 and 4, it will be value-3.
If value is between 4 and 5, this line instead simplifies to max(0.0,(3-(value-4)-(5-value))/2) which is equal to 1. So if value is between 4 and 5, the red output will be 1.
Lastly, if value is greater than 5, this line simplifies to max(0.0,(12-2*value)/2) or just 6-value.
So the output R is 1 when value is between 4 and 5, 0 when value is smaller than 3, and something in between otherwise. The calculations for the green and blue output or pretty much the same, just with tweaked value; green is brightest for values between 2 and 4, and blue is brightest for values between 1 and 2. This way the output forms a smooth rainbow color spectrum.

Related

Led bar indicator flickering within adjacent values and how to avoid this (embedded-C)

I am designing a measurement instrument that has a visible user output on a 30-LEDs bar. The program logic acts in this fashion (pseudo-code)
while(1)
{
(1)Sensor_Read();
(2)Transform_counts_into_leds();
(3)Send_to_bar();
{
The relevant function (2) is a simple algorithm that transforms the counts from the I2C sensor to a value serially sent to shift-registers that control the single leds.
The variable sent to function (3) is simply the number of LEDs that have to stay on (0 for al LEDs off, 30 for all LEDs on)
uint8_t Transform_counts_into_leds(uint16_t counts)
{
float on_leds;
on_leds = (uint8_t)(counts * 0.134); /*0.134 is a dummy value*/
return on_leds;
}
using this program logic when counts value is on the threshold between two LEDs, the next led flickers
I think this is a bad user experience for my device and I want the LEDs, once lit, to stay stable in a small range of values.
QUESTION: How a solution to this problem could be implemented in my project?
Hysteresis is useful for a number of applications, but I would suggest not appropriate in this instance. The problem is that if the level genuinely falls from say 8 to 7 for example you would not see any change until at least one sample at 6 and it would jump to 6 and there would have to be a sample of 8 before it went back to 7.
A more appropriate solution in the case is a moving average, although it is simpler and more useful to use a moving sum and use the higher resolution that gives. For example a moving-sum of 16 effectively adds (almost) 4 bits of resolution, making a 8 bit sensor effectively 12 bit - at the cost of bandwidth of course; you don't get something for nothing. In this case lower bandwidth (i.e. less responsive to higher frequencies is exactly what you need)
Moving sum:
#define BUFFER_LEN 16 ;
#define SUM_MAX (255 * BUFFER_LEN)
#define LED_MAX 30
uint8_t buffer[BUFFER_LEN] = {0} ;
int index = 0 ;
uint16_t sum = 0 ;
for(;;)
{
uint8_t sample = Sensor_Read() ;
// Maintain sum of buffered values by
// subtracting oldest buffered value and
// adding the new sample
sum -= buffer[index] ;
sum += sample ;
// Replace oldest sample with new sample
// and increment index to next oldest sample
buffer[index] = sample ;
index = (index + 1) % BUFFER_LEN ;
// Transform to LED bar level
int led_level = (LED_MAX * sum) / SUM_MAX ;
// Show level
setLedBar( led_level ) ;
}
The underlying problem -- displaying sensor data in a human-friendly way -- is very interesting. Here's my approach in pseudocode:
Loop:
Read sensor
If sensor outside valid range:
Enable warning LED
Sleep in a low-power state for a while
Restart loop
Else:
Disable warning LED
Filter sensor value
Compute display value from sensor value with extra precision:
If new display value differs sufficiently from current value:
Update current displayed value
Update display with scaled-down display value
Filtering deals with noise in the measurements. Filtering smoothes out any sudden changes in the measurement, removing sudden spikes. It is like erosion, turning sharp and jagged mountains into rolling fells and hills.
Hysteresis hides small changes, but does not otherwise filter the results. Hysteresis won't affect noisy or jagged data, it only hides small changes.
Thus, the two are separate, but complementary methods, that affect the readout in different ways.
Below, I shall describe two different filters, and two variants of simple hysteresis implementation suitable for numeric and bar graph displays.
If possible, I'd recommend you write some scripts or test programs that output the input data and the variously filtered output data, and plot it in your favourite plotting program (mine is Gnuplot). Or, better yet, experiment! Nothing beats practical experiments for human interface stuff (at least if you use existing suggestions and known theory as your basis, and leap forward from there).
Moving average:
You create an array of N sensor readings, updating them in a round-robin fashion, and using their average as the current reading. This produces very nice (as in human-friendly, intuitive) results, as only the N latest sensor readings affect the average.
When the application is first started, you should copy the very first reading into all N entries in the averaging array. For example:
#define SENSOR_READINGS 32
int sensor_reading[SENSOR_READINGS];
int sensor_reading_index;
void sensor_init(const int reading)
{
int i;
for (i = 0; i < SENSOR_READINGS; i++)
sensor_reading[i] = reading;
sensor_reading_index = 0;
}
int sensor_update(const int reading)
{
int i, sum;
sensor_reading_index = (sensor_reading_index + 1) % SENSOR_READINGS;
sensor_reading[sensor_reading_index] = reading;
sum = sensor_reading[0];
for (i = 1; i < SENSOR_READINGS; i++)
sum += sensor_reading[i];
return sum / SENSOR_READINGS;
}
At start-up, you call sensor_init() with the very first valid sensor reading, and sensor_update() with the following sensor readings. The sensor_update() will return the filtered result.
The above works best when the sensor is regularly polled, and SENSOR_READINGS can be chosen large enough to properly filter out any unwanted noise in the sensor readings. Of course, the array requires RAM, which may be on short supply in some microcontrollers.
Exponential smoothing:
When there is not enough RAM to use a moving average to filter data, an exponential smoothing filter is often applied.
The idea is that we keep an average value, and recalculate the average using each new sensor reading using (A * average + B * reading) / (A + B). The effect of each sensor reading on the average decays exponentially: the weight of the most current sensor reading is always B/(A+B), the weight of the previous one is A*B/(A+B)^2, the weight of the one before that is A^2*B/(A+B)^3, and so on (^ indicating exponentiation); the weight of the n'th sensor reading in the past (with current one being n=0) is A^n*B/(A+B)^(n+1).
The code corresponding to the previous filter is now
#define SENSOR_AVERAGE_WEIGHT 31
#define SENSOR_CURRENT_WEIGHT 1
int sensor_reading;
void sensor_init(const int reading)
{
sensor_reading = reading;
}
int sensor_update(const int reading)
return sensor_reading = (sensor_reading * SENSOR_AVERAGE_WEIGHT +
reading * SENSOR_CURRENT_WEIGHT) /
(SENSOR_AVERAGE_WEIGHT + SENSOR_CURRENT_WEIGHT);
}
Note that if you choose the weights so that their sum is a power of two, most compilers optimize the division into a simple bit shift.
Applying hysteresis:
(This section, including example code, edited on 2016-12-22 for clarity.)
Proper hysteresis support involves having the displayed value in higher precision than is used for output. Otherwise, your output value with hysteresis applied will never change by a single unit, which I would consider a bad design in an user interface. (I'd much prefer a value to flicker between two consecutive values every few seconds, to be honest -- and that's what I see in e.g. the weather stations I like best with good temperature sensors.)
There are two typical variants in how hysteresis is applied to readouts: fixed, and dynamic. Fixed hysteresis means that the displayed value is updated whenever the value differs by a fixed limit; dynamic means the limits are set dynamically. (The dynamic hysteresis is much rarer, but it may be very useful when coupled with the moving average; one can use the standard deviation (or error bars) to set the hysteresis limits, or set asymmetric limits depending on whether the new value is smaller or greater than the previous one.)
The fixed hysteresis is very simple to implement. First, because we need to apply the hysteresis to a higher-precision value than the output, we choose a suitable multiplier. That is, display_value = value / DISPLAY_MULTIPLIER, where value is the possibly filtered sensor value, and display_value is the integer value displayed (number of bars lit, for example).
Note that below, display_value and the value returned by the functions, refer to the integer value displayed, for example the number of lit LED bars. value is the (possibly filtered) sensor reading, and saved_value containing the sensor reading that is currently displayed.
#define DISPLAY_HYSTERESIS 10
#define DISPLAY_MULTIPLIER 32
int saved_value;
void display_init(const int value)
{
saved_value = value;
}
int display_update(const int value)
{
const int delta = value - saved_value;
if (delta < -DISPLAY_HYSTERESIS ||
delta > DISPLAY_HYSTERESIS)
saved_value = value;
return saved_value / DISPLAY_MULTIPLIER;
}
The delta is just the difference between the new sensor value, and the sensor value corresponding to the currently displayed value.
The effective hysteresis, in units of displayed value, is DISPLAY_HYSTERESIS/DISPLAY_MULTIPLIER = 10/32 = 0.3125 here. It means that the displayed value can be updated three times before a visible change is seen (if e.g. slowly decreasing or increasing; more if the value is just fluctuating, of course). This eliminates rapid flickering between two visible values (when the value is in the middle of two displayed values), but ensures the error of the reading is less than half display units (on average; half plus effective hysteresis in the worst case).
In a real life application, you usually use a more complete form return (saved_value * DISPLAY_SCALE + DISPLAY_OFFSET) / DISPLAY_MULTIPLIER, which scales the filtered sensor value by DISPLAY_SCALE/DISPLAY_MULTIPLIER and moves the zero point by DISPLAY_OFFSET/DISPLAY_MULTIPLIER, both evaluated at 1.0/DISPLAY_MULTIPLIER precision, but only using integer operations. However, for simplicity, I'll just assume that to derive the display value value, say the number of lit LED bars, you just divide the sensor value by DISPLAY_MULTIPLIER. In either case, the hysteresis is DISPLAY_HYSTERESIS/DISPLAY_MULTIPLIER of the output unit. Ratios of about 0.1 to 0.5 work fine; and the below test values, 10 and 32, yields 0.3125, which is about midway of the range of ratios that I believe work best.
Dynamic hysteresis is very similar to above:
#define DISPLAY_MULTIPLIER 32
int saved_value_below;
int saved_value;
int saved_value_above;
void display_init(const int value, const int below, const int above)
{
saved_value_below = below;
saved_value = value;
saved_value_above = above;
}
int display_update(const int value, const int below, const int above)
{
if (value < saved_value - saved_value_below ||
value > saved_value + saved_value_above) {
saved_value_below = below;
saved_value = value;
saved_value_above = above;
}
return saved_value / DISPLAY_MULTIPLIER;
}
Note that if DISPLAY_HYSTERESIS*2 <= DISPLAY_MULTIPLIER, the displayed value is always within a display unit of the actual (filtered) sensor value. In other words, hysteresis can easily deal with flickering, but it does not need to add much error to the displayed value.
In many practical cases the best amount of hysteresis applied depends on the amount of short-term variations in the sensor samples. This includes not only noise, but also the types of signals that are to be measured. A hysteresis of just 0.3 (relative to the output unit) is sufficient to completely eliminate the flicker when sensor readings flip the filtered sensor value between two consecutive integers that map to different integer ouputs, as it ensures that the filtered sensor value must change by at least 0.3 (in output display units) before it effects a change in the display.
The maximum error with hysteresis is half display units plus the current hysteresis. The half unit is the minimum error possible (since consecutive units are one unit apart, so when the true value is in the middle, either value shown is correct to within half a unit). With dynamic hysteresis, if you always start with some fixed hysteresis value when a reading changes enough, but when the reading is within the hysteresis, you instead just decrease the hysteresis (if greater than zero). This approach leads to a changing sensor value being tracked correctly (maximum error being half an unit plus the initial hysteresis), but a relatively static value being displayed as accurately as possible (at half an unit maximum error). I don't show an example of this, because it adds another tunable (how the hysteresis decays towards zero), and requires that you verify (calibrate) the sensor (including any filtering) first; otherwise it's like polishing a turd: possible, but not useful.
Also note that if you have 30 bars in the display, you actually have 31 states (zero bars, one bar, .., 30 bars), and thus the proper range for the value is 0 to 31*DISPLAY_MULTIPLIER - 1, inclusive.

Basic Image display of 2D array

As part of a project I am doing in C, it would be helpful to produce a visual representation of the data I am working with.
My question is: for some dynamically declared array:
double *foo;
foo = (double *)malloc(sizeof(double)*x*y);
for (i=0;i<x;i++)
for (j=0;j<y;j++)
foo[i*y+j] = someValue;
If the data has some max/min value (lets say max = 100.0 and min = 5.0), what is the simplest way to represent this data graphically?
I would like some method of displaying each number as a gradient of some color, for example, the max would be black and the min would be white.
An output of a Bitmap image for example would be a good as I need to do this for multiple arrays.
thanks.

Learning openCV : Use the pointer element to access cvptr2D to point to the middle 'green' channel. Draw the rectangle between 20,5 and 40,20

In the book learning opencv there's a question in chapter 3 :
Create a two dimensional matrix with three channels of type byte with data size 100-by-100 and initialize all the values to 0.
Use the pointer element to access cvptr2D to point to the middle 'green' channel.Draw the rectangle between 20,5 and 40,20.
I've managed to do the first part, but I can't get my head around second part. Here's what I've done so far :
/*
Create a two dimensional matrix with three channels of type byte with data size 100- by-100 and initialize all the values to 0.
Use the pointer element to access cvptr2D to point to the middle 'green' channel.Draw `enter code here`the rectangle between 20,5 and 40,20.
*/
void ex10_question3(){
CvMat* m = cvCreateMat(100,100,CV_8UC3);
CvSetZero(m); // initialize to 0.
uchar* ptr = cvPtr2D(m,0,1); // if RGB, then start from first RGB pair, Green.
cvAdd(m,r);
cvRect r(20,5,20,15);
//cvptr2d returns a pointer to a particular row element.
}
I was considering adding both the rect and matrix, but obviously that won't work because a rect is just co-ordinates, and width/height. I'm unfamiliar with cvPtr2D(). How can I visualise what the exercise wants me to do and can anyone give me a hint in the right direction? The solution must be in C.
From my understanding with interleaved RGB channels the 2nd channel will always be the channel of interest. (array index : 1,4,6..)
So that's the direction where the winds blow from...
First of all, the problem is the C API. This API is still present for legacy reasons, but will soon become obsolete. If you are serious about OpenCV please refer to C++ API. The official tutorials are great source of information.
To further target your question, this would be implementation of your question in C++.
cv::Mat canvas = cv::Mat::zero(100,100, CV_8UC3); // create matrix of bytes, filled with 0
std::vector<cv::Mat> channels(3); // prepare storage for splitting
split(canvas, channels); // split matrix to single channels
rectangle(channels[1], ...); // draw rectangle [I don't remember exact params]
merge(channels, canvas); // merge the channels together
If you only need to draw green rectangle, it's actually much easier:
cv::Mat canvas = cv::Mat::zero(100,100, CV_8UC3); // create matrix of bytes, filled with 0
rectangle(canvas, ..., Scalar(0,255,0)); // draw green rectangle
Edit:
To find out how to access single pixels in image using C++ API please refer to this answer:
https://stackoverflow.com/a/8139210/892914
Try this code:
cout<<"Chapter 3. Task 3."<<'\n';
CvMat *Mat=cvCreateMat(100, 100, CV_8UC3);
cvZero(Mat);
for(int J=5; J<=20; J++)
for(int I=20; I<40; I++)
(*(cvPtr2D(Mat, J, I)+1))=(uchar)(255);
cvNamedWindow("Chapter 3. Task 3", CV_WINDOW_FREERATIO);
cvShowImage("Chapter 3. Task 3", Mat);
cvWaitKey(0);
cvReleaseMat(&Mat);
cvDestroyAllWindows;

HVS color space in Open CV

I am going to detect a yellow color object when i open up my System CAM using Open CV programming, i got some help from the tutorial Object Recognition in Open CV but i am not clear about this line of code, what it does, i don't know. please elaborate me on the below line of code, which i am using.
cvInRangeS(imgHSV, cvScalar(20, 100, 100), cvScalar(30, 255, 255), imgThreshed);
other part of program:
CvMoments *moments = (CvMoments*)malloc(sizeof(CvMoments));
cvMoments(imgYellowThresh, moments, 1);
// The actual moment values
double moment10 = cvGetSpatialMoment(moments, 1, 0);
double moment01 = cvGetSpatialMoment(moments, 0, 1);
double area = cvGetCentralMoment(moments, 0, 0);
What about reading documentation?
inRange:
Checks if array elements lie between the elements of two other arrays.
And actually that article contains clear explanation:
And the two cvScalars represent the lower and upper bound of values
that are yellowish in colour.
About second code. From that calculations author finds center of object and its square. Quote from article:
You first allocate memory to the moments structure, and then you
calculate the various moments. And then using the moments structure,
you calculate the two first order moments (moment10 and moment01) and
the zeroth order moment (area).
Dividing moment10 by area gives the X coordinate of the yellow ball,
and similarly, dividing moment01 by area gives the Y coordinate.

RGB palette designated for ordered values

I have a function f(x,y), mostly monotonic, which produces some values in the range {0.0 .. 100.0}. I would like to draw them using different colors as a 2D picture, where (x,y) are coordinates and where distinctive colors stand for distinctive values of the function. The problem is following: I don't know how to map the values of this function to RGB color space preserving the order (visibly). I have found that smth. like:
R = f(x,y) * 10.0f;
G = f(x,y) * 20.0f;
B = f(x,y) * 30.0f;
color = B<<16|G<<8|R; //#low-endian
works fine, but the resulting picture is too dark. If I increase these constants, it makes things not better, because at some moment a color component will be greater than 0xFF, so it will overflow (one color component should be in the range {0 .. 0xFF}.
Do you have any idea how to map values from {0.0 .. 100.0} to
RGB=[{0 .. 0xFF}<<16|{0 .. 0xFF}<<8|{0 .. 0xFF}] so that the resulting RGB values are visibly Ok?
PS: maybe you know, where to find more info about related theory online? I remember only Comp.Graphics by Foley/Van Dam, but I don't have this book.
UPDATE: I am looking for how to generate a chroma palette like one on the right:
You could just try clamping the values to a maximum of 255 (0xff).
R = min((int)(f(x,y) * 10.0f), 0xff);
G = min((int)(f(x,y) * 20.0f), 0xff);
B = min((int)(f(x,y) * 30.0f), 0xff);
Edit: There are a lot of different ways to convert to colors automatically, but you might find that none of them generates the exact progression you're looking for. Since you already have a picture of an acceptable palette, one method would be to create a lookup table of 256 colors.
#define RGB(R,G,B) (B<<16|G<<8|R)
int palette[256] = { RGB(0,0,0), RGB(0,0,128), ... };
int greyscale = (int)(f(x,y) * 2.559999);
assert(greyscale >= 0 && greyscale <= 255);
int rgb = palette[greyscale];
If the lookup table is too much trouble, you could also break the greyscale range into different subranges and do a linear interpolation between the endpoints of each range.
int interpolate(int from, int to, double ratio)
{
return from + (int)((to - from) * ratio);
}
if (greyscale <= 48)
{
R = 0;
G = 0;
B = interpolate(0, 255, greyscale/48.0);
}
else if (greyscale <= 96)
{
R = 0;
G = interpolate(0, 255, (greyscale-48)/48.0);
B = interpolate(255, 0, (greyscale-48)/48.0);
}
else if ...
Actually you could use the YUV color model since it is kind of based on two coordinates, i.e. U and V.
This seems more appropriate for the task.
And YUV -> RGB conversion is pretty straightforward.
You can convert RGB to HSL and increase the brightness/contrast. You can find forumlas for the conversions and other useful info on this page: http://lodev.org/cgtutor/color.html
Use a different colour-space that means you can easily assign coordinates to different colours.
YUV or YCrCb might suit as the UV or CrCb dimensions could be treated as "at right-angles"
Or HSL/HSV if one of your dimensions wraps around like the hue does.
I believe you want a heatmap based on the ordered values. Then you have different types of heatmaps. Solution here: http://www.andrewnoske.com/wiki/Code_-_heatmaps_and_color_gradients

Resources