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

栏目: 编程工具 · 发布时间: 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;
    }

复制代码

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

查看所有标签

猜你喜欢:

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

Python高级编程(第二版)

Python高级编程(第二版)

[波兰] Michał Jaworski、[法] Tarek Ziadé / 张亮、阿信 / 人民邮电出版社 / 2017-9-19 / 89.00元

Python作为一种高级程序设计语言,凭借其简洁、易读及可扩展性日渐成为程序设计领域备受推崇的语言之一。 本书基于Python 3.5版本进行讲解,通过13章的内容,深度揭示了Python编程的高级技巧。本书从Python语言及其社区的现状开始介绍,对Python语法、命名规则、Python包的编写、部署代码、扩展程序开发、管理代码、文档编写、测试开发、代码优化、并发编程、设计模式等重要话题......一起来看看 《Python高级编程(第二版)》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具