Show image and generate palette via k-means
This commit is contained in:
parent
e5e97e14e0
commit
3ab1571add
4 changed files with 3229 additions and 10 deletions
5
deps.edn
5
deps.edn
|
|
@ -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
3105
docs/uberdoc.html
Normal file
File diff suppressed because one or more lines are too long
40
src/heyarne/vanilla_sky/marginalia.clj
Normal file
40
src/heyarne/vanilla_sky/marginalia.clj
Normal 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)))
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue