IT七剑客 IT七剑客
首页
wresource
郭霖
孤寒者
IT邦德
沉默王二
老麦
stackoverflow
GitHub (opens new window)
首页
wresource
郭霖
孤寒者
IT邦德
沉默王二
老麦
stackoverflow
GitHub (opens new window)
  • Java基础语法

  • 程序人生

  • 实用工具

  • Java重要知识点

  • Java工具

    • currentTimeMillis 统计耗时了,太 Low,试试 Spring Boot 源码在用的 StopWatch吧,够优雅!
    • WindTerm:新一代开源免费的终端工具,GitHub星标6.6k+,太酷了!
    • 在IDEA里下个五子棋不过分吧?
    • 好用到爆!GitHub 星标 32.5k+的命令行软件管理神器,功能真心强大!
    • 我们公司使用了6年的Spring Boot项目部署方案,打包 + 一键部署,稳的一批
    • 再见 Spring Task,这款老而弥坚的开源任务调度框架,用起来够优雅!
    • 解放双手!推荐一款 GitHub 星标 8.2k+的命令行软件管理器,非常酷炫!
    • 再见收费的TeamViewer,推荐一款不限速的国产远程控制软件
    • 几行代码,网站图片访问速度 100ms 飙升到 20ms!
    • 某意大利小哥,竟靠一个缓存中间件直接封神?
    • WindTerm:新一代开源免费的终端工具,GitHub星标6.6k+,太酷了!
    • 厉害!我带的实习生仅用四步就整合好SpringSecurity+JWT实现登录认证!
    • 保姆级SpringBoot+Vue图片上传到阿里云OSS教程
    • 两天两夜,1M图片优化到100kb!
      • 干掉Session?这个跨域认证解决方案真的优雅!
      • 前后端分离项目,如何解决跨域问题?
      • Spring Boot AOP 扫盲,实现接口访问的统一日志记录
      • 再见收费的Navicat!操作所有数据库就靠它了!
      • 取代 Mybatis Generator,这款代码生成神器配置更简单,开发效率更高!
      • 再见丑陋的 SwaggerUI,这款开源的API文档生成神器界面更炫酷,逼格更高!
    • 数组与字符串

    • 沉默王二 Java
    • Java工具
    沉默王二
    2022-09-01
    目录

    两天两夜,1M图片优化到100kb!

    坦白从宽吧,我就是那个花了两天两夜把 1M 图片优化到 100kb 的家伙——王小二!

    自从因为一篇报道登上热搜后,我差点抑郁,每天要靠 50 片安眠药才能入睡。

    网络上曝光的那些关于一码通的消息,有真有假,我这里就不再澄清了。就说说我是怎么把图片从 1M 优化到 100kb 的故事吧。

    是的,由于系统群体规模和访问规模的特殊性,每一行代码、每一张图片、每一个技术文档都反复核准,优化再优化,精益求精。为确保系统运行得更高效,我们将一张图片从1MB压缩到500KB,再从500KB优化到100KB。

    这样的工作在外人看起来,简单到就好像悄悄给学妹塞一张情书就能让她做我女朋友一样简单。

    但殊不知,这其中蕴含着极高的技术含量!

    不信,我给你们普及下。

    # 一、图像压缩

    图像压缩是数据压缩技术在数字图像上的应用,目的是减少图像数据中的冗余信息,从而用更加高效的格式存储和传输数据。

    图像压缩可以是有损数据压缩,也可以是无损数据压缩。

    img

    img

    怎么样?

    是不是感觉图像压缩技术没有想象中那么简单了?

    更多关于图像压缩的资料可参考以下链接。

    机器之心:www.jiqizhixin.com/graph/techn… (opens new window)

    # 二、Java数字图像处理

    作为这次“20 多万外包项目”的“主力开发人员”,我这里就给大家介绍下 Java 数字图像处理技术吧,一开始我就是用它来处理图片的。

    数字图像处理(Digital Image Processing)是通过计算机对图像进行去除噪声、增强、复原、分割、提取特征等处理的方法和技术。

    img

    输入的是图像信号,然后经过 DIP 进行有效的算法处理后,输出为数字信号。

    为了压缩图像,我们需要读取图像并将其转换成 BufferedImage 对象,BufferedImage 是 Image 类的一个子类,描述了一个具有可访问的图像数据缓冲区,由 ColorModel 和 Raster 的图像数据组成。

    img

    废话我就不多说了,直接进入实战吧!

    # 三、图像压缩实战

    刚好我本地有一张之前用过的封面图,离 1M 只差 236 KB,可以拿来作为测试用。

    img

    这其中要用到 ImageIO 类,这是一个静态类,提供了一系列方法用来读和写图像,同时还可以对图像进行简单的编码和解码。

    比如说通过 ImageIO.read() 可以将图像读取到 BufferedImage 对象:

    File input = new File("ceshi.jpg");
    BufferedImage image = ImageIO.read(input);
    复制代码
    
    1
    2
    3

    比如说通过 ImageIO.getImageWritersByFormatName() 可以返回一个Iterator,其中包含了通过命名格式对图像进行编码的 ImageWriter。

    Iterator<ImageWriter> writers =  ImageIO.getImageWritersByFormatName("jpg");
    ImageWriter writer = (ImageWriter) writers.next();
    复制代码
    
    1
    2
    3

    比如说通过 ImageIO.createImageOutputStream() 可以创建一个图像的输出流对象,有了该对象后就可以通过 ImageWriter.setOutput() 将其设置为输出流。

    File compressedImageFile = new File("bbcompress.jpg");
    OutputStream os =new FileOutputStream(compressedImageFile);
    ImageOutputStream ios = ImageIO.createImageOutputStream(os);
    writer.setOutput(ios);
    复制代码
    
    1
    2
    3
    4
    5

    紧接着,可以对 ImageWriter 进行一些参数配置,比如说压缩模式,压缩质量等等。

    ImageWriteParam param = writer.getDefaultWriteParam();
    
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionQuality(0.01f);
    复制代码
    
    1
    2
    3
    4
    5

    压缩模式一共有四种,MODE_EXPLICIT 是其中一种,表示 ImageWriter 可以根据后续的 set 的附加信息进行平铺和压缩,比如说接下来的 setCompressionQuality() 方法。

    setCompressionQuality() 方法的参数是一个 0-1 之间的数,0.0 表示尽最大程度压缩,1.0 表示保证图像质量很重要。对于有损压缩方案,压缩质量应该控制文件大小和图像质量之间的权衡(例如,通过在写入 JPEG 图像时选择量化表)。 对于无损方案,压缩质量可用于控制文件大小和执行压缩所需的时间之间的权衡(例如,通过优化行过滤器并在写入 PNG 图像时设置 ZLIB 压缩级别)。

    整体代码如下所示:

    public class Demo {
        public static void main(String[] args) {
    
            try {
                File input = new File("ceshi.jpg");
                BufferedImage image = ImageIO.read(input);
    
    
                Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
                ImageWriter writer = (ImageWriter) writers.next();
    
                File compressedImageFile = new File("bbcompress.jpg");
                OutputStream os = new FileOutputStream(compressedImageFile);
                ImageOutputStream ios = ImageIO.createImageOutputStream(os);
                writer.setOutput(ios);
    
    
                ImageWriteParam param = writer.getDefaultWriteParam();
    
                param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                param.setCompressionQuality(0.01f);
    
                writer.write(null, new IIOImage(image, null, null), param);
    
                os.close();
                ios.close();
                writer.dispose();
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    复制代码
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34

    执行压缩后,可以看到图片的大小压缩到了 19 KB:

    img

    可以看得出,质量因子为 0.01f 的时候图片已经有些失真了,可以适当提高质量因子比如说 0.5f,再来看一下。

    img

    图片质量明显提高了,但大小依然只有 64 KB,压缩效果还是值得信赖的。

    # 四、其他开源库

    接下来,推荐一些可以轻松集成到项目中的图像处理库吧,它们全都是免费的。

    1)ImageJ,用 Java 编写的,可以编辑、分析、处理、保存和打印图像。

    img

    2)Apache Commons Imaging,一个读取和写入各种图像格式的库,包括快速解析图像信息(如大小,颜色,空间,ICC配置文件等)和元数据。

    img

    3)ImageMagick,可以读取和写入超过100种格式的图像,包括DPX、EXR、GIF、JPEG、JPEG-2000、PDF、PNG、Postscript、SVG和TIFF。还可以调整大小、翻转、镜像、旋转、扭曲、剪切和变换图像,调整图像颜色,应用各种特殊效果,包括绘制文本、线条、多边形、椭圆和贝塞尔曲线。

    img

    4)OpenCV,由BSD许可证发布,可以免费学习和商业使用,提供了包括 C/C++、Python 和 Java 等主流编程语言在内的接口。OpenCV 专为计算效率而设计,强调实时应用,可以充分发挥多核处理器的优势。

    img

    这里就以 OpenCV 为例,来演示一下图像压缩。当然了,OpenCV 用来压缩图像属于典型的大材小用。

    第一步,添加 OpenCV 依赖到我们的项目当中,以 Maven 为例。

    <dependency>
    	<groupId>org.openpnp</groupId>
    	<artifactId>opencv</artifactId>
    	<version>4.5.1-2</version>
    </dependency>
    复制代码
    
    1
    2
    3
    4
    5
    6

    第二步,要想使用 OpenCV,需要先初始化。

    OpenCV.loadShared();
    复制代码
    
    1
    2

    第三步,使用 OpenCV 读取图片。

    Mat src = Imgcodecs.imread(imagePath);
    复制代码
    
    1
    2

    第四步,使用 OpenCV 压缩图片。

    MatOfInt dstImage = new MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, 1);
    Imgcodecs.imwrite("resized_image.jpg", sourceImage, dstImage);
    复制代码
    
    1
    2
    3

    MatOfInt 的构造参数是一个可变参数,第一个参数 IMWRITE_JPEG_QUALITY 表示对图片的质量进行改变,第二个是质量因子,1-100,值越大表示质量越高。

    执行代码后得到的图片如下所示:

    img

    借这个机会,来对比下 OpenCV 和 JDK 原生 API 在压缩图像时所使用的时间。

    这是我本机的配置情况,早年买的顶配 iMac,也是我的主力机。一开始只有 16 G 内存,后来加了一个 16 G 内存条,不过最近半年电脑突然死机重启的频率明显提高了,不知道是不是 Big Sur 这个操作系统的问题还是电脑硬件老了。

    img

    结果如下所示:

    opencvCompress压缩完成,所花时间:1070
    jdkCompress压缩完成,所花时间:322
    复制代码
    
    1
    2
    3

    压缩后的图片大小差不多,都是 19 KB,并且质量因子都是最低值。

    img

    # 四、一点点心声

    经过上面的技术分析后,相信你们都明白了,把1M图片优化到100kb实在是一件“不太容易”的事情。。。。

    100KB 很小了吧?只有原来的 1/10。

    要知道,我可是连续加班了两天两夜,不眠不休。

    img

    累到最后,我趴在电脑上都睡着了。

    没想到哈喇子直接给电脑整短路了,我这才算是从梦里面吓醒来了!

    😔,生活不易,且行且珍惜吧~


    本篇已收录至 GitHub 上星标 1.6k+ star 的开源专栏《Java 程序员进阶之路》,据说每一个优秀的 Java 程序员都喜欢她,风趣幽默、通俗易懂。内容包括 Java 基础、Java 并发编程、Java 虚拟机、Java 企业级开发、Java 面试等核心知识点。学 Java,就认准 Java 程序员进阶之路😄。

    github.com/itwanger/to… (opens new window)

    star 了这个仓库就等于你拥有了成为了一名优秀 Java 工程师的潜力。也可以戳下面的链接跳转到《Java 程序员进阶之路》的官网网址,开始愉快的学习之旅吧。

    tobebetterjavaer.com/ (opens new window)

    image

    没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟。

    作者:沉默王二 链接:https://juejin.cn/post/7072614545381916708 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    编辑 (opens new window)
    #Java#沉默王二
    上次更新: 2023/01/30, 02:04:53
    保姆级SpringBoot+Vue图片上传到阿里云OSS教程
    干掉Session?这个跨域认证解决方案真的优雅!

    ← 保姆级SpringBoot+Vue图片上传到阿里云OSS教程 干掉Session?这个跨域认证解决方案真的优雅!→

    最近更新
    01
    Coding 102 Writing code other people can read
    02-26
    02
    Kotlin Flow响应式编程,StateFlow和SharedFlow
    02-05
    03
    Kotlin Flow响应式编程,操作符函数进阶
    02-05
    更多文章>
    Theme by Vdoing | Copyright © 2022-2023 IT七剑客 | MIT License
  • 闽ICP备2021006579号-4
  • 闽公网安备 35012102500470号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式