簡體   English   中英

如何使用libjpeg將YUYV原始數據壓縮為JPEG?

[英]How to compress YUYV raw data to JPEG using libjpeg?

我正在尋找一個如何使用libjpeg庫將YUYV格式幀保存為JPEG文件的示例。

在典型的計算機API中,“YUV”實際上表示YCbCr,“YUYV”表示存儲為Y0,Cb01,Y1,Cr01,Y2的“YCbCr 4:2:2”...

因此,如果您有“YUV”圖像,則可以使用JCS_YCbCr顏色空間將其保存到libjpeg。

如果您有422圖像(YUYV),則必須在將掃描線寫入libjpeg之前將Cb / Cr值復制到需要它們的兩個像素。 因此,這個寫循環將為您完成:

// "base" is an unsigned char const * with the YUYV data
// jrow is a libjpeg row of samples array of 1 row pointer
cinfo.image_width = width & -1; 
cinfo.image_height = height & -1; 
cinfo.input_components = 3; 
cinfo.in_color_space = JCS_YCbCr; 
jpeg_set_defaults(&cinfo); 
jpeg_set_quality(&cinfo, 92, TRUE); 
jpeg_start_compress(&cinfo, TRUE); 
unsigned char *buf = new unsigned char[width * 3]; 
while (cinfo.next_scanline < height) { 
    for (int i = 0; i < cinfo.image_width; i += 2) { 
        buf[i*3] = base[i*2]; 
        buf[i*3+1] = base[i*2+1]; 
        buf[i*3+2] = base[i*2+3]; 
        buf[i*3+3] = base[i*2+2]; 
        buf[i*3+4] = base[i*2+1]; 
        buf[i*3+5] = base[i*2+3]; 
    } 
    jrow[0] = buf; 
    base += width * 2; 
    jpeg_write_scanlines(&cinfo, jrow, 1); 
}
jpeg_finish_compress(&cinfo);
delete[] buf;

如果您的錯誤或寫入函數可以拋出/ longjmp,請使用您喜歡的auto-ptr以避免泄漏“buf”。

直接向libjpeg提供YCbCr更適合轉換為RGB,因為它將以該格式直接存儲它,從而節省了大量的轉換工作。 當圖像來自網絡攝像頭或其他視頻源時,在某種類型的YCbCr中獲取圖像通常也是最有效的(例如YUYV。)

最后,“U”和“V”在模擬分量視頻中意味着略有不同,因此YUV在計算機API中的命名實際上意味着YCbCr非常混亂。

libjpeg還具有原始數據模式,您可以直接提供原始下采樣數據(這幾乎與YUYV格式相同)。 這比復制UV值更有效,只是讓libjpeg在內部再次縮小它們。

為此,您使用jpeg_write_raw_data而不是jpeg_write_scanlines ,默認情況下,它將一次處理16個掃描線。 JPEG期望U和V平面默認為2x下采樣。 YUYV格式已經下采樣水平尺寸,但不是垂直尺寸,所以我每隔一條掃描線跳過U和V.

初始化:

cinfo.image_width = /* width in pixels */;
cinfo.image_height = /* height in pixels */;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_YCbCr;
jpeg_set_defaults(&cinfo);

cinfo.raw_data_in = true;

JSAMPLE y_plane[16][cinfo.image_width];
JSAMPLE u_plane[8][cinfo.image_width / 2];
JSAMPLE v_plane[8][cinfo.image_width / 2];

JSAMPROW y_rows[16];
JSAMPROW u_rows[8];
JSAMPROW v_rows[8];

for (int i = 0; i < 16; ++i)
{
    y_rows[i] = &y_plane[i][0];
}

for (int i = 0; i < 8; ++i)
{
    u_rows[i] = &u_plane[i][0];
}

for (int i = 0; i < 8; ++i)
{
    v_rows[i] = &v_plane[i][0];
}

JSAMPARRAY rows[] { y_rows, u_rows, v_rows };

壓縮:

jpeg_start_compress(&cinfo, true);

while (cinfo.next_scanline < cinfo.image_height)
{
    for (JDIMENSION i = 0; i < 16; ++i)
    {
        auto offset = (cinfo.next_scanline + i) * cinfo.image_width * 2;
        for (JDIMENSION j = 0; j < cinfo.image_width; j += 2)
        {
            y_plane[i][j] = image.data[offset + j * 2 + 0];
            y_plane[i][j + 1] = image.data[offset + j * 2 + 2];

            if (i % 2 == 0)
            {
                u_plane[i / 2][j / 2] = image_data[offset + j * 2 + 1];
                v_plane[i / 2][j / 2] = image_data[offset + j * 2 + 3];
            }
        }
    }

    jpeg_write_raw_data(&cinfo, rows, 16);
}

jpeg_finish_compress(&cinfo);

與@ JonWatte的回答相比,這種方法使壓縮時間縮短了33%。 這個解決方案並不適合所有人; 一些警告:

  • 您只能壓縮尺寸為8的倍數的圖像。如果您有不同尺寸的圖像,則必須編寫代碼以填充邊緣。 如果您從相機獲取圖像,他們很可能就是這樣。
  • 由於我只是跳過交替掃描線的顏色值而不是像平均它們那樣的更高級的東西,因此質量有些受損。 對於我的應用,速度比質量更重要。
  • 它現在的編寫方式在堆棧上分配了大量的內存。 這對我來說是可以接受的,因為我的圖像很小(640x480)並且有足夠的內存可用。

libjpeg-turbo的文檔: https//raw.githubusercontent.com/libjpeg-turbo/libjpeg-turbo/master/libjpeg.txt

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM