Преобразование растрового изображения в ByteBuffer (float) в Tensorflow-lite Android - PullRequest
1 голос
/ 20 апреля 2019

В демонстрационном Android-коде tenorflow-lite для классификации изображений изображения сначала преобразуются в формат ByteBuffer для повышения производительности. Это преобразование из растрового формата в формат с плавающей запятой и последующее преобразование в байтовый буфер представляется дорогостоящей операцией (циклы, побитовые операторы, float mem-copy и т. д.) Мы пытались реализовать ту же логику с opencv, чтобы получить некоторое преимущество в скорости. Следующий код работает без ошибок; но из-за некоторой логической ошибки в этом преобразовании выходные данные модели (в которую эти данные передаются) кажутся неверными. Предполагается, что входные данные модели будут RGB с типом данных float [1 197 197,3].

Как мы можем ускорить этот процесс преобразования растрового изображения в байтовый буфер, используя opencv (или любым другим способом)?

Преобразование стандартного растрового изображения в байтовый буфер: -

/** 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 Ответ

1 голос
/ 01 мая 2019
  1. Я считаю, что 255/0 в вашем коде - ошибка копирования / вставки, а не реальный код.
  2. Интересно, какова временная стоимость решения на чистой Java, особенно когда вы сравниваете его с временными затратами логического вывода. Для меня, с немного большим битовым массивом для Google mobilenet_v1_1.0_224, подготовка простого плавающего буфера составляла менее 5% времени вывода.
  3. Я мог бы квантовать модель tflite (с той же утилитой tflite_convert , которая генерировала файл .tflite из .h5. На самом деле могло быть три операции квантования, но я использовал только две: --inference_input_type=QUANTIZED_UINT8 и --post_training_quantize.
    • Полученная модель составляет примерно 25% от размера float32, что само по себе является достижением.
    • Полученная модель работает примерно в два раза быстрее (по крайней мере, на некоторых устройствах).
    • И полученная модель потребляет unit8 входов. Это означает, что вместо imgData.putFloat(((val>> 16) & 0xFF) / 255.f) мы пишем imgData.put((val>> 16) & 0xFF) и т. Д.

Кстати, я не думаю, что ваши формулы верны. Для достижения максимальной точности при использовании буферов float32 мы используем

putFLoat(byteval / 256f)

, где byteval - это int в диапазоне [0: 255].

...