在 Java 中通过套接字发送图像的有效方法 [英] Efficient way to send an image over socket in Java

查看:39
本文介绍了在 Java 中通过套接字发送图像的有效方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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. Ultimately I want the image (screen capture) to be send as fast as possible with the smallest possible file size.

现在,我已经设置了 imageIO;

Right now, I have imageIO set up as such;

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

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

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!

谢谢.

帕特里克:

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:

  • 尽量减少网络流量
  • 尽量减少 CPU 负载
  • 最大限度地提高图像质量

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

You can reduce the network traffic with a high image compression. But this will increase the CPU load and might reduce the image quality.

是否降低图像质量取决于压缩类型:对于JPG,您可以将图像任意缩小,但是图像质量将......好吧,任意糟糕.对于 PNG,图像质量将保持不变(因为它是无损压缩),但 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. 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.

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

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). But compressing the raw image data can be a feasible option, as an alternative to JPG or PNG.

哪种压缩技术(JPG、PNG 或 ZIP)合适还取决于图像内容:JPG 更适合自然"图像,例如照片或渲染图像.这些可以承受高压缩而不会造成伪影.对于人造图像(如线条图),它会很快导致不良的伪影,特别是在锐利边缘或图像包含文本时.与此相反:当图像包含单一颜色的大面积区域时,由于这些压缩方法的运行长度压缩"性质,像 PNG(或 ZIP)这样的压缩可以减小图像大小.

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. 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.

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

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.

顺便说一句:根据您实际想要传输的内容,您可以考虑使用与 Robot#createScreenCapture(Rectangle) 不同的技术获取图像数据.众所周知,这种方法非常缓慢.例如,当您想要传输 Swing 应用程序时,您可以让您的应用程序直接绘制到图像中.大致有一个像

BTW: Depending on what you actually want to transfer, you could consider obtaining the image data with a different technique than 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. 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)

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

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:

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

这篇关于在 Java 中通过套接字发送图像的有效方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆