简体   繁体   English

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

[英]Efficient way to send an image over socket in Java

I'm a bit of a Java noob, and I have read some basics about sockets and I can successfully send images over socket using ImageIO, but I want to reduce the amount of data that is sent. 我有点Java傻瓜,并且已经阅读了一些有关套接字的基础知识,并且可以使用ImageIO通过套接字成功发送图像,但是我想减少发送的数据量。 Ultimately I want the image (screen capture) to be send as fast as possible with the smallest possible file size. 最终,我希望以尽可能小的文件大小尽快发送图像(屏幕截图)。

Right now, I have imageIO set up as such; 现在,我已经设置了imageIO;

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

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

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

And the receiver: 和接收者:

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

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

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

In case you're wondering, this is my method that is used to take the image. 如果您想知道,这是我用来拍摄图像的方法。

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

I have heard about Byte arrays, where you can send the bytes then draw the image at the other end. 我听说过字节数组,您可以在其中发送字节,然后在另一端绘制图像。 However I'm not sure if this is more efficient. 但是我不确定这是否更有效。

Any help would be greatly appreciated, please ask if you would like me to add any extra info or code for the byte array! 任何帮助将不胜感激,请询问您是否要我为字节数组添加任何其他信息或代码!

Thanks. 谢谢。

EDIT: Patrick: 编辑:帕特里克:

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

There already has been an extensive discussion in the comments, but to summarize a few points that I find important: 评论中已经进行了广泛的讨论,但总结一下我认为很重要的几点:

You have a trade-off between several criteria: 您需要在几个条件之间进行权衡:

  • Minimize network traffic 最小化网络流量
  • Minimize CPU load 最小化CPU负载
  • Maximize image quality 最大化图像质量

You can reduce the network traffic with a high image compression. 您可以通过高图像压缩来减少网络流量。 But this will increase the CPU load and might reduce the image quality. 但这会增加CPU负载,并可能降低图像质量。

Whether it reduces the image quality depends on the compression type: For JPG, you can make the image arbitrarily small, but the quality of the image will then be ... well, arbitrarily bad. 是否降低图像质量取决于压缩类型:对于JPG,您可以使图像任意小,但是图像的质量会...好,任意差。 For PNG, the image quality will stay the same (since it is a lossless compression), but the CPU load and the resulting image size may be greater. 对于PNG,图像质量将保持不变(因为它是无损压缩),但是CPU负载和生成的图像大小可能会更大。

The option of ZIPping the image data was also mentioned. 还提到了将图像数据压缩的选项。 It is true that ZIPping the JPG or PNG data of an image will hardly reduce the amount of data (because the data already is compressed). 确实,压缩图像的JPG或PNG数据几乎不会减少数据量(因为数据已经压缩)。 But compressing the raw image data can be a feasible option, as an alternative to JPG or PNG. 但是,压缩原始图像数据可能是可行的选择,可以替代JPG或PNG。

Which compression technique (JPG, PNG or ZIP) is appropriate also depends on the image content : JPG is more suitable for "natural" images, like photos or rendered images. 哪种压缩技术(JPG,PNG或ZIP)合适,还取决于图像内容 :JPG更适合“自然”图像,例如照片或渲染图像。 These can withstand a high compression without causing artefacts. 它们可以承受较高的压缩力而不会造成伪影。 For artifical images (like line drawings), it will quickly cause undesirable artefacts, particularly at sharp edges or when the image contains texts. 对于人造图像(如线条图),它将迅速引起不良的伪像,尤其是在锐利边缘或图像包含文本时。 In contrast to that: When the image contains large areas with a single color, then a compression like PNG (or ZIP) can reduce the image size due to the "run length compression" nature of these compression methods. 与此相反:当图像包含单色的大区域时,由于这些压缩方法的“游程压缩”性质,像PNG(或ZIP)这样的压缩可以减小图像大小。

I already made some experiments for such an image transfer quite a while ago, and implemented it in a way that easily allowed tweaking and tuning these parameters and switching between the different methods, and comparing the speed for different application cases. 相当早以前,我已经为这种图像传输进行了一些实验,并以易于调整和调整这些参数以及在不同方法之间切换以及比较不同应用案例的速度的方式来实现它。 But from the tip of my head, I can not give a profound summary of the results. 但是从我的脑海中,我无法给出结果的深刻总结。

BTW: Depending on what you actually want to transfer, you could consider obtaining the image data with a different technique than Robot#createScreenCapture(Rectangle) . BTW:根据你真的想调一下 ,你可以考虑用不同的技术比获得的图像数据Robot#createScreenCapture(Rectangle) This method is well-known to be distressingly slow. 众所周知,这种方法非常缓慢。 For example, when you want to transfer a Swing application, you could let your application directly paint into an image. 例如,当您要传输Swing应用程序时,可以让您的应用程序直接绘制成图像。 Roughly with a pattern like 大致像

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

(This is only a sketch, to show the basic idea!) (这只是一个草图,以显示基本思想!)

Additionally, you could consider further options for increasing the "percieved speed" of such an image transfer. 此外,您可以考虑使用其他选项来提高这种图像传输的“实际速度”。 For example, you could divide your image into tiles , and transfer these tiles one after another. 例如,您可以将图像划分为图块 ,然后将这些图块一个接一个地转移。 The receiver will possibly appreciate it if the image would at least be partially visible as quickly as possible. 如果图像尽可能快地至少部分可见,则接收者可能会欣赏它。 This idea could be extended further. 这个想法可以进一步扩展。 For example, by detecting which tiles have really changed between two frames, and only transfer these changed tiles. 例如,通过检测哪些图块在两个帧之间确实发生了更改,而仅传输这些更改的图块。 (This approach could be extended and implemented in a rather sophisticated way, by detecting the "minimum regions" that have to be transferred) (可以通过检测必须转移的“最小区域”,以相当复杂的方式扩展和实施此方法)

However, for the case that you first want to play around with the most obvious tuning parameter: Here is a method that allows writing a JPG image with a quality value between 0.0 and 1.0 into an output stream: 但是,对于您首先要使用最明显的调整参数的情况:这是一种允许将质量值介于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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM