預備接手錶情包處理業務,前期處理並不複雜,流程包括 : GIF動圖與視頻的解幀 , 逐幀處理, 組合各幀獲得新的GIF. 通過調研, 整合了ffmpeg的Java CV 可完美處理解幀 , animated-gif-lib 組件包含gif生成的成熟方案 , 進而問題解決.java
animated-gif-lib.jar是用來拆分和合成GIF的工具包,主要用到其中的GifDecoder/AnimatedGifEncoder.linux
Java CV 經常使用於音頻/圖片等處理,其中整合了經常使用的c++類庫,例如音頻處理的ffmpeg,且可與Open CV配合使用.這裏主要用到FFmpegFrameGrabber來取幀/Java2DFrameConverter來類型轉換.c++
其實,GifDecoder也能夠完成對GIF的解幀,但沒法對視頻進行操做,且實際使用中發現各幀顏色處理上有誤差,但並不影響最後新GIF的合成.綜上,爲了代碼的複用性,採用Java CV來解幀,只使用其中AnimatedGifEncoder來完成合成GIF的操做.ide
解幀,FFmpegFrameGrabber獲取GIF總幀數時異常(),故而採用GifDecoder獲取工具
String gifPath = "/home/lab/test/11.gif"; String dirPath = "/home/lab/test/gif/"; // 用以解幀 FFmpegFrameGrabber grabberGif = new FFmpegFrameGrabber(gifPath); grabberGif.start(); Frame frame ; // 用以獲取GIF總幀數 GifDecoder decoder = new GifDecoder(); int status = decoder.read(gifPath); if (status != GifDecoder.STATUS_OK) { throw new IOException("read image " + gifPath + " error!"); } // 類型轉換,Frame -> BufferedImage Java2DFrameConverter converter = new Java2DFrameConverter(); int frameCount = decoder.getFrameCount(); for (int i = 0 ; i < frameCount ; i++) { String fileName = dirPath + "img_" + i + ".jpg"; File outPut = new File(fileName); frame = grabberGif.grabImage(); if (frame != null) { ImageIO.write(converter.getBufferedImage(frame),"jpg",outPut); } } grabberGif.stop();
合成GIFoop
int frameRate = 20;// 新GIF總幀數 String resGif = "/home/lab/test/22.gif"; FileOutputStream targetFile = new FileOutputStream(resGif); // 目標文件流 int margin = 2; // 間隔幀數 AnimatedGifEncoder en = new AnimatedGifEncoder(); en.setFrameRate(frameRate); en.start(targetFile); for (int i = 0; i < frameRate; i++) { en.addFrame(converter.convert(grabberGif.grabImage())); grabberGif.setFrameNumber(grabberGif.getFrameNumber() + margin); } en.finish(); grabberGif.stop(); targetFile.close();
原GIF倒序獲得新GIFspa
String gifPath = "/home/lab/test/11.gif"; // 用以解幀 FFmpegFrameGrabber grabberGif = new FFmpegFrameGrabber(gifPath); grabberGif.start(); // 用以獲取GIF總幀數 GifDecoder decoder = new GifDecoder(); int status = decoder.read(gifPath); if (status != GifDecoder.STATUS_OK) { throw new IOException("read image " + gifPath + " error!"); } // 類型轉換,Frame -> BufferedImage Java2DFrameConverter converter = new Java2DFrameConverter(); int frameCount = decoder.getFrameCount(); String resGif = "/home/lab/test/22.gif"; FileOutputStream targetFile = new FileOutputStream(resGif); // 目標文件流 AnimatedGifEncoder en = new AnimatedGifEncoder(); en.setFrameRate(frameCount); en.start(targetFile); for (int i = frameCount - 1; i >= 0; i--) { grabberGif.setFrameNumber(i); en.addFrame(converter.convert(grabberGif.grabImage())); } en.finish(); grabberGif.stop(); targetFile.close();
基於GifDecoder和AnimatedGifEncoder實現的gif倒序code
String outputPath = "/home/lab/test/001.gif"; String imagePath = "/home/lab/test/33.gif"; GifDecoder decoder = new GifDecoder(); int status = decoder.read(imagePath); if (status != GifDecoder.STATUS_OK) { throw new IOException("read image " + imagePath + " error!"); } // 拆分一幀一幀的壓縮以後合成 AnimatedGifEncoder encoder = new AnimatedGifEncoder(); encoder.start(outputPath); encoder.setRepeat(decoder.getLoopCount()); for (int i = decoder.getFrameCount() -1; i >= 0; i--) { encoder.setDelay(decoder.getDelay(i));// 設置播放延遲時間 BufferedImage bufferedImage = decoder.getFrame(i);// 獲取每幀BufferedImage流 int height = bufferedImage.getHeight(); int width = bufferedImage.getWidth(); BufferedImage zoomImage = new BufferedImage(width, height, bufferedImage.getType()); Image image = bufferedImage.getScaledInstance(width, height, Image.SCALE_SMOOTH); Graphics gc = zoomImage.getGraphics(); gc.setColor(Color.WHITE); gc.drawImage(image, 0, 0, null); encoder.addFrame(zoomImage); } encoder.finish(); File outFile = new File(outputPath); BufferedImage image = ImageIO.read(outFile); ImageIO.write(image, outFile.getName(), outFile);
視頻轉giform
String videpPath = "/home/lab/test/t1.mp4"; String gifPath = "/home/lab/test/test.gif"; FileOutputStream targetFile = new FileOutputStream(gifPath); FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(videpPath); grabber.start(); Frame frame; int frames = grabber.getLengthInFrames(); AnimatedGifEncoder encoder = new AnimatedGifEncoder(); encoder.setFrameRate(frames); encoder.start(targetFile); Java2DFrameConverter converter = new Java2DFrameConverter(); for (int i = 0 ; i < frames ; i++) { encoder.setDelay((int) grabber.getDelayedTime()); grabber.setFrameNumber(i); frame = grabber.grabImage(); encoder.addFrame(converter.convert(frame)); } encoder.finish(); targetFile.close(); grabber.close();
pom依賴視頻
因 javacv-platform依賴太重,實際引入的時候推薦指定系統版本的便可.開發機爲64位Ubuntu,依賴以下
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv</artifactId> <version>1.4.3</version> </dependency> <dependency> <groupId>org.bytedeco.javacpp-presets</groupId> <artifactId>opencv</artifactId> <version>3.4.3-1.4.3</version> <classifier>linux-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco.javacpp-presets</groupId> <artifactId>ffmpeg</artifactId> <version>4.0.2-1.4.3</version> <classifier>linux-x86_64</classifier> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacpp</artifactId> <version>1.4.3</version> </dependency> <dependency> <groupId>org.bytedeco.javacpp-presets</groupId> <artifactId>ffmpeg</artifactId> <version>4.0.2-1.4.3</version> <classifier>linux-x86_64</classifier> </dependency> <!-- gif --> <dependency> <groupId>com.madgag</groupId> <artifactId>animated-gif-lib</artifactId> <version>1.4</version> </dependency>