yuv to rgb conversion columns not aligned - rgb

I am converting a YUV422 frame to a PPM file - (RGB888).
The image is converting mostly. Colors are fine. But each seems row is askew, as if extra
Here is the frame conversion function:
BYTE* CYUV::toRGB()
{
BYTE* rgb = new BYTE[m_width*m_height*3];
BYTE* rgb_ret = rgb;
BYTE* p_yuv = m_yuv_frame;
for (int i=m_width*m_height/2; i--;)
{
BYTE y, u, v, y2;
BYTE r,g, b;
y2 = *p_yuv++;
v = *p_yuv++;
y = *p_yuv++;
u = *p_yuv++;
r = (y + 1.370705 * (v - 128));
g = (y - (0.698001 * (v - 128)) - (0.337633 * (u - 128)));
b = (y + (1.732446 * (u - 128)));
*rgb++ = b < 0 ? 0 : b > 255 ? 255 : b;
*rgb++ = g < 0 ? 0 : g > 255 ? 255 : g;
*rgb++ = r < 0 ? 0 : r > 255 ? 255 : r;
r = (y2 + 1.370705 * (v - 128));
g = (y2 - (0.698001 * (v - 128)) - (0.337633 * (u - 128)));
b = (y2 + (1.732446 * (u - 128)));
*rgb++ = b < 0 ? 0 : b > 255 ? 255 : b;
*rgb++ = g < 0 ? 0 : g > 255 ? 255 : g;
*rgb++ = r < 0 ? 0 : r > 255 ? 255 : r;
}
return rgb_ret;
}
I believe the actual yuv-to-rgb pixel conversion is correct, because I had tried many other formulae, with color distorted results.
As for the PPM file, that is good also, since all image readers handle it.
As for the original YUV4222 frame, it to is fine - I display it using SDL without this distortion.
Any suggestions?
TIA
ken

OK: Solved this.
It was simple enough: write out the y converted pixel BEFORE the y2.
There were SO many solutions out there, but few seem completely correct.
FYI: This DOES work, but only for a yuv 4:2:2 format. This seems to be the most common one with the fourcc label of YUYV. yuv 4:2:0 is much more complex to convert. yuv 4:4:4 should be the same as this solution fetching only 1 y component, and writing out only one rgb triplet. (can't verify that, but I that was the comment along with this solution.
Hope this helps someone.
ken

Related

Color normalization results in gray tones

I use floating point numbers in my code (c code with openGL) to operate with colors, but the graphic I am using is in the rgb byte format. Loaded with the following code everything works correctly:
//... glfw window initialization...
for (int y = 0; y < 32; y++)
{
for (int x = 0; x < 32; x++)
{
int p = (y * 32 + x) * 3;
float r = rgb[p];
float g = rgb[p + 1];
float b = rgb[p + 2];
glColor3ub(r, g, b);
glPointSize(16);
glBegin(GL_POINTS);
glVertex2i(x * 16, y * 16);
glEnd();
}
}
Resulting in:
The error occurs if I try to normalize the bytes (one by one) with the following code:
//... glfw window initialization...
for (int y = 0; y < 32; y++)
{
for (int x = 0; x < 32; x++)
{
int p = (y * 32 + x) * 3;
float r = ((float)rgb[p]) / 255.0f;
float g = ((float)rgb[p + 1]) / 255.0f;
float b = ((float)rgb[p + 2]) / 255.0f;
glColor3f(r,g, b);
glPointSize(16);
glBegin(GL_POINTS);
glVertex2i(x * 16, y * 16);
glEnd();
}
}
The picture loaded this way has different colors:
Suppose the array rbg[] is signed, and consider a red component value 255.
In the first example the 255 is actually -1 and is converted to -1.000000 and this is then passed to the function after converting to type GLubyte which is unsigned 8-bit. The conversion rules will make this value 255 which is what you thought you had.
In the second example the 255 is again converted to -1.000000 and scaled to be -0.003922 instead of the expected 1.000000and so the result is very different from what you expected.
The solution is to define the array as unsigned char or uint8_t.

How to convert RGB565 to HSL Color in C

I'm new to converting image types. I would like to determine the color of each pixel on the screen. I can read the colors from the frame buffer, but they are all in RGB565. For tracking a certain color, I would like to convert the RGB565 to HSV so I can check the hue.
I've already tried converting it via an online converter from RGB565 to RGB888. For example the RGB565 '08F5' to RGB888 '081BAA'. And then from RGB888 to HSL '233 91% 35%'. However, I can't get this working with c code. The colors are in HEX format and stored per 2 in one register. I made a char array of four for each color.
int colorcodes = IORD_ALTERA_AVALON_PIO_DATA(0x08000000 + 123204);
char colorcodesInHex[9];
snprintf(colorcodesInHex, 9, "%08x\n", colorcodes);
char firstColor[4];
char secondColor[4];
for(int i = 0; i <= 7; i++)
{
if(i <= 3)
{
firstColor[i] = colorcodesInHex[i];
}
else if (i >= 4 && i <= 7)
{
secondColor[i - 4] = colorcodesInHex[i];
}
}
Does someone know how to convert the RGB565 to RGB888 and then to HSL in C?
int rgb565 = ...; // 16 bit value with rrrrrggggggbbbbb
double r = ((rgb565 >> 11) & 0x1F) / 31.0; // red 0.0 .. 1.0
double g = ((rgb565 >> 5) & 0x3F) / 63.0; // green 0.0 .. 1.0
double b = (rgb565 & 0x1F) / 31.0; // blue 0.0 .. 1.0
double cmax = max(r, max(g, b));
double cmin = min(r, min(g, b));
double delta = cmax - cmin;
// hue (in °)
double h_degrees = delta == 0.0 ? 0.0
: cmax == r ? 60 * (((g - b) / delta) % 6)
: cmax == g ? 60 * (((b - r) / delta + 2)
: /* cmax == b ? */ 60 * (((r - g) / delta + 4);
// saturation
double s = delta == 0.0 ? 0.0 : delta / (1.0 - abs(cmax + cmin - 1));
// lightness
double l = (cmax + cmin)/2;
RGB565 is a 16 packing of red-green-blue. The above is the RGB565 to HSL conversion.
With Hue in degrees 0° to 360°
The red/green/blue components are extracted with bit shifting >> and then scaling to 0.0 - 1.0.
The resulting lightness is an imperfect average, namely the average of minimal and maximal color component value.
The hue, coloredness, is an angle in a color circle divided in the three RGB colors.
The saturation, gray tendency, is as defined determined by smaller delta.

Wrong result for non square image

I am trying to implement dark (not exactly)emboss filter, my problem is when I use it on SQUARED Lena image 512x512 result is good.
But when I use it on image which has rectangular shape e.g. 1280x720 result is all messed up, why is it so? Format of images is RGB.
GOOD result with Lena 512x512 (original):
WRONG result with 1280x720 image (original not same size just for comparison):
For a 24bit image, if the width of the image is 682 then it needs padding. Because 682*3 is not a multiple of 4. Try changing the image width to 680 and try again.
To pad the image rows, use the following formula:
int pad = WIDTH % 4;
if(pad == 4) pad = 0;
WIDTH += pad;
Change the condition to fb_j < HEIGHT - 1 - FILTER_HEIGHT and fb_i < WIDTH - 1 - FILTER_WIDTH to avoid buffer overflow.
The bitmap is scanned from top to bottom. It works fine when I switched the dimension as follows (but I loaded the bitmap differently)
//Pixel frame_buffer[WIDTH][HEIGHT];
//Pixel temp_buffer[WIDTH][HEIGHT];
Pixel frame_buffer[HEIGHT][WIDTH];
Pixel temp_buffer[HEIGHT][WIDTH];
...
for(int fb_j = 1; fb_j < HEIGHT - 1 - FILTER_HEIGHT; fb_j++) {
for(int fb_i = 1; fb_i < WIDTH - 1 - FILTER_WIDTH; fb_i++) {
float r = 0, g = 0, b = 0;
for(int ker_i = 0; ker_i < FILTER_WIDTH; ker_i++) {
for(int ker_j = 0; ker_j < FILTER_HEIGHT; ker_j++) {
r += ((float)(frame_buffer[fb_j + ker_j][fb_i + ker_i].r / 255.0) * emboss_kernel[ker_j][ker_i]);
g += ((float)(frame_buffer[fb_j + ker_j][fb_i + ker_i].g / 255.0) * emboss_kernel[ker_j][ker_i]);
b += ((float)(frame_buffer[fb_j + ker_j][fb_i + ker_i].b / 255.0) * emboss_kernel[ker_j][ker_i]);
}
}
if(r > 1.0) r = 1.0;
else if(r < 0) r = 0;
if(g > 1.0) g = 1.0;
else if(g < 0) g = 0;
if(b > 1.0) b = 1.0;
else if(b < 0) b = 0;
// Output buffer which will be rendered after convolution
temp_buffer[fb_j][fb_i].r = (GLubyte)(r*255.0);
temp_buffer[fb_j][fb_i].g = (GLubyte)(g*255.0);
temp_buffer[fb_j][fb_i].b = (GLubyte)(b*255.0);
}
}
Also try running a direct copy for testing. Example:
temp_buffer[fb_j][fb_i].r = frame_buffer[fb_j][fb_i].r;
temp_buffer[fb_j][fb_i].g = frame_buffer[fb_j][fb_i].g;
temp_buffer[fb_j][fb_i].b = frame_buffer[fb_j][fb_i].b;

Ray Tracing calculation in C

I'm new to ray tracing and trying to program one in C. But My program keep on showing a dot (around 1-3 pixel) of the sphere in the wrong places and now I'm confused. This feels like a very stupid question, but I'm confused about exactly how big is 1 radius of a sphere? What I mean by that is if the radius is 1, the circle is 2 pixels?
I know all the calculations and I triple checked if I had any errors in my codes. but just incase, here is part of my codes:
Directions:
//size: 1024x768, view point (512 384 1), screen (0 0 0) to (1024 768 0)
ray[0] = x - start_x;
ray[1] = y - start_y;
ray[2] = 0 - start_z;
//normalize
double length;
length = (sqrt((ray[0]*ray[0]) + (ray[1]*ray[1]) + (ray[2]*ray[2])));
ray[0] = ray[0]/length;
ray[1] = ray[1]/length;
ray[2] = ray[2]/length;
Intersection:
temp = top; //my struct with sphere data, _x, _y, _z, _r, _red, _green, _blue
//x and y is the current pixel value
while (temp != NULL) {
x_diff = start_x - temp->_x + 0.0;
y_diff = start_y - temp->_y + 0.0;
z_diff = start_z - temp->_z + 0.0;
//a = 1 because my direction is a normalized
b = 2.0 * ((rayVector[0] * x_diff) + (rayVector[1] * y_diff) + (rayVector[2] * z_diff));
c = (x_diff * x_diff * 1.0) + (y_diff * y_diff) + (z_diff * z_diff) - (temp->_r * temp->_r);
check = (b * b) - (4.0 * c);
if (check < 0) { //0
pixels[width][height][0] = 0.0;
pixels[width][height][1] = 0.0;
pixels[width][height][2] = 0.0;
}
else if (check == 0) { //1
r1 = (b * -1.0) /2.0;
if (r1 < nearest_z) {
nearest_z = r1;
pixels[width][height][0] = temp->_red;
pixels[width][height][1] = temp->_green;
pixels[width][height][2] = temp->_blue;
}
}
else { //2
r1 = ((b * -1.0) + sqrt(check))/2.0;
r2 = ((b * -1.0) - sqrt(check))/2.0;
if ((r1 < r2) && (r1 < nearest_z)) {
nearest_z = r1;
pixels[width][height][0] = 255.0;
pixels[width][height][1] = 0;
pixels[width][height][2] = 0;
}
else if ((r2 < r1) && (r2 < nearest_z)) {
nearest_z = r2;
pixels[width][height][0] = temp->_red;
pixels[width][height][1] = temp->_green;
pixels[width][height][2] = temp->_blue;
}
}
temp = temp->next;
}
I haven't done any lightings yet since the flat colouring it doesn't work. I'm new to openGL so expect me to miss some common functions in the codes. Thanks in advance.
Edit:
I only have one sphere currently, but my output looks like: img1
I was expecting a bigger circle? Also, I had a printf for each intersection (if there is) and when I manually plot in a paper, it is a 4x5 pixel square. But there are 4 dots in the output.
Edit 2: I change the size of the sphere to: x = 512 y = 384 z = -21 r = 30, it gave me this:
img2
Again, I only have one sphere and there are 4 in the image. Also, there are holds between the lines?
If I change the z value to -20, now my output is all white (colour of sphere).
I use glDrawPixels(1024,768,GL_RGB,GL_FLOAT,pixels); to draw
I had a RBG output file, everything seems to be in the right place. but when I draw on the program, it is off.

Issues trying to scale up sine wave in c

Hopefully somebody can point out why this isnt working or where i may be going wrong. Im producing a sine wave by way of for loops in c. The ultimate aim is to produce a .ppm file displaying this. Im working on a 1 to 1 pixel ratio. My box is 128H*256W. The sine wave is displaying but due to the answer being produced in rads the result is a very small two pixel high "wave". I assume this is due to the rad values being between 1 and -1. This is my code. I tried just simply timesing by a greater number to increase the size of the y values in the hopes it would plot correctly but this does little or worse causes the applicattion to stop running. Any ideas very welcome.
for (x = 0; x < H; x++)
{
y =(int) H/2+ sin(x*(2*PI));
y = y * 50;
image[y][x][1] = 0;
image[y][x][2] = 255;
image[y][x][3] = 0;
}
EDIT: This is what is being produced in the .ppm file when opened via infraview. Also im #defining PI 3.141592653589793. Again is this possibly an area of issue.
first sine wave .ppm
I conject that y is an int.
Your sin value will be truncated to an integer; 0 for most cases, but very occasionally -1 or +1.
The fix is simple: use a floating point for y, and cast once you want to use it as an array index.
As y is commented to be an int and H appears to be an int constant, perform calculations as double first, then convert to int.
Use round to avoid truncations effect of simply casting a double to int.
y = (int) round(50*(sin(x*(2*PI)) + H/2.0));
Original code also scaled H/2 by 50. I think code may only want to scale the sin() and not the offset.
#define XOffset 0
#define YOffset (H/2.0)
#define XScale (2*PI)
#define YScale 50
y = (int) round(YScale*sin(x*XScale + XOffset) + YOffset);
Defensive programming tip: since y is calculated, insure it is in the valid index range before using it as an index.
// Assuming image` is a fixed sized array
#define Y_MAX (sizeof image/sizeof image[0] - 1)
if (y >= 0 && y <= Y_MAX) {
image[y][x][1] = 0;
image[y][x][2] = 255;
image[y][x][3] = 0;
}
y = y * 50, where y = H/2 (+ or - 1) gives you y around 25*H, which is out of bounds.
A closer approximation is this:
y = (int) ( H/2 + H/2 * sin(x*2*PI) )
which gives the extremes H/2 - H/2 = 0 and H/2 + H/2 = H, which is one too high. So, we scale not by H/2 but by (H-1)/2:
y = (int) ( H/2 + (H-1)/2 * sin(x*2*PI) )
which gives us an y-range 0 to H-1.
To have a bit more control over the period of the sine wave, let's write it like this:
sin( x/W * 2*PI )
Here, we divide x by W so that x/W itself will range from 0 to 1.
It is then scaled by 2*PI to produce a range from 0 to 2π. This will plot one period of the sine wave across the entire width. If we introduce a frequency factor f:
sin( f * x/W * 2*PI )
we can now say how many periods to draw, even fractions. For f=1 it will draw one period, f=2 two periods, and f=1 half a period.
Here's a small JS demo showing three values for f: 0.5 is red, 1 is green and 2 is white:
var c = document.getElementById('c'),
W = c.width,
H = c.height,
ctx = c.getContext('2d'),
image = ctx.getImageData(0,0,W,H);
for ( var i = 0; i < image.data.length; i +=4) {
image.data[i+0]=0;
image.data[i+1]=0;
image.data[i+2]=0;
image.data[i+3]=255;
}
function render(image,colidx,f) {
for ( var x = 0; x < W; x++ )
{
var y = H/2 - Math.round( H/2 * Math.sin(f*x/W*Math.PI*2) );
if ( y >=0 && y < H ) {
if ( colidx & 1 ) image.data[ 4*( W*y + x ) + 0] = 255;
if ( colidx & 2 ) image.data[ 4*( W*y + x ) + 1] = 255;
if ( colidx & 4 ) image.data[ 4*( W*y + x ) + 2] = 255;
}
}
}
render(image,1,0.5);
render(image,2,1);
render(image,7,2);
ctx.putImageData(image, 0,0);
canvas{ border: 1px solid red;}
<canvas id='c' width='256' height='128'></canvas>
The code then becomes:
float f = 1;
for (x = 0; x < W; x++)
{
y = (int) ( (H-1)/2 + (H-1)/2 * sin(f * x/W * 2*PI) );
image[y][x][0] = 0;
image[y][x][1] = 255;
image[y][x][2] = 0;
}

Resources