在Java中通过套接字发送图像的有效方法

时间:2014-02-23 12:43:50

标签: java image sockets javax.imageio

我有点像Java菜鸟,我已经阅读了一些关于套接字的基础知识,我可以使用ImageIO成功地通过套接字发送图像,但我希望减少发送的数据量。最终,我希望以尽可能小的文件大小尽可能快地发送图像(屏幕截图)。

现在,我已经设置了imageIO;

DataInputStream in=new DataInputStream(client.getInputStream());

DataOutputStream out = new DataOutputStream(client.getOutputStream());

ImageIO.write(captureImg(),"JPG",client.getOutputStream());

接收者:

BufferedImage img=ImageIO.read(ImageIO.createImageInputStream(server.getInputStream()));

File outputfile = new File("Screen"+(date.toString())+".jpg");

ImageIO.write(img, "jpg", outputfile);

如果您想知道,这是我用来拍摄图像的方法。

Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());              
BufferedImage capture = new Robot().createScreenCapture(screenRect);

我听说过字节数组,您可以在其中发送字节,然后在另一端绘制图像。但是我不确定这是否更有效率。

非常感谢任何帮助,请询问您是否希望我为字节数组添加任何额外的信息或代码!

感谢。

编辑:帕特里克:

ByteArrayOutputStream bScrn = new ByteArrayOutputStream(); 
ImageIO.write(captureImg(), "JPG", bScrn); 
byte imgBytes[] = bScrn.toByteArray();


out.write((Integer.toString(imgBytes.length)).getBytes());
out.write(imgBytes,0,imgBytes.length);

1 个答案:

答案 0 :(得分:5)

评论中已经进行了广泛的讨论,但总结了一些我觉得重要的观点:

您需要在以下几个标准之间进行权衡:

  • 最大限度地减少网络流量
  • 最小化CPU负载
  • 最大化图像质量

您可以通过高图像压缩来减少网络流量。但这会增加CPU负载并可能降低图像质量。

是否降低图像质量取决于压缩类型:对于JPG,您可以使图像任意小,但图像的质量将是......好吧,任意不好。对于PNG,图像质量将保持不变(因为它是无损压缩),但CPU负载和生成的图像大小可能更大。

还提到了压缩图像数据的选项。确实,对图像的JPG或PNG数据进行压缩几乎不会减少数据量(因为已经 压缩的数据)。但压缩原始图像数据可能是一种可行的选择,可替代JPG或PNG。

适合哪种压缩技术(JPG,PNG或ZIP)还取决于图像内容:JPG更适合"自然"图像,如照片或渲染图像。它们可以承受高压缩而不会造成伪影。对于人工图像(如线条图),它会很快产生不良的伪像,特别是在锐边或图像包含文本时。与此形成对比:当图像包含单个颜色的大区域时,像PNG(或ZIP)这样的压缩可以减少图像尺寸,因为"游程长度压缩"这些压缩方法的本质。

我已经在很久以前对这种图像传输做了一些实验,并以一种容易调整和调整这些参数并在不同方法之间切换,并比较不同应用案例的速度的方式实现了它。但从我的头脑中,我无法对结果作出深刻的总结。

顺便说一句:根据您实际想要传输的 ,您可以考虑使用与Robot#createScreenCapture(Rectangle)不同的技术获取图像数据。众所周知,这种方法令人痛苦地缓慢。例如,当您想要传输Swing应用程序时,可以让应用程序直接绘制到图像中。粗略地使用类似

的模式
BufferedImage image = new BufferedImage(w,h,type);
Graphics g = image.getGraphics();
myMainFrame.paint(g);
g.dispose();

(这是草图,以显示基本想法!)

此外,您可以考虑进一步增加" percieved speed"这种图像传输。例如,您可以将图像划分为 tiles ,然后逐个传输这些图块。如果图像尽可能快地部分可见,则接收器可能会欣赏它。这个想法可以进一步扩展。例如,通过检测哪些图块在两个帧之间确实已经改变,并且仅传送这些改变的图块。 (这种方法可以通过检测必须转移的最小区域来以相当复杂的方式扩展和实施)

但是,对于您首先想要使用最明显的调整参数的情况:这是一种允许将质量值介于0.0和1.0之间的JPG图像写入输出流的方法:

public static void writeJPG(
    BufferedImage bufferedImage,
    OutputStream outputStream,
    float quality) throws IOException
{
    Iterator<ImageWriter> iterator =
        ImageIO.getImageWritersByFormatName("jpg");
    ImageWriter imageWriter = iterator.next();
    ImageWriteParam imageWriteParam = imageWriter.getDefaultWriteParam();
    imageWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    imageWriteParam.setCompressionQuality(quality);
    ImageOutputStream imageOutputStream =
        new MemoryCacheImageOutputStream(outputStream);
    imageWriter.setOutput(imageOutputStream);
    IIOImage iioimage = new IIOImage(bufferedImage, null, null);
    imageWriter.write(null, iioimage, imageWriteParam);
    imageOutputStream.flush();
}