使用OpenCV+C++将Gif文件分解并且转换为视频文件

简介: 原文链接目标:认识 Gif利用 FreeImage 将Gif解析为 Mat;利用 FreeImage 获取多帧Gif图像;将获取的多帧图像保存,并利用OpenCv生成为视频文件。

原文链接

目标:

  • 认识 Gif
  • 利用 FreeImage 将Gif解析为 Mat;
  • 利用 FreeImage 获取多帧Gif图像;
  • 将获取的多帧图像保存,并利用OpenCv生成为视频文件。

实际操作:

认识 Gif:

在浏览器中输入 Gif,查看维基百科的解释:
关于科学上网的方法可以查看

img_436da75b30e464f3a649f938b6301814.gif
搜索Gif

在这里,我将其下载为了PDF,供大家下载: 点击下载

img_b99e8d9144c30524ed89ba4c8a50f3f7.png
内容

我们注意它的特性就可以了:

优秀的压缩算法使其在一定程度上保证图像质量的同时将体积变得很小。
可插入多帧,从而实现动画效果。
可设置透明色以产生对象浮现于背景之上的效果。
由于采用了8位压缩,最多只能处理256种颜色,故不宜应用于真彩色图片。

算法解析:

环境:

  • OpenCv3.1
  • window10
  • vs2013
  • FreeImage

OpenCv 3.1官方教程中文翻译链接
FreeImage 下载地址
FreeImage是没有文档的,搜索之后,在网上发现一个写得还不错的文档点击下载

环境搭建:

新建一个OpenCv的工程, 下载FreeImage,将相应的文件复制到工程文件夹下:
img_87a129869a170a75384e6ddb35eb384c.gif
复制相应的文件
将.lib 文件添加到工程:
img_a23f68143d8fbd8e8ca9a50732adc8d1.gif
配置.lib文件

如此便搭建好了环境。

算法实现:

Gif文件的加载:
代码:
//-------------  GIF文件的载入
//

bool Gif_Load(const string &filename)
{
    FIBITMAP *dib = 0;
    FIMULTIBITMAP *bitmap = 0;
    FIBITMAP * pFrame;

    fif = FreeImage_GetFileType(filename.c_str(), 0);
    if (fif == FIF_UNKNOWN)                     fif = FreeImage_GetFIFFromFilename(filename.c_str());
    if (fif == FIF_UNKNOWN)                     return false;
    if (FreeImage_FIFSupportsReading(fif))      dib = FreeImage_Load(fif, filename.c_str());
    if (!dib)                                   return false;//dib Load failed  

    //bpp = FreeImage_GetBPP(dib);
    bits = (BYTE*)FreeImage_GetBits(dib);
    width = FreeImage_GetWidth(dib);
    height = FreeImage_GetHeight(dib);

    cout << "Load The File:   " << filename.c_str()  << endl;
    cout << "The File's width: " << width << endl;
    cout << "The File's height: " << height << endl;

    if ((bits == 0) || (width == 0) || (height == 0))       return false;

    bitmap = FreeImage_OpenMultiBitmap(fif, filename.c_str(), 0, 0, 1, GIF_DEFAULT);
    if (bitmap == NULL)
    {
        cout << "BitMap == Null" << endl;
        return FALSE;
    }
    
    int count = FreeImage_GetPageCount(bitmap);//获取帧数;

    for (int i = 0; i <=count; i++)
    {
        pFrame = FreeImage_LockPage(bitmap, i);
        //cout << "pFrame:" << pFrame << endl;

        Src_Gif = Gif_To_Mat(pFrame, fif);  //转换为Mat;
        
        string Src_Gif_Name = to_string(i);
        imwrite(Src_Gif_Name + ".jpg", Src_Gif);
        FreeImage_UnlockPage(bitmap, pFrame, 1);
    }

    FreeImage_Unload(dib);
    FreeImage_DeInitialise();
    Load_flag = TRUE;
    return Load_flag;
}
解释:
  • 首先是利用 FreeImage_GetFileType()函数,通过文件名,获取文件的类型;
  • 然后判断 FreeImage_FIFSupportsReading(),是否是FreeImage支持的文件类型;
  • 之后获取文件的相关信息,位数,大小。
  • 再利用 FreeImage_OpenMultiBitmap,以 GIF_DEFAULT 的方式加载Gif文件,关于这个函数参数的意义,可查看我上面给出的文档。
  • 利用 FreeImage_GetPageCount 函数获取帧数。
  • 之后就是在一个for循环里面,使用 Gif_To_Mat,将每一帧图片转换为Mat。
  • 再使用 imwrite 将图像储存到本地。

由此Gif加载完毕,并且转换为了Mat。下面来解释一下Gif转换为Mat的方法:

Gif转换为Mat:
代码:
Mat Gif_To_Mat(FIBITMAP* fiBmp, const FREE_IMAGE_FORMAT fif)
{
    if (fiBmp == NULL || fif != FIF_GIF)
    {
        return Mat();
    }

    BYTE intensity;
    BYTE* PIintensity = &intensity;
    if (FreeImage_GetBPP(fiBmp) != 8)
        fiBmp = FreeImage_ConvertTo8Bits(fiBmp);

    RGBQUAD* pixels = new RGBQUAD;
    pixels = FreeImage_GetPalette(fiBmp);

    Mat img = Mat::zeros(height, width, CV_8UC3);

    uchar *p;

    for (int i = 0; i < height; i++)
    {
        p = img.ptr<uchar>(i);
        for (int j = 0; j < width; j++)
        {
            FreeImage_GetPixelIndex(fiBmp, j, height - i, PIintensity);
            p[3 * j] = pixels[intensity].rgbBlue;
            p[3 * j + 1] = pixels[intensity].rgbGreen;
            p[3 * j + 2] = pixels[intensity].rgbRed;
        }
    }

    return img;
}

解释:
  • for 循环之前图像的加载不需要多说了;
  • for 循环,遍历整个图像,利用 FreeImage_GetPixelIndex 获取相应(x, y)的像素值,注意这里的坐标 表示的是(j, height-1),因为坐标的x, y 和 height, width是不一样的;大可将 height-i 改为i运行查看区别。

关于对图像的遍历,可参考【OpenCvTutorials3.1中文翻译】- 如何使用OpenCV扫描图像,查找表和时间测量,这里有详细的解释。

将文件夹中的jpg文件合成为视频:
代码:

//将当前文件夹中的 “.jpg” 生成为视频文件;

bool Jpg_To_Video()
{

    VideoWriter video("output.avi", CV_FOURCC('M', 'P', '4', '2'), 25.0, Size(150, 131));
    String File_Name = "*.jpg";
    vector <String> fn;
    glob(File_Name, fn, false);//遍历文件夹的图片/文件
    size_t size = fn.size();
    cout << "Jpg_To_Video size:" << size << endl;
    cout << "开始将图片文件写入视频" << endl;
    for (size_t i = 0; i < size; i++)
    {
        Mat image = imread(fn[i]);
        //imshow(to_string(i), image);
        //resize(image, image, Size(640, 480));  //这里 必须将image的大小 转换为 VideoWriter video(...)一样的大小。
        video.write(image);
    }

    cout << "写入 成功!" << endl;

    return TRUE;
}
解释:
  • 这里大多是OpenCv的基础知识,唯一需要解释的是我们使用了 glob 函数。
    该函数的使用可参考 链接

至此,已经获取Gif文件的多帧图像,并且保存为了视频文件,接下来我们将它显示出来。

显示生成的视频文件:
代码:
bool Show_Video()
{
    cout << "Show The Video.." << endl;

    VideoCapture video_capture("output.avi");
    if (!video_capture.isOpened())      return FALSE;


    double totalFrameNumber = video_capture.get(CV_CAP_PROP_FRAME_COUNT);
    cout << "整个视频共" << totalFrameNumber << "帧" << endl;
    //设置开始帧()
    long frameToStart = 1;
    video_capture.set(CV_CAP_PROP_POS_FRAMES, frameToStart);
    cout << "从第" << frameToStart << "帧开始读" << endl;

    //设置结束帧
    int frameToStop = totalFrameNumber;

    if (frameToStop < frameToStart)
    {
        cout << "结束帧小于开始帧,程序错误,即将退出!" << endl;
        return FALSE;
    }
    else
    {
        cout << "结束帧为:第" << frameToStop << "帧" << endl;
    }

    //获取帧率
    double rate = video_capture.get(CV_CAP_PROP_FPS);
    cout << "帧率为:" << rate << endl;

    //定义一个用来控制读取视频循环结束的变量
    bool stop = false;
    //承载每一帧的图像
    Mat frame;
    //显示每一帧的窗口
    namedWindow("Extracted frame");
    //两帧间的间隔时间:
    double delay = rate;

    //利用while循环读取帧
    //currentFrame是在循环体中控制读取到指定的帧后循环结束的变量
    long currentFrame = frameToStart;


    while (!stop)
    {
        //读取下一帧
        if (!video_capture.read(frame))
        {
            cout << "读取视频结束" << endl;
            return FALSE;
        }

        imshow("Extracted frame", frame);

        cout << "正在读取第" << currentFrame << "帧" << endl;

        //waitKey(int delay=0)当delay ≤ 0时会永远等待;当delay>0时会等待delay毫秒
        //当时间结束前没有按键按下时,返回值为-1;否则返回按键
        int c = waitKey(delay);

        //按下ESC键退出视频的帧流显示
        if ((char)c == 27 || currentFrame > frameToStop)
        {
            stop = true;
        }
        //按下按键后会停留在当前帧,等待下一次按键
        if (c >= 0)
        {
            waitKey(0);
        }
        currentFrame++;
    }
    //关闭视频文件
    video_capture.release();
    waitKey(0);
    return TRUE;
}
解释:
  • 这段代码有详细的注释,不再赘言。

运行结果以及源代码:

img_2fd1f1df5a94007181eb17c3a7e4e682.gif
运行结果

GutHub:

GitHub

目录
相关文章
|
2月前
|
计算机视觉 Python
Opencv学习笔记(二):如何将整个文件下的彩色图片全部转换为灰度图
使用OpenCV库将一个文件夹内的所有彩色图片批量转换为灰度图,并提供了相应的Python代码示例。
39 0
Opencv学习笔记(二):如何将整个文件下的彩色图片全部转换为灰度图
|
2月前
|
计算机视觉
Opencv学习笔记(八):如何通过cv2读取视频和摄像头来进行人脸检测(jetson nano)
如何使用OpenCV库通过cv2模块读取视频和摄像头进行人脸检测,并提供了相应的代码示例。
129 1
|
2月前
|
编解码 计算机视觉 Python
Opencv学习笔记(九):通过CV2将摄像头视频流保存为视频文件
使用OpenCV库通过CV2将摄像头视频流保存为视频文件,包括定义视频编码格式、设置保存路径、通过write写入视频文件,并提供了相应的Python代码示例。
162 0
|
2月前
|
Ubuntu Linux 编译器
Linux/Ubuntu下使用VS Code配置C/C++项目环境调用OpenCV
通过以上步骤,您已经成功在Ubuntu系统下的VS Code中配置了C/C++项目环境,并能够调用OpenCV库进行开发。请确保每一步都按照您的系统实际情况进行适当调整。
573 3
|
2月前
|
编解码 关系型数据库 计算机视觉
Opencv学习笔记(十一):opencv通过mp4保存为H.264视频
本文介绍了如何在OpenCV中通过使用cisco开源的openh264库来解决不支持H.264编码的问题,并提供了完整的代码示例。
170 0
Opencv学习笔记(十一):opencv通过mp4保存为H.264视频
|
2月前
|
Linux C++
Linux c/c++文件的基本操作
在Linux环境下使用C/C++进行文件的基本操作,包括文件的创建、写入、读取、关闭以及文件描述符的定位。
25 0
Linux c/c++文件的基本操作
|
3月前
|
C++ 内存技术
[转]Visual C++内嵌swf文件并播放
[转]Visual C++内嵌swf文件并播放
|
3月前
|
存储 计算机视觉 C++
在C++中实现Armadillo库与OpenCV库之间的数据格式转换
在C++中实现Armadillo库与OpenCV库之间的数据格式转换是一项常见且实用的技能。上述步骤提供了一种标准的方法来进行这种转换,可以帮助开发者在两个库之间高效地转移和处理数据。虽然转换过程相对直接,但开发者应留意数据类型匹配和性能优化等关键细节。
69 11
|
2月前
|
JSON 数据格式 计算机视觉
Opencv实用笔记(一): 获取并绘制JSON标注文件目标区域(可单独保存目标小图)
本文介绍了如何使用OpenCV和Python根据JSON标注文件获取并绘制目标区域,同时可将裁剪的图像单独保存。通过示例代码,展示了如何读取图片路径、解析JSON标注、绘制标注框并保存裁剪图像的过程。此外,还提供了相关的博客链接,供读者进一步学习。
55 0
|
4月前
|
计算机视觉 索引
OpenCV读取视频失败<无可用信息,未为 opencv_world453.dll 加载任何符号> cv::VideoCapture
本文介绍了解决OpenCV读取视频失败的错误,指出问题通常由视频路径错误或摄像头索引错误导致,并提供了相应的解决方法。
OpenCV读取视频失败<无可用信息,未为 opencv_world453.dll 加载任何符号> cv::VideoCapture