use of android.media.Image.Plane in project platform_frameworks_base by android.
the class ImageUtils method imageCopy.
/**
* <p>
* Copy source image data to destination Image.
* </p>
* <p>
* Only support the copy between two non-{@link ImageFormat#PRIVATE PRIVATE} format
* images with same properties (format, size, etc.). The data from the
* source image will be copied to the byteBuffers from the destination Image
* starting from position zero, and the destination image will be rewound to
* zero after copy is done.
* </p>
*
* @param src The source image to be copied from.
* @param dst The destination image to be copied to.
* @throws IllegalArgumentException If the source and destination images
* have different format, or one of the images is not copyable.
*/
public static void imageCopy(Image src, Image dst) {
if (src == null || dst == null) {
throw new IllegalArgumentException("Images should be non-null");
}
if (src.getFormat() != dst.getFormat()) {
throw new IllegalArgumentException("Src and dst images should have the same format");
}
if (src.getFormat() == ImageFormat.PRIVATE || dst.getFormat() == ImageFormat.PRIVATE) {
throw new IllegalArgumentException("PRIVATE format images are not copyable");
}
if (src.getFormat() == ImageFormat.RAW_PRIVATE) {
throw new IllegalArgumentException("Copy of RAW_OPAQUE format has not been implemented");
}
if (!(dst.getOwner() instanceof ImageWriter)) {
throw new IllegalArgumentException("Destination image is not from ImageWriter. Only" + " the images from ImageWriter are writable");
}
Size srcSize = new Size(src.getWidth(), src.getHeight());
Size dstSize = new Size(dst.getWidth(), dst.getHeight());
if (!srcSize.equals(dstSize)) {
throw new IllegalArgumentException("source image size " + srcSize + " is different" + " with " + "destination image size " + dstSize);
}
Plane[] srcPlanes = src.getPlanes();
Plane[] dstPlanes = dst.getPlanes();
ByteBuffer srcBuffer = null;
ByteBuffer dstBuffer = null;
for (int i = 0; i < srcPlanes.length; i++) {
int srcRowStride = srcPlanes[i].getRowStride();
int dstRowStride = dstPlanes[i].getRowStride();
srcBuffer = srcPlanes[i].getBuffer();
dstBuffer = dstPlanes[i].getBuffer();
if (!(srcBuffer.isDirect() && dstBuffer.isDirect())) {
throw new IllegalArgumentException("Source and destination ByteBuffers must be" + " direct byteBuffer!");
}
if (srcPlanes[i].getPixelStride() != dstPlanes[i].getPixelStride()) {
throw new IllegalArgumentException("Source plane image pixel stride " + srcPlanes[i].getPixelStride() + " must be same as destination image pixel stride " + dstPlanes[i].getPixelStride());
}
int srcPos = srcBuffer.position();
srcBuffer.rewind();
dstBuffer.rewind();
if (srcRowStride == dstRowStride) {
// Fast path, just copy the content if the byteBuffer all together.
dstBuffer.put(srcBuffer);
} else {
// Source and destination images may have different alignment requirements,
// therefore may have different strides. Copy row by row for such case.
int srcOffset = srcBuffer.position();
int dstOffset = dstBuffer.position();
Size effectivePlaneSize = getEffectivePlaneSizeForImage(src, i);
int srcByteCount = effectivePlaneSize.getWidth() * srcPlanes[i].getPixelStride();
for (int row = 0; row < effectivePlaneSize.getHeight(); row++) {
if (row == effectivePlaneSize.getHeight() - 1) {
// Special case for NV21 backed YUV420_888: need handle the last row
// carefully to avoid memory corruption. Check if we have enough bytes to
// copy.
int remainingBytes = srcBuffer.remaining() - srcOffset;
if (srcByteCount > remainingBytes) {
srcByteCount = remainingBytes;
}
}
directByteBufferCopy(srcBuffer, srcOffset, dstBuffer, dstOffset, srcByteCount);
srcOffset += srcRowStride;
dstOffset += dstRowStride;
}
}
srcBuffer.position(srcPos);
dstBuffer.rewind();
}
}
use of android.media.Image.Plane in project platform_frameworks_base by android.
the class CameraTestUtils method getDataFromImage.
/**
* <p>Read data from all planes of an Image into a contiguous unpadded, unpacked
* 1-D linear byte array, such that it can be write into disk, or accessed by
* software conveniently. It supports YUV_420_888/NV21/YV12 and JPEG input
* Image format.</p>
*
* <p>For YUV_420_888/NV21/YV12/Y8/Y16, it returns a byte array that contains
* the Y plane data first, followed by U(Cb), V(Cr) planes if there is any
* (xstride = width, ystride = height for chroma and luma components).</p>
*
* <p>For JPEG, it returns a 1-D byte array contains a complete JPEG image.</p>
*/
public static byte[] getDataFromImage(Image image) {
assertNotNull("Invalid image:", image);
int format = image.getFormat();
int width = image.getWidth();
int height = image.getHeight();
int rowStride, pixelStride;
byte[] data = null;
// Read image data
Plane[] planes = image.getPlanes();
assertTrue("Fail to get image planes", planes != null && planes.length > 0);
// Check image validity
checkAndroidImageFormat(image);
ByteBuffer buffer = null;
// Same goes for DEPTH_POINT_CLOUD
if (format == ImageFormat.JPEG || format == ImageFormat.DEPTH_POINT_CLOUD || format == ImageFormat.RAW_PRIVATE) {
buffer = planes[0].getBuffer();
assertNotNull("Fail to get jpeg or depth ByteBuffer", buffer);
data = new byte[buffer.remaining()];
buffer.get(data);
buffer.rewind();
return data;
}
int offset = 0;
data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8];
int maxRowSize = planes[0].getRowStride();
for (int i = 0; i < planes.length; i++) {
if (maxRowSize < planes[i].getRowStride()) {
maxRowSize = planes[i].getRowStride();
}
}
byte[] rowData = new byte[maxRowSize];
if (VERBOSE)
Log.v(TAG, "get data from " + planes.length + " planes");
for (int i = 0; i < planes.length; i++) {
buffer = planes[i].getBuffer();
assertNotNull("Fail to get bytebuffer from plane", buffer);
rowStride = planes[i].getRowStride();
pixelStride = planes[i].getPixelStride();
assertTrue("pixel stride " + pixelStride + " is invalid", pixelStride > 0);
if (VERBOSE) {
Log.v(TAG, "pixelStride " + pixelStride);
Log.v(TAG, "rowStride " + rowStride);
Log.v(TAG, "width " + width);
Log.v(TAG, "height " + height);
}
// For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling.
int w = (i == 0) ? width : width / 2;
int h = (i == 0) ? height : height / 2;
assertTrue("rowStride " + rowStride + " should be >= width " + w, rowStride >= w);
for (int row = 0; row < h; row++) {
int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
int length;
if (pixelStride == bytesPerPixel) {
// Special case: optimized read of the entire row
length = w * bytesPerPixel;
buffer.get(data, offset, length);
offset += length;
} else {
// Generic case: should work for any pixelStride but slower.
// Use intermediate buffer to avoid read byte-by-byte from
// DirectByteBuffer, which is very bad for performance
length = (w - 1) * pixelStride + bytesPerPixel;
buffer.get(rowData, 0, length);
for (int col = 0; col < w; col++) {
data[offset++] = rowData[col * pixelStride];
}
}
// Advance buffer the remainder of the row stride
if (row < h - 1) {
buffer.position(buffer.position() + rowStride - length);
}
}
if (VERBOSE)
Log.v(TAG, "Finished reading data from plane " + i);
buffer.rewind();
}
return data;
}
use of android.media.Image.Plane in project platform_frameworks_base by android.
the class CameraTestUtils method isImageStronglyEqual.
/**
* <p>
* Checks whether the two images are strongly equal.
* </p>
* <p>
* Two images are strongly equal if and only if the data, formats, sizes,
* and timestamps are same. For {@link ImageFormat#PRIVATE PRIVATE} format
* images, the image data is not not accessible thus the data comparison is
* effectively skipped as the number of planes is zero.
* </p>
* <p>
* Note that this method compares the pixel data even outside of the crop
* region, which may not be necessary for general use case.
* </p>
*
* @param lhsImg First image to be compared with.
* @param rhsImg Second image to be compared with.
* @return true if the two images are equal, false otherwise.
* @throws IllegalArgumentException If either of image is null.
*/
public static boolean isImageStronglyEqual(Image lhsImg, Image rhsImg) {
if (lhsImg == null || rhsImg == null) {
throw new IllegalArgumentException("Images should be non-null");
}
if (lhsImg.getFormat() != rhsImg.getFormat()) {
Log.i(TAG, "lhsImg format " + lhsImg.getFormat() + " is different with rhsImg format " + rhsImg.getFormat());
return false;
}
if (lhsImg.getWidth() != rhsImg.getWidth()) {
Log.i(TAG, "lhsImg width " + lhsImg.getWidth() + " is different with rhsImg width " + rhsImg.getWidth());
return false;
}
if (lhsImg.getHeight() != rhsImg.getHeight()) {
Log.i(TAG, "lhsImg height " + lhsImg.getHeight() + " is different with rhsImg height " + rhsImg.getHeight());
return false;
}
if (lhsImg.getTimestamp() != rhsImg.getTimestamp()) {
Log.i(TAG, "lhsImg timestamp " + lhsImg.getTimestamp() + " is different with rhsImg timestamp " + rhsImg.getTimestamp());
return false;
}
if (!lhsImg.getCropRect().equals(rhsImg.getCropRect())) {
Log.i(TAG, "lhsImg crop rect " + lhsImg.getCropRect() + " is different with rhsImg crop rect " + rhsImg.getCropRect());
return false;
}
// Compare data inside of the image.
Plane[] lhsPlanes = lhsImg.getPlanes();
Plane[] rhsPlanes = rhsImg.getPlanes();
ByteBuffer lhsBuffer = null;
ByteBuffer rhsBuffer = null;
for (int i = 0; i < lhsPlanes.length; i++) {
lhsBuffer = lhsPlanes[i].getBuffer();
rhsBuffer = rhsPlanes[i].getBuffer();
if (!lhsBuffer.equals(rhsBuffer)) {
Log.i(TAG, "byte buffers for plane " + i + " don't matach.");
return false;
}
}
return true;
}
use of android.media.Image.Plane in project android_frameworks_base by DirtyUnicorns.
the class ImageUtils method imageCopy.
/**
* <p>
* Copy source image data to destination Image.
* </p>
* <p>
* Only support the copy between two non-{@link ImageFormat#PRIVATE PRIVATE} format
* images with same properties (format, size, etc.). The data from the
* source image will be copied to the byteBuffers from the destination Image
* starting from position zero, and the destination image will be rewound to
* zero after copy is done.
* </p>
*
* @param src The source image to be copied from.
* @param dst The destination image to be copied to.
* @throws IllegalArgumentException If the source and destination images
* have different format, or one of the images is not copyable.
*/
public static void imageCopy(Image src, Image dst) {
if (src == null || dst == null) {
throw new IllegalArgumentException("Images should be non-null");
}
if (src.getFormat() != dst.getFormat()) {
throw new IllegalArgumentException("Src and dst images should have the same format");
}
if (src.getFormat() == ImageFormat.PRIVATE || dst.getFormat() == ImageFormat.PRIVATE) {
throw new IllegalArgumentException("PRIVATE format images are not copyable");
}
if (src.getFormat() == ImageFormat.RAW_PRIVATE) {
throw new IllegalArgumentException("Copy of RAW_OPAQUE format has not been implemented");
}
if (!(dst.getOwner() instanceof ImageWriter)) {
throw new IllegalArgumentException("Destination image is not from ImageWriter. Only" + " the images from ImageWriter are writable");
}
Size srcSize = new Size(src.getWidth(), src.getHeight());
Size dstSize = new Size(dst.getWidth(), dst.getHeight());
if (!srcSize.equals(dstSize)) {
throw new IllegalArgumentException("source image size " + srcSize + " is different" + " with " + "destination image size " + dstSize);
}
Plane[] srcPlanes = src.getPlanes();
Plane[] dstPlanes = dst.getPlanes();
ByteBuffer srcBuffer = null;
ByteBuffer dstBuffer = null;
for (int i = 0; i < srcPlanes.length; i++) {
int srcRowStride = srcPlanes[i].getRowStride();
int dstRowStride = dstPlanes[i].getRowStride();
srcBuffer = srcPlanes[i].getBuffer();
dstBuffer = dstPlanes[i].getBuffer();
if (!(srcBuffer.isDirect() && dstBuffer.isDirect())) {
throw new IllegalArgumentException("Source and destination ByteBuffers must be" + " direct byteBuffer!");
}
if (srcPlanes[i].getPixelStride() != dstPlanes[i].getPixelStride()) {
throw new IllegalArgumentException("Source plane image pixel stride " + srcPlanes[i].getPixelStride() + " must be same as destination image pixel stride " + dstPlanes[i].getPixelStride());
}
int srcPos = srcBuffer.position();
srcBuffer.rewind();
dstBuffer.rewind();
if (srcRowStride == dstRowStride) {
// Fast path, just copy the content if the byteBuffer all together.
dstBuffer.put(srcBuffer);
} else {
// Source and destination images may have different alignment requirements,
// therefore may have different strides. Copy row by row for such case.
int srcOffset = srcBuffer.position();
int dstOffset = dstBuffer.position();
Size effectivePlaneSize = getEffectivePlaneSizeForImage(src, i);
int srcByteCount = effectivePlaneSize.getWidth() * srcPlanes[i].getPixelStride();
for (int row = 0; row < effectivePlaneSize.getHeight(); row++) {
if (row == effectivePlaneSize.getHeight() - 1) {
// Special case for NV21 backed YUV420_888: need handle the last row
// carefully to avoid memory corruption. Check if we have enough bytes to
// copy.
int remainingBytes = srcBuffer.remaining() - srcOffset;
if (srcByteCount > remainingBytes) {
srcByteCount = remainingBytes;
}
}
directByteBufferCopy(srcBuffer, srcOffset, dstBuffer, dstOffset, srcByteCount);
srcOffset += srcRowStride;
dstOffset += dstRowStride;
}
}
srcBuffer.position(srcPos);
dstBuffer.rewind();
}
}
use of android.media.Image.Plane in project android_frameworks_base by DirtyUnicorns.
the class CameraTestUtils method getDataFromImage.
/**
* <p>Read data from all planes of an Image into a contiguous unpadded, unpacked
* 1-D linear byte array, such that it can be write into disk, or accessed by
* software conveniently. It supports YUV_420_888/NV21/YV12 and JPEG input
* Image format.</p>
*
* <p>For YUV_420_888/NV21/YV12/Y8/Y16, it returns a byte array that contains
* the Y plane data first, followed by U(Cb), V(Cr) planes if there is any
* (xstride = width, ystride = height for chroma and luma components).</p>
*
* <p>For JPEG, it returns a 1-D byte array contains a complete JPEG image.</p>
*/
public static byte[] getDataFromImage(Image image) {
assertNotNull("Invalid image:", image);
int format = image.getFormat();
int width = image.getWidth();
int height = image.getHeight();
int rowStride, pixelStride;
byte[] data = null;
// Read image data
Plane[] planes = image.getPlanes();
assertTrue("Fail to get image planes", planes != null && planes.length > 0);
// Check image validity
checkAndroidImageFormat(image);
ByteBuffer buffer = null;
// Same goes for DEPTH_POINT_CLOUD
if (format == ImageFormat.JPEG || format == ImageFormat.DEPTH_POINT_CLOUD || format == ImageFormat.RAW_PRIVATE) {
buffer = planes[0].getBuffer();
assertNotNull("Fail to get jpeg or depth ByteBuffer", buffer);
data = new byte[buffer.remaining()];
buffer.get(data);
buffer.rewind();
return data;
}
int offset = 0;
data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8];
int maxRowSize = planes[0].getRowStride();
for (int i = 0; i < planes.length; i++) {
if (maxRowSize < planes[i].getRowStride()) {
maxRowSize = planes[i].getRowStride();
}
}
byte[] rowData = new byte[maxRowSize];
if (VERBOSE)
Log.v(TAG, "get data from " + planes.length + " planes");
for (int i = 0; i < planes.length; i++) {
buffer = planes[i].getBuffer();
assertNotNull("Fail to get bytebuffer from plane", buffer);
rowStride = planes[i].getRowStride();
pixelStride = planes[i].getPixelStride();
assertTrue("pixel stride " + pixelStride + " is invalid", pixelStride > 0);
if (VERBOSE) {
Log.v(TAG, "pixelStride " + pixelStride);
Log.v(TAG, "rowStride " + rowStride);
Log.v(TAG, "width " + width);
Log.v(TAG, "height " + height);
}
// For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling.
int w = (i == 0) ? width : width / 2;
int h = (i == 0) ? height : height / 2;
assertTrue("rowStride " + rowStride + " should be >= width " + w, rowStride >= w);
for (int row = 0; row < h; row++) {
int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
int length;
if (pixelStride == bytesPerPixel) {
// Special case: optimized read of the entire row
length = w * bytesPerPixel;
buffer.get(data, offset, length);
offset += length;
} else {
// Generic case: should work for any pixelStride but slower.
// Use intermediate buffer to avoid read byte-by-byte from
// DirectByteBuffer, which is very bad for performance
length = (w - 1) * pixelStride + bytesPerPixel;
buffer.get(rowData, 0, length);
for (int col = 0; col < w; col++) {
data[offset++] = rowData[col * pixelStride];
}
}
// Advance buffer the remainder of the row stride
if (row < h - 1) {
buffer.position(buffer.position() + rowStride - length);
}
}
if (VERBOSE)
Log.v(TAG, "Finished reading data from plane " + i);
buffer.rewind();
}
return data;
}
Aggregations