GUI (work in progress..)
[master-thesis.git] / Parasitemia / Parasitemia / GUI / GUI.fs
1 module Parasitemia.GUI.Main
2
3 open System.IO
4 open System.Linq
5 open System.Windows
6 open System.Windows.Media
7 open System.Windows.Markup
8 open System.Windows.Shapes
9 open System.Windows.Controls
10 open System.Diagnostics
11 open Microsoft.Win32 // For the common dialogs.
12
13 open Emgu.CV.WPF
14
15 open Config
16 open Types
17
18 let run (defaultConfig: Config) =
19 let app = new Application()
20 let mainWindow = Views.MainWindow()
21 let ctrl (name: string): 'a =
22 mainWindow.Root.FindName(name) :?> 'a
23
24 // Utils.log <- (fun m -> log mainWindow m)
25
26 let colorRBCHealthy = Brushes.YellowGreen
27 let colorRBCInfected = Brushes.Red
28
29 let state = State.State()
30
31 let exit: MenuItem = ctrl "menuExit"
32 let saveFile: MenuItem = ctrl "menuSave"
33 let loadFile: MenuItem = ctrl "menuOpen"
34 let newFile: MenuItem = ctrl "menuNew"
35
36 let addSourceImage: MenuItem = ctrl "menuAddSourceImage"
37 let txtPatient: TextBox = ctrl "txtPatient"
38 let stackPreviews: StackPanel = ctrl "stackPreviews"
39 let butStartAnalysis: Button = ctrl "butStartAnalysis"
40
41 let scrollViewCurrentImage: ScrollViewer = ctrl "scrollViewCurrentImage"
42 let borderCurrentImage: Border = ctrl "borderCurrentImage"
43 let canvasCurrentImage: Canvas = ctrl "canvasCurrentImage"
44
45 // Initializations.
46
47 // Operations.
48 let synchronizeState () =
49 state.PatientID <- txtPatient.Text
50
51 let setCurrentImage (srcImg: SourceImage) =
52 state.CurrentImage <- Some srcImg
53
54 // Highlight the preview.
55 stackPreviews.Children
56 |> Seq.cast<Views.ImageSourcePreview>
57 |> Seq.iter (fun preview -> preview.border.BorderThickness <- Thickness(if preview.lblImageNumber.Content = box srcImg.num then 3. else 0.))
58
59 canvasCurrentImage.Height <- float srcImg.img.Height
60 canvasCurrentImage.Width <- float srcImg.img.Width
61 canvasCurrentImage.Background <- ImageBrush(BitmapSourceConvert.ToBitmapSource(srcImg.img))
62
63 // Remove all image canvas children and add the RBC.
64 canvasCurrentImage.Children.Clear()
65 for rbc in srcImg.rbcs do
66 let rectangle =
67 Rectangle(
68 Height = rbc.size.Height,
69 Width = rbc.size.Width,
70 Stroke = (if rbc.infected then colorRBCInfected else colorRBCHealthy),
71 StrokeThickness = 1.,
72 Fill = SolidColorBrush(Color.FromArgb(0uy, 0uy, 0uy, 0uy)),
73 Tag = rbc,
74 Opacity = if rbc.infected then 1. else 0.)
75 Canvas.SetLeft(rectangle, rbc.center.X - rbc.size.Width / 2.)
76 Canvas.SetTop(rectangle, rbc.center.Y - rbc.size.Height / 2.)
77 canvasCurrentImage.Children.Add(rectangle) |> ignore
78 rectangle.MouseEnter.AddHandler(
79 fun obj args -> match obj with
80 | :? Rectangle as r ->
81 r.StrokeThickness <- 3.
82 if not (r.Tag :?> RBC).infected then r.Opacity <- 1.
83 | _ -> ())
84 rectangle.MouseLeave.AddHandler(
85 fun obj args -> match obj with
86 | :? Rectangle as r ->
87 r.StrokeThickness <- 1.
88 if not (r.Tag :?> RBC).infected then r.Opacity <- 0.
89 | _ -> ())
90
91 let addPreview (srcImg: SourceImage) =
92 let imgCtrl = Views.ImageSourcePreview(Margin = Thickness(3.))
93 imgCtrl.lblImageNumber.Content <- srcImg.num
94 let width = 200
95 let height = srcImg.img.Height * width / srcImg.img.Width
96 imgCtrl.imagePreview.Source <- BitmapSourceConvert.ToBitmapSource(srcImg.img.Resize(width, height, Emgu.CV.CvEnum.Inter.Cubic))
97 stackPreviews.Children.Add(imgCtrl) |> ignore
98 imgCtrl.MouseLeftButtonUp.AddHandler(fun obj args -> setCurrentImage (state.SourceImages |> Seq.find (fun i -> box i.num = (obj :?> Views.ImageSourcePreview).lblImageNumber.Content)))
99
100 let updatePreviews () =
101 stackPreviews.Children.Clear ()
102 if state.SourceImages.Count() > 0
103 then
104 for srcImg in state.SourceImages do
105 addPreview srcImg
106 setCurrentImage (state.SourceImages.First())
107
108 let updateGUI () =
109 txtPatient.Text <- state.PatientID
110 updatePreviews ()
111
112 exit.Click.AddHandler(fun obj args -> mainWindow.Root.Close())
113 saveFile.Click.AddHandler(fun obj args ->
114 synchronizeState ()
115 if state.FilePath = ""
116 then
117 let dialog = SaveFileDialog(AddExtension = true, DefaultExt = PiaZ.extension, Filter = PiaZ.filter);
118 let res = dialog.ShowDialog()
119 if res.HasValue && res.Value
120 then
121 state.FilePath <- dialog.FileName
122 state.Save()
123 else
124 state.Save())
125
126 loadFile.Click.AddHandler(fun obj args ->
127 // TODO: if current state not saved and not empty, ask to save it.
128 let dialog = OpenFileDialog(Filter = PiaZ.filter)
129 let res = dialog.ShowDialog()
130 if res.HasValue && res.Value
131 then
132 state.FilePath <- dialog.FileName
133 state.Load()
134 updateGUI ())
135
136 newFile.Click.AddHandler(fun obj args ->
137 // TODO: if current state not saved and not empty, ask to save it.
138 state.Reset()
139 updateGUI())
140
141 addSourceImage.Click.AddHandler(fun obj args ->
142 let dialog = OpenFileDialog(Filter = "Image Files|*.png;*.jpg;*.tif;*.tiff")
143 let res = dialog.ShowDialog()
144 if res.HasValue && res.Value
145 then
146 let srcImg = state.AddSourceImage(dialog.FileName)
147 addPreview srcImg
148 if state.SourceImages.Count() = 1
149 then
150 setCurrentImage srcImg)
151
152 butStartAnalysis.Click.AddHandler(fun obj args ->
153 let results = ImageAnalysis.doMultipleAnalysis (state.SourceImages |> Seq.map (fun srcImg -> string srcImg.num, srcImg.img) |> Seq.toList) defaultConfig
154 for id, cells in results do
155 state.SetResult (int id) cells
156 )
157
158 // Zoom on the current image.
159 let adjustCurrentImageBorders (deltaX: float) (deltaY: float) =
160 borderCurrentImage.BorderThickness <-
161 Thickness(
162 (scrollViewCurrentImage.ViewportWidth + deltaX) / 2.,
163 (scrollViewCurrentImage.ViewportHeight + deltaY) / 2.,
164 (scrollViewCurrentImage.ViewportWidth + deltaX) / 2.,
165 (scrollViewCurrentImage.ViewportHeight + deltaY) / 2.)
166
167 canvasCurrentImage.SizeChanged.AddHandler(fun obj args ->
168 let deltaX = args.NewSize.Width - args.PreviousSize.Width
169 let deltaY = args.NewSize.Height - args.PreviousSize.Height
170 if deltaX > 0.5 || deltaY > 0.5
171 then
172 adjustCurrentImageBorders 0.0 0.0
173 // Center the view at the center of the image initialy.
174 scrollViewCurrentImage.UpdateLayout()
175 scrollViewCurrentImage.ScrollToHorizontalOffset(borderCurrentImage.ActualWidth / 2. - scrollViewCurrentImage.ViewportWidth / 2.)
176 scrollViewCurrentImage.ScrollToVerticalOffset(borderCurrentImage.ActualHeight / 2. - scrollViewCurrentImage.ViewportHeight / 2.))
177
178 scrollViewCurrentImage.SizeChanged.AddHandler(fun obj args ->
179 let deltaX = args.NewSize.Width - args.PreviousSize.Width
180 let deltaY = args.NewSize.Height - args.PreviousSize.Height
181 adjustCurrentImageBorders deltaX deltaY
182 scrollViewCurrentImage.ScrollToHorizontalOffset(scrollViewCurrentImage.HorizontalOffset + deltaX / 8.)
183 scrollViewCurrentImage.ScrollToVerticalOffset(scrollViewCurrentImage.VerticalOffset + deltaY / 8.))
184
185 let mutable currentScale = 1.
186 let mutable maxScale = 4.
187 let mutable minScale = 0.25
188 let currentImageScaleTransform = ScaleTransform()
189 canvasCurrentImage.LayoutTransform <- currentImageScaleTransform
190 borderCurrentImage.PreviewMouseWheel.AddHandler(fun obj args ->
191 let scaleFactor = if args.Delta > 0 then 2.0 else 0.5
192 if scaleFactor > 1. && currentScale < maxScale || scaleFactor < 1. && currentScale > minScale
193 then
194 let previousScale = currentScale
195 currentScale <-
196 let newScale = currentScale * scaleFactor
197 if newScale > maxScale then maxScale elif newScale < minScale then minScale else newScale
198 let realScaleFactor = currentScale / previousScale
199
200 let centerX = scrollViewCurrentImage.HorizontalOffset + scrollViewCurrentImage.ViewportWidth / 2. - borderCurrentImage.BorderThickness.Left
201 let centerY = scrollViewCurrentImage.VerticalOffset + scrollViewCurrentImage.ViewportHeight / 2. - borderCurrentImage.BorderThickness.Top
202
203 //canvasCurrentImage.Margin <- Thickness(canvasCurrentImage.Margin.Top * realScaleFactor)
204 currentImageScaleTransform.ScaleX <- currentScale
205 currentImageScaleTransform.ScaleY <- currentScale
206
207 scrollViewCurrentImage.ScrollToHorizontalOffset(centerX * realScaleFactor - scrollViewCurrentImage.ViewportWidth / 2. + borderCurrentImage.BorderThickness.Left)
208 scrollViewCurrentImage.ScrollToVerticalOffset(centerY * realScaleFactor - scrollViewCurrentImage.ViewportHeight / 2. + borderCurrentImage.BorderThickness.Top)
209 args.Handled <- true)
210
211 // Pan on the current image.
212 let mutable scrollStartPosition = Point(0., 0.)
213 let mutable scrollStartOffsetX = 0.
214 let mutable scrollStartOffsetY = 0.
215 borderCurrentImage.PreviewMouseLeftButtonDown.AddHandler(fun obj args ->
216 scrollStartPosition <- args.GetPosition(scrollViewCurrentImage)
217 scrollStartOffsetX <- scrollViewCurrentImage.HorizontalOffset
218 scrollStartOffsetY <- scrollViewCurrentImage.VerticalOffset
219 borderCurrentImage.Cursor <- Input.Cursors.ScrollAll
220 borderCurrentImage.CaptureMouse() |> ignore
221 args.Handled <- true)
222
223 borderCurrentImage.PreviewMouseMove.AddHandler(fun obj args ->
224 if borderCurrentImage.IsMouseCaptured
225 then
226 let position = args.GetPosition(scrollViewCurrentImage)
227 let deltaX = scrollStartPosition.X - position.X
228 let deltaY = scrollStartPosition.Y - position.Y
229 scrollViewCurrentImage.ScrollToHorizontalOffset(deltaX + scrollStartOffsetX)
230 scrollViewCurrentImage.ScrollToVerticalOffset(deltaY + scrollStartOffsetY)
231 args.Handled <- true)
232
233 borderCurrentImage.PreviewMouseLeftButtonUp.AddHandler(fun obj args ->
234 if borderCurrentImage.IsMouseCaptured
235 then
236 borderCurrentImage.Cursor <- Input.Cursors.Arrow
237 borderCurrentImage.ReleaseMouseCapture()
238 args.Handled <- true)
239
240 // display mainWindow img
241 mainWindow.Root.Show()
242 app.Run()