attempts to refactor / speed up image -> ascii conversion. improve image route

This commit is contained in:
Gered 2014-03-30 09:46:58 -04:00
parent 7c218ec19c
commit 8df5b8af6d
2 changed files with 81 additions and 42 deletions

View file

@ -11,17 +11,25 @@
(:use hiccup.core)) (:use hiccup.core))
(def ascii-chars [\# \A \@ \% \$ \+ \= \* \: \, \. \space]) (def ascii-chars [\# \A \@ \% \$ \+ \= \* \: \, \. \space])
(def num-ascii-chars (count ascii-chars))
(defmacro get-properties [obj & properties] (defn get-image-by-url
(let [target (gensym)] (^BufferedImage [^String url]
`(let [~target ~obj] (try
(vector ~@(for [property properties] (let [java-url (query-param-url->java-url url)]
`(~property ~target)))))) (ImageIO/read java-url))
(catch Exception ex))))
(defn get-image-by-file
(^BufferedImage [^File file]
(try
(ImageIO/read file)
(catch Exception ex))))
(defn scale-image (defn scale-image
"takes a source image specified by the uri (a filename or a URL) and scales it proportionally "takes a source image specified by the uri (a filename or a URL) and scales it proportionally
using the new width, returning the newly scaled image." using the new width, returning the newly scaled image."
[url new-width] (^BufferedImage [url new-width]
(let [^Image image (ImageIO/read url) (let [^Image image (ImageIO/read url)
new-height (* (/ new-width (.getWidth image)) new-height (* (/ new-width (.getWidth image))
(.getHeight image)) (.getHeight image))
@ -31,32 +39,54 @@
RenderingHints/VALUE_INTERPOLATION_BILINEAR) RenderingHints/VALUE_INTERPOLATION_BILINEAR)
(.drawImage image 0 0 new-width new-height nil) (.drawImage image 0 0 new-width new-height nil)
(.dispose))] (.dispose))]
scaled-image)) scaled-image)))
(defn ascii [^BufferedImage img x y color?] (defn- get-css-color-attr [r g b]
(let [[red green blue] (get-properties (Color. (.getRGB img x y)) (format "color: #%02x%02x%02x;" r g b))
.getRed .getGreen .getBlue)
peak (apply max [red green blue]) (defn- get-pixel [^BufferedImage image x y]
idx (if (zero? peak) (let [argb (.getRGB image x y)]
(dec (count ascii-chars)) [(bit-shift-right (bit-and 0xff000000 argb) 24)
(dec (int (+ 1/2 (* (count ascii-chars) (/ peak 255)))))) (bit-shift-right (bit-and 0x00ff0000 argb) 16)
output (nth ascii-chars (if (pos? idx) idx 0)) ] (bit-shift-right (bit-and 0x0000ff00 argb) 8)
(bit-and 0x000000ff argb)]))
(defn get-ascii-pixel
""
[^BufferedImage image x y color?]
(let [[a r g b] (get-pixel image x y)
peak (apply max [r g b])
char-index (if (zero? peak)
(dec num-ascii-chars)
(dec (int (+ 0.5 (* num-ascii-chars (/ peak 255))))))
pixel-char (nth ascii-chars (if (pos? char-index) char-index 0))]
(if color? (if color?
(html [:span {:style (format "color: rgb(%s,%s,%s);" red green blue)} output]) [:span {:style (get-css-color-attr r g b)} pixel-char]
pixel-char)))
(defn- pixels->ascii [^BufferedImage image color?]
(let [width (.getWidth image)
ascii-image (for [y (range (.getHeight image))
x (range (.getWidth image))]
(get-ascii-pixel image x y color?))
output (->> ascii-image
(partition width)
(map #(conj % (if color? [:br] \newline)))
(apply concat))]
(if color?
(html
[:pre
{:style "font-size:5pt; letter-spacing:1px; line-height:4pt; font-weight:bold;"}
output])
output))) output)))
(defn convert-image [url w color?] (defn convert-image
(let [java-url (query-param-url->java-url url) ([^BufferedImage image color?]
^Image raw-image (scale-image java-url w) (convert-image image nil color?))
ascii-image (->> (for [y (range (.getHeight raw-image)) ([^BufferedImage image scale-width color?]
x (range (.getWidth raw-image))] (let [current-width (.getWidth image)
(ascii raw-image x y color?)) new-width (or scale-width current-width)
(partition w)) final-image (if-not (= new-width current-width)
output (->> ascii-image (scale-image image new-width)
(interpose (if color? "<BR/>" \newline)) image)]
flatten)] (pixels->ascii final-image color?))))
(if color?
(html [:pre {:style "font-size:5pt; letter-spacing:1px;
line-height:4pt; font-weight:bold;"}
output])
(println output))))

View file

@ -1,9 +1,10 @@
(ns toascii.routes.api.image (ns toascii.routes.api.image
(:import (java.awt.image BufferedImage))
(:require [clojure.string :as str] (:require [clojure.string :as str]
[liberator.core :refer [defresource]] [liberator.core :refer [defresource]]
[compojure.core :refer [ANY]] [compojure.core :refer [ANY]]
[toascii.route-utils :refer [register-routes]] [toascii.route-utils :refer [register-routes]]
[toascii.models.image :refer [convert-image]])) [toascii.models.image :refer [convert-image get-image-by-url]]))
(defresource render-image [{:keys [url width color format] :as params}] (defresource render-image [{:keys [url width color format] :as params}]
:media-type-available? :media-type-available?
@ -17,13 +18,21 @@
(fn [_] (fn [_]
(cond (cond
(str/blank? url) {:error "Missing image url"})) (str/blank? url) {:error "Missing image url"}))
:exists?
(fn [_]
(if-let [image (get-image-by-url url)]
{:image image}
[false {:error "Image could not be loaded."}]))
:handle-ok :handle-ok
(fn [ctx] (fn [ctx]
(let [rendered (convert-image url 64 true)] (let [rendered (convert-image (:image ctx) true)]
(if (= "text/html" (get-in ctx [:representation :media-type])) (if (= "text/html" (get-in ctx [:representation :media-type]))
rendered rendered
rendered))) rendered)))
:handle-malformed :handle-malformed
(fn [ctx]
(:error ctx))
:handle-not-found
(fn [ctx] (fn [ctx]
(:error ctx))) (:error ctx)))