1. 程式人生 > 實用技巧 >音視訊入門-19-使用giflib處理GIF圖片

音視訊入門-19-使用giflib處理GIF圖片

* 音視訊入門文章目錄 *

GIFLIB

The GIFLIB project

上一篇 【手動生成一張GIF圖片】, 自己生成了一張 GIF 動態圖 rainbow.gif

下面,使用 GIFLIB 分離出 GIF 每一幀的 RGB ,然後將分離出的 RGB 再合成 GIF。

GIF to RGB

GIFLIB 專案裡的 gif2rgb.c 已經實現瞭解碼 GIF -> RGB。不過 gif2rgb.c 只儲存了最後一幀圖片的 RGB,這裡需要改造。

gif2rgb.c

gif2rgb.c 在 GIF2RGB 方法最後才呼叫 DumpScreen2RGB 儲存 RGB。

......

static void DumpScreen2RGB(char *FileName,
                           ColorMapObject *ColorMap,
                           GifRowType *ScreenBuffer,
                           int ScreenWidth, int ScreenHeight)
{
......
}

static void GIF2RGB(int NumFiles, char *FileName, 
		    bool OneFileFlag, 
		    char *OutFileName)
{
    ......
    
    /* Scan the content of the GIF file and load the image(s) in: */
    do {
	......
    } while (RecordType != TERMINATE_RECORD_TYPE);
    
    /* Lets dump it - set the global variables required and do it: */
    ColorMap = (GifFile->Image.ColorMap
		? GifFile->Image.ColorMap
		: GifFile->SColorMap);
    if (ColorMap == NULL) {
        fprintf(stderr, "Gif Image does not have a colormap\n");
        exit(EXIT_FAILURE);
    }

    /* check that the background color isn't garbage (SF bug #87) */
    if (GifFile->SBackGroundColor < 0 || GifFile->SBackGroundColor >= ColorMap->ColorCount) {
        fprintf(stderr, "Background color out of range for colormap\n");
        exit(EXIT_FAILURE);
    }

    DumpScreen2RGB(OutFileName, OneFileFlag,
		   ColorMap,
		   ScreenBuffer, 
		   GifFile->SWidth, GifFile->SHeight);

    (void)free(ScreenBuffer);

    if (DGifCloseFile(GifFile, &Error) == GIF_ERROR) {
	PrintGifError(Error);
	exit(EXIT_FAILURE);
    }

}

gif-to-rgb-library.c

需要把呼叫 DumpScreen2RGB 的位置移到迴圈體內 case IMAGE_DESC_RECORD_TYPE: 位置。

......

static void DumpScreen2RGB(char *FileName,
                           ColorMapObject *ColorMap,
                           GifRowType *ScreenBuffer,
                           int ScreenWidth, int ScreenHeight)
{
......
}

static void GIF2RGB( char *FileName, char *OutFileNamePattern)
{
    ......
    do {
        ......
        switch (RecordType) {
            case IMAGE_DESC_RECORD_TYPE:
                ......

                ColorMap = (GifFile->Image.ColorMap
                            ? GifFile->Image.ColorMap
                            : GifFile->SColorMap);
                if (ColorMap == NULL) {
                    fprintf(stderr, "Gif Image does not have a colormap\n");
                    exit(EXIT_FAILURE);
                }

                /* check that the background color isn't garbage (SF bug #87) */
                if (GifFile->SBackGroundColor < 0 || GifFile->SBackGroundColor >= ColorMap->ColorCount) {
                    fprintf(stderr, "Background color out of range for colormap\n");
                    exit(EXIT_FAILURE);
                }

                char *name = malloc(255*sizeof(char));
                sprintf(name, OutFileNamePattern,  screenIndex++);
                printf("Final File Name: %s\n", name);

                DumpScreen2RGB(name,
                               ColorMap,
                               ScreenBuffer,
                               GifFile->SWidth, GifFile->SHeight);
                break;
            case EXTENSION_RECORD_TYPE:
            ......
        }
    } while (RecordType != TERMINATE_RECORD_TYPE);
   ......
}

int main(int argc, char **argv) {
    GIF2RGB( "/Users/staff/Desktop/rainbow.gif", "/Users/staff/Desktop/rainbow-%d.rgb");
    return 0;
}

檢視 GIF -> RGB 結果

根據 【手動生成一張GIF圖片】 生成的 GIF rainbow.gif 含有 7 個影象,所以會得到 7 個 .RGB 檔案。

ffplay 檢視 RGB 檔案:

ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-0.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-1.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-2.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-3.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-4.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-5.rgb
ffplay -f rawvideo -pixel_format rgb24 -s 700x700 rainbow-6.rgb

RGB to GIF

上面,從 rainbow.gif 中提取出了 7 個 RGB 檔案。
下面,讀取這 7 個 RGB 檔案,使用 GIFLIB 編碼成 GIF 動態圖。

// LoadRGB 是 gif2rgb.c 檔案中的方法
static void LoadRGB(char *FileName,
                    GifByteType **RedBuffer,
                    GifByteType **GreenBuffer,
                    GifByteType **BlueBuffer,
                    int Width, int Height)
{
   ......
}

// gif2rgb.c 檔案中 RGB2GIF 只實現了 RGB to GIF 靜態圖功能
// 這裡做了修改,可以生成動態 GIF
static void RGB2GIF(char **RGBFileNames, int NumOfRGBFile, char *GIFFileName,
                    int ExpNumOfColors, int Width, int Height)
{
    int ColorMapSize;
    GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL, *OutputBuffer = NULL;
    ColorMapObject *OutputColorMap = NULL;

    // 開啟輸出的 GIF 檔案
    int Error;
    GifFileType *GifFile;
    if ((GifFile = EGifOpenFileName(GIFFileName, false, &Error)) == NULL) {
        PrintGifError(Error);
        exit(EXIT_FAILURE);
    }

    GifFile->SWidth = Width;
    GifFile->SHeight = Height;
    GifFile->SColorResolution = 1;
    GifFile->SBackGroundColor = 0;
    GifFile->SColorMap = NULL;

    for(int i = 0; i < NumOfRGBFile; i++) {
        ColorMapSize = 1 << ExpNumOfColors;
        printf("讀取 RGB 檔案:%d\n", i);
        LoadRGB(RGBFileNames[i], &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height);
        if ((OutputColorMap = GifMakeMapObject(ColorMapSize, NULL)) == NULL ||
            (OutputBuffer = (GifByteType *) malloc(Width * Height *
                                                   sizeof(GifByteType))) == NULL)
        GIF_EXIT("Failed to allocate memory required, aborted.");

        if (GifQuantizeBuffer(Width, Height, &ColorMapSize,
                              RedBuffer, GreenBuffer, BlueBuffer,
                              OutputBuffer, OutputColorMap->Colors) == GIF_ERROR)
            exit(EXIT_FAILURE);
        free((char *) RedBuffer);
        free((char *) GreenBuffer);
        free((char *) BlueBuffer);

        printf("MakeSavedImage:%d\n", i);
        SavedImage *image = GifMakeSavedImage(GifFile, NULL);

        GifImageDesc *imageDesc = (GifImageDesc *) malloc(sizeof(GifImageDesc));
        imageDesc->Left = 0;
        imageDesc->Top = 0;
        imageDesc->Width = Width;
        imageDesc->Height = Height;
        imageDesc->Interlace = false;
        imageDesc->ColorMap = OutputColorMap;

        image->ImageDesc = *imageDesc;
        image->RasterBits = OutputBuffer;

        GraphicsControlBlock *GCB = (GraphicsControlBlock *) malloc(sizeof(GraphicsControlBlock));
        GCB->DisposalMode = DISPOSAL_UNSPECIFIED;
        GCB->DelayTime = 50;
        GCB->UserInputFlag = false;
        GCB->TransparentColor = NO_TRANSPARENT_COLOR;

        printf("GCBToSaved:%d\n", i);
        EGifGCBToSavedExtension(GCB, GifFile, i);
    }

    printf("輸出檔案。");
    // 輸出檔案
    EGifSpew(GifFile);

}

int main(int argc, char **argv) {
    char *rgbFiles[] = {
            "/Users/staff/Desktop/rainbow-0.rgb",
            "/Users/staff/Desktop/rainbow-1.rgb",
            "/Users/staff/Desktop/rainbow-2.rgb",
            "/Users/staff/Desktop/rainbow-3.rgb",
            "/Users/staff/Desktop/rainbow-4.rgb",
            "/Users/staff/Desktop/rainbow-5.rgb",
            "/Users/staff/Desktop/rainbow-6.rgb",
    };

    RGB2GIF(rgbFiles, 7, "/Users/staff/Desktop/rainbow0.gif", 3, 700, 700);
    return 0;
}

檢視 RGB -> GIF 結果

成功實現了 GIF -> RGB(s) -> GIF。


程式碼:
audio-video-blog-demos

參考資料:

影象解碼之三——giflib解碼gif圖片

How do I get the RGB colour data from a GIFLIB SavedImage structure