YUV420 to RGB color conversion Error - c

I am converting an image in YUV420 format to RGB image in opencv but im getting an Orange colored image after conversion. I used following code to do that. Is there any problem in my code ??
int step = origImage->widthStep;
uchar *data = (uchar *)origImage->imageData;
int size = origImage->width * origImage->height;
IplImage* img1 = cvCreateImage(cvGetSize(origImage), IPL_DEPTH_8U, 3);
for (int i = 0; i<origImage->height; i++)
{
for (int j=0; j<origImage->width; j++)
{
float Y = data[i*step + j];
float U = data[ (int)(size + (i/2)*(step/2) + j/2) ];
float V = data[ (int)(size*1.25 + (i/2)*(step/2) + j/2)];
float R = Y + (int)(1.772f*V);
float G = Y - (int)(0.344f*V + 0.714f*U);
float B = Y + (int)(1.402f*U);
if (R < 0){ R = 0; } if (G < 0){ G = 0; } if (B < 0){ B = 0; }
if (R > 255 ){ R = 255; } if (G > 255) { G = 255; } if (B > 255) { B = 255; }
cvSet2D(img1, i, j,cvScalar(B,G,R));
}
}
origImage -> YUV image,
img1 -> RGB image,
http://upload.wikimedia.org/wikipedia/en/0/0d/Yuv420.svg
Is there any opencv function which can convert a pixel in YUV420 format to corresponding RGB pixel ? (not entire image)

I got answer by modifying the formula for calculating R G B values,
This code is working fine
int step = origImage->widthStep;
uchar *data = (uchar *)origImage->imageData;
int size = origImage->width * origImage->height;
IplImage* img1 = cvCreateImage(cvGetSize(origImage), IPL_DEPTH_8U, 3);
for (int i = 0; i<origImage->height; i++)
{
for (int j=0; j<origImage->width; j++)
{
float Y = data[i*step + j];
float U = data[ (int)(size + (i/2)*(step/2) + j/2) ];
float V = data[ (int)(size*1.25 + (i/2)*(step/2) + j/2)];
float R = Y + 1.402 * (V - 128);
float G = Y - 0.344 * (U - 128) - 0.714 * (V - 128);
float B = Y + 1.772 * (U - 128);
if (R < 0){ R = 0; } if (G < 0){ G = 0; } if (B < 0){ B = 0; }
if (R > 255 ){ R = 255; } if (G > 255) { G = 255; } if (B > 255) { B = 255; }
cvSet2D(img1, i, j,cvScalar(B,G,R));
}
}

the 1st problem is using the outdated c-api (it's dead & gone. please use c++ instead).
the 2nd problem is writing your own (slow and error prone) pixel loops
why not use :
cvtColor(crs,dst, CV_YUV2BGR); // or CV_YUV2BGR_I420
instead ?

Related

smoothening image boundaries for NxN kernel size

In my open source project ( https://github.com/mmj-the-fighter/GraphicsLabFramework ) i am trying to add a image smoothening box filter for NxN kernel size. I have already implemented the algorithm for 3x3 kernel size. As you can see from the source code below I am not processing the image for edges. Using this logic, for a 5x5 kernel size I have to skip two rows or columns from top, right, bottom and left of the image. So the edges will not be blurred. Is there any other solution.
Here is the code:
/*applying a box filter of size 3x3*/
void blur_image(unsigned char *img, int width, int height)
{
int n = width * height;
int i, j;
int r, g, b;
int x, y;
float v = 1.0 / 9.0;
float kernel[3][3] =
{
{ v, v, v },
{ v, v, v },
{ v, v, v }
};
unsigned char* resimage = (unsigned char *)malloc(width * height * 4 * sizeof(unsigned char));
memcpy(resimage, img, width*height * 4);
for (x = 1; x < width - 1; ++x) {
for (y = 1; y < height - 1; ++y) {
float bs = 0.0;
float gs = 0.0;
float rs = 0.0;
for (i = -1; i <= 1; ++i) {
for (j = -1; j <= 1; ++j){
float weight = (float)kernel[i + 1][j + 1];
unsigned char* buffer = img + width * 4 * (y + j) + (x + i) * 4;
bs += weight * *buffer;
gs += weight * *(buffer + 1);
rs += weight * *(buffer + 2);
}
}
unsigned char* outbuffer = resimage + width * 4 * y + x * 4;
*outbuffer = bs;
*(outbuffer + 1) = gs;
*(outbuffer + 2) = rs;
*(outbuffer + 3) = 255;
}
}
memcpy(img, resimage, width*height * 4);
free(resimage);
}

Peculiar problem regarding convolution in PPM format

After debugging, trying different image softwares (xdg, gimp) I persist to have a bug which throws me off completely.
Problem is about convolution in PPM format, for images different in aspect ratio, I'm using 1500x1000px image, where mask of {0,0,0, 0,1,0, 0,0,0} works just fine (it's just copying image), however for mask where first or last row is different than 0 eg. {0,1,0, 0,0,0, 0,0,0} image is moved by 1/3 of its size rightwards. I find it peculiar, because as far as I know, I do not have an overflow or any pointer arithmetic that might cause this problem.
I've narrowed it down to the kernel of convolution. Afaik I do not have any problems saving, reading image, after running i_convolution it just moves image by predefined value?.
void i_convolution(unsigned int **in, unsigned int ***out,
int y_max, int x_max, int kernel_size)
{
int kernel_sum = 0;
for(int i = 0; i < kernel_size; i++)
{
for(int j = 0; j < kernel_size; j++)
{
kernel_sum += kernel[i * kernel_size + j];
}
}
printf("kernel sum = %d\n", kernel_sum);
for (int i = 1; i < y_max - 1; i++)
{
for (int j = 1; j < x_max - 1; j++)
{
int r = 0;
int g = 0;
int b = 0;
for (int y_conv = -1; y_conv <= 1; y_conv++)
{
for (int x_conv = -1; x_conv <= 1; x_conv++)
{
int y_index = i + y_conv;
int x_index = j + x_conv;
unsigned char rval = (unsigned char)(in[y_index][x_index] & 0xff);
unsigned char gval = (unsigned char)((in[y_index][x_index] & 0xff00) >> 8);
unsigned char bval = (unsigned char)((in[y_index][x_index] & 0xff0000) >> 16);
int kernel_val = kernel[(y_conv + 1)*kernel_size + (x_conv + 1)];
r += (int)(rval * kernel_val);
g += (int)(gval * kernel_val);
b += (int)(bval * kernel_val);
}
}
r /= kernel_sum;//median filtration
g /= kernel_sum;//median filtration
b /= kernel_sum;//median filtration
// b = abs(b);
if (r > 255) r = 255;
else if(r < 0) r = 0;
if (g > 255) g = 255;
else if(g < 0) g = 0;
if (b > 255) b = 255;
else if(b < 0) b = 0;
unsigned int val;
val = 0;
val |= b & 0xff;
val <<= 8;
val |= g & 0xff;
val <<= 8;
val |= r & 0xff;
(*out)[i][j] = val;
}
}
}
let's take kernel {0, 1, 0, 0, 0, 0,
result are like this, with left being original, right after convolution
https://i.imgur.com/rzXKjUY.png
I will be thankful for any help.
Best regards.
I mark it as solved, because there was a problem with me misinterpreting PPM format height and width, which caused this behaviour, swapping y with x (and allocating memory as such) solves it!

how to produce -400,-200,-400 in sequential order

I am trying to write a for loop in the second version which produce the same result in the original code but i am not sure how to get -400,-200,-400 in sequential order.
original code:
p->m_p[0] = randFloat(-400.0f, 400.0);
p->m_p[1] = randFloat(-200.0f, 200.0);
p->m_p[2] = randFloat(-400.0f, 400.0);
second version:
float x = -800;
float y = 800;
for(int i = 0; i < 4; i++)
{
plNew->m_fPosition[i] = randFloat(x / 2,y / 2);
}
If you need it to work in C (or in C++ before C++11), this would work:
#define NUMBER_OF_VALUES 3
float bounds[NUMBER_OF_VALUES] = { 400.0f, 200.0f, 400.0f };
for (int i = 0; i < NUMBER_OF_VALUES; i++)
{
plNew->m_fPosition[i] = randFloat(-bounds[i], bounds[i]);
}
You can extend this to make NUMBER_OF_VALUES be 4 or a larger number as long as you initialize all the members of bounds[NUMBER_OF_VALUES] with the desired constants.
A nice feature of this is that the sequence of constants can be anything you like,
not limited to alternating 400, 200, 400 or any other regular sequence.
Something like this?
for (int i = 0; i < 4; i++) {
float x, y;
if (i % 2) {
x = -400.0f;
y = 400.0f;
} else {
x = -200.0f;
y = 200.0f;
}
p->m_p[i] = randFloat(x, y);
}
What about?
float x = -800;
float y = 800;
for(int i = 0; i < 4; i++)
{
float z = 2.0 * ((float)((i + 1) % 2 + 1));
plNew->m_fPosition[i] = randFloat(x / z, y / z);
}
I would suggest keeping it simple, using something like the following :
const float arr3[] = {-400.0f, -200.0f, -400.0f};
for(int i = 0; i < 3; i++)
{
plNew->m_fPosition[i] = arr3[i];
}
float x = -400;
float y = 400;
for(int i = 0; i < 3; i++)
{
plNew->m_fPosition[i] = randFloat(x / (1 + (i & 1)), y / (1 + (i & 1)));
}

simple blur of RGB raw image

I'm trying to make a simple blur effect in c. I have an image loaded in an 512*512 array of RGB pixels and I'm using 3x3 kernel to blur that image
here is the kernel
float matrix[9] = {1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f, 1.0f/9.0f};
and here is the code that is doing the bluring
void useBlur(){
for(int i = 0; i < ARRAY_SIZE; i++){
float r = 0;
float g = 0;
float b = 0;
int m, n;
for(int y = -1, m = 0; y <= 1; y++, m++){
for(int z = -1, n = 0; z <= 1; z++, n++){
r += (float)orig_image[i + 512 * y + z].r * (float)matrix[m*3+n];
g += (float)orig_image[i + 512 * y + z].g * (float)matrix[m*3+n];
b += (float)orig_image[i + 512 * y + z].b * (float)matrix[m*3+n];
}
}
image[i].r = r;
image[i].g = g;
image[i].b = b;
}
}
I'm not sure what is wrong with that code, but it is producing result:
Any ideas why the colors are wrong? And how to fix it?
EDIT:
fixed matrix[7] from 9.0/9.0 to 1.0/9.0 and uploaded new image
I tried your code with some changes, the images I have are BGRA and I am using opencv for my image container but the structure of the code is the same and it works perfectly.
for (int i = 2*image.cols; i < image.rows*image.cols-2*image.cols; i++)
{
float r = 0;
float g = 0;
float b = 0;
for (int y = -1; y <=1; y++)
{
for (int z = -1; z <=1; z++)
{
b += (float)image.at<cv::Vec4b>(i + image.cols*y+z)(0)*(1.0/9);
g += (float)image.at<cv::Vec4b>(i + image.cols*y+z)(1)*(1.0/9);
r += (float)image.at<cv::Vec4b>(i + image.cols*y+z)(2)*(1.0/9);
}
}
image.at<cv::Vec4b>(i)(0) = b;
image.at<cv::Vec4b>(i)(1) = g;
image.at<cv::Vec4b>(i)(2) = r;
}
I would assume that means the problem is either not in the code you posted, or somehow the opencv cv::Mat class is handling something your image container is not. The obvious candidate for that would be that you seem to be implicitly converting from float to uchar at the end of your code. Could you test running this loop over your image but without modifying it? It could look like this, or any number of other ways. This is ugly, but a quick conversion.
void useBlur(){
for(int i = 0; i < ARRAY_SIZE; i++){
float r = 0;
float g = 0;
float b = 0;
int m, n;
for(int y = -1, m = 0; y <= 1; y++, m++){
for(int z = -1, n = 0; z <= 1; z++, n++){
if(y == 0 && z == 0)
{
r += (float)orig_image[i + 512 * y + z].r * (float)matrix[m*3+n]*9;
g += (float)orig_image[i + 512 * y + z].g * (float)matrix[m*3+n]*9;
b += (float)orig_image[i + 512 * y + z].b * (float)matrix[m*3+n]*9;
}
}
}
image[i].r = r;
image[i].g = g;
image[i].b = b;
}
}
Theoretically nothing should change about the image when you do that, so if the image is changing it is because of some conversion error. I admit the theory is unlikely but the structure of your code seems sound so I don't know what else to suggest.

Triangulation algorithm

I've decided to create a simple demo, dividing a polygon into triangle set. Here what i've got so far:
A sequential vertex list is given (P1) forming polygon edges (polygon is not convex in most cases); a triangle set is needed
Loop through all the vertices within the polygon P1 and find the one (v), which will satisfy next clauses:
remove v from polygon and save the new one to P2 previous vertex to v
connected to its next one form a
line which do not cross any of P2
edges
v is not inside P2
If these are satisfied, we can replace P1 with (P2 + triangle( prev(v), v, next(v)) ) and repeat this action until P1 contains more than 3 vertices.
So, the questions are: is this algorithm correct and how it could be implemented using C / C++ using the most obvious and simple way?
I think you're describing the ear clipping method. There's code for that method at http://cs.smith.edu/~orourke/books/ftp.html ; it's the code described in the book Computational Geometry in C.
Seems that i'm done with this algorithm implementation. Please, verify it someone. Thanks!
typedef struct Point
{
float x, y;
};
class MooPolygon
{
private:
vector<Point> points;
int isVertexEar(int n, const vector<Point> &p)
{
return (isVertexInsideNewPoly(n, p) && !isEdgeIntersect(n, p));
}
int isEdgeIntersect(int n, const vector<Point> &p)
{
Point v = p[n];
vector<Point> a;
for (size_t i = 0; i < p.size(); i++)
if (i != n)
a.push_back(p[i]);
int c = 0, cnt = a.size(), prev = (cnt + (n - 1)) % cnt, next = n % cnt;
Point v1 = a[prev], v2 = a[next];
for (size_t i = 0, j = cnt - 1; i < cnt; j = i++)
{
if (prev == i || prev == j || next == i || next == j)
continue;
Point v4 = a[j], v3 = a[i];
float denominator = ((v4.y - v3.y) * (v2.x - v1.x)) - ((v4.x - v3.x) * (v2.y - v1.y));
if (!denominator)
continue;
float ua = (((v4.x - v3.x) * (v1.y - v3.y)) - ((v4.y - v3.y) * (v1.x - v3.x))) / denominator;
float ub = (((v2.x - v1.x) * (v1.y - v3.y)) - ((v2.y - v1.y) * (v1.x - v3.x))) / denominator;
//float x = v1.x + (ua * (v2.x - v1.x)), y = v1.y + (ua * (v2.y - v1.y));
if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1)
{
c = 1;
break;
}
}
return c;
}
int isVertexInsideNewPoly(int n, const vector<Point> &p)
{
Point v = p[n];
vector<Point> a;
for (size_t i = 0; i < p.size(); i++)
if (i != n)
a.push_back(p[i]);
int c = 1;
for (size_t i = 0, j = a.size() - 1; i < a.size(); j = i++)
{
if ((((a[i].y <= v.y) && (v.y < a[j].y)) || ((a[j].y <= v.y) && (v.y < a[i].y))) && (v.x > (a[j].x - a[i].x) * (v.y - a[i].y) / (a[j].y - a[i].y) + a[i].x))
c = !c;
}
return c;
}
float dist(Point a, Point b)
{
return sqrt( ((a.x - b.x) * (a.x - b.x)) + (((a.y - b.y) * (a.y - b.y))) );
}
public:
void push(const Point &p)
{
for (size_t i = 0; i < points.size(); i++)
{
if (dist(points[i], p) < 7.f)
{
points.push_back(points[i]);
return;
}
}
points.push_back(p);
}
void pop()
{
if (points.size() > 0)
points.pop_back();
}
void clear()
{
points.clear();
}
Point v(int index)
{
return points[index];
}
size_t size()
{
return points.size();
}
void triangulate()
{
vector<Point> a;
for (size_t i = 0; i < points.size(); i++)
{
a.push_back(points[i]);
}
points.clear();
for (size_t t = a.size() - 1, i = 0, j = 1; i < a.size(); t = i++, j = (i + 1) % a.size())
{
if (a.size() == 3)
{
points.push_back(a[0]);
points.push_back(a[1]);
points.push_back(a[2]);
break;
}
if (isVertexEar(i, a))
{
points.push_back(a[t]);
points.push_back(a[i]);
points.push_back(a[j]);
a.erase(a.begin() + i, a.begin() + i + 1);
t = a.size() - 1;
i = 0;
j = 1;
}
}
}
};
The code has an error on the line below. The line is in the for loop in the push() function of your class:
points.push_back(points[i]);
You are not passing the pushed Point, but an empty element of the vector itself. I changed the line to
points.push_back(p);
and it worked.

Resources