zoukankan      html  css  js  c++  java
  • 摄像头 数据转换YCbCr_420_SP

    http://eyehere.net/2011/android-camera-2/

    上次讲的是摄像头的初始化,如果觉得这么就万事OK的话,那就大错特错了。接下来的东西让人感到更加头痛。

    在我的这个应用里,不需要把拍下来的图片存储,只需要把预览的图片数据处理一下就好,很自然的我只是用了onPreviewFrame调用,考虑处理传递进来的data数据流就是了。

    网上很多帖子都说,然后用BitmapFactory的decodeByteArray()函数来解析图片就行了,我试了一下,发现这真是彻头彻尾 的谎言,data字节流默认是YCbCr_420_SP(虽然可以改,但其他的格式未必兼容),decodeByteArray()压根儿不 认!SDK2.2之后,似乎提供了一个YuvImage的类来转一下(那Google一开始提供这个借口是做什么的?),难道就要把老机给抛弃了么??万 万不能啊(穷人最理解穷人们了)!

    好在这个世界总是不缺少好人和牛人的,有人提供了这么一段转换的代码:
    static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) {
        final int frameSize = width * height;
     
        for (int j = 0, yp = 0; j < height; j++) {
            int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
            for (int i = 0; i < width; i++, yp++) {
                int y = (0xff & ((int) yuv420sp[yp])) - 16;
                if (y < 0) y = 0;
                if ((i & 1) == 0) {
                    v = (0xff & yuv420sp[uvp++]) - 128;
                    u = (0xff & yuv420sp[uvp++]) - 128;
                }
     
                int y1192 = 1192 * y;
                int r = (y1192 + 1634 * v);
                int g = (y1192 - 833 * v - 400 * u);
                int b = (y1192 + 2066 * u);
     
                if (r < 0) r = 0; else if (r > 262143) r = 262143;
                if (g < 0) g = 0; else if (g > 262143) g = 262143;
                if (b < 0) b = 0; else if (b > 262143) b = 262143;
     
                rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
            }
        }
    }

    我不是很清楚这里面的原理,但是它能在我这里工作,暂时可以了……然后你才可以吧处理完的rgb[]传给decodeByteArray()。

    顺便好心的把使用SDK2.2之后的也贴上吧,万一有用呢……

    public void onPreviewFrame(byte[] data, Camera arg1) {
        FileOutputStream outStream = null;
        try {
            YuvImage yuvimage = new YuvImage(data,ImageFormat.NV21,arg1.getParameters().getPreviewSize().width,arg1.getParameters().getPreviewSize().height,null);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            yuvimage.compressToJpeg(new Rect(0,0,arg1.getParameters().getPreviewSize().width,arg1.getParameters().getPreviewSize().height), 80, baos);
     
            outStream = new FileOutputStream(String.format("/sdcard/%d.jpg", System.currentTimeMillis()));
            outStream.write(baos.toByteArray());
            outStream.close();
     
            Log.d(TAG, "onPreviewFrame - wrote bytes: " + data.length);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
        Preview.this.invalidate();
    }
    哦,得到的图像旋转了90°(似乎有的机型设置一下setRotation(90)可以搞定,但还是那句话,不通用啊,况且这个是2.1之后的API)。手动转一下吧……

     Matrix matrix = new Matrix();

    matrix.postRotate(90);
    // 这里的rgb就是刚刚转换处理的东东
    Bitmap bmp = Bitmap.createBitmap(rgb, 0, w, w, h, Bitmap.Config.ARGB_4444);
    Bitmap nbmp = Bitmap.createBitmap(bmp,
                    0, 0, bmp.getWidth(),  bmp.getHeight(), matrix, true);

    终于正常了~~~

    考虑到需要做识别,自然得先把它转成灰度图像,经典心理公式Gray = R*0.299 + G*0.587 + B*0.114出场了,但是手机的计算速度不那么快,这样的浮点运算还是尽量避免吧~ 于是考虑Gray = (R*299 + G*587 + B*114 + 500) / 1000或者Gray = (R*30 + G*59 + B*11 + 50) / 100。但是除法总是还是不够快,用移位吧……Gray = (R*19595 + G*38469 + B*7472) >> 16,稍微小一点,用Gray = (R*38 + G*75 + B*15) >> 7也足够了。

    经过一番努力学习,把写就的代码兴致勃勃的在手机上跑了一下,虽然不够快结果出来了,想想也是大负荷运算啊,自我安慰客户应该可以有这样的耐心吧。

    就在这个时候,我突然想起一件很重要的事情!
    我需要的是灰度图,也就是亮度风量,而最开始的YUV,不就是亮度色度饱和度么?!那么Y分类不就是我需要的灰度值吗!!我在做什么,辛辛苦苦转成 RGB,再转成亮度,吃饱了撑着不是。想到这里我立刻用头撞墙九九一百八十一次,一悼念我那白白死去的脑细胞的在天之灵。立刻重写,删除大量代码,快多 了,效果也好~~ 鄙视一下两小时前的自己!


  • 相关阅读:
    mysql in like GROUP_CONCAT
    StringBuilder的常用方法
    mysql 中unionall 使用
    mysql中,数据库字段为时间戳转时间的处理方法
    一个数组储存多个对象
    Java中的substring()用法
    java思想篇1
    任务调配管理
    字符窜数组去重及各种常规用法
    自定义属性的设值
  • 原文地址:https://www.cnblogs.com/leaven/p/2615318.html
Copyright © 2011-2022 走看看