Android Camera2 YUV to RGB conversion turns out green?
So I'm getting Image objects from Android's Camera2 API, then I convert them to OpenCV Mat objects via their byte buffers. The YUV_420_888 format is what I set as the output of the camera as recommended by the docs, but when I try converting the Mat from YUV to RGB, all it shows is green.
Following the answers from this thread, this is how I convert the Mat:
Image image = reader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
Mat mat = new Mat(image.getHeight()+image.getHeight()/2, image.getWidth(), CvType.CV_8UC1);
mat.put(0, 0, bytes);
Mat rgb = new Mat(image.getHeight(), image.getWidth(), CvType.CV_8UC4);
Imgproc.cvtColor(mat, rgb, Imgproc.COLOR_YUV420sp2BGR, 4);
After these lines, all I did next was use imwrite to write the mats to disk. For reference, here's some sample images resulting from the writes:
YUV - http://i.imgur.com/qm765AZ.jpg (straight from the Camera2 API, no processing yet)
RGB - http://i.imgur.com/FzLx2Cc.jpg (the exact same image, but converted from YUV to RGB)
Any insights as to why the RGB image looks the way it does? I've also tried a whole lot of other conversion options besides COLOR_YUV420sp2BGR, but they all seem to have the same effect, which is a green image. Thank you in advance!
EDIT: As has been pointed out in the comments, it seems I need to use all 3 planes of the YUV image, and not just the first one. I know how to convert each plane into a byte array, and now I have 3 byte arrays each representing a plane, but my question is now how do I create a Mat from these 3 byte arrays? The put() method I'm familiar with only accepts a single byte array. Do I concatenate or combine them somehow?
I guess (but just a guess) the problem is here
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
You're just using the 0-plane of the image, but you have 3 planes: Y, U and V.Thanks for the reply! That part of the code I actually based it from the official Camera2 example found here. It does initiate it with the 0-plane, but I just assume buffer.getBytes() does the rest of the job as when I run this example, the results are fully colored. Though the difference is the format was set to JPEG, not YUV. So are you saying that since I changed the format from JPEG to YUV, I must retrieve not just the 0-plane?
Also, if I do indeed get byte arrays for the 3 planes, how would I create a Mat from that? From the examples I saw, using the Mat's put() method accepts a single byte array. Now that I have 3 byte arrays, how do I combine them into a single one in order to pass them to put() ? Or should I create Mats using a different method or constructor? I browsed the docs for the Mat object, but I don't see any method or constructor which accepts 3 byte arrays simultaneously?
I'm not familiarized with the API, so I can't really help, but it seems unnatural to me to use just one single plane when you're working with the YUV color space. Anyway I may be fully wrong.
Another thing, why do you have a 4 here?
Imgproc.cvtColor(mat, rgb, Imgproc.COLOR_YUV420sp2BGR, 4);
Doesn't the 4 indicate the number of channels in the ouput image? If so, you only have 3 (you can even omit the number I think)Alright, thank you for your insights, they're very helpful! Anyway, yes I think you're right, that should be 3 and not 4.
OK, after a new look at your code I've spotted another important thing: in the definition of mat, there's this
CvType.CV_8UC1
when you should haveCvType.CV_8UC3
as you're storing 3 channels (same on the output mat)