Tensorflow lite gpu委托文档提供了一种更快的方法,用于在Android [3]中使用Opengl和SSBO运行tflite推理。该文档提供了示例代码,用于创建SSBO并将其绑定到
图像已在GPU中。我们如何从android实时相机复制或转换图像,然后使用OpenGL着色器代码将其复制到SSBO?当我们仅将CPU内存转储到SSBO时,与
正常的gpu委托执行。那么将相机图像传递到SSBO以便使tflite推理更快的正确或最有效的方法是什么?
在下面的代码中,我们尝试将相机框架转换为位图
然后将其转换为纹理,最后将其复制到SSBO。但是,此方法比正常的GPU委托执行管道(将数据从CPU复制到GPU的开销)要慢得多。目的是减少
通过使图像数据在GPU内存中可用,然后将其传递给模型,CPU至GPU复制图像数据。
我们可以使用标准的GPU委托推理机制在40-50毫秒的时间内运行模型[1];而需要90-100毫秒
使用上述SSBO方法(未优化)。以上时间指
在TensorFlow Lite中运行interpreter.run()
方法的时间。
而且看起来这种SSBO机制仅适用于OpenGL ES 3.1或更高版本。
理想的用例(由tensorflow建议)如下[2]:
您会以表面纹理的形式获得相机输入。
创建一个OpenGL着色器存储缓冲区对象(SSBO)。
使用GPUDelegate.bindGlBufferToTensor()
将该SSBO与输入张量关联。
编写一个小的着色器程序,将[1]的表面纹理有效地转储到[2]的SSBO中。
运行推断。
我们能够以原始字节的形式获取相机帧,或者将其转换为纹理,甚至将其渲染到GLSurface视图。
但是,我们能够实现张量流建议的加速。
https://github.com/tensorflow/tensorflow/issues/26297
https://github.com/tensorflow/tensorflow/issues/25657#issuecomment-466489248
https://www.tensorflow.org/lite/performance/gpu_advanced#android_2
Android代码:
public int[] initializeShaderBuffer(){
android.opengl.EGLContext eglContext = eglGetCurrentContext();
int[] id = new int[1];
GLES31.glGenBuffers(id.length, id, 0);
GLES31.glBindBuffer(GL_SHADER_STORAGE_BUFFER, id[0]);
GLES31.glBufferData(GL_SHADER_STORAGE_BUFFER, 257*257*3*4, null, GLES31.GL_STREAM_COPY);
GLES31.glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);// unbind
return id;
}
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
.....
.....
mTextureDataHandle0 = TextureHelper.loadTexture(mActivityContext,
R.drawable.srcim);//No error
}
@Override
public void onDrawFrame(GL10 glUnused) {
int inputSsboId = initializeShaderBuffer()[0];
interpreter = new Interpreter(GLActivity.tfliteModel);
Tensor inputTensor = interpreter.getInputTensor(0);
GpuDelegate gpuDelegate = new GpuDelegate();
gpuDelegate.bindGlBufferToTensor(inputTensor, inputSsboId);
interpreter.modifyGraphWithDelegate(gpuDelegate);
final int computeShaderHandle = ShaderHelper.compileShader(
GLES31.GL_COMPUTE_SHADER, fragmentShader);//No error
mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle,
computeShaderHandle);//No error
mTextureUniformHandle0 = GLES31.glGetUniformLocation(mProgramHandle,
"u_Texture0");
/**
* First texture map
*/
// Set the active texture0 unit to texture unit 0.
GLES31.glActiveTexture(GLES31.GL_TEXTURE0 );
// Bind the texture to this unit.
GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, mTextureDataHandle0);
// Tell the texture uniform sampler to use this texture in the shader by
// binding to texture unit 0.
GLES31.glUniform1i(mTextureUniformHandle0, 0);
GLES31.glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 1, inputSsboId, 0, 257*257*3*4);
GLES31.glUseProgram(mProgramHandle);
if(compute==1)//Always set to 1
GLES31.glDispatchCompute(16,16,1);
GLES31.glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // unbind
GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, 0); // unbind
//Tflite code ...
byte [][] outputArray = new byte [1][66049];//size based on model output
Log.d("GPU_CALL_RUN","DONE");
long oms1=System.currentTimeMillis();
interpreter.run(null,outputArray);
long cms1=System.currentTimeMillis();
Log.d("TIME_RUN_MODEL",""+(cms1-oms1));
Log.d("OUTVAL", Arrays.deepToString(outputArray));
}
计算着色器:-
#version 310 es
layout(local_size_x = 16, local_size_y = 16) in;
layout(binding = 0) uniform sampler2D u_Texture0;
layout(std430) buffer;
layout(binding = 1) buffer Output { float elements[]; } output_data;
void main() {
ivec2 gid = ivec2(gl_GlobalInvocationID.xy);
//if (gid.x >= 257 || gid.y >= 257) return;
vec3 pixel = texelFetch(u_Texture0, gid, 0).xyz;
int linear_index = 3 * (gid.y * 257 + gid.x);
output_data.elements[linear_index + 0] = pixel.x;
output_data.elements[linear_index + 1] = pixel.y;
output_data.elements[linear_index + 2] = pixel.z;
}
最佳答案
没有简单的方法可以直接将SurfaceTexture转储到SSBO。最简单的路径是SurfaceTexture-> GlTexture-> SSBO。 TFLite GPU团队也在尝试引入另一个API(bindGlTextureToTensor),但是直到那为止,这里是我用于GlTexutre-> SSBO转换的着色器程序:
#version 310 es
layout(local_size_x = 16, local_size_y = 16) in;
layout(binding = 0) uniform sampler2D input_texture;
layout(std430) buffer;
layout(binding = 1) buffer Output { float elements[]; } output_data;
void main() {
ivec2 gid = ivec2(gl_GlobalInvocationID.xy);
if (gid.x >= 224 || gid.y >= 224) return;
vec3 pixel = texelFetch(input_texture, gid, 0).xyz;
int linear_index = 3 * (gid.y * 224 + gid.x);
output_data.elements[linear_index + 0] = pixel.x;
output_data.elements[linear_index + 1] = pixel.y;
output_data.elements[linear_index + 2] = pixel.z;
}
请注意,这是针对输入张量大小为224x224x3的MobileNet v1。