|
|
@@ -0,0 +1,128 @@
|
|
|
+package com.miekir.ocr.tool;
|
|
|
+
|
|
|
+import android.graphics.ImageFormat;
|
|
|
+import android.graphics.Rect;
|
|
|
+import android.graphics.YuvImage;
|
|
|
+import android.media.Image;
|
|
|
+
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.nio.ByteBuffer;
|
|
|
+import java.nio.ReadOnlyBufferException;
|
|
|
+
|
|
|
+public final class ImageUtil {
|
|
|
+
|
|
|
+ public static byte[] imageToByteArray(Image image) {
|
|
|
+ byte[] data = null;
|
|
|
+ if (image.getFormat() == ImageFormat.JPEG) {
|
|
|
+ Image.Plane[] planes = image.getPlanes();
|
|
|
+ ByteBuffer buffer = planes[0].getBuffer();
|
|
|
+ data = new byte[buffer.capacity()];
|
|
|
+ buffer.get(data);
|
|
|
+ return data;
|
|
|
+ } else if (image.getFormat() == ImageFormat.YUV_420_888) {
|
|
|
+ data = NV21toJPEG(
|
|
|
+ YUV_420_888toNV21(image),
|
|
|
+ image.getWidth(), image.getHeight());
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static byte[] YUV_420_888toNV21(Image image) {
|
|
|
+ byte[] nv21;
|
|
|
+ ByteBuffer yBuffer = image.getPlanes()[0].getBuffer();
|
|
|
+ ByteBuffer uBuffer = image.getPlanes()[1].getBuffer();
|
|
|
+ ByteBuffer vBuffer = image.getPlanes()[2].getBuffer();
|
|
|
+
|
|
|
+ int ySize = yBuffer.remaining();
|
|
|
+ int uSize = uBuffer.remaining();
|
|
|
+ int vSize = vBuffer.remaining();
|
|
|
+
|
|
|
+ nv21 = new byte[ySize + uSize + vSize];
|
|
|
+
|
|
|
+ //U and V are swapped
|
|
|
+ yBuffer.get(nv21, 0, ySize);
|
|
|
+ vBuffer.get(nv21, ySize, vSize);
|
|
|
+ uBuffer.get(nv21, ySize + vSize, uSize);
|
|
|
+
|
|
|
+ return nv21;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private static byte[] NV21toJPEG(byte[] nv21, int width, int height) {
|
|
|
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
|
+ YuvImage yuv = new YuvImage(nv21, ImageFormat.NV21, width, height, null);
|
|
|
+ yuv.compressToJpeg(new Rect(0, 0, width, height), 100, out);
|
|
|
+ return out.toByteArray();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private static byte[] YUV_420_888toNV21Accept(Image image) {
|
|
|
+ int width = image.getWidth();
|
|
|
+ int height = image.getHeight();
|
|
|
+ int ySize = width * height;
|
|
|
+ int uvSize = width * height / 4;
|
|
|
+
|
|
|
+ byte[] nv21 = new byte[ySize + uvSize * 2];
|
|
|
+
|
|
|
+ ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); // Y
|
|
|
+ ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); // U
|
|
|
+ ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); // V
|
|
|
+
|
|
|
+ int rowStride = image.getPlanes()[0].getRowStride();
|
|
|
+ assert (image.getPlanes()[0].getPixelStride() == 1);
|
|
|
+
|
|
|
+ int pos = 0;
|
|
|
+
|
|
|
+ if (rowStride == width) { // likely
|
|
|
+ yBuffer.get(nv21, 0, ySize);
|
|
|
+ pos += ySize;
|
|
|
+ } else {
|
|
|
+ int yBufferPos = width - rowStride; // not an actual position
|
|
|
+ for (; pos < ySize; pos += width) {
|
|
|
+ yBufferPos += rowStride - width;
|
|
|
+ yBuffer.position(yBufferPos);
|
|
|
+ yBuffer.get(nv21, pos, width);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ rowStride = image.getPlanes()[2].getRowStride();
|
|
|
+ int pixelStride = image.getPlanes()[2].getPixelStride();
|
|
|
+
|
|
|
+ assert (rowStride == image.getPlanes()[1].getRowStride());
|
|
|
+ assert (pixelStride == image.getPlanes()[1].getPixelStride());
|
|
|
+
|
|
|
+ if (pixelStride == 2 && rowStride == width && uBuffer.get(0) == vBuffer.get(1)) {
|
|
|
+ // maybe V an U planes overlap as per NV21, which means vBuffer[1] is alias of uBuffer[0]
|
|
|
+ byte savePixel = vBuffer.get(1);
|
|
|
+ try {
|
|
|
+ vBuffer.put(1, (byte) ~savePixel);
|
|
|
+ if (uBuffer.get(0) == (byte) ~savePixel) {
|
|
|
+ vBuffer.put(1, savePixel);
|
|
|
+ vBuffer.get(nv21, ySize, uvSize);
|
|
|
+
|
|
|
+ return nv21; // shortcut
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ // unfortunately, the check failed. We must save U and V pixel by pixel
|
|
|
+ vBuffer.put(1, savePixel);
|
|
|
+ } catch (ReadOnlyBufferException ex) {
|
|
|
+ // unfortunately, we cannot check if vBuffer and uBuffer overlap
|
|
|
+ }
|
|
|
+
|
|
|
+ // other optimizations could check if (pixelStride == 1) or (pixelStride == 2),
|
|
|
+ // but performance gain would be less significant
|
|
|
+
|
|
|
+ for (int row = 0; row < height / 2; row++) {
|
|
|
+ for (int col = 0; col < width / 2; col++) {
|
|
|
+ int vuPos = col * pixelStride + row * rowStride;
|
|
|
+ nv21[pos++] = vBuffer.get(vuPos);
|
|
|
+ nv21[pos++] = uBuffer.get(vuPos);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nv21;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|