Android语义分割后处理太慢
Posted
技术标签:
【中文标题】Android语义分割后处理太慢【英文标题】:Android semantic segmentation post-processing is too slow 【发布时间】:2020-10-03 11:03:16 【问题描述】:如果有人能就我上周一直在工作但没有成功的任务提供建议,我将不胜感激。 我有语义分割模型(MobileNetV3 + Lightweight ASPP)。简短信息:输入 - 1024x1024,输出 - 相同大小和 2 个类(bg 和车辆),所以我的输出形状是(1、1048576、2)。我不是移动开发人员或 java 世界的人,所以我使用了一些完整的 android 示例进行图像分割来测试它: 来自谷歌的那个:https://github.com/tensorflow/examples/tree/master/lite/examples/image_segmentation 另一个开源的:https://github.com/pillarpond/image-segmenter-android
我成功地将其转换为 tflite 格式,并且它在启用 GPU 且 10 个线程的 OnePlus 7 上的推理时间在 105-140 毫秒之间。但是在这里我遇到了一个问题:这两个 android 示例或任何您可以找到的语义分割的一般执行时间约为 1050-1300 毫秒(小于 1FPS)。该管道中较慢的部分是图像后处理(~900-1150ms)。您可以在 Deeplab#segment 方法中看到该部分。因为除了 bg 之外我只有 1 个班级 - 我没有 this third loop,但其他所有内容都没有受到影响,而且仍然很慢。与其他常见的移动尺寸(如 128/226/512)相比,输出尺寸并不小,但仍然如此。我认为在现代智能手机上处理 1024x1024 矩阵并在画布上绘制矩形不应该花费太多时间。 我尝试了不同的解决方案,例如将矩阵操作拆分为线程或创建所有这些对象(如 RectF 和 Recognition),然后在嵌套循环中用新数据填充它们的属性,但我没有成功。在桌面端,我用 numpy 和 opencv 轻松处理它,我什至不了解如何在 Android 中做同样的事情,它是否有效。 这是我在 python 中使用的代码:
CLASS_COLORS = [(0, 0, 0), (255, 255, 255)] # black for bg and white for mask
def get_image_array(image_input, width, height):
img = cv2.imread(image_input, 1)
img = cv2.resize(img, (width, height))
img = img.astype(np.float32)
img[:, :, 0] -= 128.0
img[:, :, 1] -= 128.0
img[:, :, 2] -= 128.0
img = img[:, :, ::-1]
return img
def get_segmentation_array(seg_arr, n_classes):
output_height = seg_arr.shape[0]
output_width = seg_arr.shape[1]
seg_img = np.zeros((output_height, output_width, 3))
for c in range(n_classes):
seg_arr_c = seg_arr[:, :] == c
seg_img[:, :, 0] += ((seg_arr_c)*(CLASS_COLORS[c][0])).astype('uint8')
seg_img[:, :, 1] += ((seg_arr_c)*(CLASS_COLORS[c][1])).astype('uint8')
seg_img[:, :, 2] += ((seg_arr_c)*(CLASS_COLORS[c][2])).astype('uint8')
return seg_img
interpreter = tf.lite.Interpreter(model_path=f"my_model.tflite")
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
img_arr = get_image_array("input.png", 1024, 1024)
interpreter.set_tensor(input_details[0]['index'], np.array([x]))
interpreter.invoke()
output = interpreter.get_tensor(output_details[0]['index'])
output = output.reshape((1024, 1024, 2)).argmax(axis=2)
seg_img = get_segmentation_array(output, 2)
cv2.imwrite("output.png", seg_img)
也许有什么比当前的后处理解决方案更强大。 我真的很感激这方面的任何帮助。我确信有任何东西可以改进后处理并将其时间减少到 ~100 毫秒,所以我一般会有 ~5FPS。
【问题讨论】:
我认为你应该在 android 中尝试 OpenCv。看看this 项目,其中 OpenCv 在一个用于分割肺病的安卓应用程序中使用。 OpenCv 有一个用于斑点检测的类,它可以在斑点(掩码)的边缘绘制矩形。如果您需要任何帮助,请标记我 感谢@Farmaker 的帮助! 【参考方案1】:新更新。感谢Farmaker,我使用了在他的仓库中从上面的评论中找到的一段代码,现在管道看起来像:
int channels = 3;
int n_classes = 2;
int float_byte_size = 4;
int width = model.inputWidth;
int height = model.inputHeight;
int[] intValues = new int[width * height];
ByteBuffer inputBuffer = ByteBuffer.allocateDirect(width * height * channels * float_byte_size).order(ByteOrder.nativeOrder());
ByteBuffer outputBuffer = ByteBuffer.allocateDirect(width * height * n_classes * float_byte_size).order(ByteOrder.nativeOrder());
Bitmap input = textureView.getBitmap(width, height);
input.getPixels(intValues, 0, width, 0, 0, height, height);
inputBuffer.rewind();
outputBuffer.rewind();
for (final int value: intValues)
inputBuffer.putFloat(((value >> 16 & 0xff) - 128.0) / 1.0f);
inputBuffer.putFloat(((value >> 8 & 0xff) - 128.0) / 1.0f);
inputBuffer.putFloat(((value & 0xff) - 128.0) / 1.0f);
tfLite.run(inputBuffer, outputBuffer);
final Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
outputBuffer.flip();
int[] pixels = new int[width * height];
for (int i = 0; i < width * height; i++)
float max = outputBuffer.getFloat();
float val = outputBuffer.getFloat();
int id = val > max ? 1 : 0;
pixels[i] = id == 0 ? 0x00000000 : 0x990000ff;
output.setPixels(pixels, 0, width, 0, 0, width, height);
resultView.setImageBitmap(resizeBitmap(output, resultView.getWidth(), resultView.getHeight()));
public static Bitmap resizeBitmap(Bitmap bm, int newWidth, int newHeight)
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// "RECREATE" THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(
bm, 0, 0, width, height, matrix, false);
bm.recycle();
return resizedBitmap;
现在的后处理时间约为 70-130 毫秒,第 95 次约为 90 毫秒,此外,图像预处理时间约为 60 毫秒,推理时间约为 140 毫秒,其他启用 GPU 和 10 线程的东西大约需要 30-40 毫秒我的一般执行时间约为 330 毫秒,即 3FPS!这是针对 1024x1024 的大型模型。 在这一点上,我非常满意,并想为我的模型尝试不同的配置,包括 MobilenetV3 small 作为主干。
【讨论】:
以上是关于Android语义分割后处理太慢的主要内容,如果未能解决你的问题,请参考以下文章