So I've successfully access pixel data in a frame using the c++ frame access wrapper on the opencv webpage
template<class Frame>
class Frame_Data {
IplImage *imgp;
public:
Frame_Data (IplImage *img=0) {imgp = img;}
~Frame_Data () {imgp = 0;}
void operator=(IplImage *img) {imgp=img;}
inline Frame* operator[] (int rowIndex) {
return ((Frame*)(imgp->imageData + rowIndex*imgp->widthStep));
}
};
typedef struct {
unsigned char b,g,r;
} RgbPixel;
typedef struct {
float b,g,r;
} RgbPixelFloat;
typedef Frame_Data<RgbPixel> RgbImage;
Im then using 2 for loops to go through the frame pixel array such as:
for (int i = ymin; i < ymax; i++)
{
for (int j = xmin; j < xmax; j++)
{
int r = image[i][j].r;
int g = image[i][j].g;
int b = image[i][j].b;
So lets say I want to throw in an IF statement to check pixel data colors. I've seen some websites list them as stuff like
image[i][j].r=0xFF;
or if g < 0x20
Im not used to the hex looking values, i tried to look them up but can't find any refernece, im used to cvscalars, so what do these mean? Like what does 0x20 stand for? or what about 0xFF?
thanks
The range from 0x00 ... 0xFF that you are seeing is one byte which can hold a value between 0 and 255 which is how pixel color data is stored, generally in 3 or 4 bytes consisting of Red, Blue, Green and optionally Alpha.
The CvScalar is just a convenience container of 1, 2, 3 or 4 doubles which can be used to hold these values in a slightly different form.
For example:
cv.RGB(1.0, 0.5, 0.3) sets the red component of the color to 1.0 or 100%, the green component to 0.5 or 50% and the blue component to 0.3 or 30%. When the actual color structure is created each of these components will be made up of exactly one byte so this is analagous to setting the
R (red component) to 1.0 * 0xFF = 0xFF
G (green component) to 0.5 * 0xFF = 0x7F
B (blue component) to 0.3 * 0xFF = 0x26
The alpha is automatically set to 1.0 or 0xFF
Hexidecimal is just another representation of a number (base 16).
It's not too hard to get used to, you just need to learn how to convert to and from regular base 10 numbers.
Open up your favourite windows/mac calculator, switch to Hex mode, and type in FF. (the 0x prefix just tells the code that it's it's hexidecimal number)
Switch to Dec[imal] and the number will change to 255.
Type 32 in, in Decimal mode, then click hex, you'll see the number change to 20 (or 0x20 as it is in your code)
Now you can go from hexidecimal to decimal, you can go from decimal to scalar quite easily; Just convert the range;
float Scalar = static_cast<float>( Decimal ) / 255.f; // 255 being the largest value for your byte-colour
Enjoy Hex! You'll find it a very useful, neat and important way of looking at data.
Related
I have a program that generates an grayscale image. The grayshading for every pixel is set with the following code sniped:
//loop over all pixel
*PixelColorPointer = Number * 0x010101;
in this case the Number is an integer number between 0 and 255. Which generates all grayscale colors from black to white.
What I try to do is have an colored image (in order to have false colors), but I don't really understand the calculation with the hex value. I figured out if I assign e.g. Number * 0xFFFFFF I have a gradient/variety from white to yellow.
Can someone explain me how the calculation of this colors is working? Please remember that (as said already) I want/have to pass the Number variable to get variety.
RGB color are stored byte by byte (0 to 255).
When you say 0x010203 it is (in hex) 01 red, 02 green, and 03 blue. It can also be inverted (03 red, 02 green, 01 blue) depending on your Endianness.
Here you have to separate your 3 colors. Then you shoud multiply every color by it's coefficient.
You can store your color in an union, it's the easiest way.
union Color
{
struct
{
unsigned char b;
unsigned char g;
unsigned char r;
unsigned char a; // Useless here
};
unsigned int full;
};
Color c;
c.full = 0x102030;
unsigned char res = 0.229 * c.r + 0.587 * c.g + 0.114 * c.b;
Color grey;
grey.r = grey.g = grey.b = res;
0.229, 0.587 and 0.114 are the Relative luminance.
Remember, you might need to invert the rgba order and move the a place :)
You need to give a little more information. What library of function are you using to do this??
But by just seeing the problems I think the hexadecimal number refers to the color in the Color-hex code, the Number would refer to the brightness 0 being blank and 255 max color intensity (white)
EDIT: Actually I think the whole number resulting from:
Number * 0x010101
Is the hexadecimal color code, in the particular case of 0x010101 the Number works as the intensity. But any other hexadecimal would give you some weird result.
Use an Color-hex code table choose any random color and just input :
*PixelColorPointer = 0XHEXCODE;
if the output is the desired color then I'm right
I am implementing an alpha blending, and one of the examples I came across used this format. I am confused why the division by 256 and why isn't there inv_alpha in red and blue channels
int pixel,vga_pixel;
int alpha, blue, green, red, pixel;
int height = 1296;
int width = 968;
int x, y;
for (y = 0; y <= height; y++){
for (x = 0; x <= width; x++){
pixel = *(img.memloc + x + y);
//0xff gets the first 8 bits, in this case red
red = pixel & 0xff;
//shift by 8 to get rid of red then AND to get first 8, here green
green = pixel >> 8 & 0xff;
blue = pixel >> 16 & 0xff;
alpha = pixel >> 24 & 0xff;
int inv_alpha = 0xff - alpha; // 1-alpha
int vga_red = (red*(int)alpha);
int vga_green = (green*(int)alpha + inv_alpha/256);
int vga_blue = (blue*(int)alpha);
int vga_alpha = 0xff;
int vga_pixel = vga_alpha << 24 | vga_blue << 16 | vga_green << 8 | vga_red;
}
}
Can anyone clarify if this is a valid method, and why?
It look's like you've mixed the formulas from integer and floating point blending. For example the vga_red should probably become 255 if both red and alpha is, so it would be closer to (red*alpha)/255, but probably you should probably ensure correct rounding by using (red*alpha+127)/255.
The dividing inv_alpha with 256 would always yield zero so it's probably wrong, I'd guess that you want the result to be green if alpha is 255 and 255 if alpha is 0. So it would become something like (green*alpha+127)/255 + inv_alpha.
The formula for alpha blending is C = alpha_A * A + (1 - alpha_A * B). But in this formula we're working with floating point values and alpha is a value between 0 and 1.
Since we're working with integer values the alpha (and red, green and blue as well) is a value between 0 and 255. So the value of 1 - alpha_A is encapsulated in inv_alpha_A = 255 - alpha_A.
Since each color may not exceed the maximum value of one byte we have to ensure that the calculated number does not exceed 255. So we have to divide by 255.This results in:
C = (alpha_A * A + inv_aplha_A * B) / 255
(I intentionally skipped the rounding problem).
I used method:
val = 0.299 * R + 0.587 * G + 0.114 * B;
image.setRGBA(val, val, val, 0);
to convert bitmap (24-bit RGB888 and 32-bit RGBA8888) to grayscale successfully.
Example: BMP 24-bit: 0E 0F EF (BGR) --> 51 51 51 (Grayscale) // using above method
But can't apply for bitmap 16-bit RGBA4444.
Example: BMP 16-bit: 'A7 36' (BGRA) --> 'CE 39' (Grayscale) // ???
Anyone know how?
Are you sure you need RGBA4444 format? Maybe you need an old format where green channel gets 6 bits while red and blue get 5 bits (total 16 bits)
In case of 5-6-5 format - the answer is simple.
Just do
R = (R>>3); G = (G>>2); B = (B>>3); to reduce the 24 bits into 16. Now just combine them using | operation.
Here is a sample code in C
// Combine RGB into 16bits 565 representation. Assuming all inputs are in range of 0..255
static INT16 make565(int red, int green, int blue){
return (INT16)( ((red << 8) & 0xf800)|
((green << 2) & 0x03e0)|
((blue >> 3) & 0x001f));
}
The method above uses roughly the same structure as the regular ARGB construction method but squeezes the colors to 16 bits instead of 32 like in the following example:
// Combine RGB into 32bit ARGB representation. Assuming all inputs are in range of 0..255
static INT32 makeARGB(int red, int green, int blue){
return (INT32)((red)|(green << 8)|(blue<<16)|(0xFF000000)); // Alpha is always FF
}
If you do need RGBA4444 then the method would be a combination of the two above
// Combine RGBA into 32bit 4444 representation. Assuming all inputs are in range of 0..255
static INT16 make4444(int red, int green, int blue, int alpha){
return (INT32)((red>>4)|(green&0xF0)|((blue&0xF0)<<4)|((alpha&0xF0)<<8));
}
I am trying to convert a given Mat representing an RGB image with 8-bit depth to Lab using the function provided in the documentation:
cvtColor(source, destination, <conversion code>);
I have tried the following conversion codes:
CV_RGB2Lab
CV_BGR2Lab
CV_LBGR2Lab
I have received bizarre results each time around, with an "L" value of greater than 100 for some samples, literally <107, 125, 130>.
I am also using Photoshop to check the results - but given that 107 is beyond the accepted range of 0 ≤ L ≤ 100, I can not comprehend what my error is.
Update:
I'll post my overall results here:
Given an image (Mat) represented by 8-bit BGR, the image can be converted by the following:
cvtColor(source, destination, CV_BGR2Lab);
The pixel values can then be accessed in the following manner:
int step = destination.step;
int channels = destination.channels();
for (int i = 0; i < destination.rows(); i++) {
for (int j = 0; j < destination.cols(); j++) {
Point3_<uchar> pixelData;
//L*: 0-255 (elsewhere is represented by 0 to 100)
pixelData.x = destination.data[step*i + channels*j + 0];
//a*: 0-255 (elsewhere is represented by -127 to 127)
pixelData.y = destination.data[step*i + channels*j + 1];
//b*: 0-255 (elsewhere is represented by -127 to 127)
pixelData.z = destination.data[step*i + channels*j + 2];
}
}
If anyone is interested in the range of the other variables a and b I made a small program to test their range.
If you convert all the colors that are represented with RGB to the CieLab used in OpenCV the ranges are:
0 <=L<= 255
42 <=a<= 226
20 <=b<= 223
And if you're using RGB values in the float mode instead of uint8 the ranges will be:
0.0 <=L<= 100.0
-86.1813 <=a<= 98.2352
-107.862 <=b<= 94.4758
P.S. If you want to see how distinguishable (regarding human perception) is a LAB value from another LAB value, you should use the floating point. The scale used to keep the lab values in the uint8 ranges messes up with their euclidean distance.
This is the code I used (python):
L=[0]*256**3
a=[0]*256**3
b=[0]*256**3
i=0
for r in xrange(256):
for g in xrange(256):
for bb in xrange(256):
im = np.array((bb,g,r),np.uint8).reshape(1,1,3)
cv2.cvtColor(im,cv2.COLOR_BGR2LAB,im) #tranform it to LAB
L[i] = im[0,0,0]
a[i] = im[0,0,1]
b[i] = im[0,0,2]
i+=1
print min(L), '<=L<=', max(L)
print min(a), '<=a<=', max(a)
print min(b), '<=b<=', max(b)
That's because L value is in range [0..255] in OpenCV. You can simply scale this value to needed interval ([0..100] in your case).
I am not sure about João Abrantes's range on A and B.
The opencv documentation has clearly mentioned the CIE L*a*b*range.
8 bit images
Thus leading to a range of
0 <= L <= 255
0 <= a <= 255
0 <= b <= 255
In case anyone runs into the same issue:
Please note that in OpenCV (2.4.13), you can not convert CV_32FC3 BGR images into the Lab color space. That is to say:
//this->xImage is CV_8UC3
this->xImage.convertTo(FloatPrecisionImage, CV_32FC3);
Mat result;
cvtColor(FloatPrecisionImage, result, COLOR_BGR2Lab);
this->xImage = result;
will not work
while
Mat result;
cvtColor(this->xImage, result, COLOR_BGR2Lab);
result.convertTo(this->xImage, CV_32FC3);
works like a charm.
I did not track down the reason for said behavior; however it seems off to me, because this in effect puts limits on the image's quality.
All right, so I have been very frustrated trying to convert a 12-bit buffer to an 8-bit one.
The image source is a 12-bit GrayScale (decompressed from JPEG2000) whose color range goes from 0-4095. Now I have to reduce that to 0-255. Common sense tells me that I should simply divide each pixel value like this. But when I try this, the image comes out too light.
void
TwelveToEightBit(
unsigned char * charArray,
unsigned char * shortArray,
const int num )
{
short shortValue = 0; //Will contain the two bytes in the shortArray.
double doubleValue = 0; //Will contain intermediary calculations.
for( int i = 0, j =0; i < num; i++, j +=2 )
{
// Bitwise manipulations to fit two chars onto one short.
shortValue = (shortArray[j]<<8);
shortValue += (shortArray[j+1]);
charArray[i] = (( unsigned char)(shortValue/16));
}
}
Now I can tell that there needs to be some contrast adjustments. Any ideas anyone?
Many Thanks in advance
In actuality, it was merely some simple Contrast adjustments that needed to be made. I realized this as soon as I loaded up the result image in Photoshop and did auto-contrast....the image result would very closely resemble the expected output image.
I found out an algorithm that does the contrast and will post it here for other's convenience:
#include <math.h>
short shortValue = 0; //Will contain the two bytes in the shortBuffer.
double doubleValue = 0; //Will contain intermediary calculations.
//Contrast adjustment necessary when converting
//setting 50 as the contrast seems to be real sweetspot.
double contrast = pow( ((100.0f + 50.0f) / 100.0f), 2);
for ( int i = 0, j =0; i < num; i++, j += 2 )
{
//Bitwise manipulations to fit two chars onto one short.
shortValue = (shortBuffer[j]<<8);
shortValue += (shortBuffer[j+1]);
doubleValue = (double)shortValue;
//Divide by 16 to bring down to 0-255 from 0-4095 (12 to 8 bits)
doubleValue /= 16;
//Flatten it out from 0-1
doubleValue /= 255;
//Center pixel values at 0, so that the range is -0.5 to 0.5
doubleValue -= 0.5f;
//Multiply and just by the contrast ratio, this distances the color
//distributing right at the center....see histogram for further details
doubleValue *= contrast;
//change back to a 0-1 range
doubleValue += 0.5f;
//and back to 0-255
doubleValue *= 255;
//If the pixel values clip a little, equalize them.
if (doubleValue >255)
doubleValue = 255;
else if (doubleValue<0)
doubleValue = 0;
//Finally, put back into the char buffer.
charBuffer[i] = (( unsigned char)(doubleValue));
}
The main problem, as I understand, is to convert a 12-bit value to a 8-bit one.
Range of 12-bit value = 0 - 4095 (4096 values)
Range of 8-bit value = 0 - 255 ( 256 values)
I would try to convert a 12-bit value x to a 8-bit value y
First, scale down first to the range 0-1, and
Then, scale up to the range 0-256.
Some C-ish code:
uint16_t x = some_value;
uint8_t y = (uint8_t) ((double) x/4096 ) * 256;
Update
Thanks to Kriss's comment, I realized that I disregarded the speed issue. The above solution, due to floating operations, might be slower than pure integer operations.
Then I started considering another solution. How about constructing y with the 8 most significant bits of x? In other words, by trimming off the 4 least significant bits.
y = x >> 4;
Will this work?
if you just want to drop the bottom 4 least significant bits you can do the following:
unsigned int start_value = SOMEVALUE; // starting value
value = (value & 0xFF0 ); // drop bits
unsigned char final_value =(uint8_t)value >> 4; //bit shift to 8 bits
Note the "unsigned". You don't want the signed bit mucking with your values.
Like this:
// Image is stored in 'data'
unsigned short* I = (unsigned short*)data;
for(int i=0; i<imageSize; i++) {
// 'color' is the 8-bit value
char color = (char)((double)(255*I[i])/(double)(1<<12));
/*...*/
}
Wild guess: your code assumes a big-endian machine (most significant byte first). A Windows PC is little-endian. So perhaps try
shortValue = (shortArray[j+1]<<8);
shortValue += (shortArray[j]);
If indeed endiasness is the problem then the code you presented would just shave off the 4 most significant bits of every value, and expand the rest to the intensity range. Hm, EDIT, 2 secs later: no, that was a thinko. But try it anyway?
Cheers & hth.,
– Alf