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