I am building a program in objective C/C++ and openCV. I am pretty skilled in Objective C but new to C++.
I am building custom RGB2HSV algorithm. My algorithm is slightly different from the openCV library cvtColor(in, out, CV_RGB2HSV).
The one I try to translate form Matlab to opencV/C++ produces so clear HSV image that no additional filtering is needed before further processing. Code below – Matlab code is self-explanatory.
I try to translate it to C++/openCV function out of it but I hit the wall trying to access pixel values of the image. I am new to C++.
I read a lot on the ways how to access Mat structure but usually I obtain either bunch of letters in a place of zeros or a number typically something like this “\202 k g”. When I try to do any multiplication operations on the say \202 the result has nothing to do with math.
Please help me to properly access the pixel values. Also in current version using uchar won’t work because some values are outside 0-255 range.
The algorithm is not mine. I cannot even point the source but it gives clearly better results than stock RGB2HSV.
Also the algorithm below is for one pixel. It needs to be applied each pixel in the image so in final version it need to wrapped with for { for {}} loops.
I also wish to share this method with community so everyone can benefit from it and saving on pre-filtering.
Please help me translate it to C++ / openCV. If possible with the best practices speed wise. Or at least how to clearly access the pixel value so it is workable with range of mathematical equations. Thanks in advance.
function[H, S, V] = rgb2hsvPixel(R,G,B)
% Algorithm:
% In case of 8-bit and 16-bit images, `R`, `G`, and `B` are converted to the
% floating-point format and scaled to fit the 0 to 1 range.
%
% V = max(R,G,B)
% S = / (V - min(R,G,B)) / V if V != 0
% \ 0 otherwise
% / 60*(G-B) / (V - min(R,G,B)) if V=R
% H = | 120 + 60*(B-R) / (V - min(R,G,B)) if V=G
% \ 240 + 60*(R-G) / (V - min(R,G,B)) if V=B
%
% If `H<0` then `H=H+360`. On output `0<=V<=1`, `0<=S<=1`, `0<=H<=360`.
red = (double(R)-16)*255/224; % \
green = (double(G)-16)*255/224; % }- R,G,B (0 <-> 255) -> (-18.2143 <-> 272.0759)
blue = (min(double(B)*2,240)-16)*255/224; % /
minV = min(red,min(green,blue));
value = max(red,max(green,blue));
delta = value - minV;
if(value~=0)
sat = (delta*255) / value;% s
if (delta ~= 0)
if( red == value )
hue = 60*( green - blue ) / delta; % between yellow & magenta
elseif( green == value )
hue = 120 + 60*( blue - red ) / delta; % between cyan & yellow
else
hue = 240 + 60*( red - green ) / delta; % between magenta & cyan
end
if( hue < 0 )
hue = hue + 360;
end
else
hue = 0;
sat = 0;
end
else
% r = g = b = 0
sat = 0;
hue = 0;
end
H = max(min(floor(((hue*255)/360)),255),0);
S = max(min(floor(sat),255),0);
V = max(min(floor(value),255),0);
end
To access the value of a pixel in a 3-channel, 8-bit precision image (type CV_8UC3) you have to do it like this:
cv::Mat image;
cv::Vec3b BGR = image.at<cv::Vec3b>(i,j);
If, as you say, 8-bit precision and range are not enough, you can declare a cv::Mat of type CV_32F to store floating point 32-bit numbers.
cv::Mat image(height, width, CV_32FC3);
//fill your image with data
for(int i = 0; i < image.rows; i++) {
for(int j = 0; j < image.cols; j++) {
cv::Vec3f BGR = image.at<cv::Vec3f>(i,j)
//process your pixel
cv::Vec3f HSV; //your calculated HSV values
image.at<cv::Vec3f>(i,j) = HSV;
}
}
Be aware that OpenCV stores rgb values in the BGR order and not RGB. Take a look at OpenCV docs to learn more about it.
If you are concerned by performance and fairly comfortable with pixel indexes, you can use directly the Mat ptr.
For example:
cv::Mat img = cv::Mat::zeros(4, 8, CV_8UC3);
uchar *ptr_row_img;
int cpt = 0;
for(int i = 0; i < img.rows; i++) {
ptr_row_img = img.ptr<uchar>(i);
for(int j = 0; j < img.cols; j++) {
for(int c = 0; c < img.channels(); c++, cpt++, ++ptr_row_img) {
*ptr_row_img = cpt;
}
}
}
std::cout << "img=\n" << img << std::endl;
The previous code should print:
img= [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23; 24, 25, 26,
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47; 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68,
69, 70, 71; 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82,
83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95]
The at access should be enough for most of the cases and is much more readable / less likely to make a mistake than using the ptr access.
References:
How to scan images, lookup tables and time measurement with OpenCV
C++: OpenCV: fast pixel iteration
Thanks everybody for help.
Thanks to your hints I constructed the custom rgb2hsv function C++/openCV.
From the top left respectively, edges after bgr->gray->edges, bgr->HSV->edges, bgr->customHSV->edges
Below each of them corresponding settings of the filters to achieve approximately the same clear results. The bigger the radius of a filter the more complex and time consuming computations.
It produces clearer edges in next steps of image processing.
It can be tweaked further experimenting with parameters in r g b channels:
red = (red-16)*1.1384; //255/244=1.1384
here 16 – the bigger the number the clearer V becomes
255/244 – also affect the outcome extending it beyond ranges 0-255, later to be clipped.
This numbers here seem to be golden ratio but anyone can adjust for specific needs.
With this function translating BGR to RGB can be avoided by directly connecting colors to proper channels in raw image.
Probably it is a little clumsy performance wise. In my case it serves in first step of color balance and histogram adjustment so speed is not that critical.
To use in constant processing video stream it need speed optimization, I think by using pointers and reducing loop complexity. Optimization is not exactly my cup of tea. So if someone helped to optimize it for the community that would be great.
Here it is ready to use:
Mat bgr2hsvCustom ( Mat& image )
{
//smallParam = 16;
for(int x = 0; x < image.rows; x++)
{
for(int y = 0; y<image.cols; y++)
{
//assigning vector to individual float BGR values
float blue = image.at<cv::Vec3b>(x,y)[0];
float green = image.at<cv::Vec3b>(x,y)[1];
float red = image.at<cv::Vec3b>(x,y)[2];
float sat, hue, minValue, maxValue, delta;
float const ang0 = 0; // func min and max don't accept varaible and number
float const ang240 = 240;
float const ang255 = 255;
red = (red-16)*1.1384; //255/244
green = (green-16)*1.1384;
blue = (min(blue*2,ang240)-16)*1.1384;
minValue = min(red,min(green,blue));
maxValue = max(red,max(green,blue));
delta = maxValue - minValue;
if (maxValue != 0)
{
sat = (delta*255) / maxValue;
if ( delta != 0)
{
if (red == maxValue){
hue = 60*(green - blue)/delta;
}
else if( green == maxValue ) {
hue = 120 + 60*( blue - red )/delta;
}
else{
hue = 240 + 60*( red - green )/delta;
}
if( hue < 0 ){
hue = hue + 360;
}
}
else{
sat = 0;
hue = 0;
}
}
else{
hue = 0;
sat = 0;
}
image.at<cv::Vec3b>(x,y)[0] = max(min(floor(maxValue),ang255),ang0); //V
image.at<cv::Vec3b>(x,y)[1] = max(min(floor(sat),ang255),ang0); //S
image.at<cv::Vec3b>(x,y)[2] = max(min(floor(((hue*255)/360)),ang255),ang0); //H
}
}
return image;
}
Related
I am now working on a motion control system. I want to make the motion control interface more flexible, so I want to provide an api that can specifiy the controlled axis.
It just like.
void moveTo(/*data*/, Axis which);
Axis parameter is used to define how the motion data should be treated as and the internal implement will check the data according the requested axis.
So i want to define a operation rules on Axis parameters.
I tried the bit flags, defined as follow.
enum class Axis : std::uint_fast16_t {
X = 1l << 0,
Y = 1l << 1,
Z = 1l << 2
};
// operator | and & and so on are also defined.
It can be used as follow.
const auto which = Axis::X | Axis::Y;
But what i want is that, Axis::X | Axis::Y and Axis::Y | Axis::X should be different, but now they have same value. There is no way to distinguish them inside the function.
How should I define such a bit mask that appears to be related to "order of operations"?
It seems to do this by listing all allowed combined states in a single enumeration. But I still want to ask if there is a more elegant and clear solution.
Or any suggestion for a better implementation of such functionality?
I hope the final usage is still as simple as the bit mask, I don't mind if I do some really complicated work behind the scenes, I don't mind using any 3rd party library.
Given that you just have 3 axes and a limited amount of combinations, I'd suggest just defining them all.
enum class Axis : std::uint_fast16_t {
X = 0,
Y = 1,
Z = 2,
XX = 3,
YX = 4,
ZX = 5,
XY = 6,
YY = 7,
ZY = 8,
XZ = 9,
YZ = 10,
ZZ = 11,
XXX = 12,
YXX = 13,
ZXX = 14,
XYX = 15,
YYX = 16,
ZYX = 17,
XZX = 18,
YZX = 19,
ZZX = 20,
XXY = 21,
YXY = 22,
ZXY = 23,
XYY = 24,
YYY = 25,
ZYY = 26,
XZY = 27,
YZY = 28,
ZZY = 29,
XXZ = 30,
YXZ = 31,
ZXZ = 32,
XYZ = 33,
YYZ = 34,
ZYZ = 35,
XZZ = 36,
YZZ = 37,
ZZZ = 38,
};
If you want to do it programmatically, what you can do is to make the flag by shifting the value by 2 every time you add a new flag, as each value is represented by 2 bits.
#include <type_traits>
#include <array>
#include <iostream>
#include <bitset>
enum class Axis : std::uint_fast16_t {
X = 0b01,
Y = 0b10,
Z = 0b11,
};
Axis append_flag(Axis current, Axis to_append) {
std::uint_fast16_t flag = static_cast<std::uint_fast16_t>(current);
flag = (flag << 2) | static_cast<std::uint_fast16_t>(to_append);
return static_cast<Axis>(flag);
}
template <class ... T>
Axis make_flag(T ... axes) {
std::array values = { axes... };
auto flag = values[0];
for (std::size_t i = 1; i < values.size(); ++i) {
flag = append_flag(flag, values[i]);
}
return flag;
}
int main(int argc, const char* argv[]) {
auto result = make_flag(Axis::X, Axis::Y, Axis::Z);
auto value = static_cast<std::size_t>(result);
std::cout << value << " (" << std::bitset<16>(value) << ")" << std::endl;
return 0;
}
I've been trying for the past hour to get this binary search algorithm to work and by using an example of the algorithm as explained on khan academy, I still can't get it to work, it should output a number but nothing happens. The example on khan academy is like this:
Let min = 0 and max = n-1.
If max < min, then stop: target is not present in array. Return -1.
Compute guess as the average of max and min, rounded down (so that it is an integer).
If array[guess] equals target, then stop. You found it! Return guess.
If the guess was too low, that is, array[guess] < target, then set min = guess + 1.
Otherwise, the guess was too high. Set max = guess - 1.
Go back to step 2.
And the code I wrote according to the steps is:
#include <iostream>
int main() {
int arr[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 };
int min = 0;
int max = 24;
int guess;
int targetValue = 73;
while (max > min) {
guess = ((max + min) / 2);
if (arr[guess] == targetValue) {
std::cout << guess;
break;
}
else if (arr[guess] < targetValue) {
min = guess + 1;
}
else {
max = guess - 1;
}
}
return 0;
}
The binary search algorithm states
If L > R, the search terminates as unsuccessful.
In your implementation however, you terminate the search on the condition L >= R. In the case of L == R, the algorithm should do one more iteration, because it did not consider this position in the list yet.
In your case of target value 73, when the algorithm reaches the position of the target, 20, L == R. Your implementation terminates one step too early to recognize the target.
try this:
(max > min) to (max >= min)
what I'm trying to do is fill an array with an rainbow gradient. The array should hold 256 entries an filled with the hex value of colors.
like:
array[0] = 0xFF0000 //red
...
array[85] = 0xFFA200 //orange
...
array[170] = 0x00AF0F //green
...
array[255] = 0xAE00FF //purple
because I do not want to assign all 256 colors "by hand" to the array I'm looking for an dynamicly approach for that. It's not necessarily need to be the ranbow displayed above. The picture is just for demonstration purpose.
Any suggestions how to do things like this in an (preferably) short code snipit avoiding a couple of nested for loops?
We want to go from:
red -> yellow
yellow -> green
green -> cyan
cyan -> blue
blue -> magenta
magenta -> red
In each pass, one of the red, green, blue components is always 0, the second is 255, and the third is increasing or decreasing between 0 and 255.
In other words:
{255, 0, 0} -> {255, 255, 0} grn increases to 255
{255, 255, 0} -> { 0, 255, 0} red decreases to 0
{0 , 255, 0} -> { 0, 255, 255} blu increases to 255
{0 , 255, 255} -> { 0, 0, 255} grn decreases to 0
{0 , 0, 255} -> {255, 0, 255} red increases to 255
{255, 0, 255} -> {255, 0, 0} blu decreases to 0
This produces 256 * 6 colors, we may not want all of those colors, so it has to be normalized. This can be done with following code:
//input: ratio is between 0.0 to 1.0
//output: rgb color
uint32_t rgb(double ratio)
{
//we want to normalize ratio so that it fits in to 6 regions
//where each region is 256 units long
int normalized = int(ratio * 256 * 6);
//find the region for this position
int region = normalized / 256;
//find the distance to the start of the closest region
int x = normalized % 256;
uint8_t r = 0, g = 0, b = 0;
switch (region)
{
case 0: r = 255; g = 0; b = 0; g += x; break;
case 1: r = 255; g = 255; b = 0; r -= x; break;
case 2: r = 0; g = 255; b = 0; b += x; break;
case 3: r = 0; g = 255; b = 255; g -= x; break;
case 4: r = 0; g = 0; b = 255; r += x; break;
case 5: r = 255; g = 0; b = 255; b -= x; break;
}
return r + (g << 8) + (b << 16);
}
Usage:
double range = 500.0;
for (double i = 0; i < range; i++)
{
uint32_t color = rgb(i / range);
...
}
Output:
One way to do what you want is to interpolate each of the component colours (RGB) from their starting to their ending value. This means using steps that are of as equal size as possible.
To get the RGB components some bit manipulation is necessary. This code gives you the three component colours for a fully-stated colour fullColour:
std::array<float, 3> colourRGB;
for(unsigned i = 0; i<3; ++i) {
colourRGB[i] = (fullColour & (0xFF << (i*8))) >> (i*8);
You use this to get the RGB array of your starting and ending colour (for example, for the red-orange range in your example this would be the RGB breakdown of 0xFF0000 and 0xFFA200). For each component you then work out the step size required for each step in your 'fade'. This is given by the total change in that component divided by the total number of steps. For the red-orange example the step size for the Red component would thus be 1.91 (because you have 86 steps and a total change of (0xA2 - 0x00, or 162 in decimal, yielding 162/86=1.91). The step sizes for the other components are zero as they don't change.
You can then iterate over the steps, at each one adding another step size and rounding to get the new value for each component. For example, at step s:
red[s] = startingRed + ((float)s * stepSizeRed)
To re-combine your rgb values back into a full colour, just apply some bit manipulation again:
fullColour[s] = 0;
for(unsigned i=0; i<3; ++i)
fullColour[s] += rgbComponent[s][i] << (i*8);
This approach yields the required 'fade' from any starting to any ending colour in any number of steps.
I want calculate angles of gradients from depth map and group it for some directions (8 sectors)
But my function calculates only first 3 directions
cv::Mat calcAngles(cv::Mat dimg)//dimg is depth map
{
const int directions_num = 8;//number of directions
const int degree_grade = 360;
int range_coeff = 255 / (directions_num + 1);//just for visualize
cv::Mat x_edge, y_edge, full_edge, angles;
dimg.copyTo(x_edge);
dimg.copyTo(y_edge);
dimg.copyTo(full_edge);
//compute gradients
Sobel( dimg, x_edge, CV_8U, 1, 0, 5, 1, 19, 4 );
Sobel( dimg, y_edge, CV_8U, 0, 1, 5, 1, 19, 4 );
Sobel( dimg, full_edge, CV_8U, 1, 1, 5, 1, 19, 4 );
float freq[directions_num + 1];//for collect direction's frequency
memset(freq, 0, sizeof(freq));
angles = cv::Mat::zeros(dimg.rows, dimg.cols, CV_8U);//store directions here
for(int i = 0; i < angles.rows; i++)
{
for(int j = 0; j < angles.cols; j++)
{
angles.at<uchar>(i, j) = (((int)cv::fastAtan2(y_edge.at<uchar>(i, j), x_edge.at<uchar>(i, j))) / (degree_grade/directions_num) + 1
) * (dimg.at<uchar>(i, j) ? 1 : 0);//fastatan returns values from 0 to 360, if i not mistaken. I want group angles by directions_num sectors. I use first 'direction' (zero value) for zero values from depth map (zero value at my depth map suggest that it is bad pixel)
freq[angles.at<uchar>(i, j)] += 1;
}
}
for(int i = 0; i < directions_num + 1; i++)
{
printf("%2.2f\t", freq[i]);
}
printf("\n");
angles *= range_coeff;//for visualization
return angles;
}
Out from one of the frames:
47359.00 15018.00 8199.00 6224.00 0.00 0.00 0.00 0.00 0.00
(first value is "zero pixel", next is number of gradients in n-place but only 3 are not zero)
Visualization
Is there way out? Or these result is OK?
PS Sorry for my writing mistakes. English in not my native language.
You used CV_8U type for Sobel output. It is unsigned integer 8 bit. So it can store only positive values. That's why fastAtan2 returns less or equal than 90. Change type to CV_16S and use short type for accessing the elements:
cv::Sobel(dimg, x_edge, CV_16S, 1, 0, 5, 1, 19, 4);
cv::Sobel(dimg, y_edge, CV_16S, 0, 1, 5, 1, 19, 4);
cv::fastAtan2(y_edge.at<short>(i, j), x_edge.at<short>(i, j))
At Wikipedia's Mandelbrot set page there are really beautiful generated images of the Mandelbrot set.
I also just implemented my own Mandelbrot algorithm. Given n is the number of iterations used to calculate each pixel, I color them pretty simple from black to green to white like that (with C++ and Qt 5.0):
QColor mapping(Qt::white);
if (n <= MAX_ITERATIONS){
double quotient = (double) n / (double) MAX_ITERATIONS;
double color = _clamp(0.f, 1.f, quotient);
if (quotient > 0.5) {
// Close to the mandelbrot set the color changes from green to white
mapping.setRgbF(color, 1.f, color);
}
else {
// Far away it changes from black to green
mapping.setRgbF(0.f, color, 0.f);
}
}
return mapping;
My result looks like that:
I like it pretty much already, but which color gradient is used for the images in Wikipedia? How to calculate that gradient with a given n of iterations?
(This question is not about smoothing.)
The gradient is probably from Ultra Fractal. It is defined by 5 control points:
Position = 0.0 Color = ( 0, 7, 100)
Position = 0.16 Color = ( 32, 107, 203)
Position = 0.42 Color = (237, 255, 255)
Position = 0.6425 Color = (255, 170, 0)
Position = 0.8575 Color = ( 0, 2, 0)
where Position is in range [0, 1) and Color is RGB in range [0, 255].
The catch is that the colors are not linearly interpolated. The interpolation of colors is likely cubic (or something similar). Following image shows the difference between linear and Monotone cubic interpolation:
As you can see the cubic interpolation results in smoother and "prettier" gradient. I used monotone cubic interpolation to avoid "overshooting" of the [0, 255] color range that can be caused by cubic interpolation. Monotone cubic ensures that interpolated values are always in the range of input points.
I use following code to compute the color based on iteration i:
double smoothed = Math.Log2(Math.Log2(re * re + im * im) / 2); // log_2(log_2(|p|))
int colorI = (int)(Math.Sqrt(i + 10 - smoothed) * gradient.Scale) % colors.Length;
Color color = colors[colorI];
where i is the diverged iteration number, re and im are diverged coordinates, gradient.Scale is 256, and the colors is and array with pre-computed gradient colors showed above. Its length is 2048 in this case.
Well, I did some reverse engineering on the colours used in wikipedia using the Photoshop eyedropper. There are 16 colours in this gradient:
R G B
66 30 15 # brown 3
25 7 26 # dark violett
9 1 47 # darkest blue
4 4 73 # blue 5
0 7 100 # blue 4
12 44 138 # blue 3
24 82 177 # blue 2
57 125 209 # blue 1
134 181 229 # blue 0
211 236 248 # lightest blue
241 233 191 # lightest yellow
248 201 95 # light yellow
255 170 0 # dirty yellow
204 128 0 # brown 0
153 87 0 # brown 1
106 52 3 # brown 2
Simply using a modulo and an QColor array allows me to iterate through all colours in the gradient:
if (n < MAX_ITERATIONS && n > 0) {
int i = n % 16;
QColor mapping[16];
mapping[0].setRgb(66, 30, 15);
mapping[1].setRgb(25, 7, 26);
mapping[2].setRgb(9, 1, 47);
mapping[3].setRgb(4, 4, 73);
mapping[4].setRgb(0, 7, 100);
mapping[5].setRgb(12, 44, 138);
mapping[6].setRgb(24, 82, 177);
mapping[7].setRgb(57, 125, 209);
mapping[8].setRgb(134, 181, 229);
mapping[9].setRgb(211, 236, 248);
mapping[10].setRgb(241, 233, 191);
mapping[11].setRgb(248, 201, 95);
mapping[12].setRgb(255, 170, 0);
mapping[13].setRgb(204, 128, 0);
mapping[14].setRgb(153, 87, 0);
mapping[15].setRgb(106, 52, 3);
return mapping[i];
}
else return Qt::black;
The result looks pretty much like what I was looking for:
:)
I believe they're the default colours in Ultra Fractal. The evaluation version comes with source for a lot of the parameters, and I think that includes that colour map (if you can't infer it from the screenshot on the front page) and possibly also the logic behind dynamically scaling that colour map appropriately for each scene.
This is an extension of NightElfik's great answer.
The python library Scipy has monotone cubic interpolation methods in version 1.5.2 with pchip_interpolate. I included the code I used to create my gradient below. I decided to include helper values less than 0 and larger than 1 to help the interpolation wrap from the end to the beginning (no sharp corners).
#set up the control points for your gradient
yR_observed = [0, 0,32,237, 255, 0, 0, 32]
yG_observed = [2, 7, 107, 255, 170, 2, 7, 107]
yB_observed = [0, 100, 203, 255, 0, 0, 100, 203]
x_observed = [-.1425, 0, .16, .42, .6425, .8575, 1, 1.16]
#Create the arrays with the interpolated values
x = np.linspace(min(x_observed), max(x_observed), num=1000)
yR = pchip_interpolate(x_observed, yR_observed, x)
yG = pchip_interpolate(x_observed, yG_observed, x)
yB = pchip_interpolate(x_observed, yB_observed, x)
#Convert them back to python lists
x = list(x)
yR = list(yR)
yG = list(yG)
yB = list(yB)
#Find the indexs where x crosses 0 and crosses 1 for slicing
start = 0
end = 0
for i in x:
if i > 0:
start = x.index(i)
break
for i in x:
if i > 1:
end = x.index(i)
break
#Slice away the helper data in the begining and end leaving just 0 to 1
x = x[start:end]
yR = yR[start:end]
yG = yG[start:end]
yB = yB[start:end]
#Plot the values if you want
#plt.plot(x, yR, color = "red")
#plt.plot(x, yG, color = "green")
#plt.plot(x, yB, color = "blue")
#plt.show()