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