Use two radius in the configuration, one computed with the image resolution and one...
[master-thesis.git] / Parasitemia / Parasitemia / GUI / Analysis.fs
index acb9ce2..6ae1e20 100644 (file)
@@ -1,15 +1,23 @@
 module Parasitemia.GUI.Analysis
 
 open System
+open System.IO
+open System.Linq
 open System.Windows
 open System.Windows.Media
 open System.Windows.Markup
 open System.Windows.Shapes
 open System.Windows.Controls
+open System.Diagnostics
+open Microsoft.Win32 // For the common dialogs.
 
+open Emgu.CV.WPF
+
+open UnitsOfMeasure
 open Config
+open Types
 
-let showWindow (parent: Window) (state: State.State) (defaultConfig: Config) : bool =
+let showWindow (parent: Window) (state: State.State) : bool =
     let window = Views.AnalysisWindow()
     window.Root.Owner <- parent
     window.Root.Left <- parent.Left + parent.ActualWidth / 2. - window.Root.Width / 2.
@@ -21,6 +29,7 @@ let showWindow (parent: Window) (state: State.State) (defaultConfig: Config) : b
     let butClose: Button = ctrl "butClose"
     let butStart: Button = ctrl "butStart"
 
+    let stackImagesSourceSelection: StackPanel = ctrl "stackImagesSourceSelection"
     let progressBar: ProgressBar = ctrl "progress"
     let textLog: TextBlock = ctrl "textLog"
     let scrollLog: ScrollViewer = ctrl "scrollLog"
@@ -30,36 +39,94 @@ let showWindow (parent: Window) (state: State.State) (defaultConfig: Config) : b
         textLog.Inlines.Add(Documents.LineBreak())
         scrollLog.ScrollToBottom()))
 
+    let minPPI = 1.
+    let maxPPI = 10e6
+    let parseAndValidatePPI (input: string) : float option =
+        let res = ref 0.
+        if Double.TryParse(input, res) && !res >= minPPI && !res <= maxPPI
+        then Some !res
+        else None
+
     let monitor = Object()
+    let mutable atLeastOneAnalysisPerformed = false
     let mutable analysisPerformed = false
     let mutable analysisCancelled = false
 
+    let updateSourceImages () =
+        stackImagesSourceSelection.Children.Clear()
+        let width = int stackImagesSourceSelection.ActualWidth
+        for srcImg in state.SourceImages do
+            let imageSourceSelection = Views.ImageSourceSelection(Tag = srcImg, Margin = Thickness(3.))
+
+            let updateResolution () =
+                match parseAndValidatePPI imageSourceSelection.txtResolution.Text with
+                | Some resolution -> srcImg.config.Parameters <- { srcImg.config.Parameters with resolution = resolution * 1.<ppi> }
+                | None -> ()
+
+            imageSourceSelection.txtImageNumber.Text <- srcImg.num.ToString()
+            let height = srcImg.img.Height * width / srcImg.img.Width
+            imageSourceSelection.imagePreview.Source <- BitmapSourceConvert.ToBitmapSource(srcImg.img.Resize(width, height, Emgu.CV.CvEnum.Inter.Cubic))
+            imageSourceSelection.chkSelection.IsChecked <- Nullable<bool>(srcImg.dateLastAnalysis.Ticks = 0L)
+            imageSourceSelection.lblDateLastAnalysis.Content <- if srcImg.dateLastAnalysis.Ticks = 0L then "<Never>" else srcImg.dateLastAnalysis.ToString()
+
+            imageSourceSelection.txtResolution.Text <- srcImg.config.Parameters.resolution.ToString()
+            imageSourceSelection.menuZoom50X.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- "200000"; updateResolution ())
+            imageSourceSelection.menuZoom100X.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- "400000"; updateResolution ())
+
+            imageSourceSelection.txtResolution.PreviewTextInput.AddHandler(fun obj args ->
+                let text = imageSourceSelection.txtResolution.Text + args.Text
+                args.Handled <- match parseAndValidatePPI text with Some _ -> false | None -> true)
+
+            imageSourceSelection.imagePreview.MouseLeftButtonDown.AddHandler(fun obj args ->
+                let checkbox = imageSourceSelection.chkSelection
+                checkbox.IsChecked <- Nullable<bool>(not (checkbox.IsChecked.HasValue && checkbox.IsChecked.Value)))
+
+            imageSourceSelection.txtResolution.LostFocus.AddHandler(fun obj args -> updateResolution ())
+
+            stackImagesSourceSelection.Children.Add(imageSourceSelection) |> ignore
+
     butClose.Click.AddHandler(fun obj args -> window.Root.Close())
 
     butStart.Click.AddHandler(fun obj args ->
-        butStart.IsEnabled <- false
-        butClose.Content <- "Abort"
-        async {
-            let results =
-                ImageAnalysis.doMultipleAnalysis
-                    (state.SourceImages |> Seq.map (fun srcImg -> string srcImg.num, srcImg.img) |> Seq.toList)
-                    defaultConfig
-                    (Some (fun progress -> window.Root.Dispatcher.Invoke(fun () -> progressBar.Value <- float progress)))
-
-            lock monitor (
-                fun() ->
-                    if not analysisCancelled
-                    then
-                        for id, rbcRadius, cells in results do
-                            state.SetResult (int id) rbcRadius cells
-
-                        window.Root.Dispatcher.Invoke(fun () ->
-                            butStart.IsEnabled <- false
-                            butClose.Content <- "Close")
-
-                        Utils.log "Analysis terminated successfully"
-                        analysisPerformed <- true)
-        } |> Async.Start)
+        let imagesToProcess = [
+            for imageSelection in stackImagesSourceSelection.Children |> Seq.cast<Views.ImageSourceSelection> do
+                let chk = imageSelection.chkSelection.IsChecked
+                if chk.HasValue && chk.Value
+                then
+                    let srcImg = imageSelection.Tag :?> SourceImage
+                    yield srcImg.num.ToString(), srcImg.config, srcImg.img ]
+
+        if imagesToProcess.IsEmpty
+        then
+            MessageBox.Show("No image selected", "Cannot start analysis", MessageBoxButton.OK, MessageBoxImage.Information) |> ignore
+        else
+            analysisPerformed <- false
+            butStart.IsEnabled <- false
+            butClose.Content <- "Abort"
+            async {
+                let results =
+                    ImageAnalysis.doMultipleAnalysis
+                        imagesToProcess
+                        (Some (fun progress -> window.Root.Dispatcher.Invoke(fun () -> progressBar.Value <- float progress)))
+
+                lock monitor (
+                    fun() ->
+                        if not analysisCancelled
+                        then
+                            for id, cells in results do
+                                state.SetResult (int id) cells
+
+                            window.Root.Dispatcher.Invoke(fun () ->
+                                butStart.IsEnabled <- true
+                                butClose.Content <- "Close"
+                                updateSourceImages ())
+
+                            Utils.log "All analyses terminated successfully"
+                            atLeastOneAnalysisPerformed <- true
+                            analysisPerformed <- true)
+            } |> Async.Start)
+
+    window.Root.Loaded.AddHandler(fun obj args -> updateSourceImages ())
 
     window.Root.ShowDialog() |> ignore
 
@@ -67,9 +134,4 @@ let showWindow (parent: Window) (state: State.State) (defaultConfig: Config) : b
         if not analysisPerformed
             then
                 analysisCancelled <- true
-        analysisPerformed)
-
-
-    (*let results = ImageAnalysis.doMultipleAnalysis (state.SourceImages |> Seq.map (fun srcImg -> string srcImg.num, srcImg.img) |> Seq.toList) defaultConfig
-    for id, rbcRadius, cells in results do
-        state.SetResult (int id) rbcRadius cells*)
\ No newline at end of file
+        atLeastOneAnalysisPerformed)
\ No newline at end of file