Android шейдерная программа opengl для копирования изображения с камеры в SSBO для вывода графического процессора TF-lite

1

Документация делегата gpu Tensorflow lite обеспечивает более быстрый способ запуска вывода tflite с использованием Opengl и SSBO в Android [3]. В документации приведен пример кода для создания и связывания SSBO с изображением, уже имеющимся в графическом процессоре. Как мы можем скопировать или преобразовать изображение с живого фотоаппарата Android и скопировать его в SSBO, используя шейдерный код OpenGL? Когда мы просто сбрасываем память процессора в SSBO, производительность становится хуже по сравнению с обычным выполнением делегата gpu. Итак, каков правильный или наиболее эффективный способ передачи изображения с камеры в SSBO, чтобы ускорить вывод tflite?

В следующем коде мы попытались преобразовать кадр камеры в растровое изображение, а затем преобразовать его в текстуру и, наконец, скопировать его в SSBO. Однако этот метод сравнительно медленнее, чем обычный конвейер выполнения делегатов GPU (где данные копируются из CPU в GPU -overhead). Цель состоит в том, чтобы сократить копирование данных изображения с CPU на GPU, сделав данные изображения доступными в памяти GPU, а затем передав их в модель. Мы можем запустить модель [1] за 40-50 мс, используя стандартный механизм вывода делегатов GPU; в то время как это занимает 90-100 мс с использованием вышеупомянутого метода SSBO (неоптимизировано). Приведенное выше время относится ко времени запуска методаinterter.run interpreter.run() в tenorflow lite. Также похоже, что этот механизм SSBO работает только с OpenGL ES 3.1 или выше.

Идеальным вариантом использования (как предлагает тензор потока) является следующий [2]:

  1. Вы получаете вход камеры в виде текстуры поверхности.
  2. Создайте объект буфера хранилища OpenGL (SSBO).
  3. Используйте GPUDelegate.bindGlBufferToTensor() чтобы связать этот SSBO с входным тензором.

  4. Напишите небольшую шейдерную программу для эффективного сброса текстуры поверхности [1] в этот SSBO из [2].

  5. Запустите вывод.

Мы можем получить кадры камеры в виде необработанных байтов или преобразовать их в текстуру и даже отобразить их в GLSurface View. Но мы можем добиться ускорения, как это было предложено тензорным потоком.

  1. https://github.com/tensorflow/tensorflow/issues/26297
  2. https://github.com/tensorflow/tensorflow/issues/25657#issuecomment-466489248
  3. 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;
}
Теги:
shader
tensorflow-lite
opengl-es-3.1

1 ответ

0

Нет простого способа выгрузить 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;
    }

Обратите внимание, что это было для MobileNet v1 с размером входного тензора 224x224x3.

  • 0
    Зачем вам даже шейдер для этого? Разве вы не можете просто скопировать данные текстуры в SSBO напрямую, glGetTexImage как PBO и выполнив glGetTexImage ? Если только ... OpenGL ES не имеет PBO, что было бы очень неудачно.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню