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

  合成GIF

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

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

        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);

   視頻轉gif

        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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM