成为Clojure的新手我想计算(很多)jpg图像的平均亮度。为此,我使用Java中的ImageIO/read
将图像加载到内存中,提取其后面的字节缓冲区并应用平均值。
(defn brightness
"Computes the average brightness of an image."
[^File file]
(-> file
ImageIO/read
.getRaster
.getDataBuffer
.getData
byteaverage))
这里,平均值
(defn byteaverage
[numbers]
(/ (float
(->> numbers
(map bytetoint)
(apply +)))
(count numbers))
)
需要考虑字节是用Java签名的,需要先将其转换为足够大的整数。
(defn bytetoint
[b]
(bit-and b 0xFF)
)
虽然这确实给出了正确的结果,但它非常慢。 20万像素图像需要大约10到20秒。磁盘访问不是问题。从time
开始,罪魁祸首似乎是bytetoint
转换。只是将此bytetoint
映射到字节数组就会占用8 GB的内存,并且不会在REPL中终止。
为什么会这样做呢?
PS:我知道可以使用其他编程语言,库,多线程或更改算法。我的观点是上面的Clojure代码应该要快得多,我想理解它为什么不是。
答案 0 :(得分:2)
你基本上是在一个非常紧凑的循环中运行大量的管道,例如拳击,转换,使用chuncked懒惰序列等。你从现代cpus中获得的许多好处飞出窗外;例如预加载缓存行,分支预测等。
这种循环(计算总和)在更直接的计算形式方面要好得多,例如clojure loop
构造,其形式为:
(defn get-sum [^bytes data]
(let [m (alength data)]
(loop [idx 0 sum 0]
(if (< idx m)
(recur (inc idx) (unchecked-add sum (bit-and (aget data idx) 0xff)))
(/ sum m)))))
这是未经测试的,因此您可能需要对其进行调整,但它显示了一些内容:
你可以使用其他形式,它们可能表现得更好,例如具有内部可变状态的dotimes
(比如大小为1的长向量),如果你真的需要挤出性能,但是那么,你不妨在java中写一点方法;)
答案 1 :(得分:1)
除了@ shlomi的回答:
你也可以使用areduce
函数减少冗长(并且可能更快):
(defn get-sum-2 [^bytes data]
(/ (areduce data i res 0
(unchecked-add res (bit-and (aget data i) 0xff)))
(alength data)))
答案 2 :(得分:0)
如果您想在Java中快速完成,那么您可以使用这些选项(最好是使用所有这些选项):
关于负字节值...... 不要将颜色值转换为字节,将其直接转换为int,如:
int rgb = somePixelColor;
int b = rgb & 0xFF;
int g = (rgb>>8) & 0xFF;
int r = (rgb>>16) & 0xFF;
int sillyBrightness = (r + g + b)/3; // because each color should have a weight for calculating brightness, there are some models of that.
答案 3 :(得分:0)
除了上述好的信息之外,您可能对HipHip库感兴趣,该库专为处理来自Clojure的原始值数组而设计:https://github.com/plumatic/hiphip
这是自述文件中关于计算平均值的示例。原始数组的标准偏差:
(defn std-dev [xs]
(let [mean (dbl/amean xs)
square-diff-sum (dbl/asum [x xs] (Math/pow (- x mean) 2))]
(/ square-diff-sum (dbl/alength xs))))
(defn covariance [xs ys]
(let [ys-mean (dbl/amean ys)
xs-mean (dbl/amean xs)
diff-sum (dbl/asum [x xs y ys] (* (- x xs-mean) (- y ys-mean)))]
(/ diff-sum (dec (dbl/alength xs)))))
(defn correlation [xs ys std-dev1 std-dev2]
(/ (covariance xs ys) (* std-dev1 std-dev2)))