ryoakg
9/21/2016 - 5:17 AM

jpeg-compression-quality.clj

(require '[clojure.java.io :as io])

(defn- write-jpeg-with-compression-quality
  [^java.awt.image.BufferedImage image ^java.io.File output-file ^double quality]
  {:pre [(<= 0.0 quality 1.0)]}
  (with-open [ios (javax.imageio.ImageIO/createImageOutputStream output-file)]
    (let [w (.next (javax.imageio.ImageIO/getImageWritersByFormatName "jpg"))]
      (.setOutput w ios)
      (.write w
              nil
              (javax.imageio.IIOImage. image nil nil)
              ;; javax.imageio.ImageIO/write でも同じ様にフォーマットを指定して書き込めるけど
              ;; jpeg とかフォーマット毎のパラメタの指定はできない
              (doto (javax.imageio.plugins.jpeg.JPEGImageWriteParam. (java.util.Locale/getDefault))
                (.setCompressionMode javax.imageio.ImageWriteParam/MODE_EXPLICIT)
                (.setCompressionQuality quality)))
      (.flush ios)
      (.dispose w))))

(-> (java.net.URL. "https://upload.wikimedia.org/wikipedia/commons/d/de/Shanghai_montage.png")
    javax.imageio.ImageIO/read
    (write-jpeg-with-compression-quality (io/file "/tmp/out.jpg") 0.8))


(defn- jpeg-byte-array-output-stream-with-compression-quality
  [^java.awt.image.BufferedImage image ^double quality]
  {:pre [(<= 0.0 quality 1.0)]}
  (let [bos (java.io.ByteArrayOutputStream.)]
    (with-open [ios (javax.imageio.ImageIO/createImageOutputStream bos)]
      (let [w (.next (javax.imageio.ImageIO/getImageWritersByFormatName "jpg"))]
        (.setOutput w ios)
        (.write w
                nil
                (javax.imageio.IIOImage. image nil nil)
                (doto (javax.imageio.plugins.jpeg.JPEGImageWriteParam. (java.util.Locale/getDefault))
                  (.setCompressionMode javax.imageio.ImageWriteParam/MODE_EXPLICIT)
                  (.setCompressionQuality quality)))
        (.flush ios)
        (.dispose w)))
    bos))

;;; ファイルサイズの上限を決めて画質を落す場合
(def upper-limit 500000)

;;; 画質がどこまで落るか見てみる
;;; これだと、何度も画像をダウンロードするので良くないけど、サンプルなので簡単に書いておく
;;; ディスクにあるファイルなら気にしなくていい
(->> (iterate #(- % 0.1) 0.9)
     (take 5)
     (keep #(let [bos (-> (java.net.URL. "https://upload.wikimedia.org/wikipedia/commons/d/de/Shanghai_montage.png")
                          javax.imageio.ImageIO/read
                          (jpeg-byte-array-output-stream-with-compression-quality %))]
              (when (< (.size bos) upper-limit)
                [% (.size bos)])))
     first)
;; => [0.7000000000000001 444917]

;;; 書き込んでみる
(when-let [bos (->> (iterate #(- % 0.1) 0.9)
                    (take 5)
                    (keep #(let [bos (-> (java.net.URL. "https://upload.wikimedia.org/wikipedia/commons/d/de/Shanghai_montage.png")
                                         javax.imageio.ImageIO/read
                                         (jpeg-byte-array-output-stream-with-compression-quality %))]
                             (when (< (.size bos) upper-limit)
                               bos)))
                    first)]
  (with-open [os (io/output-stream (io/file "/tmp/out.jpg"))]
    (.writeTo bos os)))