From f4fdf61ef98b913129ddb771392c0bcb1475e6fb Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Sun, 10 Jan 2016 12:50:39 +0100 Subject: [PATCH] GUI (work in progress..) --- Parasitemia/Parasitemia/GUI/GUI.fs | 75 +++++++++++++++------ Parasitemia/Parasitemia/GUI/MainWindow.xaml | 4 +- Parasitemia/Parasitemia/GUI/Pia.fs | 57 ++++++++++++---- Parasitemia/Parasitemia/GUI/State.fs | 4 +- Parasitemia/Parasitemia/GUI/Types.fs | 2 +- 5 files changed, 105 insertions(+), 37 deletions(-) diff --git a/Parasitemia/Parasitemia/GUI/GUI.fs b/Parasitemia/Parasitemia/GUI/GUI.fs index 6f11379..c6e6390 100644 --- a/Parasitemia/Parasitemia/GUI/GUI.fs +++ b/Parasitemia/Parasitemia/GUI/GUI.fs @@ -25,7 +25,8 @@ let run (defaultConfig: Config) = // Utils.log <- (fun m -> log mainWindow m) - let currentImageMargin = 0.1 // 10 %. + let colorRBCHealthy = Brushes.Green + let colorRBCInfected = Brushes.Yellow let state = State.State() @@ -53,8 +54,19 @@ let run (defaultConfig: Config) = canvasCurrentImage.Height <- float srcImg.img.Height canvasCurrentImage.Width <- float srcImg.img.Width canvasCurrentImage.Background <- ImageBrush(BitmapSourceConvert.ToBitmapSource(srcImg.img)) - (*for rbc in srcImg.rbcs do - let rectangle = Rectangle(Height = float rbc.size.Height, Width = float rbc.size.Width, )*) + + // Remove all image canvas children and add the RBC. + canvasCurrentImage.Children.Clear() + for rbc in (*srcImg.rbcs*) [{ num = 1; infected = true; addedManually = false; removed = false; center = Point(100., 100.); size = Size(40., 40.); stainArea = 10 }] do + let rectangle = + Rectangle( + Height = rbc.size.Height, + Width = rbc.size.Width, + Stroke = (if rbc.infected then colorRBCInfected else colorRBCHealthy), + StrokeThickness = 1.) + Canvas.SetLeft(rectangle, rbc.center.X + rbc.size.Width / 2.) + Canvas.SetTop(rectangle, rbc.center.Y + rbc.size.Height / 2.) + canvasCurrentImage.Children.Add(rectangle) |> ignore let addPreview (srcImg: SourceImage) = let imgCtrl = Views.ImageSourcePreview(Margin = Thickness(3.)) @@ -66,8 +78,11 @@ let run (defaultConfig: Config) = let updatePreviews () = stackPreviews.Children.Clear () - for srcImg in state.SourceImages do - addPreview srcImg + if state.SourceImages.Count() > 0 + then + for srcImg in state.SourceImages do + addPreview srcImg + setCurrentImage (state.SourceImages.First()) let updateGUI () = txtPatient.Text <- state.PatientID @@ -116,18 +131,37 @@ let run (defaultConfig: Config) = butStartAnalysis.Click.AddHandler(fun obj args -> ()) // Zoom on the current image. - let adjustCurrentImageMargins () = + let adjustCurrentImageBorders (deltaX: float) (deltaY: float) = borderCurrentImage.BorderThickness <- - Thickness(scrollViewCurrentImage.ViewportWidth / 2., scrollViewCurrentImage.ViewportHeight / 2., scrollViewCurrentImage.ViewportWidth / 2., scrollViewCurrentImage.ViewportHeight / 2.) - - scrollViewCurrentImage.Loaded.AddHandler(fun obj args -> adjustCurrentImageMargins ()) - scrollViewCurrentImage.SizeChanged.AddHandler(fun obj args -> adjustCurrentImageMargins ()) + Thickness( + (scrollViewCurrentImage.ViewportWidth + deltaX) / 2., + (scrollViewCurrentImage.ViewportHeight + deltaY) / 2., + (scrollViewCurrentImage.ViewportWidth + deltaX) / 2., + (scrollViewCurrentImage.ViewportHeight + deltaY) / 2.) + + canvasCurrentImage.SizeChanged.AddHandler(fun obj args -> + let deltaX = args.NewSize.Width - args.PreviousSize.Width + let deltaY = args.NewSize.Height - args.PreviousSize.Height + if deltaX > 0.5 || deltaY > 0.5 + then + adjustCurrentImageBorders 0.0 0.0 + // Center the view at the center of the image initialy. + scrollViewCurrentImage.UpdateLayout() + scrollViewCurrentImage.ScrollToHorizontalOffset(borderCurrentImage.ActualWidth / 2. - scrollViewCurrentImage.ViewportWidth / 2.) + scrollViewCurrentImage.ScrollToVerticalOffset(borderCurrentImage.ActualHeight / 2. - scrollViewCurrentImage.ViewportHeight / 2.)) + + scrollViewCurrentImage.SizeChanged.AddHandler(fun obj args -> + let deltaX = args.NewSize.Width - args.PreviousSize.Width + let deltaY = args.NewSize.Height - args.PreviousSize.Height + adjustCurrentImageBorders deltaX deltaY + scrollViewCurrentImage.ScrollToHorizontalOffset(scrollViewCurrentImage.HorizontalOffset + deltaX / 8.) + scrollViewCurrentImage.ScrollToVerticalOffset(scrollViewCurrentImage.VerticalOffset + deltaY / 8.)) let mutable currentScale = 1. let mutable maxScale = 5. let mutable minScale = 0.1 let currentImageScaleTransform = ScaleTransform() - borderCurrentImage.LayoutTransform <- currentImageScaleTransform + canvasCurrentImage.LayoutTransform <- currentImageScaleTransform borderCurrentImage.PreviewMouseWheel.AddHandler(fun obj args -> let scaleFactor = if args.Delta > 0 then 2.0 else 0.5 if scaleFactor > 1. && currentScale < maxScale || scaleFactor < 1. && currentScale > minScale @@ -138,15 +172,15 @@ let run (defaultConfig: Config) = if newScale > maxScale then maxScale elif newScale < minScale then minScale else newScale let realScaleFactor = currentScale / previousScale - let centerX = scrollViewCurrentImage.HorizontalOffset + scrollViewCurrentImage.ViewportWidth / 2. - canvasCurrentImage.Margin.Left - let centerY = scrollViewCurrentImage.VerticalOffset + scrollViewCurrentImage.ViewportHeight / 2. - canvasCurrentImage.Margin.Top + let centerX = scrollViewCurrentImage.HorizontalOffset + scrollViewCurrentImage.ViewportWidth / 2. - borderCurrentImage.BorderThickness.Left + let centerY = scrollViewCurrentImage.VerticalOffset + scrollViewCurrentImage.ViewportHeight / 2. - borderCurrentImage.BorderThickness.Top - canvasCurrentImage.Margin <- Thickness(canvasCurrentImage.Margin.Top * realScaleFactor) + //canvasCurrentImage.Margin <- Thickness(canvasCurrentImage.Margin.Top * realScaleFactor) currentImageScaleTransform.ScaleX <- currentScale currentImageScaleTransform.ScaleY <- currentScale - scrollViewCurrentImage.ScrollToHorizontalOffset(centerX * realScaleFactor - scrollViewCurrentImage.ViewportWidth / 2. + canvasCurrentImage.Margin.Left) - scrollViewCurrentImage.ScrollToVerticalOffset(centerY * realScaleFactor - scrollViewCurrentImage.ViewportHeight / 2. + canvasCurrentImage.Margin.Top) + scrollViewCurrentImage.ScrollToHorizontalOffset(centerX * realScaleFactor - scrollViewCurrentImage.ViewportWidth / 2. + borderCurrentImage.BorderThickness.Left) + scrollViewCurrentImage.ScrollToVerticalOffset(centerY * realScaleFactor - scrollViewCurrentImage.ViewportHeight / 2. + borderCurrentImage.BorderThickness.Top) args.Handled <- true) // Pan on the current image. @@ -178,14 +212,13 @@ let run (defaultConfig: Config) = borderCurrentImage.ReleaseMouseCapture() args.Handled <- true) - - (*let txtPatient: Controls.TextBox = ctrl "txtPatient" + (* let txtPatient: Controls.TextBox = ctrl "txtPatient" txtPatient.TextChanged.AddHandler(fun obj args -> - state.PatientID <- txtPatient.Text)*) + state.PatientID <- txtPatient.Text) *) - (*saveFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" ; + (* saveFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*" ; saveFileDialog1.FilterIndex = 2 ; - saveFileDialog1.RestoreDirectory = true ;*) + saveFileDialog1.RestoreDirectory = true ; *) // display mainWindow img mainWindow.Root.Show() diff --git a/Parasitemia/Parasitemia/GUI/MainWindow.xaml b/Parasitemia/Parasitemia/GUI/MainWindow.xaml index 9b710e5..281493b 100644 --- a/Parasitemia/Parasitemia/GUI/MainWindow.xaml +++ b/Parasitemia/Parasitemia/GUI/MainWindow.xaml @@ -1,5 +1,5 @@  + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Name="MainWindow" Height="681.888" Width="787.61" MinHeight="200" MinWidth="300"> @@ -55,7 +55,7 @@ - + diff --git a/Parasitemia/Parasitemia/GUI/Pia.fs b/Parasitemia/Parasitemia/GUI/Pia.fs index 432bad9..1821f26 100644 --- a/Parasitemia/Parasitemia/GUI/Pia.fs +++ b/Parasitemia/Parasitemia/GUI/Pia.fs @@ -1,7 +1,7 @@ // ParasitemIA file format. module Parasitemia.GUI.Pia -open System.Drawing +open System.Windows open System.IO open System.IO.Compression @@ -28,10 +28,10 @@ type JSONSourceImage = JsonProvider<""" "infected": true, "addedManually": false, "removed": false, - "posX" : 42, - "posY" : 42, - "width" : 10, - "height" : 10, + "posX" : 42.5, + "posY" : 42.5, + "width" : 10.5, + "height" : 10.5, "stainArea" : 10 } ] @@ -46,20 +46,38 @@ type JSONMainInformation = JsonProvider<""" """> let mainFilename = "info.json" +let imageExtension = ".tiff" let save (filePath: string) (data: FileData) = use file = ZipFile.Open(filePath, ZipArchiveMode.Update) - let mainJSON = JSONMainInformation.Root(data.patientID) - - let mainFile = - match file.GetEntry(mainFilename) with - | null -> file.CreateEntry(mainFilename) - | entry -> entry + for e in List.ofSeq file.Entries do // 'ofSeq' to not iterate a collection currently modified. + e.Delete() + // Main JSON file. + let mainJSON = JSONMainInformation.Root(data.patientID) + let mainFile = file.CreateEntry(mainFilename, CompressionLevel.Fastest) use mainFileWriter = new StreamWriter(mainFile.Open()) mainJSON.JsonValue.WriteTo(mainFileWriter, JsonSaveOptions.None) + // Write each images and the associated information. + for imgSrc in data.sources do + let imgFilename = (string imgSrc.num) + imageExtension + let imgEntry = file.CreateEntry(imgFilename, CompressionLevel.NoCompression) // FIXME: It seems a compression is applied to this file despite of the 'NoCompression' flag. + imgSrc.img.ToBitmap().Save(imgEntry.Open(), System.Drawing.Imaging.ImageFormat.Tiff) + + let imgJSON = + JSONSourceImage.Root([| for rbc in imgSrc.rbcs -> + JSONSourceImage.Rbc( + rbc.num, + rbc.infected, rbc.addedManually, rbc.removed, + decimal rbc.center.X, decimal rbc.center.Y, decimal rbc.size.Width, decimal rbc.size.Height, + rbc.stainArea) |]) + + let imgJSONEntry = file.CreateEntry(imgFilename + ".json", CompressionLevel.Fastest) + use imgJSONFileWriter = new StreamWriter(imgJSONEntry.Open()) + imgJSON.JsonValue.WriteTo(imgJSONFileWriter, JsonSaveOptions.None) + let load (filePath: string) : FileData = use file = ZipFile.Open(filePath, ZipArchiveMode.Read) @@ -67,4 +85,19 @@ let load (filePath: string) : FileData = let mainFile = file.GetEntry(mainFilename) let mainJSON = JSONMainInformation.Load(mainFile.Open()) - { sources = []; patientID = mainJSON.PatientId } \ No newline at end of file + let sources = [ + let mutable imgNum = 0 + for imgEntry in file.Entries do + let filename = imgEntry.Name + if filename.EndsWith(imageExtension) + then + let img = new Image(new System.Drawing.Bitmap(imgEntry.Open(), false)) // FIXME: Should we dispose the bitmap? + imgNum <- imgNum + 1 + let imgJSONEntry = file.GetEntry(filename + ".json") + let imgJSON = JSONSourceImage.Load(imgJSONEntry.Open()) + yield { num = imgNum; img = img; rbcs = [ for rbc in imgJSON.Rbcs -> + { num = rbc.Num; + infected = rbc.Infected; addedManually = rbc.AddedManually; removed = rbc.Removed; + center = Point(float rbc.PosX, float rbc.PosY); size = Size(float rbc.Width, float rbc.Height); + stainArea = rbc.StainArea } ] } ] + { sources = sources; patientID = mainJSON.PatientId } \ No newline at end of file diff --git a/Parasitemia/Parasitemia/GUI/State.fs b/Parasitemia/Parasitemia/GUI/State.fs index b35b331..4502536 100644 --- a/Parasitemia/Parasitemia/GUI/State.fs +++ b/Parasitemia/Parasitemia/GUI/State.fs @@ -15,12 +15,14 @@ type State () = member val PatientID: string = "" with get, set member this.Save () = - let data = { Pia.sources = []; Pia.patientID = this.PatientID } + let data = { Pia.sources = List.ofSeq sourceImages; Pia.patientID = this.PatientID } Pia.save this.FilePath data member this.Load () = let data = Pia.load this.FilePath this.PatientID <- data.patientID + sourceImages.Clear() + sourceImages.InsertRange(0, data.sources) member this.AddSourceImage (filePath: string) : SourceImage = let srcImg = { num = sourceImages.Count + 1; rbcs = []; img = new Image(filePath) } diff --git a/Parasitemia/Parasitemia/GUI/Types.fs b/Parasitemia/Parasitemia/GUI/Types.fs index 4f1260f..d6e9cf9 100644 --- a/Parasitemia/Parasitemia/GUI/Types.fs +++ b/Parasitemia/Parasitemia/GUI/Types.fs @@ -1,6 +1,6 @@ module Parasitemia.GUI.Types -open System.Drawing +open System.Windows open Emgu.CV open Emgu.CV.Structure -- 2.43.0