资讯动态

浅谈Thumbnails压缩gif图片质量的实现方式

发布时间:2026/9/9 12:18:42 来源:尧图企业网站定制
Thumbnails是一个比较大众的图片处理工具类似的工具还有hutool可以对图片进行裁剪、缩放、旋转、格式转换、水印等。然而它只提供单张图片的压缩对于gif的压缩却是需要我们自己去处理。 下面是一段压缩图片质量的代码public static BufferedImage compressPic(BufferedImage frame,float scale) throws Exception{ return Thumbnails.of(frame).outputFormat(jpg).scale(scale).outputQuality(scale).asBufferedImage(); }其中scale是对图片进行缩放的比例传入一个0到1之间的浮点数大于1表示放大outputQuality代表输出图片的质量值在0到1之间但是我在实际压缩图片的使用中outputQuality这个参数并不生效图片最终的质量只受到scale影响不清楚是何原因可能是因为这个outputQuality取值和图片字节大小有联系。官方提供了一个根据文件大小动态调整压缩率的计算方式如下/** * 自动调节精度(经验数值) * * param size 源图片大小 * return 图片压缩质量比 */ private static double getAccuracy(long size) { double accuracy; if (size 900) { accuracy 0.85; } else if (size 2047) { accuracy 0.6; } else if (size 3275) { accuracy 0.44; } else { accuracy 0.4; } return accuracy; }通过动态调整压缩比例重复几次压缩可以实现将单张图片压缩到指定大小一下代码如下/** * 根据指定大小压缩图片 * * param imageBytes 源图片字节数组 * param desFileSize 指定图片大小单位kb * return 压缩质量后的图片字节数组 */ public static byte[] compressPicForScale(byte[] imageBytes, long desFileSize,String prefix) throws Exception{ long d desFileSize * 1024; if (imageBytes null || imageBytes.length 0 || imageBytes.length d) { return imageBytes; } long srcSize imageBytes.length; double accuracy getAccuracy(srcSize / 1024); while (imageBytes.length d) { ByteArrayInputStream inputStream new ByteArrayInputStream(imageBytes); ByteArrayOutputStream outputStream new ByteArrayOutputStream(imageBytes.length); BufferedImage read ImageIO.read(inputStream); Thumbnails.of(read) .scale(accuracy) .outputQuality(accuracy) .outputFormat(prefix) .toOutputStream(outputStream); imageBytes outputStream.toByteArray(); } return imageBytes; }这种方式在压缩单张图片时没有问题然而压缩gif时并不适用因为gif是由多张图片组成的每一张都是GIF的一个帧每帧之间有一个间隔播放时间类似于视频所以我们要压缩gif有两个方向去实现1.压缩每张图片的质量我们可以读取到gif的每一帧然后对每一帧都进行压缩最后再将压缩后的帧按顺序拼接起来组成新的gif原则上新的gif占用的空间会减少。然而存在一个问题我们使用上面的代码去压缩时由于每一帧图片大小不一致压缩的比例也不一样无法事先确定图片宽高导致压缩出来的每帧的宽高都不一致这样就导致gif没法看了。因此我们不能将图片帧按大小压缩而是应该直接进行缩放比例这样也能压缩质量保证每张图片宽高一致。2.减少帧的数量一般比较大的图片都是帧数非常多通过减少帧数也能显著降低图片大小可以对帧数进行一个采样只选出固定数量的帧数进行压缩然后拼接成新的gif这样的方式其实我们在日常生活中有时也能看到gif变成了一帧一帧不连续的动图。采样的方式有很多种顺序采样或者均匀采样随机采样等等。对于缩放比例我们可以根据图片宽高进行动态计算public static float getScare(int width,int height){ int n Math.max(width,height); float rate; //大于450像素 if(n 450){ //缩放到450*0.290像素 rate 0.2f; }else if(n 400){ rate 0.26f; }else if(n 300){ rate 0.3f; }else if(n 200){ rate 0.4f; }else if(n 100){ rate 0.6f; }else if(n 80){ rate 0.7f; }else{ rate 0.8f; } return rate; }gif生成使用到了如下依赖dependency groupIdcom.madgag/groupId artifactIdanimated-gif-lib/artifactId version1.4/version /dependency具体压缩gif代码如下public static byte[] compressGif(byte[] data) throws Exception{ ByteArrayInputStream stream new ByteArrayInputStream(data); ByteArrayOutputStream out new ByteArrayOutputStream(); GifDecoder decoder new GifDecoder(); decoder.read(stream); int cnt decoder.getFrameCount(); //如果只有一帧直接压缩到20kb if(cnt 1){ return compressPicForScale(data, 20, jpg); } int width decoder.getImage().getWidth(); int height decoder.getImage().getHeight(); float scale getScare(width,height); width (int) (width*scale); height (int) (height*scale); AnimatedGifEncoder e new AnimatedGifEncoder(); // 设置生成图片大小 e.setSize(width, height); //保存到数组 e.start(out); //重复次数 0表示无限重复 默认不重复 e.setRepeat(0); //进行采样 BufferedImage[] fs getFrames(decoder); for (BufferedImage f : fs) { if (fs.length 1) { //设置延迟 int delay; if (fs.length 5) { delay 200; } else if (fs.length 4) { delay 400; } else { delay 1000; } e.setDelay(delay); } BufferedImage image compressPic(f, scale); e.addFrame(image); } e.finish(); return out.toByteArray(); }采样代码private static BufferedImage[] getFrames(GifDecoder decoder){ int cnt decoder.getFrameCount(); //我这里只采了5帧 int max 5; if(cnt max){ BufferedImage[] r new BufferedImage[cnt]; for (int i 0; i cnt; i) { r[i] decoder.getFrame(i); } return r; }else if(cnt max*2){ BufferedImage[] r new BufferedImage[max]; for (int i 0; i max; i) { r[i] decoder.getFrame(i); } return r; }else{ BufferedImage[] r new BufferedImage[max]; int sec cnt/max; int n 0; for (int i 0; i cnt n max; isec) { r[n] decoder.getFrame(i); n; } return r; } }调用方式public static void main(String[] args) throws Exception{ byte[] d FileUtils.readFileToByteArray(new File(D:\\zhou\\111.gif)); byte[] compress compress(d,111.gif); if(compress.length d){ //压缩后体积是减小的才保存 FileUtils.writeByteArrayToFile(new File(D:\\zhou\\222.gif),compress); } }

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价