首页 > 解决方案 > TfLite 图像分类分数不一致,同一图像会不断增加,直到达到一定的饱和度(实际分数)

问题描述

对于相同的“解释器”实例,相同图像的分数会增加,直到达到某种饱和度。

Interpreter tflite = new Interpreter(loadModelFile(context));

为 ImageClassifier 创建实例并使用相同的实例对 Frame 进行分类并为同一图像运行推理。

ImageClassifier(Activity activity) throws IOException {
    tflite = new Interpreter(loadModelFile(activity));
    labelList = loadLabelList(activity);
    imgData =
        ByteBuffer.allocateDirect(
            DIM_BATCH_SIZE
                * getImageSizeX()
                * getImageSizeY()
                * DIM_PIXEL_SIZE
                * getNumBytesPerChannel());
    imgData.order(ByteOrder.nativeOrder());
    filterLabelProbArray = new float[FILTER_STAGES][getNumLabels()];
    Log.d(TAG, "Created a Tensorflow Lite Image Classifier.");
  }

对同一图像的帧进行分类。可以从 SD 卡中获取相同的图像。

private void classifyImage() {
    if (classifier == null || getActivity() == null || cameraDevice == null) {
      showToast("Uninitialized Classifier or invalid context.");
      return;
    }
    String imgPath =  "/storage/emulated/0/DCIM/test.jpg";
    Log.d("Image Path is %s", imgPath);
    Bitmap bitmap = BitmapFactory.decodeFile(imgPath);
    Bitmap newbitmap = Bitmap.createScaledBitmap(bitmap, 299, 299, false);
    String textToShow = classifier.classifyFrame(newbitmap);
    bitmap.recycle();
    showToast(textToShow);
  }

ClassifyFrame() ImageClassifier.java 的方法

String classifyFrame(Bitmap bitmap) {
    if (tflite == null) {
      Log.e(TAG, "Image classifier has not been initialized; Skipped.");
      return "Uninitialized Classifier.";
    }
    convertBitmapToByteBuffer(bitmap);
    // Here's where the magic happens!!!
    long startTime = SystemClock.uptimeMillis();
    runInference();
    long endTime = SystemClock.uptimeMillis();
    Log.d(TAG, "Timecost to run model inference: " + Long.toString(endTime - startTime));

    // Smooth the results across frames.
    applyFilter();

    // Print the results.
    String textToShow = printTopKLabels();
    textToShow = Long.toString(endTime - startTime) + "ms" + textToShow;
    return textToShow;
  }

ImageClassifier.java 的 applyFilter() 方法

void applyFilter() {
    int numLabels = getNumLabels();

    // Low pass filter `labelProbArray` into the first stage of the filter.
    for (int j = 0; j < numLabels; ++j) {
      filterLabelProbArray[0][j] +=
          FILTER_FACTOR * (getProbability(j) - filterLabelProbArray[0][j]);
    }
    // Low pass filter each stage into the next.
    for (int i = 1; i < FILTER_STAGES; ++i) {
      for (int j = 0; j < numLabels; ++j) {
        filterLabelProbArray[i][j] +=
            FILTER_FACTOR * (filterLabelProbArray[i - 1][j] - filterLabelProbArray[i][j]);
      }
    }

    // Copy the last stage filter output back to `labelProbArray`.
    for (int j = 0; j < numLabels; ++j) {
      setProbability(j, filterLabelProbArray[FILTER_STAGES - 1][j]);
    }
  }

打印前 K 个标签,作为结果显示在 UI 中。

  private String printTopKLabels() {
    for (int i = 0; i < getNumLabels(); ++i) {
      sortedLabels.add(
          new AbstractMap.SimpleEntry<>(labelList.get(i), getNormalizedProbability(i)));
      if (sortedLabels.size() > RESULTS_TO_SHOW) {
        sortedLabels.poll();
      }
    }
    String textToShow = "";
    final int size = sortedLabels.size();
    for (int i = 0; i < size; ++i) {
      Map.Entry<String, Float> label = sortedLabels.poll();
      textToShow = String.format("\n%s: %4.2f", label.getKey(), label.getValue()) + textToShow;
    }
    return textToShow;
  }

在应用程序第一次启动时,图像分类得分为 0.06,然后如果我们在某些事件上调用分类图像(),点击得分再次增加到 0.13,并且在相同的过程中它不断增加,直到达到 0.86(饱和度)。

我不确定它为什么会发生,但它发生在两种类型的 TfLite 模型 inceptionV3 和 MobileNet 上。

标签: androidtensorflow-lite

解决方案


结果由applyFilter方法过滤。它是一个简单的低通滤波器,因此分数逐渐达到其中期平均值。注释掉对的调用applyFilter,它应该会立即响应,但对于某些应用程序来说可能过于紧张。


推荐阅读