I have encoded floats into a texture (4 bytes of a float32 are stored as RBGA values).
Now I need to decode them back into a single float.
Here's what I've tried so far and what didn't work for me:
float rgbaToFloat(vec4 rgba) {
uint r = uint(rgba.x*255.);
uint g = uint(rgba.y*255.);
uint b = uint(rgba.z*255.);
uint a = uint(rgba.w*255.);
return uintBitsToFloat((r << 24) | (g << 16) | (b << 8) | a);
}
I pack floats into a texture image using the following snippet (python):
import numpy as np
data_flat = [...] #floats
dim = int(np.sqrt(len(data_flat))) + 1
image = np.zeros((dim, dim, 4), dtype='uint8')
for i, d in enumerate(data_flat):
image[i // dim, i % dim] = np.array([d], dtype=np.float32).view(np.uint8)
For a single floating-point value, it produces the following output:
d = 0.06797099858522415;
np.array([d], dtype=np.float32).view(np.uint8)
>>> array([ 97, 52, 139, 61], dtype=uint8)
Which seems to be correct, as binary representations match.
It seems to return overflowed values, hard to say, with the only debug way being comparing pictures, but it's certainly not what I expect it to output.
Thanks to #LJᛃ for referring me to this answer, it works like charm.
Related
I need help figuring out how OpenCV handles setting a matrix equal to something.
I have an 8-Bit Mat called Radiance that I want to tone map. Here is working code that accomplishes this for me, with K being the constant 450.
cv::cvtColor(radiance, radiance, CV_BGR2XYZ);
radiance = (K * radiance)/(1 + (K * radiance));
cv::cvtColor(radiance, radiance, CV_XYZ2BGR);`
This does not seem like it should work, but it does. It will create a fully tone mapped image that looks great. However, if you try to do this method on the individual pixels, they become a decimal that is between 0 and 1, which truncates to 0. Here is an example of this -
cv::cvtColor(radiance, radiance, CV_BGR2XYZ);
int x = radiance.at<cv::Vec3b>(500, 500)[0];
x = (K * x)/(1 + (K * x));
std::cout << x << "\n";
The output of this is exactly what I would expect
0
I understand why the second snippet of code prints out a zero, but what is going on in the first part that allows it to tone map the image properly, and how can I recreate this on the individual pixel level?
Can't you just define radiance as float matrix?
Mat radiance(m, n, DataType<float>::type);
So you can get a float
cv::cvtColor(radiance, radiance, CV_BGR2XYZ);
float x = radiance.at<cv::Vec3b>(500, 500)[0];
x = (K*x)/(1 + (K*x));
std::cout << x << "\n";
given a grey cv::Mat (CV_8UC1) I want to return another cv::Mat containing the square root of the elements (CV_32FC1) and I want to do it with SSE2 intrinsics. I am having some problems with the conversion from 8-bit values to 32 float values to perform the square root. I would really appreciate any help. This is my code for now(it does not give correct values):
uchar *source = (uchar *)cv::alignPtr(image.data, 16);
float *sqDataPtr = cv::alignPtr((float *)Squared.data, 16);
for (x = 0; x < (pixels - 16); x += 16) {
__m128i a0 = _mm_load_si128((__m128i *)(source + x));
__m128i first8 = _mm_unpacklo_epi8(a0, _mm_set1_epi8(0));
__m128i last8 = _mm_unpackhi_epi8(a0, _mm_set1_epi8(0));
__m128i first4i = _mm_unpacklo_epi16(first8, _mm_set1_epi16(0));
__m128i second4i = _mm_unpackhi_epi16(first8, _mm_set1_epi16(0));
__m128 first4 = _mm_cvtepi32_ps(first4i);
__m128 second4 = _mm_cvtepi32_ps(second4i);
__m128i third4i = _mm_unpacklo_epi16(last8, _mm_set1_epi16(0));
__m128i fourth4i = _mm_unpackhi_epi16(last8, _mm_set1_epi16(0));
__m128 third4 = _mm_cvtepi32_ps(third4i);
__m128 fourth4 = _mm_cvtepi32_ps(fourth4i);
// Store
_mm_store_ps(sqDataPtr + x, _mm_sqrt_ps(first4));
_mm_store_ps(sqDataPtr + x + 4, _mm_sqrt_ps(second4));
_mm_store_ps(sqDataPtr + x + 8, _mm_sqrt_ps(third4));
_mm_store_ps(sqDataPtr + x + 12, _mm_sqrt_ps(fourth4));
}
The SSE code looks OK, except that you're not processing the last 16 pixels:
for (x = 0; x < (pixels - 16); x += 16)
should be:
for (x = 0; x <= (pixels - 16); x += 16)
Note that if your image width is not a multiple of 16 then you will need to take care of any remaining pixels after the last full vector.
Also note that you are taking the sqrt of values in the range 0..255. It may be that you want normalised value in the range 0..1.0, in which case you'll want to scale the values accordingly.
I have no experience with SSE2, but I think that if performance is the issue you should use look-up table. Creation of look-up table is fast since you have only 256 possible values. Copy 4 bytes from look-up table into destination matrix should be a very efficient operation.
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.
I'm using a security camera DLL to retreive the image from the camera. The DLL call a function of my program passing the image buffer as a parameter, but the image is in yuy2 format. I need to convert this buffer to RGB, but I tried every formula I found on Internet with no success. Every example I tried (including http://msdn.microsoft.com/en-us/library/aa904813(VS.80).aspx#yuvformats_2) gives me wrong colors.
I'm able to convert the buffer to a BW image using only the Y component of the pixel, but I really need the color picture. I debugged (assembly only) the DLL that shows the image in the screen and it uses DirectDraw to do this.
Using the information from the Microsoft link in the question:
for (int i = 0; i < width/2; ++i)
{
int y0 = ptrIn[0];
int u0 = ptrIn[1];
int y1 = ptrIn[2];
int v0 = ptrIn[3];
ptrIn += 4;
int c = y0 - 16;
int d = u0 - 128;
int e = v0 - 128;
ptrOut[0] = clip(( 298 * c + 516 * d + 128) >> 8); // blue
ptrOut[1] = clip(( 298 * c - 100 * d - 208 * e + 128) >> 8); // green
ptrOut[2] = clip(( 298 * c + 409 * e + 128) >> 8); // red
c = y1 - 16;
ptrOut[3] = clip(( 298 * c + 516 * d + 128) >> 8); // blue
ptrOut[4] = clip(( 298 * c - 100 * d - 208 * e + 128) >> 8); // green
ptrOut[5] = clip(( 298 * c + 409 * e + 128) >> 8); // red
ptrOut += 6;
}
This formula worked:
int C = luma - 16;
int D = cr - 128;
int E = cb - 128;
r = (298*C+409*E+128)/256;
g = (298*C-100*D-208*E+128)/256;
b = (298*C+516*D+128)/256;
I got this from a matlab example.
The gotcha is: in memory, Windows bitmaps aren't RGB, they are BGR. If you are writing to a memory buffer, you need to do something like this:
rgbbuffer[rgbindex] = (char)b;
rgbbuffer[rgbindex + 1] = (char)g;
rgbbuffer[rgbindex + 2] = (char)r;
If you were already using DirectShow to get video data out of the security camera, then you could simply add the "Color Space Converter Filter" to your DirectShow graph. But if you aren't using DirectShow already (it sounds like you're not) then it will be much, much simpler to just convert the data to RGB yourself using the formulas that Daniel linked to. Adding DirectShow to a project is very complicated.
You will have to write your own converter. GDI+ doesn't know how to deal with YUY2 bitmaps.
Look here.
Please note that 2 pixels share same color values and have different luminance values.
Here are some formulas to help you write your converter.
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.