racket/collects/images/private/flomap-blur.rkt

344 lines
14 KiB
Racket
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#lang typed/racket/base
(require racket/match racket/math
(only-in racket/unsafe/ops unsafe-flvector-ref)
"flonum.rkt"
"flomap-struct.rkt")
(provide flomap-gaussian-blur-x flomap-gaussian-blur-y flomap-gaussian-blur
flomap-box-blur-x flomap-box-blur-y flomap-box-blur
flomap-blur-x flomap-blur-y flomap-blur)
;; ===================================================================================================
;; Gaussian blur
(: flomap-gaussian-blur (case-> (flomap Real -> flomap)
(flomap Real Real -> flomap)))
(define flomap-gaussian-blur
(case-lambda
[(fm xσ) (flomap-gaussian-blur fm xσ xσ)]
[(fm xσ yσ)
(flomap-gaussian-blur-y (flomap-gaussian-blur-x fm (abs (real->double-flonum xσ)))
(abs (real->double-flonum yσ)))]))
(: flomap-gaussian-blur-x (flomap Real -> flomap))
(define (flomap-gaussian-blur-x fm σ*)
(cond
[(σ* . = . 0) fm]
[else
(define σ (abs (real->double-flonum σ*)))
(define dx-min (fl->fx (floor (* (- 3.0) σ))))
(define dx-max (fx+ 1 (fl->fx (ceiling (* 3.0 σ)))))
(define ss (gaussian-kernel-1d dx-min dx-max σ))
(match-define (flomap vs c w h) fm)
(inline-build-flomap
c w h
(λ (k x y i)
(define dx-start (fx- (fxmax (fx+ x dx-min) 0) x))
(define dx-end (fx- (fxmin (fx+ x dx-max) w) x))
(define j (fx+ i (fx* c dx-start)))
(let: src-loop : Float ([sum : Float 0.0] [dx : Fixnum dx-start] [j : Fixnum j])
(cond [(dx . fx< . dx-end) (define s (unsafe-flvector-ref ss (fx- dx dx-min)))
(src-loop (+ sum (* s (unsafe-flvector-ref vs j)))
(fx+ dx 1)
(fx+ j c))]
[else sum]))))]))
(: flomap-gaussian-blur-y (flomap Real -> flomap))
(define (flomap-gaussian-blur-y fm σ*)
(cond
[(σ* . = . 0) fm]
[else
(define σ (abs (real->double-flonum σ*)))
(define dy-min (fl->fx (floor (* (- 3.0) σ))))
(define dy-max (fx+ 1 (fl->fx (ceiling (* 3.0 σ)))))
(define ss (gaussian-kernel-1d dy-min dy-max σ))
(match-define (flomap vs c w h) fm)
(define cw (* c w))
(inline-build-flomap
c w h
(λ (k x y i)
(define dy-start (fx- (fxmax (fx+ y dy-min) 0) y))
(define dy-end (fx- (fxmin (fx+ y dy-max) h) y))
(define j (fx+ i (fx* cw dy-start)))
(let: src-loop : Float ([sum : Float 0.0] [dy : Fixnum dy-start] [j : Fixnum j])
(cond [(dy . fx< . dy-end) (define s (unsafe-flvector-ref ss (fx- dy dy-min)))
(src-loop (+ sum (* s (unsafe-flvector-ref vs j)))
(fx+ dy 1)
(fx+ j cw))]
[else sum]))))]))
(: gaussian-kernel-1d (Fixnum Fixnum Float -> FlVector))
(define (gaussian-kernel-1d mn mx σ)
(define n (fx- mx mn))
(define ys (make-flvector n))
(define sum
(let: loop : Float ([i : Fixnum 0] [sum : Float 0.0])
(cond [(i . fx< . n) (define v (flgaussian (fx->fl (fx+ i mn)) σ))
(flvector-set! ys i v)
(loop (fx+ i 1) (+ sum v))]
[else sum])))
(let: loop : FlVector ([i : Integer 0])
(cond [(i . fx< . n) (flvector-set! ys i (/ (flvector-ref ys i) sum))
(loop (fx+ i 1))]
[else ys])))
;; ===================================================================================================
;; Integral images
(: flomap-integral (flomap -> flomap))
(define (flomap-integral fm)
(match-define (flomap vs c w h) fm)
(define w+1 (fx+ w 1))
(define c*w+1 (fx* c w+1))
(define h+1 (fx+ h 1))
(define new-vs (make-flvector (* c w+1 h+1)))
(let: y-loop : Void ([y : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum 0])
(when (y . fx< . h)
(let: x-loop : Void ([x : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum i])
(cond [(x . fx< . w)
(let: k-loop : Void ([k : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum i])
(cond [(k . fx< . c)
(define j00 (coords->index c w+1 k x y))
(define j01 (fx+ j00 c*w+1))
(flvector-set! new-vs (fx+ j01 c)
(- (+ (flvector-ref vs i)
(flvector-ref new-vs j01)
(flvector-ref new-vs (fx+ j00 c)))
(flvector-ref new-vs j00)))
(k-loop (fx+ k 1) (fx+ i 1))]
[else (x-loop (fx+ x 1) i)]))]
[else (y-loop (fx+ y 1) i)]))))
(flomap new-vs c w+1 h+1))
(: flomap-integral-x (flomap -> flomap))
(define (flomap-integral-x fm)
(match-define (flomap vs c w h) fm)
(define w+1 (fx+ w 1))
(define new-vs (make-flvector (* c w+1 h)))
(let: y-loop : Void ([y : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum 0])
(when (y . fx< . h)
(let: x-loop : Void ([x : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum i])
(cond [(x . fx< . w)
(let: k-loop : Void ([k : Nonnegative-Fixnum 0] [i : Nonnegative-Fixnum i])
(cond [(k . fx< . c)
(define j0 (coords->index c w+1 k x y))
(define j1 (fx+ j0 c))
(flvector-set! new-vs j1 (+ (flvector-ref vs i)
(flvector-ref new-vs j0)))
(k-loop (fx+ k 1) (fx+ i 1))]
[else (x-loop (fx+ x 1) i)]))]
[else (y-loop (fx+ y 1) i)]))))
(flomap new-vs c w+1 h))
(: flomap-integral-y (flomap -> flomap))
(define (flomap-integral-y fm)
(match-define (flomap vs c w h) fm)
(define h+1 (fx+ h 1))
(define cw (fx* c w))
(define new-vs (make-flvector (* c w h+1)))
(let: y-loop : Void ([y : Nonnegative-Fixnum 0])
(when (y . fx< . h)
(let: x-loop : Void ([x : Nonnegative-Fixnum 0])
(cond [(x . fx< . w)
(let: k-loop : Void ([k : Nonnegative-Fixnum 0])
(cond [(k . fx< . c)
(define j0 (coords->index c w k x y))
(define j1 (fx+ j0 cw))
(flvector-set! new-vs j1 (+ (flvector-ref vs j0)
(flvector-ref new-vs j0)))
(k-loop (fx+ k 1))]
[else (x-loop (fx+ x 1))]))]
[else (y-loop (fx+ y 1))]))))
(flomap new-vs c w h+1))
(: raw-flomap-integral-sum (FlVector Integer Integer Integer
Integer Integer Integer Integer Integer
-> Float))
(define (raw-flomap-integral-sum vs c w h k x-start y-start x-end y-end)
(define w-1 (fx- w 1))
(define h-1 (fx- h 1))
(define x1 (fxmax 0 (fxmin x-start w-1)))
(define x2 (fxmax 0 (fxmin x-end w-1)))
(define y1 (fxmax 0 (fxmin y-start h-1)))
(define y2 (fxmax 0 (fxmin y-end h-1)))
(- (+ (flvector-ref vs (coords->index c w k x1 y1))
(flvector-ref vs (coords->index c w k x2 y2)))
(+ (flvector-ref vs (coords->index c w k x1 y2))
(flvector-ref vs (coords->index c w k x2 y1)))))
(: raw-flomap-integral-x-sum (FlVector Integer Integer
Integer Integer Integer Integer -> Float))
(define (raw-flomap-integral-x-sum vs c w k x-start x-end y)
(define w-1 (fx- w 1))
(define x1 (fxmax 0 (fxmin x-start w-1)))
(define x2 (fxmax 0 (fxmin x-end w-1)))
(- (flvector-ref vs (coords->index c w k x2 y))
(flvector-ref vs (coords->index c w k x1 y))))
(: raw-flomap-integral-y-sum (FlVector Integer Integer Integer
Integer Integer Integer Integer -> Float))
(define (raw-flomap-integral-y-sum vs c w h k x y-start y-end)
(define h-1 (fx- h 1))
(define y1 (fxmax 0 (fxmin y-start h-1)))
(define y2 (fxmax 0 (fxmin y-end h-1)))
(- (flvector-ref vs (coords->index c w k x y2))
(flvector-ref vs (coords->index c w k x y1))))
;; ===================================================================================================
;; Box blur
(: flomap-box-blur (case-> (flomap Real -> flomap)
(flomap Real Real -> flomap)))
(define flomap-box-blur
(case-lambda
[(fm xr) (flomap-box-blur fm xr xr)]
[(fm xr yr)
(let ([xr (abs (real->double-flonum xr))]
[yr (abs (real->double-flonum yr))])
(cond [(and (integer? xr) (integer? yr))
(let ([xr (fl->fx xr)] [yr (fl->fx yr)])
(with-asserts ([xr nonnegative-fixnum?] [yr nonnegative-fixnum?])
(flomap-box-blur/int fm xr yr)))]
[else
(flomap-box-blur-y (flomap-box-blur-x fm xr) yr)]))]))
(: flomap-box-blur-x (flomap Real -> flomap))
(define (flomap-box-blur-x fm r*)
(define r (abs (real->double-flonum r*)))
(cond
[(integer? r) (let ([r (fl->fx r)])
(with-asserts ([r nonnegative-fixnum?])
(flomap-box-blur-x/int fm r)))]
[else
(define r1 (fl->fx (floor r)))
(define r2 (fx+ r1 1))
(define s (+ 1.0 (* 2.0 r)))
(define s1 (+ 1.0 (* 2.0 r1)))
(define s2 (+ 1.0 (* 2.0 r2)))
(define α (/ (- (sqr s2) (sqr s)) (- (sqr s2) (sqr s1))))
(define norm1 (/ α s1))
(define norm2 (/ (- 1.0 α) s2))
(define r1+1 (fx+ r1 1))
(define r2+1 (fx+ r2 1))
(match-define (flomap _ c w h) fm)
(match-define (flomap int-vs int-c int-w int-h) (flomap-integral-x fm))
(inline-build-flomap
c w h
(λ (k x y _i)
(+ (* norm1 (raw-flomap-integral-x-sum int-vs int-c int-w k (fx- x r1) (fx+ x r1+1) y))
(* norm2 (raw-flomap-integral-x-sum int-vs int-c int-w k (fx- x r2) (fx+ x r2+1) y))
)))]))
(: flomap-box-blur-y (flomap Real -> flomap))
(define (flomap-box-blur-y fm r*)
(define r (abs (real->double-flonum r*)))
(cond
[(integer? r) (let ([r (fl->fx r)])
(with-asserts ([r nonnegative-fixnum?])
(flomap-box-blur-y/int fm r)))]
[else
(define r1 (fl->fx (floor r)))
(define r2 (fx+ r1 1))
(define s (+ 1.0 (* 2.0 r)))
(define s1 (+ 1.0 (* 2.0 r1)))
(define s2 (+ 1.0 (* 2.0 r2)))
(define α (/ (- (sqr s2) (sqr s)) (- (sqr s2) (sqr s1))))
(define norm1 (/ α s1))
(define norm2 (/ (- 1.0 α) s2))
(define r1+1 (fx+ r1 1))
(define r2+1 (fx+ r2 1))
(match-define (flomap _ c w h) fm)
(match-define (flomap int-vs int-c int-w int-h) (flomap-integral-y fm))
(inline-build-flomap
c w h
(λ (k x y _i)
(+ (* norm1 (raw-flomap-integral-y-sum int-vs int-c int-w int-h k x (fx- y r1) (fx+ y r1+1)))
(* norm2 (raw-flomap-integral-y-sum int-vs int-c int-w int-h k x (fx- y r2) (fx+ y r2+1)))
)))]))
(: flomap-box-blur/int (flomap Nonnegative-Fixnum Nonnegative-Fixnum -> flomap))
(define (flomap-box-blur/int fm xr yr)
(define norm (/ 1.0 (* (+ 1.0 (* 2.0 xr)) (+ 1.0 (* 2.0 yr)))))
(define xr+1 (fx+ xr 1))
(define yr+1 (fx+ yr 1))
(match-define (flomap _ c w h) fm)
(match-define (flomap int-vs int-c int-w int-h) (flomap-integral fm))
(inline-build-flomap
c w h
(λ (k x y _i)
(* norm (raw-flomap-integral-sum int-vs int-c int-w int-h k
(fx- x xr) (fx- y yr)
(fx+ x xr+1) (fx+ y yr+1))))))
(: flomap-box-blur-x/int (flomap Nonnegative-Fixnum -> flomap))
(define (flomap-box-blur-x/int fm r)
(define norm (/ 1.0 (+ 1.0 (* 2.0 r))))
(define r+1 (fx+ r 1))
(match-define (flomap _ c w h) fm)
(match-define (flomap int-vs int-c int-w int-h) (flomap-integral-x fm))
(inline-build-flomap
c w h
(λ (k x y _i)
(* norm (raw-flomap-integral-x-sum int-vs int-c int-w k (fx- x r) (fx+ x r+1) y)))))
(: flomap-box-blur-y/int (flomap Nonnegative-Fixnum -> flomap))
(define (flomap-box-blur-y/int fm r)
(define norm (/ 1.0 (+ 1.0 (* 2.0 r))))
(define r+1 (fx+ r 1))
(match-define (flomap _ c w h) fm)
(match-define (flomap int-vs int-c int-w int-h) (flomap-integral-y fm))
(inline-build-flomap
c w h
(λ (k x y _i)
(* norm (raw-flomap-integral-y-sum int-vs int-c int-w int-h k x (fx- y r) (fx+ y r+1))))))
;; ===================================================================================================
;; Default blur
(: box-radius->variance (Float -> Float))
(define (box-radius->variance r)
(* 1/12 (sqr (+ 1 (* 2 r)))))
(: variance->box-radius (Float -> Float))
(define (variance->box-radius σ^2)
(* 1/2 (- (flsqrt (* 12 σ^2)) 1)))
(: flomap-blur (case-> (flomap Real -> flomap)
(flomap Real Real -> flomap)))
(define flomap-blur
(case-lambda
[(fm σ) (flomap-blur fm σ σ)]
[(fm xσ yσ)
(let ([xσ (abs (real->double-flonum xσ))]
[yσ (abs (real->double-flonum yσ))])
(cond
[(and (xσ . >= . 1.5) (yσ . >= . 1.5))
(define xσ^2 (sqr xσ))
(define yσ^2 (sqr yσ))
(define xr (floor (variance->box-radius (/ xσ^2 3.0))))
(define yr (floor (variance->box-radius (/ yσ^2 3.0))))
(flomap-box-blur (flomap-box-blur (flomap-box-blur fm xr yr) xr yr)
(variance->box-radius (- xσ^2 (* 2.0 (box-radius->variance xr))))
(variance->box-radius (- yσ^2 (* 2.0 (box-radius->variance yr)))))]
[else
(flomap-blur-x (flomap-blur-y fm yσ) xσ)]))]))
(: make-flomap-blur-dimension
((flomap Float -> flomap) (flomap Float -> flomap) -> (flomap Float -> flomap)))
(define ((make-flomap-blur-dimension gaussian-blur box-blur) fm σ)
(cond
[(σ . = . 0.0) fm]
[(σ . < . 1.5) (gaussian-blur fm σ)]
[else
(define σ^2 (sqr σ))
(define r (floor (variance->box-radius (/ σ^2 3.0))))
(box-blur (box-blur (box-blur fm r) r)
(variance->box-radius (- σ^2 (* 2.0 (box-radius->variance r)))))]))
(define flomap-blur-x (make-flomap-blur-dimension flomap-gaussian-blur-x flomap-box-blur-x))
(define flomap-blur-y (make-flomap-blur-dimension flomap-gaussian-blur-y flomap-box-blur-y))