【问题标题】:Efficient way to send an image over socket in Java在 Java 中通过套接字发送图像的有效方法
【发布时间】:2014-02-23 12:43:49
【问题描述】:

我是一个 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);

【问题讨论】:

  • 嗯.. 目前每个人的尺寸有多大?例如,您对质量有什么要求?使用 jpg 格式已经可以压缩图像大小,但是您是否考虑过通过压缩流来压缩数据?
  • 顺便说一句;您的标题不是对问题的解释,而是为了减小图像的大小而不是“发送有效的东西”..
  • 现在图像以大约 150kb 的大小进入服务器。正如标题所暗示的那样,我问的原因是,有没有比 imageIO 更好的方法来发送图像?因为我见过类似的 SO 问题,其中有人专门要求不要使用 imageIO,也就是说,不发送图像本身,而是发送数据,然后服务器端程序绘制图像并存储它。就质量而言,它们不必那么好,我没有具体数字。我已经研究过压缩图像本身,但我想我会询问字节数组等。
  • 嗯,压缩 JPEG 不会给你带来太多好处,而且会浪费双方的 CPU 时间,我建议不要这样做......
  • 谢谢,fge。我会记住这一点。另外,感谢帕特里克的回复!

标签: java image sockets javax.imageio


【解决方案1】:

在 cmets 中已经进行了广泛的讨论,但总结一下我认为重要的几点:

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

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

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

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

还提到了压缩图像数据的选项。确实,压缩图像的 JPG 或 PNG 数据几乎不会减少数据量(因为数据已经 压缩)。但是压缩 raw 图像数据可能是一个可行的选择,作为 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();

(这只是一个草图,展示基本思想!)

此外,您可以考虑更多选项来提高此类图像传输的“感知速度”。例如,您可以将您的图像分成tiles,然后一个接一个地传输这些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();
}

【讨论】:

  • Marco13,非常感谢您的深入回复,非常感谢。我肯定会调查您提供的关于质量的最后一段代码。本质上,该程序不会发送 JFrame 本身的图像,而是发送用户屏幕的图像——无论他们正在看什么。每 4 分钟左右向服务器发送一张图像。
猜你喜欢
  • 2021-06-09
  • 2014-09-25
  • 2013-11-04
  • 2013-12-31
  • 1970-01-01
  • 1970-01-01
  • 2013-03-24
相关资源
最近更新 更多