Save predefined PPI and sensor sizes in JSON files.
[master-thesis.git] / Parasitemia / ParasitemiaUI / Analysis.fs
1 module ParasitemiaUI.Analysis
2
3 open System
4 open System.IO
5 open System.Linq
6 open System.Windows
7 open System.Windows.Media
8 open System.Windows.Markup
9 open System.Windows.Shapes
10 open System.Windows.Controls
11 open System.Diagnostics
12 open Microsoft.Win32 // For the common dialogs.
13
14 open Emgu.CV.WPF
15
16 open ParasitemiaCore.UnitsOfMeasure
17 open ParasitemiaCore.Config
18
19 open Types
20
21 let showWindow (parent: Window) (state: State.State) : bool =
22 let win = Views.AnalysisWindow()
23 win.Root.Owner <- parent
24 win.Root.Left <- parent.Left + parent.ActualWidth / 2. - win.Root.Width / 2.
25 win.Root.Top <- parent.Top + parent.ActualHeight / 2. - win.Root.Height / 2.
26
27 let logListener =
28 { new Logger.IListener with
29 member this.NewEntry severity mess =
30 win.Root.Dispatcher.Invoke(fun () ->
31 win.textLog.Inlines.Add(Documents.Run(mess))
32 win.textLog.Inlines.Add(Documents.LineBreak())
33 win.scrollLog.ScrollToBottom()) }
34
35 Logger.Log.AddListener(logListener)
36
37 let minPPI = 1.
38 let maxPPI = 10e6
39 let parseAndValidatePPI (input: string) : float option =
40 match Double.TryParse(input) with
41 | true, value when value >= minPPI && value <= maxPPI -> Some value
42 | _ -> None
43
44 let monitor = Object()
45 let mutable atLeastOneAnalysisPerformed = false
46 let mutable analysisPerformed = false
47 let mutable analysisCancelled = false
48
49 let updateSourceImages () =
50 win.stackSourceImagesSelection.Children.Clear()
51 let width = int win.stackSourceImagesSelection.ActualWidth
52 for srcImg in state.SourceImages do
53 let imageSourceSelection = Views.ImageSourceSelection(Tag = srcImg, Margin = Thickness(3.))
54 imageSourceSelection.Tag <- srcImg
55
56 imageSourceSelection.txtImageNumber.Text <- srcImg.num.ToString()
57 let height = srcImg.img.Height * width / srcImg.img.Width
58 imageSourceSelection.imagePreview.Source <- BitmapSourceConvert.ToBitmapSource(srcImg.img.Resize(width, height, Emgu.CV.CvEnum.Inter.Cubic))
59 imageSourceSelection.chkSelection.IsChecked <- Nullable<bool>(srcImg.dateLastAnalysis.Ticks = 0L)
60 imageSourceSelection.lblDateLastAnalysis.Content <- if srcImg.dateLastAnalysis.Ticks = 0L then "<Never>" else srcImg.dateLastAnalysis.ToString()
61
62 imageSourceSelection.txtResolution.Text <- if srcImg.dateLastAnalysis.Ticks = 0L then "" else srcImg.config.Parameters.resolution.ToString()
63
64 for ppi in Utils.predefinedPPI do
65 let menu = MenuItem()
66 menu.Header <- ppi.ToString()
67 menu.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- ppi.ppi.ToString())
68 imageSourceSelection.predefinedValuesMenu.Items.Add(menu) |> ignore
69
70 imageSourceSelection.butPPICalculator.Click.AddHandler(fun obj args ->
71 match PPICalculator.showWindow win.Root with
72 | Some resolution -> imageSourceSelection.txtResolution.Text <- resolution.ToString()
73 | None -> ())
74
75 imageSourceSelection.txtResolution.PreviewTextInput.AddHandler(fun obj args ->
76 let text = imageSourceSelection.txtResolution.Text + args.Text
77 args.Handled <- match parseAndValidatePPI text with Some _ -> false | None -> true)
78
79 imageSourceSelection.imagePreview.MouseLeftButtonDown.AddHandler(fun obj args ->
80 let checkbox = imageSourceSelection.chkSelection
81 checkbox.IsChecked <- Nullable<bool>(not (checkbox.IsChecked.HasValue && checkbox.IsChecked.Value)))
82
83 win.stackSourceImagesSelection.Children.Add(imageSourceSelection) |> ignore
84
85 // Get the new parameters for each image. If an error occurs then 'None' is returned and a message box is displayed.
86 // The boolean is 'true' if the image is selected (checked).
87 let getInputImagesParameters () : (SourceImage * bool * Parameters) list option =
88 let sourceImagesControls = win.stackSourceImagesSelection.Children |> Seq.cast<Views.ImageSourceSelection>
89 let parameters = seq {
90 for srcImgCtrl in sourceImagesControls do
91 let srcImg = srcImgCtrl.Tag :?> SourceImage
92 let isChecked = srcImgCtrl.chkSelection.IsChecked
93 match parseAndValidatePPI srcImgCtrl.txtResolution.Text with
94 | Some resolution ->
95 yield Some (srcImg, isChecked.HasValue && isChecked.Value, { srcImg.config.Parameters with resolution = resolution * 1.<ppi> })
96 | None ->
97 MessageBox.Show(sprintf "No resolution defined for the image number %d" srcImg.num, "No resolution defined", MessageBoxButton.OK, MessageBoxImage.Information) |> ignore
98 yield None } |> Seq.takeWhile (fun e -> e.IsSome) |> Seq.map (fun e -> e.Value) |> List.ofSeq
99 if parameters.Count() <> sourceImagesControls.Count()
100 then None
101 else Some parameters
102
103 win.butClose.Click.AddHandler(fun obj args -> win.Root.Close())
104
105 win.butStart.Click.AddHandler(fun obj args ->
106 match getInputImagesParameters () with
107 | Some imagesParameters ->
108 let imagesToProcess = [
109 for srcImg, selected, parameters in imagesParameters do
110 srcImg.config.Parameters <- parameters // Save parameters.
111 if selected
112 then yield srcImg.num.ToString(), srcImg.config, srcImg.img ]
113
114 if imagesToProcess.IsEmpty
115 then
116 MessageBox.Show("No image selected", "Cannot start analysis", MessageBoxButton.OK, MessageBoxImage.Information) |> ignore
117 else
118 win.stackSourceImagesSelection.IsEnabled <- false
119 analysisPerformed <- false
120 win.butStart.IsEnabled <- false
121 win.butClose.Content <- "Abort"
122
123 async {
124 let maybeResults =
125 ParasitemiaCore.Analysis.doMultipleAnalysis
126 imagesToProcess
127 (Some (fun progress -> win.Root.Dispatcher.Invoke(fun () -> win.progress.Value <- float progress); not analysisCancelled))
128
129 lock monitor (
130 fun() ->
131 match maybeResults with
132 | Some results ->
133 for id, cells in results do
134 state.SetResult (int id) cells
135 Logger.Log.User("All analyses terminated successfully")
136 atLeastOneAnalysisPerformed <- true
137 analysisPerformed <- true
138 | None ->
139 Logger.Log.User("Analysis aborted")
140
141 win.Root.Dispatcher.Invoke(fun () ->
142 win.progress.Value <- if maybeResults.IsSome then 100. else 0.
143 win.stackSourceImagesSelection.IsEnabled <- true
144 win.butStart.IsEnabled <- true
145 win.butClose.Content <- "Close"
146 updateSourceImages ()))
147 } |> Async.Start
148 | _ -> ())
149
150 win.Root.Loaded.AddHandler(fun obj args -> updateSourceImages ())
151
152 win.Root.ShowDialog() |> ignore
153
154 Logger.Log.RmListener(logListener)
155
156 lock monitor (fun () ->
157 if not analysisPerformed
158 then
159 // To cancel the current analysis if one is running on the next call to the progress function.
160 analysisCancelled <- true
161 atLeastOneAnalysisPerformed)