Use float32 to reduce memory footprint.
[master-thesis.git] / Parasitemia / Parasitemia / Program.fs
index b44c65f..09f4312 100644 (file)
@@ -1,5 +1,6 @@
 module Parasitemia.Main
 
+open System
 open System.IO
 open System.Windows
 open System.Windows.Media
@@ -8,6 +9,9 @@ open System.Windows.Shapes
 open System.Windows.Controls
 open System.Drawing
 open System.Diagnostics
+open System.Threading
+
+open FSharp.Collections.ParallelSeq
 
 open Emgu.CV
 open Emgu.CV.Structure
@@ -15,58 +19,122 @@ open Emgu.CV.WPF
 
 open Config
 
-let display (window : Views.MainWindow) (img : IImage) = 
+let display (window : Views.MainWindow) (img : IImage) =
    let imgControl = window.Root.FindName("img") :?> Controls.Image
    imgControl.Source <- BitmapSourceConvert.ToBitmapSource(img)
 
 let log (window : Views.MainWindow) (mess : string) =
    let txtLog = window.Root.FindName("txtLog") :?> Controls.TextBlock
    txtLog.Text <- txtLog.Text + mess + "\n"
-   
-[<System.STAThreadAttribute>]
-do
-    Utils.dprintfn "OpenCV test"
-
-    let app = new Application()
-    let mainWindow = Views.MainWindow()
-
-    Utils.log <- (fun m -> log mainWindow m)
-
-    let config = {
-        scale = 1.0
-
-        doGSigma1 = 1.5
-        doGSigma2 = 20.0
-        doGLowFreqPercentageReduction = 0.9
-
-        darkStainLevel = 0.839     
-        
-        stainSigma = 10.0
-        stainLevel = 0.9
-        stainSpreadRequired = 3.0
-    
-        infectionSigma = 2.0
-        infectionLevel = 0.762
-        infectionPixelsRequired = 1 }
-   
-    ///// ELLIPSES /////
-    //use img = new Image<Bgr, byte>("../../../../src/Tests_hough/images/1508133543-7-4-120-0001.png")
-    //use img = new Image<Bgr, byte>("../../../../src/Tests_hough/images/rbc_single.png")
-    //use img = new Image<Bgr, byte>("../../../../src/Tests_hough/images/rbc_single_oblong_4.png")
-    //use img = new Image<Bgr, byte>("../../../../src/Tests_hough/images/strange_rbc_1.png")
-    //use img = new Image<Bgr, byte>("../../../../src/Tests_hough/images/rbc_single_blurred.png")   
-    use img = new Image<Bgr, byte>("../../../../src/Tests_hough/images/lot.png")
-
-
-    ///// PARASITES /////
-    //use img = new Image<Bgr, byte>("../../../../src/Parasites/images/1.png")
-    
-//    KdTree.test3 ()
-//    Utils.dprintfn "area: %A" area
-
-    let result = Utils.logTime "Total" (fun () ->
-        ImageAnalysis.doAnalysis img config )
-    
-    display mainWindow img
-    mainWindow.Root.Show()
-    app.Run() |> ignore
+
+
+type Input =
+    | File of string
+    | Dir of string
+
+type RunningMode =
+    | CmdLine of Input * string
+    | Window
+
+type Arguments = RunningMode * bool
+
+let parseArgs (args: string[]) : Arguments =
+
+    let output = Array.tryFindIndex ((=) "--output") args
+
+    let runningMode =
+        match Array.tryFindIndex ((=) "--folder") args, output with
+        | Some i, Some i_output when i < args.Length - 2 && i_output < args.Length - 2 ->
+            CmdLine ((Dir args.[i+1]), args.[i_output + 1])
+        | _ ->
+            match Array.tryFindIndex ((=) "--file") args, output with
+            | Some i, Some i_output when i < args.Length - 2 && i_output < args.Length - 2 ->
+                CmdLine ((File args.[i+1]), args.[i_output + 1])
+            |_ ->
+                Window
+
+    runningMode, Array.exists ((=) "--debug") args
+
+
+[<EntryPoint>]
+let main args =
+    match parseArgs args with
+    | mode, debug ->
+        let config =
+            Config(
+              { scale = 1.
+
+                initialAreaOpen = 2000
+
+                minRbcRadius = -0.32f
+                maxRbcRadius = 0.32f
+
+                preFilterSigma = 1.7 // 1.5
+
+                factorNbPick = 1.0
+
+                darkStainLevel = 0.22 // Lower -> more sensitive. 0.3. Careful about illumination on the borders.
+                maxDarkStainRatio = 0.1 // 10 %
+
+                infectionArea = 0.012f // 1.2 %
+                infectionLevel = 1.12 // Lower -> more sensitive.
+
+                stainArea = 0.08f // 8 %
+                stainLevel = 1.1 // Lower -> more sensitive.
+                maxStainRatio = 0.12 // 12 %
+
+                standardDeviationMaxRatio = 0.5 // 0.55
+                minimumCellArea = 0.5f })
+
+        match mode with
+        | CmdLine (input, output) ->
+            if debug
+            then
+                config.Debug <- DebugOn output
+
+            Directory.CreateDirectory output |> ignore
+
+            use logFile = new StreamWriter(new FileStream(Path.Combine(output, "log.txt"), FileMode.Append, FileAccess.Write))
+            Utils.log <- (fun m -> logFile.WriteLine(m))
+            Utils.log (sprintf "=== New run : %A %A ===" DateTime.Now  (if debug then "[DEBUG]" else "[RELEASE]"))
+
+            let files = match input with
+                        | File file -> [ file ]
+                        | Dir dir -> Directory.EnumerateFiles dir |> List.ofSeq
+
+            use resultFile = new StreamWriter(new FileStream(Path.Combine(output, "results.txt"), FileMode.Append, FileAccess.Write))
+
+            //try
+            let images = seq { for file in files -> Path.GetFileNameWithoutExtension(FileInfo(file).Name), new Image<Bgr, byte>(file) }
+
+            let nbConcurrentTaskLimit = 4
+            let n = Environment.ProcessorCount
+
+            Utils.logTime "Whole analyze" (fun () ->
+                let results =
+                    images
+                    |> PSeq.map (fun (id, img) -> id, ImageAnalysis.doAnalysis img id (config.Copy()))
+                    |> PSeq.withDegreeOfParallelism (if n > nbConcurrentTaskLimit then nbConcurrentTaskLimit else n)
+
+                for id, cells in results do
+                    let total, infected = Utils.countCells cells
+                    fprintf resultFile "File: %s %d %d %.2f\n" id total infected (100. * (float infected) / (float total)))
+
+            //Utils.log (sprintf "== File: %A" file)
+            //with
+            //| :? IOException as ex -> Utils.log (sprintf "Unable to open the image '%A': %A" file ex)
+            0
+
+        | Window ->
+            let app = new Application()
+            let mainWindow = Views.MainWindow()
+
+            if debug
+            then
+                config.Debug <- DebugOn "."
+
+            Utils.log <- (fun m -> log mainWindow m)
+
+            //display mainWindow img
+            mainWindow.Root.Show()
+            app.Run()