3D Auto-stereograms

canvas3d

A SIRD (Single Image Random Dot) diagram, a.k.a. 'Magic Eye' or autostereogram is a single-image stereogram (SIS), designed to create the visual illusion of a three-dimensional (3D) scene within the human brain from an external two-dimensional image. In order to perceive 3D shapes in these autostereograms, one must overcome the normally automatic coordination between accommodation (focus) and vergence (angle of one's eyes). A random depth-map image from https://github.com/rm-hull/depth-maps is used to generate the autostereogram; some of them may be hard to distinguish initially - look in the javascript console for a link to which depth map was used.

8 Jun 2013 by rm-hull
rm-hull/5736733
autostereogram.cljs
(ns autostereogram.demo    
  (:use [monet.canvas :only [draw-image]]
        [enchilada :only [canvas ctx proxy-request]]
        [jayq.core :only [show]]
        [jayq.util :only [log]]))

(def greens
  (map #(vector 0x30 % 0x30 0xFF) (range 0xCF)))

(defn random-data [w h colors]
  (let [v (vec colors)
        sz (count v)
        rnd-color #(get v (rand-int sz))]
    { :width w
      :height h
      :data (vec (repeatedly h
              #(vec (repeatedly w rnd-color))))})) 
        
(defn depthmap-url
  ([] (depthmap-url (rand-int 10)))
  ([n] (str "https://raw.github.com/rm-hull/depth-maps/master/" n ".jpg")))

(defn load-image! [url & [callback-fn]]
  (let [img (js/Image.)]
    (println "Loading image: " url)
    (when callback-fn
      (set! (.-onload img) (fn [] (callback-fn img))))
    (set! (.-crossOrigin img) "anonymous")
    (set! (.-src img) (proxy-request url)))
    img)

(defn offset [[x y] w]
  (* (+ (* y w) x) 4))

(defn get-point [arr width co-ords]
  (let [offset (offset co-ords width)]
    (map #(aget arr %) (range offset (+ offset 4)))))

(defn set-point! [arr width co-ords data]
  (loop [idx (offset co-ords width)
         data   data]
    (when (seq data)
      (aset arr idx (first data))
      (recur (inc idx) (rest data)))))

(defn depth 
  "Input data assumed to be [r g b a], normalize to range 0..31 from green byte"
  [data]
  (quot (second data) 8))

(defn generate-sird [img rand-data]
  (let [width     (.-width img)
        height    (.-height img)]
    (draw-image ctx img { :x 0 :y 0 :w width :h height })
    (let [depth-map   (. ctx (getImageData 0 0 width height))
          data        (.-data depth-map)
          rand-w      (:width rand-data)
          set-pt!     (partial set-point! data width)
          split-point (- width 1 rand-w)]
      (doseq [y (range height)
              :let [rand-row (get (:data rand-data) (mod y (:height rand-data)))]
              x (range (dec width) -1 -1)
              :let [depth (depth (get-point data width [x y]))]]
        (set-pt! 
          [x y]
          (if (> x split-point) 
            (get rand-row (mod (- x depth) rand-w))
            (get-point data width [(- (+ x rand-w) depth) y]))))
      (. ctx (putImageData depth-map 0 0)))))

(show canvas)

(load-image! 
  (depthmap-url) 
  #(generate-sird % (random-data 100 100 greens)))