Документация делегата Tensorflow Lite gpu обеспечивает более быстрый способ запуска вывода tflite с использованием Opengl и SSBO в Android [3]. В документации приведен пример кода для создания и привязки SSBO с помощью
изображение уже в графическом процессоре. Как мы можем скопировать или преобразовать изображение с живого фотоаппарата Android и скопировать его в SSBO, используя шейдерный код OpenGL? Когда мы просто сбрасываем память процессора в SSBO, производительность становится хуже по сравнению с
нормальное выполнение делегата GPU. Итак, каков правильный или наиболее эффективный способ передачи изображения с камеры в SSBO, чтобы ускорить вывод tflite?
В следующем коде мы попытались преобразовать кадр камеры в растровое изображение.
а затем преобразовать его в текстуру и, наконец, скопировать его в SSBO. Однако этот метод сравнительно медленнее, чем обычный конвейер выполнения делегатов графического процессора (где данные копируются из ЦП в накладные расходы графического процессора). Цель состоит в том, чтобы уменьшить
Копирование данных изображения с CPU на GPU путем размещения данных изображения в памяти GPU и последующей передачи их модели.
Мы можем запустить модель [1] за 40-50 мс, используя стандартный механизм вывода делегатов GPU; тогда как это занимает 90-100 мс
используя вышеупомянутый метод SSBO (неоптимизированный). Вышеуказанное время относится к
время запуска метода interpreter.run()
в тензорном потоке lite.
Также похоже, что этот механизм SSBO работает только с OpenGL ES 3.1 или выше.
Идеальным вариантом использования (как предлагает тензор потока) является следующий [2]:
- Вы получаете вход камеры в виде текстуры поверхности.
- Создание объекта буфера хранилища OpenGL (SSBO).
Используйте GPUDelegate.bindGlBufferToTensor()
, чтобы связать это SSBO с входным тензором.
Напишите небольшую шейдерную программу для эффективного сброса текстуры поверхности [1] в этот SSBO из [2].
Выполнить вывод.
Мы можем получить кадры камеры в виде необработанных байтов или преобразовать их в текстуру и даже отобразить их в GLSurface View.
Но мы можем добиться ускорения, как это было предложено тензорным потоком.
- 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;
}