The answer http://answers.opencv.org/question/61... did not fully work for me, it seems that the data format can vary between devices.
The mentioned answer probably works if the pixel stride is 1, but all devices I have available produce images with pixel stride 2 (Nexus 5, Sony Xperia Z3).
The pixel stride means that the U and V planes are padded. The full buffer has the size of 2 bytes per pixel.
There is no way to directly convert this padded format with OpenCV currently, but it can be converted to YUV NV21 before converting it to a RGB/BGR image. (NV21 because it requires less work to convert to NV21 than to normal YUV420).
I do not know if a different pixel stride than 1 or 2 is possible (i guess not).
@TargetApi(21)
public Mat convertYuv420888ToMat(Image image, boolean isGreyOnly) {
int width = image.getWidth();
int height = image.getHeight();
Image.Plane yPlane = image.getPlanes()[0];
int ySize = yPlane.getBuffer().remaining();
if (isGreyOnly) {
byte[] data = new byte[ySize];
yPlane.getBuffer().get(data, 0, ySize);
Mat greyMat = new Mat(height, width, CvType.CV_8UC1);
greyMat.put(0, 0, data);
return greyMat;
}
Image.Plane uPlane = image.getPlanes()[1];
Image.Plane vPlane = image.getPlanes()[2];
// be aware that this size does not include the padding at the end, if there is any
// (e.g. if pixel stride is 2 the size is ySize / 2 - 1)
int uSize = uPlane.getBuffer().remaining();
int vSize = vPlane.getBuffer().remaining();
byte[] data = new byte[ySize + (ySize/2)];
yPlane.getBuffer().get(data, 0, ySize);
ByteBuffer ub = uPlane.getBuffer();
ByteBuffer vb = vPlane.getBuffer();
int uvPixelStride = uPlane.getPixelStride(); //stride guaranteed to be the same for u and v planes
if (uvPixelStride == 1) {
uPlane.getBuffer().get(data, ySize, uSize);
vPlane.getBuffer().get(data, ySize + uSize, vSize);
Mat yuvMat = new Mat(height + (height / 2), width, CvType.CV_8UC1);
yuvMat.put(0, 0, data);
Mat rgbMat = new Mat(height, width, CvType.CV_8UC3);
Imgproc.cvtColor(yuvMat, rgbMat, Imgproc.COLOR_YUV2RGB_I420, 3);
yuvMat.release();
return rgbMat;
}
// if pixel stride is 2 there is padding between each pixel
// converting it to NV21 by filling the gaps of the v plane with the u values
vb.get(data, ySize, vSize);
for (int i = 0; i < uSize; i += 2) {
data[ySize + i + 1] = ub.get(i);
}
Mat yuvMat = new Mat(height + (height / 2), width, CvType.CV_8UC1);
yuvMat.put(0, 0, data);
Mat rgbMat = new Mat(height, width, CvType.CV_8UC3);
Imgproc.cvtColor(yuvMat, rgbMat, Imgproc.COLOR_YUV2RGB_NV21, 3);
yuvMat.release();
return rgbMat;
}