我正在处理需要非常高级的图像操作的 Java 项目。事实上,我正在使用 OpenCV 进行大部分操作,并且我正在使用 JNI 来封装我需要的 OpenCV 函数。我对 OpenCV 提供的性能非常满意,编写 OpenCV 代码的人值得为代码付出巨大的努力。与我对 Java 开发人员编写的代码的体验形成鲜明对比。
我开始对我的编程语言的选择持乐观态度,我的项目的第一个工作迭代运行良好,但它的性能远不及实时(每 2 秒获得大约 1 帧。)我已经做了一些优化我的代码及其帮助很大。我已经能够将帧速率提高到大约每秒 10-20 帧,这很好,但我发现要进行任何进一步的优化,我必须重写 Java 代码来做同样的事情,但 10 -20 倍效率更高。
我对 Java 开发人员很少关注性能感到震惊,尤其是在编写媒体相关类的类时。我已经下载了 OpenJDK,并且正在探索我正在使用的功能。例如Raster类下有一个函数叫getPixels(...),它获取图像的像素点。我原以为这个函数在源代码中是一个高度优化的函数,多次调用System.arrayCopy 进一步优化性能。相反,我发现的是非常“优雅”的代码,它们调用 5-6 个不同的类和 10-20 个不同的方法只是为了完成我可以在一行中完成的事情:
for (int i =0; i < n; i++) {
long p = rawFrame[i];
p = (p << 32) >>> 32;
byte red = (byte) ((p >> 16) & 0xff);
byte green = (byte) ((p >> 8) & 0xff);
byte blue = (byte) ((p) & 0xff);
byte val = (byte)(0.212671f * red + 0.715160f * green + 0.072169f * blue);
data[i] = val;
grayFrameData[i] = (val & 0x80) + (val & (0x7f));
}
上面的代码将图像转换为灰度并获取浮点像素数据,大约需要 1-10 毫秒。如果我想用 Java 内置函数做同样的事情,转换为灰度本身需要 200-300 毫秒,然后抓取 float 像素大约需要 50-100 毫秒。这对于实时性能来说是 Not Acceptable 。请注意,为了加快速度,我大量使用按位运算符,而 Java 开发人员却回避它。
我知道他们需要处理一般情况,但即便如此,他们不能至少提供优化选项或至少警告此代码执行速度有多慢。
我的问题是,在开发的后期(我已经有了第一次迭代,而不是我正在处理实时执行更多的第二次迭代)我是否应该硬着头皮切换到 C/C++我可以做更多的微调,或者我应该坚持使用 Java,并希望事情会变得更加实时友好,这样我就不必重写已经实现的 Java 代码来获得加速。
我真的开始厌恶 Java 的“优雅”和缓慢。类(class)的数量似乎有点过头了。
最佳答案
我用 Java 完成了计算机视觉工作,我认为它非常适合计算机视觉和实时的东西,你只需要知道如何使用它。
潜在的优化:
如果您需要帮助优化您的代码,我很乐意提供帮助——例如,我可以告诉您,通过创建一个方法您可能会获得性能提升
`public static final int getGrayScale(final int pixelRGB){
return (0.212671f * ((pixelRGB >> 16) & 0xff) + 0.715160f * ((pixelRGB >> 8) & 0xff) + 0.072169f * ((pixelRGB) & 0xff));
}`
并在您的 for{pixels} 循环中使用它。通过使用方法调用,JVM 可以更多地优化此操作,并且可能也可以更多地优化 for 循环。
如果您有要刻录的 RAM,您可以为所有可能的 24 位像素像素颜色创建一个静态的、最终的输出灰度字节查找表。这将是大约 16 MB 的 RAM,但是您不必进行任何浮点运算,只需访问一个数组即可。这可能更快,具体取决于您使用的 JVM,以及它是否可以优化数组边界检查。
寻找类似、更快的图像处理代码的地方:
我强烈建议您查看 ImageJ 图像处理应用程序及其库的代码,特别是 ij.process.TypeConverter。就像您的代码一样,它在很大程度上依赖于使用位旋转的直接数组操作 和最少的额外数组创建。 Java2D 库(标准 JRE 的一部分)和 Java Advanced Imaging (JAI) 库提供了其他方法来快速直接对图像数据进行图像处理,而无需每次都自己进行操作。对于 Java2D,您只需要小心使用哪些函数。
为什么 Java2D 库如此间接:
大部分“分类”是由于支持多种颜色模型和存储格式(即 HSB 图像、基于 float 的颜色模型、索引颜色模型)。间接存在是有原因的,有时实际上可以提高性能——BufferedImage 类(例如)直接挂接到最近的 VM 中的图形内存,以使某些操作更快。间接允许它在很多时候向用户掩盖这一点。
关于Java实时性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/895725/