簡體   English   中英

在 Tensorflow-lite Android 中將 Bitmap 轉換為 ByteBuffer (float)

[英]Converting Bitmap to ByteBuffer (float) in Tensorflow-lite Android

在用於圖像分類的 tensorflow-lite android 演示代碼中,圖像首先被轉換為 ByteBuffer 格式以獲得更好的性能。 這種從位圖到浮點格式的轉換以及隨后到字節緩沖區的轉換似乎是一個昂貴的操作(循環、按位運算符、 float mem-copy 等)。我們試圖用 opencv 實現相同的邏輯以獲得一些速度優勢。以下代碼可以正常工作; 但是由於這個轉換中的一些邏輯錯誤,模型的輸出(這個數據被饋送到)似乎是不正確的。模型的輸入應該是數據類型為 float[1,197,197,3] 的 RGB。

我們如何使用 opencv(或任何其他方式)加速位圖到字節緩沖區的轉換過程?

標准位圖到 ByteBuffer 轉換:-

/** Writes Image data into a {@code ByteBuffer}. */
  private void convertBitmapToByteBuffer(Bitmap bitmap) {
    if (imgData == null) {
      return;
    }
    imgData.rewind();


    bitmap.getPixels(intValues, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());



    long startTime = SystemClock.uptimeMillis();

    // Convert the image to floating point.
    int pixel = 0;

    for (int i = 0; i < getImageSizeX(); ++i) {
      for (int j = 0; j < getImageSizeY(); ++j) {
        final int val = intValues[pixel++];

        imgData.putFloat(((val>> 16) & 0xFF) / 255.f);
        imgData.putFloat(((val>> 8) & 0xFF) / 255.f);
        imgData.putFloat((val & 0xFF) / 255.f);
      }
    }

    long endTime = SystemClock.uptimeMillis();
    Log.d(TAG, "Timecost to put values into ByteBuffer: " + Long.toString(endTime - startTime));
  }

OpenCV 位圖到 ByteBuffer :-

    /** Writes Image data into a {@code ByteBuffer}. */
      private void convertBitmapToByteBuffer(Bitmap bitmap) {
        if (imgData == null) {
          return;
        }
        imgData.rewind();


        bitmap.getPixels(intValues, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());

        long startTime = SystemClock.uptimeMillis();


        Mat bufmat = new Mat(197,197,CV_8UC3);
        Mat newmat = new Mat(197,197,CV_32FC3);


        Utils.bitmapToMat(bitmap,bufmat);
        Imgproc.cvtColor(bufmat,bufmat,Imgproc.COLOR_RGBA2RGB);

        List<Mat> sp_im = new ArrayList<Mat>(3);


        Core.split(bufmat,sp_im);

        sp_im.get(0).convertTo(sp_im.get(0),CV_32F,1.0/255/0);
        sp_im.get(1).convertTo(sp_im.get(1),CV_32F,1.0/255.0);
        sp_im.get(2).convertTo(sp_im.get(2),CV_32F,1.0/255.0);

        Core.merge(sp_im,newmat);



        //bufmat.convertTo(newmat,CV_32FC3,1.0/255.0);
        float buf[] = new float[197*197*3];


        newmat.get(0,0,buf);

        //imgData.wrap(buf).order(ByteOrder.nativeOrder()).getFloat();
        imgData.order(ByteOrder.nativeOrder()).asFloatBuffer().put(buf);


        long endTime = SystemClock.uptimeMillis();
        Log.d(TAG, "Timecost to put values into ByteBuffer: " + Long.toString(endTime - startTime));
      }
  1. 我相信您代碼中的255/0是復制/粘貼錯誤,而不是真正的代碼。
  2. 我想知道純 Java 解決方案的時間成本是多少,尤其是當您將其與推理的時間成本進行權衡時。 對我來說,對於 Google 的mobilenet_v1_1.0_224稍大的位圖,簡單的浮點緩沖區准備時間不到推理時間的 5%。
  3. 我可以量化 tflite 模型(使用從.h5生成.tflite文件的相同tflite_convert實用程序。實際上可能有三個量化操作,但我只使用了兩個: --inference_input_type=QUANTIZED_UINT8--post_training_quantize
    • 生成的模型大約是 float32 模型的 25%,這本身就是一項成就。
    • 生成的模型運行速度大約快兩倍(至少在某些設備上)。
    • 並且,生成的模型消耗unit8輸入。 這意味着我們編寫imgData.put((val>> 16) & 0xFF)而不是imgData.putFloat(((val>> 16) & 0xFF) / 255.f) imgData.put((val>> 16) & 0xFF) ,依此類推。

順便說一句,我不認為你的公式是正確的。 為了在涉及 float32 緩沖區時獲得最佳精度,我們使用

putFLoat(byteval / 256f)

其中byteval是 [0:255] 范圍內的 int。

如所提到的這里使用下面的代碼從這里用於轉換的位圖,以ByteBuffer(float32)

private fun convertBitmapToByteBuffer(bitmap: Bitmap): ByteBuffer? {
    val byteBuffer =
        ByteBuffer.allocateDirect(4 * BATCH_SIZE * inputSize * inputSize * PIXEL_SIZE)
    byteBuffer.order(ByteOrder.nativeOrder())
    val intValues = IntArray(inputSize * inputSize)
    bitmap.getPixels(intValues, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
    var pixel = 0
    for (i in 0 until inputSize) {
        for (j in 0 until inputSize) {
            val `val` = intValues[pixel++]
            byteBuffer.putFloat(((`val` shr 16 and 0xFF) - IMAGE_MEAN) / IMAGE_STD)
            byteBuffer.putFloat(((`val` shr 8 and 0xFF) - IMAGE_MEAN) / IMAGE_STD)
            byteBuffer.putFloat(((`val` and 0xFF) - IMAGE_MEAN) / IMAGE_STD)
        }
    }
    return byteBuffer
}

對於浮點數,mean = 1 和 std = 255.0 函數將是:

fun bitmapToBytebufferWithOpenCV(bitmap: Bitmap): ByteBuffer {
            val startTime = SystemClock.uptimeMillis()
            val imgData = ByteBuffer.allocateDirect(1 * 257 * 257 * 3 * 4)
            imgData.order(ByteOrder.nativeOrder())

            val bufmat = Mat()
            val newmat = Mat()
            Utils.bitmapToMat(bitmap, bufmat)
            Imgproc.cvtColor(bufmat, bufmat, Imgproc.COLOR_RGBA2RGB)
            val splitImage: List<Mat> = ArrayList(3)

            Core.split(bufmat, splitImage)
            splitImage[0].convertTo(splitImage[0], CV_32F, 1.0 / 255.0)
            splitImage[1].convertTo(splitImage[1], CV_32F, 1.0 / 255.0)
            splitImage[2].convertTo(splitImage[2], CV_32F, 1.0 / 255.0)
            Core.merge(splitImage, newmat)

            val buf = FloatArray(257 * 257 * 3)
            newmat.get(0, 0, buf)

            for (i in buf.indices) {
                imgData.putFloat(buf[i])
            }
            imgData.rewind()
            val endTime = SystemClock.uptimeMillis()
            Log.v("Bitwise", (endTime - startTime).toString())
            return imgData
        }

不幸的是,這比 Sunit 上面寫的 for 循環和按位運算(8ms)稍慢(10ms)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM