Skip to content

Commit

Permalink
Add 'compare histograms' command
Browse files Browse the repository at this point in the history
Issue #32

This adds the ability to:

- Create a histogram given a literal string encoding of one.
- Create a histogram from a rectangle of pixels on the screen.
- A condition comparing whether two histograms are sufficiently
  similar.

This allows branching based on the contents on the screen compared
a known earlier state (which must be precomputed and encoded for now.)
  • Loading branch information
emiln committed Oct 18, 2015
1 parent 38e2ec1 commit 7f21067
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 6 deletions.
31 changes: 26 additions & 5 deletions src/cljck/color.clj
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
(ns cljck.color
(:import [java.awt.image BufferedImage Raster]))
(:require [cljck.io.sync :refer [robot]])
(:import [java.awt Rectangle]
[java.awt.image BufferedImage Raster]))

(defn image->pixels
"Converts a BufferedImage into a flat sequence of pixels. A pixel is really
Expand Down Expand Up @@ -36,9 +38,7 @@
The result would increment the buckets as follows:
[[0 0 0 0 0 0 0 1] [0 0 0 1 0 0 0 0] [1 0 0 0 0 0 0 0]]"
[[reds greens blues] [red green blue]]
[(update-buckets reds red)
(update-buckets greens green)
(update-buckets blues blue)])
(map update-buckets [reds greens blues] [red green blue]))

(defn normalize-bucket
"Takes a bucket count, divides it by the total number of pixels and multiplies
Expand All @@ -52,6 +52,27 @@
(Math/round)
int))

(defn hex->histogram
"Converts a string into a color histogram. The string is expected to conform
to the following format:
[rrrrrrrrrrrrrrrrggggggggggggggggbbbbbbbbbbbbbbbb]
where each section r, g, and b is divided into
[[hh] [hh] [hh] [hh] [hh] [hh] [hh] [hh]]
each h being a hexadecimal value. The output is a vector with 3 elements
(one for each color), each element is itself a vector with 8 elements
(dividing the color into 8 'buckets' from low to high RGB values). Each bucket
will have a value in the inclusive range [0 255]."
[hex-string]
(sequence
(comp (map (partial partition 2))
(map (partial map (partial apply str)))
(map (partial map #(Long/parseLong % 16))))
(partition 16 hex-string)))

(defn histogram
"Constructs a histogram of a given BufferedImage, which is a measure of where
in the color spectrum the pixels comprising the image fall. This is an image
Expand All @@ -75,4 +96,4 @@
(let [diff-red (reduce + (map diff red-a red-b))
diff-green (reduce + (map diff green-a green-b))
diff-blue (reduce + (map diff blue-a blue-b))]
(/ (+ diff-red diff-green diff-blue) 255 3))))
(/ (+ diff-red diff-green diff-blue) 255 3 2))))
17 changes: 16 additions & 1 deletion src/cljck/io.clj
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
(ns cljck.io
(:gen-class)
(:require
[cljck.utils :refer [multi-functions]]
[cljck
[color :refer [hex->histogram histogram histogram-diff]]
[utils :refer [multi-functions]]]
[cljck.io
[images :refer [fetch-screen]]
[keyboard :refer [press]]
[mouse :refer [click move-to mouse-pointer scroll-down scroll-up]]]
[clojure.core.async :refer [<! <!! chan go go-loop timeout]]
Expand Down Expand Up @@ -37,6 +40,18 @@
[_]
(click :left))

(defmethod process-event "histogram"
[[_ hex-code]]
(hex->histogram hex-code))

(defmethod process-event "histogram-at"
[[_ x y width height]]
(histogram (fetch-screen x y width height)))

(defmethod process-event "histogram-diff"
[[_ hist1 hist2 max-diff]]
(<= (histogram-diff (process-event hist1) (process-event hist2)) max-diff))

(defmethod process-event "if"
[[_ condition then else]]
(if (process-event condition)
Expand Down
17 changes: 17 additions & 0 deletions src/cljck/io/images.clj
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
(ns cljck.io.images
(:require [cljck.io.sync :refer [robot]])
(:import
[java.awt Rectangle Toolkit]
[java.awt.image BufferedImage]
[java.io File]
[javax.imageio ImageIO]))

(defn screen-resolution
"Returns the current width and height of the screen."
[]
((juxt (memfn getWidth) (memfn getHeight))
(.. Toolkit getDefaultToolkit getScreenSize)))

(defn buffered-image
"Takes a path to an image file, which is assumed available on the class path,
and constructs and returns a BufferedImage from it."
[path]
(ImageIO/read (File. ^String path)))

(defn fetch-screen
"Returns a BufferedImage of the pixels currently on the screen. Optionally
takes a rectangle to limit the fetching to."
([]
(apply fetch-screen 0 0 (screen-resolution)))
([x y width height]
(.createScreenCapture robot (Rectangle. x y width height))))

0 comments on commit 7f21067

Please sign in to comment.