图像格式转化在人脸识别应用中的实践

栏目: 编程工具 · 发布时间: 5年前

内容简介:ArcFace 2.0 API目前支持多种图像格式:在

ArcFace 2.0 API目前支持多种图像格式: BGR24NV21NV12I420YUYV (Android、IOS只支持其中的部分)。接下来将开始介绍这几种图像格式以及部分转换方式。

一、相关图像颜色空间介绍

1. RGB颜色空间

RGB颜色空间 以Red、Green、Blue三种基本色为基础,进行不同程度的叠加,产生丰富而广泛的颜色,所以俗称三基色模式。 常见的RGB格式有: RGB_565RGB_888ARGB_8888ARGB_4444 等。

2. YUV颜色空间

YUV颜色空间 中,Y用来表示亮度,U和V用来表示色度。 常见的YUV格式有以下几大类: planar: Y、U、V全部连续存储,如 I420YV12 packed: Y、U、V交叉存储,如 YUYV semi-planar: Y连续存储,U、V交叉存储,如 NV21NV12

二、相关图像格式介绍

1. BGR24图像格式

BGR24 图像格式是一种采用24bpp(bit per pixel)的格式。每个颜色通道B、G、R各占8bpp。 排列方式如:

B G R  B G R  B G R  B G R  B G R  B G R  B G R  B G R 

B G R  B G R  B G R  B G R  B G R  B G R  B G R  B G R 

B G R  B G R  B G R  B G R  B G R  B G R  B G R  B G R 

B G R  B G R  B G R  B G R  B G R  B G R  B G R  B G R 
复制代码

2. NV21图像格式

NV21 图像格式属于YUV颜色空间中的 YUV420SP 格式,每四个Y分量共用一组U分量和V分量,Y连续排序,U与V交叉排序。 排列方式如:

Y Y  Y Y  Y Y  Y Y
Y Y  Y Y  Y Y  Y Y

Y Y  Y Y  Y Y  Y Y
Y Y  Y Y  Y Y  Y Y

V U  V U  V U  V U

V U  V U  V U  V U
复制代码

3. NV12图像格式

NV12 图像格式属于YUV颜色空间中的 YUV420SP 格式,每四个Y分量共用一组U分量和V分量,Y连续排序,U与V交叉排序( NV12NV21 只是U与V的位置相反)。 排列方式如:

Y Y  Y Y  Y Y  Y Y
Y Y  Y Y  Y Y  Y Y

Y Y  Y Y  Y Y  Y Y
Y Y  Y Y  Y Y  Y Y

U V  U V  U V  U V

U V  U V  U V  U V
复制代码

4. I420图像格式

I420 图像格式属于YUV颜色空间中的 YUV420P 格式,每四个Y分量共用一组U分量和V分量,Y、U、V各自连续排序。(为了便于说明Y、U、V的共用关系,U和V都未换行) 排列方式如:

Y Y  Y Y  Y Y  Y Y
Y Y  Y Y  Y Y  Y Y

Y Y  Y Y  Y Y  Y Y
Y Y  Y Y  Y Y  Y Y

U  U  U  U  U  U  U  U 
V  V  V  V  V  V  V  V 
复制代码

5. YV12图像格式

YV12 图像格式属于YUV颜色空间中的 YUV420P 格式,每四个Y分量共用一组U分量和V分量,Y、U、V各自连续排序(为了便于说明Y、U、V的共用关系,U和V都未换行)( YV12I420 只是U与V的位置相反)。 排列方式如:

Y Y  Y Y  Y Y  Y Y
Y Y  Y Y  Y Y  Y Y

Y Y  Y Y  Y Y  Y Y
Y Y  Y Y  Y Y  Y Y

V  V  V  V  V  V  V  V 
U  U  U  U  U  U  U  U 
复制代码

6. YUYV图像格式

YUYV 图像格式属于YUV颜色空间中的 YUV422 格式,每两个Y分量共用一组U分量和V分量,Y、U、V交叉排序。 排列方式如:

Y U Y V   Y U Y V  Y U Y V  Y U Y V

Y U Y V   Y U Y V  Y U Y V  Y U Y V

Y U Y V   Y U Y V  Y U Y V  Y U Y V

Y U Y V   Y U Y V  Y U Y V  Y U Y V

复制代码

三、图像格式转换

由于图像的格式多种多样,转换的方法也不胜枚举,只要了解了 YUVRGB 数据的排列方式,自己编写图像转换代码也花不了多少时间。以下列出部分的图像转换的 Java 代码供参考。

1. 从 Bitmap 中获取 ARGB_8888 图像格式数据(Android平台)

Bitmap 支持多种格式: ALPHA_8,RGB_565,ARGB_4444,ARGB_8888,RGBA_F16,HARDWARE 。我们目前主要选择 ARGB_8888 进行格式转换。 我们可使用 Bitmap 类中的 public void getPixels(@ColorInt int[] pixels, int offset, int stride, int x, int y, int width, int height) 方法获取 int[] 类型的argb数据或 public void copyPixelsToBuffer (Buffer dst) 方法获取 byte[] 类型的 ARGB_8888 数据。

2. ARGB_8888 转换为 BGR_24

举个例子,对于4x2的图片, ARGB_8888 格式内容为:

A1 R1 G1 B1  A2 R2 G2 B2  A3 R3 G3 B3  A4 R4 G4 B4
A5 R5 G5 B5  A6 R6 G6 B6  A7 R7 G7 B7  A8 R8 G8 B8
复制代码

那么若需要转化为 BGR_24 ,内容将变成:

B1 G1 R1  B2 G2 R2  B3 G3 R3  B4 G4 R4
B5 G5 R5  B6 G6 R6  B7 G7 R7  B8 G8 R8
复制代码

BGR_24 内容为3个 byte 一组, ARGB_8888 内容为4个 byte 一组。因此,对于第一组 ARGB_8888(A1 R1 G1 B1) 和第一组 BGR_24(B1 G1 R1) ,其对应关系为:

bgr24[0] = argb8888[3];
bgr24[1] = argb8888[2];
bgr24[2] = argb8888[1];
复制代码

对应的转换代码:

public static byte[] argb8888ToBgr24(byte[] argb8888) {
        if (argb8888 == null){
            throw new IllegalArgumentException("invalid image params!");
        }
        int groupNum = argb8888.length / 4;
        byte[] bgr24 = new byte[groupNum * 3];
        int bgr24Index = 0;
        int argb8888Index = 0;
        for (int i = 0; i < groupNum; i++) {
            bgr24[bgr24Index] = argb8888[argb8888Index + 2];
            bgr24[bgr24Index + 1] = argb8888[argb8888Index + 1];
            bgr24[bgr24Index + 2] = argb8888[argb8888Index];
            bgr24Index += 3;
            argb8888Index += 4;
        }
        return bgr24;
    }
复制代码

3. ARGB_8888 转换为 NV21

rgbyuv 的算法:

int y = (66 * r + 129 * g + 25 * b + 128 >> 8) + 16;
int u = (-38 * r - 74 * g + 112 * b + 128 >> 8) + 128;
int v = (112 * r - 94 * g - 18 * b + 128 >> 8) + 128;
复制代码

转换方法:

  • int[] 类型的 ARGB_8888 数据转换为 NV21
private static byte[] argbToNv21(int[] argb, int width, int height) {
        if (argb == null || argb.length == 0 || width * height != argb.length) {
            throw new IllegalArgumentException("invalid image params!");
        }
        int yIndex = 0;
        int uvIndex = width * height;
        int argbIndex = 0;
        byte[] nv21 = new byte[width * height * 3 / 2];
        for (int j = 0; j < height; ++j) {
            for (int i = 0; i < width; ++i) {
                //对于int型color数据,格式为0xAARRGGBB,可进行与运算后移位取对应A R G B,
                //但是该YUV转换公式中不需要ALPHA,因此我们只需要取 R G B 即可。
                int r = (argb[argbIndex] & 0xFF0000) >> 16;
                int g = (argb[argbIndex] & 0x00FF00) >> 8;
                int b = argb[argbIndex] & 0x0000FF;
                //获取该像素点的R G B,并转换为Y U V,但byte范围是0x00~0xFF,因此在赋值时还需进行判断
                int y = (66 * r + 129 * g + 25 * b + 128 >> 8) + 16;
                nv21[yIndex++] = (byte) (y < 0 ? 0 : (y > 0xFF ? 0xFF : y));
                if ((j & 1) == 0 && (argbIndex & 1) == 0 && uvIndex < nv21.length - 2) {
                    int u = (-38 * r - 74 * g + 112 * b + 128 >> 8) + 128;
                    int v = (112 * r - 94 * g - 18 * b + 128 >> 8) + 128;
                    nv21[uvIndex++] = (byte) (v < 0 ? 0 : (v > 0xFF ? 0xFF : v));
                    nv21[uvIndex++] = (byte) (u < 0 ? 0 : (u > 0xFF ? 0xFF : u));
                }
                ++argbIndex;
            }
        }
        return nv21;
    }
复制代码
  • byte[] 类型的 ARGB_8888 数据转换为 NV21 (原理同方法1):
private static byte[] argbToNv21(byte[] argb, int width, int height) {
        if (argb == null || argb.length == 0 || width * height * 4 != argb.length) {
            throw new IllegalArgumentException("invalid image params!");
        }
        int yIndex = 0;
        int uvIndex = width * height;
        int argbIndex = 0;
        byte[] nv21 = new byte[width * height * 3 / 2];
        for (int j = 0; j < height; ++j) {
            for (int i = 0; i < width; ++i) {
                argbIndex++;
                int r = argb[argbIndex++];
                int g = argb[argbIndex++];
                int b = argb[argbIndex++];
                r &= 0x000000FF;
                g &= 0x000000FF;
                b &= 0x000000FF;
                int y = ((66 * r + 129 * g + 25 * b + 128 >> 8) + 16);
                nv21[yIndex++] = (byte) (y > 0xFF ? 0xFF : (y < 0 ? 0 : y));
                if ((j & 1) == 0 && ((argbIndex >> 2) & 1) == 0 && uvIndex < nv21.length - 2) {
                    int u = ((-38 * r - 74 * g + 112 * b + 128 >> 8) + 128);
                    int v = ((112 * r - 94 * g - 18 * b + 128 >> 8) + 128);
                    nv21[uvIndex++] = (byte) (v > 0xFF ? 0xFF : (v < 0 ? 0 : v));
                    nv21[uvIndex++] = (byte) (u > 0xFF ? 0xFF : (u < 0 ? 0 : u));
                }
            }
        }
        return nv21;
    }
复制代码

4. NV21 转换为 BGR24

yuvrgb 算法:

int r = (int) ((y & 0xFF) + 1.4075 * ((v & 0xFF) - 128));
int g = (int) ((y & 0xFF) - 0.3455 * ((u & 0xFF) - 128) - 0.7169 * ((v & 0xFF) - 128));
int b = (int) ((y & 0xFF) + 1.779 * ((u & 0xFF) - 128));
复制代码

转换方法:

private static byte[] nv21ToBgr24(byte[] nv21, int width, int height) {
        if (nv21 == null || nv21.length == 0 || width * height * 3 / 2 != nv21.length) {
            throw new IllegalArgumentException("invalid image params!");
        }
        byte[] bgr24 = new byte[width * height * 3];
        int bgrLineSize = width * 3;
        //偶数行的bgr数据下标
        int evenLineBgrIndex = 0;
        //奇数行的bgr数据下标
        int oddLineBgrIndex = bgrLineSize;
        //当前一行y数据最左边的下标
        int yLineStart = 0;
        //uv数据的下标
        int uvIndex = width * height;
        //由于NV21的共用关系,每2行做一次转换
        for (int i = 0; i < height; i += 2) {
            for (int widthOffset = 0; widthOffset < width; widthOffset++) {
                byte v = nv21[uvIndex];
                byte u = nv21[uvIndex + 1];
                byte yEven = nv21[yLineStart + widthOffset];
                byte yOdd = nv21[yLineStart + width + widthOffset];
                //偶数行YUV转RGB
                int r, g, b;
                r = (int) ((yEven & 0xFF) + 1.4075 * ((v & 0xFF) - 128));
                g = (int) ((yEven & 0xFF) - 0.3455 * ((u & 0xFF) - 128) - 0.7169 * ((v & 0xFF) - 128));
                b = (int) ((yEven & 0xFF) + 1.779 * ((u & 0xFF) - 128));
                r = r < 0 ? 0 : r > 0xFF ? 0xFF : r;
                g = g < 0 ? 0 : g > 0xFF ? 0xFF : g;
                b = b < 0 ? 0 : b > 0xFF ? 0xFF : b;
                bgr24[evenLineBgrIndex++] = (byte) b;
                bgr24[evenLineBgrIndex++] = (byte) g;
                bgr24[evenLineBgrIndex++] = (byte) r;
                //奇数行YUV转RGB
                r = (int) ((yOdd & 0xFF) + 1.4075 * ((v & 0xFF) - 128));
                g = (int) ((yOdd & 0xFF) - 0.3455 * ((u & 0xFF) - 128) - 0.7169 * ((v & 0xFF) - 128));
                b = (int) ((yOdd & 0xFF) + 1.779 * ((u & 0xFF) - 128));
                r = r < 0 ? 0 : r > 0xFF ? 0xFF : r;
                g = g < 0 ? 0 : g > 0xFF ? 0xFF : g;
                b = b < 0 ? 0 : b > 0xFF ? 0xFF : b;
                bgr24[oddLineBgrIndex++] = (byte) b;
                bgr24[oddLineBgrIndex++] = (byte) g;
                bgr24[oddLineBgrIndex++] = (byte) r;
                //每两个y将uv下标增1
                if ((widthOffset & 1) == 1) {
                    uvIndex += 2;
                }
            }
            //由于在内层循环中已经做过width * 3次自增,所以外层循环中只需要增加一行
            evenLineBgrIndex += bgrLineSize;
            oddLineBgrIndex += bgrLineSize;
            //y增2行
            yLineStart += width * 2;
        }
        return bgr24;
    }
复制代码

5. NV12NV21 的互换

NV21NV12 只是U与V的数据位置不同,因此, NV21 转换为 NV12 的代码同样适用于 NV12 转换为 NV21 。可参考如下代码:

public static byte[] nv21ToNv12(byte[] nv21, int width, int height) {
        if (nv21 == null || nv21.length == 0 || width * height * 3 / 2 != nv21.length) {
            throw new IllegalArgumentException("invalid image params!");
        }
        final int ySize = width * height;
        int totalSize = width * height * 3 / 2;

        byte[] nv12 = new byte[nv21.length];
        //复制Y
        System.arraycopy(nv21, 0, nv12, 0, ySize);
        //UV互换
        for (int uvIndex = ySize; uvIndex < totalSize; uvIndex += 2) {
            nv12[uvIndex] = nv21[uvIndex + 1];
            nv12[uvIndex + 1] = nv21[uvIndex];
        }
        return nv12;
    }
复制代码

6. NV21YV12

NV21 转化为 YV12 的过程主要是将其UV数据的交叉 排序 修改为连续排序。可参考如下代码:

public static byte[] nv21ToYv12(byte[] nv21, int width, int height) {
        if (nv21 == null || nv21.length == 0 || width * height * 3 / 2 != nv21.length) {
            throw new IllegalArgumentException("invalid image params!");
        }
        final int ySize = width * height;
        int totalSize = width * height * 3 / 2;
        byte[] yv12 = new byte[nv21.length];
        int yv12UIndex = ySize;
        int yv12VIndex = ySize * 5 / 4;
        //复制Y
        System.arraycopy(nv21, 0, yv12, 0, ySize);
        //复制UV
        for (int uvIndex = ySize; uvIndex < totalSize; uvIndex += 2) {
            yv12[yv12UIndex++] = nv21[uvIndex];
            yv12[yv12VIndex++] = nv21[uvIndex + 1];
        }
        return yv12;
    }
复制代码

7. YUYVNV12

YUYV 格式中,两个 Y 共用一组 UV ,而 NV12 是四个 Y 共用一组 UV ,因此,这是一个 YUV422YUV420 的过程,需要舍弃一半的 UV 。可参考如下代码:

public static byte[] yuyvToNv12(byte[] yuyv, int width, int height) {
        if (yuyv == null || yuyv.length == 0) {
            throw new IllegalArgumentException("invalid image params!");
        }
        int ySize = yuyv.length / 2;
        byte[] nv12 = new byte[yuyv.length * 3 / 4];
        int nv12YIndex = 0;
        int nv12UVIndex = ySize;
        boolean copyUV = false;
        int lineDataSize = width * 2;
        for (int i = 0, yuyvIndex = 0; i < height; i++, yuyvIndex += lineDataSize) {
            if (copyUV) {
                for (int lineOffset = 0; lineOffset < lineDataSize; lineOffset += 4) {
                    //复制Y
                    nv12[nv12YIndex++] = yuyv[yuyvIndex + lineOffset];
                    nv12[nv12YIndex++] = yuyv[yuyvIndex + lineOffset + 2];
                    //复制UV
                    nv12[nv12UVIndex++] = yuyv[yuyvIndex + lineOffset + 1];
                    nv12[nv12UVIndex++] = yuyv[yuyvIndex + lineOffset + 3];
                }
            } else {
                for (int lineOffset = 0; lineOffset < lineDataSize; lineOffset += 4) {
                    //复制Y
                    nv12[nv12YIndex++] = yuyv[yuyvIndex + lineOffset];
                    nv12[nv12YIndex++] = yuyv[yuyvIndex + lineOffset + 2];
                }
            }
            copyUV = !copyUV;
        }
        return nv12;
    }
复制代码

8. I420YV12 的互换

I420YV12 只是 UV 的数据位置不同,因此, I420 转换为 YV12 的代码同样适用于 YV12 转换为 I420 。可参考如下代码:

public static byte[] i420ToYv12(byte[] i420) {
        if (i420 == null || i420.length == 0 || i420.length % 6 != 0) {
            throw new IllegalArgumentException("invalid image params!");
        }
        int ySize = i420.length * 2 / 3;
        int uvSize = i420.length / 6;
        byte[] yv12 = new byte[i420.length];
        //复制Y
        System.arraycopy(i420, 0, yv12, 0, ySize);
        //UV互换
        System.arraycopy(i420, ySize, yv12, ySize + uvSize, uvSize);
        System.arraycopy(i420, ySize + uvSize, yv12, ySize, uvSize);
        return yv12;
    }
复制代码

9. I420 转换为 YUYV

I420YUYV 相比, I420UV 只有 YUYV 的一半,这是一个 YUV420YUV422 的过程,缺损的数据只能通过复用 UV 弥补。

public static byte[] i420ToYuyv(byte[] i420, int width, int height) {
        if (i420 == null || i420.length == 0 || i420.length != width * height * 3 / 2) {
            throw new IllegalArgumentException("invalid image params!");
        }
        byte[] yuyv = new byte[width * height * 2];
        int yuyvLineSize = width * 2;
        int i420YIndex = 0;
        int i420UIndex = width * height;
        int i420VIndex = width * height * 5 / 4;
        int yuyvLineStart = 0;
        for (int i = 0; i < height; i += 2) {
            for (int lineOffset = 0; lineOffset < yuyvLineSize; lineOffset += 4) {
                byte u = i420[i420UIndex++];
                byte v = i420[i420VIndex++];
                //偶数行数据赋值
                int yuyvOffset = yuyvLineStart + lineOffset;
                yuyv[yuyvOffset] = i420[i420YIndex];
                yuyv[yuyvOffset + 1] = u;
                yuyv[yuyvOffset + 2] = i420[i420YIndex + 1];
                yuyv[yuyvOffset + 3] = v;
                //奇数行数据赋值
                int yuyvNextLineOffset = yuyvLineStart + yuyvLineSize + lineOffset;
                yuyv[yuyvNextLineOffset] = i420[i420YIndex + width];
                yuyv[yuyvNextLineOffset + 1] = u;
                yuyv[yuyvNextLineOffset + 2] = i420[i420YIndex + width + 1];
                yuyv[yuyvNextLineOffset + 3] = v;

                i420YIndex += 2;
            }
            i420YIndex += width;
            yuyvLineStart += (width << 2);
        }
        return yuyv;
    }
复制代码

四、图像裁剪

与格式转换相同,只要了解的图像的排列方式,图像的裁剪也并不困难。本文提供一种 RGB 颜色空间的图像裁剪和一种 YUV 颜色空间的图像裁剪。

1. 裁剪NV21

public static byte[] i420ToYv12(byte[] i420) {
        if (i420 == null || i420.length == 0 || i420.length % 6 != 0) {
            throw new IllegalArgumentException("invalid image params!");
        }
        int ySize = i420.length * 2 / 3;
        int uvSize = i420.length / 6;
        byte[] yv12 = new byte[i420.length];
        //复制Y
        System.arraycopy(i420, 0, yv12, 0, ySize);
        //UV互换
        System.arraycopy(i420, ySize, yv12, ySize + uvSize, uvSize);
        System.arraycopy(i420, ySize + uvSize, yv12, ySize, uvSize);
        return yv12;
    }
复制代码

9. I420 转换为 YUYV

I420YUYV 相比, I420UV 只有 YUYV 的一半,这是一个 YUV420YUV422 的过程,缺损的数据只能通过复用 UV 弥补。

public static byte[] i420ToYuyv(byte[] i420, int width, int height) {
        if (i420 == null || i420.length == 0 || i420.length != width * height * 3 / 2) {
            throw new IllegalArgumentException("invalid image params!");
        }
        byte[] yuyv = new byte[width * height * 2];
        int yuyvLineSize = width * 2;
        int i420YIndex = 0;
        int i420UIndex = width * height;
        int i420VIndex = width * height * 5 / 4;
        int yuyvLineStart = 0;
        for (int i = 0; i < height; i += 2) {
            for (int lineOffset = 0; lineOffset < yuyvLineSize; lineOffset += 4) {
                byte u = i420[i420UIndex++];
                byte v = i420[i420VIndex++];
                //偶数行数据赋值
                int yuyvOffset = yuyvLineStart + lineOffset;
                yuyv[yuyvOffset] = i420[i420YIndex];
                yuyv[yuyvOffset + 1] = u;
                yuyv[yuyvOffset + 2] = i420[i420YIndex + 1];
                yuyv[yuyvOffset + 3] = v;
                //奇数行数据赋值
                int yuyvNextLineOffset = yuyvLineStart + yuyvLineSize + lineOffset;
                yuyv[yuyvNextLineOffset] = i420[i420YIndex + width];
                yuyv[yuyvNextLineOffset + 1] = u;
                yuyv[yuyvNextLineOffset + 2] = i420[i420YIndex + width + 1];
                yuyv[yuyvNextLineOffset + 3] = v;

                i420YIndex += 2;
            }
            i420YIndex += width;
            yuyvLineStart += (width << 2);
        }
        return yuyv;
    }
复制代码

四、图像裁剪

与格式转换相同,只要了解的图像的排列方式,图像的裁剪也并不困难。本文提供一种 RGB 颜色空间的图像裁剪和一种 YUV 颜色空间的图像裁剪。

1. 裁剪NV21或NV12

public static byte[] cropYuv420sp(byte[] yuv420sp, int width, int height, int left, int top, int right, int bottom) {
        if (yuv420sp == null || yuv420sp.length == 0 || width * height * 3 / 2 != yuv420sp.length) {
            throw new IllegalArgumentException("invalid image params!");
        }
        if (left < 0 || top < 0 || right > width || bottom > height) {
            throw new IllegalArgumentException("rect out of bounds!");
        }
        if (right < left || bottom < top) {
            throw new IllegalArgumentException("invalid rect!");
        }
        if (((right - left) & 1) == 1 || ((bottom - top) & 1) == 1) {
            throw new IllegalArgumentException("yuv420sp width and height must be even!");
        }
        if ((left & 1 )== 1){
            throw new IllegalArgumentException("yuv420sp crop left borderIndex and right borderIndex must be even!");
        }
        int cropImageWidth = right - left;
        int cropImageHeight = bottom - top;
        byte[] cropYuv420sp = new byte[cropImageWidth * cropImageHeight * 3 / 2];

        //复制Y
        int originalYLineStart = top * width;
        int targetYIndex = 0;

        //复制UV
        int originalUVLineStart = width * height + top * width / 2;
        int targetUVIndex = cropImageWidth * cropImageHeight;
        for (int i = top; i < bottom; i++) {
            System.arraycopy(yuv420sp, originalYLineStart + left, cropYuv420sp, targetYIndex, cropImageWidth);
            originalYLineStart += width;
            targetYIndex += cropImageWidth;
            if ((i & 1) == 0) {
                System.arraycopy(yuv420sp, originalUVLineStart + left, cropYuv420sp, targetUVIndex, cropImageWidth);
                originalUVLineStart += width;
                targetUVIndex += cropImageWidth;
            }
        }
        return cropYuv420sp;
    }
复制代码

2. 裁剪BGR24

public static byte[] cropBgr24(byte[] bgr24, int width, int height, int left, int top, int right, int bottom) {
        if (bgr24 == null || bgr24.length == 0 || width * height * 3 != bgr24.length) {
            throw new IllegalArgumentException("invalid image params!");
        }
        if (left < 0 || top < 0 || right > width || bottom > height) {
            throw new IllegalArgumentException("rect out of bounds!");
        }
        if (right < left || bottom < top) {
            throw new IllegalArgumentException("invalid rect!");
        }
        int cropImageWidth = right - left;
        int cropImageHeight = bottom - top;
        byte[] cropBgr24 = new byte[cropImageWidth * cropImageHeight * 3];

        int originalLineStart = top * width * 3;
        int targetIndex = 0;
        for (int i = top; i < bottom; i++) {
            System.arraycopy(bgr24, originalLineStart + left * 3, cropBgr24, targetIndex, cropImageWidth * 3);
            originalLineStart += width * 3;
            targetIndex += cropImageWidth * 3;
        }
        return cropBgr24;
    }

复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

How to Design Programs, 2nd Edition

How to Design Programs, 2nd Edition

Matthias Felleisen、Robert Bruce Findler、Matthew Flatt、Shriram Krishnamurthi / MIT Press / 2018-5-4 / USD 57.00

A completely revised edition, offering new design recipes for interactive programs and support for images as plain values, testing, event-driven programming, and even distributed programming. This ......一起来看看 《How to Design Programs, 2nd Edition》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具