【问题标题】:How to fix the image preprocessing difference between tensorflow and android studio?如何修复tensorflow和android studio之间的图像预处理差异?
【发布时间】:2019-09-25 20:20:11
【问题描述】:

我正在尝试使用 keras 构建分类模型并将模型部署到我的 Android 手机。我使用来自this website 的代码将我自己的转换模型(一个 .pb 文件)部署到我的 Android 手机。我从手机加载了一张图片,一切正常,但预测结果与我从电脑上得到的结果完全不同。

在我的电脑上测试的过程是:

  1. 用cv2加载图片,并转换成np.float32

  2. 使用 keras resnet50 'preprocess_input' python 函数对图片进行预处理

  3. 扩展图片维度进行批处理(batch size为1)

  4. 将图像转发到模型并得到结果

相关代码:

img = cv2.imread('./my_test_image.jpg')
x = preprocess_input(img.astype(np.float32))
x = np.expand_dims(x, axis=0)
net = load_model('./my_model.h5')
prediction_result = net.predict(x)

而且我注意到Android的图像预处理部分与我在keras中使用的方法不同,模式是caffe(将图像从RGB转换为BGR,然后将每个颜色通道相对于ImageNet数据集进行零中心化) .似乎原始代码用于模式 tf(将在 -1 到 1 之间缩放像素)。

所以我将下面的“preprocessBitmap”代码修改为我认为应该的样子,并使用像素值为 [127,127,127] 的 3 通道 RGB 图像进行测试。该代码预测的结果与 .h5 模型的结果相同。但是当我加载图像进行分类时,预测结果与 .h5 模型不同。

有人知道吗?非常感谢。

我尝试了以下方法:

  1. 在我的手机中加载一个像素值为 [127,127,127] 的 3 通道 RGB 图像,并使用下面的修改代码,它会给我一个与在 PC 上使用 .h5 模型的预测结果相同的预测结果。

  2. 使用带有图像的 tensorflow gfile 模块测试转换后的 .pb 模型在 PC 上,它给了我正确的预测结果(与 .h5 模型相比)。所以我认为转换后的 .pb 文件没有任何问题。

preprocessBitmap 的整个部分

// code of 'preprocessBitmap' section in TensorflowImageClassifier.java
TraceCompat.beginSection("preprocessBitmap");
        // Preprocess the image data from 0-255 int to normalized float based
        // on the provided parameters.
        bitmap.getPixels(intValues, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());

        for (int i = 0; i < intValues.length; ++i) {
            // this is a ARGB format, so we need to mask the least significant 8 bits to get blue, and next 8 bits to get green and next 8 bits to get red. Since we have an opaque image, alpha can be ignored.
            final int val = intValues[i];

            // original
            /*
            floatValues[i * 3 + 0] = (((val >> 16) & 0xFF) - imageMean) / imageStd;
            floatValues[i * 3 + 1] = (((val >> 8) & 0xFF) - imageMean) / imageStd;
            floatValues[i * 3 + 2] = ((val & 0xFF) - imageMean) / imageStd;
            */

            // what I think it should be to do the same thing in mode caffe when using keras
            floatValues[i * 3 + 0] = (((val >> 16) & 0xFF) - (float)123.68);
            floatValues[i * 3 + 1] = (((val >> 8) & 0xFF) - (float)116.779);
            floatValues[i * 3 + 2] = (((val & 0xFF)) - (float)103.939);
        }
TraceCompat.endSection();

【问题讨论】:

    标签: android tensorflow keras image-preprocessing


    【解决方案1】:

    这个问题很老,但仍然是 Android 上 ResNet50 的 preprocess_input 的最高 Google 结果。我找不到为 Java/Android 实现 preprocess_input 的答案,所以我根据原始 python/keras 代码提出了以下内容:

    /*
        Preprocesses RGB bitmap IAW keras/imagenet
    
        Port of https://github.com/tensorflow/tensorflow/blob/v2.3.1/tensorflow/python/keras/applications/imagenet_utils.py#L169
        with data_format='channels_last', mode='caffe'
    
         Convert the images from RGB to BGR, then will zero-center each color channel with respect to the ImageNet dataset, without scaling. 
         Returns 3D float array
    */
    static float[][][] imagenet_preprocess_input_caffe( Bitmap bitmap ) {
        // https://github.com/tensorflow/tensorflow/blob/v2.3.1/tensorflow/python/keras/applications/imagenet_utils.py#L210
        final float[] imagenet_means_caffe = new float[]{103.939f, 116.779f, 123.68f};
    
        float[][][] result = new float[bitmap.getHeight()][bitmap.getWidth()][3];   // assuming rgb
        for (int y = 0; y < bitmap.getHeight(); y++) {
            for (int x = 0; x < bitmap.getWidth(); x++) {
    
                final int px = bitmap.getPixel(x, y);
    
                // rgb-->bgr, then subtract means.  no scaling
                result[y][x][0] = (Color.blue(px) - imagenet_means_caffe[0] );
                result[y][x][1] = (Color.green(px) - imagenet_means_caffe[1] );
                result[y][x][2] = (Color.red(px) - imagenet_means_caffe[2] );
            }
        }
    
        return result;
    }
    

    使用形状为 (1,224,224,3) 的 3D tensorflow-lite 输入:

    Bitmap bitmap = <your bitmap of size 224x224x3>;
    float[][][][] imgValues = new float[1][bitmap.getHeight()][bitmap.getWidth()][3];
    imgValues[0]=imagenet_preprocess_input_caffe(bitmap);
    
    ... <prep tfInput, tfOutput> ...
    
    tfLite.run(tfInput, tfOutput);
    

    【讨论】:

      猜你喜欢
      • 2021-05-27
      • 2021-05-24
      • 2012-02-06
      • 1970-01-01
      • 1970-01-01
      • 2020-11-21
      • 1970-01-01
      • 2011-12-22
      • 1970-01-01
      相关资源
      最近更新 更多