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