Android ffmpeg解析和合成gif

概述

Android项目中使用ffmpeg3.0.9通过命令行调用的方式解析和合成gif。简单学习总结一下。

解析gif,由gif得到一堆png

/**
 * 解析gif图片
 * @param gifPath gif图片地址
 * @param pngDir 解析后图片保存目录
 * @return
 */
public static String[] gifToPng(String gifPath, String pngDir) {
    final int count = 6;
    String[] commands = new String[count];
    int index = 0;
    commands[index++] = "ffmpeg";
    commands[index++] = "-i";
    commands[index++] = gifPath;
    commands[index++] = "-c:v";
    commands[index++] = "png";
    commands[index++] = pngDir + File.separator + "image%d.png";
    if (TestLog.isDebug()) {
        TestLog.d(TAG, "index=" + index + ",count=" + count);
        for (int i = 0 ; i < index; i++) {
            TestLog.d(TAG, "commend=" + commands[i]);
        }
    }
    return commands;
}

解析后的图片保存到指定的文件目录下,命名规则image%d.png,如下图所示:

QQ截图20180308193203.png

一堆png合成gif

/**
 * png图片合成gif
 * @param pngDir png文件夹地址 该文件夹下的png图片命名需要满足正则image%d.png
 * @param gifPath 生成的gif地址
 * @param delay 帧间隔 ms
 * @param threadNum 线程数 0为默认最佳
 * @return
 */
public static String[] pngToGif(String pngDir, String gifPath, long delay, int threadNum) {
    final int count = 11;
    String[] commands = new String[count];
    int index = 0;
    commands[index++] = "ffmpeg";
    commands[index++] = "-r";
    commands[index++] = String.valueOf(1000.0f / delay);
    commands[index++] = "-threads";
    commands[index++] = String.valueOf(threadNum);
    commands[index++] = "-c:v";
    commands[index++] = "png";
    commands[index++] = "-i";
    commands[index++] = pngDir + File.separator + "image%d.png";
    commands[index++] = "-y";
    commands[index++] = gifPath;
    if (TestLog.isDebug()) {
        TestLog.d(TAG, "index=" + index + ",count=" + count);
        for (int i = 0 ; i < index; i++) {
            TestLog.d(TAG, "commend=" + commands[i]);
        }
    }
    return commands;
}

合成后发现gif图片质量很差,对比如下:

原图.gif
合成后.gif

实际使用中还发现合成前后部分颜色前后不一致,如之前显示黑色,合成后显示黄色。类似问题还有很多,这里不在举例。

优化gif合成

参考:http://blog.pkh.me/p/21-high-quality-gif-with-ffmpeg.html#content

原因分析

GIF仅限于256色的调色板。默认情况下,FFmpeg 只使用一个通用调色版去尝试覆盖所有的颜色区域,以此来支持含有大量内容的文件。这就解释了上述方法生成的gif文件为何效果很差。

优化

FFmpeg 只使用一个通用调色版去尝试覆盖所有的颜色区域,所以我们可以针对特定的GIF图片,提供特定的调色板即重新定义一个调色板来替代FFmpeg提供的通用调色板。

为特定的gif生成调色板

使用 palettegen 滤波器对每一帧的所有颜色制作一个直方图,并且基于这些生成一个调色板(以PNG文件的形式保存)。

/**
 * 特定gif生成调色板
 * @param gifPath 输入gif图片文件地址
 * @param globalPalettePicPath 输出gif图片文件地址
 * @return
 */
public static String[] getglobalPalettePicPath(String gifPath, String globalPalettePicPath) {
    int count = 7;

    String[] commands = new String[count];
    int index = 0;
    commands[index++] = "ffmpeg";

    commands[index++] = "-i";
    commands[index++] = gifPath;
    /*commands[index++] = "-i";
    commands[index++] = waterImageUrl;*/
    commands[index++] = "-vf";
    commands[index++] = "palettegen";

    commands[index++] = "-y";
    commands[index++] = globalPalettePicPath;

    if (TestLog.isDebug()) {
        TestLog.d(TAG, "index=" + index + ",count=" + count);
        for (int i = 0 ; i < index; i++) {
            TestLog.d(TAG, "commend=" + commands[i]);
        }
    }
    return commands;
}

下图是生成的调色板:

gif_paletter.png

替换通用的调色板

生成全局调色板后,就可以使用 paletteuse 滤波器替换通用的调色板将颜色效果映射到颜色输出流中。它将会使用这个调色板来生成最终的量化颜色流,它的任务是在生成的调色板中找出最合适的颜色来表示输入的颜色。

/**
 * png图片合成gif
 * @param pngDir png文件夹地址 该文件夹下的png图片命名需要满足正则image%d.png
 * @param gifPath 生成的gif地址
 * @param globalPalettePicPath 全局颜色列表png图地址
 * @param delay 帧间隔 ms
 * @param threadNum 线程数 0为默认最佳
 * @return
 */
public static String[] pngToGif(String pngDir, String gifPath, String globalPalettePicPath, long delay, int threadNum) {
    final int count = 15;
    String[] commands = new String[count];
    int index = 0;
    commands[index++] = "ffmpeg";
    commands[index++] = "-r";
    commands[index++] = String.valueOf(1000.0f / delay);
    commands[index++] = "-threads";
    commands[index++] = String.valueOf(threadNum);
    commands[index++] = "-c:v";
    commands[index++] = "png";
    commands[index++] = "-i";
    commands[index++] = pngDir + File.separator + "image%d.png";

    commands[index++] = "-i";
    commands[index++] = globalPalettePicPath;
    commands[index++] = "-lavfi";
    commands[index++] = "paletteuse=bayer";

    commands[index++] = "-y";
    commands[index++] = gifPath;
    if (TestLog.isDebug()) {
        TestLog.d(TAG, "index=" + index + ",count=" + count);
        for (int i = 0 ; i < index; i++) {
            TestLog.d(TAG, "commend=" + commands[i]);
        }
    }
    return commands;
}

参考

1.https://link.jianshu.com/?t=http://blog.pkh.me/p/21-high-quality-gif-with-ffmpeg.html#content
2.https://www.jianshu.com/p/a11bbf804e75

扫描二维码关注公众号,回复: 1425989 查看本文章

猜你喜欢

转载自blog.csdn.net/tugele/article/details/79535748