欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android NV21裁剪算法

程序员文章站 2024-03-16 20:41:58
...

NV21裁剪算法

简要

最近,在做Android摄像头预览方法的事情,usb摄像头出来的数据都是16:9的,无法正常在竖屏状态下显示,所以就要对摄像头的数据进行裁剪处理,摄像头出来的数据是NV21(就是yuv420sp)格式,libyuv的裁剪算法是针对i420格式进行操作,裁剪NV21就得进行格式转换,一个裁剪需要三个操作,效率可想而知,经过测试libyuv对1920x1080进行裁剪,在rk3399上需要耗时40ms左右,算了,自己研究了一下,写了一个裁剪算法,水平差,写了个的NV21裁剪算法,首先是要了解 NV21的数据格式,网上太多文章了,我就不赘述了

NV21格式 YYYYYYYYVUVU

好了,开始裁剪吧,裁剪就是删数据嘛

 /**
     * NV21裁剪 by lake 算法效率 11ms
     *
     * @param src    源数据
     * @param width  源宽
     * @param height 源高
     * @param left   顶点坐标
     * @param top    顶点坐标
     * @param clip_w 裁剪后的宽
     * @param clip_h 裁剪后的高
     * @return 裁剪后的数据
     */
    public static byte[] cropNV21(byte[] src, int width, int height, int left, int top, int clip_w, int clip_h) {
        if (left > width || top > height) {
            return null;
        }
        //取偶
        int x = left * 2 / 2, y = top * 2 / 2;
        int w = clip_w * 2 / 2, h = clip_h * 2 / 2;
        int y_unit = w * h;
        int src_unit = width * height;
        int uv = y_unit >> 1;
        byte[] nData = new byte[y_unit + uv];


        for (int i = y, len_i = y + h; i < len_i; i++) {
            for (int j = x, len_j = x + w; j < len_j; j++) {
                nData[(i - y) * w + j - x] = src[i * width + j];
                nData[y_unit + ((i - y) / 2) * w + j - x] = src[src_unit + i / 2 * width + j];
            }
        }

        return nData;
    }

原理很简单,就是源数据根据条件把yvu塞到一个新数组里,测试一下这个算法,需要11ms,还是太慢了。参照了一下别人c实现的裁剪算法。做了一下修改

/**
     * NV21裁剪  算法效率 3ms
     *
     * @param src    源数据
     * @param width  源宽
     * @param height 源高
     * @param left   顶点坐标
     * @param top    顶点坐标
     * @param clip_w 裁剪后的宽
     * @param clip_h 裁剪后的高
     * @return 裁剪后的数据
     */
    public static byte[] clipNV21(byte[] src, int width, int height, int left, int top, int clip_w, int clip_h) {
        if (left > width || top > height) {
            return null;
        }
        //取偶
        int x = left * 2 / 2, y = top * 2 / 2;
        int w = clip_w * 2 / 2, h = clip_h * 2 / 2;
        int y_unit = w * h;
        int uv = y_unit / 2;
        byte[] nData = new byte[y_unit + uv];
        int uv_index_dst = w * h - y / 2 * w;
        int uv_index_src = width * height + x;
        int srcPos0 = y * width;
        int destPos0 = 0;
        int uvSrcPos0 = uv_index_src;
        int uvDestPos0 = uv_index_dst;
        for (int i = y; i < y + h; i++) {
            System.arraycopy(src, srcPos0 + x, nData, destPos0, w);//y内存块复制
            srcPos0 += width;
            destPos0 += w;
            if ((i & 1) == 0) {
                System.arraycopy(src, uvSrcPos0, nData, uvDestPos0, w);//uv内存块复制
                uvSrcPos0 += width;
                uvDestPos0 += w;
            }
        }
        return nData;
    }

运行一下,只需要2ms~3ms就能裁剪完。
接着自己又实现了一下NV21裁剪的同时进行镜像操作。

/**
     * 剪切NV21数据并且镜像 算法效率1080x1920 14ms 1280x720 6ms
     *
     * @param src
     * @param width
     * @param height
     * @param left
     * @param top
     * @param clip_w
     * @param clip_h
     * @return
     */
    public static byte[] clipMirrorNV21(byte[] src, int width, int height, int left, int top, int clip_w, int clip_h) {
        if (left > width || top > height) {
            return null;
        }
        //取偶
        int x = left, y = top;
        int w = clip_w, h = clip_h;
        int y_unit = w * h;
        int src_unit = width * height;
        int uv = y_unit / 2;
        byte[] nData = new byte[y_unit + uv];
        int nPos = (y - 1) * width;
        int mPos;

        for (int i = y, len_i = y + h; i < len_i; i++) {
            nPos += width;
            mPos = src_unit + (i >> 1) * width;
            for (int j = x, len_j = x + w; j < len_j; j++) {
                nData[(i - y + 1) * w - j + x - 1] = src[nPos + j];
                if ((i & 1) == 0) {
                    int m = y_unit + (((i - y) >> 1) + 1) * w - j + x - 1;
                    if ((m & 1) == 0) {
                        m++;
                        nData[m] = src[mPos + j];
                        continue;
                    }
                    m--;
                    nData[m] = src[mPos + j];
                }
            }
        }
        return nData;
    }

就单纯预览的话,这个时间是可以接受的。