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
isPIX_FMT_YUV420P
, set the format toPIX_FMT_YUVJ420P
. - If
src->format
isPIX_FMT_YUV422P
, set the format toPIX_FMT_YUVJ422P
. - If
src->format
isPIX_FMT_YUV444P
, set the format toPIX_FMT_YUVJ444P
. - If
PIX_FMT_YUV440P
, usePIX_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
Is There Any Function Equivalent to Matlab's Imadjust in Opencv with C++
Returning VS. Using a Reference Parameter
What Is a Possible Workaround for Object Slicing in C++
Why Default Return Value of Main Is 0 and Not Exit_Success
C++ Error on Ms Visual Studio: "Windows Has Triggered a Breakpoint in Javaw.Exe"
How to Use Ms Code Coverage Tool in Command Line
How to Reproduce Tcp Protocol 3-Way Handshake with Raw Sockets Correctly
Cudamalloc of a Structure and an Element of Same Structure
What Is Double Evaluation and Why Should It Be Avoided
How Would You Benchmark the Performance of a Function
Why Derive from a Concrete Class Is a Poor Design
How to Add Code at the Entry of Every Function
C or C++ Bigint Library on Microsoft Windows
How to Assign Two Dimensional Array to **Pointer