java gif 帧_javacv之于视频/GIF解帧及重新拼接生成GIF实现

预备接手表情包处理业务,前期处理并不复杂,流程包括 : GIF动图与视频的解帧 , 逐帧处理, 组合各帧得到新的GIF. 经过调研, 整合了ffmpeg的Java CV 可完美处理解帧 , animated-gif-lib 组件包含gif生成的成熟方案 , 进而问题解决.

animated-gif-lib + Java CV

animated-gif-lib.jar是用来拆分和合成GIF的工具包,主要用到其中的GifDecoder/AnimatedGifEncoder.

Java CV 常用于音频/图片等处理,其中整合了常用的c++类库,例如音频处理的ffmpeg,且可与Open CV配合使用.这里主要用到FFmpegFrameGrabber来取帧/Java2DFrameConverter来类型转换.

其实,GifDecoder也可以完成对GIF的解帧,但无法对视频进行操作,且实际使用中发现各帧颜色处理上有偏差,但并不影响最后新GIF的合成.综上,为了代码的复用性,采用Java CV来解帧,只使用其中AnimatedGifEncoder来完成合成GIF的操作.

代码实现

解帧,FFmpegFrameGrabber获取GIF总帧数时异常(),故而采用GifDecoder获取

String gifPath = "/home/lab/test/11.gif";

String dirPath= "/home/lab/test/gif/";//用以解帧

FFmpegFrameGrabber grabberGif = newFFmpegFrameGrabber(gifPath);

grabberGif.start();

Frame frame ;//用以获取GIF总帧数

GifDecoder decoder = newGifDecoder();int status =decoder.read(gifPath);if (status !=GifDecoder.STATUS_OK) {throw new IOException("read image " + gifPath + " error!");

}//类型转换,Frame -> BufferedImage

Java2DFrameConverter converter = newJava2DFrameConverter();int frameCount =decoder.getFrameCount();for (int i = 0 ; i < frameCount ; i++) {

String fileName= dirPath + "img_" + i + ".jpg";

File outPut= newFile(fileName);

frame=grabberGif.grabImage();if (frame != null) {

ImageIO.write(converter.getBufferedImage(frame),"jpg",outPut);

}

}

grabberGif.stop();

合成GIF

int frameRate = 20;//新GIF总帧数

String resGif = "/home/lab/test/22.gif";

FileOutputStream targetFile= new FileOutputStream(resGif); //目标文件流

int margin = 2; //间隔帧数

AnimatedGifEncoder en = newAnimatedGifEncoder();

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倒序得到新GIF

String gifPath = "/home/lab/test/11.gif";//用以解帧

FFmpegFrameGrabber grabberGif = newFFmpegFrameGrabber(gifPath);

grabberGif.start();//用以获取GIF总帧数

GifDecoder decoder = newGifDecoder();int status =decoder.read(gifPath);if (status !=GifDecoder.STATUS_OK) {throw new IOException("read image " + gifPath + " error!");

}//类型转换,Frame -> BufferedImage

Java2DFrameConverter converter = newJava2DFrameConverter();int frameCount =decoder.getFrameCount();

String resGif= "/home/lab/test/22.gif";

FileOutputStream targetFile= new FileOutputStream(resGif); //目标文件流

AnimatedGifEncoder en = newAnimatedGifEncoder();

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倒序

String outputPath = "/home/lab/test/001.gif";

String imagePath= "/home/lab/test/33.gif";

GifDecoder decoder= newGifDecoder();int status =decoder.read(imagePath);if (status !=GifDecoder.STATUS_OK) {throw new IOException("read image " + imagePath + " error!");

}//拆分一帧一帧的压缩之后合成

AnimatedGifEncoder encoder = newAnimatedGifEncoder();

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= newBufferedImage(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= newFile(outputPath);

BufferedImage image=ImageIO.read(outFile);

ImageIO.write(image, outFile.getName(), outFile);

视频转gif

String videpPath = "/home/lab/test/t1.mp4";

String gifPath= "/home/lab/test/test.gif";

FileOutputStream targetFile= newFileOutputStream(gifPath);

FFmpegFrameGrabber grabber= newFFmpegFrameGrabber(videpPath);

grabber.start();

Frame frame;int frames =grabber.getLengthInFrames();

AnimatedGifEncoder encoder= newAnimatedGifEncoder();

encoder.setFrameRate(frames);

encoder.start(targetFile);

Java2DFrameConverter converter= newJava2DFrameConverter();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,依赖如下

org.bytedeco

javacv

1.4.3

org.bytedeco.javacpp-presets

opencv

3.4.3-1.4.3

linux-x86_64

org.bytedeco.javacpp-presets

ffmpeg

4.0.2-1.4.3

linux-x86_64

org.bytedeco

javacpp

1.4.3

org.bytedeco.javacpp-presets

ffmpeg

4.0.2-1.4.3

linux-x86_64

com.madgag

animated-gif-lib

1.4

你可能感兴趣的:(java,gif,帧)