From: Greg Burri Date: Mon, 18 Jan 2016 12:22:20 +0000 (+0100) Subject: Add a logger assembly and split the main assembly in two : the UI and the parasitemia... X-Git-Tag: 1.0.11~47 X-Git-Url: http://git.euphorik.ch/?p=master-thesis.git;a=commitdiff_plain;h=4bfa3cbdc6145e6944f02e24829ab2ef3a851ac1 Add a logger assembly and split the main assembly in two : the UI and the parasitemia evaluation. --- diff --git a/Parasitemia/Logger/AssemblyInfo.fs b/Parasitemia/Logger/AssemblyInfo.fs new file mode 100644 index 0000000..c810d05 --- /dev/null +++ b/Parasitemia/Logger/AssemblyInfo.fs @@ -0,0 +1,41 @@ +namespace Logger.AssemblyInfo + +open System.Reflection +open System.Runtime.CompilerServices +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: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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 new file mode 100644 index 0000000..552e37d --- /dev/null +++ b/Parasitemia/Logger/Logger.fs @@ -0,0 +1,168 @@ +namespace Logger + +open System +open System.Text +open System.IO +open System.Diagnostics +open System.Threading +open System.Collections.Generic + +type Severity = DEBUG = 1 | USER = 2 | WARNING = 3 | ERROR = 4 | FATAL = 5 + +type IListener = abstract NewEntry : Severity -> string -> unit + +[] +type Log () = + let maxSizeFile = 10L * 1024L * 1024L // [byte] (10 MB). + let nbEntriesCheckSize = 100; // Each 100 entries added we check the size of the log file to test if it is greater than 'MAX_SIZE_FILE'. + let LogDefaultDirectory = "Parasitemia\\Log" + let filenameFormat = "{0:D4}.log" + let encoding = Encoding.GetEncoding("UTF-8") + + let moduleName = System.Diagnostics.StackFrame(1).GetMethod().Module.Name + + let mutable stream: StreamWriter = null + + let mutable logDir: string = null + let mutable absoluteDir: string = null + + let mutable nbEntries = 0L + + let monitor = Object() + + let listeners = List() + + let debug = +#if DEBUG + true +#else + false +#endif + + static let instance = new Log() + + let setLogDirectory (dir: string) = + lock monitor (fun () -> + logDir <- dir + absoluteDir <- Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), logDir) + + if stream <> null + then + stream.Close() + stream <- null + + try + if not <| Directory.Exists(absoluteDir) + then + Directory.CreateDirectory(absoluteDir) |> ignore + with + | _ as ex -> Console.Error.WriteLine("Unable to create the log directory: {0}", absoluteDir)) + + let openLogFile () = + try + if stream = null || (nbEntries % (int64 nbEntriesCheckSize) = 0L) && stream.BaseStream.Length > maxSizeFile + then + if stream <> null + then + stream.Close() + + let mutable n = 1 + for existingFile in Directory.GetFiles(absoluteDir) do + let current_n = ref 0 + if Int32.TryParse(existingFile.Remove(existingFile.LastIndexOf('.')), current_n) && !current_n > n + then + n <- !current_n + + let mutable filename = Path.Combine(absoluteDir, String.Format(filenameFormat, n)) + try + if (FileInfo(filename).Length > maxSizeFile) + then + filename <- Path.Combine(absoluteDir, String.Format(filenameFormat, n + 1)) + with + | :? FileNotFoundException -> () // The file may not exist. + + stream <- new StreamWriter(filename, true, encoding) + with + | _ as ex -> Console.Error.WriteLine("Can't open the file log: {0}", ex) + + do + setLogDirectory LogDefaultDirectory + + interface IDisposable with + member this.Dispose () = + if stream <> null + then + stream.Dispose() + + member private this.Write (message: string, severity: Severity) = + lock monitor (fun () -> + nbEntries <- nbEntries + 1L + openLogFile () + + if stream <> null + then + let mutable moduleNameCaller = moduleName + match StackTrace().GetFrames() |> Array.tryPick (fun frame -> let name = frame.GetMethod().Module.Name + if name <> moduleName then Some name else None) with + | Some name -> moduleNameCaller <- name + | _ -> () + + let threadName = Thread.CurrentThread.Name + + for listener in listeners do + listener.NewEntry severity message + + try + stream.WriteLine( + "{0:yyyy-MM-dd HH:mm:ss.fff} [{1}] {{{2}}} ({3}) : {4}", + TimeZone.CurrentTimeZone.ToLocalTime(DateTime.UtcNow), + severity.ToString(), + moduleNameCaller, + (if String.IsNullOrEmpty(threadName) then Thread.CurrentThread.ManagedThreadId.ToString() else String.Format("{0}-{1}", threadName, Thread.CurrentThread.ManagedThreadId)), + message + ) + stream.Flush() + with + | :? IOException as ex -> Console.Error.WriteLine("Unable to write to the log file: {0}", ex)) + + + member private this.AddListener (listener: IListener) = + lock monitor (fun () -> + if not <| listeners.Contains(listener) + then + listeners.Add(listener)) + + member private this.RmListener (listener: IListener) = + 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 LogWithTime (message: string, severity: Severity, f: unit -> 'a, [] args: Object[]) : 'a = + let sw = Stopwatch() + sw.Start() + let res = f () + sw.Stop() + instance.Write(String.Format(message, args) + sprintf " (time: %d ms)" sw.ElapsedMilliseconds, severity) + res + + static member Debug (message: string, [] args: Object[]) = +#if DEBUG + instance.Write(String.Format(message, args), Severity.DEBUG) +#else + () +#endif + + static member User (message: string, [] args: Object[]) = + instance.Write(String.Format(message, args), Severity.USER) + + static member Warning (message: string, [] args: Object[]) = + instance.Write(String.Format(message, args), Severity.WARNING) + + static member Error (message: string, [] args: Object[]) = + instance.Write(String.Format(message, args), Severity.ERROR) + + static member Fatal (message: string, [] args: Object[]) = + instance.Write(String.Format(message, args), Severity.FATAL) + diff --git a/Parasitemia/Logger/Logger.fsproj b/Parasitemia/Logger/Logger.fsproj new file mode 100644 index 0000000..19243e6 --- /dev/null +++ b/Parasitemia/Logger/Logger.fsproj @@ -0,0 +1,72 @@ + + + + + Debug + AnyCPU + 2.0 + a4f183ae-562a-4bad-88e6-658b4ce15dc3 + Library + Logger + Logger + v4.0 + 4.4.0.0 + true + Logger + + + true + full + false + false + bin\Debug\ + DEBUG;TRACE + 3 + bin\Debug\Logger.XML + + + pdbonly + true + true + bin\Release\ + TRACE + 3 + bin\Release\Logger.XML + + + + + True + + + + + + + + + + + 11 + + + + + $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets + + + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets + + + + + + \ No newline at end of file diff --git a/Parasitemia/Parasitemia.sln b/Parasitemia/Parasitemia.sln index ea32a4b..545e796 100644 --- a/Parasitemia/Parasitemia.sln +++ b/Parasitemia/Parasitemia.sln @@ -3,10 +3,14 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Parasitemia", "Parasitemia\Parasitemia.fsproj", "{70838E65-F211-44FC-B28F-0ED1CA6E850F}" +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ParasitemiaUI", "ParasitemiaUI\ParasitemiaUI.fsproj", "{70838E65-F211-44FC-B28F-0ED1CA6E850F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPF", "WPF\WPF.csproj", "{314FD78E-870E-4794-BB16-EA4586F2ABDB}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Logger", "Logger\Logger.fsproj", "{A4F183AE-562A-4BAD-88E6-658B4CE15DC3}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ParasitemiaCore", "ParasitemiaCore\ParasitemiaCore.fsproj", "{0F8A85F4-9328-40C3-B8FF-44FB39CEB01F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -26,6 +30,18 @@ Global {314FD78E-870E-4794-BB16-EA4586F2ABDB}.DebugGUI|Any CPU.Build.0 = Debug|Any CPU {314FD78E-870E-4794-BB16-EA4586F2ABDB}.Release|Any CPU.ActiveCfg = Release|Any CPU {314FD78E-870E-4794-BB16-EA4586F2ABDB}.Release|Any CPU.Build.0 = Release|Any CPU + {A4F183AE-562A-4BAD-88E6-658B4CE15DC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A4F183AE-562A-4BAD-88E6-658B4CE15DC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A4F183AE-562A-4BAD-88E6-658B4CE15DC3}.DebugGUI|Any CPU.ActiveCfg = Debug|Any CPU + {A4F183AE-562A-4BAD-88E6-658B4CE15DC3}.DebugGUI|Any CPU.Build.0 = Debug|Any CPU + {A4F183AE-562A-4BAD-88E6-658B4CE15DC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A4F183AE-562A-4BAD-88E6-658B4CE15DC3}.Release|Any CPU.Build.0 = Release|Any CPU + {0F8A85F4-9328-40C3-B8FF-44FB39CEB01F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0F8A85F4-9328-40C3-B8FF-44FB39CEB01F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0F8A85F4-9328-40C3-B8FF-44FB39CEB01F}.DebugGUI|Any CPU.ActiveCfg = Debug|Any CPU + {0F8A85F4-9328-40C3-B8FF-44FB39CEB01F}.DebugGUI|Any CPU.Build.0 = Debug|Any CPU + {0F8A85F4-9328-40C3-B8FF-44FB39CEB01F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0F8A85F4-9328-40C3-B8FF-44FB39CEB01F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Parasitemia/Parasitemia/App.config b/Parasitemia/Parasitemia/App.config deleted file mode 100644 index 4be2caf..0000000 --- a/Parasitemia/Parasitemia/App.config +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Parasitemia/Parasitemia/AssemblyInfo.fs b/Parasitemia/Parasitemia/AssemblyInfo.fs deleted file mode 100644 index 1027e98..0000000 --- a/Parasitemia/Parasitemia/AssemblyInfo.fs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Parasitemia.AssemblyInfo - -open System.Reflection -open System.Runtime.CompilerServices -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: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// 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/Parasitemia/Classifier.fs b/Parasitemia/Parasitemia/Classifier.fs deleted file mode 100644 index 121a7f5..0000000 --- a/Parasitemia/Parasitemia/Classifier.fs +++ /dev/null @@ -1,200 +0,0 @@ -module Classifier - -open System -open System.Collections.Generic -open System.Drawing - -open Emgu.CV -open Emgu.CV.Structure - -open Types -open Utils - - -type private EllipseFlaggedKd (e: Ellipse) = - inherit Ellipse (e.Cx, e.Cy, e.A, e.B, e.Alpha) - - member val Removed = false with get, set - - interface KdTree.I2DCoords with - member this.X = this.Cx - member this.Y = this.Cy - - -let findCells (ellipses: Ellipse list) (parasites: ParasitesMarker.Result) (img: Image) (config: Config.Config) : Cell list = - if ellipses.IsEmpty - then - [] - else - let infection = parasites.infection.Copy() // To avoid to modify the parameter. - - // This is the minimum window size to check if other ellipses touch 'e'. - let searchRegion (e: Ellipse) = { KdTree.minX = e.Cx - (e.A + config.RBCRadius.Max) - KdTree.maxX = e.Cx + (e.A + config.RBCRadius.Max) - KdTree.minY = e.Cy - (e.A + config.RBCRadius.Max) - KdTree.maxY = e.Cy + (e.A + config.RBCRadius.Max) } - - // The minimum window to contain a given ellipse. - let ellipseWindow (e: Ellipse) = - let cx, cy = roundInt e.Cx, roundInt e.Cy - let a = int (e.A + 0.5f) - cx - a, cy - a, cx + a, cy + a - - let w = img.Width - let w_f = float32 w - let h = img.Height - let h_f = float32 h - - // Return 'true' if the point 'p' is owned by e. - // The lines represents all intersections with other ellipses. - let pixelOwnedByE (p: PointD) (e: Ellipse) (others: (Ellipse * Line) list) = - e.Contains p.X p.Y && - seq { - let c = PointD(e.Cx, e.Cy) - for e', d1 in others do - let d2 = Utils.lineFromTwoPoints c p - let c' = PointD(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 d2.Valid - then - let p' = Utils.pointFromTwoLines d1 d2 - // Yield 'false' when the point is owned by another ellipse. - if case1 - then - yield sign (c.X - p.X) <> sign (c.X - p'.X) || Utils.squaredDistanceTwoPoints c p' > Utils.squaredDistanceTwoPoints c p - else - yield sign (c.X - p.X) = sign (c.X - p'.X) && Utils.squaredDistanceTwoPoints c p' < Utils.squaredDistanceTwoPoints c p - else - yield case1 - } |> Seq.forall id - - let ellipses = ellipses |> List.map EllipseFlaggedKd - - // 1) Associate touching ellipses with each ellipses and remove ellipse with more than two intersections. - let tree = KdTree.Tree.BuildTree ellipses - let neighbors (e: EllipseFlaggedKd) : (EllipseFlaggedKd * PointD * PointD) list = - if not e.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.Removed <- true - None - | Some (area, px, py) when area > 0.f && px.Length = 2 -> - Some (otherE, PointD(px.[0], py.[0]), PointD(px.[1], py.[1])) - | _ -> - None - else - None ) - else - [] - - // We reverse the list to get the lower score ellipses first. - let ellipsesWithNeigbors = ellipses |> List.map (fun e -> e, neighbors e) |> List.rev - - // 2) Remove ellipses touching the edges. - for e in ellipses do - if e.isOutside w_f h_f then e.Removed <- true - - // 3) Remove ellipses with a high standard deviation (high contrast). - let imgData = img.Data - 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] }) - - for e in ellipses do - if not e.Removed - then - let shrinkedE = e.Scale 0.9f - let minX, minY, maxX, maxY = ellipseWindow shrinkedE - - let stdDeviation = MathNet.Numerics.Statistics.Statistics.StandardDeviation (seq { - for y in (if minY < 0 then 0 else minY) .. (if maxY >= h then h - 1 else maxY) do - for x in (if minX < 0 then 0 else minX) .. (if maxX >= w then w - 1 else maxX) do - if shrinkedE.Contains (float32 x) (float32 y) - then - yield float imgData.[y, x, 0] }) - - if stdDeviation > globalStdDeviation * config.Parameters.standardDeviationMaxRatio then - e.Removed <- true - - - // 4) Remove ellipses with little area. - let minArea = config.RBCRadius.MinArea - for e, neighbors in ellipsesWithNeigbors do - if not e.Removed - then - let minX, minY, maxX, maxY = ellipseWindow e - - let mutable area = 0 - for y in (if minY < 0 then 0 else minY) .. (if maxY >= h then h - 1 else maxY) do - for x in (if minX < 0 then 0 else minX) .. (if maxX >= w then w - 1 else maxX) do - let p = PointD(float32 x, float32 y) - if pixelOwnedByE p e (neighbors |> List.choose (fun (otherE, p1, p2) -> if otherE.Removed then None else Some (otherE :> Ellipse, Utils.lineFromTwoPoints p1 p2))) - then - area <- area + 1 - - if area < int minArea - then - e.Removed <- true - - // 5) Define pixels associated to each ellipse and create the cells. - ellipsesWithNeigbors - |> List.choose (fun (e, neighbors) -> - if e.Removed - then - None - else - let minX, minY, maxX, maxY = ellipseWindow e - - let infectedPixels = List() - let mutable stainPixels = 0 - let mutable darkStainPixels = 0 - let mutable nbElement = 0 - - let elements = new Matrix(maxY - minY + 1, maxX - minX + 1) - for y in minY .. maxY do - for x in minX .. maxX do - let p = PointD(float32 x, float32 y) - if pixelOwnedByE p e (neighbors |> List.choose (fun (otherE, p1, p2) -> if otherE.Removed then None else Some (otherE :> Ellipse, Utils.lineFromTwoPoints p1 p2))) - then - elements.[y-minY, x-minX] <- 1uy - nbElement <- nbElement + 1 - - if infection.Data.[y, x, 0] > 0uy - then - infectedPixels.Add(Point(x, y)) - - if parasites.stain.Data.[y, x, 0] > 0uy - then - stainPixels <- stainPixels + 1 - - if parasites.darkStain.Data.[y, x, 0] > 0uy - then - darkStainPixels <- darkStainPixels + 1 - - let cellClass = - if float darkStainPixels > config.Parameters.maxDarkStainRatio * (float nbElement) || - float stainPixels > config.Parameters.maxStainRatio * (float nbElement) - then - Peculiar - elif infectedPixels.Count >= 1 - then - let infectionToRemove = ImgTools.connectedComponents parasites.stain infectedPixels - for p in infectionToRemove do - infection.Data.[p.Y, p.X, 0] <- 0uy - InfectedRBC - else - HealthyRBC - - Some { cellClass = cellClass - center = Point(roundInt e.Cx, roundInt e.Cy) - infectedArea = infectedPixels.Count - stainArea = stainPixels - elements = elements }) diff --git a/Parasitemia/Parasitemia/Config.fs b/Parasitemia/Parasitemia/Config.fs deleted file mode 100644 index e7deb61..0000000 --- a/Parasitemia/Parasitemia/Config.fs +++ /dev/null @@ -1,121 +0,0 @@ -module Config - -open System - -open Const -open UnitsOfMeasure - -type Debug = - | DebugOff - | DebugOn of string // Output directory. - -type Parameters = { - rbcDiameter: float<μm> - resolution: float - - ratioAreaPaleCenter: float32 // The area of the second opening is 'ratioSecondAreaOpen' * mean RBC area. It's applied only if greater than 'initialAreaOpen'. - - granulometryRange: float32 // The radius will be seeked from radius - granulometryRange * radius to radius + granulometryRange * radius. - - minRbcRadius: float32 // Factor of the mean RBC radius. - maxRbcRadius: float32 // Factor of the mean RBC radius. - - LPFStandardDeviation: float<μm> // Sigma parameter of the gaussian to remove the high frequency noise. - - // Ellipse. - factorNbPick: float // The number of computed ellipse per edge pixel. - - // Parasites detection. - darkStainLevel: float // Lower -> more sensitive. Careful about illumination on the borders. - maxDarkStainRatio: float // When a cell must own less than this ratio to be a RBC. - - stainArea: float32 // Factor of a RBC area. 0.5 means the half of RBC area. - stainSensitivity: float // between 0 (the least sensitive) and 1 (the most sensitive). - maxStainRatio: float // A cell must own less than this ratio to be a RBC. - - infectionArea: float32 // Factor of a RBC area. 0.5 means the half of RBC area. - infectionSensitivity: float // between 0 (the least sensitive) and 1 (the most sensitive). - - standardDeviationMaxRatio: float // The standard deviation of the pixel values of a cell can't be greater than standardDeviationMaxRatio * global standard deviation - minimumCellAreaFactor: float32 // Factor of the mean RBC area. A cell with an area below this will be rejected. -} - -let defaultParameters = { - rbcDiameter = 8.<μm> - resolution = 220.e3 // Correspond to 50X. - - ratioAreaPaleCenter = 1.f / 3.f // The ratio between an RBC area and the area of the its pale center. - - granulometryRange = 0.5f - - minRbcRadius = -0.3f - maxRbcRadius = 0.3f - - LPFStandardDeviation = 0.2<μm> // 8.5e-6. - - factorNbPick = 1.0 - - darkStainLevel = 0.25 // 0.3 - maxDarkStainRatio = 0.1 // 10 % - - infectionArea = 0.012f // 1.2 % - infectionSensitivity = 0.9 - - stainArea = 0.08f // 8 % - stainSensitivity = 0.9 - maxStainRatio = 0.12 // 12 % - - standardDeviationMaxRatio = 0.5 // 0.5 - minimumCellAreaFactor = 0.4f } - -type RBCRadius (radius: float32, parameters: Parameters) = - member this.Pixel = radius - member this.μm : float<μm> = - 1. * (float radius) / parameters.resolution |> inchToμm - - member this.Min = radius + parameters.minRbcRadius * radius - member this.Max = radius + parameters.maxRbcRadius * radius - - member this.Area = PI * radius ** 2.f - member this.MinArea = parameters.minimumCellAreaFactor * radius - - member this.InfectionArea = parameters.infectionArea * this.Area - member this.StainArea = parameters.stainArea * this.Area - - override this.ToString() = - sprintf "%d px (%.1f μm)" (Utils.roundInt <| 2.f * radius) (2. * this.μm) - -type Config (param: Parameters) = - let RBCadiusInPixels (rbcDiameter: float<μm>) (resolution: float) : float32 = - let rbcRadiusInch: float = (μmToInch rbcDiameter) / 2. - let rbcRadiusPx: float = resolution * rbcRadiusInch - float32 rbcRadiusPx - - let mutable parameters: Parameters = param - let mutable rbcRadiusByResolution = RBCRadius(RBCadiusInPixels parameters.rbcDiameter parameters.resolution, parameters) - let mutable rbcRadius = RBCRadius(0.f, parameters) - - new () = Config(defaultParameters) - - member this.Parameters - with get() = parameters - and set(param) = - parameters <- param - rbcRadiusByResolution <- RBCRadius(RBCadiusInPixels parameters.rbcDiameter parameters.resolution, param) - rbcRadius <- RBCRadius(rbcRadius.Pixel, param) - - member val Debug = DebugOff with get, set - - member this.LPFStandardDeviation = - let stdDeviation: float = (μmToInch parameters.LPFStandardDeviation) * parameters.resolution - float stdDeviation - - member this.RBCRadiusByResolution = rbcRadiusByResolution - member this.RBCRadius = rbcRadius - - member this.SetRBCRadius (radiusPixel: float32) = - rbcRadius <- RBCRadius(radiusPixel, parameters) - - member this.Copy () = - this.MemberwiseClone() :?> Config - diff --git a/Parasitemia/Parasitemia/Const.fs b/Parasitemia/Parasitemia/Const.fs deleted file mode 100644 index 304d3a9..0000000 --- a/Parasitemia/Parasitemia/Const.fs +++ /dev/null @@ -1,3 +0,0 @@ -module Const - -let PI = float32 System.Math.PI \ No newline at end of file diff --git a/Parasitemia/Parasitemia/EEOver.fs b/Parasitemia/Parasitemia/EEOver.fs deleted file mode 100644 index 2bd13b2..0000000 --- a/Parasitemia/Parasitemia/EEOver.fs +++ /dev/null @@ -1,724 +0,0 @@ -module EEOver - -open System - -let private EPS = 1.0e-5 - -let inline private ellipse2tr (x: float) (y: float) (aa: float) (bb: float) (cc: float) (dd: float) (ee: float) (ff: float) : float = - aa * x * x + bb * x * y + cc * y * y + dd * x + ee * y + ff - -let private nointpts (a1: float) (b1: float) (a2: float) (b2: float) (h1: float) (k1: float) (h2: float) (k2: float) (phi_1: float) (phi_2: float) (h2_tr: float) (k2_tr: float) (aa: float) (bb: float) (cc: float) (dd: float) (ee: float) (ff: float) = - let a1b1 = a1 * b1 - let a2b2 = a2 * b2 - let area_1 = Math.PI * a1b1 - let area_2 = Math.PI * a2b2 - let relsize = a1b1 - a2b2 - - if relsize > 0.0 - then - if (h2_tr * h2_tr) / (a1 * a1) + (k2_tr * k2_tr) / (b1 * b1) < 1.0 - then area_2 - else 0.0 - - elif relsize < 0.0 - then - if ff < 0.0 - then area_1 - else 0.0 - - else - if abs (h1 - h2) < EPS && abs (k1 - k2) < EPS && abs (area_1 - area_2) < EPS - then area_1 - else 0.0 - -type private PointType = TANGENT_POINT | INTERSECTION_POINT - -let private istanpt (x: float) (y: float) (a1: float) (b1: float) (aa: float) (bb: float) (cc: float) (dd: float) (ee: float) (ff: float) : PointType = - let x = - if abs x > a1 - then - if x < 0.0 then -a1 else a1 - else x - - let theta = - if y < 0.0 - then 2.0 * Math.PI - acos (x / a1) - else acos (x / a1) - - let eps_radian = 0.1 - - let x1 = a1 * cos (theta + eps_radian) - let y1 = b1 * sin (theta + eps_radian) - let x2 = a1 * cos (theta - eps_radian) - let y2 = b1 * sin (theta - eps_radian) - - let test1 = ellipse2tr x1 y1 aa bb cc dd ee ff - let test2 = ellipse2tr x2 y2 aa bb cc dd ee ff - -#if DEBUG_LOG - printf "\t\t--- debug istanpt with (x,y)=(%f, %f), A1=%f, B1=%f\n" x y a1 b1 - printf "theta=%f\n" theta - printf "eps_Radian=%f\n" eps_radian - printf "(x1, y1)=(%f, %f)\n" x1 y1 - printf "(x2, y2)=(%f, %f)\n" x2 y2 - printf "test1=%f\n" test1 - printf "test2=%f\n" test2 -#endif - - if test1 * test2 > 0.0 - then TANGENT_POINT - else INTERSECTION_POINT - -let private twointpts (x: float[]) (y: float[]) (a1: float) (b1: float) (phi_1: float) (a2: float) (b2: float) (h2_tr: float) (k2_tr: float) (phi_2: float) (aa: float) (bb: float) (cc: float) (dd: float) (ee: float) (ff: float) = - if abs x.[0] > a1 - then x.[0] <- if x.[0] < 0.0 then -a1 else a1 - - let mutable theta1 = - if y.[0] < 0.0 - then 2.0 * Math.PI - acos (x.[0] / a1) - else acos (x.[0] / a1) - - if abs x.[1] > a1 - then x.[1] <- if x.[1] < 0.0 then -a1 else a1 - - let mutable theta2 = - if y.[1] < 0.0 - then 2.0 * Math.PI - acos (x.[1] / a1) - else acos (x.[1] / a1) - - if theta1 > theta2 - then - let tmp = theta1 - theta1 <- theta2 - theta2 <- tmp - - let xmid = a1 * cos ((theta1 + theta2) / 2.0) - let ymid = b1 * sin ((theta1 + theta2) / 2.0) - - if ellipse2tr xmid ymid aa bb cc dd ee ff > 0.0 - then - let tmp = theta1 - theta1 <- theta2 - theta2 <- tmp - - if theta1 > theta2 - then - theta1 <- theta1 - 2.0 * Math.PI - - let trsign = if (theta2 - theta1) > Math.PI then 1.0 else -1.0 - - let mutable area1 = 0.5 * (a1 * b1 * (theta2 - theta1) + trsign * abs (x.[0] * y.[1] - x.[1] * y.[0])) - - if area1 < 0.0 - then -#if DEBUG_LOG - printf "TWO area1=%f\n" area1 -#endif - area1 <- area1 + a1 * b1 - - let cosphi = cos (phi_1 - phi_2) - let sinphi = sin (phi_1 - phi_2) - - let mutable x1_tr = (x.[0] - h2_tr) * cosphi + (y.[0] - k2_tr) * -sinphi - let mutable y1_tr = (x.[0] - h2_tr) * sinphi + (y.[0] - k2_tr) * cosphi - let mutable x2_tr = (x.[1] - h2_tr) * cosphi + (y.[1] - k2_tr) * -sinphi - let mutable y2_tr = (x.[1] - h2_tr) * sinphi + (y.[1] - k2_tr) * cosphi - - if abs x1_tr > a2 - then - x1_tr <- if x1_tr < 0.0 then -a2 else a2 - - if y1_tr < 0.0 - then - theta1 <- 2.0 * Math.PI - acos (x1_tr / a2) - else - theta1 <- acos (x1_tr / a2) - - if abs x2_tr > a2 - then - x2_tr <- if x2_tr < 0.0 then -a2 else a2 - - if y2_tr < 0.0 - then - theta2 <- 2.0 * Math.PI - acos (x2_tr / a2) - else - theta2 <- acos (x2_tr / a2) - - if theta1 > theta2 - then - let tmp = theta1 - theta1 <- theta2 - theta2 <- tmp - - let xmid = a2 * cos ((theta1 + theta2) / 2.0) - let ymid = b2 * sin ((theta1 + theta2) / 2.0) - - let cosphi = cos (phi_2 - phi_1) - let sinphi = sin (phi_2 - phi_1) - let xmid_rt = xmid * cosphi + ymid * -sinphi + h2_tr - let ymid_rt = xmid * sinphi + ymid * cosphi + k2_tr - - if (xmid_rt * xmid_rt) / (a1 * a1) + (ymid_rt * ymid_rt) / (b1 * b1) > 1.0 - then - let tmp = theta1 - theta1 <- theta2 - theta2 <- tmp - - if theta1 > theta2 - then - theta1 <- theta1 - 2.0 * Math.PI - - let trsign = if theta2 - theta1 > Math.PI then 1.0 else -1.0 - - let mutable area2 = 0.5 * (a2 * b2 * (theta2 - theta1) + trsign * abs (x1_tr * y2_tr - x2_tr * y1_tr)) - if area2 < 0.0 - then -#if DEBUG_LOG - printf "TWO area2=%f\n" area2 -#endif - area2 <- area2 + a2 * b2 - - area1 + area2 - -let private threeintpts (xint: float[]) (yint: float[]) (a1: float) (b1: float) (phi_1: float) (a2: float) (b2: float) (h2_tr: float) (k2_tr: float) (phi_2: float) (aa: float) (bb: float) (cc: float) (dd: float) (ee: float) (ff: float) : float = - let mutable tanpts = 0 - let mutable tanindex = 0 - for i in 0..2 do - if istanpt xint.[i] yint.[i] a1 b2 aa bb cc dd ee ff = TANGENT_POINT - then - tanpts <- tanpts + 1 - tanindex <- i -#if DEBUG_LOG - printf "tanindex=%d\n" tanindex -#endif - - if tanpts <> 1 - then - -1.0 - else - match tanindex with - | 0 -> - xint.[0] <- xint.[2] - yint.[0] <- yint.[2] - | 1 -> - xint.[1] <- xint.[2] - yint.[1] <- yint.[2] - | _ -> - () - twointpts xint yint a1 b1 phi_1 a2 b2 h2_tr k2_tr phi_2 aa bb cc dd ee ff - - -let private fourintpts (xint: float[]) (yint: float[]) (a1: float) (b1: float) (phi_1: float) (a2: float) (b2: float) (h2_tr: float) (k2_tr: float) (phi_2: float) (aa: float) (bb: float) (cc: float) (dd: float) (ee: float) (ff: float) : float = - let a1b1 = a1 * b1 - let a2b2 = a2 * b2 - let area_1 = Math.PI * a1b1 - let area_2 = Math.PI * a2b2 - - let theta = Array.zeroCreate 4 - - for i in 0 .. 3 do - if abs xint.[i] > a1 - then - xint.[i] <- if xint.[i] < 0.0 then -a1 else a1 - theta.[i] <- if yint.[i] < 0.0 then 2.0 * Math.PI - acos (xint.[i] / a1) else acos (xint.[i] / a1) - -#if DEBUG_LOG - for k in 0..3 do - printf "k=%d: Theta = %f, xint=%f, yint=%f\n" k theta.[k] xint.[k] yint.[k] -#endif - - for j in 1 .. 3 do - let tmp0 = theta.[j] - let tmp1 = xint.[j] - let tmp2 = yint.[j] - - let mutable k = j - 1 - let mutable k2 = 0 - while k >= 0 do - if theta.[k] <= tmp0 - then - k2 <- k + 1 - k <- -1 - else - theta.[k+1] <- theta.[k] - xint.[k+1] <- xint.[k] - yint.[k+1] <- yint.[k] - k <- k - 1 - k2 <- k + 1 - - theta.[k2] <- tmp0 - xint.[k2] <- tmp1 - yint.[k2] <- tmp2 - - -#if DEBUG_LOG - printf "AFTER sorting\n" - for k in 0..3 do - printf "k=%d: Theta = %f, xint=%f, yint=%f\n" k theta.[k] xint.[k] yint.[k] -#endif - - let area1 = 0.5 * abs ((xint.[2] - xint.[0]) * (yint.[3] - yint.[1]) - (xint.[3] - xint.[1]) * (yint.[2] - yint.[0])) - - let cosphi = cos (phi_1 - phi_2) - let sinphi = sin (phi_1 - phi_2) - - let theta_tr = Array.zeroCreate 4 - let xint_tr = Array.zeroCreate 4 - let yint_tr = Array.zeroCreate 4 - - for i in 0..3 do - xint_tr.[i] <- (xint.[i] - h2_tr) * cosphi + (yint.[i] - k2_tr) * -sinphi - yint_tr.[i] <- (xint.[i] - h2_tr) * sinphi + (yint.[i] - k2_tr) * cosphi - - if abs xint_tr.[i] > a2 - then - xint_tr.[i] <- if xint_tr.[i] < 0.0 then -a2 else a2 - - theta_tr.[i] <- if yint_tr.[i] < 0.0 then 2.0 * Math.PI - acos (xint_tr.[i] / a2) else acos (xint_tr.[i] / a2) - - let xmid = a1 * cos ((theta.[0] + theta.[1]) / 2.0) - let ymid = b1 * sin ((theta.[0] + theta.[1]) / 2.0) - - let mutable area2, area3, area4, area5 = 0.0, 0.0, 0.0, 0.0 - - if ellipse2tr xmid ymid aa bb cc dd ee ff < 0.0 - then - area2 <- 0.5 * (a1b1 * (theta.[1] - theta.[0]) - abs (xint.[0] * yint.[1] - xint.[1] * yint.[0])) - area3 <- 0.5 * (a1b1 * (theta.[3] - theta.[2]) - abs (xint.[2] * yint.[3] - xint.[3] * yint.[2])) - area4 <- 0.5 * (a2b2 * (theta_tr.[2] - theta_tr.[1]) - abs (xint_tr.[1] * yint_tr.[2] - xint_tr.[2] * yint_tr.[1])) - - if theta_tr.[3] > theta_tr.[0] - then - area5 <- 0.5 * (a2b2 * (theta_tr.[0] - (theta_tr.[3] - 2.0 * Math.PI)) - abs (xint_tr.[3] * yint_tr.[0] - xint_tr.[0] * yint_tr.[3])) - else - area5 <- 0.5 * (a2b2 * (theta_tr.[0] - theta_tr.[3]) - abs (xint_tr.[3] * yint_tr.[0] - xint_tr.[0] * yint_tr.[3])) - else - area2 <- 0.5 * (a1b1 * (theta.[2] - theta.[1]) - abs (xint.[1] * yint.[2] - xint.[2] * yint.[1])) - area3 <- 0.5 * (a1b1 * (theta.[0] - (theta.[3] - 2.0 * Math.PI)) - abs (xint.[3] * yint.[0] - xint.[0] * yint.[3])) - area4 <- 0.5 * (a2b2 * (theta_tr.[1] - theta_tr.[0]) - abs (xint_tr.[0] * yint_tr.[1] - xint_tr.[1] * yint_tr.[0])) - area5 <- 0.5 * (a2b2 * (theta_tr.[3] - theta_tr.[2]) - abs (xint_tr.[2] * yint_tr.[3] - xint_tr.[3] * yint_tr.[2])) - - if area5 < 0.0 - then -#if DEBUG_LOG - printf "\n\t\t-------------> area5 is negativ (%f). Add: pi*A2*B2=%f <------------\n" area5 area_2 -#endif - area5 <- area5 + area_2 - - if area4 < 0.0 - then -#if DEBUG_LOG - printf "\n\t\t-------------> area4 is negativ (%f). Add: pi*A2*B2=%f <------------\n" area4 area_2 -#endif - area4 <- area4 + area_2 - - if area3 < 0.0 - then -#if DEBUG_LOG - printf "\n\t\t-------------> area3 is negativ (%f). Add: pi*A2*B2=%f <------------\n" area3 area_1 -#endif - area3 <- area3 + area_1 - - if area2 < 0.0 - then -#if DEBUG_LOG - printf "\n\t\t-------------> area2 is negativ (%f). Add: pi*A2*B2=%f <------------\n" area2 area_1 -#endif - area2 <- area2 + area_1 - -#if DEBUG_LOG - printf "\narea1=%f, area2=%f area3=%f, area4=%f, area5=%f\n\n" area1 area2 area3 area4 area5 -#endif - - area1 + area2 + area3 + area4 + area5 - -let private quadroots (p: float[]) (r: float[,]) = - let mutable b = -p.[1] / (2.0 * p.[0]) - let c = p.[2] / p.[0] - let mutable d = b * b - c - - if d >= 0.0 - then - if b > 0.0 - then - b <- sqrt d + b - r.[1, 2] <- b - else - b <- -sqrt d + b - r.[1, 2] <- b - r.[1, 1] <- c / b - r.[2, 1] <- 0.0 - r.[2, 2] <- 0.0 - else - d <- sqrt -d - r.[2, 1] <- d - r.[2, 2] <- -d - r.[1, 1] <- b - r.[1, 2] <- b - -let private cubicroots (p: float[]) (r: float[,]) = - if p.[0] <> 1.0 then - for k in 1..3 do - p.[k] <- p.[k] / p.[0] - p.[0] <- 1.0 - let s = p.[1] / 3.0 - let mutable t = s * p.[1] - let mutable b = 0.5 * (s * (t / 1.5 - p.[2]) + p.[3]) - t <- (t - p.[2]) / 3.0 - let mutable c = t * t * t - let mutable d = b * b - c - - if d >= 0.0 - then - d <- ((sqrt d) + (abs b)) ** (1.0 / 3.0) - if d <> 0.0 - then - if b > 0.0 - then b <- -d - else b <- d - c <- t / b - d <- sqrt(0.75) * (b - c) - r.[2, 2] <- d - b <- b + c - c <- -0.5 * b - s - r.[1, 2] <- c - if b > 0.0 && s <= 0.0 || b < 0.0 && s > 0.0 - then - r.[1, 1] <- c - r.[2, 1] <- -d - r.[1, 3] <- b - s - r.[2, 3] <- 0.0 - else - r.[1, 1] <- b - s - r.[2, 1] <- 0.0 - r.[1, 3] <- c - r.[2, 3] <- -d - else - if b = 0.0 - then d <- (atan 1.0) / 1.5 - else d <- atan ((sqrt -d) / (abs b)) / 3.0 - - if b < 0.0 - then b <- 2.0 * (sqrt t) - else b <- -2.0 * (sqrt t) - - c <- (cos d) * b - t <- -(sqrt 0.75) * (sin d) * b - 0.5 * c - d <- -t - c - s - c <- c - s - t <- t - s - - if abs c > abs t - then - r.[1, 3] <- c - else - r.[1, 3] <- t - t <- c - - if abs d > abs t - then - r.[1, 2] <- d - else - r.[1, 2] <- t - t <- d - - r.[1, 1] <- t - for k in 1..3 do - r.[2, k] <- 0.0 - -let private biquadroots (p: float[]) (r: float[,]) = - if p.[0] <> 1.0 - then - for k in 1..4 do - p.[k] <- p.[k] / p.[0] - p.[0] <- 1.0 - let e = 0.25 * p.[1] - let mutable b = 2.0 * e - let mutable c = b ** 2.0 - let mutable d = 0.75 * c - b <- p.[3] + b *(c - p.[2]) - let mutable a = p.[2] - d - c <- p.[4] + e * (e * a - p.[3]) - a <- a - d - - let mutable quadExecuted = false - let inline quad () = - if not quadExecuted - then - p.[2] <- c / b - quadroots p r - for k in 1..2 do - for j in 1..2 do - r.[j, k+2] <- r.[j, k] - p.[1] <- -p.[1] - p.[2] <- b - quadroots p r - for k in 1..4 do - r.[1,k] <- r.[1,k] - e - quadExecuted <- true - - p.[1] <- 0.5 * a - p.[2] <- (p.[1] * p.[1] - c) * 0.25 - p.[3] <- b * b / -64.0 - if p.[3] < 0.0 - then - cubicroots p r - let mutable k = 1 - while k < 4 do - if r.[2, k] = 0.0 && r.[1, k] > 0.0 - then - d <- r.[1, k] * 4.0 - a <- a + d - if a >= 0.0 && b >= 0.0 - then - p.[1] <- sqrt d - elif a <= 0.0 && b <= 0.0 - then - p.[1] <- sqrt d - else - p.[1] <- -(sqrt d) - b <- 0.5 * (a + b / p.[1]) - quad () - k <- 4 - k <- k + 1 - - if not quadExecuted && p.[2] < 0.0 - then - b <- sqrt c - d <- b + b - a - p.[1] <- 0.0 - if d > 0.0 - then - p.[1] <- sqrt d - elif not quadExecuted - then - if p.[1] > 0.0 - then - b <- (sqrt p.[2]) * 2.0 + p.[1] - else - b <- -(sqrt p.[2]) * 2.0 + p.[1] - - if b <> 0.0 - then - p.[1] <- 0.0 - else - for k in 1..4 do - r.[1, k] <- -e - r.[2, k] <- 0.0 - quadExecuted <- true - - quad () - -// Return a tuple (area, x intersections, y intersections) -let EEOverlapArea (e1: Types.Ellipse) (e2: Types.Ellipse) : (float32 * float32[] * float32[]) option = - let h1, k1, a1, b1, phi_1 = float e1.Cx, float e1.Cy, float e1.A, float e1.B, float e1.Alpha - let h2, k2, a2, b2, phi_2 = float e2.Cx, float e2.Cy, float e2.A, float e2.B, float e2.Alpha - - if a1 <= EPS || b1 <= EPS || a2 <= EPS || b2 <= EPS - then - None - else - let phi_1 = phi_1 % Math.PI //(if phi_1 > Math.PI / 2.0 then phi_1 - Math.PI else phi_1) % Math.PI - let phi_2 = phi_2 % Math.PI //(if phi_2 > Math.PI / 2.0 then phi_2 - Math.PI else phi_2) % Math.PI - let h2_tr, k2_tr, phi_2r = - let cosphi = cos phi_1 - let sinphi = sin phi_1 - (h2 - h1) * cosphi + (k2 - k1) * sinphi, (h1 - h2) * sinphi + (k2 - k1) * cosphi, (phi_2 - phi_1) % (2.0 * Math.PI) - -#if DEBUG_LOG - printf "H2_TR=%f, K2_TR=%f, PHI_2R=%f\n" h2_tr k2_tr phi_2r -#endif - - let cosphi = cos phi_2r - let cosphi2 = cosphi ** 2.0 - let sinphi = sin phi_2r - let sinphi2 = sinphi ** 2.0 - let cosphisinphi = 2.0 * cosphi * sinphi - let a22 = a2 ** 2.0 - let b22 = b2 ** 2.0 - let tmp0 = (cosphi * h2_tr + sinphi * k2_tr) / a22 - let tmp1 = (sinphi * h2_tr - cosphi * k2_tr) / b22 - let tmp2 = cosphi * h2_tr + sinphi * k2_tr - let tmp3 = sinphi * h2_tr - cosphi * k2_tr - - let aa = cosphi2 / a22 + sinphi2 / b22 - let bb = cosphisinphi / a22 - cosphisinphi / b22 - let cc = sinphi2 / a22 + cosphi2 / b22 - let dd = -2.0 * cosphi * tmp0 - 2.0 * sinphi * tmp1 - let ee = -2.0 * sinphi * tmp0 + 2.0 * cosphi * tmp1 - let ff = tmp2 * tmp2 / a22 + tmp3 * tmp3 / b22 - 1.0 - - let cy = [| - (a1 * (a1 * aa - dd) + ff) * (a1 * (a1 * aa + dd) + ff) - 2.0 * b1 * (a1 * a1 * (aa * ee - bb * dd) + ee * ff) - a1 * a1 * ((b1 * b1 * (2.0 * aa * cc - bb * bb) + dd * dd - 2.0 * aa * ff) - 2.0 * a1 * a1 * aa * aa) + b1 * b1 * (2.0 * cc * ff + ee * ee) - 2.0 * b1 * (b1 * b1 * cc * ee + a1 * a1 * (bb * dd - aa * ee)) - a1 * a1 * a1 * a1 * aa * aa + b1 * b1 * (a1 * a1 * (bb * bb - 2.0 * aa * cc) + b1 * b1 * cc * cc) - |] - -#if DEBUG_LOG - for i in 0..4 do - printf "cy[%d]=%f\n" i cy.[i] -#endif - - let py = Array.zeroCreate 5 - let r = Array2D.zeroCreate 3 5 - - let nroots = - if abs cy.[4] > EPS - then - for i in 0 .. 3 do - py.[4-i] <- cy.[i] / cy.[4] - py.[0] <- 1.0 -#if DEBUG_LOG - for i in 0..4 do - printf "py[%d]=%f\n" i py.[i] -#endif - biquadroots py r - 4 - - elif abs cy.[3] > EPS - then - for i in 0..2 do - py.[3 - i] <- cy.[i] / cy.[3] - py.[0] <- 1.0 - cubicroots py r - 3 - - elif abs cy.[2] > EPS - then - for i in 0..1 do - py.[2-i] <- cy.[i] / cy.[2] - py.[0] <- 1.0 - quadroots py r - 2 - - elif abs cy.[1] > EPS - then - r.[1, 1] <- -cy.[0] / cy.[1] - r.[2, 1] <- 0.0 - 1 - - else - 0 - -#if DEBUG_LOG - printf "nroots = %d\n" nroots -#endif - - let ychk = Array.init nroots (fun _ -> Double.MaxValue) - let mutable nychk = 0 - for i in 1 .. nroots do - if abs r.[2, i] < EPS - then - ychk.[nychk] <- r.[1, i] * b1 - nychk <- nychk + 1 -#if DEBUG_LOG - printf "ROOT is Real, i=%d --> %f (B1=%f)\n" i r.[1, i] b1 -#endif - Array.sortInPlace ychk - -#if DEBUG_LOG - printf "nychk=%d\n" ychk.Length - for j in 0 .. ychk.Length - 1 do - printf "\t j=%d, ychk=%f\n" j ychk.[j] -#endif - - let mutable nintpts = 0 - - let xint = Array.zeroCreate 4 - let yint = Array.zeroCreate 4 - - let mutable returnValue = 0.0 - - let mutable i = 0 - while returnValue = 0.0 && i < nychk do -#if DEBUG_LOG - printf "------------->i=%d (nychk=%d)\n" i nychk -#endif - - if not (i < nychk - 1 && abs (ychk.[i] - ychk.[i+1]) < EPS / 2.0) - then -#if DEBUG_LOG - printf "check intersecting points. nintps is %d" nintpts -#endif - - let x1 = if abs ychk.[i] > b1 then 0.0 else a1 * sqrt (1.0 - (ychk.[i] * ychk.[i]) / (b1 * b1)) - let x2 = -x1 - -#if DEBUG_LOG - printf "\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 - nintpts <- nintpts + 1 -#if DEBUG_LOG - printf "first if x1. acc nintps=%d\n" nintpts -#endif - if nintpts > 4 - then - returnValue <- -1.0 - else - xint.[nintpts-1] <- x1 - yint.[nintpts-1] <- ychk.[i] -#if DEBUG_LOG - printf "nintpts=%d, xint=%f, x2=%f, i=%d, yint=%f\n" nintpts x1 x2 i ychk.[i] -#endif - - if returnValue <> -1.0 && abs (ellipse2tr x2 ychk.[i] aa bb cc dd ee ff) < EPS && abs (x2 - x1) > EPS - then - nintpts <- nintpts + 1 -#if DEBUG_LOG - printf "first if x2. nintps=%d, Dx=%f (eps2=%f) \n" nintpts (abs (x2 - x1)) EPS -#endif - if nintpts > 4 - then - returnValue <- -1.0 - else - xint.[nintpts-1] <- x2 - yint.[nintpts-1] <- ychk.[i] - -#if DEBUG_LOG - printf "nintpts=%d, x1=%f, xint=%f, i=%d, yint=%f\n" nintpts x1 x2 i ychk.[i] -#endif - -#if DEBUG_LOG - else - printf "i=%d, multiple roots: %f <--------> %f. continue\n" i ychk.[i] ychk.[i-1] -#endif - i <- i + 1 - - - if returnValue = -1.0 - then - None - else - let area = - match nintpts with - | 0 | 1 -> nointpts a1 b1 a2 b2 h1 k1 h2 k2 phi_1 phi_2 h2_tr k2_tr aa bb cc dd ee ff - | 2 -> match istanpt xint.[0] yint.[0] a1 b1 aa bb cc dd ee ff with - | TANGENT_POINT -> -#if DEBUG_LOG - printf "one point is tangent\n" -#endif - nointpts a1 b1 a2 b2 h1 k1 h2 k2 phi_1 phi_2 h2_tr k2_tr aa bb cc dd ee ff - - | INTERSECTION_POINT -> -#if DEBUG_LOG - printf "check twointpts\n" -#endif - twointpts xint yint a1 b1 phi_1 a2 b2 h2_tr k2_tr phi_2 aa bb cc dd ee ff - | 3 -> threeintpts xint yint a1 b1 phi_1 a2 b2 h2_tr k2_tr phi_2 aa bb cc dd ee ff - | 4 -> fourintpts xint yint a1 b1 phi_1 a2 b2 h2_tr k2_tr phi_2 aa bb cc dd ee ff - | _ -> -1.0 - if nintpts = 0 - then Some (float32 area, [||], [||]) - else - let xTransform : float32[] = Array.zeroCreate nintpts - let yTransform : float32[] = Array.zeroCreate nintpts - for i in 0 .. (nintpts - 1) do - xTransform.[i] <- float32 <| cos phi_1 * xint.[i] - sin phi_1 * yint.[i] + h1 - yTransform.[i] <- float32 <| sin phi_1 * xint.[i] + cos phi_1 * yint.[i] + k1 - Some (float32 area, xTransform, yTransform) \ No newline at end of file diff --git a/Parasitemia/Parasitemia/Ellipse.fs b/Parasitemia/Parasitemia/Ellipse.fs deleted file mode 100644 index ac3c041..0000000 --- a/Parasitemia/Parasitemia/Ellipse.fs +++ /dev/null @@ -1,350 +0,0 @@ -module Ellipse - -open System -open System.Collections.Generic -open System.Drawing - -open MathNet.Numerics.LinearAlgebra - -open Emgu.CV -open Emgu.CV.Structure - -open Utils -open Config -open MatchingEllipses -open Const - -type private SearchExtremum = Minimum | Maximum - -let private goldenSectionSearch (f: float -> float) (nbIter: int) (xmin: float) (xmax: float) (searchExtremum: SearchExtremum) : (float * float) = - let gr = 1. / 1.6180339887498948482 - let mutable a = xmin - let mutable b = xmax - let mutable c = b - gr * (b - a) - let mutable d = a + gr * (b - a) - - for i in 1 .. nbIter do - let mutable fc = f c - let mutable fd = f d - - if searchExtremum = Maximum - then - let tmp = fc - fc <- fd - fd <- tmp - - if fc < fd - then - b <- d - d <- c - c <- b - gr * (b - a) - else - a <- c - c <- d - d <- a + gr * (b - a) - - let x = (b + a) / 2. - x, f x - -// Ellipse.A is always equal or greater than Ellipse.B. -// 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 accuracy_extremum_search_1 = 10 // 3 - let accuracy_extremum_search_2 = 10 // 4 - - // p3 as the referencial. - let p1x = p1x - p3x - let p1y = p1y - p3y - - let p2x = p2x - p3x - let p2y = p2y - p3y - - // Convert to polar coordinates. - let alpha1 = atan m1 - let alpha2 = atan m2 - - let r1 = sqrt (p1x ** 2. + p1y ** 2.) - let theta1 = atan2 p1y p1x - - let r2 = sqrt (p2x ** 2. + p2y ** 2.) - let theta2 = atan2 p2y p2x - - let valid = - 4. * sin (alpha1 - theta1) * (-r1 * sin (alpha1 - theta1) + r2 * sin (alpha1 - theta2)) * - sin (alpha2 - theta2) * (-r1 * sin (alpha2 - theta1) + r2 * sin (alpha2 - theta2)) + - r1 * r2 * sin (alpha1 - alpha2) ** 2. * sin (theta1 - theta2) ** 2. < 0. - - if valid - then - let r theta = - (r1 * r2 * (r1 * (cos (alpha2 + theta - theta1 - theta2) - cos (alpha2 - theta) * cos (theta1 - theta2)) * sin (alpha1 - theta1) + r2 * (-cos (alpha1 + theta - theta1 - theta2) + cos (alpha1 - theta) * cos (theta1 - theta2)) * sin (alpha2 - theta2)) * sin (theta1 - theta2)) / - (sin (alpha1 - theta1) * sin (alpha2 - theta2) * (r1 * sin (theta - theta1) - r2 * sin (theta - theta2)) ** 2. - r1 * r2 * sin (alpha1 - theta) * sin (alpha2 - theta) * sin (theta1 - theta2) ** 2.) - - let rabs = r >> abs - - // We search for an interval [theta_a, theta_b] and assume the function is unimodal in this interval. - let thetaTan, _ = goldenSectionSearch rabs accuracy_extremum_search_1 0. Math.PI Maximum - let rTan = r thetaTan - - let PTanx = rTan * cos thetaTan - let PTany = rTan * sin thetaTan - - let d1a = tan alpha1 - let d1b = -d1a * p1x + p1y - - let d2a = tan alpha2 - let d2b = -d2a * p2x + p2y - - let d3a = -1. / tan thetaTan - let d3b = -d3a * PTanx + PTany - - let Ux = -(d1b - d2b) / (d1a - d2a) - let Uy = -(d2a * d1b - d1a * d2b) / (d1a - d2a) - - let Vx = -(d1b - d3b) / (d1a - d3a) - let Vy = -(d3a * d1b - d1a * d3b) / (d1a - d3a) - - let Wx = p1x + (p2x - p1x) / 2. - let Wy = p1y + (p2y - p1y) / 2. - - let Zx = p1x + (PTanx - p1x) / 2. - let Zy = p1y + (PTany - p1y) / 2. - - let va = -(-Vy + Zy) / (Vx - Zx) - let vb = -(Zx * Vy - Vx * Zy) / (Vx - Zx) - - let ua = -(-Uy + Wy) / (Ux - Wx) - let ub = -(Wx * Uy - Ux * Wy) / (Ux - Wx) - - let cx = -(vb - ub) / (va - ua) - let cy = -(ua * vb - va * ub) / (va - ua) - - let rc = sqrt (cx ** 2. + cy ** 2.) - let psi = atan2 cy cx - - let rellipse theta = - sqrt ( - rc ** 2. + (r1 ** 2. * r2 ** 2. * (r1 * (cos (alpha2 + theta - theta1 - theta2) - cos (alpha2 - theta) * cos (theta1 - theta2)) * sin (alpha1 - theta1) + r2 * (-cos (alpha1 + theta - theta1 - theta2) + cos (alpha1 - theta) * cos (theta1 - theta2)) * sin (alpha2 - theta2)) ** 2. * sin (theta1 - theta2) ** 2.) / - (sin (alpha1 - theta1) * sin (alpha2 - theta2) * (r1 * sin (theta - theta1) - r2 * sin (theta - theta2)) ** 2. - r1 * r2 * sin (alpha1 - theta) * sin (alpha2 - theta) * sin (theta1 - theta2) ** 2.) ** 2. - - (2. * r1 * r2 * rc * cos (theta - psi) * (r1 * (cos (alpha2 + theta - theta1 - theta2) - cos (alpha2 - theta) * cos (theta1 - theta2)) * sin (alpha1 - theta1) + r2 * (-cos (alpha1 + theta - theta1 - theta2) + cos (alpha1 - theta) * cos (theta1 - theta2)) * sin (alpha2 - theta2)) * sin (theta1 - theta2)) / - (sin (alpha1 - theta1) * sin (alpha2 - theta2) * (r1 * sin (theta - theta1) - r2 * sin (theta - theta2)) ** 2. - r1 * r2 * sin (alpha1 - theta) * sin (alpha2 - theta) * sin (theta1 - theta2) ** 2.)) - - // We search for an interval [theta_a, theta_b] and assume the function is unimodal in this interval. - let r1eTheta, r1e = goldenSectionSearch rellipse accuracy_extremum_search_2 0. (Math.PI / 2.) Maximum // Pi/2 and not pi because the period is Pi. - let r2eTheta, r2e = goldenSectionSearch rellipse accuracy_extremum_search_2 0. (Math.PI / 2.) Minimum - - let rr1e = r r1eTheta - let r1ex = rr1e * cos r1eTheta - let r1ey = rr1e * sin r1eTheta - let mutable alpha = atan ((r1ey - cy) / (r1ex - cx)) - if alpha < 0. - then - alpha <- alpha + Math.PI - - // Ride off the p3 referential. - let cx = cx + p3x - let cy = cy + p3y - - Some (Types.Ellipse(float32 cx, float32 cy, float32 r1e, float32 r2e, float32 alpha)) - else - None - -let ellipse2 (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 p0x, p0y = float p0.X, float p0.Y - - let s = matrix [[ 1.; 0.; 0. ] - [ 0.; 0.; -0.5 ] - [ 0.; -0.5; 0. ]] - - let v0 = matrix [[ 1.; p0x; p0y ]] - let v1 = matrix [[ 1.; p1x; p1y ]] - 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 a = conicMat.[0, 0] - let b = conicMat.[0, 1] - let c = conicMat.[1, 1] - let d = conicMat.[0, 2] - let e = conicMat.[1, 2] - let f = conicMat.[2, 2] - - // Center. - let cx = b / a - let cy = d / a - - let at = c * f - e ** 2. + (e * d - b * f) * cx + (b * e - c * d) * cy - if at = 0. - then - 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 eigenValues = eigen.EigenValues - let lambda = eigenValues.[1].Real - let mu = eigenValues.[0].Real - - if lambda <= 0. || mu <= 0. - then - None - else - let r1, r2 = 1. / (sqrt lambda), 1. / (sqrt mu) - - let eigenVectors = eigen.EigenVectors - let v_a = eigenVectors.[0, 0] - let v_b = eigenVectors.[1, 0] // [0, 1] - - // Angle against the longest axis. - let phi = (if r2 > r1 then atan (v_b / v_a) else atan (v_a / v_b)) - - 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')) - - -let private vectorRotation (p1x: float32) (p1y: float32) (v1x: float32) (v1y: float32) (px: float32) (py: float32) : float32 = - let mutable rotation = 1.f - if p1y > py - then - if v1x > 0.f - then - rotation <- -1.f - elif p1y < py - then - if v1x < 0.f - then - rotation <- -1.f - elif p1x > px - then - if v1y < 0.f - then - rotation <- -1.f - elif p1x < px - then - if v1y > 0.f - then - rotation <- -1.f - rotation - -let private areVectorsValid (p1x: float32) (p1y: float32) (p2x: float32) (p2y: float32) (v1x: float32) (v1y: float32) (v2x: float32) (v2y: float32) : (float32 * float32) option = - let m1 = -v1x / v1y - let m2 = -v2x / v2y - - let b1 = -m1 * p1x + p1y - let b2 = -m2 * p2x + p2y - let px = -((b1 - b2) / (m1 - m2)) - let py = -((m2 * b1 - m1 * b2) / (m1 - m2)) - - let rot1 = vectorRotation p1x p1y v1x v1y px py - let rot2 = vectorRotation p2x p2y v2x v2y px py - - if rot1 = rot2 - then - None - else - let alpha1 = atan2 (p1y - py) (p1x - px) - let alpha2 = atan2 (p2y - py) (p2x - px) - - let alpha1' = if alpha1 < 0.f then 2.f * PI + alpha1 else alpha1 - let alpha2' = if alpha2 < 0.f then 2.f * PI + alpha2 else alpha2 - - let diff = rot1 * alpha1' + rot2 * alpha2' - - if diff > PI || (diff < 0.f && diff > -PI) - then - None - else - Some (m1, m2) - - -let find (edges: Matrix) - (xGradient: Image) - (yGradient: Image) - (config: Config) : MatchingEllipses = - - let r1, r2 = config.RBCRadius.Min, config.RBCRadius.Max - let incrementWindowDivisor = 4.f - - // We choose a window size for which the biggest ellipse can always be fitted in. - let windowSize = roundInt (2.f * r2 / (incrementWindowDivisor - 1.f) * incrementWindowDivisor) - let factorNbPick = config.Parameters.factorNbPick - - let increment = windowSize / (int incrementWindowDivisor) - - let radiusTolerance = (r2 - r1) * 0.2f - - let squaredMinimumDistance = (float r2 / 1.5) ** 2. - let inline squaredDistance x1 y1 x2 y2 = (x1 - x2) ** 2. + (y1 - y2) ** 2. - - let h = edges.Height - let w = edges.Width - let h_f = float32 h - let w_f = float32 w - - let mutable last_i, last_j = Int32.MaxValue, Int32.MaxValue - - let currentElements = List() - - let edgesData = edges.Data - let xDirData = xGradient.Data - let yDirData = yGradient.Data - - let rng = Random(42) - - 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 - - let window_i_begin = if window_i < 0 then 0 else window_i - let window_i_end = if window_i + windowSize - 1 >= h then h - 1 else window_i + windowSize - 1 - let window_j_begin = if window_j < 0 then 0 else window_j - 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) - if indexFirstElement > 0 - then currentElements.RemoveRange(0, indexFirstElement) - - // Add the new elements. - let newElemsBegin_j = window_j + windowSize - increment - let newElemsEnd_j = window_j + windowSize - 1 - for j in (if newElemsBegin_j < 0 then 0 else newElemsBegin_j) .. (if newElemsEnd_j >= w then w - 1 else newElemsEnd_j) do - for i in window_i_begin .. window_i_end do - if edgesData.[i, j] = 1uy - then currentElements.Add(Point(j, i)) - - if currentElements.Count >= 10 - then - let mutable nbOfPicks = (float currentElements.Count) * factorNbPick |> int - while nbOfPicks > 0 do - 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 - let p2yf, p2xf = float p2.Y, float p2.X - let p3yf, p3xf = float p3.Y, float p3.X - if squaredDistance p1xf p1yf p2xf p2yf >= squaredMinimumDistance && - squaredDistance p1xf p1yf p3xf p3yf >= squaredMinimumDistance && - squaredDistance p2xf p2yf p3xf p3yf >= squaredMinimumDistance - then - match areVectorsValid (float32 p1xf) (float32 p1yf) (float32 p2xf) (float32 p2yf) -xDirData.[p1.Y, p1.X, 0] -yDirData.[p1.Y, p1.X, 0] -xDirData.[p2.Y, p2.X, 0] -yDirData.[p2.Y, p2.X, 0] with - | Some (m1, m2) -> - //let pouet = ellipse2 p1xf p1yf (float m1) p2xf p2yf (float m2) p3xf p3yf - match ellipse2 p1xf p1yf (float m1) p2xf p2yf (float m2) p3xf p3yf with - | Some e when e.Cx > 0.f && e.Cx < w_f - 1.f && e.Cy > 0.f && e.Cy < h_f - 1.f && - e.A >= r1 - radiusTolerance && e.A <= r2 + radiusTolerance && e.B >= r1 - radiusTolerance && e.B <= r2 + radiusTolerance -> - ellipses.Add e - | _ -> () - | _ -> () - - currentElements.Clear() - - ellipses - diff --git a/Parasitemia/Parasitemia/GUI/About.fs b/Parasitemia/Parasitemia/GUI/About.fs deleted file mode 100644 index bc8f3e5..0000000 --- a/Parasitemia/Parasitemia/GUI/About.fs +++ /dev/null @@ -1,33 +0,0 @@ -module Parasitemia.GUI.About - -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 window = Views.AboutWindow() - window.Root.Owner <- parent - window.Root.Left <- parent.Left + parent.ActualWidth / 2. - window.Root.Width / 2. - window.Root.Top <- parent.Top + parent.ActualHeight / 2. - window.Root.Height / 2. - - let ctrl (name: string): 'a = window.Root.FindName(name) :?> 'a - - let butClose: Button = ctrl "butClose" - let txtAbout: TextBlock = ctrl "txtAbout" - - let version = System.Reflection.Assembly.GetEntryAssembly().GetName().Version - let txtVersion = sprintf "%d.%d.%d" version.Major version.Minor version.Revision - txtAbout.Inlines.FirstInline.ElementEnd.InsertTextInRun(txtVersion) - -#if DEBUG - txtAbout.Inlines.FirstInline.ElementEnd.InsertTextInRun(" - DEBUG") -#endif - - butClose.Click.AddHandler(fun obj args -> window.Root.Close()) - - window.Root.ShowDialog() |> ignore - diff --git a/Parasitemia/Parasitemia/GUI/AboutWindow.xaml b/Parasitemia/Parasitemia/GUI/AboutWindow.xaml deleted file mode 100644 index 3cb0de4..0000000 --- a/Parasitemia/Parasitemia/GUI/AboutWindow.xaml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - Parasitemia - - HES-SO / - CHUV - - Grégory Burri - - - - - - \ No newline at end of file diff --git a/Parasitemia/Parasitemia/GUI/ImageSourceSelection.xaml.fs b/Parasitemia/Parasitemia/GUI/ImageSourceSelection.xaml.fs deleted file mode 100644 index 1ca5c66..0000000 --- a/Parasitemia/Parasitemia/GUI/ImageSourceSelection.xaml.fs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Parasitemia.GUI.Views - -open System -open System.Windows -open System.Windows.Data -open System.Windows.Input - -open FSharp.ViewModule -open FsXaml - -type ImageSourceSelection = XAML<"GUI/ImageSourceSelection.xaml", true> - -(* type ImageSourcePreviewController() = - inherit UserControlViewController() *) - -(* type ImageSourcePreviewViewModel() = - inherit ViewModelBase() *) diff --git a/Parasitemia/Parasitemia/GUI/MainWindow.xaml b/Parasitemia/Parasitemia/GUI/MainWindow.xaml deleted file mode 100644 index 37ea0c0..0000000 --- a/Parasitemia/Parasitemia/GUI/MainWindow.xaml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Parasitemia/Parasitemia/GUI/MainWindow.xaml.fs b/Parasitemia/Parasitemia/GUI/MainWindow.xaml.fs deleted file mode 100644 index 4e327e0..0000000 --- a/Parasitemia/Parasitemia/GUI/MainWindow.xaml.fs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Parasitemia.GUI.Views - -open FsXaml - -type MainWindow = XAML<"GUI/MainWindow.xaml"> - diff --git a/Parasitemia/Parasitemia/GUI/NumericUpDown.xaml b/Parasitemia/Parasitemia/GUI/NumericUpDown.xaml deleted file mode 100644 index ae20a5b..0000000 --- a/Parasitemia/Parasitemia/GUI/NumericUpDown.xaml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - + + + + \ No newline at end of file diff --git a/Parasitemia/ParasitemiaUI/XAML/ImageSourceSelection.xaml.fs b/Parasitemia/ParasitemiaUI/XAML/ImageSourceSelection.xaml.fs new file mode 100644 index 0000000..4c91ed2 --- /dev/null +++ b/Parasitemia/ParasitemiaUI/XAML/ImageSourceSelection.xaml.fs @@ -0,0 +1,17 @@ +namespace ParasitemiaUI.Views + +open System +open System.Windows +open System.Windows.Data +open System.Windows.Input + +open FSharp.ViewModule +open FsXaml + +type ImageSourceSelection = XAML<"XAML/ImageSourceSelection.xaml", true> + +(* type ImageSourcePreviewController() = + inherit UserControlViewController() *) + +(* type ImageSourcePreviewViewModel() = + inherit ViewModelBase() *) diff --git a/Parasitemia/ParasitemiaUI/XAML/MainWindow.xaml b/Parasitemia/ParasitemiaUI/XAML/MainWindow.xaml new file mode 100644 index 0000000..37ea0c0 --- /dev/null +++ b/Parasitemia/ParasitemiaUI/XAML/MainWindow.xaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Parasitemia/ParasitemiaUI/XAML/MainWindow.xaml.fs b/Parasitemia/ParasitemiaUI/XAML/MainWindow.xaml.fs new file mode 100644 index 0000000..adbfff5 --- /dev/null +++ b/Parasitemia/ParasitemiaUI/XAML/MainWindow.xaml.fs @@ -0,0 +1,6 @@ +namespace ParasitemiaUI.Views + +open FsXaml + +type MainWindow = XAML<"XAML/MainWindow.xaml"> + diff --git a/Parasitemia/ParasitemiaUI/XAML/NumericUpDown.xaml b/Parasitemia/ParasitemiaUI/XAML/NumericUpDown.xaml new file mode 100644 index 0000000..ae20a5b --- /dev/null +++ b/Parasitemia/ParasitemiaUI/XAML/NumericUpDown.xaml @@ -0,0 +1,21 @@ + + + + + + + + + + +