I just started messing around with Java's BufferedImage
. I had an idea to try to encode a message into a picture by coloring each pixel a certain color that corresponds to a certain character.
I needed a way of enumerating colors to match colors to characters.
To do this, I decided to treat the RGB color as a 3-digit, base-255 number, then just incrementing each channel as though I was counting (increment the rightmost channel until it gets to 255, then make it 0 and increment the channel to the left). I used this for a previous project, and although it's slow, it was acceptable.
(defn inc-permutation
"\"Counts\" arbitrary symbols.
Example: (inc-permutation \\a \\c #(char (inc (int %))) [\\a \\b \\b])
returns [\\a \\b \\c], then [\\a \\c \\a], [\\a \\c \\b], [\\a \\c \\c], [\\b \\a \\a], [\\b \\a \\lein pomb]...
Quite slow."
[first-symbol last-symbol inc-f current-permutation]
(let [current-ones (last current-permutation)
carry? (= current-ones last-symbol)
overflown? (empty? current-permutation)
last-i (dec (count current-permutation))]
(cond
carry? (conj
(inc-permutation first-symbol last-symbol inc-f (subvec current-permutation 0 last-i))
first-symbol)
overflown? []
:else (assoc current-permutation last-i (inc-f current-ones)))))
It works, but it seems like a hack more than anything.
I calculated that to cover the entire range of colors with a char, I would need to increment each color 174540 times * (- (int (character) 32)
. At 88ms per increment, it will take a little over 8 seconds to increment a \~
to [253 3 19]
, which for a message of any length is unacceptable.
Below is the full code. The last method, color-of-char
is where it's all tied together.
(ns bits.image.color-encode.conversion-helper)
(def min-char-code 32)
(def max-char-code "inclusive" 127)
(def total-colors (int (Math/pow 255 3)))
(def color-code-mult (int (inc (/ total-colors (- max-char-code min-char-code)))))
(def starting-color [0 0 0])
(defn inc-permutation ...)
(defn advance-color [color color-step]
(let [f #(g/inc-permutation 0 255 inc %)]
(if (= color-step 1)
(f color)
(reduce (fn [c _] ; Faster than `iterate`
(f c))
color
(range color-step)))))
(defn color-of-char [chr]
(let [code (- (int chr) min-char-code)]
(advance-color [0 0 0]
(* code color-code-mult))))
(color-of-char \a)
-> [173 29 13]
, but it takes an average of 5.879696 seconds to get there (according to Criterium).
Is there anything I can do to speed up the current way? Any general comments would be welcome too.
Is there a better way to "increment a RGB color"? Alternatively, is there a way to get some nth color, that isn't
O(n)
?