【问题标题】:Convert bitmap array to YUV (YCbCr NV21)将位图数组转换为 YUV (YCbCr NV21)
【发布时间】:2011-08-23 01:07:48
【问题描述】:

如何将BitmapFactory.decodeFile()返回的Bitmap转为YUV格式(类似于相机的onPreviewFrame()返回字节数组)?

【问题讨论】:

    标签: android image-processing android-camera yuv android-image


    【解决方案1】:

    下面是一些实际有效的代码:

        // untested function
        byte [] getNV21(int inputWidth, int inputHeight, Bitmap scaled) {
    
            int [] argb = new int[inputWidth * inputHeight];
    
            scaled.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);
    
            byte [] yuv = new byte[inputWidth*inputHeight*3/2];
            encodeYUV420SP(yuv, argb, inputWidth, inputHeight);
    
            scaled.recycle();
    
            return yuv;
        }
    
        void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
            final int frameSize = width * height;
    
            int yIndex = 0;
            int uvIndex = frameSize;
    
            int a, R, G, B, Y, U, V;
            int index = 0;
            for (int j = 0; j < height; j++) {
                for (int i = 0; i < width; i++) {
    
                    a = (argb[index] & 0xff000000) >> 24; // a is not used obviously
                    R = (argb[index] & 0xff0000) >> 16;
                    G = (argb[index] & 0xff00) >> 8;
                    B = (argb[index] & 0xff) >> 0;
    
                    // well known RGB to YUV algorithm
                    Y = ( (  66 * R + 129 * G +  25 * B + 128) >> 8) +  16;
                    U = ( ( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128;
                    V = ( ( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128;
    
                    // NV21 has a plane of Y and interleaved planes of VU each sampled by a factor of 2
                    //    meaning for every 4 Y pixels there are 1 V and 1 U.  Note the sampling is every other
                    //    pixel AND every other scanline.
                    yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
                    if (j % 2 == 0 && index % 2 == 0) { 
                        yuv420sp[uvIndex++] = (byte)((V<0) ? 0 : ((V > 255) ? 255 : V));
                        yuv420sp[uvIndex++] = (byte)((U<0) ? 0 : ((U > 255) ? 255 : U));
                    }
    
                    index ++;
                }
            }
        }
    

    【讨论】:

    • 如果有人需要 YV12 而不是 NV21,这个答案可以稍作修改以产生每个人最喜欢的三平面格式:gist.github.com/wobbals/5725412
    • 根据 yuv420sp 规范,U 值存储在缓冲区的最后 3 个 V 之前,因此我必须交换 if-case 内的行以防止蓝色/红色被翻转。
    • 我在来自BitmapFactory.decodeResource() 的位图上使用此方法时遇到了问题,但是当我从BitmapFactory.decodeStream(getAssets().open("myasset.bmp")) 加载位图时它工作正常。
    • encodeYUV420SP 在 inputWidth 或 inputHeight 为奇数时抛出 ArrayIndexOutOfRange 异常。将 yuv 数组声明为 byte [] yuv = new byte[inputHeight * inputWidth + 2 * (int) Math.ceil(inputHeight/2.0) *(int) Math.ceil(inputWidth/2.0)]; 解决了这个问题。
    • 永远不要使用 Java 进行此类对话,使用 RenderScript 或 Lubyuv 库。 Java 清晰的对话非常慢
    【解决方案2】:

    以下是将Bitmap转换为Yuv(NV21)格式的代码。

    void yourFunction(){
    
        // mBitmap is your bitmap
    
        int mWidth = mBitmap.getWidth();
        int mHeight = mBitmap.getHeight();
    
        int[] mIntArray = new int[mWidth * mHeight];
    
        // Copy pixel data from the Bitmap into the 'intArray' array
        mBitmap.getPixels(mIntArray, 0, mWidth, 0, 0, mWidth, mHeight);
    
        // Call to encoding function : convert intArray to Yuv Binary data
        encodeYUV420SP(data, intArray, mWidth, mHeight);
    
    }
    
    static public void encodeYUV420SP(byte[] yuv420sp, int[] rgba,
            int width, int height) {
        final int frameSize = width * height;
    
        int[] U, V;
        U = new int[frameSize];
        V = new int[frameSize];
    
        final int uvwidth = width / 2;
    
        int r, g, b, y, u, v;
        for (int j = 0; j < height; j++) {
            int index = width * j;
            for (int i = 0; i < width; i++) {
    
                r = Color.red(rgba[index]);
                g = Color.green(rgba[index]);
                b = Color.blue(rgba[index]);
    
                // rgb to yuv
                y = (66 * r + 129 * g + 25 * b + 128) >> 8 + 16;
                u = (-38 * r - 74 * g + 112 * b + 128) >> 8 + 128;
                v = (112 * r - 94 * g - 18 * b + 128) >> 8 + 128;
    
                // clip y
                yuv420sp[index] = (byte) ((y < 0) ? 0 : ((y > 255) ? 255 : y));
                U[index] = u;
                V[index++] = v;
            }
        }
    

    【讨论】:

    • 试过了,但是在第 r = (rgba[index] & 0xff000000) >> 24;
    • 这段代码效率极低,最好避开这段代码。
    【解决方案3】:

    bmp 文件将是 RGB888 格式,因此您需要将其转换为 YUV。 我没有在 Android 中遇到任何可以为您执行此操作的 api。

    但是您可以自己执行此操作,请参阅此link 以了解如何..

    【讨论】:

    • 请提供实际答案,而不是链接到内容。链接可能会过时。
    【解决方案4】:

    如果使用java将Bitmap转换为YUV byte[]对你来说太慢了,你可以试试libyuv by Google

    【讨论】:

      【解决方案5】:

      通过OpenCV 库,您可以将encodeYUV420SP java 函数替换为一个本机OpenCV 行,它的速度~4x 更快

      Mat mFrame = Mat(height,width,CV_8UC4,pFrameData).clone();
      

      完整示例:

      Java端:

          Bitmap bitmap = mTextureView.getBitmap(mWidth, mHeight);
          int[] argb = new int[mWidth * mHeight];
          // get ARGB pixels and then proccess it with 8UC4 opencv convertion
          bitmap.getPixels(argb, 0, mWidth, 0, 0, mWidth, mHeight);
          // native method (NDK or CMake)
          processFrame8UC4(argb, mWidth, mHeight);
      

      本机端(NDK):

      JNIEXPORT jint JNICALL com_native_detector_Utils_processFrame8UC4
          (JNIEnv *env, jobject object, jint width, jint height, jintArray frame) {
      
          jint *pFrameData = env->GetIntArrayElements(frame, 0);
          // it is the line:
          Mat mFrame = Mat(height,width,CV_8UC4,pFrameData).clone();
          // the next only is a extra example to gray convertion:
          Mat mout;
          cvtColor(mFrame, mout,CV_RGB2GRAY);
          int objects = face_detection(env, mout);
          env->ReleaseIntArrayElements(frame, pFrameData, 0);
          return objects;
      }
      

      【讨论】:

        【解决方案6】:

        首先你计算 rgb 数据:

        r=(p>>16) & 0xff;
        g=(p>>8) & 0xff;
        b= p & 0xff;
        y=0.2f*r+0.7*g+0.07*b;
        u=-0.09991*r-0.33609*g+0.436*b;
        v=0.615*r-0.55861*g-0.05639*b;
        

        y、u 和 v 是 yuv 矩阵的合成子。

        【讨论】:

        • 这几乎没有回答任何问题,没有解释数据是如何打包的。
        猜你喜欢
        • 1970-01-01
        • 2017-06-19
        • 2012-09-10
        • 1970-01-01
        • 2013-07-05
        • 2015-11-23
        • 2018-06-15
        • 2011-04-26
        • 1970-01-01
        相关资源
        最近更新 更多