From 2d712781def419c9acc98368f7102b19b064f16d Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Sat, 1 Jul 2017 17:36:26 +0200 Subject: [PATCH 1/1] Update coding style. --- Parasitemia/Logger/AssemblyInfo.fs | 26 +- Parasitemia/Logger/Logger.fs | 109 ++--- Parasitemia/Parasitemia.sln | 5 +- Parasitemia/ParasitemiaCore/Analysis.fs | 39 +- Parasitemia/ParasitemiaCore/AssemblyInfo.fs | 26 +- Parasitemia/ParasitemiaCore/Classifier.fs | 79 ++-- Parasitemia/ParasitemiaCore/Config.fs | 20 +- Parasitemia/ParasitemiaCore/EEOver.fs | 6 +- Parasitemia/ParasitemiaCore/Ellipse.fs | 30 +- Parasitemia/ParasitemiaCore/Granulometry.fs | 21 +- Parasitemia/ParasitemiaCore/Heap.fs | 24 +- .../ParasitemiaCore/ImgTools/Drawing.fs | 28 +- Parasitemia/ParasitemiaCore/ImgTools/Edges.fs | 28 +- .../ParasitemiaCore/ImgTools/Histogram.fs | 12 +- Parasitemia/ParasitemiaCore/ImgTools/IO.fs | 6 +- .../ParasitemiaCore/ImgTools/ImgTools.fs | 20 +- .../ParasitemiaCore/ImgTools/Morpho.fs | 159 ++++--- Parasitemia/ParasitemiaCore/KMeans.fs | 18 +- Parasitemia/ParasitemiaCore/KMedians.fs | 30 +- Parasitemia/ParasitemiaCore/KdTree.fs | 12 +- .../ParasitemiaCore/MatchingEllipses.fs | 12 +- .../ParasitemiaCore/ParasitemiaCore.fsproj | 2 +- .../ParasitemiaCore/ParasitesMarker.fs | 22 +- Parasitemia/ParasitemiaCore/Types.fs | 16 +- Parasitemia/ParasitemiaCore/UnitsOfMeasure.fs | 8 +- Parasitemia/ParasitemiaCore/Utils.fs | 4 +- Parasitemia/ParasitemiaCore/packages.config | 2 +- Parasitemia/ParasitemiaUI/About.fs | 28 +- Parasitemia/ParasitemiaUI/Analysis.fs | 185 ++++---- Parasitemia/ParasitemiaUI/AssemblyInfo.fs | 26 +- Parasitemia/ParasitemiaUI/DPICalculator.fs | 20 +- Parasitemia/ParasitemiaUI/Export.fs | 2 +- Parasitemia/ParasitemiaUI/GUI.fs | 418 +++++++++--------- .../ParasitemiaUI/ParasitemiaUI.fsproj | 4 +- Parasitemia/ParasitemiaUI/PiaZ.fs | 44 +- Parasitemia/ParasitemiaUI/Program.fs | 54 +-- Parasitemia/ParasitemiaUI/State.fs | 34 +- Parasitemia/ParasitemiaUI/Types.fs | 10 +- Parasitemia/ParasitemiaUI/Utils.fs | 26 +- Parasitemia/ParasitemiaUI/packages.config | 4 +- .../ParasitemiaCore.Tests/AssemblyInfo.fs | 28 +- .../Tests/ParasitemiaCore.Tests/KdTree.fs | 20 +- .../ParasitemiaCore.Tests.fsproj | 2 +- .../ParasitemiaCore.Tests/packages.config | 2 +- 44 files changed, 858 insertions(+), 813 deletions(-) diff --git a/Parasitemia/Logger/AssemblyInfo.fs b/Parasitemia/Logger/AssemblyInfo.fs index c810d05..106b6af 100644 --- a/Parasitemia/Logger/AssemblyInfo.fs +++ b/Parasitemia/Logger/AssemblyInfo.fs @@ -7,22 +7,22 @@ open System.Runtime.InteropServices // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[] -[] -[] -[] -[] -[] -[] -[] +[] +[] +[] +[] +[] +[] +[] +[] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. -[] +[] // The following GUID is for the ID of the typelib if this project is exposed to COM -[] +[] // Version information for an assembly consists of the following four values: // @@ -33,9 +33,9 @@ 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/Logger/Logger.fs b/Parasitemia/Logger/Logger.fs index df2144f..4fc1736 100644 --- a/Parasitemia/Logger/Logger.fs +++ b/Parasitemia/Logger/Logger.fs @@ -33,9 +33,9 @@ type Log () = if isNull path then None else - let filename = path.Substring(path.LastIndexOf(Path.DirectorySeparatorChar) + 1) - let filenameWithoutExtension = filename.Remove(filename.IndexOf('.')) - match Int32.TryParse(filenameWithoutExtension) with + let filename = path.Substring (path.LastIndexOf Path.DirectorySeparatorChar + 1) + let filenameWithoutExtension = filename.Remove (filename.IndexOf '.') + match Int32.TryParse filenameWithoutExtension with | (true, n) -> Some n | _ -> None @@ -44,13 +44,13 @@ type Log () = let [] COMPRESS_ARCHIVED_FILES = true let [] FILENAME_FORMAT = "{0:D4}.log" let [] COMPRESSED_FILE_POSTFIX = ".gzip" - let encoding = Encoding.GetEncoding("UTF-8") + let encoding = Encoding.GetEncoding "UTF-8" let compress (filename : string) = - use inputStream = new FileStream(filename, FileMode.Open, FileAccess.Read) + use inputStream = new FileStream (filename, FileMode.Open, FileAccess.Read) let filenameCompressed = filename + COMPRESSED_FILE_POSTFIX - use compressedStream = new GZipStream(new FileStream(filenameCompressed, FileMode.Create, FileAccess.Write), CompressionLevel.Optimal) - inputStream.CopyTo(compressedStream) + use compressedStream = new GZipStream (new FileStream (filenameCompressed, FileMode.Create, FileAccess.Write), CompressionLevel.Optimal) + inputStream.CopyTo compressedStream let moduleName = System.Diagnostics.StackFrame(1).GetMethod().Module.Name @@ -59,9 +59,9 @@ type Log () = let mutable logDir : string = null - let monitor = Object() + let monitor = Object () - let listeners = List() + let listeners = List () let debug = #if DEBUG @@ -70,7 +70,7 @@ type Log () = false #endif - static let instance = new Log() + static let instance = new Log () let openLogFile (entryNumber : int64) = if not (isNull logDir) then @@ -79,40 +79,40 @@ type Log () = then if not (isNull stream) then - stream.Close() + stream.Close () if COMPRESS_ARCHIVED_FILES then compress filename - File.Delete(filename) + File.Delete filename // Search the last id among the log files. let mutable n = 1 - for existingFile in Directory.GetFiles(logDir) do + for existingFile in Directory.GetFiles logDir do match extractNumberFromLogfilepath existingFile with | Some n' when n' > n -> n <- n' | _ -> () - filename <- Path.Combine(logDir, String.Format(FILENAME_FORMAT, n)) + filename <- Path.Combine (logDir, String.Format (FILENAME_FORMAT, n)) try - if File.Exists(filename + COMPRESSED_FILE_POSTFIX) || FileInfo(filename).Length > MAX_SIZE_FILE + if File.Exists (filename + COMPRESSED_FILE_POSTFIX) || FileInfo(filename).Length > MAX_SIZE_FILE then - filename <- Path.Combine(logDir, String.Format(FILENAME_FORMAT, n + 1)) + filename <- Path.Combine (logDir, String.Format (FILENAME_FORMAT, n + 1)) with | :? FileNotFoundException -> () // The file may not exist. - stream <- new StreamWriter(filename, true, encoding) + stream <- new StreamWriter (filename, true, encoding) with - | ex -> Console.Error.WriteLine("Can't open the file log: {0}", ex) + | ex -> Console.Error.WriteLine ("Can't open the file log: {0}", ex) let write (msg : Message) (entryNumber : int64) = openLogFile entryNumber let header = - String.Format( + String.Format ( "{0:yyyy-MM-dd HH:mm:ss.fff} [{1}] {{{2}}} ({3})", - TimeZone.CurrentTimeZone.ToLocalTime(DateTime.UtcNow), + TimeZone.CurrentTimeZone.ToLocalTime DateTime.UtcNow, string msg.Severity, msg.ModuleCaller, - (if String.IsNullOrEmpty(msg.ThreadName) then string msg.ThreadId else sprintf "%s-%i" msg.ThreadName msg.ThreadId) + (if String.IsNullOrEmpty msg.ThreadName then string msg.ThreadId else sprintf "%s-%i" msg.ThreadName msg.ThreadId) ) for listener in listeners do @@ -121,17 +121,17 @@ type Log () = if not (isNull stream) then try - stream.WriteLine("{0} : {1}", header, msg.Message) - stream.Flush() + stream.WriteLine ("{0} : {1}", header, msg.Message) + stream.Flush () with - | :? IOException as ex -> Console.Error.WriteLine("Unable to write to the log file: {0}", ex) + | :? IOException as ex -> Console.Error.WriteLine ("Unable to write to the log file: {0}", ex) let writeAgent = - new MailboxProcessor( + new MailboxProcessor ( fun inbox -> let rec loop (nbEntries : int64) = async { - let! command = inbox.Receive() + let! command = inbox.Receive () match command with | Write message -> write message nbEntries @@ -143,29 +143,31 @@ type Log () = ) do - writeAgent.Start() + writeAgent.Start () let setLogDirectory (dir : string) = - lock monitor (fun () -> - logDir <- dir + lock monitor ( + fun () -> + logDir <- dir - if not <| isNull stream then - stream.Close() - stream <- null + if not <| isNull stream then + stream.Close () + stream <- null - try - if not <| Directory.Exists(logDir) - then - Directory.CreateDirectory(logDir) |> ignore - with - | _ -> Console.Error.WriteLine("Unable to create the log directory: {0}", logDir)) + try + if not <| Directory.Exists logDir + then + Directory.CreateDirectory logDir |> ignore + with + | _ -> Console.Error.WriteLine ("Unable to create the log directory: {0}", logDir) + ) interface IDisposable with member this.Dispose () = if not (isNull stream) then - stream.Dispose() - (writeAgent :> IDisposable).Dispose() + stream.Dispose () + (writeAgent :> IDisposable).Dispose () member private this.Write (message : string) (severity : Severity) = let moduleNameCaller = @@ -190,7 +192,7 @@ type Log () = /// Will stop and wait a reply. Used to flush the remaining messages. /// member private this.Stop () = - writeAgent.PostAndReply( + writeAgent.PostAndReply ( fun replyChannel -> Stop replyChannel ) @@ -203,23 +205,24 @@ type Log () = instance.LogDirectory <- dir member this.AddListener (listener : IListener) = - lock monitor (fun () -> - if not <| listeners.Contains(listener) - then - listeners.Add(listener)) + lock monitor ( + fun () -> + if not <| listeners.Contains listener + then + listeners.Add listener + ) member this.RmListener (listener : IListener) = - lock monitor (fun () -> - listeners.Remove(listener) |> ignore) + lock monitor (fun () -> listeners.Remove listener |> ignore) - static member AddListener (listener : IListener) = instance.AddListener(listener) - static member RmListener (listener : IListener) = instance.RmListener(listener) + static member AddListener (listener : IListener) = instance.AddListener listener + static member RmListener (listener : IListener) = instance.RmListener listener static member LogWithTime (severity : Severity) (f : unit -> 'a) (format : Printf.StringFormat<'b, 'a>) : 'b = - let sw = Stopwatch() - sw.Start() + let sw = Stopwatch () + sw.Start () let res = f () - sw.Stop() + sw.Stop () Printf.kprintf (fun s -> instance.Write (s + sprintf " (time: %d ms)" sw.ElapsedMilliseconds) severity; res) format static member Debug format = @@ -242,4 +245,4 @@ type Log () = Printf.kprintf (fun s -> instance.Write s Severity.FATAL) format static member Shutdown () = - instance.Stop() + instance.Stop () diff --git a/Parasitemia/Parasitemia.sln b/Parasitemia/Parasitemia.sln index ba7560b..42e8f4e 100644 --- a/Parasitemia/Parasitemia.sln +++ b/Parasitemia/Parasitemia.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26403.7 +VisualStudioVersion = 15.0.26430.6 MinimumVisualStudioVersion = 10.0.40219.1 Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ParasitemiaUI", "ParasitemiaUI\ParasitemiaUI.fsproj", "{70838E65-F211-44FC-B28F-0ED1CA6E850F}" EndProject @@ -68,4 +68,7 @@ Global GlobalSection(Performance) = preSolution HasPerformanceSessions = true EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal diff --git a/Parasitemia/ParasitemiaCore/Analysis.fs b/Parasitemia/ParasitemiaCore/Analysis.fs index f607760..8b16c45 100644 --- a/Parasitemia/ParasitemiaCore/Analysis.fs +++ b/Parasitemia/ParasitemiaCore/Analysis.fs @@ -49,7 +49,7 @@ let doAnalysis (img : Image) (name : string) (config : Config) (repor logWithName "Starting analysis ..." - use img_float = img.Convert() + use img_float = img.Convert () use img_RBC = img_float.[1] // Green. use img_RBC_filtered = gaussianFilter img_RBC config.LPFStandardDeviationRBC @@ -66,8 +66,8 @@ let doAnalysis (img : Image) (name : string) (config : Config) (repor let delta = config.Parameters.granulometryRange * config.RBCRadiusByResolution.Pixel int <| config.RBCRadiusByResolution.Pixel - delta, int <| config.RBCRadiusByResolution.Pixel + delta - let! radius = logTimeWithName "Granulometry (area)" (fun() -> reportWithVal 10 (Granulometry.findRadiusByAreaClosing img_RBC_filtered range |> float32)) - //let! radius = logTimeWithName "Granulometry (morpho)" (fun() -> reportWithVal 10 (Granulometry.findRadiusByClosing img_RBC_filtered range 1. true |> float32)) + let! radius = logTimeWithName "Granulometry (area)" (fun () -> reportWithVal 10 (Granulometry.findRadiusByAreaClosing img_RBC_filtered range |> float32)) + //let! radius = logTimeWithName "Granulometry (morpho)" (fun () -> reportWithVal 10 (Granulometry.findRadiusByClosing img_RBC_filtered range 1. true |> float32)) config.SetRBCRadius <| radius logWithName (sprintf "Found erythrocyte diameter: %O" config.RBCRadius) @@ -90,10 +90,13 @@ let doAnalysis (img : Image) (name : string) (config : Config) (repor let! parasites, imgWhitoutParasite, imgWithoutNucleus = logTimeWithName "Parasites segmentation" (fun () -> reportWithVal 40 (ParasitesMarker.find img_parasites_filtered config)) - let! edges, xGradient, yGradient = logTimeWithName "Finding edges" (fun () -> - let edges, xGradient, yGradient = Edges.find img_RBC_filtered - removeArea edges (config.RBCRadius.Pixel ** 2.f / 50.f |> int) - reportWithVal 50 (edges, xGradient, yGradient)) + let! edges, xGradient, yGradient = + logTimeWithName "Finding edges" ( + fun () -> + let edges, xGradient, yGradient = Edges.find img_RBC_filtered + removeArea edges (config.RBCRadius.Pixel ** 2.f / 50.f |> int) + reportWithVal 50 (edges, xGradient, yGradient) + ) let! matchingEllipses = logTimeWithName "Finding ellipses" (fun () -> reportWithVal 60 (Ellipse.find edges xGradient yGradient config)) @@ -107,10 +110,10 @@ let doAnalysis (img : Image) (name : string) (config : Config) (repor // Output pictures if debug flag is set. match config.Debug with | DebugOn output -> - let dirPath = System.IO.Path.Combine(output, name) + let dirPath = System.IO.Path.Combine (output, name) System.IO.Directory.CreateDirectory dirPath |> ignore - let buildFileName postfix = System.IO.Path.Combine(dirPath, name + postfix) + let buildFileName postfix = System.IO.Path.Combine (dirPath, name + postfix) IO.saveMat (edges * 255.0) (buildFileName " - edges.png") @@ -118,19 +121,19 @@ let doAnalysis (img : Image) (name : string) (config : Config) (repor IO.saveImg parasites.parasite (buildFileName " - parasites - stain.png") IO.saveImg parasites.nucleus (buildFileName " - parasites - infection.png") - let imgAllEllipses = img_RBC_filtered.Copy() - Drawing.drawEllipses imgAllEllipses matchingEllipses.Ellipses (Gray(200.0)) 0.04 + let imgAllEllipses = img_RBC_filtered.Copy () + Drawing.drawEllipses imgAllEllipses matchingEllipses.Ellipses (Gray 200.0) 0.04 IO.saveImg imgAllEllipses (buildFileName " - ellipses - all.png") - let imgEllipses = img_RBC_filtered.Convert() - Drawing.drawEllipses imgEllipses prunedEllipses (Bgr(0.0, 240.0, 240.0)) 1.0 + let imgEllipses = img_RBC_filtered.Convert () + Drawing.drawEllipses imgEllipses prunedEllipses (Bgr (0.0, 240.0, 240.0)) 1.0 IO.saveImg imgEllipses (buildFileName " - ellipses.png") - let imgCells = img.Copy() + let imgCells = img.Copy () Drawing.drawCells imgCells false cells IO.saveImg imgCells (buildFileName " - cells.png") - let imgCells' = img.Copy() + let imgCells' = img.Copy () Drawing.drawCells imgCells' true cells IO.saveImg imgCells' (buildFileName " - cells - full.png") @@ -167,12 +170,12 @@ let doMultipleAnalysis (imgs : (string * Config * Image) list) (repor | Some f -> f percent | _ -> true - let progressPerAnalysis = System.Collections.Concurrent.ConcurrentDictionary() + let progressPerAnalysis = System.Collections.Concurrent.ConcurrentDictionary () let nbImgs = List.length imgs let reportProgressImg (id : string) (progress : int) = - progressPerAnalysis.AddOrUpdate(id, progress, (fun _ _ -> progress)) |> ignore - report (progressPerAnalysis.Values.Sum() / nbImgs) + progressPerAnalysis.AddOrUpdate (id, progress, (fun _ _ -> progress)) |> ignore + report (progressPerAnalysis.Values.Sum () / nbImgs) let n = Environment.ProcessorCount diff --git a/Parasitemia/ParasitemiaCore/AssemblyInfo.fs b/Parasitemia/ParasitemiaCore/AssemblyInfo.fs index 665d7d7..9e190e0 100644 --- a/Parasitemia/ParasitemiaCore/AssemblyInfo.fs +++ b/Parasitemia/ParasitemiaCore/AssemblyInfo.fs @@ -7,22 +7,22 @@ open System.Runtime.InteropServices // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[] -[] -[] -[] -[] -[] -[] -[] +[] +[] +[] +[] +[] +[] +[] +[] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. -[] +[] // The following GUID is for the ID of the typelib if this project is exposed to COM -[] +[] // Version information for an assembly consists of the following four values: // @@ -33,9 +33,9 @@ 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/Classifier.fs b/Parasitemia/ParasitemiaCore/Classifier.fs index 36e2851..9b247f4 100644 --- a/Parasitemia/ParasitemiaCore/Classifier.fs +++ b/Parasitemia/ParasitemiaCore/Classifier.fs @@ -45,7 +45,7 @@ let findCells (ellipses : Ellipse list) (parasites : ParasitesMarker.Result) (wi let pixelOwnedByE (p : PointF) (e : EllipseFlaggedKd) (neighbors : (EllipseFlaggedKd * PointF * PointF) list) = e.Contains p.X p.Y && seq { - let c = PointF(e.Cx, e.Cy) + let c = PointF (e.Cx, e.Cy) for e', d1 in (neighbors @@ -58,7 +58,7 @@ let findCells (ellipses : Ellipse list) (parasites : ParasitesMarker.Result) (wi )) do if e'.State = e.State then // Peculiar vs peculiar or RBC vs RBC. let d2 = lineFromTwoPoints c p - let c' = PointF(e'.Cx, e'.Cy) + let c' = PointF (e'.Cx, e'.Cy) let v = pointFromTwoLines d1 (lineFromTwoPoints c c') let case1 = sign (v.X - c.X) <> sign (v.X - c'.X) || Utils.squaredDistanceTwoPoints v c > Utils.squaredDistanceTwoPoints v c' if not (Single.IsInfinity d2.A) then @@ -91,18 +91,19 @@ let findCells (ellipses : Ellipse list) (parasites : ParasitesMarker.Result) (wi if e.State <> CellState.Removed then tree.Search (searchRegion e) // We only keep the ellipses touching 'e'. - |> List.choose (fun otherE -> - if e <> otherE then - match EEOver.EEOverlapArea e otherE with - | Some (_, px, _) when px.Length > 2 -> - otherE.State <- CellState.Removed - None - | Some (area, px, py) when area > 0.f && px.Length = 2 -> - Some (otherE, PointF(px.[0], py.[0]), PointF(px.[1], py.[1])) - | _ -> + |> List.choose ( + fun otherE -> + if e <> otherE then + match EEOver.EEOverlapArea e otherE with + | Some (_, px, _) when px.Length > 2 -> + otherE.State <- CellState.Removed + None + | Some (area, px, py) when area > 0.f && px.Length = 2 -> + Some (otherE, PointF (px.[0], py.[0]), PointF (px.[1], py.[1])) + | _ -> + None + else None - else - None ) else [] @@ -117,7 +118,7 @@ let findCells (ellipses : Ellipse list) (parasites : ParasitesMarker.Result) (wi // 3) Remove ellipses with a high standard deviation (high contrast). // Obsolete. It was useful when the ellipses result quality wasn't good. (* let imgData = img.Data - let globalStdDeviation = MathNet.Numerics.Statistics.Statistics.PopulationStandardDeviation(seq { + let globalStdDeviation = MathNet.Numerics.Statistics.Statistics.PopulationStandardDeviation (seq { for y in 0 .. h - 1 do for x in 0 .. w - 1 do yield float imgData.[y, x, 0] }) @@ -145,7 +146,7 @@ let findCells (ellipses : Ellipse list) (parasites : ParasitesMarker.Result) (wi let mutable area = 0 for y = (if minY < 0 then 0 else minY) to (if maxY >= height then height - 1 else maxY) do for x = (if minX < 0 then 0 else minX) to (if maxX >= width then width - 1 else maxX) do - let p = PointF(float32 x, float32 y) + let p = PointF (float32 x, float32 y) if pixelOwnedByE p e neighbors then area <- area + 1 @@ -155,22 +156,24 @@ let findCells (ellipses : Ellipse list) (parasites : ParasitesMarker.Result) (wi // 5) Define non-rbc (peculiar) cells. let darkStainData = parasites.darkStain.Data ellipsesWithNeigbors - |> List.choose (fun (e, neighbors) -> - if e.State = CellState.Removed then - None - else - let mutable darkStainPixels = 0 - let mutable nbElement = 0 - let minX, minY, maxX, maxY = ellipseWindow e - for y = minY to maxY do - for x = minX to maxX do - let p = PointF(float32 x, float32 y) - if pixelOwnedByE p e neighbors then - nbElement <- nbElement + 1 - if darkStainData.[y, x, 0] > 0uy then - darkStainPixels <- darkStainPixels + 1 + |> List.choose ( + fun (e, neighbors) -> + if e.State = CellState.Removed then + None + else + let mutable darkStainPixels = 0 + let mutable nbElement = 0 + let minX, minY, maxX, maxY = ellipseWindow e + for y = minY to maxY do + for x = minX to maxX do + let p = PointF (float32 x, float32 y) + if pixelOwnedByE p e neighbors then + nbElement <- nbElement + 1 + if darkStainData.[y, x, 0] > 0uy then + darkStainPixels <- darkStainPixels + 1 - if float darkStainPixels > config.Parameters.maxDarkStainRatio * (float nbElement) then Some e else None) + if float darkStainPixels > config.Parameters.maxDarkStainRatio * (float nbElement) then Some e else None + ) // We do not change the state during the process to avoid to have peculiar neighbors which change the behavior of 'pixelOwnedByE'. |> List.iter (fun e -> e.State <- CellState.Peculiar) @@ -191,30 +194,30 @@ let findCells (ellipses : Ellipse list) (parasites : ParasitesMarker.Result) (wi else let minX, minY, maxX, maxY = ellipseWindow e - let nucleusPixels = List() - let parasitePixels = List() + let nucleusPixels = List () + let parasitePixels = List () let mutable nbElement = 0 - let elements = new Matrix(maxY - minY + 1, maxX - minX + 1) + let elements = new Matrix (maxY - minY + 1, maxX - minX + 1) for y = minY to maxY do for x = minX to maxX do - let p = PointF(float32 x, float32 y) + let p = PointF (float32 x, float32 y) if pixelOwnedByE p e neighbors then elements.[y - minY, x - minX] <- 1uy nbElement <- nbElement + 1 if nucleusData.[y, x, 0] > 0uy then - nucleusPixels.Add(Point(x, y)) + nucleusPixels.Add (Point (x, y)) if parasiteData.[y, x, 0] > 0uy then - parasitePixels.Add(Point(x, y)) + parasitePixels.Add (Point (x, y)) let parasiteArea = if nucleusPixels.Count > 0 then seq { for parasitePixel in parasitePixels do - if nucleusPixels.Exists(fun p -> pown (p.X - parasitePixel.X) 2 + pown (p.Y - parasitePixel.Y) 2 <= diameterParasiteSquared) then + if nucleusPixels.Exists (fun p -> pown (p.X - parasitePixel.X) 2 + pown (p.Y - parasitePixel.Y) 2 <= diameterParasiteSquared) then yield 1 } |> Seq.sum else @@ -236,7 +239,7 @@ let findCells (ellipses : Ellipse list) (parasites : ParasitesMarker.Result) (wi Some { cellClass = cellClass - center = Point(roundInt e.Cx, roundInt e.Cy) + center = Point (roundInt e.Cx, roundInt e.Cy) nucleusArea = if cellClass = InfectedRBC then nucleusPixels.Count else 0 parasiteArea = parasiteArea elements = elements diff --git a/Parasitemia/ParasitemiaCore/Config.fs b/Parasitemia/ParasitemiaCore/Config.fs index fdeed8f..e000d65 100644 --- a/Parasitemia/ParasitemiaCore/Config.fs +++ b/Parasitemia/ParasitemiaCore/Config.fs @@ -98,7 +98,7 @@ type RBCRadius (radius : float32, parameters : Parameters) = member this.NucleusArea = parameters.nucleusAreaRatio * this.Area member this.MinimumParasiteArea = parameters.minimumParasiteAreaRatio * this.Area - override this.ToString() = + override this.ToString () = sprintf "%d px (%.1f μm)" (Utils.roundInt <| 2.f * radius) (2. * this.μm) type Config (param : Parameters) = @@ -108,17 +108,17 @@ type Config (param : Parameters) = float32 rbcRadiusPx let mutable parameters : Parameters = param - let mutable rbcRadiusByResolution = RBCRadius(RBCadiusInPixels parameters.rbcDiameter parameters.resolution, parameters) - let mutable rbcRadius = RBCRadius(0.f, parameters) + let mutable rbcRadiusByResolution = RBCRadius (RBCadiusInPixels parameters.rbcDiameter parameters.resolution, parameters) + let mutable rbcRadius = RBCRadius (0.f, parameters) - new () = Config(defaultParameters) + new () = Config defaultParameters member this.Parameters - with get() = parameters - and set(param) = + with get () = parameters + and set param = parameters <- param - rbcRadiusByResolution <- RBCRadius(RBCadiusInPixels parameters.rbcDiameter parameters.resolution, param) - rbcRadius <- RBCRadius(rbcRadius.Pixel, param) + rbcRadiusByResolution <- RBCRadius (RBCadiusInPixels parameters.rbcDiameter parameters.resolution, param) + rbcRadius <- RBCRadius (rbcRadius.Pixel, param) member val Debug = DebugOff with get, set @@ -134,8 +134,8 @@ type Config (param : Parameters) = member this.RBCRadius = rbcRadius member this.SetRBCRadius (radiusPixel : float32) = - rbcRadius <- RBCRadius(radiusPixel, parameters) + rbcRadius <- RBCRadius (radiusPixel, parameters) member this.Copy () = - this.MemberwiseClone() :?> Config + this.MemberwiseClone () :?> Config diff --git a/Parasitemia/ParasitemiaCore/EEOver.fs b/Parasitemia/ParasitemiaCore/EEOver.fs index 0824e98..422e86f 100644 --- a/Parasitemia/ParasitemiaCore/EEOver.fs +++ b/Parasitemia/ParasitemiaCore/EEOver.fs @@ -356,7 +356,7 @@ let private cubicroots (p : float[]) (r : float[,]) = else b <- d c <- t / b - d <- sqrt(0.75) * (b - c) + d <- sqrt 0.75 * (b - c) r.[2, 2] <- d b <- b + c c <- -0.5 * b - s @@ -609,8 +609,8 @@ let EEOverlapArea (e1 : Types.Ellipse) (e2 : Types.Ellipse) : (float32 * float32 let x2 = -x1 #if DEBUG_LOG - printf "\n\tx1=%f, y1=%f, A=%f. B=%f ---> ellipse2tr(x1)= %f\n" x1 ychk.[i] a1 b1 (ellipse2tr x1 ychk.[i] aa bb cc dd ee ff) - printf "\tx2=%f, y1=%f, A=%f. B=%f ---> ellipse2tr(x2)= %f\n" x2 ychk.[i] a1 b1 (ellipse2tr x2 ychk.[i] aa bb cc dd ee ff) + printf "\n\tx1=%f, y1=%f, A=%f. B=%f ---> ellipse2tr (x1)= %f\n" x1 ychk.[i] a1 b1 (ellipse2tr x1 ychk.[i] aa bb cc dd ee ff) + printf "\tx2=%f, y1=%f, A=%f. B=%f ---> ellipse2tr (x2)= %f\n" x2 ychk.[i] a1 b1 (ellipse2tr x2 ychk.[i] aa bb cc dd ee ff) #endif if abs (ellipse2tr x1 ychk.[i] aa bb cc dd ee ff) < EPS then diff --git a/Parasitemia/ParasitemiaCore/Ellipse.fs b/Parasitemia/ParasitemiaCore/Ellipse.fs index 174dd68..d0dea6e 100644 --- a/Parasitemia/ParasitemiaCore/Ellipse.fs +++ b/Parasitemia/ParasitemiaCore/Ellipse.fs @@ -23,7 +23,7 @@ let minimumDistanceBetweenDrawnPoints = 0.6 /// 'Ellipse.Alpha' is between 0 and Pi. /// let ellipse (p1x : float) (p1y : float) (m1 : float) (p2x : float) (p2y : float) (m2 : float) (p3x : float) (p3y : float) : Types.Ellipse option = - let p0 = pointFromTwoLines (Types.Line(float32 m1, float32 (p1y - m1 * p1x))) (Types.Line(float32 m2, float32(p2y - m2 * p2x))) + let p0 = pointFromTwoLines (Types.Line (float32 m1, float32 (p1y - m1 * p1x))) (Types.Line (float32 m2, float32(p2y - m2 * p2x))) let p0x, p0y = float p0.X, float p0.Y let s = @@ -39,8 +39,8 @@ let ellipse (p1x : float) (p1y : float) (m1 : float) (p2x : float) (p2y : float) let v2 = matrix [[ 1.; p2x; p2y ]] let v3 = matrix [[ 1.; p3x; p3y ]] - let p = (v3.Stack(v1).Stack(v2).Determinant() * v0).Stack(v0.Stack(v3).Stack(v2).Determinant() * v1).Stack(v0.Stack(v1).Stack(v3).Determinant() * v2).Transpose() - let conicMat = p * s.Inverse() * p.Transpose() + let p = (v3.Stack(v1).Stack(v2).Determinant () * v0).Stack(v0.Stack(v3).Stack(v2).Determinant () * v1).Stack(v0.Stack(v1).Stack(v3).Determinant () * v2).Transpose () + let conicMat = p * s.Inverse () * p.Transpose () let a = conicMat.[0, 0] let b = conicMat.[0, 1] let c = conicMat.[1, 1] @@ -57,7 +57,7 @@ let ellipse (p1x : float) (p1y : float) (m1 : float) (p2x : float) (p2y : float) None else let q = (-1. / at) * (matrix [[ a * f - d ** 2.0; b * d - a * e ]; [ b * d - a * e; a * c - b ** 2.0 ]]) - let eigen = q.Evd() + let eigen = q.Evd () let eigenValues = eigen.EigenValues let lambda = eigenValues.[1].Real let mu = eigenValues.[0].Real @@ -77,7 +77,7 @@ let ellipse (p1x : float) (p1y : float) (m1 : float) (p2x : float) (p2y : float) let phi' = if phi < 0. then phi + Math.PI else phi let majorAxis, minorAxis = if r1 > r2 then r1, r2 else r2, r1 - Some (Types.Ellipse(float32 cx, float32 cy, float32 majorAxis, float32 minorAxis, float32 phi')) + Some (Types.Ellipse (float32 cx, float32 cy, float32 majorAxis, float32 minorAxis, float32 phi')) let inline private vectorRotation (px : float32) (py : float32) (vx : float32) (vy : float32) (p0x : float32) (p0y : float32) : float32 = if py > p0y then @@ -150,15 +150,15 @@ let find (edges : Matrix) let mutable last_i, last_j = Int32.MaxValue, Int32.MaxValue - let currentElements = List() + let currentElements = List () let edgesData = edges.Data let xDirData = xGradient.Data let yDirData = yGradient.Data - let rng = Random(42) + let rng = Random 42 - let ellipses = MatchingEllipses(config.RBCRadius.Pixel) + let ellipses = MatchingEllipses config.RBCRadius.Pixel for window_i in -windowSize + increment .. increment .. h - increment do for window_j in -windowSize + increment .. increment .. w - increment do @@ -169,9 +169,9 @@ let find (edges : Matrix) let window_j_end = if window_j + windowSize - 1 >= w then w - 1 else window_j + windowSize - 1 // Remove old elements. - let indexFirstElement = currentElements.FindIndex(fun p -> p.X >= window_j_begin) + let indexFirstElement = currentElements.FindIndex (fun p -> p.X >= window_j_begin) if indexFirstElement > 0 then - currentElements.RemoveRange(0, indexFirstElement) + currentElements.RemoveRange (0, indexFirstElement) // Add the new elements. let newElemsBegin_j = window_j + windowSize - increment @@ -179,15 +179,15 @@ let find (edges : Matrix) for j = (if newElemsBegin_j < 0 then 0 else newElemsBegin_j) to (if newElemsEnd_j >= w then w - 1 else newElemsEnd_j) do for i = window_i_begin to window_i_end do if edgesData.[i, j] = 1uy then - currentElements.Add(Point(j, i)) + currentElements.Add (Point (j, i)) if currentElements.Count >= nbPickElementsMin then let mutable nbOfPicks = (float currentElements.Count) * factorNbMaxPick |> int let mutable nbOfValidPicks = (float currentElements.Count) * factorNbValidPick |> int while nbOfPicks > 0 && nbOfValidPicks > 0 do - let p1 = currentElements.[rng.Next(currentElements.Count)] - let p2 = currentElements.[rng.Next(currentElements.Count)] - let p3 = currentElements.[rng.Next(currentElements.Count)] + let p1 = currentElements.[rng.Next currentElements.Count] + let p2 = currentElements.[rng.Next currentElements.Count] + let p3 = currentElements.[rng.Next currentElements.Count] if p1 <> p2 && p1 <> p3 && p2 <> p3 then nbOfPicks <- nbOfPicks - 1 let p1yf, p1xf = float p1.Y, float p1.X @@ -207,7 +207,7 @@ let find (edges : Matrix) | _ -> () | _ -> () - currentElements.Clear() + currentElements.Clear () ellipses diff --git a/Parasitemia/ParasitemiaCore/Granulometry.fs b/Parasitemia/ParasitemiaCore/Granulometry.fs index bbb6d66..287cd3e 100644 --- a/Parasitemia/ParasitemiaCore/Granulometry.fs +++ b/Parasitemia/ParasitemiaCore/Granulometry.fs @@ -16,7 +16,7 @@ open Utils /// Minimum radius * maximum radius /// le 1.0, to speed up the process. let findRadiusByClosing (img : Image) (range : int * int) (scale : float) (useOctagon : bool) : int = - use scaledImg = if scale = 1. then img else img.Resize(scale, CvEnum.Inter.Area) + use scaledImg = if scale = 1. then img else img.Resize (scale, CvEnum.Inter.Area) let r1, r2 = range let r1', r2' = roundInt (float r1 * scale), roundInt (float r2 * scale) @@ -27,7 +27,7 @@ let findRadiusByClosing (img : Image) (range : int * int) (scale // 's' must be odd. let octagon (s : int) : Matrix = if s % 2 = 0 then failwith "s must be odd" - let m = new Matrix(Array2D.create s s 1uy) + let m = new Matrix (Array2D.create s s 1uy) let r = (float s) / (Math.Sqrt 2. + 2.) |> roundInt for i = 0 to r - 1 do for j = 0 to r - 1 do @@ -44,9 +44,9 @@ let findRadiusByClosing (img : Image) (range : int * int) (scale if useOctagon then (octagon (2 * r - 1)).Mat // It doesn't speed up the process. else - CvInvoke.GetStructuringElement(CvEnum.ElementShape.Ellipse, Size(2 * r, 2 * r), Point(-1, -1)) + CvInvoke.GetStructuringElement (CvEnum.ElementShape.Ellipse, Size (2 * r, 2 * r), Point (-1, -1)) - use closed = scaledImg.MorphologyEx(CvEnum.MorphOp.Close, se, Point(-1, -1), 1, CvEnum.BorderType.Replicate, MCvScalar(0.0)) + use closed = scaledImg.MorphologyEx (CvEnum.MorphOp.Close, se, Point (-1, -1), 1, CvEnum.BorderType.Replicate, MCvScalar (0.0)) let n = closed.GetSum().Intensity @@ -67,14 +67,17 @@ let findRadiusByAreaClosing (img : Image) (radiusRange : int * in if r1 > r2 then failwithf "'radiusRange' invalid : %O" radiusRange - use imgCopy = img.Copy() + use imgCopy = img.Copy () let mutable maxDiff = 0.f let mutable max_r = r1 - Morpho.areaCloseFWithFun imgCopy [ for r in r1 .. r2 -> Math.PI * float r ** 2. |> roundInt, r ] (fun r diff -> - if r <> r1 && diff > maxDiff then - maxDiff <- diff - max_r <- r - 1) + Morpho.areaCloseFWithFun imgCopy [ for r in r1 .. r2 -> Math.PI * float r ** 2. |> roundInt, r ] ( + fun r diff -> + if r <> r1 && diff > maxDiff then + maxDiff <- diff + max_r <- r - 1 + ) + max_r diff --git a/Parasitemia/ParasitemiaCore/Heap.fs b/Parasitemia/ParasitemiaCore/Heap.fs index 350164b..356ea06 100644 --- a/Parasitemia/ParasitemiaCore/Heap.fs +++ b/Parasitemia/ParasitemiaCore/Heap.fs @@ -18,16 +18,16 @@ type private Node<'k, 'v> = /// The goal is to have a set of data and be able to get the value associated with the min (or max) key value. /// type Heap<'k, 'v> (kComparer : IComparer<'k>) = - let a = List>() + let a = List> () let rec heapUp (i : int) = let l, r = left i, right i // Is the left child greater than the parent? - let mutable max = if l < a.Count && kComparer.Compare(a.[l].key, a.[i].key) > 0 then l else i + let mutable max = if l < a.Count && kComparer.Compare (a.[l].key, a.[i].key) > 0 then l else i // Is the right child greater than the parent and the left child? - if r < a.Count && kComparer.Compare(a.[r].key, a.[max].key) > 0 then + if r < a.Count && kComparer.Compare (a.[r].key, a.[max].key) > 0 then max <- r // If a child is greater than the parent. @@ -42,41 +42,41 @@ type Heap<'k, 'v> (kComparer : IComparer<'k>) = let l, r = left i, right i let leftIntegrity = if l < a.Count then - if kComparer.Compare(a.[l].key, a.[i].key) > 0 then false else checkIntegrity l + if kComparer.Compare (a.[l].key, a.[i].key) > 0 then false else checkIntegrity l else true let rightIntegrity = if r < a.Count then - if kComparer.Compare(a.[r].key, a.[i].key) > 0 then false else checkIntegrity r + if kComparer.Compare (a.[r].key, a.[i].key) > 0 then false else checkIntegrity r else true leftIntegrity && rightIntegrity interface IEnumerable<'k * 'v> with member this.GetEnumerator () : IEnumerator<'k * 'v> = - (seq { for e in a -> e.key, e.value }).GetEnumerator() + (seq { for e in a -> e.key, e.value }).GetEnumerator () interface System.Collections.IEnumerable with member this.GetEnumerator () : System.Collections.IEnumerator = - (this :> IEnumerable<'k * 'v>).GetEnumerator() :> System.Collections.IEnumerator + (this :> IEnumerable<'k * 'v>).GetEnumerator () :> System.Collections.IEnumerator member this.Next () : 'k * 'v = let node = a.[0] a.[0] <- a.[a.Count - 1] - a.RemoveAt(a.Count - 1) + a.RemoveAt (a.Count - 1) heapUp 0 node.key, node.value member this.RemoveNext () = a.[0] <- a.[a.Count - 1] - a.RemoveAt(a.Count - 1) + a.RemoveAt (a.Count - 1) heapUp 0 member this.Add (key : 'k) (value : 'v) = - a.Add(Node(key, value)) + a.Add (Node (key, value)) let mutable i = a.Count - 1 - while i > 0 && kComparer.Compare(a.[parent i].key, a.[i].key) < 0 do + while i > 0 && kComparer.Compare (a.[parent i].key, a.[i].key) < 0 do let tmp = a.[parent i] a.[parent i] <- a.[i] a.[i] <- tmp @@ -89,4 +89,4 @@ type Heap<'k, 'v> (kComparer : IComparer<'k>) = let max = a.[0] max.key, max.value - member this.Clear () = a.Clear() + member this.Clear () = a.Clear () diff --git a/Parasitemia/ParasitemiaCore/ImgTools/Drawing.fs b/Parasitemia/ParasitemiaCore/ImgTools/Drawing.fs index 9662049..2882f4b 100644 --- a/Parasitemia/ParasitemiaCore/ImgTools/Drawing.fs +++ b/Parasitemia/ParasitemiaCore/ImgTools/Drawing.fs @@ -14,14 +14,14 @@ let drawPoints (img : Image) (points : Points) (intensity : 'TDep img.Data.[p.Y, p.X, 0] <- intensity let drawLine (img : Image<'TColor, 'TDepth>) (color : 'TColor) (x0 : int) (y0 : int) (x1 : int) (y1 : int) (thickness : int) = - img.Draw(LineSegment2D(Point(x0, y0), Point(x1, y1)), color, thickness); + img.Draw (LineSegment2D (Point (x0, y0), Point (x1, y1)), color, thickness); let drawLineF (img : Image<'TColor, 'TDepth>) (color : 'TColor) (x0 : float) (y0 : float) (x1 : float) (y1 : float) (thickness : int) = - img.Draw(LineSegment2DF(PointF(float32 x0, float32 y0), PointF(float32 x1, float32 y1)), color, thickness, CvEnum.LineType.AntiAlias); + img.Draw (LineSegment2DF (PointF (float32 x0, float32 y0), PointF (float32 x1, float32 y1)), color, thickness, CvEnum.LineType.AntiAlias); let drawEllipse (img : Image<'TColor, 'TDepth>) (e : Ellipse) (color : 'TColor) (alpha : float) = if alpha >= 1.0 then - img.Draw(Emgu.CV.Structure.Ellipse(PointF(e.Cx, e.Cy), SizeF(2.f * e.B, 2.f * e.A), e.Alpha / PI * 180.f), color, 1, CvEnum.LineType.AntiAlias) + img.Draw (Emgu.CV.Structure.Ellipse (PointF (e.Cx, e.Cy), SizeF (2.f * e.B, 2.f * e.A), e.Alpha / PI * 180.f), color, 1, CvEnum.LineType.AntiAlias) else let windowPosX = e.Cx - e.A - 5.f let gapX = windowPosX - (float32 (int windowPosX)) @@ -29,24 +29,24 @@ let drawEllipse (img : Image<'TColor, 'TDepth>) (e : Ellipse) (color : 'TColor) let windowPosY = e.Cy - e.A - 5.f let gapY = windowPosY - (float32 (int windowPosY)) - let roi = Rectangle(int windowPosX, int windowPosY, 2.f * (e.A + 5.f) |> int, 2.f * (e.A + 5.f) |> int) + let roi = Rectangle (int windowPosX, int windowPosY, 2.f * (e.A + 5.f) |> int, 2.f * (e.A + 5.f) |> int) img.ROI <- roi if roi = img.ROI then // We do not display ellipses touching the edges (FIXME) - use i = new Image<'TColor, 'TDepth>(img.ROI.Size) - i.Draw(Emgu.CV.Structure.Ellipse(PointF(e.A + 5.f + gapX, e.A + 5.f + gapY), SizeF(2.f * e.B, 2.f * e.A), e.Alpha / PI * 180.f), color, 1, CvEnum.LineType.AntiAlias) - CvInvoke.AddWeighted(img, 1.0, i, alpha, 0.0, img) + use i = new Image<'TColor, 'TDepth> (img.ROI.Size) + i.Draw (Emgu.CV.Structure.Ellipse(PointF(e.A + 5.f + gapX, e.A + 5.f + gapY), SizeF(2.f * e.B, 2.f * e.A), e.Alpha / PI * 180.f), color, 1, CvEnum.LineType.AntiAlias) + CvInvoke.AddWeighted (img, 1.0, i, alpha, 0.0, img) img.ROI <- Rectangle.Empty let drawEllipses (img : Image<'TColor, 'TDepth>) (ellipses : Ellipse list) (color : 'TColor) (alpha : float) = List.iter (fun e -> drawEllipse img e color alpha) ellipses -let rngCell = System.Random() +let rngCell = System.Random () let drawCell (img : Image) (drawCellContent : bool) (c : Cell) = if drawCellContent then - let colorB = rngCell.Next(20, 70) - let colorG = rngCell.Next(20, 70) - let colorR = rngCell.Next(20, 70) + let colorB = rngCell.Next (20, 70) + let colorG = rngCell.Next (20, 70) + let colorR = rngCell.Next (20, 70) for y = 0 to c.elements.Height - 1 do for x = 0 to c.elements.Width - 1 do @@ -61,9 +61,9 @@ let drawCell (img : Image) (drawCellContent : bool) (c : Cell) = let crossColor, crossColor2 = match c.cellClass with - | HealthyRBC -> Bgr(255., 0., 0.), Bgr(255., 255., 255.) - | InfectedRBC -> Bgr(0., 0., 255.), Bgr(120., 120., 255.) - | Peculiar -> Bgr(0., 0., 0.), Bgr(80., 80., 80.) + | HealthyRBC -> Bgr (255., 0., 0.), Bgr (255., 255., 255.) + | InfectedRBC -> Bgr (0., 0., 255.), Bgr (120., 120., 255.) + | Peculiar -> Bgr (0., 0., 0.), Bgr (80., 80., 80.) drawLine img crossColor2 (c.center.X - 3) c.center.Y (c.center.X + 3) c.center.Y 2 drawLine img crossColor2 c.center.X (c.center.Y - 3) c.center.X (c.center.Y + 3) 2 diff --git a/Parasitemia/ParasitemiaCore/ImgTools/Edges.fs b/Parasitemia/ParasitemiaCore/ImgTools/Edges.fs index 63ec80a..7c3e6d3 100644 --- a/Parasitemia/ParasitemiaCore/ImgTools/Edges.fs +++ b/Parasitemia/ParasitemiaCore/ImgTools/Edges.fs @@ -25,27 +25,27 @@ let find (img : Image) : Matrix * Matrix * Matrix< let h = img.Height use sobelKernel = - new Matrix( + new Matrix ( array2D [[ -1.0f; 0.0f; 1.0f ] [ -2.0f; 0.0f; 2.0f ] [ -1.0f; 0.0f; 1.0f ]] ) - let xGradient = new Matrix(img.Size) - let yGradient = new Matrix(img.Size) - CvInvoke.Filter2D(img, xGradient, sobelKernel, Point(1, 1)) - CvInvoke.Filter2D(img, yGradient, sobelKernel.Transpose(), Point(1, 1)) + let xGradient = new Matrix (img.Size) + let yGradient = new Matrix (img.Size) + CvInvoke.Filter2D (img, xGradient, sobelKernel, Point (1, 1)) + CvInvoke.Filter2D (img, yGradient, sobelKernel.Transpose (), Point (1, 1)) - use magnitudes = new Matrix(xGradient.Size) - use angles = new Matrix(xGradient.Size) - CvInvoke.CartToPolar(xGradient, yGradient, magnitudes, angles) // Compute the magnitudes and angles. The angles are between 0 and 2 * pi. + use magnitudes = new Matrix (xGradient.Size) + use angles = new Matrix (xGradient.Size) + CvInvoke.CartToPolar (xGradient, yGradient, magnitudes, angles) // Compute the magnitudes and angles. The angles are between 0 and 2 * pi. let thresholdHigh, thresholdLow = let threshold, _, _ = otsu (histogramMat magnitudes 300) threshold + (sensibilityHigh * threshold), threshold - (sensibilityLow * threshold) // Non-maximum suppression. - use nms = new Matrix(xGradient.Size) + use nms = new Matrix (xGradient.Size) let nmsData = nms.Data let anglesData = angles.Data @@ -88,18 +88,18 @@ let find (img : Image) : Matrix * Matrix * Matrix< // suppressMConnections nms // It's not helpful for the rest of the process (ellipse detection). - let edges = new Matrix(xGradient.Size) + let edges = new Matrix (xGradient.Size) let edgesData = edges.Data // Hysteresis thresholding. - let toVisit = Stack() + let toVisit = Stack () for i = 0 to h - 1 do for j = 0 to w - 1 do if nmsData.[i, j] = 1uy && magnitudesData.[i, j] >= thresholdHigh then nmsData.[i, j] <- 0uy - toVisit.Push(Point(j, i)) + toVisit.Push (Point (j, i)) while toVisit.Count > 0 do - let p = toVisit.Pop() + let p = toVisit.Pop () edgesData.[p.Y, p.X] <- 1uy for i' = -1 to 1 do for j' = -1 to 1 do @@ -108,6 +108,6 @@ let find (img : Image) : Matrix * Matrix * Matrix< let nj = p.X + j' if ni >= 0 && ni < h && nj >= 0 && nj < w && nmsData.[ni, nj] = 1uy then nmsData.[ni, nj] <- 0uy - toVisit.Push(Point(nj, ni)) + toVisit.Push (Point (nj, ni)) edges, xGradient, yGradient \ No newline at end of file diff --git a/Parasitemia/ParasitemiaCore/ImgTools/Histogram.fs b/Parasitemia/ParasitemiaCore/ImgTools/Histogram.fs index df1ecb6..718a9de 100644 --- a/Parasitemia/ParasitemiaCore/ImgTools/Histogram.fs +++ b/Parasitemia/ParasitemiaCore/ImgTools/Histogram.fs @@ -20,10 +20,10 @@ let histogramImg (img : Image) (nbSamples : int) : Histogram = let min, max = let min = ref [| 0.0 |] - let minLocation = ref <| [| Point() |] + let minLocation = ref <| [| Point () |] let max = ref [| 0.0 |] - let maxLocation = ref <| [| Point() |] - img.MinMax(min, max, minLocation, maxLocation) + let maxLocation = ref <| [| Point () |] + img.MinMax (min, max, minLocation, maxLocation) float32 (!min).[0], float32 (!max).[0] let inline bin (x : float32) : int = @@ -44,10 +44,10 @@ let histogramMat (mat : Matrix) (nbSamples : int) : Histogram = let min, max = let min = ref 0.0 - let minLocation = ref <| Point() + let minLocation = ref <| Point () let max = ref 0.0 - let maxLocation = ref <| Point() - mat.MinMax(min, max, minLocation, maxLocation) + let maxLocation = ref <| Point () + mat.MinMax (min, max, minLocation, maxLocation) float32 !min, float32 !max let inline bin (x : float32) : int = diff --git a/Parasitemia/ParasitemiaCore/ImgTools/IO.fs b/Parasitemia/ParasitemiaCore/ImgTools/IO.fs index 1a663c7..08adfb0 100644 --- a/Parasitemia/ParasitemiaCore/ImgTools/IO.fs +++ b/Parasitemia/ParasitemiaCore/ImgTools/IO.fs @@ -7,9 +7,9 @@ open Emgu.CV open Emgu.CV.Structure let saveImg (img : Image<'TColor, 'TDepth>) (filepath : string) = - img.Save(filepath) + img.Save filepath let saveMat (mat : Matrix<'TDepth>) (filepath : string) = - use img = new Image(mat.Size) - mat.CopyTo(img) + use img = new Image (mat.Size) + mat.CopyTo img saveImg img filepath diff --git a/Parasitemia/ParasitemiaCore/ImgTools/ImgTools.fs b/Parasitemia/ParasitemiaCore/ImgTools/ImgTools.fs index 5a404e3..fc89047 100644 --- a/Parasitemia/ParasitemiaCore/ImgTools/ImgTools.fs +++ b/Parasitemia/ParasitemiaCore/ImgTools/ImgTools.fs @@ -14,10 +14,10 @@ open Emgu.CV.Structure /// let normalize (img : Image) (upperLimit : float) : Image = let min = ref [| 0.0 |] - let minLocation = ref <| [| Point() |] + let minLocation = ref <| [| Point () |] let max = ref [| 0.0 |] - let maxLocation = ref <| [| Point() |] - img.MinMax(min, max, minLocation, maxLocation) + let maxLocation = ref <| [| Point () |] + img.MinMax (min, max, minLocation, maxLocation) let normalized = (img - (!min).[0]) / ((!max).[0] - (!min).[0]) if upperLimit = 1.0 then normalized @@ -30,17 +30,17 @@ let mergeChannels (img : Image) (rgbWeights : float * float * floa | 0., 1., 0. -> img.[1] | 0., 0., 1. -> img.[0] | redFactor, greenFactor, blueFactor -> - let result = new Image(img.Size) - CvInvoke.AddWeighted(result, 1., img.[2], redFactor, 0., result) - CvInvoke.AddWeighted(result, 1., img.[1], greenFactor, 0., result) - CvInvoke.AddWeighted(result, 1., img.[0], blueFactor, 0., result) + let result = new Image (img.Size) + CvInvoke.AddWeighted (result, 1., img.[2], redFactor, 0., result) + CvInvoke.AddWeighted (result, 1., img.[1], greenFactor, 0., result) + CvInvoke.AddWeighted (result, 1., img.[0], blueFactor, 0., result) result let mergeChannelsWithProjection (img : Image) (v1r : float32, v1g : float32, v1b : float32) (v2r : float32, v2g : float32, v2b : float32) (upperLimit : float) : Image = let vr, vg, vb = v2r - v1r, v2g - v1g, v2b - v1b let vMagnitude = sqrt (vr ** 2.f + vg ** 2.f + vb ** 2.f) let project (r : float32) (g : float32) (b : float32) = ((r - v1r) * vr + (g - v1g) * vg + (b - v1b) * vb) / vMagnitude - let result = new Image(img.Size) + let result = new Image (img.Size) // TODO: Essayer en bindant Data pour gagner du temps for i = 0 to img.Height - 1 do for j = 0 to img.Width - 1 do @@ -49,8 +49,8 @@ let mergeChannelsWithProjection (img : Image) (v1r : float32, v1g // Normalize image values between 0uy and 255uy. let normalizeAndConvert (img : Image) : Image = - (normalize (img.Convert()) 255.).Convert() + (normalize (img.Convert ()) 255.).Convert () let gaussianFilter (img : Image<'TColor, 'TDepth>) (standardDeviation : float) : Image<'TColor, 'TDepth> = let size = 2 * int (ceil (4.0 * standardDeviation)) + 1 - img.SmoothGaussian(size, size, standardDeviation, standardDeviation) + img.SmoothGaussian (size, size, standardDeviation, standardDeviation) diff --git a/Parasitemia/ParasitemiaCore/ImgTools/Morpho.fs b/Parasitemia/ParasitemiaCore/ImgTools/Morpho.fs index 6c8d7a0..d2826bf 100644 --- a/Parasitemia/ParasitemiaCore/ImgTools/Morpho.fs +++ b/Parasitemia/ParasitemiaCore/ImgTools/Morpho.fs @@ -37,28 +37,28 @@ let inline findExtremum (img : Image) (extremumType : ExtremumTyp let imgData = img.Data let suppress : bool[,] = Array2D.zeroCreate h w - let result = List>() + let result = List> () let flood (start : Point) : List> = - let sameLevelToCheck = Stack() - let betterLevelToCheck = Stack() - betterLevelToCheck.Push(start) + let sameLevelToCheck = Stack () + let betterLevelToCheck = Stack () + betterLevelToCheck.Push start - let result' = List>() + let result' = List> () while betterLevelToCheck.Count > 0 do - let p = betterLevelToCheck.Pop() + let p = betterLevelToCheck.Pop () if not suppress.[p.Y, p.X] then suppress.[p.Y, p.X] <- true - sameLevelToCheck.Push(p) - let current = List() + sameLevelToCheck.Push p + let current = List () let mutable betterExists = false while sameLevelToCheck.Count > 0 do - let p' = sameLevelToCheck.Pop() + let p' = sameLevelToCheck.Pop () let currentLevel = imgData.[p'.Y, p'.X, 0] - current.Add(p') |> ignore + current.Add p' |> ignore for i, j in se do let ni = i + p'.Y let nj = j + p'.X @@ -68,23 +68,23 @@ let inline findExtremum (img : Image) (extremumType : ExtremumTyp if notSuppressed && level = currentLevel then suppress.[ni, nj] <- true - sameLevelToCheck.Push(Point(nj, ni)) + sameLevelToCheck.Push (Point (nj, ni)) elif (if extremumType = ExtremumType.Maxima then level > currentLevel else level < currentLevel) then betterExists <- true if notSuppressed then - betterLevelToCheck.Push(Point(nj, ni)) + betterLevelToCheck.Push (Point (nj, ni)) if not betterExists then - result'.Add(current) + result'.Add current result' for i = 0 to h - 1 do for j = 0 to w - 1 do - let maxima = flood (Point(j, i)) + let maxima = flood (Point (j, i)) if maxima.Count > 0 then - result.AddRange(maxima) + result.AddRange maxima - result.Select(fun l -> Points(l)) + result.Select (fun l -> Points l) let inline findMaxima (img : Image) : IEnumerable when 'TDepth : unmanaged = findExtremum img ExtremumType.Maxima @@ -94,7 +94,7 @@ let inline findMinima (img : Image) : IEnumerable when 'T type PriorityQueue () = let size = 256 - let q : Points[] = Array.init size (fun i -> Points()) + let q : Points[] = Array.init size (fun i -> Points ()) let mutable highest = -1 // Value of the first elements of 'q'. let mutable lowest = size @@ -103,8 +103,8 @@ type PriorityQueue () = invalidOp "Queue is empty" else let l = q.[highest] - let next = l.First() - l.Remove(next) |> ignore + let next = l.First () + l.Remove next |> ignore let value = byte highest if l.Count = 0 then @@ -122,8 +122,8 @@ type PriorityQueue () = invalidOp "Queue is empty" else let l = q.[lowest + 1] - let next = l.First() - l.Remove(next) |> ignore + let next = l.First () + l.Remove next |> ignore let value = byte (lowest + 1) if l.Count = 0 then @@ -150,11 +150,11 @@ type PriorityQueue () = if vi <= lowest then lowest <- vi - 1 - q.[vi].Add(p) |> ignore + q.[vi].Add p |> ignore member this.Remove (value : byte) (p : Point) = let vi = int value - if q.[vi].Remove(p) && q.[vi].Count = 0 then + if q.[vi].Remove p && q.[vi].Count = 0 then if vi = highest then highest <- highest - 1 while highest > lowest && q.[highest].Count = 0 do @@ -173,7 +173,7 @@ type PriorityQueue () = member this.Clear () = while highest > lowest do - q.[highest].Clear() + q.[highest].Clear () highest <- highest - 1 highest <- -1 lowest <- size @@ -199,33 +199,33 @@ let private areaOperation (img : Image) (area : int) (op : AreaOpera let imgData = img.Data let se = [| -1, 0; 0, -1; 1, 0; 0, 1 |] - let areas = List((if op = AreaOperation.Opening then findMaxima img else findMinima img) |> Seq.map Area) + let areas = List ((if op = AreaOperation.Opening then findMaxima img else findMinima img) |> Seq.map Area) let pixels : Area[,] = Array2D.create h w null for m in areas do for e in m.Elements do pixels.[e.Y, e.X] <- m - let queue = PriorityQueue() + let queue = PriorityQueue () let addEdgeToQueue (elements : Points) = for p in elements do for i, j in se do let ni = i + p.Y let nj = j + p.X - let p' = Point(nj, ni) - if ni >= 0 && ni < h && nj >= 0 && nj < w && not (elements.Contains(p')) then + let p' = Point (nj, ni) + if ni >= 0 && ni < h && nj >= 0 && nj < w && not (elements.Contains p') then queue.Add (imgData.[ni, nj, 0]) p' // Reverse order is quicker. for i = areas.Count - 1 downto 0 do let m = areas.[i] if m.Elements.Count <= area && m.State <> AreaState.Removed then - queue.Clear() + queue.Clear () addEdgeToQueue m.Elements let mutable intensity = if op = AreaOperation.Opening then queue.Max else queue.Min - let nextElements = Points() + let nextElements = Points () let mutable stop = false while not stop do @@ -238,10 +238,10 @@ let private areaOperation (img : Image) (area : int) (op : AreaOpera m.Intensity <- Some intensity stop <- true else - nextElements.Add(p) |> ignore + nextElements.Add p |> ignore elif (if op = AreaOperation.Opening then intensity' < intensity else intensity' > intensity) then - m.Elements.UnionWith(nextElements) + m.Elements.UnionWith nextElements for e in nextElements do pixels.[e.Y, e.X] <- m @@ -251,8 +251,8 @@ let private areaOperation (img : Image) (area : int) (op : AreaOpera stop <- true else intensity <- intensity' - nextElements.Clear() - nextElements.Add(p) |> ignore + nextElements.Clear () + nextElements.Add p |> ignore else match pixels.[p.Y, p.X] with @@ -264,11 +264,11 @@ let private areaOperation (img : Image) (area : int) (op : AreaOpera pixels.[e.Y, e.X] <- m queue.Remove imgData.[e.Y, e.X, 0] e addEdgeToQueue m'.Elements - m.Elements.UnionWith(m'.Elements) + m.Elements.UnionWith m'.Elements let intensityMax = if op = AreaOperation.Opening then queue.Max else queue.Min if intensityMax <> intensity then intensity <- intensityMax - nextElements.Clear() + nextElements.Clear () merged <- true if not merged then @@ -280,19 +280,19 @@ let private areaOperation (img : Image) (area : int) (op : AreaOpera for i, j in se do let ni = i + p.Y let nj = j + p.X - let p' = Point(nj, ni) + let p' = Point (nj, ni) if ni < 0 || ni >= h || nj < 0 || nj >= w then m.State <- AreaState.Validated m.Intensity <- Some (intensity) stop <- true - elif not (m.Elements.Contains(p')) && not (nextElements.Contains(p')) then + elif not (m.Elements.Contains p') && not (nextElements.Contains p') then queue.Add (imgData.[ni, nj, 0]) p' if queue.IsEmpty then if m.Elements.Count + nextElements.Count <= area then m.State <- AreaState.Validated m.Intensity <- Some intensity' - m.Elements.UnionWith(nextElements) + m.Elements.UnionWith nextElements stop <- true for m in areas do @@ -331,8 +331,8 @@ let areaOpen2 (img : Image) (area : int) = let flooded : bool[,] = Array2D.zeroCreate h w - let pointsChecked = HashSet() - let pointsToCheck = Stack() + let pointsChecked = HashSet () + let pointsToCheck = Stack () for level = 255 downto 0 do let mutable n = histogram.[level] @@ -341,22 +341,22 @@ let areaOpen2 (img : Image) (area : int) = for j = 0 to w - 1 do if not flooded.[i, j] && imgData.[i, j, 0] = byte level then let mutable maxNeighborValue = 0uy - pointsChecked.Clear() - pointsToCheck.Clear() - pointsToCheck.Push(Point(j, i)) + pointsChecked.Clear () + pointsToCheck.Clear () + pointsToCheck.Push (Point (j, i)) while pointsToCheck.Count > 0 do - let next = pointsToCheck.Pop() - pointsChecked.Add(next) |> ignore + let next = pointsToCheck.Pop () + pointsChecked.Add next |> ignore flooded.[next.Y, next.X] <- true for nx, ny in se do - let p = Point(next.X + nx, next.Y + ny) + let p = Point (next.X + nx, next.Y + ny) if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h then let v = imgData.[p.Y, p.X, 0] if v = byte level then - if not (pointsChecked.Contains(p)) then - pointsToCheck.Push(p) + if not (pointsChecked.Contains p) then + pointsToCheck.Push p elif v > maxNeighborValue then maxNeighborValue <- v @@ -366,7 +366,7 @@ let areaOpen2 (img : Image) (area : int) = [] type Island (cmp : IComparer) = - member val Shore = Heap.Heap(cmp) with get + member val Shore = Heap.Heap cmp with get member val Level = 0.f with get, set member val Surface = 0 with get, set member this.IsInfinite = this.Surface = Int32.MaxValue @@ -379,29 +379,29 @@ let private areaOperationF (img : Image) (areas : (int * 'a) list let comparer = if op = AreaOperation.Opening then - { new IComparer with member this.Compare(v1, v2) = v1.CompareTo(v2) } + { new IComparer with member this.Compare (v1, v2) = v1.CompareTo v2 } else - { new IComparer with member this.Compare(v1, v2) = v2.CompareTo(v1) } + { new IComparer with member this.Compare (v1, v2) = v2.CompareTo v1 } let ownership : Island[,] = Array2D.create h w null // Initialize islands with their shore. - let islands = List() + let islands = List () let extremum = img |> if op = AreaOperation.Opening then findMaxima else findMinima for e in extremum do let island = - let p = e.First() - Island(comparer, Level = earth.[p.Y, p.X, 0], Surface = e.Count) - islands.Add(island) - let shorePoints = Points() + let p = e.First () + Island (comparer, Level = earth.[p.Y, p.X, 0], Surface = e.Count) + islands.Add island + let shorePoints = Points () for p in e do ownership.[p.Y, p.X] <- island for i, j in se do let ni = i + p.Y let nj = j + p.X - let neighbor = Point(nj, ni) - if ni >= 0 && ni < h && nj >= 0 && nj < w && Object.ReferenceEquals(ownership.[ni, nj], null) && not (shorePoints.Contains(neighbor)) then - shorePoints.Add(neighbor) |> ignore + let neighbor = Point (nj, ni) + if ni >= 0 && ni < h && nj >= 0 && nj < w && Object.ReferenceEquals (ownership.[ni, nj], null) && not (shorePoints.Contains neighbor) then + shorePoints.Add neighbor |> ignore island.Shore.Add earth.[ni, nj, 0] neighbor for area, obj in areas do @@ -422,23 +422,22 @@ let private areaOperationF (img : Image) (areas : (int * 'a) list if other = island then // During merging, some points on the shore may be owned by the island itself -> ignored. island.Shore.RemoveNext () else - if not <| Object.ReferenceEquals(other, null) then + if not <| Object.ReferenceEquals (other, null) then // We touching another island. - if island.IsInfinite || other.IsInfinite || island.Surface + other.Surface >= area || comparer.Compare(island.Level, other.Level) < 0 then + if island.IsInfinite || other.IsInfinite || island.Surface + other.Surface >= area || comparer.Compare (island.Level, other.Level) < 0 then stop <- true else // We can merge 'other' into 'surface'. island.Surface <- island.Surface + other.Surface island.Level <- other.Level - // island.Level <- if comparer.Compare(island.Level, other.Level) > 0 then other.Level else island.Level for l, p in other.Shore do let mutable currentY = p.Y + 1 while currentY < h && ownership.[currentY, p.X] = other do ownership.[currentY, p.X] <- island currentY <- currentY + 1 island.Shore.Add l p - other.Shore.Clear() + other.Shore.Clear () - elif comparer.Compare(level, island.Level) > 0 then + elif comparer.Compare (level, island.Level) > 0 then stop <- true else island.Shore.RemoveNext () @@ -449,7 +448,7 @@ let private areaOperationF (img : Image) (areas : (int * 'a) list island.Surface <- Int32.MaxValue stop <- true else - let neighbor = Point(nj, ni) + let neighbor = Point (nj, ni) if not <| ownedOrAdjacent neighbor then island.Shore.Add earth.[ni, nj, 0] neighbor if not stop then @@ -566,10 +565,10 @@ let removeArea (mat : Matrix) (areaSize : int) = ( 0, -1) // p8 (-1, -1) |] // p9 - use mat' = new Matrix(mat.Size) + use mat' = new Matrix (mat.Size) let w = mat'.Width let h = mat'.Height - mat.CopyTo(mat') + mat.CopyTo mat' let data = mat.Data let data' = mat'.Data @@ -577,19 +576,19 @@ let removeArea (mat : Matrix) (areaSize : int) = for i = 0 to h - 1 do for j = 0 to w - 1 do if data'.[i, j] = 1uy then - let neighborhood = List() - let neighborsToCheck = Stack() - neighborsToCheck.Push(Point(j, i)) + let neighborhood = List () + let neighborsToCheck = Stack () + neighborsToCheck.Push (Point (j, i)) data'.[i, j] <- 0uy while neighborsToCheck.Count > 0 do - let n = neighborsToCheck.Pop() - neighborhood.Add(n) + let n = neighborsToCheck.Pop () + neighborhood.Add n for (ni, nj) in neighbors do let pi = n.Y + ni let pj = n.X + nj if pi >= 0 && pi < h && pj >= 0 && pj < w && data'.[pi, pj] = 1uy then - neighborsToCheck.Push(Point(pj, pi)) + neighborsToCheck.Push (Point (pj, pi)) data'.[pi, pj] <- 0uy if neighborhood.Count <= areaSize then for n in neighborhood do @@ -599,19 +598,19 @@ let connectedComponents (img : Image) (startPoints : List) : let w = img.Width let h = img.Height - let pointChecked = Points() - let pointToCheck = Stack(startPoints); + let pointChecked = Points () + let pointToCheck = Stack startPoints; let data = img.Data while pointToCheck.Count > 0 do - let next = pointToCheck.Pop() - pointChecked.Add(next) |> ignore + let next = pointToCheck.Pop () + pointChecked.Add next |> ignore for ny = -1 to 1 do for nx = -1 to 1 do if ny <> 0 && nx <> 0 then - let p = Point(next.X + nx, next.Y + ny) + let p = Point (next.X + nx, next.Y + ny) if p.X >= 0 && p.X < w && p.Y >= 0 && p.Y < h && data.[p.Y, p.X, 0] > 0uy && not (pointChecked.Contains p) then - pointToCheck.Push(p) + pointToCheck.Push p pointChecked diff --git a/Parasitemia/ParasitemiaCore/KMeans.fs b/Parasitemia/ParasitemiaCore/KMeans.fs index dfd2593..c0276d9 100644 --- a/Parasitemia/ParasitemiaCore/KMeans.fs +++ b/Parasitemia/ParasitemiaCore/KMeans.fs @@ -20,10 +20,10 @@ let kmeans (img : Image) : Result = let h = img.Height let min = ref [| 0.0 |] - let minLocation = ref <| [| Point() |] + let minLocation = ref <| [| Point () |] let max = ref [| 0.0 |] - let maxLocation = ref <| [| Point() |] - img.MinMax(min, max, minLocation, maxLocation) + let maxLocation = ref <| [| Point () |] + img.MinMax (min, max, minLocation, maxLocation) let minf = float32 (!min).[0] let maxf = float32 (!max).[0] @@ -32,7 +32,7 @@ let kmeans (img : Image) : Result = let mutable mean_fg = minf + (maxf - minf) / 4.f use mutable d_bg : Image = null let mutable d_fg : Image = null - let fg = new Image(img.Size) + let fg = new Image (img.Size) let imgData = img.Data let fgData = fg.Data @@ -41,14 +41,14 @@ let kmeans (img : Image) : Result = match d_bg with | null -> () | _ -> - d_bg.Dispose() - d_fg.Dispose() + d_bg.Dispose () + d_fg.Dispose () // EmGu doesn't import the in-place version of 'AbsDiff' so we have to create two images for each iteration. - d_bg <- img.AbsDiff(Gray(float mean_bg)) - d_fg <- img.AbsDiff(Gray(float mean_fg)) + d_bg <- img.AbsDiff (Gray (float mean_bg)) + d_fg <- img.AbsDiff (Gray (float mean_fg)) - CvInvoke.Compare(d_fg, d_bg, fg, CvEnum.CmpType.LessThan) + CvInvoke.Compare (d_fg, d_bg, fg, CvEnum.CmpType.LessThan) let mutable bg_total = 0.f let mutable bg_nb = 0 diff --git a/Parasitemia/ParasitemiaCore/KMedians.fs b/Parasitemia/ParasitemiaCore/KMedians.fs index bae7a24..b2ac738 100644 --- a/Parasitemia/ParasitemiaCore/KMedians.fs +++ b/Parasitemia/ParasitemiaCore/KMedians.fs @@ -20,35 +20,35 @@ let kmedians (img : Image) : Result = let h = img.Height let min = ref [| 0.0 |] - let minLocation = ref <| [| Point() |] + let minLocation = ref <| [| Point () |] let max = ref [| 0.0 |] - let maxLocation = ref <| [| Point() |] - img.MinMax(min, max, minLocation, maxLocation) + let maxLocation = ref <| [| Point () |] + img.MinMax (min, max, minLocation, maxLocation) let mutable median_bg = (!max).[0] - ((!max).[0] - (!min).[0]) / 4.0 let mutable median_fg = (!min).[0] + ((!max).[0] - (!min).[0]) / 4.0 - use mutable d_bg = new Image(img.Size) - let mutable d_fg = new Image(img.Size) - let mutable fg = new Image(img.Size) + use mutable d_bg = new Image (img.Size) + let mutable d_fg = new Image (img.Size) + let mutable fg = new Image (img.Size) for i = 1 to nbIteration do - d_bg <- img.AbsDiff(Gray(median_bg)) - d_fg <- img.AbsDiff(Gray(median_fg)) + d_bg <- img.AbsDiff (Gray median_bg) + d_fg <- img.AbsDiff (Gray median_fg) - CvInvoke.Compare(d_fg, d_bg, fg, CvEnum.CmpType.LessThan) + CvInvoke.Compare (d_fg, d_bg, fg, CvEnum.CmpType.LessThan) - let bg_values = List() - let fg_values = List() + let bg_values = List () + let fg_values = List () for i = 0 to h - 1 do for j = 0 to w - 1 do if fg.Data.[i, j, 0] > 0uy then - fg_values.Add(float img.Data.[i, j, 0]) + fg_values.Add (float img.Data.[i, j, 0]) else - bg_values.Add(float img.Data.[i, j, 0]) + bg_values.Add (float img.Data.[i, j, 0]) - median_bg <- MathNet.Numerics.Statistics.Statistics.Median(bg_values) - median_fg <- MathNet.Numerics.Statistics.Statistics.Median(fg_values) + median_bg <- MathNet.Numerics.Statistics.Statistics.Median bg_values + median_fg <- MathNet.Numerics.Statistics.Statistics.Median fg_values { fg = fg; median_bg = median_bg; median_fg = median_fg; d_fg = d_fg } diff --git a/Parasitemia/ParasitemiaCore/KdTree.fs b/Parasitemia/ParasitemiaCore/KdTree.fs index f98cfa2..2525e27 100644 --- a/Parasitemia/ParasitemiaCore/KdTree.fs +++ b/Parasitemia/ParasitemiaCore/KdTree.fs @@ -8,17 +8,17 @@ type I2DCoords = // Compare 'e1' and 'e2' by X. let cmpX (e1 : I2DCoords) (e2 : I2DCoords) : int = - match e1.X.CompareTo(e2.X) with - | 0 -> match e1.Y.CompareTo(e2.Y) with - | 0 -> e1.GetHashCode().CompareTo(e2.GetHashCode()) + match e1.X.CompareTo e2.X with + | 0 -> match e1.Y.CompareTo e2.Y with + | 0 -> e1.GetHashCode().CompareTo (e2.GetHashCode ()) | v -> v | v -> v // Compare 'e1' and 'e2' by Y. let cmpY (e1 : I2DCoords) (e2 : I2DCoords) : int = - match e1.Y.CompareTo(e2.Y) with - | 0 -> match e1.X.CompareTo(e2.X) with - | 0 -> e1.GetHashCode().CompareTo(e2.GetHashCode()) + match e1.Y.CompareTo e2.Y with + | 0 -> match e1.X.CompareTo e2.X with + | 0 -> e1.GetHashCode().CompareTo (e2.GetHashCode ()) | v -> v | v -> v diff --git a/Parasitemia/ParasitemiaCore/MatchingEllipses.fs b/Parasitemia/ParasitemiaCore/MatchingEllipses.fs index 13fd556..03a3869 100644 --- a/Parasitemia/ParasitemiaCore/MatchingEllipses.fs +++ b/Parasitemia/ParasitemiaCore/MatchingEllipses.fs @@ -34,10 +34,10 @@ type private EllipseScoreFlaggedKd (matchingScore : float32, e : Ellipse) = member this.Y = this.Ellipse.Cy type MatchingEllipses (radius : float32) = - let ellipses = List() + let ellipses = List () member this.Add (e : Ellipse) = - ellipses.Add(EllipseScoreFlaggedKd(0.f, e)) + ellipses.Add (EllipseScoreFlaggedKd (0.f, e)) member this.Ellipses : Ellipse list = List.ofSeq ellipses |> List.map (fun e -> e.Ellipse) @@ -69,8 +69,8 @@ type MatchingEllipses (radius : float32) = // Because of approximation error, see https://github.com/chraibi/EEOver/issues/4 when overlapArea - areaE < 1.f && overlapArea - areaOther < 1.f -> let matchingScore = (2.f * overlapArea / (areaE + areaOther)) ** matchingScorePower - other.AddMatchingScore(matchingScore) - e.AddMatchingScore(matchingScore) + other.AddMatchingScore matchingScore + e.AddMatchingScore matchingScore | _ -> () // 3) Remove ellipses whose center is near the center of another ellipse with a better score. @@ -89,7 +89,7 @@ type MatchingEllipses (radius : float32) = for other in tree.Search window do if not other.Removed && e.MatchingScore > other.MatchingScore then // Case where ellipses are too close. - if distanceTwoPoints (PointF(e.Ellipse.Cx, e.Ellipse.Cy)) (PointF(other.Ellipse.Cx, other.Ellipse.Cy)) < minimumDistanceFromCenterRadiusFactor * e.Ellipse.B then + if distanceTwoPoints (PointF (e.Ellipse.Cx, e.Ellipse.Cy)) (PointF (other.Ellipse.Cx, other.Ellipse.Cy)) < minimumDistanceFromCenterRadiusFactor * e.Ellipse.B then other.Removed <- true else // Case where ellipses are overlapped. @@ -101,6 +101,6 @@ type MatchingEllipses (radius : float32) = ellipses |> List.ofSeq |> List.filter (fun e -> not e.Removed) - |> List.sortWith (fun e1 e2 -> e2.MatchingScore.CompareTo(e1.MatchingScore)) + |> List.sortWith (fun e1 e2 -> e2.MatchingScore.CompareTo e1.MatchingScore) |> List.map (fun e -> e.Ellipse) diff --git a/Parasitemia/ParasitemiaCore/ParasitemiaCore.fsproj b/Parasitemia/ParasitemiaCore/ParasitemiaCore.fsproj index 5a14573..c841f71 100644 --- a/Parasitemia/ParasitemiaCore/ParasitemiaCore.fsproj +++ b/Parasitemia/ParasitemiaCore/ParasitemiaCore.fsproj @@ -91,7 +91,7 @@ True - ..\packages\FSharp.Core.4.1.17\lib\net45\FSharp.Core.dll + ..\packages\FSharp.Core.4.2.1\lib\net45\FSharp.Core.dll ..\packages\MathNet.Numerics.3.19.0\lib\net40\MathNet.Numerics.dll diff --git a/Parasitemia/ParasitemiaCore/ParasitesMarker.fs b/Parasitemia/ParasitemiaCore/ParasitesMarker.fs index 263e35e..b89caa6 100644 --- a/Parasitemia/ParasitemiaCore/ParasitesMarker.fs +++ b/Parasitemia/ParasitemiaCore/ParasitesMarker.fs @@ -21,7 +21,7 @@ type Result = let find (img : Image) (config : Config.Config) : Result * Image * Image = - let imgWithoutNucleus = img.Copy() + let imgWithoutNucleus = img.Copy () areaCloseF imgWithoutNucleus (roundInt config.RBCRadius.NucleusArea) let darkStain = @@ -29,30 +29,30 @@ let find (img : Image) (config : Config.Config) : Result * Image< let _, mean_fg, mean_bg = let hist = histogramImg imgWithoutNucleus 300 otsu hist - imgWithoutNucleus.Cmp(float mean_fg - config.Parameters.darkStainLevel * float (mean_bg - mean_fg), CvEnum.CmpType.LessThan) + imgWithoutNucleus.Cmp (float mean_fg - config.Parameters.darkStainLevel * float (mean_bg - mean_fg), CvEnum.CmpType.LessThan) let marker (img : Image) (closed : Image) (level : float) : Image = - let diff = img.Copy() - diff._Mul(level) - CvInvoke.Subtract(closed, diff, diff) - diff._ThresholdBinary(Gray(0.0), Gray(255.)) - diff.Convert() + let diff = img.Copy () + diff._Mul level + CvInvoke.Subtract (closed, diff, diff) + diff._ThresholdBinary (Gray 0.0, Gray 255.) + diff.Convert () // Nucleus. let nucleusMarker = marker img imgWithoutNucleus (1. / config.Parameters.infectionSensitivity) // Cytoplasm. - let imgWithoutParasite = img.CopyBlank() + let imgWithoutParasite = img.CopyBlank () let kernelSize = let size = roundInt config.RBCRadius.CytoplasmSize if size % 2 = 0 then size + 1 else size use kernel = if kernelSize <= 3 then - CvInvoke.GetStructuringElement(CvEnum.ElementShape.Rectangle, Size(3, 3), Point(-1, -1)) + CvInvoke.GetStructuringElement (CvEnum.ElementShape.Rectangle, Size (3, 3), Point (-1, -1)) else - CvInvoke.GetStructuringElement(CvEnum.ElementShape.Ellipse, Size(kernelSize, kernelSize), Point(-1, -1)) + CvInvoke.GetStructuringElement (CvEnum.ElementShape.Ellipse, Size (kernelSize, kernelSize), Point (-1, -1)) - CvInvoke.MorphologyEx(img, imgWithoutParasite, CvEnum.MorphOp.Close, kernel, Point(-1, -1), 1, CvEnum.BorderType.Replicate, MCvScalar()) + CvInvoke.MorphologyEx (img, imgWithoutParasite, CvEnum.MorphOp.Close, kernel, Point(-1, -1), 1, CvEnum.BorderType.Replicate, MCvScalar()) let parasiteMarker = marker img imgWithoutParasite (1. / config.Parameters.cytoplasmSensitivity) { diff --git a/Parasitemia/ParasitemiaCore/Types.fs b/Parasitemia/ParasitemiaCore/Types.fs index c1a635d..8f526f9 100644 --- a/Parasitemia/ParasitemiaCore/Types.fs +++ b/Parasitemia/ParasitemiaCore/Types.fs @@ -36,14 +36,14 @@ type Ellipse (cx : float32, cy : float32, a : float32, b : float32, alpha : floa this.CutAnHorizontalLine 0.f || this.CutAnHorizontalLine height member this.Scale (factor : float32) : Ellipse = - Ellipse(this.Cx, this.Cy, this.A * factor, this.B * factor, alpha) + Ellipse (this.Cx, this.Cy, this.A * factor, this.B * factor, alpha) // Approximation of Ramanujan. member this.Perimeter = PI * (3.f * (this.A + this.B) - sqrt ((3.f * this.A + this.B) * (this.A + 3.f * this.B))) override this.ToString () = - sprintf "(cx: %f, cy: %f, a: %f, b: %f, alpha: %f)" this.Cx this.Cy this.A this.B this.Alpha + sprintf "{Ellipse: cx = %f, cy = %f, a = %f, b = %f, alpha = %f}" this.Cx this.Cy this.A this.B this.Alpha type CellClass = HealthyRBC | InfectedRBC | Peculiar @@ -71,16 +71,16 @@ type MaybeBuilder () = member this.TryFinally (body, compensation) = try - this.ReturnFrom(body()) + this.ReturnFrom (body ()) finally - compensation() + compensation () member this.Using (disposable : 'a when 'a :> IDisposable, body) = let body' = fun () -> body disposable - this.TryFinally(body', fun () -> + this.TryFinally (body', fun () -> match disposable with | null -> () - | disp -> disp.Dispose()) + | disp -> disp.Dispose ()) member this.Zero () = None @@ -88,7 +88,7 @@ type MaybeBuilder () = member this.Return x = Some x -let maybe = MaybeBuilder() +let maybe = MaybeBuilder () type Result<'a> = | Success of 'a @@ -102,4 +102,4 @@ type ResultBuilder () = member this.ReturnFrom (x) = x -let result = ResultBuilder() \ No newline at end of file +let result = ResultBuilder () \ No newline at end of file diff --git a/Parasitemia/ParasitemiaCore/UnitsOfMeasure.fs b/Parasitemia/ParasitemiaCore/UnitsOfMeasure.fs index 8202b8e..4af1e55 100644 --- a/Parasitemia/ParasitemiaCore/UnitsOfMeasure.fs +++ b/Parasitemia/ParasitemiaCore/UnitsOfMeasure.fs @@ -9,9 +9,9 @@ let μmPerInch = 25.4e3<μm/inch> let mmPerInch = 25.4 -let μmToInch(x : float<μm>) : float = x / μmPerInch -let inchToμm(x : float) : float<μm> = x * μmPerInch +let μmToInch (x : float<μm>) : float = x / μmPerInch +let inchToμm (x : float) : float<μm> = x * μmPerInch -let mmToInch(x : float) : float = x / mmPerInch -let inchTomm(x : float) : float = x * mmPerInch +let mmToInch (x : float) : float = x / mmPerInch +let inchTomm (x : float) : float = x * mmPerInch diff --git a/Parasitemia/ParasitemiaCore/Utils.fs b/Parasitemia/ParasitemiaCore/Utils.fs index 77e5e6f..6a13337 100644 --- a/Parasitemia/ParasitemiaCore/Utils.fs +++ b/Parasitemia/ParasitemiaCore/Utils.fs @@ -13,12 +13,12 @@ let inline dprintfn fmt = let inline lineFromTwoPoints (p1 : PointF) (p2 : PointF) : Line = let a = (p1.Y - p2.Y) / (p1.X - p2.X) let b = -(p2.X * p1.Y - p1.X * p2.Y) / (p1.X - p2.X) - Line(a, b) + Line (a, b) let inline pointFromTwoLines (l1 : Line) (l2 : Line) : PointF = let x = -(l1.B - l2.B) / (l1.A - l2.A) let y = -(l2.A * l1.B - l1.A * l2.B) / (l1.A - l2.A) - PointF(x, y) + PointF (x, y) let inline linePassThroughSegment (l : Line) (p1 : PointF) (p2 : PointF) : bool = let p = pointFromTwoLines l (lineFromTwoPoints p1 p2) diff --git a/Parasitemia/ParasitemiaCore/packages.config b/Parasitemia/ParasitemiaCore/packages.config index 91a2fc7..d0073d5 100644 --- a/Parasitemia/ParasitemiaCore/packages.config +++ b/Parasitemia/ParasitemiaCore/packages.config @@ -2,7 +2,7 @@ - + diff --git a/Parasitemia/ParasitemiaUI/About.fs b/Parasitemia/ParasitemiaUI/About.fs index 452573a..8c643b7 100644 --- a/Parasitemia/ParasitemiaUI/About.fs +++ b/Parasitemia/ParasitemiaUI/About.fs @@ -2,14 +2,11 @@ open System open System.Windows -open System.Windows.Media -open System.Windows.Markup -open System.Windows.Shapes open System.Windows.Controls open System.Diagnostics let showWindow (parent : Window) = - let win = Views.AboutWindow() + let win = Views.AboutWindow () win.Owner <- parent win.Left <- (if parent.WindowState = WindowState.Maximized then 0. else parent.Left) + parent.ActualWidth / 2. - win.Width / 2. @@ -17,21 +14,24 @@ let showWindow (parent : Window) = let version = System.Reflection.Assembly.GetEntryAssembly().GetName().Version let txtVersion = sprintf " %d.%d.%d" version.Major version.Minor version.Revision - win.txtAbout.Inlines.FirstInline.ElementEnd.InsertTextInRun(txtVersion) + win.txtAbout.Inlines.FirstInline.ElementEnd.InsertTextInRun txtVersion - let navigateTo = Navigation.RequestNavigateEventHandler(fun obj args -> - Process.Start(ProcessStartInfo(args.Uri.AbsoluteUri)) |> ignore - args.Handled <- true) + let navigateTo = + Navigation.RequestNavigateEventHandler ( + fun obj args -> + Process.Start (ProcessStartInfo args.Uri.AbsoluteUri) |> ignore + args.Handled <- true + ) - win.linkHESSO.RequestNavigate.AddHandler(navigateTo); - win.linkCHUV.RequestNavigate.AddHandler(navigateTo); - win.linkGBurri.RequestNavigate.AddHandler(navigateTo); + win.linkHESSO.RequestNavigate.AddHandler navigateTo; + win.linkCHUV.RequestNavigate.AddHandler navigateTo; + win.linkGBurri.RequestNavigate.AddHandler navigateTo; #if DEBUG - win.txtAbout.Inlines.FirstInline.ElementEnd.InsertTextInRun(" - DEBUG") + win.txtAbout.Inlines.FirstInline.ElementEnd.InsertTextInRun " - DEBUG" #endif - win.butClose.Click.AddHandler(fun obj args -> win.Close()) + win.butClose.Click.AddHandler (fun obj args -> win.Close ()) - win.ShowDialog() |> ignore + win.ShowDialog () |> ignore diff --git a/Parasitemia/ParasitemiaUI/Analysis.fs b/Parasitemia/ParasitemiaUI/Analysis.fs index e276be6..4a75a91 100644 --- a/Parasitemia/ParasitemiaUI/Analysis.fs +++ b/Parasitemia/ParasitemiaUI/Analysis.fs @@ -19,7 +19,7 @@ open ParasitemiaCore.Config open Types let showWindow (parent : Window) (state : State.State) : bool = - let win = Views.AnalysisWindow() + let win = Views.AnalysisWindow () win.Owner <- parent win.Left <- (if parent.WindowState = WindowState.Maximized then 0. else parent.Left) + parent.ActualWidth / 2. - win.Width / 2. win.Top <- (if parent.WindowState = WindowState.Maximized then 0. else parent.Top) + parent.ActualHeight / 2. - win.Height / 2. @@ -28,62 +28,69 @@ let showWindow (parent : Window) (state : State.State) : bool = { new Logger.IListener with member this.NewEntry severity _header mess = - win.Dispatcher.Invoke(fun () -> - win.textLog.Inlines.Add(Documents.Run(mess)) - win.textLog.Inlines.Add(Documents.LineBreak()) - win.scrollLog.ScrollToBottom() + win.Dispatcher.Invoke ( + fun () -> + win.textLog.Inlines.Add (Documents.Run mess) + win.textLog.Inlines.Add (Documents.LineBreak ()) + win.scrollLog.ScrollToBottom () ) } - Logger.Log.AddListener(logListener) + Logger.Log.AddListener (logListener) let minPPI = 1. let maxPPI = 10e6 let parseAndValidatePPI (input : string) : float option = - match Double.TryParse(input) with + match Double.TryParse input with | true, value when value >= minPPI && value <= maxPPI -> Some value | _ -> None - let monitor = Object() + let monitor = Object () let mutable atLeastOneAnalysisPerformed = false let mutable analysisPerformed = false let mutable analysisCancelled = false let updateSourceImages () = - win.stackSourceImagesSelection.Children.Clear() + win.stackSourceImagesSelection.Children.Clear () let width = int win.stackSourceImagesSelection.ActualWidth for srcImg in state.SourceImages do - let imageSourceSelection = Views.ImageSourceSelection(Tag = srcImg, Margin = Thickness(3.)) + let imageSourceSelection = Views.ImageSourceSelection (Tag = srcImg, Margin = Thickness 3.) imageSourceSelection.Tag <- srcImg - imageSourceSelection.txtImageNumber.Text <- srcImg.num.ToString() + 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 srcImg.dateLastAnalysis.ToString() + 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 srcImg.config.Parameters.resolution.ToString() + imageSourceSelection.txtResolution.Text <- if srcImg.dateLastAnalysis.Ticks = 0L then "" else string srcImg.config.Parameters.resolution for ppi in Utils.predefinedPPI do - let menu = MenuItem() - menu.Header <- ppi.ToString() - menu.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- ppi.ppi.ToString()) - imageSourceSelection.predefinedValuesMenu.Items.Add(menu) |> ignore - - imageSourceSelection.butPPICalculator.Click.AddHandler(fun obj args -> - match PPICalculator.showWindow win with - | Some resolution -> imageSourceSelection.txtResolution.Text <- resolution.ToString() - | None -> ()) - - imageSourceSelection.txtResolution.PreviewTextInput.AddHandler(fun obj args -> - let text = imageSourceSelection.txtResolution.Text + args.Text - args.Handled <- match parseAndValidatePPI text with Some _ -> false | None -> true) - - imageSourceSelection.imagePreview.MouseLeftButtonDown.AddHandler(fun obj args -> - let checkbox = imageSourceSelection.chkSelection - checkbox.IsChecked <- Nullable(not (checkbox.IsChecked.HasValue && checkbox.IsChecked.Value))) - - win.stackSourceImagesSelection.Children.Add(imageSourceSelection) |> ignore + let menu = MenuItem () + menu.Header <- string ppi + menu.Click.AddHandler (fun obj args -> imageSourceSelection.txtResolution.Text <- string ppi.ppi) + imageSourceSelection.predefinedValuesMenu.Items.Add menu |> ignore + + imageSourceSelection.butPPICalculator.Click.AddHandler ( + fun obj args -> + match PPICalculator.showWindow win with + | Some resolution -> imageSourceSelection.txtResolution.Text <- string resolution + | None -> () + ) + + imageSourceSelection.txtResolution.PreviewTextInput.AddHandler ( + fun obj args -> + let text = imageSourceSelection.txtResolution.Text + args.Text + args.Handled <- match parseAndValidatePPI text with Some _ -> false | None -> true + ) + + imageSourceSelection.imagePreview.MouseLeftButtonDown.AddHandler ( + fun obj args -> + let checkbox = imageSourceSelection.chkSelection + checkbox.IsChecked <- Nullable (not (checkbox.IsChecked.HasValue && checkbox.IsChecked.Value)) + ) + + win.stackSourceImagesSelection.Children.Add (imageSourceSelection) |> ignore // Get the new parameters for each image. If an error occurs then 'None' is returned and a message box is displayed. // The boolean is 'true' if the image is selected (checked). @@ -98,71 +105,73 @@ let showWindow (parent : Window) (state : State.State) : bool = | Some resolution -> 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 - if parameters.Count() <> sourceImagesControls.Count() then + if parameters.Count () <> sourceImagesControls.Count () then None else Some parameters - win.butClose.Click.AddHandler(fun obj args -> win.Close()) - - win.butStart.Click.AddHandler(fun obj args -> - match getInputImagesParameters () with - | Some imagesParameters -> - let imagesToProcess = - [ - for srcImg, selected, parameters in imagesParameters do - srcImg.config.Parameters <- parameters // Save parameters. - if selected then - yield srcImg.num.ToString(), srcImg.config, srcImg.img - ] - - if imagesToProcess.IsEmpty then - MessageBox.Show("No image selected", "Cannot start analysis", MessageBoxButton.OK, MessageBoxImage.Information) |> ignore - else - win.stackSourceImagesSelection.IsEnabled <- false - analysisPerformed <- false - win.butStart.IsEnabled <- false - win.butClose.Content <- "Abort" - - async { - let maybeResults = - ParasitemiaCore.Analysis.doMultipleAnalysis - imagesToProcess - (Some (fun progress -> win.Dispatcher.Invoke(fun () -> win.progress.Value <- float progress); not analysisCancelled)) - - lock monitor ( - fun() -> - match maybeResults with - | Some results -> - for id, cells in results do - state.SetResult (int id) cells - Logger.Log.Info "All analyses terminated successfully" - atLeastOneAnalysisPerformed <- true - analysisPerformed <- true - | None -> - Logger.Log.Info "Analysis aborted" - - win.Dispatcher.Invoke(fun () -> - win.progress.Value <- if maybeResults.IsSome then 100. else 0. - win.stackSourceImagesSelection.IsEnabled <- true - win.butStart.IsEnabled <- true - win.butClose.Content <- "Close" - updateSourceImages () - ) - ) - } |> Async.Start - | _ -> () + win.butClose.Click.AddHandler (fun obj args -> win.Close ()) + + win.butStart.Click.AddHandler ( + fun obj args -> + match getInputImagesParameters () with + | Some imagesParameters -> + let imagesToProcess = + [ + for srcImg, selected, parameters in imagesParameters do + srcImg.config.Parameters <- parameters // Save parameters. + if selected then + yield string srcImg.num, srcImg.config, srcImg.img + ] + + if imagesToProcess.IsEmpty then + MessageBox.Show ("No image selected", "Cannot start analysis", MessageBoxButton.OK, MessageBoxImage.Information) |> ignore + else + win.stackSourceImagesSelection.IsEnabled <- false + analysisPerformed <- false + win.butStart.IsEnabled <- false + win.butClose.Content <- "Abort" + + async { + let maybeResults = + ParasitemiaCore.Analysis.doMultipleAnalysis + imagesToProcess + (Some (fun progress -> win.Dispatcher.Invoke (fun () -> win.progress.Value <- float progress); not analysisCancelled)) + + lock monitor ( + fun () -> + match maybeResults with + | Some results -> + for id, cells in results do + state.SetResult (int id) cells + Logger.Log.Info "All analyses terminated successfully" + atLeastOneAnalysisPerformed <- true + analysisPerformed <- true + | None -> + Logger.Log.Info "Analysis aborted" + + win.Dispatcher.Invoke ( + fun () -> + win.progress.Value <- if maybeResults.IsSome then 100. else 0. + win.stackSourceImagesSelection.IsEnabled <- true + win.butStart.IsEnabled <- true + win.butClose.Content <- "Close" + updateSourceImages () + ) + ) + } |> Async.Start + | _ -> () ) - win.Loaded.AddHandler(fun obj args -> updateSourceImages ()) + win.Loaded.AddHandler (fun obj args -> updateSourceImages ()) - win.ShowDialog() |> ignore + win.ShowDialog () |> ignore - Logger.Log.RmListener(logListener) + Logger.Log.RmListener (logListener) lock monitor ( fun () -> diff --git a/Parasitemia/ParasitemiaUI/AssemblyInfo.fs b/Parasitemia/ParasitemiaUI/AssemblyInfo.fs index 3814371..d0754f8 100644 --- a/Parasitemia/ParasitemiaUI/AssemblyInfo.fs +++ b/Parasitemia/ParasitemiaUI/AssemblyInfo.fs @@ -7,22 +7,22 @@ open System.Runtime.InteropServices // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[] -[] -[] -[] -[] -[] -[] -[] +[] +[] +[] +[] +[] +[] +[] +[] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. -[] +[] // The following GUID is for the ID of the typelib if this project is exposed to COM -[] +[] // Version information for an assembly consists of the following four values: // @@ -33,9 +33,9 @@ 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/DPICalculator.fs b/Parasitemia/ParasitemiaUI/DPICalculator.fs index c8d25c8..2cdc184 100644 --- a/Parasitemia/ParasitemiaUI/DPICalculator.fs +++ b/Parasitemia/ParasitemiaUI/DPICalculator.fs @@ -14,13 +14,13 @@ open ParasitemiaCore.UnitsOfMeasure open Types let showWindow (parent : Window) : int option = - let win = Views.PPICalculatorWindow() + let win = Views.PPICalculatorWindow () win.Owner <- parent win.Left <- parent.Left + parent.ActualWidth / 2. - win.Width / 2. win.Top <- parent.Top + parent.ActualHeight / 2. - win.Height / 2. for size in Utils.sensorSizes do - win.cmbSensorSize.Items.Add(size) |> ignore + win.cmbSensorSize.Items.Add (size) |> ignore win.cmbSensorSize.SelectedIndex <- 0 let resolution (w_p : float) (w_mm : float) (zoom : float) : float = @@ -30,7 +30,7 @@ let showWindow (parent : Window) : int option = let { w = w; h = h } = win.cmbSensorSize.SelectedValue :?> SensorSize let ratio = h / w - let parseDouble txt errorMess = match Double.TryParse(txt) with true, value -> Success value | _ -> Fail errorMess + let parseDouble txt errorMess = match Double.TryParse (txt) with true, value -> Success value | _ -> Fail errorMess match (result { @@ -39,17 +39,17 @@ let showWindow (parent : Window) : int option = let wPixel = 1. * sqrt (sensorResolution * 1e6 / ratio) return! Success (float <| resolution wPixel w zoom) }) with - | Success res -> win.txtImageResolution.Text <- (int (res / 1000.) * 1000).ToString() + | Success res -> win.txtImageResolution.Text <- int (res / 1000.) * 1000 |> string | Fail mess -> win.txtImageResolution.Text <- mess - win.butCancel.Click.AddHandler(fun obj args -> win.DialogResult <- Nullable(false); win.Close()) - win.butOK.Click.AddHandler(fun obj args -> win.DialogResult <- Nullable(true); win.Close()) + win.butCancel.Click.AddHandler (fun obj args -> win.DialogResult <- Nullable (false); win.Close ()) + win.butOK.Click.AddHandler (fun obj args -> win.DialogResult <- Nullable (true); win.Close ()) - win.cmbSensorSize.SelectionChanged.AddHandler(fun obj arg -> updateCurrentResolution ()) - win.txtSensorResolution.TextChanged.AddHandler(fun obj arg -> updateCurrentResolution ()) - win.txtZoom.TextChanged.AddHandler(fun obj arg -> updateCurrentResolution ()) + win.cmbSensorSize.SelectionChanged.AddHandler (fun obj arg -> updateCurrentResolution ()) + win.txtSensorResolution.TextChanged.AddHandler (fun obj arg -> updateCurrentResolution ()) + win.txtZoom.TextChanged.AddHandler (fun obj arg -> updateCurrentResolution ()) - let result = win.ShowDialog() + let result = win.ShowDialog () if result.HasValue && result.Value then match Int32.TryParse win.txtImageResolution.Text with | true, res -> Some res diff --git a/Parasitemia/ParasitemiaUI/Export.fs b/Parasitemia/ParasitemiaUI/Export.fs index 29fc0a5..52638d3 100644 --- a/Parasitemia/ParasitemiaUI/Export.fs +++ b/Parasitemia/ParasitemiaUI/Export.fs @@ -7,7 +7,7 @@ open State /// If the results cannot be exported let exportResults (state : State) (filePath : string) = - use writer = new StreamWriter(new FileStream(filePath, FileMode.Create, FileAccess.Write)) + use writer = new StreamWriter (new FileStream (filePath, FileMode.Create, FileAccess.Write)) fprintfn writer "File: %s" state.FilePath fprintfn writer "Export date: %O" DateTime.Now diff --git a/Parasitemia/ParasitemiaUI/GUI.fs b/Parasitemia/ParasitemiaUI/GUI.fs index 35bc2ad..46171a7 100644 --- a/Parasitemia/ParasitemiaUI/GUI.fs +++ b/Parasitemia/ParasitemiaUI/GUI.fs @@ -20,10 +20,10 @@ open ParasitemiaCore.Utils open Types let run (defaultConfig : Config) (fileToOpen : string option) = - let app = new Application() - let win = Views.MainWindow() + let app = new Application () + let win = Views.MainWindow () - let state = State.State(defaultConfig) + let state = State.State defaultConfig let mutable currentScale = 1. let mutable displayHealthy = false let warningBelowNumberOfRBC = 1000 @@ -40,9 +40,12 @@ let run (defaultConfig : Config) (fileToOpen : string option) = let y = rbc.center.Y - rbcHeight / 2. |> roundInt let w = roundInt rbcWidth let h = roundInt rbcHeight - img.GetSubRect(System.Drawing.Rectangle(System.Drawing.Point((if x < 0 then 0 else x), (if y < 0 then 0 else y)), - System.Drawing.Size((if x + w >= img.Width then img.Width - x else w), - (if y + h >= img.Height then img.Height - y else h)))) + img.GetSubRect ( + System.Drawing.Rectangle ( + System.Drawing.Point ((if x < 0 then 0 else x), (if y < 0 then 0 else y)), + System.Drawing.Size ((if x + w >= img.Width then img.Width - x else w), (if y + h >= img.Height then img.Height - y else h)) + ) + ) let setRBCFrameStyle (srcImg : SourceImage) (rbc : RBC) (frame : Views.RBCFrame) = frame.Opacity <- if displayHealthy || rbc.setManually || rbc.infected then 1. else 0. @@ -58,19 +61,19 @@ let run (defaultConfig : Config) (fileToOpen : string option) = frame.Tag <- rbc setRBCFrameStyle srcImg rbc frame frame.border.StrokeThickness <- 1. - frame.txtRBCNumber.Text <- rbc.num.ToString() + frame.txtRBCNumber.Text <- string rbc.num frame let updateDocumentStatus () = win.txtDocumentStatus.Text <- if state.FilePath = "" then "" else state.FilePath - let statusMessageTimer = Threading.DispatcherTimer() - statusMessageTimer.Tick.AddHandler(fun obj args -> statusMessageTimer.Stop(); win.txtMessageStatus.Text <- "") - statusMessageTimer.Interval <- TimeSpan(0, 0, 2) + let statusMessageTimer = Threading.DispatcherTimer () + statusMessageTimer.Tick.AddHandler (fun obj args -> statusMessageTimer.Stop (); win.txtMessageStatus.Text <- "") + statusMessageTimer.Interval <- TimeSpan (0, 0, 2) let displayStatusMessage (message : string) = win.txtMessageStatus.Text <- message - statusMessageTimer.Stop() - statusMessageTimer.Start() + statusMessageTimer.Stop () + statusMessageTimer.Start () let highlightRBCFrame (frame : Views.RBCFrame) (highlight : bool) = let rbc = frame.Tag :?> RBC @@ -82,51 +85,51 @@ let run (defaultConfig : Config) (fileToOpen : string option) = 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) + 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)) + TextChangedEventHandler (fun obj args -> state.CurrentImage |> Option.iter (fun srcImg -> state.SetName srcImg win.txtImageName.Text)) let updateCurrentImageInformation () = - win.txtImageName.TextChanged.RemoveHandler(txtImageName_TextChanged) - win.txtImageInformation1.Inlines.Clear() - win.txtImageInformation2.Inlines.Clear() + win.txtImageName.TextChanged.RemoveHandler (txtImageName_TextChanged) + win.txtImageInformation1.Inlines.Clear () + win.txtImageInformation2.Inlines.Clear () win.txtImageName.Text <- "" match state.CurrentImage with | Some srcImg -> win.gridImageInformation.Visibility <- Visibility.Visible win.txtImageName.Text <- srcImg.name - win.txtImageName.TextChanged.AddHandler(txtImageName_TextChanged) + win.txtImageName.TextChanged.AddHandler txtImageName_TextChanged // The left part. let parasitemiaStr = Utils.percentText (state.ImageParasitemia srcImg) - 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 ("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 srcImg.dateLastAnalysis.ToLocalTime().ToString())) + 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()))) // 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.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 ("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.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.gridImageInformation.Visibility <- Visibility.Hidden let updateGlobalParasitemia () = - win.txtGlobalParasitemia.Inlines.Clear() + win.txtGlobalParasitemia.Inlines.Clear () let total, infected = state.GlobalParasitemia - win.txtGlobalParasitemia.Inlines.Add(Documents.Run(Utils.percentText (total, infected), FontWeight = FontWeights.Bold)) + win.txtGlobalParasitemia.Inlines.Add (Documents.Run (Utils.percentText (total, infected), FontWeight = FontWeights.Bold)) if total > 0 && total < warningBelowNumberOfRBC then - win.txtGlobalParasitemia.Inlines.Add( - Documents.Run( + win.txtGlobalParasitemia.Inlines.Add ( + Documents.Run ( sprintf " Warning: the number of erythrocytes should be above %d" warningBelowNumberOfRBC, FontWeight = FontWeights.Bold, Foreground = Brushes.Red @@ -150,7 +153,7 @@ let run (defaultConfig : Config) (fileToOpen : string option) = let marginBottom = previewHeight * (canvasHeight - (win.scrollViewCurrentImage.VerticalOffset - win.borderCurrentImage.BorderThickness.Bottom) - win.scrollViewCurrentImage.ViewportHeight) / canvasHeight - 2. preview.viewport.Margin <- - Thickness( + Thickness ( marginLeft, marginTop, marginRight, @@ -173,11 +176,11 @@ let run (defaultConfig : Config) (fileToOpen : string option) = updateGlobalParasitemia () and RBCFrame (srcImg : SourceImage) (rbc : RBC) : Views.RBCFrame = - let frame = RBCFrameFromExisting srcImg rbc (Views.RBCFrame()) - 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) - frame.ContextMenuOpening.AddHandler( + let frame = RBCFrameFromExisting srcImg rbc (Views.RBCFrame ()) + 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) + frame.ContextMenuOpening.AddHandler ( fun obj args -> if (frame.Tag :?> RBC).infected then frame.menuRBCSetAsHealthy.Visibility <- Visibility.Visible @@ -186,9 +189,9 @@ let run (defaultConfig : Config) (fileToOpen : string option) = frame.menuRBCSetAsHealthy.Visibility <- Visibility.Collapsed frame.menuRBCSetAsInfected.Visibility <- Visibility.Visible ) - frame.ContextMenuClosing.AddHandler(fun obj args -> if not frame.IsMouseOver then highlightRBCFrame frame false ) - frame.MouseEnter.AddHandler(fun obj args -> highlightRBCFrame frame true) - frame.MouseLeave.AddHandler(fun obj args -> if not frame.grid.ContextMenu.IsOpen then highlightRBCFrame frame false) + frame.ContextMenuClosing.AddHandler (fun obj args -> if not frame.IsMouseOver then highlightRBCFrame frame false ) + frame.MouseEnter.AddHandler (fun obj args -> highlightRBCFrame frame true) + frame.MouseLeave.AddHandler (fun obj args -> if not frame.grid.ContextMenu.IsOpen then highlightRBCFrame frame false) frame and updateRBCFramesPreview () = @@ -201,17 +204,17 @@ let run (defaultConfig : Config) (fileToOpen : string option) = RBCFrameFromExisting srcImg rbc (win.stackRBC.Children.[currentPreview] :?> Views.RBCFrame) else let f = RBCFrame srcImg rbc - f.MouseLeftButtonUp.AddHandler(fun obj args -> zoomToRBC (f.Tag :?> RBC)) - win.stackRBC.Children.Add(f) |> ignore + f.MouseLeftButtonUp.AddHandler (fun obj args -> zoomToRBC (f.Tag :?> RBC)) + win.stackRBC.Children.Add f |> ignore f currentPreview <- currentPreview + 1 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) + win.stackRBC.Children.RemoveRange (currentPreview, win.stackRBC.Children.Count - currentPreview) | _ -> () updateViewportPreview () @@ -226,27 +229,27 @@ let run (defaultConfig : Config) (fileToOpen : string option) = RBCFrameFromExisting srcImg rbc (win.canvasCurrentImage.Children.[currentCanvas] :?> Views.RBCFrame) else let f = RBCFrame srcImg rbc - win.canvasCurrentImage.Children.Add(f) |> ignore + win.canvasCurrentImage.Children.Add f |> ignore f currentCanvas <- currentCanvas + 1 - Canvas.SetLeft(frame, rbc.center.X - rbc.size.Width / 2.) - Canvas.SetTop(frame, rbc.center.Y - rbc.size.Height / 2.) + Canvas.SetLeft (frame, rbc.center.X - rbc.size.Width / 2.) + Canvas.SetTop (frame, rbc.center.Y - rbc.size.Height / 2.) for i in currentCanvas .. win.canvasCurrentImage.Children.Count - 1 do win.canvasCurrentImage.Children.[i].Visibility <- Visibility.Hidden | _ -> () let askDocumentPathToSave () : string option = - let dialog = SaveFileDialog(AddExtension = true, DefaultExt = PiaZ.extension, Filter = PiaZ.filter) + let dialog = SaveFileDialog (AddExtension = true, DefaultExt = PiaZ.extension, Filter = PiaZ.filter) if state.FilePath <> "" then dialog.FileName <- FileInfo(state.FilePath).Name elif state.PatientID <> "" then dialog.FileName <- state.PatientID + PiaZ.extension - let res = dialog.ShowDialog() + let res = dialog.ShowDialog () if res.HasValue && res.Value then Some dialog.FileName else @@ -258,16 +261,16 @@ let run (defaultConfig : Config) (fileToOpen : string option) = match askDocumentPathToSave () with | Some filepath -> state.FilePath <- filepath - state.Save() + state.Save () | _ -> () else - state.Save() + state.Save () updateDocumentStatus () displayStatusMessage "Document saved" with | :? IOException as ex -> Log.Error "%O" ex - MessageBox.Show(sprintf "The document cannot be save in \"%s\"" state.FilePath, "Error saving the document", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore + MessageBox.Show (sprintf "The document cannot be save in \"%s\"" state.FilePath, "Error saving the document", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore let saveCurrentDocumentAsNewFile () = match askDocumentPathToSave () with @@ -279,7 +282,7 @@ let run (defaultConfig : Config) (fileToOpen : string option) = // Ask the use to save the current document if neccessary. let askSaveCurrent () = if state.AlteredSinceLastSave then - match MessageBox.Show("Would you like to save the current document?", "Saving the current document", MessageBoxButton.YesNo, MessageBoxImage.Question) with + match MessageBox.Show ("Would you like to save the current document?", "Saving the current document", MessageBoxButton.YesNo, MessageBoxImage.Question) with | MessageBoxResult.Yes -> saveCurrentDocument () | _ -> () @@ -291,11 +294,11 @@ let run (defaultConfig : Config) (fileToOpen : string option) = // Highlight the preview. win.stackPreviews.Children |> Seq.cast - |> Seq.iter (fun preview -> preview.border.BorderThickness <- Thickness(if preview.Tag = (srcImg :> Object) then 3. else 0.)) + |> 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.Background <- ImageBrush (BitmapSourceConvert.ToBitmapSource (srcImg.img)) updateRBCFramesCurrent () updateRBCFramesPreview () @@ -303,8 +306,8 @@ let run (defaultConfig : Config) (fileToOpen : string option) = | None -> win.imgLogos.Visibility <- Visibility.Visible - win.stackRBC.Children.Clear() - win.canvasCurrentImage.Children.Clear() + win.stackRBC.Children.Clear () + win.canvasCurrentImage.Children.Clear () win.canvasCurrentImage.Background <- canvasCurrentImageColor updateCurrentImageInformation () @@ -315,27 +318,29 @@ let run (defaultConfig : Config) (fileToOpen : string option) = updateCurrentImage () let addPreview (srcImg : SourceImage) = - let imgCtrl = Views.ImageSourcePreview(Margin = Thickness(3.)) + let imgCtrl = Views.ImageSourcePreview (Margin = Thickness 3.) - imgCtrl.menuRemoveImage.Click.AddHandler(fun obj args -> - win.stackPreviews.Children.Remove(imgCtrl) - let srcImg = imgCtrl.Tag :?> SourceImage - let currentRemoved = Some srcImg = state.CurrentImage - state.RemoveSourceImage srcImg - if currentRemoved then - updateCurrentImage() + imgCtrl.menuRemoveImage.Click.AddHandler ( + fun obj args -> + win.stackPreviews.Children.Remove (imgCtrl) + let srcImg = imgCtrl.Tag :?> SourceImage + let currentRemoved = Some srcImg = state.CurrentImage + state.RemoveSourceImage srcImg + if currentRemoved then + updateCurrentImage () - updateGlobalParasitemia() + updateGlobalParasitemia () - // Update image numbers. - win.stackPreviews.Children |> Seq.cast |> Seq.iter (fun imgPreview -> imgPreview.txtImageNumber.Text <- (imgPreview.Tag :?> SourceImage).num.ToString())) + // Update image numbers. + win.stackPreviews.Children |> Seq.cast |> Seq.iter (fun imgPreview -> imgPreview.txtImageNumber.Text <- (imgPreview.Tag :?> SourceImage).num.ToString ()) + ) imgCtrl.Tag <- srcImg - imgCtrl.txtImageNumber.Text <- srcImg.num.ToString() + 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)) - win.stackPreviews.Children.Add(imgCtrl) |> ignore + 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'. let zoomTo (mousePos : Point) = @@ -343,24 +348,27 @@ let run (defaultConfig : Config) (fileToOpen : string option) = let canvasH = win.canvasCurrentImage.ActualHeight * currentScale let centerX = (mousePos.X - imgCtrl.BorderThickness.Left) / (imgCtrl.ActualWidth - imgCtrl.BorderThickness.Left) * canvasW let centerY = (mousePos.Y - imgCtrl.BorderThickness.Top) / (imgCtrl.ActualHeight - imgCtrl.BorderThickness.Top) * canvasH - win.scrollViewCurrentImage.ScrollToHorizontalOffset(centerX - win.scrollViewCurrentImage.ViewportWidth / 2. + win.borderCurrentImage.BorderThickness.Left) - win.scrollViewCurrentImage.ScrollToVerticalOffset(centerY - win.scrollViewCurrentImage.ViewportHeight / 2. + win.borderCurrentImage.BorderThickness.Top) - - imgCtrl.MouseLeftButtonDown.AddHandler(fun obj args -> - setCurrentImage (state.SourceImages |> Seq.find (fun srcImg -> (srcImg :> Object) = imgCtrl.Tag)) - imgCtrl.UpdateLayout() - zoomTo (args.GetPosition(imgCtrl)) - imgCtrl.CaptureMouse() |> ignore + win.scrollViewCurrentImage.ScrollToHorizontalOffset (centerX - win.scrollViewCurrentImage.ViewportWidth / 2. + win.borderCurrentImage.BorderThickness.Left) + win.scrollViewCurrentImage.ScrollToVerticalOffset (centerY - win.scrollViewCurrentImage.ViewportHeight / 2. + win.borderCurrentImage.BorderThickness.Top) + + imgCtrl.MouseLeftButtonDown.AddHandler ( + fun obj args -> + setCurrentImage (state.SourceImages |> Seq.find (fun srcImg -> (srcImg :> Object) = imgCtrl.Tag)) + imgCtrl.UpdateLayout () + zoomTo (args.GetPosition (imgCtrl)) + imgCtrl.CaptureMouse () |> ignore ) - imgCtrl.MouseMove.AddHandler(fun obj args -> - if imgCtrl.IsMouseCaptured then - zoomTo (args.GetPosition(imgCtrl)) + imgCtrl.MouseMove.AddHandler ( + fun obj args -> + if imgCtrl.IsMouseCaptured then + zoomTo (args.GetPosition imgCtrl) ) - imgCtrl.MouseLeftButtonUp.AddHandler(fun obj args -> - if imgCtrl.IsMouseCaptured then - imgCtrl.ReleaseMouseCapture() + imgCtrl.MouseLeftButtonUp.AddHandler ( + fun obj args -> + if imgCtrl.IsMouseCaptured then + imgCtrl.ReleaseMouseCapture () ) let updatePreviews () = @@ -380,48 +388,48 @@ let run (defaultConfig : Config) (fileToOpen : string option) = let previousFilePath = state.FilePath try state.FilePath <- filepath - state.Load() + state.Load () updateGUI () with | :? IOException as ex -> Log.Error "%O" ex state.FilePath <- previousFilePath - MessageBox.Show(sprintf "The document cannot be loaded from \"%s\"" filepath, "Error loading the document", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore + MessageBox.Show (sprintf "The document cannot be loaded from \"%s\"" filepath, "Error loading the document", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore let askLoadFile () = - let dialog = OpenFileDialog(Filter = PiaZ.filter) - let res = dialog.ShowDialog() + let dialog = OpenFileDialog (Filter = PiaZ.filter) + let res = dialog.ShowDialog () if res.HasValue && res.Value then loadFile dialog.FileName let newFile () = askSaveCurrent () - state.Reset() - updateGUI() + state.Reset () + updateGUI () let exportResults () = let extension = ".txt" - let dialog = SaveFileDialog(AddExtension = true, DefaultExt = extension) + let dialog = SaveFileDialog (AddExtension = true, DefaultExt = extension) if state.FilePath <> "" then - dialog.FileName <- Path.GetFileNameWithoutExtension(state.FilePath) + extension + dialog.FileName <- Path.GetFileNameWithoutExtension state.FilePath + extension elif state.PatientID <> "" then dialog.FileName <- state.PatientID + extension - let res = dialog.ShowDialog() + let res = dialog.ShowDialog () if res.HasValue && res.Value then try Export.exportResults state dialog.FileName with | :? IOException as ex -> Log.Error "%O" ex - MessageBox.Show(sprintf "The results cannot be exported in \"%s\"" state.FilePath, "Error exporting the files", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore + MessageBox.Show (sprintf "The results cannot be exported in \"%s\"" state.FilePath, "Error exporting the files", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore let importImage () = - let dialog = OpenFileDialog(Filter = "Image Files|*.png;*.jpg;*.tif;*.tiff", Multiselect = true) - let res = dialog.ShowDialog() + let dialog = OpenFileDialog (Filter = "Image Files|*.png;*.jpg;*.tif;*.tiff", Multiselect = true) + let res = dialog.ShowDialog () if res.HasValue && res.Value then - let noSourceImage = state.SourceImages.Count() = 0 + let noSourceImage = state.SourceImages.Count () = 0 for filename in dialog.FileNames do try @@ -430,7 +438,7 @@ let run (defaultConfig : Config) (fileToOpen : string option) = with | _ as ex -> Log.Error "%O" ex - MessageBox.Show(sprintf "Unable to read the image from \"%s\"" filename, "Error adding an image", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore + MessageBox.Show (sprintf "Unable to read the image from \"%s\"" filename, "Error adding an image", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore updateGlobalParasitemia () @@ -447,173 +455,179 @@ let run (defaultConfig : Config) (fileToOpen : string option) = updateRBCFramesPreview () updateRBCFramesCurrent () - win.txtPatient.TextChanged.AddHandler(fun obj args -> state.PatientID <- win.txtPatient.Text) + win.txtPatient.TextChanged.AddHandler (fun obj args -> state.PatientID <- win.txtPatient.Text) - win.menuExit.Click.AddHandler(fun obj args -> win.Close()) - win.menuSave.Click.AddHandler(fun obj args -> saveCurrentDocument ()) - win.menuSaveAs.Click.AddHandler(fun obj args -> saveCurrentDocumentAsNewFile ()) - win.menuOpen.Click.AddHandler(fun obj args -> askLoadFile ()) - win.menuNew.Click.AddHandler(fun obj args -> newFile ()) - win.menuExportResults.Click.AddHandler(fun obj args -> exportResults ()) + win.menuExit.Click.AddHandler (fun obj args -> win.Close ()) + win.menuSave.Click.AddHandler (fun obj args -> saveCurrentDocument ()) + win.menuSaveAs.Click.AddHandler (fun obj args -> saveCurrentDocumentAsNewFile ()) + win.menuOpen.Click.AddHandler (fun obj args -> askLoadFile ()) + win.menuNew.Click.AddHandler (fun obj args -> newFile ()) + win.menuExportResults.Click.AddHandler (fun obj args -> exportResults ()) - win.menuAddSourceImage.Click.AddHandler(fun obj args -> importImage ()) + win.menuAddSourceImage.Click.AddHandler (fun obj args -> importImage ()) - win.menuAnalysis.SubmenuOpened.AddHandler(fun obj args -> win.menuStartAnalysis.IsEnabled <- state.SourceImages.Count() > 0) + win.menuAnalysis.SubmenuOpened.AddHandler (fun obj args -> win.menuStartAnalysis.IsEnabled <- state.SourceImages.Count () > 0) - win.menuStartAnalysis.Click.AddHandler(fun obj args -> showAnalysisWindow ()) + win.menuStartAnalysis.Click.AddHandler (fun obj args -> showAnalysisWindow ()) - win.menuHightlightRBC.Click.AddHandler(fun obj args -> setHighlightRBC win.menuHightlightRBC.IsChecked) + win.menuHightlightRBC.Click.AddHandler (fun obj args -> setHighlightRBC win.menuHightlightRBC.IsChecked) - win.menuAbout.Click.AddHandler(fun obj args -> About.showWindow win) + win.menuAbout.Click.AddHandler (fun obj args -> About.showWindow win) - win.Closing.AddHandler(fun obj args -> askSaveCurrent ()) + win.Closing.AddHandler (fun obj args -> askSaveCurrent ()) // Zoom on the current image. let adjustCurrentImageBorders (deltaX : float) (deltaY : float) = win.borderCurrentImage.BorderThickness <- - Thickness( + Thickness ( (win.scrollViewCurrentImage.ViewportWidth + deltaX) / 2., (win.scrollViewCurrentImage.ViewportHeight + deltaY) / 2., (win.scrollViewCurrentImage.ViewportWidth + deltaX) / 2., (win.scrollViewCurrentImage.ViewportHeight + deltaY) / 2. ) - win.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. - win.scrollViewCurrentImage.UpdateLayout() - win.scrollViewCurrentImage.ScrollToHorizontalOffset(win.borderCurrentImage.ActualWidth / 2. - win.scrollViewCurrentImage.ViewportWidth / 2.) - win.scrollViewCurrentImage.ScrollToVerticalOffset(win.borderCurrentImage.ActualHeight / 2. - win.scrollViewCurrentImage.ViewportHeight / 2.) + win.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. + win.scrollViewCurrentImage.UpdateLayout () + win.scrollViewCurrentImage.ScrollToHorizontalOffset (win.borderCurrentImage.ActualWidth / 2. - win.scrollViewCurrentImage.ViewportWidth / 2.) + win.scrollViewCurrentImage.ScrollToVerticalOffset (win.borderCurrentImage.ActualHeight / 2. - win.scrollViewCurrentImage.ViewportHeight / 2.) ) - win.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 - win.scrollViewCurrentImage.ScrollToHorizontalOffset(win.scrollViewCurrentImage.HorizontalOffset + deltaX / 8.) - win.scrollViewCurrentImage.ScrollToVerticalOffset(win.scrollViewCurrentImage.VerticalOffset + deltaY / 8.) + win.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 + win.scrollViewCurrentImage.ScrollToHorizontalOffset (win.scrollViewCurrentImage.HorizontalOffset + deltaX / 8.) + win.scrollViewCurrentImage.ScrollToVerticalOffset (win.scrollViewCurrentImage.VerticalOffset + deltaY / 8.) ) let mutable maxScale = 4. let mutable minScale = 0.25 - let currentImageScaleTransform = ScaleTransform() + let currentImageScaleTransform = ScaleTransform () win.canvasCurrentImage.LayoutTransform <- currentImageScaleTransform - win.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 then - let previousScale = currentScale - currentScale <- - let newScale = currentScale * scaleFactor - if newScale > maxScale then maxScale elif newScale < minScale then minScale else newScale - let realScaleFactor = currentScale / previousScale + win.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 then + let previousScale = currentScale + currentScale <- + let newScale = currentScale * scaleFactor + if newScale > maxScale then maxScale elif newScale < minScale then minScale else newScale + let realScaleFactor = currentScale / previousScale - let centerX = win.scrollViewCurrentImage.HorizontalOffset + win.scrollViewCurrentImage.ViewportWidth / 2. - win.borderCurrentImage.BorderThickness.Left - let centerY = win.scrollViewCurrentImage.VerticalOffset + win.scrollViewCurrentImage.ViewportHeight / 2. - win.borderCurrentImage.BorderThickness.Top + let centerX = win.scrollViewCurrentImage.HorizontalOffset + win.scrollViewCurrentImage.ViewportWidth / 2. - win.borderCurrentImage.BorderThickness.Left + let centerY = win.scrollViewCurrentImage.VerticalOffset + win.scrollViewCurrentImage.ViewportHeight / 2. - win.borderCurrentImage.BorderThickness.Top - currentImageScaleTransform.ScaleX <- currentScale - currentImageScaleTransform.ScaleY <- currentScale + currentImageScaleTransform.ScaleX <- currentScale + currentImageScaleTransform.ScaleY <- currentScale - win.scrollViewCurrentImage.ScrollToHorizontalOffset(centerX * realScaleFactor - win.scrollViewCurrentImage.ViewportWidth / 2. + win.borderCurrentImage.BorderThickness.Left) - win.scrollViewCurrentImage.ScrollToVerticalOffset(centerY * realScaleFactor - win.scrollViewCurrentImage.ViewportHeight / 2. + win.borderCurrentImage.BorderThickness.Top) + win.scrollViewCurrentImage.ScrollToHorizontalOffset (centerX * realScaleFactor - win.scrollViewCurrentImage.ViewportWidth / 2. + win.borderCurrentImage.BorderThickness.Left) + win.scrollViewCurrentImage.ScrollToVerticalOffset (centerY * realScaleFactor - win.scrollViewCurrentImage.ViewportHeight / 2. + win.borderCurrentImage.BorderThickness.Top) - args.Handled <- true + args.Handled <- true ) // Pan on the current image. - let mutable scrollStartPosition = Point(0., 0.) + let mutable scrollStartPosition = Point (0., 0.) let mutable scrollStartOffsetX = 0. let mutable scrollStartOffsetY = 0. - win.borderCurrentImage.PreviewMouseLeftButtonDown.AddHandler(fun obj args -> - scrollStartPosition <- args.GetPosition(win.scrollViewCurrentImage) - scrollStartOffsetX <- win.scrollViewCurrentImage.HorizontalOffset - scrollStartOffsetY <- win.scrollViewCurrentImage.VerticalOffset - win.borderCurrentImage.Cursor <- Input.Cursors.ScrollAll - win.borderCurrentImage.CaptureMouse() |> ignore - args.Handled <- true + win.borderCurrentImage.PreviewMouseLeftButtonDown.AddHandler ( + fun obj args -> + scrollStartPosition <- args.GetPosition win.scrollViewCurrentImage + scrollStartOffsetX <- win.scrollViewCurrentImage.HorizontalOffset + scrollStartOffsetY <- win.scrollViewCurrentImage.VerticalOffset + win.borderCurrentImage.Cursor <- Input.Cursors.ScrollAll + win.borderCurrentImage.CaptureMouse () |> ignore + args.Handled <- true ) - win.borderCurrentImage.PreviewMouseMove.AddHandler(fun obj args -> - if win.borderCurrentImage.IsMouseCaptured then - let position = args.GetPosition(win.scrollViewCurrentImage) - let deltaX = scrollStartPosition.X - position.X - let deltaY = scrollStartPosition.Y - position.Y - win.scrollViewCurrentImage.ScrollToHorizontalOffset(deltaX + scrollStartOffsetX) - win.scrollViewCurrentImage.ScrollToVerticalOffset(deltaY + scrollStartOffsetY) + win.borderCurrentImage.PreviewMouseMove.AddHandler ( + fun obj args -> + if win.borderCurrentImage.IsMouseCaptured then + let position = args.GetPosition win.scrollViewCurrentImage + let deltaX = scrollStartPosition.X - position.X + let deltaY = scrollStartPosition.Y - position.Y + win.scrollViewCurrentImage.ScrollToHorizontalOffset (deltaX + scrollStartOffsetX) + win.scrollViewCurrentImage.ScrollToVerticalOffset (deltaY + scrollStartOffsetY) - args.Handled <- true + args.Handled <- true ) - win.borderCurrentImage.PreviewMouseLeftButtonUp.AddHandler(fun obj args -> - if win.borderCurrentImage.IsMouseCaptured then - win.borderCurrentImage.Cursor <- Input.Cursors.Arrow - win.borderCurrentImage.ReleaseMouseCapture() - args.Handled <- true + win.borderCurrentImage.PreviewMouseLeftButtonUp.AddHandler ( + fun obj args -> + if win.borderCurrentImage.IsMouseCaptured then + win.borderCurrentImage.Cursor <- Input.Cursors.Arrow + win.borderCurrentImage.ReleaseMouseCapture () + args.Handled <- true ) // Shortcuts. // Save. - win.InputBindings.Add( - Input.KeyBinding( - ViewModule.FunCommand((fun obj -> saveCurrentDocument ()), (fun obj -> true)), - Input.KeyGesture(Input.Key.S, Input.ModifierKeys.Control) + win.InputBindings.Add ( + Input.KeyBinding ( + ViewModule.FunCommand ((fun obj -> saveCurrentDocument ()), (fun obj -> true)), + Input.KeyGesture (Input.Key.S, Input.ModifierKeys.Control) ) ) |> ignore // Save as. - win.InputBindings.Add( - Input.KeyBinding( - ViewModule.FunCommand((fun obj -> saveCurrentDocumentAsNewFile ()), (fun obj -> true)), - Input.KeyGesture(Input.Key.S, Input.ModifierKeys.Control ||| Input.ModifierKeys.Shift) + win.InputBindings.Add ( + Input.KeyBinding ( + ViewModule.FunCommand ((fun obj -> saveCurrentDocumentAsNewFile ()), (fun obj -> true)), + Input.KeyGesture (Input.Key.S, Input.ModifierKeys.Control ||| Input.ModifierKeys.Shift) ) ) |> ignore // Open. - win.InputBindings.Add( - Input.KeyBinding( - ViewModule.FunCommand((fun obj -> askLoadFile ()), (fun obj -> true)), - Input.KeyGesture(Input.Key.O, Input.ModifierKeys.Control) + win.InputBindings.Add ( + Input.KeyBinding ( + ViewModule.FunCommand ((fun obj -> askLoadFile ()), (fun obj -> true)), + Input.KeyGesture (Input.Key.O, Input.ModifierKeys.Control) ) ) |> ignore // New file. - win.InputBindings.Add( - Input.KeyBinding( - ViewModule.FunCommand((fun obj -> newFile ()), (fun obj -> true)), - Input.KeyGesture(Input.Key.N, Input.ModifierKeys.Control) + win.InputBindings.Add ( + Input.KeyBinding ( + ViewModule.FunCommand ((fun obj -> newFile ()), (fun obj -> true)), + Input.KeyGesture (Input.Key.N, Input.ModifierKeys.Control) ) ) |> ignore // Export results. - win.InputBindings.Add( - Input.KeyBinding( - ViewModule.FunCommand((fun obj -> exportResults ()), (fun obj -> true)), - Input.KeyGesture(Input.Key.E, Input.ModifierKeys.Control) + win.InputBindings.Add ( + Input.KeyBinding ( + ViewModule.FunCommand ((fun obj -> exportResults ()), (fun obj -> true)), + Input.KeyGesture (Input.Key.E, Input.ModifierKeys.Control) ) ) |> ignore // Import an image. - win.InputBindings.Add( - Input.KeyBinding( - ViewModule.FunCommand((fun obj -> importImage ()), (fun obj -> true)), - Input.KeyGesture(Input.Key.A, Input.ModifierKeys.Control) + win.InputBindings.Add ( + Input.KeyBinding ( + ViewModule.FunCommand ((fun obj -> importImage ()), (fun obj -> true)), + Input.KeyGesture (Input.Key.A, Input.ModifierKeys.Control) ) ) |> ignore // Show analysis dialog. - win.InputBindings.Add( - Input.KeyBinding( - ViewModule.FunCommand((fun obj -> showAnalysisWindow ()), (fun obj -> state.SourceImages.Count() > 0)), - Input.KeyGesture(Input.Key.Y, Input.ModifierKeys.Control) + win.InputBindings.Add ( + Input.KeyBinding ( + ViewModule.FunCommand ((fun obj -> showAnalysisWindow ()), (fun obj -> state.SourceImages.Count () > 0)), + Input.KeyGesture (Input.Key.Y, Input.ModifierKeys.Control) ) ) |> ignore // Toggle RBC highlight. - win.InputBindings.Add( - Input.KeyBinding( - ViewModule.FunCommand( + win.InputBindings.Add ( + Input.KeyBinding ( + ViewModule.FunCommand ( ( fun obj -> win.menuHightlightRBC.IsChecked <- not win.menuHightlightRBC.IsChecked @@ -621,20 +635,20 @@ let run (defaultConfig : Config) (fileToOpen : string option) = ), (fun obj -> true) ), - Input.KeyGesture(Input.Key.H, Input.ModifierKeys.Control) + Input.KeyGesture (Input.Key.H, Input.ModifierKeys.Control) ) ) |> ignore // Viewport preview. - win.scrollViewCurrentImage.ScrollChanged.AddHandler(fun obj args -> updateViewportPreview ()) + win.scrollViewCurrentImage.ScrollChanged.AddHandler (fun obj args -> updateViewportPreview ()) updateDocumentStatus () win.gridImageInformation.Visibility <- Visibility.Hidden - win.Show() + win.Show () match fileToOpen with | Some filepath -> loadFile filepath | None -> () - app.Run() + app.Run () diff --git a/Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj b/Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj index 200460e..0324219 100644 --- a/Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj +++ b/Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj @@ -110,7 +110,7 @@ ..\packages\EmguCV.3.1.0.1\lib\net30\Emgu.CV.World.dll - ..\packages\FSharp.Core.4.1.17\lib\net45\FSharp.Core.dll + ..\packages\FSharp.Core.4.2.1\lib\net45\FSharp.Core.dll ..\packages\FSharp.ViewModule.Core.1.0.7.0\lib\portable-net45+netcore45+wpa81+wp8+MonoAndroid1+MonoTouch1\FSharp.ViewModule.dll @@ -123,7 +123,7 @@ - ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll ..\packages\OpenTK.2.0.0\lib\net20\OpenTK.dll diff --git a/Parasitemia/ParasitemiaUI/PiaZ.fs b/Parasitemia/ParasitemiaUI/PiaZ.fs index 0215254..92ebbe0 100644 --- a/Parasitemia/ParasitemiaUI/PiaZ.fs +++ b/Parasitemia/ParasitemiaUI/PiaZ.fs @@ -56,26 +56,26 @@ let currentFileVersion = 2 /// /// If the file cannot be written let save (filePath : string) (data : DocumentData) = - use file = ZipFile.Open(filePath, ZipArchiveMode.Update) + use file = ZipFile.Open (filePath, ZipArchiveMode.Update) for e in List.ofSeq file.Entries do // 'ofSeq' to not iterate a collection currently modified. - e.Delete() + e.Delete () // Main JSON file. - let mainEntry = file.CreateEntry(mainEntryName, CompressionLevel.Fastest) - use mainEntryWriter = new StreamWriter(mainEntry.Open()) - mainEntryWriter.Write(JsonConvert.SerializeObject({ patientID = data.patientID; fileVersion = currentFileVersion })) + let mainEntry = file.CreateEntry (mainEntryName, CompressionLevel.Fastest) + use mainEntryWriter = new StreamWriter (mainEntry.Open ()) + mainEntryWriter.Write (JsonConvert.SerializeObject ({ patientID = data.patientID; fileVersion = currentFileVersion })) // Write each images and the associated information. for srcImg in data.images do 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) + 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) - let imgJSONEntry = file.CreateEntry(imgFilename + ".json", CompressionLevel.Fastest) - use imgJSONFileWriter = new StreamWriter(imgJSONEntry.Open()) - imgJSONFileWriter.Write( - JsonConvert.SerializeObject( + let imgJSONEntry = file.CreateEntry (imgFilename + ".json", CompressionLevel.Fastest) + use imgJSONFileWriter = new StreamWriter (imgJSONEntry.Open ()) + imgJSONFileWriter.Write ( + JsonConvert.SerializeObject ( { num = srcImg.num name = srcImg.name @@ -103,11 +103,11 @@ let updateDocumentData (fromVersion : int) (toVersion : int) (data : DocumentDat /// /// If the file cannot be read let load (filePath : string) (defaultConfig : ParasitemiaCore.Config.Config) : DocumentData = - use file = ZipFile.Open(filePath, ZipArchiveMode.Read) + use file = ZipFile.Open (filePath, ZipArchiveMode.Read) - let mainEntry = file.GetEntry(mainEntryName) - use mainEntryReader = new StreamReader(mainEntry.Open()) - let info = JsonConvert.DeserializeObject(mainEntryReader.ReadToEnd()) + let mainEntry = file.GetEntry (mainEntryName) + use mainEntryReader = new StreamReader (mainEntry.Open ()) + let info = JsonConvert.DeserializeObject (mainEntryReader.ReadToEnd ()) updateDocumentData info.fileVersion currentFileVersion { @@ -116,15 +116,15 @@ let load (filePath : string) (defaultConfig : ParasitemiaCore.Config.Config) : D [ let mutable imgNum = 0 for imgEntry in file.Entries do - if imgEntry.Name.EndsWith(imageExtension) then - use bitmap = new System.Drawing.Bitmap(imgEntry.Open(), false) - let img = new Image(bitmap) + if imgEntry.Name.EndsWith (imageExtension) then + use bitmap = new System.Drawing.Bitmap (imgEntry.Open (), false) + let img = new Image (bitmap) imgNum <- imgNum + 1 - let imgJSONEntry = file.GetEntry(imgEntry.Name + ".json") - use imgJSONFileReader = new StreamReader(imgJSONEntry.Open()) - let imgInfo = JsonConvert.DeserializeObject(imgJSONFileReader.ReadToEnd()) + let imgJSONEntry = file.GetEntry (imgEntry.Name + ".json") + use imgJSONFileReader = new StreamReader (imgJSONEntry.Open ()) + let imgInfo = JsonConvert.DeserializeObject (imgJSONFileReader.ReadToEnd ()) - let config = defaultConfig.Copy() + let config = defaultConfig.Copy () config.Parameters <- { ParasitemiaCore.Config.defaultParameters with diff --git a/Parasitemia/ParasitemiaUI/Program.fs b/Parasitemia/ParasitemiaUI/Program.fs index 0f6df12..b7b4202 100644 --- a/Parasitemia/ParasitemiaUI/Program.fs +++ b/Parasitemia/ParasitemiaUI/Program.fs @@ -35,7 +35,7 @@ let parseArgs (args : string[]) : Arguments = | Some i, Some i_output when i < args.Length - 2 && i_output < args.Length - 2 -> CmdLine ((File args.[i + 1]), args.[i_output + 1]) |_ -> - Window (if args.Length > 0 && not (args.[0].StartsWith("--")) then Some args.[0] else None) + Window (if args.Length > 0 && not (args.[0].StartsWith ("--")) then Some args.[0] else None) runningMode, Array.exists ((=) "--debug") args @@ -53,14 +53,14 @@ let showArgsHelp () = printfn " : a PIAZ file to automatically open at startup" printfn " --debug : output information like intermediate images if set in the current directory" -[] -extern bool AttachConsole(int dwProcessId) +[] +extern bool AttachConsole (int dwProcessId) [] -[] +[] let main args = // To redirect stdout to the attached console. - AttachConsole(-1) |> ignore // -1 to attach to the parent process. + AttachConsole -1 |> ignore // -1 to attach to the parent process. if Array.exists (fun e -> e = "--help" || e = "-h") args then showArgsHelp () @@ -72,7 +72,7 @@ let main args = let result = match parseArgs args with | mode, debug -> - let config = Config(defaultParameters) + let config = Config defaultParameters match mode with | CmdLine (input, output) -> @@ -81,9 +81,9 @@ let main args = Directory.CreateDirectory output |> ignore - use logFile = new StreamWriter(new FileStream(Path.Combine(output, "log.txt"), FileMode.Append, FileAccess.Write)) - let listener = { new IListener with member this.NewEntry severity header mess = logFile.WriteLine(header + " : " + mess) } - Log.AddListener(listener) + use logFile = new StreamWriter (new FileStream (Path.Combine(output, "log.txt"), FileMode.Append, FileAccess.Write)) + let listener = { new IListener with member this.NewEntry severity header mess = logFile.WriteLine (header + " : " + mess) } + Log.AddListener listener Log.Info "=== New run : %O %s ===" DateTime.Now (if debug then "[DEBUG]" else "[RELEASE]") @@ -91,23 +91,25 @@ let main args = | File file -> [ file ] | Dir dir -> Directory.EnumerateFiles dir |> List.ofSeq - use resultFile = new StreamWriter(new FileStream(Path.Combine(output, "results.txt"), FileMode.Append, FileAccess.Write)) - - let images = [ for file in files -> Path.GetFileNameWithoutExtension(FileInfo(file).Name), config.Copy(), new Image(file) ] - - Log.LogWithTime Severity.INFO (fun () -> - match ParasitemiaCore.Analysis.doMultipleAnalysis images None with - | Some results -> - for id, cells 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 - 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" - Some ()) "Whole analyze" |> ignore - - Log.RmListener(listener) + use resultFile = new StreamWriter (new FileStream (Path.Combine (output, "results.txt"), FileMode.Append, FileAccess.Write)) + + let images = [ for file in files -> Path.GetFileNameWithoutExtension (FileInfo(file).Name), config.Copy(), new Image (file) ] + + Log.LogWithTime Severity.INFO ( + fun () -> + match ParasitemiaCore.Analysis.doMultipleAnalysis images None with + | Some results -> + for id, cells 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 + 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" + Some () + ) "Whole analyze" |> ignore + + Log.RmListener (listener) 0 | Window fileToOpen -> diff --git a/Parasitemia/ParasitemiaUI/State.fs b/Parasitemia/ParasitemiaUI/State.fs index d09fe23..3355a42 100644 --- a/Parasitemia/ParasitemiaUI/State.fs +++ b/Parasitemia/ParasitemiaUI/State.fs @@ -10,7 +10,7 @@ open Emgu.CV.Structure open Types type State (defaultConfig : ParasitemiaCore.Config.Config) = - let sourceImages = List() + let sourceImages = List () let mutable alteredSinceLastSave = false let mutable patientID = "" @@ -80,8 +80,8 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = member this.Load () = let data = PiaZ.load this.FilePath defaultConfig this.PatientID <- data.patientID - sourceImages.Clear() - sourceImages.InsertRange(0, data.images) + sourceImages.Clear () + sourceImages.InsertRange (0, data.images) this.CurrentImage <- if sourceImages.Count > 0 then Some sourceImages.[0] else None alteredSinceLastSave <- false @@ -93,15 +93,15 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = { num = sourceImages.Count + 1 name = System.IO.FileInfo(filePath).Name - config = defaultConfig.Copy() - dateLastAnalysis = DateTime(0L) + config = defaultConfig.Copy () + dateLastAnalysis = DateTime (0L) rbcs = [] - img = new Image(filePath) + img = new Image (filePath) healthyRBCBrightness = 1.f infectedRBCBrightness = 1.f } - sourceImages.Add(srcImg) + sourceImages.Add srcImg if sourceImages.Count = 1 then this.CurrentImage <- Some sourceImages.[0] alteredSinceLastSave <- true @@ -113,7 +113,7 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = | Some srcImg' -> srcImg = srcImg' | _ -> false - if sourceImages.Remove(srcImg) then + if sourceImages.Remove srcImg then alteredSinceLastSave <- true if isCurrent then this.CurrentImage <- if sourceImages.Count > 0 then Some sourceImages.[0] else None @@ -126,7 +126,7 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = alteredSinceLastSave <- true member this.SetResult (imgNum : int) (cells : ParasitemiaCore.Types.Cell list) = - let sourceImage = sourceImages.Find(fun srcImg -> srcImg.num = imgNum) + let sourceImage = sourceImages.Find (fun srcImg -> srcImg.num = imgNum) let w = sourceImage.img.Width let h = sourceImage.img.Height @@ -137,8 +137,14 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = 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 (fun rbc -> rbc.center.X > center.X - tolerance && rbc.center.X < center.X + tolerance && - rbc.center.Y > center.Y - tolerance && rbc.center.Y < center.Y + tolerance) + manuallyAlteredPreviousRBCS + |> List.tryFind ( + fun rbc -> + rbc.center.X > center.X - tolerance && + rbc.center.X < center.X + tolerance && + rbc.center.Y > center.Y - tolerance && + rbc.center.Y < center.Y + tolerance + ) sourceImage.rbcs <- cells @@ -146,7 +152,7 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = |> List.sortByDescending (fun cell -> cell.nucleusArea, (w - cell.center.X) + (h - cell.center.Y)) |> List.mapi ( fun i cell -> - let center = Point(float cell.center.X, float cell.center.Y) + let center = Point (float cell.center.X, float cell.center.Y) let infected, setManually = let infected = cell.cellClass = ParasitemiaCore.Types.InfectedRBC match getPreviousManuallyAlteredRBC center with @@ -157,7 +163,7 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = infected = infected setManually = setManually center = center - size = Size(float cell.elements.Width, float cell.elements.Height) + size = Size (float cell.elements.Width, float cell.elements.Height) infectedArea = cell.nucleusArea } ) @@ -171,5 +177,5 @@ type State (defaultConfig : ParasitemiaCore.Config.Config) = this.PatientID <- "" this.FilePath <- "" this.CurrentImage <- None - sourceImages.Clear() + sourceImages.Clear () alteredSinceLastSave <- false \ No newline at end of file diff --git a/Parasitemia/ParasitemiaUI/Types.fs b/Parasitemia/ParasitemiaUI/Types.fs index eb778d8..ada2a1f 100644 --- a/Parasitemia/ParasitemiaUI/Types.fs +++ b/Parasitemia/ParasitemiaUI/Types.fs @@ -11,8 +11,8 @@ 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. +let healthyRBColor = Color.FromRgb (255uy, 255uy, 0uy) // Yellow-green. +let infectedRBColor = Color.FromRgb (255uy, 0uy, 40uy) // Red with a bit of blue. type RBC = { @@ -46,12 +46,12 @@ type SourceImage = member this.HealthyRBCColor : SolidColorBrush = let mutable color = healthyRBColor * this.healthyRBCBrightness color.A <- 255uy - SolidColorBrush(color) + SolidColorBrush (color) member this.InfectedRBCColor : SolidColorBrush = let mutable color = infectedRBColor * this.infectedRBCBrightness color.A <- 255uy - SolidColorBrush(color) + SolidColorBrush (color) type PredefinedPPI = { @@ -59,7 +59,7 @@ type PredefinedPPI = label : string } with - override this.ToString() = + override this.ToString () = sprintf "%s: %d" this.label this.ppi type SensorSize = diff --git a/Parasitemia/ParasitemiaUI/Utils.fs b/Parasitemia/ParasitemiaUI/Utils.fs index fa4af25..384a0d0 100644 --- a/Parasitemia/ParasitemiaUI/Utils.fs +++ b/Parasitemia/ParasitemiaUI/Utils.fs @@ -7,8 +7,8 @@ open Newtonsoft.Json.Converters open Types -let listAsStr (s : 'a seq) = - s |> Seq.fold (fun acc obj -> acc + (if acc = "" then "" else ", ") + obj.ToString()) "" +let inline listAsStr (s : 'a seq) = + s |> Seq.fold (fun acc obj -> acc + (if acc = "" then "" else ", ") + string obj) "" let percentText (nbTotal : int, nb : int) : string = if nbTotal = 0 then @@ -18,34 +18,34 @@ let percentText (nbTotal : int, nb : int) : string = sprintf "%.1f %% (%d / %d)" percent nb nbTotal let roamingDir = - Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), "Parasitemia") + Path.Combine (System.Environment.GetFolderPath (System.Environment.SpecialFolder.ApplicationData), "Parasitemia") let predefinedPPIFilename = "predefined-ppi.json" -let predefinedPPIFilepath = Path.Combine(roamingDir, predefinedPPIFilename) +let predefinedPPIFilepath = Path.Combine (roamingDir, predefinedPPIFilename) let sensorSizesFilename = "sensor-sizes.json" -let sensorSizesFilepath = Path.Combine(roamingDir, sensorSizesFilename) +let sensorSizesFilepath = Path.Combine (roamingDir, sensorSizesFilename) let private savePredefinedPPIToFile (predefinedPPI : PredefinedPPI list) = try - use file = new StreamWriter(predefinedPPIFilepath) - file.Write(JsonConvert.SerializeObject(predefinedPPI, JsonSerializerSettings(Formatting = Formatting.Indented))) + use file = new StreamWriter (predefinedPPIFilepath) + file.Write (JsonConvert.SerializeObject (predefinedPPI, JsonSerializerSettings (Formatting = Formatting.Indented))) with ex -> Logger.Log.Error "Unable to save predefined PPI to file \"%s\": %O" predefinedPPIFilepath ex let private saveSensorSizesToFile (sensorSizes : SensorSize list) = try - use file = new StreamWriter(sensorSizesFilepath) - file.Write(JsonConvert.SerializeObject(sensorSizes, JsonSerializerSettings(Formatting = Formatting.Indented))) + use file = new StreamWriter (sensorSizesFilepath) + file.Write (JsonConvert.SerializeObject (sensorSizes, JsonSerializerSettings (Formatting = Formatting.Indented))) with ex -> Logger.Log.Error "Unable to save sensor sizes to file \"%s\": %O" sensorSizesFilepath ex let predefinedPPI : PredefinedPPI list = try - use file = new StreamReader(predefinedPPIFilepath) - JsonConvert.DeserializeObject(file.ReadToEnd()) + use file = new StreamReader (predefinedPPIFilepath) + JsonConvert.DeserializeObject (file.ReadToEnd ()) with | ex -> savePredefinedPPIToFile defaultPredefinedPPI @@ -53,8 +53,8 @@ let predefinedPPI : PredefinedPPI list = let sensorSizes : SensorSize list = try - use file = new StreamReader(sensorSizesFilepath) - JsonConvert.DeserializeObject(file.ReadToEnd()) + use file = new StreamReader (sensorSizesFilepath) + JsonConvert.DeserializeObject (file.ReadToEnd ()) with | ex -> saveSensorSizesToFile defaultSensorSizes diff --git a/Parasitemia/ParasitemiaUI/packages.config b/Parasitemia/ParasitemiaUI/packages.config index 89d3dcf..30067c6 100644 --- a/Parasitemia/ParasitemiaUI/packages.config +++ b/Parasitemia/ParasitemiaUI/packages.config @@ -2,10 +2,10 @@ - + - + diff --git a/Parasitemia/Tests/ParasitemiaCore.Tests/AssemblyInfo.fs b/Parasitemia/Tests/ParasitemiaCore.Tests/AssemblyInfo.fs index d448a0d..931f17d 100644 --- a/Parasitemia/Tests/ParasitemiaCore.Tests/AssemblyInfo.fs +++ b/Parasitemia/Tests/ParasitemiaCore.Tests/AssemblyInfo.fs @@ -7,22 +7,22 @@ open System.Runtime.InteropServices // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[] -[] -[] -[] -[] -[] -[] -[] +[] +[] +[] +[] +[] +[] +[] +[] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. -[] +[] // The following GUID is for the ID of the typelib if this project is exposed to COM -[] +[] // Version information for an assembly consists of the following four values: // @@ -33,11 +33,11 @@ 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/Tests/ParasitemiaCore.Tests/KdTree.fs b/Parasitemia/Tests/ParasitemiaCore.Tests/KdTree.fs index 9c9d859..9181c26 100644 --- a/Parasitemia/Tests/ParasitemiaCore.Tests/KdTree.fs +++ b/Parasitemia/Tests/ParasitemiaCore.Tests/KdTree.fs @@ -20,13 +20,13 @@ type KdTreeTests (output : ITestOutputHelper) = member this.test () = let pts = [ - Point(1.0f, 1.0f) - Point(2.0f, 2.0f) - Point(1.5f, 3.6f) - Point(3.0f, 3.2f) - Point(4.0f, 4.0f) - Point(3.5f, 1.5f) - Point(2.5f, 0.5f) + Point (1.0f, 1.0f) + Point (2.0f, 2.0f) + Point (1.5f, 3.6f) + Point (3.0f, 3.2f) + Point (4.0f, 4.0f) + Point (3.5f, 1.5f) + Point (2.5f, 0.5f) ] let tree = Tree.BuildTree pts @@ -45,9 +45,9 @@ type KdTreeTests (output : ITestOutputHelper) = member this.test2 () = let pts = [ - Point(1.0f, 1.0f) - Point(1.0f, 2.0f) - Point(1.0f, 3.0f) + Point (1.0f, 1.0f) + Point (1.0f, 2.0f) + Point (1.0f, 3.0f) ] let tree = Tree.BuildTree pts diff --git a/Parasitemia/Tests/ParasitemiaCore.Tests/ParasitemiaCore.Tests.fsproj b/Parasitemia/Tests/ParasitemiaCore.Tests/ParasitemiaCore.Tests.fsproj index dc8cf62..d0cdfa5 100644 --- a/Parasitemia/Tests/ParasitemiaCore.Tests/ParasitemiaCore.Tests.fsproj +++ b/Parasitemia/Tests/ParasitemiaCore.Tests/ParasitemiaCore.Tests.fsproj @@ -54,7 +54,7 @@ - ..\packages\FSharp.Core.4.1.17\lib\net45\FSharp.Core.dll + ..\..\packages\FSharp.Core.4.2.1\lib\net45\FSharp.Core.dll diff --git a/Parasitemia/Tests/ParasitemiaCore.Tests/packages.config b/Parasitemia/Tests/ParasitemiaCore.Tests/packages.config index 63ccec1..6d282b2 100644 --- a/Parasitemia/Tests/ParasitemiaCore.Tests/packages.config +++ b/Parasitemia/Tests/ParasitemiaCore.Tests/packages.config @@ -1,6 +1,6 @@  - + -- 2.45.2