RGB32 in YUV420p

Im try conver rgb32 image to yuv420p for record video.

I have image

QImage image = QGuiApplication::primaryScreen()->grabWindow(0, rect_x, rect_y, rect_width, rect_height).toImage().convertToFormat(QImage::Format_RGB32);
AVFrame *frame;

and convert

for (y = 0; y < c->height; y++) {
    QRgb *rowData = (QRgb*)image.scanLine(y);
    for (x = 0; x < c->width; x++) {
        QRgb pixelData = rowData[x];

        int r = qRed(pixelData);
        int g = qGreen(pixelData);
        int b = qBlue(pixelData);

        int y0 = (int)(0.2126   * (float)(r) + 0.7152   * (float)(g) + 0.0722   * (float)(b));
        int u = 128 + (int)(-0.09991 * (float)(r) - 0.33609 * (float)(g) + 0.436 * (float)(b));
        int v = 128 + (int)(0.615 * (float)(r) - 0.55861 * (float)(g) - 0.05639 * (float)(b));

        frame->data[0][y * frame->linesize[0] + x] = y0;
        frame->data[1][y / 2 * frame->linesize[1] + x / 2] = u;
        frame->data[2][y / 2 * frame->linesize[2] + x / 2] = v;


    }
}

but on result image im see artefact. Text look blended http://joxi.ru/eAORRX0u4d46a2

this bug in convert alogritm or something else?

UDP

for (y = 0; y < c->height; y++) {
    QRgb *rowData = (QRgb*)image.scanLine(y);
    for (x = 0; x < c->width; x++) {
        QRgb pixelData = rowData[x];

        int r = qRed(pixelData);
        int g = qGreen(pixelData);
        int b = qBlue(pixelData);

        int y0 = (int)(0.2126   * (float)(r) + 0.7152   * (float)(g) + 0.0722   * (float)(b));

        if (y0 < 0)
            y0 = 0;
        if (y0 > 255)
            y0 = 255;


        frame->data[0][y * frame->linesize[0] + x] = y0;
     }
 }

 int x_pos = 0;
 int y_pos = 0;

 for (y = 1; y < c->height; y+=2) {
     QRgb *pRow = (QRgb*)image.scanLine(y - 1);
     QRgb *sRow = (QRgb*)image.scanLine(y);
     for (x = 1; x < c->width; x+=2) {
         QRgb pd1 = pRow[x - 1];
         QRgb pd2 = pRow[x];
         QRgb pd3 = sRow[x - 1];
         QRgb pd4 = sRow[x];

         int r = (qRed(pd1) + qRed(pd2) + qRed(pd3) + qRed(pd4)) / 4;
         int g = (qGreen(pd1) + qGreen(pd2) + qGreen(pd3) + qGreen(pd4)) / 4;
         int b = (qBlue(pd1) + qBlue(pd2) + qBlue(pd3) + qBlue(pd4)) / 4;

         int u = 128 + (int)(-0.147 * (float)(r) - 0.289 * (float)(g) + 0.436 * (float)(b));
         int v = 128 + (int)(0.615 * (float)(r) - 0.515 * (float)(g) - 0.1 * (float)(b));

         if (u < 0)
             u = 0;
         if (v > 255)
             v = 255;

         frame->data[1][y_pos * frame->linesize[1] + x_pos] = u;
         frame->data[2][y_pos * frame->linesize[2] + x_pos] = v;

         x_pos++;
     }

     x_pos = 0;
     y_pos++;
 }

this work for me, but its wery slow, 60-70ms for one frame

Upvotes: 0

Views: 2309

Answers (1)

Roman Ryltsov
Roman Ryltsov

Reputation: 69706

The first problem is that you are letting your YUV values go beyond allowed range (which is even stricter than 0x00..0xFF. but you don't do any capping anyway). See:

Y' values are conventionally shifted and scaled to the range [16, 235] (referred to as studio swing or "TV levels") rather than using the full range of [0, 255] (referred to as full swing or "PC levels"). This confusing practice derives from the MPEG standards and explains why 16 is added to Y' and why the Y' coefficients in the basic transform sum to 220 instead of 255.[8] U and V values, which may be positive or negative, are summed with 128 to make them always positive, giving a studio range of 16–240 for U and V. (These ranges are important in video editing and production, since using the wrong range will result either in an image with "clipped" blacks and whites, or a low-contrast image.)

Second problem is that 4:2:0 means that you end up with one Y value for every pixel, and one U and one V value for every four pixels. That is, U and V should be averages of corresponding pixels, and your loop simply overwrites the values with U and V of the fourth input pixel, ignoring the previous three.

enter image description here

You tagged the question with and your previous question is FFmpeg related too. Note that FFmpeg offers swscale library, which sws_scale does the conversion way more efficiently compared to your loop and optimizations you could add to it. See related questions on SO:

Upvotes: 3

Related Questions