Last active
February 4, 2024 17:06
-
-
Save crearo/2d4b007f0e7e32ffa56fc0974e227a33 to your computer and use it in GitHub Desktop.
YUV420P to RGBA8888 color conversion - with explanation as to why you may fail trying this in java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* It took me a whole 6 hours to finally get the color conversion right. | |
* I have a fairly good understanding of the various color formats (YUV420P, SP, 422, etc etc), | |
* and how to access individual Y, U, and V components. | |
* I however struggled because of a very simple yet hair-pulling gotcha. | |
* All primitives in Java are signed! If you come from a Python-like world where 0xFF prints 255, | |
* you see yourself struggle just the same. I am however embarrassed at spending 6 hours on this. | |
* | |
* @author rish | |
*/ | |
public class ColorConversions { | |
/** | |
* Converts yuv420p to rgba 8888 | |
* The tricky bit here is: | |
* Java has signed primitives only. This means 0xFF is -1, and not 0x255, as you'd expect | |
* in an unsigned python like world. | |
* <p> | |
* And-ing with 0xFF (0xFF is an int, not a byte ;)) converts the individual Y, U, V, bits | |
* from an signed byte to a signed int, but for our purposes behaves as an unsigned byte. | |
* This means, (byte)0xFF=-1, whereas 0xFF & (byte)0xFF = 255. | |
**/ | |
public void yuv420pToRGBA8888(int[] out, byte[] yuv, int width, int height) { | |
if (out.length < width * height) { | |
throw new IllegalArgumentException("Size of out must be " + width * height); | |
} | |
if (yuv.length < width * height * 3.0 / 2) { | |
throw new IllegalArgumentException("Size of yuv must be " + width * height * 3.0 / 2); | |
} | |
int size = width * height; | |
for (int j = 0; j < height; j++) { | |
for (int i = 0; i < width; i++) { | |
/*accessing YUV420P elements*/ | |
int indexY = j * width + i; | |
int indexU = (size + (j / 2) * (width / 2) + i / 2); | |
int indexV = (int) (size * 1.25 + (j / 2) * (width / 2) + i / 2); | |
// todo; this conversion to int and then later back to int really isn't required. | |
// There's room for better work here. | |
int Y = 0xFF & yuv[indexY]; | |
int U = 0xFF & yuv[indexU]; | |
int V = 0xFF & yuv[indexV]; | |
/*constants picked up from http://www.fourcc.org/fccyvrgb.php*/ | |
int R = (int) (Y + 1.402f * (V - 128)); | |
int G = (int) (Y - 0.344f * (U - 128) - 0.714f * (V - 128)); | |
int B = (int) (Y + 1.772f * (U - 128)); | |
/*clamping values*/ | |
R = R < 0 ? 0 : R; | |
G = G < 0 ? 0 : G; | |
B = B < 0 ? 0 : B; | |
R = R > 255 ? 255 : R; | |
G = G > 255 ? 255 : G; | |
B = B > 255 ? 255 : B; | |
out[width * j + i] = 0xff000000 + (R << 16) + (G << 8) + B; | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sout(0xFF); // outputs 255 | |
sout(((byte)(0xFF))); // outputs -1 | |
sout((0xFF & (byte)0xFF)); // outputs 255 |
can you show how to convert RGBA8888 to YUV420
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thanks alot!