美文网首页
UVC Camera使用简单记录

UVC Camera使用简单记录

作者: 梧叶已秋声 | 来源:发表于2020-05-18 17:16 被阅读0次

    程序是根据 UVCCamera去修改的。
    使用过程中遇到的几点关键问题:
    1.YUV 格式:NV21等格式。
    2.YUV转RGB效率问题。
    3.Bitmap内存泄漏问题。
    4.Handler内存泄漏的问题

    首先onFrame(这是camera取图的callBack函数,byte[] data就是camera的数据)中不能做耗时操作(例如将data转bitmap等操作),不然会阻塞(之前把这个操作放到这里然后直接将bitmap传出)导致卡顿。

                @Override
                public void onFrame(byte[] data, int camera) throws RemoteException {
                    Log.d(TAG,"onFrame ,Data length is : " + data.length );
    //              final Bitmap bitmap = rawByteArray2RGBABitmap2(data ,640,480);
                    if (camera == 0){
                        Message obtainMessage = mHander.obtainMessage();
                        obtainMessage.obj= data;
                        obtainMessage.arg1=camera;
                        obtainMessage.what= MSG_IMAGE;
                        mHander.sendMessage(obtainMessage);
                    }else {
                        Message obtainMessage = mHander.obtainMessage();
                        obtainMessage.obj= data;
                        obtainMessage.arg1=camera;
                        obtainMessage.what= MSG_IMAGE_1;
                        mHander.sendMessage(obtainMessage);
                    }
    
                }
    

    在传输byte[]数据之后,需要将byte[]转bitmap。
    在byte[] 转bitmap的过程中,由于创建了过多的Bitmap,会导致内存泄漏,程序崩溃。
    因此1.将Bitmap设置为静态变量,避免重复创建的问题。2.修改byte[] 转bitmap的函数,原来函数是返回Bitmap,现在是将Bitmap作为参数传入函数中,直接将Bitmap数据传递出去。减少了一个返回的步骤。

    之前是这样的:

    
                        case MSG_IMAGE:
                            Bitmap bitmap=(Bitmap)msg.obj;
                            int camera=msg.arg1;
                            handleImage(bitmap);
                            break;
                        case MSG_IMAGE_1:
                            Bitmap bitmap1=(Bitmap)msg.obj;
                            int camera1=msg.arg1;
                            handleImage1(bitmap1);
                            break;
                        default:
                            break;
    
    
    

    现在是这样的:

    
    
        private static Bitmap bmp = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888);
        private static Bitmap bmp_1 = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888);
    .......
    
    
                    @Override
                public boolean handleMessage(Message msg) {
                    switch (msg.what) {
                        case MSG_IMAGE:
                            byte[] data = (byte[]) msg.obj;
                            rawByteArray2RGBABitmap2(bmp,data ,640,480);
                            handleImage(bmp);
                            break;
                        case MSG_IMAGE_1:
                            byte[] data1 = (byte[]) msg.obj;
                            rawByteArray2RGBABitmap2(bmp_1,data1 ,640,480);
                            handleImage1(bmp_1);
                            break;
                        default:
                            break;
                    }
    
                    return false;
                }
    
    
    
    

    后面就是通过handleImage将Bitmap显示在ImageView上。

    NV21转bitmap的问题。
    我的程序基本是根据UVCCamera去修改的,UVCCamera中没有取每一帧,但是留有接口,我这里设定每一帧的数据为NV21格式。

    camera.setFrameCallback(mIFrameCallback, UVCCamera.PIXEL_FORMAT_NV21);
    

    并且在onFrame中取出的data格式是NV21(NV21是YUV中的一个,YUV有很多格式),因此在转成bitmap的时候,需要根据公式去转化。用系统自带的RenderScript去转换,发现图片虽然清晰但是颜色不对。

    查找yuv转bitmap函数,找到的是这个。依旧是转出来的图片,非常明显,成像很清晰,但是就是颜色不对。

    
    public Bitmap rawByteArray2RGBABitmap2(byte[] data, int width, int height) {
            int frameSize = width * height;
            int[] rgba = new int[frameSize];
                for (int i = 0; i < height; i++)
                    for (int j = 0; j < width; j++) {
                        int y = (0xff & ((int) data[i * width + j]));
                        int u = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 0]));
                        int v = (0xff & ((int) data[frameSize + (i >> 1) * width + (j & ~1) + 1]));
                        y = y < 16 ? 16 : y;
                        int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128));
                        int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128));
                        int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128));
                        r = r < 0 ? 0 : (r > 255 ? 255 : r);
                        g = g < 0 ? 0 : (g > 255 ? 255 : g);
                        b = b < 0 ? 0 : (b > 255 ? 255 : b);
                        rgba[i * width + j] = 0xff000000 + (b << 16) + (g << 8) + r;
                    }
            Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            bmp.setPixels(rgba, 0 , width, 0, 0, width, height);
            return bmp;
        }
    

    因此对函数进行了修改。修改后如下所示。

                public void rawByteArray2RGBABitmap2(Bitmap bitmap,byte[] data, int width, int height) {
                    int frameSize = width * height;
                    int[] rgba = new int[frameSize];
                    for (int h = 0; h < height; h++)
                        for (int w = 0; w < width; w++) {
                            int y = (0xff & ((int) data[h * width + w]));
                            int u = (0xff & ((int) data[frameSize + (h >> 1) * width + (w & ~1) + 0]));
                            int v = (0xff & ((int) data[frameSize + (h >> 1) * width + (w & ~1) + 1]));
                            y = y < 16 ? 16 : y;
    
                            int b = Math.round(1.164f * (y-16) + 2.018f * (u - 128));
                            int g = Math.round(1.164f * (y-16) - 0.813f * (v - 128) - 0.391f * (u - 128));
                            int r =  Math.round(1.164f * (y-16) + 1.596f*(v - 128));
    
                            r = r < 0 ? 0 : (r > 255 ? 255 : r);
                            g = g < 0 ? 0 : (g > 255 ? 255 : g);
                            b = b < 0 ? 0 : (b > 255 ? 255 : b);
    
                            rgba[h * width + w] = 0xff000000 + (r << 16) + (g << 8) + b;
                        }
    
                    bitmap.setPixels(rgba, 0 , width, 0, 0, width, height);
                    //return bmp;
                }
    
    

    由于对YUV与RGB这个概念模糊不清,而搜索得到的YUV转bitmap函数是虽然的确是NV21转bitmap,但转换后有色差。后修改rgba[]的赋值顺序后,颜色才正常。其实就是在由YUV编码转RGB编码的过程中,数据传递出现问题才导致色彩异常,这种情况就需要一步步分析YUV到RGB的数据转换中哪里出现了问题。之前是存储为ABGR,后面才是存储为ARGB。
    具体分析可以看这篇:
    RGB和YUV简单学习记录

    但是上面这种方式实际使用中发现效率过低。因此需要使用so的方式去转换。

    具体参考链接:
    libyuv—libyuv测试使用ARGBToI420和ConvertToARGB接口
    https://blog.csdn.net/XIAIBIANCHENG/article/details/73065646

    但是这个是yuvI420 数据转为bitmap 。我在使用的时候需要对format进行修改。

    即将void Java_com_example_libyuv_Test_convertToArgb函数中的FOURCC_IYUV改为FOURCC_NV21。

    Handle内存泄漏的问题。
    之前因为想偷懒所以没有处理Android studio关于Handle内存泄漏的提示。然后让程序跑了一天果然崩了。

    之前是如下所示,这种写法Android Studio会提示你这是有风险的。

    public class MainActivity extends AppCompatActivity {
        ........
        private Handler mHandler;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ........
            mHandler = new Handler(){
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                        case IMAGE_1:
                            .....
                            break;
                        case IMAGE_2:
                            .....
                            break;
                    }
                }
            };
              ........
        }
    }
    
    

    现在改成如下所示

    public class MainActivity extends AppCompatActivity{
        ....
        private Handler mHandler;
        ....
        private static class MyHandler extends Handler{
            //持有弱引用HandlerActivity,GC回收时会被回收掉.
            private final WeakReference<MainActivity> mActivityWeakReference;
            public MyHandler(MainActivity activity){
                mActivityWeakReference =new WeakReference<MainActivity>(activity);
            }
            @Override
            public void handleMessage(Message msg) {
                MainActivity activity= mActivityWeakReference.get();
                super.handleMessage(msg);
                if(activity!=null){
                    //执行业务逻辑
                    switch (msg.what) {
                        case IMAGE_1:
                            ....
                            break;
                        case IMAGE_2:
                            ....
                            break;
                    }
                }
            }
        }
    
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ....
            mHandler = new MyHandler(this);
            ....
        }
    
    
    }
    
    

    其实还可通过在activity的destroy中调用handler.removeCallbacksAndMessages(null),来达到避免内存泄漏的目的,android 系统源码中也基本是使用这个方法。

    //frameworks\base\core\java\android\os\Handler.java
        /**
         * Remove any pending posts of callbacks and sent messages whose
         * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
         * all callbacks and messages will be removed.
         */
        public final void removeCallbacksAndMessages(Object token) {
            mQueue.removeCallbacksAndMessages(this, token);
        }
    

    后记:这篇文章始于2018年,当时是用于双目USB摄像头上,最近在整理东西所以完善了一下。
    参考链接:
    Android yuv转换成bitmap
    图文详解YUV420数据格式
    Android中的YUV格式解析
    Nv21转Bitmap(高效率转化)
    Android 之 Bitmap
    Android解惑之Handler为什么需要是static的

    相关文章

      网友评论

          本文标题:UVC Camera使用简单记录

          本文链接:https://www.haomeiwen.com/subject/hbjcgftx.html