Корректное преобразование YUV422 в RGB

#c #image-processing #opencv #rgb #yuv

#c #обработка изображений #opencv #rgb #yuv

Вопрос:

Я пытался решить проблему преобразования YUV422 в RGB около недели. Я посетил много разных веб-сайтов и получил разные формулы с каждого. Если у кого-нибудь еще есть какие-либо предложения, я был бы рад услышать о них. Приведенные ниже формулы дают мне изображение либо с фиолетовым, либо с зеленым оттенком в них. На данный момент я не смог найти формулу, которая позволяет мне вернуть правильное изображение RGB. Я включил все свои различные фрагменты кода ниже.

     //for(int i = 0; i < 1280 * 720 * 3; i=i 3)
    //{
    //  /*m_RGB->imageData[i] = pData[i]   pData[i 2]*((1 - 0.299)/0.615);
    //  m_RGB->imageData[i 1] = pData[i] - pData[i 1]*((0.114*(1-0.114))/(0.436*0.587)) - pData[i 2]*((0.299*(1 - 0.299))/(0.615*0.587));
    //  m_RGB->imageData[i 2] = pData[i]   pData[i 1]*((1 - 0.114)/0.436);*/

    //  m_RGB->imageData[i] = pData[i]   1.403 * (pData[i 1] - 128);
    //  m_RGB->imageData[i 1] = pData[i]   0.344 * (pData[i 1] - 128) - 0.714 * (pData[i 2] - 128);
    //  m_RGB->imageData[i 2] = pData[i]   1.773 * (pData[i 2] - 128);
    //}

    for(int i = 0, j=0; i < 1280 * 720 * 3; i =6, j =4)
    {
        /*m_RGB->imageData[i] = pData[j]   pData[j 3]*((1 - 0.299)/0.615);
        m_RGB->imageData[i 1] = pData[j] - pData[j 1]*((0.114*(1-0.114))/(0.436*0.587)) - pData[j 3]*((0.299*(1 - 0.299))/(0.615*0.587));
        m_RGB->imageData[i 2] = pData[j]   pData[j 1]*((1 - 0.114)/0.436);
        m_RGB->imageData[i 3] = pData[j 2]   pData[j 3]*((1 - 0.299)/0.615);
        m_RGB->imageData[i 4] = pData[j 2] - pData[j 1]*((0.114*(1-0.114))/(0.436*0.587)) - pData[j 3]*((0.299*(1 - 0.299))/(0.615*0.587));
        m_RGB->imageData[i 5] = pData[j 2]   pData[j 1]*((1 - 0.114)/0.436);*/

        /*m_RGB->imageData[i] = pData[j]   1.403 * (pData[j 3] - 128);
        m_RGB->imageData[i 1] = pData[j]   0.344 * (pData[j 1] - 128) - 0.714 * (pData[j 3] - 128);
        m_RGB->imageData[i 2] = pData[j]   1.773 * (pData[j 1] - 128);
        m_RGB->imageData[i 3] = pData[j 2]   1.403 * (pData[j 3] - 128);
        m_RGB->imageData[i 4] = pData[j 2]   0.344 * (pData[j 1] - 128) - 0.714 * (pData[j 3] - 128);
        m_RGB->imageData[i 5] = pData[j 2]   1.773 * (pData[j 1] - 128);*/

        BYTE Cr = pData[j 3] - 128;
        BYTE Cb = pData[j 1] - 128;
        /*m_RGB->imageData[i] = pData[j]   Cr   (Cr >> 2)   (Cr >> 3)   (Cr >> 5);
        m_RGB->imageData[i 1] = pData[j] - ((Cb >> 2)   (Cb >> 4)   (Cb >> 5)) - ((Cr >> 1)   (Cr >> 3)   (Cr >> 4)   (Cr >> 5));
        m_RGB->imageData[i 2] = pData[j]   Cb   (Cb >> 1)   (Cb >> 2)   (Cb >> 6);
        m_RGB->imageData[i 3] = pData[j 2]   Cr   (Cr >> 2)   (Cr >> 3)   (Cr >> 5);
        m_RGB->imageData[i 4] = pData[j 2] - ((Cb >> 2)   (Cb >> 4)   (Cb >> 5)) - ((Cr >> 1)   (Cr >> 3)   (Cr >> 4)   (Cr >> 5));
        m_RGB->imageData[i 5] = pData[j 2]   Cb   (Cb >> 1)   (Cb >> 2)   (Cb >> 6);*/

        /*int R1 = clamp(1 * pData[j]   0 * Cb   1.4 * Cr, 0, 255), R2 = clamp(1 * pData[j 2]   0 * Cb   1.4 * Cr, 0, 255);
        int G1 = clamp(1 * pData[j] - 0.343 * Cb - 0.711 * Cr, 0, 255), G2 = clamp(1 * pData[j 2] - 0.343 * Cb - 0.711 * Cr, 0, 255);
        int B1 = clamp(1 * pData[j]   1.765 * Cb   0 * Cr, 0, 255), B2 = clamp(1 * pData[j 2]   1.765 * Cb   0 * Cr, 0, 255);*/

        /*int R1 = clamp(pData[j]   1.403 * (pData[j 3] - 128), 0, 255), R2 = clamp(pData[j 2]   1.403 * (pData[j 3] - 128), 0, 255);
        int G1 = clamp(pData[j]   0.344 * (pData[j 1] - 128) - 0.714 * (pData[j 3] - 128), 0, 255), G2 = clamp(pData[j 2]   0.344 * (pData[j 1] - 128) - 0.714 * (pData[j 3] - 128), 0, 255);
        int B1 = clamp(pData[j]   1.773 * (pData[j 1] - 128), 0, 255), B2 = clamp(pData[j 2]   1.773 * (pData[j 1] - 128), 0, 255);*/

        int R1 = clamp((298 * (pData[j] - 16)   409 * (pData[j 3] - 128)   128) >> 8, 0, 255), R2 = clamp((298 * (pData[j 2] - 16)   409 * (pData[j 3] - 128)   128) >> 8, 0, 255);
        int G1 = clamp((298 * (pData[j] - 16) - 100 * (pData[j 1] - 128) - 208 * (pData[j 3] - 128)   128) >> 8, 0, 255), G2 = clamp((298 * (pData[j 2] - 16) - 100 * (pData[j 1] - 128) - 208 * (pData[j 3] - 128)   128) >> 8, 0, 255);
        int B1 = clamp((298 * (pData[j] - 16)   516 * (pData[j 1] - 128)   128) >> 8, 0, 255), B2 = clamp((298 * (pData[j 2] - 16)   516 * (pData[j 1] - 128)   128) >> 8, 0, 255);

        //printf("R: %d, G: %d, B: %d, R': %d, G': %d, B': %d n", R1, G1, B1, R2, G2, B2);

        m_RGB->imageData[i] = (char)R1;
        m_RGB->imageData[i 1] = (char)G1;
        m_RGB->imageData[i 2] = (char)B1;
        m_RGB->imageData[i 3] = (char)R2;
        m_RGB->imageData[i 4] = (char)G2;
        m_RGB->imageData[i 5] = (char)B2;

        /*m_RGB->imageData[i] = (char)(clamp(1.164 * (pData[j] - 16)   1.793 * (Cr), 0, 255));
        m_RGB->imageData[i 1] = (char)(clamp(1.164 * (pData[j] - 16) - 0.534 * (Cr) - 0.213 * (Cb), 0, 255));
        m_RGB->imageData[i 2] = (char)(clamp(1.164 * (pData[j] - 16)   2.115 * (Cb), 0, 255));
        m_RGB->imageData[i 3] = (char)(clamp(1.164 * (pData[j 2] - 16)   1.793 * (Cr), 0, 255));
        m_RGB->imageData[i 4] = (char)(clamp(1.164 * (pData[j 2] - 16) - 0.534 * (Cr) - 0.213 * (Cb), 0, 255));
        m_RGB->imageData[i 5] = (char)(clamp(1.164 * (pData[j 2] - 16)   2.115 * (Cb), 0, 255));*/
    }
  

Любая помощь приветствуется.

Комментарии:

1. Каков источник данных YUV и каково назначение? Например, если местом назначения является Windows, вам нужно использовать порядок BGR, а не RGB.

2. YUV поступает с карты захвата Decklink Intensity Pro. Я также попытался изменить значения BGR / RGB, но это не помогло. Это делается в окне Windows

3. Если вы используете decklink SDK, почему бы вам просто не использовать метод ConvertFrame, который является частью API?

4. Мне кажется, что вы путаете Cr с Cb, вам следует поменяться местами.

5. Я пытался использовать ConvertFrame, но при его использовании он выдает ошибку. я связался с DeckLink, и они сказали, что преобразования YUV в RGB в настоящее время не поддерживаются

Ответ №1:

Несколько подсказок, которые помогут вам:

Вы путаете Cr с Cb.

Предполагая UYVY/422

 Y1 = data[j 0];
Cr = data[j 1];
Y2 = data[j 2];
Cb = data[j 3];
  

Ваши вычисления преобразования неверны для HD.

Для SD

 R = max(0, min(255, 1.164(Y - 16)   1.596(Cr - 128)));
G = max(0, min(255, 1.164(Y - 16) - 0.813(Cr - 128) - 0.391(Cb - 128)));
B = max(0, min(255, 1.164(Y - 16)   2.018(Cr - 128)));
  

Для HD

 R = max(0, min(255, 1.164(Y - 16)   1.793(Cr - 128)));
G = max(0, min(255, 1.164(Y - 16) - 0.534(Cr - 128) - 0.213(Cb - 128)));
B = max(0, min(255, 1.164(Y - 16)   2.115(Cr - 128)));
  

Вы могли бы просто использовать ConvertFrame который является частью Decklink SDK.

Комментарии:

1. спасибо, так как ваш ответ помог мне. Вопросы: HD означает 1280×720 или 1920×1080? как насчет 4k? кстати, порядок Y1CrY2Cb сработал для меня с yuy2! хотя на основе msdn Cb — это «u», и предполагалось, что это будет Y1CbY2Cr, но при этом вместо красного цвета отображается синий! можете ли вы объяснить, почему порядок изменен? или msdn Cb=’u’ недопустимо?1

2. Ошибка… разве Cr для B не должен быть Cb?

3. Повторяя комментарий Хьюго, уравнение для B необходимо исправить как на SD, так и на HD, должно быть Cb, а не Cr

Ответ №2:

Ваша проблема в том, что существует множество форматов YUV422. Вы должны найти точный (индекс FOURCC для конкретного видео, которое вы используете), а затем выяснить правильный способ его декодирования.

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

http://www.fourcc.org/yuv.php

Ответ №3:

Предполагая, что упакованный 422, я не вижу, чтобы какой-либо из ваших блоков правильно отбирал входные данные. В packed 422 входные данные будут отправлены Y1U1Y2V1 Y3U2Y4V2, где общее изображение представляет собой изображение Y (luma) с полным разрешением и по одному изображению U и V с разрешением в половину горизонтального.

Вот с чего я бы начал: распакуйте переменные значения входных данных и извлеките изображение в оттенках серого:

 for (uint i = 0, j = 0; i < 1280 * 720 * 3; i  = 3, j  = 2) {
    m_RGB->imageData[i] = pData[j];
    m_RGB->imageData[i 1] = pData[j];
    m_RGB->imageData[i 2] = pData[j];
}
  

Как только вы настроите его для получения изображения в оттенках серого, затем введите U и V, посмотрев на pData[j 1] и pData[j 3] (или, для четных пикселей, pData[j-1] и pData[j 1] ). Упрощение — вот почему некоторые алгоритмы обрабатывают два YUV-пикселя одновременно.

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

(Обратите внимание, что другие устройства, такие как 420, имеют еще более сложное совместное размещение)

Комментарии:

1. Это дало мне изображение в оттенках серого, но все выглядит так, как будто к нему было применено размытие по Гауссу.

Ответ №4:

Я также боролся с преобразованием

 // Get the bytes
var u = bytes[0]; 
var y1 = bytes[1];
var v = bytes[2];
var y2 = bytes[3];

// Convert, cast to signed byte is important!
var r = y   (1.403 * (sbyte)v);
var g = y - (0.344 * (sbyte)u) - (0.714 * (sbyte)v);
var b = y   (1.770 * (sbyte)u);

if (r < 0)
    r = 0;
else if (r > 255)
    r = 255;

if (g < 0)
    g = 0;
else if (g > 255)
    g = 255;

if (b < 0)
    b = 0;
else if (b > 255)
    b = 255;

return Color.FromArgb((byte)r, (byte)g, (byte)b);
  

u и v являются sbyte , и y это просто byte .

Комментарии:

1. Вы хотите сказать, что это правильное преобразование или что вы изо всех сил пытались использовать это преобразование?