Show image and generate palette via k-means

This commit is contained in:
heyarne 2022-02-19 18:57:30 +01:00
commit 3ab1571add
4 changed files with 3229 additions and 10 deletions

View file

@ -1,3 +1,6 @@
{:deps {clojure2d {:mvn/version "1.4.0-SNAPSHOT"} {:deps {clojure2d {:mvn/version "1.4.0-SNAPSHOT"}
enlive {:mvn/version "1.1.6"} enlive {:mvn/version "1.1.6"}
clj-http {:mvn/version "3.10.1"}}} clj-http {:mvn/version "3.10.1"}
generateme/fastmath {:mvn/version "2.0.3"}}
:aliases {:docs {:extra-deps {marginalia {:mvn/version "0.9.1"}}
:main-opts ["-m" "heyarne.vanilla-sky.marginalia"]}}}

3105
docs/uberdoc.html Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,40 @@
(ns heyarne.vanilla-sky.marginalia
(:gen-class)
(:require [marginalia.parser :as p]
[marginalia.core :refer [run-marginalia find-processable-file-paths]]
[marginalia.html :refer [*resources*]]
[clojure.string :as str]))
;; taken from https://gist.github.com/genmeblog/14a03bf7ee67f3435376e482e3981759
(defn- code-block
"Create code block from given string `s`"
[s]
(str "\n```clojure\n" s "\n```\n\n"))
(defn save-md
"Save markdown built from clojure source"
[filename]
(let [target (str (second (re-find #"(.*)\.(\w+)$" filename)) ".md")]
(spit target "")
(doseq [{:keys [docstring raw form type] :as all} (p/parse-file filename)]
(spit target
(condp = type
:code (str docstring (code-block raw))
:comment (if (str/starts-with? raw "=>")
(str "Result:" (code-block raw))
(str raw "\n\n")))
:append true))))
;; taken from https://github.com/gdeer81/marginalia/blob/master/src/marginalia/main.clj
(defn generate-html [sources]
(binding [*resources* ""]
(run-marginalia sources)
(shutdown-agents)))
(defn -main [& sources]
(generate-html
(if (seq sources)
sources
(find-processable-file-paths "./src" #(re-find #"\.clj[sc]?$" %))))
#_(save-md (first args)))

View file

@ -10,17 +10,20 @@
(require '[clj-http.client :as http]) (require '[clj-http.client :as http])
(require '[net.cgrand.enlive-html :as html]) (require '[net.cgrand.enlive-html :as html])
(require 'hashp.core)
(defn fetch-url [url] (defn fetch-url [url]
(future (html/html-snippet (:body (http/get url))))) (future (html/html-snippet (:body (http/get url)))))
;; here are some hand-picked webcams: ;; here are some hand-picked webcams:
;; colorado springs: https://www.insecam.org/en/view/481423/ ;; colorado springs, usa: https://www.insecam.org/en/view/481423/
;; salzburg: https://www.insecam.org/en/view/540433/ ;; salzburg, austria: https://www.insecam.org/en/view/540433/
;; not madrid: https://www.insecam.org/en/view/856408/ ;; not madrid, spain: https://www.insecam.org/en/view/856408/
;; florence, italy: https://www.insecam.org/en/view/866450/
;; portoferaio, italy: https://www.insecam.org/en/view/870342/
(def some-detail-page (def some-detail-page
@(fetch-url "https://www.insecam.org/en/view/856408/")) @(fetch-url "https://www.insecam.org/en/view/870342/"))
(def some-camera-image (def some-camera-image
(-> (->
@ -77,6 +80,7 @@
;; if you need a refresher what multipart messages look like: ;; if you need a refresher what multipart messages look like:
;; https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html ;; https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
;; We need this for mjpeg streams: https://en.wikipedia.org/wiki/Motion_JPEG#M-JPEG_over_HTTP
(defn parse-multipart-alternative [body] (defn parse-multipart-alternative [body]
(let [parsed (partition-with-seq (map int [\return \newline \return \newline]) body)] (let [parsed (partition-with-seq (map int [\return \newline \return \newline]) body)]
@ -127,10 +131,77 @@
(with-open [in (java.io.ByteArrayInputStream. bs)] (with-open [in (java.io.ByteArrayInputStream. bs)]
(javax.imageio.ImageIO/read in))) (javax.imageio.ImageIO/read in)))
(def img (byte-array->image (:body first-multipart-chunk))) (def img (byte-array->image webcam-picture))
(def canvas (c2d/canvas (c2d/width img) (c2d/height img))) (def aspect-ratio (/ (c2d/width img) (c2d/height img)))
(c2d/with-canvas [c canvas] (def width (min 640 (c2d/width img)))
(c2d/image c img)) (def height (int (/ width aspect-ratio)))
(c2d/show-window canvas "Hello World") (def canvas (c2d/canvas width height))
(defn place-image [canvas img]
(c2d/with-canvas [c canvas]
(->> (c2d/resize img width height)
(c2d/image c))))
(place-image canvas img)
(c2d/show-window canvas "Webcam Image")
;; now that we have the image, let's generate the palette.
;; how about we start with some pixel sorting?
(require '[clojure2d.pixels :as pix])
(require '[clojure2d.color :as col])
(require '[clojure2d.extra.utils :as util])
(def playground (c2d/canvas (c2d/width canvas) (c2d/height canvas)))
(place-image playground img)
#_(let [pixels (pix/to-pixels canvas)]
(pix/set-canvas-pixels! canvas (pix/filter-channels pix/dilate pixels)))
(pix/set-canvas-pixels!
playground
(let [filter (fn [p x y] (pix/get-color p x (+ y 75)))]
(binding [pix/*pixels-edge* :wrap]
(pix/filter-colors-xy filter (pix/to-pixels playground)))))
(defn hsv-colors [pixels]
(->>
(map #(-> (pix/get-color pixels %)
(col/to-HSV*)) (range (count pixels)))
(sort-by (juxt #(nth % 0) #(nth % 2) #(nth % 1)))))
(let [src (pix/to-pixels playground)
dst (pix/clone-pixels src)
sorted (->>
(hsv-colors src)
(sort-by (juxt #(nth % 1) #(nth % 0) #(nth % 2) ))
(map col/from-HSV*))]
(doseq [[idx col] (map-indexed vector sorted)]
(pix/set-color! dst idx col))
(pix/set-canvas-pixels! playground dst))
(c2d/show-window playground "Pixel Manipulation")
;; ok so that's how pixel access works in generl, quote straight forward.
;; how about we try some k-means clustering on the colors?
(require '[fastmath.core :as m])
(require '[fastmath.clustering :as cluster])
;; white and black are oftentimes used for informational overlays. they aren't
;; really part of the scenery, except for some very dark or maybe very
;; hazy conditions.
(def colors (->> (hsv-colors (pix/to-pixels playground))
(remove #{(col/color :white) (col/color :black)})))
(def palette
(->> (cluster/k-means colors 16)
(:representatives)
(map (comp col/from-HSV* col/color))
(sort-by (comp (juxt first last) col/to-HSB*))))
(util/show-palette palette)