Let's write β

プログラミング中にできたことか、思ったこととか

大津の閾値判別法

大津の閾値判別法

(ql:quickload :iterate)
(ql:quickload :lispbuilder-sdl)
(ql:quickload :lispbuilder-sdl-gfx)
(use-package :iterate)

(defclass <data> ()
  ((max :initform (error "max must specified") :reader data-max :initarg :max)
   (min :initform (error "min must specified") :reader data-min :initarg :min)
   (data :initform (error "data must specified") :reader data :initarg :data)))

(defun my-random (min max)
  (+ min (random (1+ (- max min)))))

(defun make-random-data (len min max)
  (loop repeat len
       for datum = (my-random min max)
       maximize datum into data-max
       minimize datum into data-min
       collect datum into data
       finally (return (make-instance '<data>
				      :min data-min
				      :max data-max
				      :data data))))

(defmethod make-hist-gram ((data <data>))
  (let ((hist-gram (make-hash-table)))
    (loop for datum in (data data)
       do
	 (incf (gethash datum hist-gram 0)))
    hist-gram))

(defun calc-avarage (data)
  (/ (loop for datum in data
	  sum datum)
     (length data)))

(defmethod split-by ((data <data>) (threshold number))
  (loop for datum in (data data)
       if (<= datum threshold)
       collect datum into class1
       else
       collect datum into class2
     finally (return
	       (cons class1 class2))))

(defmethod calc-threshold ((data <data>))
  (iter:iter (for th from (data-min data) below (data-max data))
	     (finding th maximizing
		      (let* ((cls(split-by data th))
                             (c1 (car cls)) (c2 (cdr cls)))
                        (* (length c1)
                           (length c2)
                           (expt (- (calc-avarage c1)
                                    (calc-avarage c2)) 2))))))

(defmethod threshold-viewer ((data <data>))
  (let* ((hist-gram (make-hist-gram data))
         (threshold (calc-threshold data))
         (width (+ (data-max data) 50))
         (height (+ (loop for datum in (data data)
                          maximizing (gethash datum hist-gram))
                    25)))
    (sdl:with-init ()
        (sdl:window width height)
        (sdl:clear-display sdl:*white*)
        ;;Draw data
        (loop for datum in (data data)
              do
              (sdl:draw-line-* 
                (+ 25 datum) height
                (+ 25 datum) (- height (gethash datum hist-gram))
                :color sdl:*black*)) 
        ;;show threshold
        (sdl:draw-line-* 
          threshold 0
          threshold height
          :color sdl:*red*)
        (sdl:update-display)
        (sdl:with-events ()
          (:quit-event () t)))))
CL-USER(3): (calc-threshold (make-random-data 10000 0 255))

128