Skip to content

Instantly share code, notes, and snippets.

@xaviershay
Created August 7, 2011 06:34
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xaviershay/1130134 to your computer and use it in GitHub Desktop.
Save xaviershay/1130134 to your computer and use it in GitHub Desktop.
OCR with Clojure. See the blog post at rhnh.net.
(ns ocr.main
(:use [clojure.string :only (split trim)])
(:use [clojure.contrib.duck-streams :only (write-lines read-lines)])
(:use [clojure.contrib.shell-out :only (sh)])
(:use [clojure.contrib.math :only (sqrt)]))
; Input handling
(defn read-text-image-line [line]
(if (= "white" (last (split line #"[,:\s]+"))) "0" "1"))
(defn load-text-image
"Loads a black and white image stored in imagemagick's text format
into a bitmap with '0' representing white and '1' black."
[filename]
(let [lines (vec (drop 1 (read-lines filename)))
converted (map read-text-image-line lines)
]
(map #(apply str %) (partition 32 converted))))
(defn convert-image
"Convert any image into the format required by the classifier."
[in out]
(sh "convert" in "-colorspace" "gray" "+dither" "-colors" "2"
"-normalize" "-resize" "32x32!" out)
(write-lines out (load-text-image out)))
(def temp-outfile "/tmp/clj-converted.txt")
; Training Data
(defn parse-char-row [row]
(map #(Integer/parseInt %) (filter #(or (= % "1") (= % "0")) (split row #""))))
(defn parse-char-data [element]
(let [label (trim (last element))
rows (take 32 element)]
[label (vec (flatten (map parse-char-row rows)))]))
(defn load-training-data
"Loads training data from the weird format used by
http://archive.ics.uci.edu/ml/datasets/Optical+Recognition+of+Handwritten+Digits"
[filename]
(let [lines (drop 21 (read-lines filename))
elements (partition 33 lines)]
(map parse-char-data elements)
))
(def training-set (load-training-data "training-set.tra"))
; Classification
(defn load-char-file [file]
(let [filename (.getName file)
tokens (split filename #"[_\.]")
label (first tokens)
contents (parse-char-row (slurp file))]
[label contents]))
(defn minus-vector [& args]
(map #(apply - %) (apply map vector args)))
(defn sum-of-squares [coll]
(reduce (fn [a v] (+ a (* v v))) coll))
(defn calculate-distances [in]
(fn [row]
(let [vector-diff (minus-vector (last in) (last row))
label (first row)
distance (sqrt (sum-of-squares vector-diff))]
[label distance])))
(defn classify
"Classify the given vector using a kNN algorithm."
[in]
(let [k 10
diffs (map (calculate-distances in) training-set)
nearest-neighbours (frequencies (map first (take k (sort-by last diffs))))
classification (first (last (sort-by second nearest-neighbours)))]
classification))
; Main functions
(defn classify-image [filename]
(convert-image filename temp-outfile)
(classify (load-char-file (java.io.File. temp-outfile))))
(defn -main [& args]
(doseq [filename args]
(println "I think that is the number" (classify-image filename))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment