Put the RBC number text below border on the main image.
[master-thesis.git] / Parasitemia / ParasitemiaUI / GUI.fs
index 46171a7..89f38e3 100644 (file)
@@ -5,23 +5,19 @@ open System.IO
 open System.Linq
 open System.Windows
 open System.Windows.Media
-open System.Windows.Markup
-open System.Windows.Shapes
 open System.Windows.Controls
-open System.Diagnostics
 open Microsoft.Win32 // For the common dialogs.
 
-open Emgu.CV.WPF
-
 open Logger
 
+open ParasitemiaUIControls
 open ParasitemiaCore.Config
 open ParasitemiaCore.Utils
 open Types
 
 let run (defaultConfig : Config) (fileToOpen : string option) =
     let app = new Application ()
-    let win = Views.MainWindow ()
+    let win = MainWindow ()
 
     let state = State.State defaultConfig
     let mutable currentScale = 1.
@@ -47,21 +43,29 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
             )
         )
 
-    let setRBCFrameStyle (srcImg : SourceImage) (rbc : RBC) (frame : Views.RBCFrame) =
+    let setRBCFrameStyle (srcImg : SourceImage) (rbc : RBC) (frame : RBCFrame) =
         frame.Opacity <- if displayHealthy || rbc.setManually || rbc.infected then 1. else 0.
         let color = if rbc.infected then srcImg.InfectedRBCColor else srcImg.HealthyRBCColor
         frame.manuallyAdded.Visibility <- if rbc.setManually then Visibility.Visible else Visibility.Hidden
         frame.manuallyAdded.Fill <- color
         frame.border.Stroke <- color
 
-    let RBCFrameFromExisting (srcImg : SourceImage) (rbc : RBC) (frame : Views.RBCFrame) : Views.RBCFrame =
+    let frameStrokeThickness (averageRBCSize : float) =
+        max 1. (averageRBCSize / 60.)
+
+    let frameFontSize (averageRBCSize : float) =
+        max 1. (averageRBCSize / 6.)
+
+    let createRBCFrameFromExisting (srcImg : SourceImage) (rbc : RBC) (frame : RBCFrame) (frameThickness : float) (fontSize : float) (isTextBelow : bool) : RBCFrame =
         frame.Visibility <- Visibility.Visible
-        frame.Height <- rbc.size.Height
         frame.Width <- rbc.size.Width
+        frame.Height <- rbc.size.Height
         frame.Tag <- rbc
+        frame.IsTextBelow <- isTextBelow
         setRBCFrameStyle srcImg rbc frame
-        frame.border.StrokeThickness <- 1.
+        frame.border.StrokeThickness <- frameThickness
         frame.txtRBCNumber.Text <- string rbc.num
+        frame.txtRBCNumber.FontSize <- fontSize
         frame
 
     let updateDocumentStatus () =
@@ -70,25 +74,26 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
     let statusMessageTimer = Threading.DispatcherTimer ()
     statusMessageTimer.Tick.AddHandler (fun obj args -> statusMessageTimer.Stop (); win.txtMessageStatus.Text <- "")
     statusMessageTimer.Interval <- TimeSpan (0, 0, 2)
+
+    // To show a use message while a short period of time.
     let displayStatusMessage (message : string) =
         win.txtMessageStatus.Text <- message
         statusMessageTimer.Stop ()
         statusMessageTimer.Start ()
 
-    let highlightRBCFrame (frame : Views.RBCFrame) (highlight : bool) =
+    let highlightRBCFrame (frame : RBCFrame) (highlight : bool) =
         let rbc = frame.Tag :?> RBC
         if highlight then
-            frame.border.StrokeThickness <- 3.
+            frame.border.StrokeThickness <- 3. * frame.border.StrokeThickness
             if not rbc.infected && not rbc.setManually && not displayHealthy then frame.Opacity <- 1.
         else
-            frame.border.StrokeThickness <- 1.
+            frame.border.StrokeThickness <- frame.border.StrokeThickness / 3.
             if not rbc.infected && not rbc.setManually && not displayHealthy then frame.Opacity <- 0.
 
     let zoomToRBC (rbc : RBC) =
         win.scrollViewCurrentImage.ScrollToHorizontalOffset (rbc.center.X * currentScale - win.scrollViewCurrentImage.ViewportWidth / 2. + win.borderCurrentImage.BorderThickness.Left)
         win.scrollViewCurrentImage.ScrollToVerticalOffset (rbc.center.Y * currentScale - win.scrollViewCurrentImage.ViewportHeight / 2. + win.borderCurrentImage.BorderThickness.Top)
 
-
     let txtImageName_TextChanged =
         TextChangedEventHandler (fun obj args -> state.CurrentImage |> Option.iter (fun srcImg -> state.SetName srcImg win.txtImageName.Text))
 
@@ -101,24 +106,24 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
         match state.CurrentImage with
         | Some srcImg ->
             win.gridImageInformation.Visibility <- Visibility.Visible
-            win.txtImageName.Text <- srcImg.name
+            win.txtImageName.Text <- srcImg.Name
             win.txtImageName.TextChanged.AddHandler txtImageName_TextChanged
 
             // The left part.
-            let parasitemiaStr = Utils.percentText (state.ImageParasitemia srcImg)
+            let parasitemiaStr = Utils.percentText srcImg.ImageParasitemia
             win.txtImageInformation1.Inlines.Add (Documents.Run ("Parasitemia: ", FontWeight = FontWeights.Bold))
             win.txtImageInformation1.Inlines.Add parasitemiaStr
             win.txtImageInformation1.Inlines.Add (Documents.LineBreak ())
 
             win.txtImageInformation1.Inlines.Add (Documents.Run ("Last analysis: ", FontWeight = FontWeights.Bold))
-            win.txtImageInformation1.Inlines.Add (Documents.Run (if srcImg.dateLastAnalysis.Ticks = 0L then "<Never>" else string (srcImg.dateLastAnalysis.ToLocalTime())))
+            win.txtImageInformation1.Inlines.Add (Documents.Run (if srcImg.DateLastAnalysis.Ticks = 0L then "<Never>" else string (srcImg.DateLastAnalysis.ToLocalTime())))
 
             // The right part part.
             win.txtImageInformation2.Inlines.Add (Documents.Run ("Added infected erythrocyte: ", FontWeight = FontWeights.Bold))
-            win.txtImageInformation2.Inlines.Add (Documents.Run ((state.ImageNbManuallyChangedRBCStr srcImg true) + " " + (state.ImageManuallyChangedRBCStr srcImg true)))
+            win.txtImageInformation2.Inlines.Add (Documents.Run ((srcImg.ImageNbManuallyChangedRBCStr true) + " " + (srcImg.ImageManuallyChangedRBCStr true)))
             win.txtImageInformation2.Inlines.Add (Documents.LineBreak ())
             win.txtImageInformation2.Inlines.Add (Documents.Run ("Removed infected erythrocyte: ", FontWeight = FontWeights.Bold))
-            win.txtImageInformation2.Inlines.Add (Documents.Run ((state.ImageNbManuallyChangedRBCStr srcImg false) + " " + (state.ImageManuallyChangedRBCStr srcImg false)))
+            win.txtImageInformation2.Inlines.Add (Documents.Run ((srcImg.ImageNbManuallyChangedRBCStr false) + " " + (srcImg.ImageManuallyChangedRBCStr false)))
 
         | _ ->
             win.gridImageInformation.Visibility <- Visibility.Hidden
@@ -137,7 +142,7 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
             )
 
     let updateViewportPreview () =
-        for preview in win.stackPreviews.Children |> Seq.cast<Views.ImageSourcePreview> do
+        for preview in win.stackPreviews.Children |> Seq.cast<ImageSourcePreview> do
             let srcImg = preview.Tag :?> SourceImage
             if Some srcImg = state.CurrentImage then
                 preview.viewport.Visibility <- Visibility.Visible
@@ -165,7 +170,7 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
     let rec setAsInfected (srcImg : SourceImage) (rbc : RBC) (infected : bool) =
         state.SetAsInfected rbc infected
         win.canvasCurrentImage.Children
-        |> Seq.cast<Views.RBCFrame>
+        |> Seq.cast<RBCFrame>
         |> Seq.iter (
             fun frame ->
                 if (frame.Tag :?> RBC) = rbc then
@@ -175,8 +180,8 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
         updateCurrentImageInformation ()
         updateGlobalParasitemia ()
 
-    and RBCFrame (srcImg : SourceImage) (rbc : RBC) : Views.RBCFrame =
-        let frame = RBCFrameFromExisting srcImg rbc (Views.RBCFrame ())
+    and createRBCFrame (srcImg : SourceImage) (rbc : RBC) (frameThickness : float) (fontSize : float) (isTextBelow : bool) : RBCFrame =
+        let frame = createRBCFrameFromExisting srcImg rbc (RBCFrame ()) frameThickness fontSize isTextBelow
         frame.SetValue (Panel.ZIndexProperty, Int32.MaxValue - rbc.num) // To be sure the
         frame.menuRBCSetAsHealthy.Click.AddHandler (fun obj args -> setAsInfected srcImg (frame.Tag :?> RBC) false)
         frame.menuRBCSetAsInfected.Click.AddHandler (fun obj args -> setAsInfected srcImg (frame.Tag :?> RBC) true)
@@ -198,12 +203,12 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
         match state.CurrentImage with
         | Some srcImg ->
             let mutable currentPreview = 0
-            for rbc in srcImg.rbcs |> List.filter (fun rbc -> displayHealthy || rbc.infected) do
+            for rbc in srcImg.RBCs |> List.filter (fun rbc -> displayHealthy || rbc.infected) do
                 let previewInfected =
                     if currentPreview < win.stackRBC.Children.Count then
-                        RBCFrameFromExisting srcImg rbc (win.stackRBC.Children.[currentPreview] :?> Views.RBCFrame)
+                        createRBCFrameFromExisting srcImg rbc (win.stackRBC.Children.[currentPreview] :?> RBCFrame) 1. 12. false
                     else
-                        let f = RBCFrame srcImg rbc
+                        let f = createRBCFrame srcImg rbc 1. 12. false
                         f.MouseLeftButtonUp.AddHandler (fun obj args -> zoomToRBC (f.Tag :?> RBC))
                         win.stackRBC.Children.Add f |> ignore
                         f
@@ -212,7 +217,7 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
 
                 previewInfected.Height <- win.stackRBC.ActualHeight
                 previewInfected.Width <- win.stackRBC.ActualHeight * rbc.size.Width / rbc.size.Height
-                previewInfected.border.Fill <- ImageBrush (BitmapSourceConvert.ToBitmapSource (extractRBCPreview srcImg.img rbc))
+                previewInfected.border.Fill <- ImageBrush (BitmapSourceConvert.ToBitmapSource (extractRBCPreview srcImg.Img rbc))
 
             win.stackRBC.Children.RemoveRange (currentPreview, win.stackRBC.Children.Count - currentPreview)
         | _ -> ()
@@ -223,12 +228,14 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
         match state.CurrentImage with
         | Some srcImg ->
             let mutable currentCanvas = 0
-            for rbc in srcImg.rbcs do
+            let strokeThickness = frameStrokeThickness srcImg.AverageRBCSize
+            let fontSize = frameFontSize srcImg.AverageRBCSize
+            for rbc in srcImg.RBCs do
                 let frame =
                     if currentCanvas < win.canvasCurrentImage.Children.Count then
-                        RBCFrameFromExisting srcImg rbc (win.canvasCurrentImage.Children.[currentCanvas] :?> Views.RBCFrame)
+                        createRBCFrameFromExisting srcImg rbc (win.canvasCurrentImage.Children.[currentCanvas] :?> RBCFrame) strokeThickness fontSize true
                     else
-                        let f = RBCFrame srcImg rbc
+                        let f = createRBCFrame srcImg rbc strokeThickness fontSize true
                         win.canvasCurrentImage.Children.Add f |> ignore
                         f
 
@@ -293,12 +300,12 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
 
             // Highlight the preview.
             win.stackPreviews.Children
-            |> Seq.cast<Views.ImageSourcePreview>
+            |> Seq.cast<ImageSourcePreview>
             |> Seq.iter (fun preview -> preview.border.BorderThickness <- Thickness (if preview.Tag = (srcImg :> Object) then 3. else 0.))
 
-            win.canvasCurrentImage.Height <- float srcImg.img.Height
-            win.canvasCurrentImage.Width <- float srcImg.img.Width
-            win.canvasCurrentImage.Background <- ImageBrush (BitmapSourceConvert.ToBitmapSource (srcImg.img))
+            win.canvasCurrentImage.Height <- float srcImg.Img.Height
+            win.canvasCurrentImage.Width <- float srcImg.Img.Width
+            win.canvasCurrentImage.Background <- ImageBrush (BitmapSourceConvert.ToBitmapSource (srcImg.Img))
 
             updateRBCFramesCurrent ()
             updateRBCFramesPreview ()
@@ -318,7 +325,7 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
             updateCurrentImage ()
 
     let addPreview (srcImg : SourceImage) =
-        let imgCtrl = Views.ImageSourcePreview (Margin = Thickness 3.)
+        let imgCtrl = ImageSourcePreview (Margin = Thickness 3.)
 
         imgCtrl.menuRemoveImage.Click.AddHandler (
             fun obj args ->
@@ -332,14 +339,14 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
                 updateGlobalParasitemia ()
 
                 // Update image numbers.
-                win.stackPreviews.Children |> Seq.cast<Views.ImageSourcePreview> |> Seq.iter (fun imgPreview -> imgPreview.txtImageNumber.Text <- (imgPreview.Tag :?> SourceImage).num.ToString ())
+                win.stackPreviews.Children |> Seq.cast<ImageSourcePreview> |> Seq.iter (fun imgPreview -> imgPreview.txtImageNumber.Text <- (imgPreview.Tag :?> SourceImage).RomanNum)
         )
 
         imgCtrl.Tag <- srcImg
-        imgCtrl.txtImageNumber.Text <- string srcImg.num
+        imgCtrl.txtImageNumber.Text <- string srcImg.RomanNum
         let width = 200
-        let height = srcImg.img.Height * width / srcImg.img.Width
-        imgCtrl.imagePreview.Source <- BitmapSourceConvert.ToBitmapSource (srcImg.img.Resize (width, height, Emgu.CV.CvEnum.Inter.Cubic))
+        let height = srcImg.Img.Height * width / srcImg.Img.Width
+        imgCtrl.imagePreview.Source <- BitmapSourceConvert.ToBitmapSource (srcImg.Img.Resize (width, height, Emgu.CV.CvEnum.Inter.Cubic))
         win.stackPreviews.Children.Add imgCtrl |> ignore
 
         // Zoom to a mouse position into the control 'imgCtrl'.
@@ -474,6 +481,8 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
 
     win.menuAbout.Click.AddHandler (fun obj args -> About.showWindow win)
 
+    win.menuCommandLineArguments.Click.AddHandler (fun obj args -> CommandLineArguments.showWindow win)
+
     win.Closing.AddHandler (fun obj args -> askSaveCurrent ())
 
     // Zoom on the current image.
@@ -512,7 +521,7 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
     let currentImageScaleTransform = ScaleTransform ()
     win.canvasCurrentImage.LayoutTransform <- currentImageScaleTransform
     win.borderCurrentImage.PreviewMouseWheel.AddHandler (
-        fun obj args ->
+        fun _obj args ->
             let scaleFactor = if args.Delta > 0 then 2.0 else 0.5
             if scaleFactor > 1. && currentScale < maxScale || scaleFactor < 1. && currentScale > minScale then
                 let previousScale = currentScale
@@ -538,7 +547,7 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
     let mutable scrollStartOffsetX = 0.
     let mutable scrollStartOffsetY = 0.
     win.borderCurrentImage.PreviewMouseLeftButtonDown.AddHandler (
-        fun obj args ->
+        fun _obj args ->
             scrollStartPosition <- args.GetPosition win.scrollViewCurrentImage
             scrollStartOffsetX <- win.scrollViewCurrentImage.HorizontalOffset
             scrollStartOffsetY <- win.scrollViewCurrentImage.VerticalOffset
@@ -548,7 +557,7 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
     )
 
     win.borderCurrentImage.PreviewMouseMove.AddHandler (
-        fun obj args ->
+        fun _obj args ->
             if win.borderCurrentImage.IsMouseCaptured then
                 let position = args.GetPosition win.scrollViewCurrentImage
                 let deltaX = scrollStartPosition.X - position.X
@@ -560,7 +569,7 @@ let run (defaultConfig : Config) (fileToOpen : string option) =
     )
 
     win.borderCurrentImage.PreviewMouseLeftButtonUp.AddHandler (
-        fun obj args ->
+        fun _obj args ->
             if win.borderCurrentImage.IsMouseCaptured then
                 win.borderCurrentImage.Cursor <- Input.Cursors.Arrow
                 win.borderCurrentImage.ReleaseMouseCapture ()