与其他语言相比,Java BuffereImage到字节数组的转换太慢

与其他语言相比,Java BuffereImage到字节数组的转换太慢,java,bufferedimage,javax.imageio,Java,Bufferedimage,Javax.imageio,我正在尝试将图像转换为字节数组,以便通过网络传输图像以进行进一步处理 现在在C#中,下面的代码大约在3或2毫秒内完成这项工作 Image image = Image.FromFile("D:/tst.jpg"); DateTime pre = DateTime.Now; int sz; using (MemoryStream sourceImageStream = new MemoryStream()) { image.Save(sourceImageStream,

我正在尝试将图像转换为字节数组,以便通过网络传输图像以进行进一步处理

现在在C#中,下面的代码大约在3或2毫秒内完成这项工作

Image image = Image.FromFile("D:/tst.jpg");
DateTime pre = DateTime.Now;
int sz;
using (MemoryStream sourceImageStream = new MemoryStream())
{
     image.Save(sourceImageStream, System.Drawing.Imaging.ImageFormat.Jpeg);
     byte[] sourceImageData = sourceImageStream.ToArray();
     sz = sourceImageData.Count();
}
MessageBox.Show("Size " + sz + " time : " + (DateTime.Now - pre).TotalMilliseconds);

尺寸268152时间:3.0118 但在Java中,执行以下操作需要花费太多的时间

BuffredImage image = ImageIO.read(new File("D:/tst.jpg"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Instant pre = Instant.now();
ImageIO.write( image, "jpeg", baos );
baos.flush();
Instant now = Instant.now();
System.out.println("Size " + baos.size() + " time : " + ChronoUnit.MILLIS.between(pre, now));

尺寸268167时间:91.0 注意源图像是
jpg
图像。在C#中使用
png
压缩时。时间大约是90毫秒。所以我猜
java
需要时间来压缩相同的
jpg
图像。图像尺寸为2048*1536

Java在这里的速度令人沮丧。如何在
java
中解决这个问题

提前谢谢

编辑: 考虑到这一点

C#: 尺寸1987059时间:11.0129

爪哇: 尺寸845093时间:155.0


源图像是
1987059
Bytes
(与
C#
编码字节数组相同)。但是在
java
中,它被压缩为
845093
字节。我曾尝试将压缩质量设置为1f-like,但这无助于缩短时间。

第一条评论指出了此类测试的主要问题:这是一个微观基准测试。如果在Java中只运行该代码一次,那么您将主要度量初始化运行时、类加载和初始化所花费的时间

这是一个稍微修改过的代码版本(我最初写这篇文章是为了回答您的后续问题,现在作为一个副本结束,但同样的概念适用),其中至少包括一个预热时间。你会发现测量结果有很大的差异。在我的2014 MacBook Pro上,输出为:

Initial load time 415 ms (5)
Average warm up load time 73 ms (5)
Normal load time 65 ms (5)
如您所见,加载图像的“正常”时间比初始时间要短得多,这包括大量开销

代码:

公共类测试速度{
公共静态void main(字符串[]args)引发IOException{
文件输入=新文件(args[0]);
测试(输入,1,“首字母”);
测试(输入,100,“平均预热”);
测试(输入,1,“正常”);
}
私有静态无效测试(文件输入、int运行、最终字符串类型)引发IOException{
BuffereImage图像=空;
长启动=System.currentTimeMillis();
for(int i=0;i
(我还编写了另一个版本,采用了第二个参数,并在“正常”情况下加载了不同的文件,但测量结果相似,因此我将其忽略)

很可能这个基准仍然存在问题,比如测量I/O时间,而不是解码时间,但至少它更公平一些



附:一些背景资料。如果您至少使用Oracle JRE,那么用于
ImageIO
的捆绑JPEG插件使用JNI,以及IJG的libjpeg的本机编译版本(用C编写)。这用于读取和写入JPEG。如果使用libjpegTurbo的本机绑定,您可能会看到更好的性能。但是,由于这些都是本机代码,因此性能不太可能因平台而异。

您知道微基准测试的效果是什么吗?当然,请参见Java压缩图像。您的
图像
不是JPEG,而是
渲染图像
。当你以JPEG格式写出来时,你有一组字节代表一个JPEG图像。我怀疑代码一点也不一样,你在把苹果和桔子做比较。您还假设Java和C#的默认设置(JPEG编码)是相同的。您可能希望通过tearrayoutPutStream
初始化
,使用比默认设置更多的空间(
32
),因此不需要不断调整大小。您也可以删除
flush()
。您可以尝试将ImageIO.setUseCache设置为false以防止磁盘访问。我不确定ByteArrayOutputStream是否会尝试使用该磁盘(或仅使用内存)。请参阅。如果您只想通过网络传输图像文件,只需传输原始压缩图像的字节,就像传输任何其他二进制文件一样。不要涉及图像解码/编码。或者,在传输之前是否需要修改图像(像素)数据?在这种情况下,您的基准测试也应该这样做,以确保您正在对苹果进行比较。非常感谢@haraldK。最近几天,我在寻找更好的解决方案,我发现了同样的问题。我需要一些受过教育的基准。现在,我正试图调整我的库以直接获取字节[],并将它们传递给服务器。
public class TestJPEGSpeed {
    public static void main(String[] args) throws IOException {
        File input = new File(args[0]);

        test(input, 1, "Initial");
        test(input, 100, "Average warm up");
        test(input, 1, "Normal");
    }

    private static void test(File input, int runs, final String type) throws IOException {
        BufferedImage image = null;

        long start = System.currentTimeMillis();
        for (int i = 0; i < runs; i++) {
            image = ImageIO.read(input);
        }
        long stop = System.currentTimeMillis();

        System.out.println(type + " load time " + ((stop - start) / runs) + " ms (type=" + image.getType() + ")");
    }
}