見出し画像

AndroidのImageProxyをBitmapに変換

AndroidのImageProxyをBitmapに変換する方法をまとめました。

1. Image

「Image」は、メディアソースで使用する画像バッファです。アプリから画像のピクセルデータに効率的にアクセスできます。ピクセルデータは「Plane」にカプセル化して保持します。

画像のプレーンの数と意味は、画像形式によって異なります。

JPEG(プレーンの数:1)
・YUV_420_888(プレーンの数:3)
・YUV_422_888(プレーンの数:3)
・YUV_444_888(プレーンの数:3)
・FLEX_RGB_888(プレーンの数:3)
・FLEX_RGBA_8888(プレーンの数:4)
・RAW_SENSOR(プレーンの数:1)
・RAW_PRIVATE(プレーンの数:1)
・HEIC(プレーンの数:1

「Image」は多くの場合ハードウェアによって直接生成されるため、システム全体で共有されるリソースであり、不要になったらクローズする必要があります。

主なメソッドは次のとおりです。

void close()
画像のクローズ。

int getFormat()
画像形式の取得。

Plane[] getPlanes()
プレーン群の取得。

int getWidth()
画像の幅の取得。

int getHeight()
画像の高さの取得。

2. ImageProxy

「ImageProxy」は、「Image」と同様のインターフェースを持つ画像プロキシです。

主なメソッドは次のとおりです。

void close()
画像のクローズ。

int getFormat()
画像形式の取得。

Plane[] getPlanes()
プレーン群の取得。

int getWidth()
画像の幅の取得。

int getHeight()
画像の高さの取得。

Image getImage()
Imageの取得。

3. Image.Plane

「Image.Plane」は、画像データのピクセル平面です。画像がクローズされると、プレーンのByteBufferへのアクセスはできなくなります。

主なメソッドは次のとおりです。

ByteBuffer getBuffer()
フレームデータを含む直接ByteBufferの取得。

int getPixelStride()
ピクセルストライド(バイト単位)の取得。

int getRowStride()
行ストライド(バイト単位)の取得。

4. AndroidのImageをBitmapに変換

AndroidのImageをBitmapに変換するコードは、次のとおりです。

    // ImageProxy → Bitmap
    private Bitmap imageToToBitmap(Image image, int rotationDegrees) {
        byte[] data = imageToByteArray(image);
        Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
        if (rotationDegrees == 0) {
            return bitmap;
        } else {
            return rotateBitmap(bitmap, rotationDegrees);
        }
    }

    // Bitmapの回転
    private Bitmap rotateBitmap(Bitmap bitmap, int rotationDegrees) {
        Matrix mat = new Matrix();
        mat.postRotate(rotationDegrees);
        return Bitmap.createBitmap(bitmap, 0, 0,
            bitmap.getWidth(), bitmap.getHeight(), mat, true);
    }

    // Image → JPEGのバイト配列
    private 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;
    }

    // YUV_420_888 → NV21
    private 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];
        yBuffer.get(nv21, 0, ySize);
        vBuffer.get(nv21, ySize, vSize);
        uBuffer.get(nv21, ySize + vSize, uSize);
        return nv21;
    }

    // NV21 → JPEG
    private 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();
    }


この記事が気に入ったらサポートをしてみませんか?