How to Perform Rgb->Yuv Conversion in C/C++

How to convert RGB - YUV - RGB (both ways)

Yes, invertible transformations exist.

equasys GmbH posted invertible transformations from RGB to YUV, YCbCr, and YPbPr, along with explanations of which situation each is appropriate for, what this clamping is really about, and links to references. (Like a good SO answer.)

For my own application (jpg images, not analog voltages) YCbCr was appropriate, so I wrote code for those two transformations. Indeed, there-and-back-again values differed by less than 1 part in 256, for many images; and the before-and-after images were visually indistinguishable.

PIL's colour space conversion YCbCr -> RGB gets credit for mentioning equasys's web page.

Other answers, that could doubtfully improve on equasys's precision and concision:

  • https://code.google.com/p/imagestack/ includes rgb_to_x and x_to_rgb
    functions, but I didn't try to compile and test them.

  • Cory Nelson's answer links to code with similar functions, but it says that
    inversion's not possible in general, contradicting equasys.

  • The source code of FFmpeg, OpenCV, VLFeat, or ImageMagick.

2019 Edit: Here's the C code from github, mentioned in my comment.

void YUVfromRGB(double& Y, double& U, double& V, const double R, const double G, const double B)
{
Y = 0.257 * R + 0.504 * G + 0.098 * B + 16;
U = -0.148 * R - 0.291 * G + 0.439 * B + 128;
V = 0.439 * R - 0.368 * G - 0.071 * B + 128;
}
void RGBfromYUV(double& R, double& G, double& B, double Y, double U, double V)
{
Y -= 16;
U -= 128;
V -= 128;
R = 1.164 * Y + 1.596 * V;
G = 1.164 * Y - 0.392 * U - 0.813 * V;
B = 1.164 * Y + 2.017 * U;
}

How to enhance this YUV420P to RGB conversion in C/C++?

Is it just me, but but shouldn't you be reading from the yuv array and writing to the rgbData array? You actually have it reversed in your implementation.

There's not need to invoke ceil on an integer expression such as i/4. And when you implement an image processing route, invoking a function call on every pixel is just going to kill performance (been there, done that). Maybe the compiler can optimize it out, but why take that chance.

So change this:

    Cr = rgbData[CrBase + ceil(i/4)]  - 128;
Cb = rgbData[CbBase + ceil(i/4)] - 128;

To this:

    Cr = rgbData[CrBase + i/4]  - 128;
Cb = rgbData[CbBase + i/4] - 128;

The only other thing to be wary of is that you may want to clamp R, G, and B to be in the 8-bit byte range before assigning back to the yuv array. Those math equations can produce results < 0 and > 255.

Another micro-optimization is to declare all your variables within the for-loop block so the compiler has more hints about optimizing on it as temporaries. And declaring some of your other constants as const May I suggest:

JNIEXPORT void JNICALL Java_com_example_mediacodecdecoderexample_YuvToRgb_YUVtoRBGA2(JNIEnv * env, jobject obj, jbyteArray yuv420sp, jint width, jint height, jbyteArray rgbOut)
{
//ITU-R BT.601 conversion
//
// R = 1.164*(Y-16)+1.596*(Cr-128)
// G = 1.164*(Y-16)-0.392*(Cb-128)-0.813*(Cr-128)
// B = 1.164*(Y-16)+2.017*(Cb-128)
//
const int size = width * height;
//After width*height luminance values we have the Cr values

const size_t CrBase = size;
//After width*height luminance values + width*height/4 we have the Cb values

const size_t CbBase = size + width*height/4;

jbyte *rgbData = (jbyte*) ((*env)->GetPrimitiveArrayCritical(env, rgbOut, 0));
jbyte* yuv= (jbyte*) (*env)->GetPrimitiveArrayCritical(env, yuv420sp, 0);

for (int i=0; i<size; i++) {
int Y = yuv[i] - 16;
int Cr = yuv[CrBase + i/4] - 128;
int Cb = yuv[CbBase + i/4] - 128;

int R = 1.164*Y+1.596*Cr;
int G = 1.164*Y-0.392*Cb-0.813*Cr;
int B = 1.164*Y+2.017*Cb;

rgbData[i*3] = (R > 255) ? 255 : ((R < 0) ? 0 : R);
rgbData[i*3+1] = (G > 255) ? 255 : ((G < 0) ? 0 : G);
rgbData[i*3+2] = (B > 255) ? 255 : ((B < 0) ? 0 : B);
}

(*env)->ReleasePrimitiveArrayCritical(env, rgbOut, rgbData, 0);
(*env)->ReleasePrimitiveArrayCritical(env, yuv420sp, yuv, 0);
}

Then the only left to do is just to compile with max optimizations on. The compiler will take care of the rest.

After that, investigating SIMD optimizations, which some compilers offer as a compiler switch (or enabled via pragma).

Convert from YUV to RGB in c++ (android-ndk)

Conversion from YUYV to RGB in C++:

unsigned char* rgb_image = new unsigned char[width * height * 3]; //width and height of the image to be converted

int y;
int cr;
int cb;

double r;
double g;
double b;

for (int i = 0, j = 0; i < width * height * 3; i+=6 j+=4) {
//first pixel
y = yuyv_image[j];
cb = yuyv_image[j+1];
cr = yuyv_image[j+3];

r = y + (1.4065 * (cr - 128));
g = y - (0.3455 * (cb - 128)) - (0.7169 * (cr - 128));
b = y + (1.7790 * (cb - 128));

//This prevents colour distortions in your rgb image
if (r < 0) r = 0;
else if (r > 255) r = 255;
if (g < 0) g = 0;
else if (g > 255) g = 255;
if (b < 0) b = 0;
else if (b > 255) b = 255;

rgb_image[i] = (unsigned char)r;
rgb_image[i+1] = (unsigned char)g;
rgb_image[i+2] = (unsigned char)b;

//second pixel
y = yuyv_image[j+2];
cb = yuyv_image[j+1];
cr = yuyv_image[j+3];

r = y + (1.4065 * (cr - 128));
g = y - (0.3455 * (cb - 128)) - (0.7169 * (cr - 128));
b = y + (1.7790 * (cb - 128));

if (r < 0) r = 0;
else if (r > 255) r = 255;
if (g < 0) g = 0;
else if (g > 255) g = 255;
if (b < 0) b = 0;
else if (b > 255) b = 255;

rgb_image[i+3] = (unsigned char)r;
rgb_image[i+4] = (unsigned char)g;
rgb_image[i+5] = (unsigned char)b;
}

This method assumes that your yuyv_image is an unsigned char* as well.

More information on YUYV can be found here

And for more clarification on YUYV --> RGB check out this

Converting YUV into BGR or RGB in OpenCV

It looks to me like you're decoding a YUV422 stream as YUV444. Try this modification to the code you provided:

for(int i = 0, j=0; i < 1280 * 720 * 3; i+=6, j+=4)
{
m_RGB->imageData[i] = pData[j] + pData[j+3]*((1 - 0.299)/0.615);
m_RGB->imageData[i+1] = pData[j] - pData[j+1]*((0.114*(1-0.114))/(0.436*0.587)) - pData[j+3]*((0.299*(1 - 0.299))/(0.615*0.587));
m_RGB->imageData[i+2] = pData[j] + pData[j+1]*((1 - 0.114)/0.436);
m_RGB->imageData[i+3] = pData[j+2] + pData[j+3]*((1 - 0.299)/0.615);
m_RGB->imageData[i+4] = pData[j+2] - pData[j+1]*((0.114*(1-0.114))/(0.436*0.587)) - pData[j+3]*((0.299*(1 - 0.299))/(0.615*0.587));
m_RGB->imageData[i+5] = pData[j+2] + pData[j+1]*((1 - 0.114)/0.436);
}

I'm not sure you've got your constants correct, but at worst your colors will be off - the image should be recognizable.

sws_scale, YUV to RGB conversion

You may the source format to "full scale" YUVJ.

As far as I know, sws_scale has no option for selecting Studio RGB as output format.

Changing the input format is the best solution I can think of.

The color conversion formula of "JPEG: YUV -> RGB" is the same as the formula in your post.

Examples for setting the source format:

  • If src->format is PIX_FMT_YUV420P, set the format to PIX_FMT_YUVJ420P.
  • If src->format is PIX_FMT_YUV422P, set the format to PIX_FMT_YUVJ422P.
  • If src->format is PIX_FMT_YUV444P, set the format to PIX_FMT_YUVJ444P.
  • If PIX_FMT_YUV440P, use PIX_FMT_YUVJ440P.

I know the solution is not covering all the possibilists, and there might be some output pixels exceeding the range of [16, 235], so it's not the most general solution...

Converting YUV-RGB(Image processing)-YUV during onPreviewFrame in android?

You can use ColorHelper library for this:

using ColorHelper;

YUV yuv = new YUV(0.1, 0.1, 0.2);
RGB rgb = ColorConverter.YuvToRgb(yuv);

Links:

  • Github
  • Nuget

Function to convert YCbCr to RGB?

The problem comes that nearly everybody confuses YCbCr YUV and YPbPr. So literature you can find is often crappy. First you have to know if you really have YCbCr or if someone lies to you :-).

  • YUV coded data comes from analog sources (PAL video decoder, S-Video, ...)
  • YPbPr coded data also comes from analog sources but produces better color results as YUV (Component Video)
  • YCbCr coded data comes from digital sources (DVB, HDMI, ...)

YPbPr and YCbCr are related. Here are the right formulae:

https://web.archive.org/web/20180421030430/http://www.equasys.de/colorconversion.html

(the archive.org has been added to fix the old, broken link).



Related Topics



Leave a reply



Submit