Помогите завершить функцию конвертации RGB в YUV420 с разделением планов

262
18 декабря 2017, 14:44

Совсем запутался с этими кодеками... Нужно из RGB (или BGR) буфера с картинкой получить три плана Y, U, V, которые "ест" x264 энкодер. У меня ступор с порядком записи байт UV, помогите разобраться пожалуйста:

#define CLAMP(t) ((t>255)?255:((t<0)?0:t))
// RGB to YUV
#define GET_Y(R,G,B) CLAMP((( 66 * (R) + 129 * (G) +  25 * (B) + 128) >> 8) +  16)
#define GET_U(R,G,B) CLAMP(((-38 * (R) -  74 * (G) + 112 * (B) + 128) >> 8) + 128)
#define GET_V(R,G,B) CLAMP(((112 * (R) -  94 * (G) -  18 * (B) + 128) >> 8) + 128)
unsigned int Capture::GrabPlanar(unsigned char **Y, unsigned char **U, unsigned char **V){
    if(*Y != nullptr || *U != nullptr || *V != nullptr){return 0;}
    unsigned char *frame = nullptr;
    unsigned int width, height, plane_size = 0;
    unsigned int size = GrabBitmap(&frame, width, height, 1);
    if( size){ // I420 algorithm...
        plane_size = width * height;
        *Y = (unsigned char *)malloc(plane_size     );
        *U = (unsigned char *)malloc(plane_size >> 2);
        *V = (unsigned char *)malloc(plane_size >> 2);
        unsigned char R, G, B;
        unsigned int iY, iU, iV;
        iY = iU = iV = 0;
        for(unsigned int i; i < size; i+=3){
            R = frame[i  ];
            G = frame[i+1];
            B = frame[i+2];
            (*Y)[iY] = GET_Y(R,G,B);
            // Не знаю что делать с индексами iU и iV
            // там ведь буфер в 4 раза меньше, можно на ошибку сегментации нарваться...
            // С какого смещения RGB считать U и V?
            iY++;
        }
    }
    if( frame != nullptr ){free(frame);}
    return plane_size;
}

И правильно ли выделена память под компоненты? (взял инфо отсюда)

Answer 1

В общем, благодаря первому комментарию под вопросом родилось такое решение

void Capture::BitmapToPlanar(unsigned char *src, unsigned char **Y, unsigned char **U, unsigned char **V, unsigned int width, unsigned int height){
    unsigned char pix_buf[3];
    int pix_avg[3];
    unsigned int pix_pos[4], i, uvi = 0;
    for(unsigned int h = 0; h < height; h+=2){
        for(unsigned int w = 0; w < width; w+=2){
            pix_avg[0] = pix_avg[1] = pix_avg[2] = 0;
            pix_pos[0] = w + h * width;
            pix_pos[1] = pix_pos[0] + 1;
            pix_pos[2] = pix_pos[0] + width;
            pix_pos[3] = pix_pos[1] + width;
            i = pix_pos[0] * 3;
            pix_buf[0] = src[i++];
            pix_buf[1] = src[i++];
            pix_buf[2] = src[i++];
            pix_avg[0]+= pix_buf[0];
            pix_avg[1]+= pix_buf[1];
            pix_avg[2]+= pix_buf[2];
            (*Y)[pix_pos[0]] = GET_Y(pix_buf[0],pix_buf[1],pix_buf[2]);
            pix_buf[0] = src[i++];
            pix_buf[1] = src[i++];
            pix_buf[2] = src[i  ];
            pix_avg[0]+= pix_buf[0];
            pix_avg[1]+= pix_buf[1];
            pix_avg[2]+= pix_buf[2];
            (*Y)[pix_pos[1]] = GET_Y(pix_buf[0],pix_buf[1],pix_buf[2]);
            i = pix_pos[2] * 3;
            pix_buf[0] = src[i++];
            pix_buf[1] = src[i++];
            pix_buf[2] = src[i++];
            pix_avg[0]+= pix_buf[0];
            pix_avg[1]+= pix_buf[1];
            pix_avg[2]+= pix_buf[2];
            (*Y)[pix_pos[2]] = GET_Y(pix_buf[0],pix_buf[1],pix_buf[2]);
            pix_buf[0] = src[i++];
            pix_buf[1] = src[i++];
            pix_buf[2] = src[i  ];
            pix_avg[0]+= pix_buf[0];
            pix_avg[1]+= pix_buf[1];
            pix_avg[2]+= pix_buf[2];
            (*Y)[pix_pos[3]] = GET_Y(pix_buf[0],pix_buf[1],pix_buf[2]);
            pix_avg[0]/= 4;
            pix_avg[1]/= 4;
            pix_avg[2]/= 4;
            (*U)[uvi] = GET_U(pix_avg[0],pix_avg[1],pix_avg[2]);
            (*V)[uvi] = GET_V(pix_avg[0],pix_avg[1],pix_avg[2]);
            uvi++;
        }
    }
}

которое, судя по всему, пришлось энкодеру по вкусу:

Повторяющийся код конечно режет глаза, но главное работает. Видео записал, цвета в порядке!

READ ALSO
Как хранить матрицу [требует правки]

Как хранить матрицу [требует правки]

Вопрос такой: как лучше произвольную(самую обычную) хранить матрицу? В виде массива указателей или цельным вектором?

228
На входе 0.16e-4 , на выходе 1.6e-05 Почему?

На входе 0.16e-4 , на выходе 1.6e-05 Почему?

// ConsoleApplication5cpp: определяет точку входа для консольного приложения

277
Как вывести double с заданным количеством символов перед запятой?

Как вывести double с заданным количеством символов перед запятой?

Мне нужно значение double перевести в строку с определенным количеством символов до и после запятойЯ использую функцию snprintf():

414
Как построить из ОПЗ бинарное дерево с++?

Как построить из ОПЗ бинарное дерево с++?

Вот пример реализации ОПЗ, возможно местами кривоКак из этого сделать бинарное дерево? У меня идея была построить двумерный массив и уже...

196