簡體   English   中英

Clojure的平均亮度非常慢

[英]Average brightness with Clojure very slow

作為Clojure的新手,我想計算(很多)jpg圖像的平均亮度。 為此,我使用ImageIO/read從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代碼應該更快,我想理解為什么它不是。

你基本上是在一個非常緊湊的循環中運行大量的管道,例如拳擊,轉換,使用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)))))

這是未經測試的,因此您可能需要對其進行調整,但它顯示了一些內容:

  1. 使用類型提示數組訪問
  2. 使用非常有效的直接循環
  3. 對實際循環使用“整數”(長整數)數學,並僅在結尾處進行除法
  4. 使用unchecked-math在“緊密循環”中增加了很多性能

編輯

您也可以使用其他形式,它們可能表現得更好,例如具有內部可變狀態的dotimes (比如大小為1的長向量)如果你真的需要擠出性能,但到那時,你不妨寫一下java中的一個小方法;)

除了@ shlomi的回答:

你也可以使用areduce函數減少冗長(並且可能更快):

(defn get-sum-2 [^bytes data]
  (/ (areduce data i res 0 
              (unchecked-add res (bit-and (aget data i) 0xff)))
     (alength data)))

如果您想在Java中快速完成,那么您可以使用這些選項(最好是使用所有這些選項):

  1. 將libjpeg-turbo的java包裝器用作jpeg解壓縮庫 - 它比ImageIO快30倍...
  2. 不要計算圖像中所有像素的平均值,使用均勻分布在圖像上的1%到10%的像素(使用一些散列函數來選擇偽隨機像素 - 或者只是跳過一個for循環多於一個像素,取決於您想要拍攝的像素數量) - 以這種方式計算的平均值要快得多。 您使用的像素越多,獲得的結果就越准確 - 但如果您使用5%均勻分布的選定像素,那么獲得非常好的結果就足夠了。
  3. 多線程。
  4. 避免使用浮點計算,使用整數計算 - 浮點計算速度最慢可達3-4倍。 在可能的情況
  5. 不要將所有圖像加載到內存中,因為圖像通常會占用大量內存,因此垃圾收集器很難工作,而且應用程序運行速度很慢,因此需要更好地加載它們,然后讓它們進行GC編輯 - 逐步計算平均值

對於負字節值...不要將顏色值轉換為字節,將其直接轉換為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.

除了上述好的信息之外,您可能對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)))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM