Median filter

From Rosetta Code
Revision as of 22:14, 7 December 2008 by rosettacode>Blue Prawn (initial version with OCaml ; code (originally in lua) borrowed from excellent pippin's doc)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Task
Median filter
You are encouraged to solve this task according to the task description, using any language you may know.

The median filter takes in the neighbourhood the median color.

(to test the function below, you can use these input and output solutions)

OCaml

<ocaml>let color_add (r1,g1,b1) (r2,g2,b2) =

 ( (r1 + r2),
   (g1 + g2),
   (b1 + b2) )

let color_div (r,g,b) d =

 ( (r / d),
   (g / d),
   (b / d) )

let compare_as_grayscale (r1,g1,b1) (r2,g2,b2) =

 let v1 = (2_126 * r1 +  7_152 * g1 + 722 * b1)
 and v2 = (2_126 * r2 +  7_152 * g2 + 722 * b2) in
 (Pervasives.compare v1 v2)

let get_rgb img x y =

 let _, r_channel,_,_ = img in
 let width = Bigarray.Array2.dim1 r_channel
 and height = Bigarray.Array2.dim2 r_channel in
 if (x < 0) || (x >= width) then (0,0,0) else
 if (y < 0) || (y >= height) then (0,0,0) else  (* feed borders with black *)
 (get_pixel img x y)


let median_value img radius =

 let samples = (radius*2+1) * (radius*2+1) in
 fun x y ->
   let sample = ref [] in
   for _x = (x - radius) to (x + radius) do
     for _y = (y - radius) to (y + radius) do
     
       let v = get_rgb img _x _y in
       sample := v :: !sample;
     done;
   done;
   let ssample = List.sort compare_as_grayscale !sample in
   let mid = (samples / 2) in
   if (samples mod 2) = 1
   then List.nth ssample (mid+1)
   else
     let median1 = List.nth ssample (mid)
     and median2 = List.nth ssample (mid+1) in
     (color_div (color_add median1 median2) 2)


let median img radius =

 let _, r_channel,_,_ = img in
 let width = Bigarray.Array2.dim1 r_channel
 and height = Bigarray.Array2.dim2 r_channel in
 let _median_value = median_value img radius in
 let res = new_img ~width ~height in
 for y = 0 to pred height do
   for x = 0 to pred width do
     let color = _median_value x y in
     put_pixel res color x y;
   done;
 done;
 (res)</ocaml>

an alternate version of the function median_value using arrays instead of lists: <ocaml>let median_value img radius =

 let samples = (radius*2+1) * (radius*2+1) in
 let sample = Array.make samples (0,0,0) in
 fun x y ->
   let i = ref 0 in
   for _x = (x - radius) to (x + radius) do
     for _y = (y - radius) to (y + radius) do
       let v = get_rgb img _x _y in
       sample.(!i) <- v;
       incr i;
     done;
   done;
   Array.sort compare_as_grayscale sample;
   let mid = (samples / 2) in
   if (samples mod 2) = 1
   then sample.(mid+1)
   else (color_div (color_add sample.(mid)
                              sample.(mid+1)) 2)</ocaml>