Преобразование YUV (yCbCr420p) в RGB в шейдерном фрагменте GLSL? - PullRequest
0 голосов
/ 01 мая 2019

Я понимаю, что один и тот же вопрос появлялся довольно часто, я пробовал каждое решение, которое смог найти, и ничего не работает.

Я получаю 3 буфера (каждый для Y, Cr, Cb) на кадр,В настоящее время я просто привязываю их к красному каналу.Размер буфера Y составляет 1 байт / пиксель, а размер буферов Cr и Cb составляет 1/4, поэтому он хорошо согласуется с примерами, которые я вижу, используя width / 2 и height / 2 для текстурных карт U и V, следующим образом:

int glFormat = GL20.GL_RED;
int glType = GL20.GL_UNSIGNED_BYTE;
int glInternalFormat = GL20.GL_RED;

GL20.glActiveTexture(GL20.GL_TEXTURE0);
GL20.glBindTexture(glTarget, yTexHandle);
GL20.glTexImage2D(glTarget, 0, glInternalFormat, width, height, 0, glFormat, glType, buffer);

int uvWidth = videoWidth/2;
int uvHeight = videoHeight/2;

GL20.glActiveTexture(GL20.GL_TEXTURE0+2);
GL20.glBindTexture(glTarget, cbTexHandle);
GL20.glTexImage2D(glTarget, 0, glInternalFormat, uvWidth, uvHeigth, 0, glFormat, glType, cbBuffer);
GL20.glActiveTexture(GL20.GL_TEXTURE0+1);
GL20.glBindTexture(glTarget, crTexHandle);
GL20.glTexImage2D(glTarget, 0, glInternalFormat, uvWidth, uvHeight, 0, glFormat, glType, crBuffer);

Это мой фрагментный шейдер;

uniform sampler2D u_texture;
uniform sampler2D u_texture_cr;
uniform sampler2D u_texture_cb;

void main() {
  float y = texture2D(u_texture, v_texCoords).r;
  float u = texture2D(u_texture_cr, v_texCoords).r - 0.5;
  float v = texture2D(u_texture_cb, v_texCoords).r - 0.5;
  float r = y + 1.402 * v;
  float g = y - 0.344 * u - 0.714 * v;
  float b = y + 1.772 * u;
  gl_FragColor = v_color * vec4(r, g, b, 1.0);
}

Хотя это может быть не самый лучший алгоритм для преобразования, я попробовал все возможные альтернативы, и он всегда выглядит примерно одинаково,очень зеленый и очень розовый.

Я полагаю, что проблема заключается в самих буферах или в том, как они связаны с GL, а не в самом шейдере фрагмента.Я попытался переключить u и v и даже попробовать u для всего и v для всего, и результат всегда одинаков, поэтому кажется, что к тому времени, как буферы u и v попадают в шейдер, они уже не правы.

Я распечатал сегменты буфера Cb и Cr, чтобы понять, каковы их значения, вот пример;

Cr: -124 Cb: 110
Cr: -126 Cb: 109
Cr: -127 Cb: 107
Cr: -128 Cb: 106
Cr: 127 Cb: 104
Cr: 127 Cb: 101
Cr: 127 Cb: 99

Обратите внимание, что это Java ByteBuffer.Я попытался использовать glType как GL_BYTE вместо GL_UNSIGNED_BYTE, и это выглядит намного хуже.Я также пытался использовать альфа-канал с GL_ALPHA или GL_LUMINANCE в качестве формата, GL_LUMINANCE выглядит немного по-другому, но все еще примерно одинаково.

Снимок экрана из 2 разных видео; enter image description here

Кроме того, пакет, из которого я получаю эти кадры, имеет возможность конвертировать в кадры RGBA, это прекрасно работает, однако это дорогостоящий процесс (~ 30 мс по сравнению с ~ 2 мс).Это также нативный метод, и я не могу найти источник для него, поэтому я не знаю, что он делает в фоновом режиме, но я предполагаю, что это доказательство того, что когда я получаю буферы, они верны.

ОБНОВЛЕНИЕ

Я попытался реализовать гамму и насыщенность в шейдере в соответствии с предложением MoDJ, используя this (преобразования BT709) из этого ответа.Вывод все еще в значительной степени такой же, хотя.В результате получился шейдер:

uniform sampler2D u_texture;
uniform sampler2D u_texture_cr;
uniform sampler2D u_texture_cb;

const float yScale = 255.0 / (235.0 - 16.0); //(BT709_YMax-BT709_YMin)
const float uvScale = 255.0 / (240.0 - 16.0); //(BT709_UVMax-BT709_UVMin)

float BT709_nonLinearNormToLinear(float normV) {
    if (normV < 0.081) {
        normV *= (1.0 / 4.5);
    } else {
        float a = 0.099;
        float gamma = 1.0 / 0.45;
        normV = (normV + a) * (1.0 / (1.0 + a));
        normV = pow(normV, gamma);
    }
    return normV;
}

void main() {
    float y = texture2D(u_texture, v_texCoords).r;
    float u = texture2D(u_texture_cr, v_texCoords).r - 0.5;
    float v = texture2D(u_texture_cb, v_texCoords).r - 0.5;

    y = y - 16.0/255.0;

    float r = y*yScale +                          v*uvScale*1.5748;
    float g = y*yScale - u*uvScale*1.8556*0.101 - v*uvScale*1.5748*0.2973;
    float b = y*yScale + u*uvScale*1.8556;

    r = clamp(r, 0.0, 1.0);
    g = clamp(g, 0.0, 1.0);
    b = clamp(b, 0.0, 1.0);

    r = BT709_nonLinearNormToLinear(r);
    g = BT709_nonLinearNormToLinear(g);
    b = BT709_nonLinearNormToLinear(b);

1 Ответ

0 голосов
/ 01 мая 2019

Я только что решил эту глупую ошибку с моей стороны при настройке формы шейдера!

Это то, что я делал;

GL20.glActiveTexture(GL20.GL_TEXTURE0+2);
GL20.glBindTexture(GL20.GL_TEXTURE_2D, cbTexHandle);
program.setUniformi("u_texture_cb", GL20.GL_TEXTURE0+2);

GL20.glActiveTexture(GL20.GL_TEXTURE0+1);
GL20.glBindTexture(GL20.GL_TEXTURE_2D, crTexHandle);
program.setUniformi("u_texture_cr", GL20.GL_TEXTURE0+1);

На самом деле должно быть;

program.setUniformi("u_texture_cb", 2);
program.setUniformi("u_texture_cr", 1);

Так что, если вы в конечном итоге получите зеленый и розовый дисплей от YUV-шейдера, я считаю, что это просто сводится к тому, что фактически не получаются какие-либо значения УФ (они, вероятно, просто все нули)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...