java中图片按质量压缩的再一次小结

研究了那么久的图片压缩原理之后

虽然没能带回一个自己用JAVA实现的图片压缩软件

但是总算是自己终于对图片压缩有了个清晰的了解

 

好了,废话不多说

继续上次关于远程监控系统中用UDP广播图片遇到的图片压缩大小瓶颈问题

 

首先再次讨论上次给出的那组数据(关于对比ImageIO默认参数下写出GIF/JPEG,以及自己设置参数的JPEG)的讨论

GIF采用的是字典LZW算法,该算法是无损压缩,能提供近乎完美的无损压缩比,我们记得压缩后的图片大小大约为90KB

而JPEG默认情况下为有损压缩,压缩后大小大约200KB

在ImageIO中通过自己设置压缩质量来压缩,我们发现当压缩质量小于0.5以后,图片大小的变化是很缓慢的

用import com.sun.image.codec.jpeg.JPEGCodec提供的编码解码类来设置压缩质量,在同等质量的情况下,虽然比ImageIO中图片大小变小了,其实也是很有限的。通过前文,我们了解了这样设置图片质量其实知识改变量化表,在图片质量已经不高的情况下,其改变对图片大小的影响其实是很小的

package cn.mzd.newIM.test;  
  
import java.awt.AWTException;  
import java.awt.Dimension;  
import java.awt.Rectangle;  
import java.awt.image.BufferedImage;  
import java.awt.image.ColorModel;  
import java.io.ByteArrayOutputStream;  
import java.io.IOException;  
import java.util.Calendar;  
import java.util.GregorianCalendar;  
import java.util.Iterator;  
  
import javax.imageio.IIOImage;  
import javax.imageio.ImageIO;  
import javax.imageio.ImageWriteParam;  
import javax.imageio.ImageWriter;  
  
import com.sun.image.codec.jpeg.JPEGCodec;  
import com.sun.image.codec.jpeg.JPEGEncodeParam;  
import com.sun.image.codec.jpeg.JPEGImageEncoder;  
  
public class ImageSizeTest {  
    /** 
     * @param args 
     * @throws AWTException 
     */  
    public void getImageSize() throws AWTException {  
        java.awt.Robot rb = new java.awt.Robot();  
        Dimension d = java.awt.Toolkit.getDefaultToolkit().getScreenSize();  
        Rectangle rt = new Rectangle(0, 0, (int) d.getWidth(), (int) d  
                .getHeight());  
        for (int i = 0; i < 1000; i++) {  
            BufferedImage image = rb.createScreenCapture(rt);  
            bufferedImageTobytes(image, "gif");  
            bufferedImageTobytes(image, "jpeg");  
            bufferedImageTobytes(image, 0.9f);  
            newCompressImage(image, 0.9f);  
  
        }  
    }  
  
    /** 
     * 用Format对应格式中ImageIO默认参数把IMAGE打包成BYTE[] 
     * @param image 
     * @return 
     */  
    private byte[] bufferedImageTobytes(BufferedImage image, String format) {  
        System.out.println(format + "格式开始打包" + getCurrentTime());  
        ByteArrayOutputStream out = new ByteArrayOutputStream();  
        try {  
            ImageIO.write(image, format, out);  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
        System.out.println(format + "格式完成打包-----" + getCurrentTime()  
                + "----lenth------" + out.toByteArray().length);  
        return out.toByteArray();  
    }  
  
    /** 
     *  
     * 自己设置压缩质量来把图片压缩成byte[] 
     *  
     * @param image 
     *            压缩源图片 
     * @param quality 
     *            压缩质量,在0-1之间, 
     * @return 返回的字节数组 
     */  
    private byte[] bufferedImageTobytes(BufferedImage image, float quality) {  
        System.out.println("jpeg" + quality + "质量开始打包" + getCurrentTime());  
        // 如果图片空,返回空  
        if (image == null) {  
            return null;  
        }     
        // 得到指定Format图片的writer  
        Iterator<ImageWriter> iter = ImageIO  
                .getImageWritersByFormatName("jpeg");// 得到迭代器  
        ImageWriter writer = (ImageWriter) iter.next(); // 得到writer  
  
        // 得到指定writer的输出参数设置(ImageWriteParam )  
        ImageWriteParam iwp = writer.getDefaultWriteParam();  
        iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); // 设置可否压缩  
        iwp.setCompressionQuality(quality); // 设置压缩质量参数  
  
        iwp.setProgressiveMode(ImageWriteParam.MODE_DISABLED);  
  
        ColorModel colorModel = ColorModel.getRGBdefault();  
        // 指定压缩时使用的色彩模式  
        iwp.setDestinationType(new javax.imageio.ImageTypeSpecifier(colorModel,  
                colorModel.createCompatibleSampleModel(16, 16)));  
  
        // 开始打包图片,写入byte[]  
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 取得内存输出流  
        IIOImage iIamge = new IIOImage(image, null, null);  
        try {  
            // 此处因为ImageWriter中用来接收write信息的output要求必须是ImageOutput  
            // 通过ImageIo中的静态方法,得到byteArrayOutputStream的ImageOutput  
            writer.setOutput(ImageIO  
                    .createImageOutputStream(byteArrayOutputStream));  
            writer.write(null, iIamge, iwp);  
        } catch (IOException e) {  
            System.out.println("write errro");  
            e.printStackTrace();  
        }  
        System.out.println("jpeg" + quality + "质量完成打包-----" + getCurrentTime()  
                + "----lenth----" + byteArrayOutputStream.toByteArray().length);  
        return byteArrayOutputStream.toByteArray();  
    }  
  
    /** 
     * 自己定义格式,得到当前系统时间 
     *  
     * @return 
     */  
    private String getCurrentTime() {  
        Calendar c = new GregorianCalendar();  
        int hour = c.get(Calendar.HOUR_OF_DAY);  
        int min = c.get(Calendar.MINUTE);  
        int second = c.get(Calendar.SECOND);  
        int millsecond = c.get(Calendar.MILLISECOND);  
        String time = hour + "点" + min + "分" + second + "秒" + millsecond;  
        return time;  
    }  
  
    /** 
     *  通过 com.sun.image.codec.jpeg.JPEGCodec提供的编码器来实现图像压缩 
     * @param image 
     * @param quality 
     * @return 
     */  
    private byte[] newCompressImage(BufferedImage image, float quality) {  
        // 如果图片空,返回空  
        if (image == null) {  
            return null;  
        }  
        // 开始开始,写入byte[]  
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 取得内存输出流  
        // 设置压缩参数  
        JPEGEncodeParam param = JPEGCodec.getDefaultJPEGEncodeParam(image);  
        param.setQuality(quality, false);  
        // 设置编码器  
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(  
                byteArrayOutputStream, param);  
        System.out.println("newCompressive" + quality + "质量开始打包"  
                + getCurrentTime());  
        try {  
            encoder.encode(image);  
        } catch (Exception ef){  
            ef.printStackTrace();  
        }  
        System.out.println("newCompressive" + quality + "质量打包完成"  
                + getCurrentTime() + "----lenth----"  
                + byteArrayOutputStream.toByteArray().length);  
        return byteArrayOutputStream.toByteArray();  
  
    }  
    public static void main(String args[]) throws Exception {  
        ImageSizeTest test = new ImageSizeTest();  
        test.getImageSize();  
    } 
  •   
  • }  
  •  

 测试结果依然不理想

 

突发奇想

GIF采用的是LZW编码进行压缩

JPEG后期的熵编码用的是Huffman,那如果先进行JPEG算法,再进行LZW算法,会有什么样的效果呢?

想干就干

咱写代码来测试一下

测试代码如下

package cn.mzd.newIM.test;  
  
import java.awt.AWTException;  
import java.awt.Dimension;  
import java.awt.Rectangle;  
import java.awt.image.BufferedImage;  
import java.awt.image.ColorModel;  
import java.io.ByteArrayInputStream;  
import java.io.ByteArrayOutputStream;  
import java.io.IOException;  
import java.util.Calendar;  
import java.util.GregorianCalendar;  
import java.util.Iterator;  
  
import javax.imageio.IIOImage;  
import javax.imageio.ImageIO;  
import javax.imageio.ImageWriteParam;  
import javax.imageio.ImageWriter;  
  
import sun.awt.image.JPEGImageDecoder;  
  
import com.sun.image.codec.jpeg.JPEGCodec;  
import com.sun.image.codec.jpeg.JPEGEncodeParam;  
import com.sun.image.codec.jpeg.JPEGImageEncoder;  
  
public class ImageSizeTest {  
    /** 
     * @param args 
     * @throws AWTException 
     */  
    public void getImageSize() throws AWTException {  
        java.awt.Robot rb = new java.awt.Robot();  
        Dimension d = java.awt.Toolkit.getDefaultToolkit().getScreenSize();  
        Rectangle rt = new Rectangle(0, 0, (int) d.getWidth(), (int) d  
                .getHeight());  
        for (int i = 0; i < 1000; i++) {  
            BufferedImage image = rb.createScreenCapture(rt);  
            // bufferedImageTobytes(image, "gif");  
            giftest(bufferedImageTobytes(image, "jpeg"));  
            giftest(bufferedImageTobytes(image, 0.2f));  
            giftest(newCompressImage(image, 0.2f));  
  
        }  
    }  
  
    /** 
     * 用Format对应格式中ImageIO默认参数把IMAGE打包成BYTE[] 
     *  
     * @param image 
     * @return 
     */  
    private byte[] bufferedImageTobytes(BufferedImage image, String format) {  
        System.out.println(format + "格式开始打包" + getCurrentTime());  
        ByteArrayOutputStream out = new ByteArrayOutputStream();  
        try {  
            ImageIO.write(image, format, out);  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
        System.out.println(format + "格式完成打包-----" + getCurrentTime()  
                + "----lenth------" + out.toByteArray().length);  
        return out.toByteArray();  
    }  
  
    /** 
     *  
     * 自己设置压缩质量来把图片压缩成byte[] 
     *  
     * @param image 
     *            压缩源图片 
     * @param quality 
     *            压缩质量,在0-1之间, 
     * @return 返回的字节数组 
     */  
    private byte[] bufferedImageTobytes(BufferedImage image, float quality) {  
        System.out.println("jpeg" + quality + "质量开始打包" + getCurrentTime());  
        // 如果图片空,返回空  
        if (image == null) {  
            return null;  
        }  
        // 得到指定Format图片的writer  
        Iterator<ImageWriter> iter = ImageIO  
                .getImageWritersByFormatName("jpeg");// 得到迭代器  
        ImageWriter writer = (ImageWriter) iter.next(); // 得到writer  
  
        // 得到指定writer的输出参数设置(ImageWriteParam )  
        ImageWriteParam iwp = writer.getDefaultWriteParam();  
        iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); // 设置可否压缩  
        iwp.setCompressionQuality(quality); // 设置压缩质量参数  
  
        iwp.setProgressiveMode(ImageWriteParam.MODE_DISABLED);  
  
        ColorModel colorModel = ColorModel.getRGBdefault();  
        // 指定压缩时使用的色彩模式  
        iwp.setDestinationType(new javax.imageio.ImageTypeSpecifier(colorModel,  
                colorModel.createCompatibleSampleModel(16, 16)));  
  
        // 开始打包图片,写入byte[]  
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 取得内存输出流  
        IIOImage iIamge = new IIOImage(image, null, null);  
        try {  
            // 此处因为ImageWriter中用来接收write信息的output要求必须是ImageOutput  
            // 通过ImageIo中的静态方法,得到byteArrayOutputStream的ImageOutput  
            writer.setOutput(ImageIO  
                    .createImageOutputStream(byteArrayOutputStream));  
            writer.write(null, iIamge, iwp);  
        } catch (IOException e) {  
            System.out.println("write errro");  
            e.printStackTrace();  
        }  
        System.out.println("jpeg" + quality + "质量完成打包-----" + getCurrentTime()  
                + "----lenth----" + byteArrayOutputStream.toByteArray().length);  
        return byteArrayOutputStream.toByteArray();  
    }  
  
    /** 
     * 自己定义格式,得到当前系统时间 
     *  
     * @return 
     */  
    private String getCurrentTime() {  
        Calendar c = new GregorianCalendar();  
        int hour = c.get(Calendar.HOUR_OF_DAY);  
        int min = c.get(Calendar.MINUTE);  
        int second = c.get(Calendar.SECOND);  
        int millsecond = c.get(Calendar.MILLISECOND);  
        String time = hour + "点" + min + "分" + second + "秒" + millsecond;  
        return time;  
    }  
  
    /** 
     * 通过 com.sun.image.codec.jpeg.JPEGCodec提供的编码器来实现图像压缩 
     *  
     * @param image 
     * @param quality 
     * @return 
     */  
    private byte[] newCompressImage(BufferedImage image, float quality) {  
        // 如果图片空,返回空  
        if (image == null) {  
            return null;  
        }  
        // 开始开始,写入byte[]  
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // 取得内存输出流  
        // 设置压缩参数  
        JPEGEncodeParam param = JPEGCodec.getDefaultJPEGEncodeParam(image);  
        param.setQuality(quality, false);  
        // 设置编码器  
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(  
                byteArrayOutputStream, param);  
        System.out.println("newCompressive" + quality + "质量开始打包"  
                + getCurrentTime());  
        try {  
            encoder.encode(image);  
        } catch (Exception ef) {  
            ef.printStackTrace();  
        }  
        System.out.println("newCompressive" + quality + "质量打包完成"  
                + getCurrentTime() + "----lenth----"  
                + byteArrayOutputStream.toByteArray().length);  
        return byteArrayOutputStream.toByteArray();  
  
    }  
  
    /** 
     * 测试把图片先压缩成JPEG,再用JPEG压缩成GIF 
     */  
    public byte[] giftest(byte[] imagedata) {  
        System.out.println("giftest开始打包" + getCurrentTime());  
        BufferedImage image = null;  
        ByteArrayInputStream input = new ByteArrayInputStream(imagedata);  
        // 得到解码器  
        JPEGImageDecoder decoder = (JPEGImageDecoder) JPEGCodec  
                .createJPEGDecoder(input);  
        // 把JPEG 数据流解压缩  
        try {  
            image = ((com.sun.image.codec.jpeg.JPEGImageDecoder) decoder)  
                    .decodeAsBufferedImage();  
        } catch (Exception ef) {  
            ef.printStackTrace();  
        }  
        ByteArrayOutputStream out = new ByteArrayOutputStream();  
        try {  
            ImageIO.write(image, "gif", out);  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
        System.out.println("giftest开始打包" + getCurrentTime() + "----lenth----"  
                + out.toByteArray().length);  
        return out.toByteArray();  
    }  
  
    public static void main(String args[]) throws Exception {  
        ImageSizeTest test = new ImageSizeTest();  
        test.getImageSize();  
    }  
  
}  

测试结果就补贴了

发现,对于默认的JPEG参数压缩,GIF能二次压缩到90K左右(类似之间GIF压缩)

而对于自己设定参数的压缩,当质量很高时(高于0.5),GIF效果还是有的

当质量很低时(低于0.1)再进行GIF压缩,大小反而变大-------------------分布均匀

 

 

此次试验再次宣告失败

 

难道我们的监控系统就不能用UDP来实现了吗?

 

虽然通过压缩图片直接打到保证图片质量和要求大小小于64KB的试验失败了,但是我们还有其他的办法

我们要始终相信灰太狼的那句“我还会再回来的”

 

具体怎么实现呢?

我想,思路如下----------把图片数据分包,用UDP广播分包后的消息,每个数据包内容里有属于第几个包的第几块

UDP的不可靠性,以及根据概率学知识,大家都知道分的包越多,越危险,于是,我们还是得先对图像进行压缩,再分包,再传送,尽量少分包

在接收方,用一个缓冲区来接收数据包。根本数据包内容中的标识来组装包,根据实时性要求,当接收消息超过3秒还未收到兄弟包,在丢弃。

别以为在这里我要用UDP来实现类型TCP的差错重传和确认机制,事情还没糟糕到需要做那一步。


http://java-mzd.iteye.com/blog/730504
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用Javajavax.imageioImageWriter来压缩图片质量。下面是一个简单的示例代码: ```java import javax.imageio.ImageIO; import javax.imageio.ImageWriter; import javax.imageio.plugins.jpeg.JPEGImageWriteParam; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Iterator; public class ImageCompressionExample { public static void compressImageQuality(File inputFile, File outputFile, float quality) throws IOException { BufferedImage image = ImageIO.read(inputFile); Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpeg"); if (!writers.hasNext()) { throw new IllegalStateException("No writers found"); } ImageWriter writer = writers.next(); ImageWriteParam param = writer.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(quality); writer.setOutput(ImageIO.createImageOutputStream(outputFile)); writer.write(null, new IIOImage(image, null, null), param); writer.dispose(); } public static void main(String[] args) { try { File inputFile = new File("input.jpg"); File outputFile = new File("output.jpg"); // 压缩图片质量为0.5(50%) compressImageQuality(inputFile, outputFile, 0.5f); System.out.println("Image compression complete"); } catch (IOException e) { e.printStackTrace(); } } } ``` 上述代码将读取名为"input.jpg"的原始图片文件,并将压缩后的图像保存为"output.jpg",压缩质量为0.5(50%)。你可以根据需要调整压缩质量的值来获得不同质量的图像。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值