attempts to refactor / speed up image -> ascii conversion. improve image route
This commit is contained in:
parent
7c218ec19c
commit
8df5b8af6d
|
@ -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))))
|
|
|
@ -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)))
|
||||||
|
|
||||||
|
|
Reference in a new issue