# 大津の閾値判別法

```(ql:quickload :iterate)
(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
```