Android ffmpeg解析和合成gif
阿新 • • 發佈:2019-02-04
概述
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合成
原因分析
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;
}