Java對GIF的簡單刪幀操作
問題簡介
最近在一些網站爬了一些搞笑動態圖片,沒想到儲存好之後預覽竟然是是這樣:
用圖片瀏覽器逐幀看了一下,原來每一張圖片第一幀都是類似空白的畫面,所以預覽的縮圖也是第一張畫面。
那麼如果能用程式碼讀取到GIF的每一幀,刪除後在合併為一個新GIF那問題就解決了。
於是找了下Java 操作GIF圖片的類庫,最後在GitHub上找到了這個:
方法簡介
整個工程主要就兩個java檔案,一個用於解碼的GifDecoder
類,一個用於編碼的AnimatedGifEncoder
,另外有兩個輔助類複製進來即可。
GifDecoder類
主要方法有:
public int read(String name)
指定需要解碼的GIF圖片路徑,還有兩個過載方法,支援讀取
InputStream
和BufferedInputStream
public int getDelay(int n)
獲取指定幀的延遲時間。
public int getFrameCount()
獲取GIF圖片的幀數
public int getLoopCount()
獲取GIF圖片的播放次數,0表示無限迴圈播放
public BufferedImage getFrame(int n)
獲取指定幀的影象資料
AnimatedGifEncoder類
另一個是用於編碼的AnimatedGifEncoder
類,方法和解碼基本相反:
public void setDelay(int ms)
設定每幀之間的延遲時間,
GifDecoder
能獲取到任意兩幀之間的延遲,而AnimatedGifEncoder
貌似沒有提供兩幀之間的延遲設定,按照同一延遲時間處理。public void setRepeat(int iter)
設定播放次數,0表示無限迴圈播放
public boolean addFrame(BufferedImage im)
新增幀
public void setFrameRate(float fps)
設定幀率,與delay作用類似,相對設定delay為Math.round(100f / fps)
public boolean finish()
新增幀並配置好之後呼叫這個方法關閉檔案輸出流等
public boolean start(String file)
設定輸出的檔案路徑,另一個過載方法以流的形式輸出
另外AnimatedGifEncoder
還提供一些其他方法,包括設定透明度、背景顏色、圖片質量、尺寸等。
程式碼
知道用法之後就簡單了,先解碼將原始GIF的所有幀資料、幀延遲、播放次數讀取出來,然後再編碼回去,同時刪除第一幀即可。
這裡除了點小問題,我的環境中Eclipse突然無法讀取絕對路徑,所以使用File.separator代替斜槓。
private String baseOutPath = "D:" + File.separator + "p" + File.separator+ "2" + File.separator;
//存放解碼出來的所有幀
private BufferedImage[] bi;
//延遲時間
private int delay;
public static void main(String[] args) throws IOException {
GIFHandler handler = new GIFHandler();
dt.start();
}
private void start() {
//"D:/p/1",要轉換的GIF圖片目錄
File gifPath = new File("D:" + File.separator + "p" + File.separator+ "1");
File[] gifs = null;
//讀取出所有GIF圖片
if (gifPath.isDirectory()) {
gifs = gifPath.listFiles();
}
if (gifs != null && gifs.length != 0) {
for (File f : gifs) {
trans(f);
System.out.println(f.getName());
}
}
System.out.println("轉換完成");
}
/**
* 轉換
*/
private void trans(File transFile) {
decode(transFile);
encode(baseOutPath + transFile.getName());
}
private void encode(String outPath) {
AnimatedGifEncoder encoder = new AnimatedGifEncoder();
// 設定迴圈模式,0為無限迴圈 這裡沒有使用原始檔的播放次數
encoder.setRepeat(0);
// 設定輸出路徑
encoder.start(outPath);
//這裡從1開始,去掉第一幀
for (int i = 1; i < bi.length; i++) {
encoder.setDelay(delay);
encoder.addFrame(bi[i]);
}
encoder.finish();
}
private void decode(File f) {
GifDecoder decoder = new GifDecoder();
try {
decoder.read(new FileInputStream(f));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 獲取到幀數
int frameCount = decoder.getFrameCount();
bi = new BufferedImage[frameCount];
// 獲取到每一幀的資料儲存到bi
for (int i = 0; i < frameCount; i++) {
bi[i] = decoder.getFrame(i);
}
// 獲取到每幀之間的延遲時間,這裡只取第一幀的
delay = decoder.getDelay(0);
// ImageIO.write(frame, "jpeg", out); 該方法用於輸出分解得到的單個圖片檔案
}
使用這兩個類可以進行一些簡單的幀操作,比如新增水印、設定延遲等。另外原始碼也不復雜,可以自行定義。