From: Greg Burri Date: Wed, 18 Oct 2017 15:00:54 +0000 (+0200) Subject: Frame width depends now from the RBC sizes #275 X-Git-Tag: 1.0.13~17^2~2 X-Git-Url: http://git.euphorik.ch/?p=master-thesis.git;a=commitdiff_plain;h=d3f9cd7b16d25f49bd8d06394b0f1d4040809fbd Frame width depends now from the RBC sizes #275 --- diff --git a/Parasitemia/ParasitemiaCore/Analysis.fs b/Parasitemia/ParasitemiaCore/Analysis.fs index 8b16c45..19a92fe 100644 --- a/Parasitemia/ParasitemiaCore/Analysis.fs +++ b/Parasitemia/ParasitemiaCore/Analysis.fs @@ -27,7 +27,7 @@ open Types /// The first call returning 'false' will cancel the analysis. /// The 'int' parameter correspond to the progression from 0 to 100 /// A list of detected cells or nothing if the process has been cancelled -let doAnalysis (img : Image) (name : string) (config : Config) (reportProgress : (int -> bool) option) : Cell list option = +let doAnalysis (img : Image) (name : string) (config : Config) (reportProgress : (int -> bool) option) : AnalysisResult option = // To report the progress of this function from 0 to 100. // Return 'None' if the process must be aborted. @@ -153,7 +153,14 @@ let doAnalysis (img : Image) (name : string) (config : Config) (repor IO.saveImg img_float.[0] (buildFileName " - source - blue.png") | _ -> () - return cells + return + { + Cells = cells + RBCSize_μm = config.RBCRadius.μm + RBCSize_px = config.RBCRadius.Pixel + } + + //return cells } /// @@ -164,7 +171,7 @@ let doAnalysis (img : Image) (name : string) (config : Config) (repor /// The first call returning 'false' will cancel the analysis. /// The 'int' parameter correspond to the progression from 0 to 100 /// 'None' if the process has been cancelled or the list of result as (name * cells), 'name' corresponds to the given name -let doMultipleAnalysis (imgs : (string * Config * Image) list) (reportProgress : (int -> bool) option) : (string * Cell list) list option = +let doMultipleAnalysis (imgs : (string * Config * Image) list) (reportProgress : (int -> bool) option) : (string * AnalysisResult) list option = let report (percent : int) : bool = match reportProgress with | Some f -> f percent diff --git a/Parasitemia/ParasitemiaCore/AssemblyInfo.fs b/Parasitemia/ParasitemiaCore/AssemblyInfo.fs index b7c1930..e5eace6 100644 --- a/Parasitemia/ParasitemiaCore/AssemblyInfo.fs +++ b/Parasitemia/ParasitemiaCore/AssemblyInfo.fs @@ -34,8 +34,8 @@ open System.Runtime.InteropServices // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [] -[] -[] +[] +[] do () \ No newline at end of file diff --git a/Parasitemia/ParasitemiaCore/Types.fs b/Parasitemia/ParasitemiaCore/Types.fs index 8f526f9..0cb2768 100644 --- a/Parasitemia/ParasitemiaCore/Types.fs +++ b/Parasitemia/ParasitemiaCore/Types.fs @@ -8,6 +8,7 @@ open Emgu.CV open Emgu.CV.Structure open Const +open UnitsOfMeasure type Points = HashSet @@ -102,4 +103,11 @@ type ResultBuilder () = member this.ReturnFrom (x) = x -let result = ResultBuilder () \ No newline at end of file +let result = ResultBuilder () + +type AnalysisResult = + { + Cells : Cell list + RBCSize_μm : float<μm> + RBCSize_px : float32 + } \ No newline at end of file diff --git a/Parasitemia/ParasitemiaUI/Analysis.fs b/Parasitemia/ParasitemiaUI/Analysis.fs index 4a75a91..4fa5290 100644 --- a/Parasitemia/ParasitemiaUI/Analysis.fs +++ b/Parasitemia/ParasitemiaUI/Analysis.fs @@ -57,13 +57,13 @@ let showWindow (parent : Window) (state : State.State) : bool = let imageSourceSelection = Views.ImageSourceSelection (Tag = srcImg, Margin = Thickness 3.) imageSourceSelection.Tag <- srcImg - imageSourceSelection.txtImageNumber.Text <- string srcImg.num - let height = srcImg.img.Height * width / srcImg.img.Width - imageSourceSelection.imagePreview.Source <- BitmapSourceConvert.ToBitmapSource (srcImg.img.Resize (width, height, Emgu.CV.CvEnum.Inter.Cubic)) - imageSourceSelection.chkSelection.IsChecked <- Nullable (srcImg.dateLastAnalysis.Ticks = 0L) - imageSourceSelection.lblDateLastAnalysis.Content <- if srcImg.dateLastAnalysis.Ticks = 0L then "" else string srcImg.dateLastAnalysis + imageSourceSelection.txtImageNumber.Text <- string srcImg.Num + let height = srcImg.Img.Height * width / srcImg.Img.Width + imageSourceSelection.imagePreview.Source <- BitmapSourceConvert.ToBitmapSource (srcImg.Img.Resize (width, height, Emgu.CV.CvEnum.Inter.Cubic)) + imageSourceSelection.chkSelection.IsChecked <- Nullable (srcImg.DateLastAnalysis.Ticks = 0L) + imageSourceSelection.lblDateLastAnalysis.Content <- if srcImg.DateLastAnalysis.Ticks = 0L then "" else string srcImg.DateLastAnalysis - imageSourceSelection.txtResolution.Text <- if srcImg.dateLastAnalysis.Ticks = 0L then "" else string srcImg.config.Parameters.resolution + imageSourceSelection.txtResolution.Text <- if srcImg.DateLastAnalysis.Ticks = 0L then "" else string srcImg.Config.Parameters.resolution for ppi in Utils.predefinedPPI do let menu = MenuItem () @@ -103,9 +103,9 @@ let showWindow (parent : Window) (state : State.State) : bool = let isChecked = srcImgCtrl.chkSelection.IsChecked match parseAndValidatePPI srcImgCtrl.txtResolution.Text with | Some resolution -> - yield Some (srcImg, isChecked.HasValue && isChecked.Value, { srcImg.config.Parameters with resolution = resolution * 1. }) + yield Some (srcImg, isChecked.HasValue && isChecked.Value, { srcImg.Config.Parameters with resolution = resolution * 1. }) | None -> - MessageBox.Show (sprintf "No resolution defined for the image number %d" srcImg.num, "No resolution defined", MessageBoxButton.OK, MessageBoxImage.Information) |> ignore + MessageBox.Show (sprintf "No resolution defined for the image number %d" srcImg.Num, "No resolution defined", MessageBoxButton.OK, MessageBoxImage.Information) |> ignore yield None } |> Seq.takeWhile (fun e -> e.IsSome) |> Seq.map (fun e -> e.Value) |> List.ofSeq @@ -123,9 +123,9 @@ let showWindow (parent : Window) (state : State.State) : bool = let imagesToProcess = [ for srcImg, selected, parameters in imagesParameters do - srcImg.config.Parameters <- parameters // Save parameters. + srcImg.Config.Parameters <- parameters // Save parameters. if selected then - yield string srcImg.num, srcImg.config, srcImg.img + yield string srcImg.Num, srcImg.Config, srcImg.Img ] if imagesToProcess.IsEmpty then diff --git a/Parasitemia/ParasitemiaUI/AssemblyInfo.fs b/Parasitemia/ParasitemiaUI/AssemblyInfo.fs index 799a4c6..3fdcd4b 100644 --- a/Parasitemia/ParasitemiaUI/AssemblyInfo.fs +++ b/Parasitemia/ParasitemiaUI/AssemblyInfo.fs @@ -34,8 +34,8 @@ open System.Runtime.InteropServices // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [] -[] -[] +[] +[] do () \ No newline at end of file diff --git a/Parasitemia/ParasitemiaUI/Export.fs b/Parasitemia/ParasitemiaUI/Export.fs index 52638d3..805bcd7 100644 --- a/Parasitemia/ParasitemiaUI/Export.fs +++ b/Parasitemia/ParasitemiaUI/Export.fs @@ -17,8 +17,8 @@ let exportResults (state : State) (filePath : string) = for srcImg in state.SourceImages do fprintfn writer "" - fprintfn writer "Image name: %s" srcImg.name - fprintfn writer "Parasitemia: %s" (Utils.percentText (state.ImageParasitemia srcImg)) - fprintfn writer "Added infected erythrocyte: %s %s" (state.ImageNbManuallyChangedRBCStr srcImg true) (state.ImageManuallyChangedRBCStr srcImg true) - fprintfn writer "Removed infected erythrocyte: %s %s" (state.ImageNbManuallyChangedRBCStr srcImg false) (state.ImageManuallyChangedRBCStr srcImg false) + fprintfn writer "Image name: %s" srcImg.Name + fprintfn writer "Parasitemia: %s" (Utils.percentText srcImg.ImageParasitemia) + fprintfn writer "Added infected erythrocyte: %s %s" (srcImg.ImageNbManuallyChangedRBCStr true) (srcImg.ImageManuallyChangedRBCStr true) + fprintfn writer "Removed infected erythrocyte: %s %s" (srcImg.ImageNbManuallyChangedRBCStr false) (srcImg.ImageManuallyChangedRBCStr false) () \ No newline at end of file diff --git a/Parasitemia/ParasitemiaUI/GUI.fs b/Parasitemia/ParasitemiaUI/GUI.fs index 46171a7..4eee1e5 100644 --- a/Parasitemia/ParasitemiaUI/GUI.fs +++ b/Parasitemia/ParasitemiaUI/GUI.fs @@ -54,14 +54,21 @@ let run (defaultConfig : Config) (fileToOpen : string option) = 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 / 8.) + + let RBCFrameFromExisting (srcImg : SourceImage) (rbc : RBC) (frame : Views.RBCFrame) (frameThickness : float) (fontSize : float) : Views.RBCFrame = frame.Visibility <- Visibility.Visible - frame.Height <- rbc.size.Height frame.Width <- rbc.size.Width + frame.Height <- rbc.size.Height frame.Tag <- rbc 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,6 +77,8 @@ 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 () @@ -78,17 +87,16 @@ let run (defaultConfig : Config) (fileToOpen : string option) = let highlightRBCFrame (frame : Views.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 +109,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 "" else string (srcImg.dateLastAnalysis.ToLocalTime()))) + win.txtImageInformation1.Inlines.Add (Documents.Run (if srcImg.DateLastAnalysis.Ticks = 0L then "" 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 @@ -175,8 +183,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 RBCFrame (srcImg : SourceImage) (rbc : RBC) (frameThickness : float) (fontSize : float) : Views.RBCFrame = + let frame = RBCFrameFromExisting srcImg rbc (Views.RBCFrame ()) frameThickness fontSize 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 +206,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) + RBCFrameFromExisting srcImg rbc (win.stackRBC.Children.[currentPreview] :?> Views.RBCFrame) 1. 12. else - let f = RBCFrame srcImg rbc + let f = RBCFrame srcImg rbc 1. 12. f.MouseLeftButtonUp.AddHandler (fun obj args -> zoomToRBC (f.Tag :?> RBC)) win.stackRBC.Children.Add f |> ignore f @@ -212,7 +220,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 +231,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) + RBCFrameFromExisting srcImg rbc (win.canvasCurrentImage.Children.[currentCanvas] :?> Views.RBCFrame) strokeThickness fontSize else - let f = RBCFrame srcImg rbc + let f = RBCFrame srcImg rbc strokeThickness fontSize win.canvasCurrentImage.Children.Add f |> ignore f @@ -296,9 +306,9 @@ let run (defaultConfig : Config) (fileToOpen : string option) = |> Seq.cast |> 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 () @@ -332,14 +342,14 @@ let run (defaultConfig : Config) (fileToOpen : string option) = updateGlobalParasitemia () // Update image numbers. - win.stackPreviews.Children |> Seq.cast |> Seq.iter (fun imgPreview -> imgPreview.txtImageNumber.Text <- (imgPreview.Tag :?> SourceImage).num.ToString ()) + win.stackPreviews.Children |> Seq.cast |> Seq.iter (fun imgPreview -> imgPreview.txtImageNumber.Text <- (imgPreview.Tag :?> SourceImage).Num.ToString ()) ) imgCtrl.Tag <- srcImg - imgCtrl.txtImageNumber.Text <- string srcImg.num + imgCtrl.txtImageNumber.Text <- string srcImg.Num 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'. @@ -512,7 +522,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 +548,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 +558,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 +570,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 () diff --git a/Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj b/Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj index 0324219..b9ad387 100644 --- a/Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj +++ b/Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj @@ -86,6 +86,7 @@ + diff --git a/Parasitemia/ParasitemiaUI/PiaZ.fs b/Parasitemia/ParasitemiaUI/PiaZ.fs index 92ebbe0..df72212 100644 --- a/Parasitemia/ParasitemiaUI/PiaZ.fs +++ b/Parasitemia/ParasitemiaUI/PiaZ.fs @@ -68,23 +68,23 @@ let save (filePath : string) (data : DocumentData) = // Write each images and the associated information. for srcImg in data.images do - let imgFilename = (string srcImg.num) + imageExtension + let imgFilename = (string srcImg.Num) + imageExtension let imgEntry = file.CreateEntry (imgFilename, CompressionLevel.NoCompression) // FIXME: It seems a compression is applied to this file despite of the 'NoCompression' flag. - srcImg.img.ToBitmap().Save (imgEntry.Open (), System.Drawing.Imaging.ImageFormat.Tiff) + srcImg.Img.ToBitmap().Save (imgEntry.Open (), System.Drawing.Imaging.ImageFormat.Tiff) let imgJSONEntry = file.CreateEntry (imgFilename + ".json", CompressionLevel.Fastest) use imgJSONFileWriter = new StreamWriter (imgJSONEntry.Open ()) imgJSONFileWriter.Write ( JsonConvert.SerializeObject ( { - num = srcImg.num - name = srcImg.name - RBCRadius = srcImg.config.RBCRadius.Pixel - parameters = srcImg.config.Parameters - dateLastAnalysis = srcImg.dateLastAnalysis - rbcs = srcImg.rbcs - healthyRBCBrightness = srcImg.healthyRBCBrightness - infectedRBCBrightness = srcImg.infectedRBCBrightness + num = srcImg.Num + name = srcImg.Name + RBCRadius = srcImg.Config.RBCRadius.Pixel + parameters = srcImg.Config.Parameters + dateLastAnalysis = srcImg.DateLastAnalysis + rbcs = srcImg.RBCs + healthyRBCBrightness = srcImg.HealthyRBCBrightness + infectedRBCBrightness = srcImg.InfectedRBCBrightness } ) ) @@ -93,7 +93,7 @@ let updateDocumentData (fromVersion : int) (toVersion : int) (data : DocumentDat for v in fromVersion + 1 .. toVersion do match v with | 1 -> // Version 0 -> 1 : set initial brightness for rbc. - data.images |> List.iter (fun i -> i.healthyRBCBrightness <- 1.f; i.infectedRBCBrightness <- 1.f) + data.images |> List.iter (fun i -> i.HealthyRBCBrightness <- 1.f; i.InfectedRBCBrightness <- 1.f) | _ -> () data @@ -132,16 +132,7 @@ let load (filePath : string) (defaultConfig : ParasitemiaCore.Config.Config) : D } config.SetRBCRadius imgInfo.RBCRadius - yield - { - num = imgNum - name = imgInfo.name - config = config - dateLastAnalysis = imgInfo.dateLastAnalysis - img = img - rbcs = imgInfo.rbcs - healthyRBCBrightness = imgInfo.healthyRBCBrightness - infectedRBCBrightness = imgInfo.infectedRBCBrightness - } + + yield SourceImage (imgNum, imgInfo.name, config, imgInfo.dateLastAnalysis, img, imgInfo.rbcs, HealthyRBCBrightness = imgInfo.healthyRBCBrightness, InfectedRBCBrightness = imgInfo.infectedRBCBrightness) ] } \ No newline at end of file diff --git a/Parasitemia/ParasitemiaUI/Program.fs b/Parasitemia/ParasitemiaUI/Program.fs index b7b4202..87e1c26 100644 --- a/Parasitemia/ParasitemiaUI/Program.fs +++ b/Parasitemia/ParasitemiaUI/Program.fs @@ -99,10 +99,10 @@ let main args = fun () -> match ParasitemiaCore.Analysis.doMultipleAnalysis images None with | Some results -> - for id, cells in results do + for id, result in results do let config, img = images |> List.pick (fun (id', config', img') -> if id' = id then Some (config', img') else None) img.Dispose () - let total, infected = countCells cells + let total, infected = countCells result.Cells fprintf resultFile "File: %s %d %d %.2f (diameter: %O)\n" id total infected (100. * (float infected) / (float total)) config.RBCRadius | None -> fprintf resultFile "Analysis aborted" diff --git a/Parasitemia/ParasitemiaUI/SourceImage.fs b/Parasitemia/ParasitemiaUI/SourceImage.fs new file mode 100644 index 0000000..2330227 --- /dev/null +++ b/Parasitemia/ParasitemiaUI/SourceImage.fs @@ -0,0 +1,88 @@ +namespace ParasitemiaUI + +open System +open System.Windows +open System.Windows.Media + +open Emgu.CV +open Emgu.CV.Structure + +open Types + +type SourceImage (num : int, name : string, config : ParasitemiaCore.Config.Config, dateLastAnalysis : DateTime, img : Image, rbcs : RBC list) = + let mutable num = num + let mutable name = name + let mutable config = config + let mutable dateLastAnalysis = dateLastAnalysis // UTC. + let img = img + let mutable rbcs = rbcs + let mutable healthyRBCBrightness = 1.f + let mutable infectedRBCBrightness = 1.f + + let mutable averageRBCSize = 1. + + let healthyRBColor = Color.FromRgb (255uy, 255uy, 0uy) // Yellow-green. + let infectedRBColor = Color.FromRgb (255uy, 0uy, 40uy) // Red with a bit of blue. + + let updateAverageRBCSize () = + averageRBCSize <- + rbcs + |> List.collect (fun rbc -> [ rbc.size.Width; rbc.size.Height ]) + |> List.average + + do + updateAverageRBCSize () + + member this.Num with get () = num and set value = num <- value + + member this.Name with get () = name and set value = name <- value + + member this.Config = config + + member this.DateLastAnalysis with get () = dateLastAnalysis and set value = dateLastAnalysis <- value + + member this.Img = img + + member this.RBCs + with get () = rbcs + and set value = rbcs <- value + + member this.ImageParasitemia : int * int = + List.length rbcs, + rbcs |> List.fold (fun nbInfected rbc -> if rbc.infected then nbInfected + 1 else nbInfected) 0 + + member this.ImageNbManuallyChangedRBC (setAsInfected : bool) : int * int = + List.length rbcs, + rbcs |> List.fold (fun nb rbc -> if rbc.setManually && rbc.infected = setAsInfected then nb + 1 else nb) 0 + + member this.ImageNbManuallyChangedRBCStr (setAsInfected : bool) : string = + Utils.percentText (this.ImageNbManuallyChangedRBC setAsInfected) + + member this.ImageManuallyChangedRBC (setAsInfected : bool) : int seq = + query { + for rbc in rbcs do + where (rbc.setManually && rbc.infected = setAsInfected) + select rbc.num + } + + member this.ImageManuallyChangedRBCStr (setAsInfected : bool) : string = + let listStr = Utils.listAsStr <| this.ImageManuallyChangedRBC setAsInfected + if listStr = "" then + "" + else + "[" + listStr + "]" + + member this.HealthyRBCBrightness with get () = healthyRBCBrightness and set value = healthyRBCBrightness <- value + member this.InfectedRBCBrightness with get () = infectedRBCBrightness and set value = infectedRBCBrightness <- value + + member this.HealthyRBCColor : SolidColorBrush = + let mutable color = healthyRBColor * healthyRBCBrightness + color.A <- 255uy + SolidColorBrush (color) + + member this.InfectedRBCColor : SolidColorBrush = + let mutable color = infectedRBColor * infectedRBCBrightness + color.A <- 255uy + SolidColorBrush (color) + + member this.AverageRBCSize = averageRBCSize \ No newline at end of file diff --git a/Parasitemia/ParasitemiaUI/State.fs b/Parasitemia/ParasitemiaUI/State.fs index 3355a42..129e6a6 100644 --- a/Parasitemia/ParasitemiaUI/State.fs +++ b/Parasitemia/ParasitemiaUI/State.fs @@ -25,36 +25,11 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = alteredSinceLastSave <- true patientID <- id - member this.ImageParasitemia (srcImg : SourceImage) : int * int = - List.length srcImg.rbcs, - srcImg.rbcs |> List.fold (fun nbInfected rbc -> if rbc.infected then nbInfected + 1 else nbInfected) 0 - - member this.ImageNbManuallyChangedRBC (srcImg : SourceImage) (setAsInfected : bool) : int * int = - List.length srcImg.rbcs, - srcImg.rbcs |> List.fold (fun nb rbc -> if rbc.setManually && rbc.infected = setAsInfected then nb + 1 else nb) 0 - - member this.ImageNbManuallyChangedRBCStr (srcImg : SourceImage) (setAsInfected : bool) : string = - Utils.percentText (this.ImageNbManuallyChangedRBC srcImg setAsInfected) - - member this.ImageManuallyChangedRBC (srcImg : SourceImage) (setAsInfected : bool) : int seq = - query { - for rbc in srcImg.rbcs do - where (rbc.setManually && rbc.infected = setAsInfected) - select rbc.num - } - - member this.ImageManuallyChangedRBCStr (srcImg : SourceImage) (setAsInfected : bool) : string = - let listStr = Utils.listAsStr <| this.ImageManuallyChangedRBC srcImg setAsInfected - if listStr = "" then - "" - else - "[" + listStr + "]" - member this.GlobalParasitemia : int * int = sourceImages |> Seq.fold ( fun (nbTotal, nbTotalInfected) srcImg -> - let nb, nbInfected = this.ImageParasitemia srcImg + let nb, nbInfected = srcImg.ImageParasitemia nbTotal + nb, nbTotalInfected + nbInfected ) (0, 0) @@ -89,18 +64,7 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = /// /// If the image cannot be read member this.AddSourceImage (filePath : string) (defaultConfig : ParasitemiaCore.Config.Config) : SourceImage = - let srcImg = - { - num = sourceImages.Count + 1 - name = System.IO.FileInfo(filePath).Name - config = defaultConfig.Copy () - dateLastAnalysis = DateTime (0L) - rbcs = [] - img = new Image (filePath) - healthyRBCBrightness = 1.f - infectedRBCBrightness = 1.f - } - + let srcImg = SourceImage (sourceImages.Count + 1, System.IO.FileInfo(filePath).Name, defaultConfig.Copy (), DateTime (0L), new Image (filePath), []) sourceImages.Add srcImg if sourceImages.Count = 1 then this.CurrentImage <- Some sourceImages.[0] @@ -118,24 +82,24 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = if isCurrent then this.CurrentImage <- if sourceImages.Count > 0 then Some sourceImages.[0] else None // Re-numbered the images. - sourceImages |> Seq.iteri (fun i srcImg -> srcImg.num <- i + 1) + sourceImages |> Seq.iteri (fun i srcImg -> srcImg.Num <- i + 1) member this.SetName (srcImg : SourceImage) (name : string) = - if name <> srcImg.name then - srcImg.name <- name + if name <> srcImg.Name then + srcImg.Name <- name alteredSinceLastSave <- true - member this.SetResult (imgNum : int) (cells : ParasitemiaCore.Types.Cell list) = - let sourceImage = sourceImages.Find (fun srcImg -> srcImg.num = imgNum) + member this.SetResult (imgNum : int) (result : ParasitemiaCore.Types.AnalysisResult) = + let sourceImage = sourceImages.Find (fun srcImg -> srcImg.Num = imgNum) - let w = sourceImage.img.Width - let h = sourceImage.img.Height + let w = sourceImage.Img.Width + let h = sourceImage.Img.Height - sourceImage.dateLastAnalysis <- DateTime.UtcNow + sourceImage.DateLastAnalysis <- DateTime.UtcNow // To match with previously manually altered RBC. - let manuallyAlteredPreviousRBCS = sourceImage.rbcs |> List.filter (fun rbc -> rbc.setManually) - let tolerance = (float sourceImage.config.RBCRadius.Pixel) * 0.5 // +/-. + let manuallyAlteredPreviousRBCS = sourceImage.RBCs |> List.filter (fun rbc -> rbc.setManually) + let tolerance = (float sourceImage.Config.RBCRadius.Pixel) * 0.5 // +/-. let getPreviousManuallyAlteredRBC (center : Point) : RBC option = manuallyAlteredPreviousRBCS |> List.tryFind ( @@ -146,8 +110,8 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = rbc.center.Y < center.Y + tolerance ) - sourceImage.rbcs <- - cells + sourceImage.RBCs <- + result.Cells |> List.filter (fun cell -> match cell.cellClass with ParasitemiaCore.Types.HealthyRBC | ParasitemiaCore.Types.InfectedRBC -> true | _ -> false ) |> List.sortByDescending (fun cell -> cell.nucleusArea, (w - cell.center.X) + (h - cell.center.Y)) |> List.mapi ( diff --git a/Parasitemia/ParasitemiaUI/Types.fs b/Parasitemia/ParasitemiaUI/Types.fs index ada2a1f..3a3cf56 100644 --- a/Parasitemia/ParasitemiaUI/Types.fs +++ b/Parasitemia/ParasitemiaUI/Types.fs @@ -11,9 +11,6 @@ open Newtonsoft.Json open ParasitemiaCore.UnitsOfMeasure -let healthyRBColor = Color.FromRgb (255uy, 255uy, 0uy) // Yellow-green. -let infectedRBColor = Color.FromRgb (255uy, 0uy, 40uy) // Red with a bit of blue. - type RBC = { num : int @@ -29,30 +26,6 @@ type RBC = infectedArea : int } -type SourceImage = - { - mutable num : int - mutable name : string - - mutable config : ParasitemiaCore.Config.Config - mutable dateLastAnalysis : DateTime // UTC. - img : Image - mutable rbcs : RBC list - - mutable healthyRBCBrightness : float32 - mutable infectedRBCBrightness : float32 - } - with - member this.HealthyRBCColor : SolidColorBrush = - let mutable color = healthyRBColor * this.healthyRBCBrightness - color.A <- 255uy - SolidColorBrush (color) - - member this.InfectedRBCColor : SolidColorBrush = - let mutable color = infectedRBColor * this.infectedRBCBrightness - color.A <- 255uy - SolidColorBrush (color) - type PredefinedPPI = { ppi : int diff --git a/Parasitemia/ParasitemiaUI/Utils.fs b/Parasitemia/ParasitemiaUI/Utils.fs index 97c7425..8635bcd 100644 --- a/Parasitemia/ParasitemiaUI/Utils.fs +++ b/Parasitemia/ParasitemiaUI/Utils.fs @@ -49,7 +49,7 @@ let predefinedPPI : PredefinedPPI list = use file = new StreamReader (predefinedPPIFilepath) JsonConvert.DeserializeObject (file.ReadToEnd ()) with - | ex -> + | _ex -> savePredefinedPPIToFile defaultPredefinedPPI defaultPredefinedPPI @@ -58,6 +58,6 @@ let sensorSizes : SensorSize list = use file = new StreamReader (sensorSizesFilepath) JsonConvert.DeserializeObject (file.ReadToEnd ()) with - | ex -> + | _ex -> saveSensorSizesToFile defaultSensorSizes defaultSensorSizes diff --git a/Parasitemia/ParasitemiaUI/XAML/RBCFrame.xaml b/Parasitemia/ParasitemiaUI/XAML/RBCFrame.xaml index 124dc6f..41c1fc6 100644 --- a/Parasitemia/ParasitemiaUI/XAML/RBCFrame.xaml +++ b/Parasitemia/ParasitemiaUI/XAML/RBCFrame.xaml @@ -16,7 +16,7 @@ - + \ No newline at end of file