From 4bfa3cbdc6145e6944f02e24829ab2ef3a851ac1 Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Mon, 18 Jan 2016 13:22:20 +0100 Subject: [PATCH 1/1] Add a logger assembly and split the main assembly in two : the UI and the parasitemia evaluation. --- Parasitemia/Logger/AssemblyInfo.fs | 41 +++++ Parasitemia/Logger/Logger.fs | 168 ++++++++++++++++++ Parasitemia/Logger/Logger.fsproj | 72 ++++++++ Parasitemia/Parasitemia.sln | 18 +- .../Parasitemia/GUI/AboutWindow.xaml.fs | 6 - .../Parasitemia/GUI/AnalysisWindow.xaml.fs | 6 - .../Parasitemia/GUI/MainWindow.xaml.fs | 6 - Parasitemia/Parasitemia/Program.fs | 94 ---------- Parasitemia/Parasitemia/packages.config | 14 -- Parasitemia/ParasitemiaCore/AssemblyInfo.fs | 41 +++++ .../Classifier.fs | 2 +- .../Config.fs | 2 +- .../{Parasitemia => ParasitemiaCore}/Const.fs | 2 +- .../EEOver.fs | 2 +- .../Ellipse.fs | 2 +- .../Granulometry.fs | 2 +- .../{Parasitemia => ParasitemiaCore}/Heap.fs | 2 +- .../ImgTools.fs | 4 +- .../KMeans.fs | 2 +- .../KMedians.fs | 2 +- .../KdTree.fs | 2 +- .../MainAnalysis.fs | 8 +- .../MatchingEllipses.fs | 21 ++- .../ParasitemiaCore/ParasitemiaCore.fsproj | 117 ++++++++++++ .../ParasitesMarker.fs | 7 +- .../{Parasitemia => ParasitemiaCore}/Types.fs | 2 +- .../UnitsOfMeasure.fs | 2 +- .../{Parasitemia => ParasitemiaCore}/Utils.fs | 15 +- Parasitemia/ParasitemiaCore/packages.config | 7 + .../GUI => ParasitemiaUI}/About.fs | 2 +- .../GUI => ParasitemiaUI}/Analysis.fs | 28 +-- .../{Parasitemia => ParasitemiaUI}/App.config | 0 .../AssemblyInfo.fs | 6 +- .../{Parasitemia/GUI => ParasitemiaUI}/GUI.fs | 49 +++-- .../ParasitemiaUI.fsproj} | 107 ++++------- .../GUI => ParasitemiaUI}/PiaZ.fs | 26 ++- Parasitemia/ParasitemiaUI/Program.fs | 94 ++++++++++ .../Resources/icon.ico | Bin .../GUI => ParasitemiaUI}/State.fs | 20 ++- .../GUI => ParasitemiaUI}/Types.fs | 4 +- .../XAML}/AboutWindow.xaml | 0 .../ParasitemiaUI/XAML/AboutWindow.xaml.fs | 6 + .../XAML}/AnalysisWindow.xaml | 0 .../ParasitemiaUI/XAML/AnalysisWindow.xaml.fs | 6 + .../XAML}/ImageSourcePreview.xaml | 0 .../XAML}/ImageSourcePreview.xaml.fs | 4 +- .../XAML}/ImageSourceSelection.xaml | 0 .../XAML}/ImageSourceSelection.xaml.fs | 4 +- .../XAML}/MainWindow.xaml | 0 .../ParasitemiaUI/XAML/MainWindow.xaml.fs | 6 + .../XAML}/NumericUpDown.xaml | 0 .../XAML}/NumericUpDown.xaml.fs | 4 +- .../GUI => ParasitemiaUI/XAML}/RBCFrame.xaml | 0 .../XAML}/RBCFrame.xaml.fs | 4 +- Parasitemia/ParasitemiaUI/packages.config | 8 + .../resources.rc | 0 .../resources.res | Bin 57 files changed, 755 insertions(+), 292 deletions(-) create mode 100644 Parasitemia/Logger/AssemblyInfo.fs create mode 100644 Parasitemia/Logger/Logger.fs create mode 100644 Parasitemia/Logger/Logger.fsproj delete mode 100644 Parasitemia/Parasitemia/GUI/AboutWindow.xaml.fs delete mode 100644 Parasitemia/Parasitemia/GUI/AnalysisWindow.xaml.fs delete mode 100644 Parasitemia/Parasitemia/GUI/MainWindow.xaml.fs delete mode 100644 Parasitemia/Parasitemia/Program.fs delete mode 100644 Parasitemia/Parasitemia/packages.config create mode 100644 Parasitemia/ParasitemiaCore/AssemblyInfo.fs rename Parasitemia/{Parasitemia => ParasitemiaCore}/Classifier.fs (99%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/Config.fs (99%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/Const.fs (50%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/EEOver.fs (99%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/Ellipse.fs (99%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/Granulometry.fs (98%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/Heap.fs (98%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/ImgTools.fs (99%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/KMeans.fs (98%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/KMedians.fs (97%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/KdTree.fs (99%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/MainAnalysis.fs (97%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/MatchingEllipses.fs (78%) create mode 100644 Parasitemia/ParasitemiaCore/ParasitemiaCore.fsproj rename Parasitemia/{Parasitemia => ParasitemiaCore}/ParasitesMarker.fs (95%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/Types.fs (98%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/UnitsOfMeasure.fs (86%) rename Parasitemia/{Parasitemia => ParasitemiaCore}/Utils.fs (80%) create mode 100644 Parasitemia/ParasitemiaCore/packages.config rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI}/About.fs (97%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI}/Analysis.fs (89%) rename Parasitemia/{Parasitemia => ParasitemiaUI}/App.config (100%) rename Parasitemia/{Parasitemia => ParasitemiaUI}/AssemblyInfo.fs (91%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI}/GUI.fs (94%) rename Parasitemia/{Parasitemia/Parasitemia.fsproj => ParasitemiaUI/ParasitemiaUI.fsproj} (68%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI}/PiaZ.fs (80%) create mode 100644 Parasitemia/ParasitemiaUI/Program.fs rename Parasitemia/{Parasitemia => ParasitemiaUI}/Resources/icon.ico (100%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI}/State.fs (84%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI}/Types.fs (81%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI/XAML}/AboutWindow.xaml (100%) create mode 100644 Parasitemia/ParasitemiaUI/XAML/AboutWindow.xaml.fs rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI/XAML}/AnalysisWindow.xaml (100%) create mode 100644 Parasitemia/ParasitemiaUI/XAML/AnalysisWindow.xaml.fs rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI/XAML}/ImageSourcePreview.xaml (100%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI/XAML}/ImageSourcePreview.xaml.fs (74%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI/XAML}/ImageSourceSelection.xaml (100%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI/XAML}/ImageSourceSelection.xaml.fs (73%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI/XAML}/MainWindow.xaml (100%) create mode 100644 Parasitemia/ParasitemiaUI/XAML/MainWindow.xaml.fs rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI/XAML}/NumericUpDown.xaml (100%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI/XAML}/NumericUpDown.xaml.fs (70%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI/XAML}/RBCFrame.xaml (100%) rename Parasitemia/{Parasitemia/GUI => ParasitemiaUI/XAML}/RBCFrame.xaml.fs (59%) create mode 100644 Parasitemia/ParasitemiaUI/packages.config rename Parasitemia/{Parasitemia => ParasitemiaUI}/resources.rc (100%) rename Parasitemia/{Parasitemia => ParasitemiaUI}/resources.res (100%) 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/GUI/AboutWindow.xaml.fs b/Parasitemia/Parasitemia/GUI/AboutWindow.xaml.fs deleted file mode 100644 index 5901226..0000000 --- a/Parasitemia/Parasitemia/GUI/AboutWindow.xaml.fs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Parasitemia.GUI.Views - -open FsXaml - -type AboutWindow = XAML<"GUI/AboutWindow.xaml"> - diff --git a/Parasitemia/Parasitemia/GUI/AnalysisWindow.xaml.fs b/Parasitemia/Parasitemia/GUI/AnalysisWindow.xaml.fs deleted file mode 100644 index 78cc682..0000000 --- a/Parasitemia/Parasitemia/GUI/AnalysisWindow.xaml.fs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Parasitemia.GUI.Views - -open FsXaml - -type AnalysisWindow = XAML<"GUI/AnalysisWindow.xaml"> - 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/Program.fs b/Parasitemia/Parasitemia/Program.fs deleted file mode 100644 index 06059f6..0000000 --- a/Parasitemia/Parasitemia/Program.fs +++ /dev/null @@ -1,94 +0,0 @@ -module Parasitemia.Main - -open System -open System.IO -open System.Threading - -open Emgu.CV -open Emgu.CV.Structure - -open Config - -type Input = - | File of string - | Dir of string - -type RunningMode = - | CmdLine of Input * string // A file or a directory to process and the output directory. - | Window of string option // An optional path to a file to open can be given in window mode. - -type Arguments = RunningMode * bool - -let parseArgs (args: string[]) : Arguments = - - let output = Array.tryFindIndex ((=) "--output") args - - let runningMode = - match Array.tryFindIndex ((=) "--folder") args, output with - | Some i, Some i_output when i < args.Length - 2 && i_output < args.Length - 2 -> - CmdLine ((Dir args.[i+1]), args.[i_output + 1]) - | _ -> - match Array.tryFindIndex ((=) "--file") args, output with - | 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) - - runningMode, Array.exists ((=) "--debug") args - -[] -[] -let main args = - - let e = Ellipse.ellipse2 -11.4 -7.8 -0.169811 -23.75 0.8 -3.885714 -19. 1.5 - - match parseArgs args with - | mode, debug -> - let config = Config(defaultParameters) - - match mode with - | CmdLine (input, output) -> - if debug - then - config.Debug <- DebugOn output - - Directory.CreateDirectory output |> ignore - - use logFile = new StreamWriter(new FileStream(Path.Combine(output, "log.txt"), FileMode.Append, FileAccess.Write)) - Utils.log <- (fun m -> logFile.WriteLine(m)) - Utils.log (sprintf "=== New run : %A %A ===" DateTime.Now (if debug then "[DEBUG]" else "[RELEASE]")) - - let files = match input with - | 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)) - - //try - let images = [ for file in files -> Path.GetFileNameWithoutExtension(FileInfo(file).Name), config.Copy(), new Image(file) ] - - - Utils.logTime "Whole analyze" (fun () -> - let results = ImageAnalysis.doMultipleAnalysis images None - - for id, cells in results do - let config = images |> List.pick (fun (id', config', _) -> if id' = id then Some config' else None) - let total, infected = Utils.countCells cells - fprintf resultFile "File: %s %d %d %.2f (diameter: %A)\n" id total infected (100. * (float infected) / (float total)) config.RBCRadius) - - //Utils.log (sprintf "== File: %A" file) - //with - //| :? IOException as ex -> Utils.log (sprintf "Unable to open the image '%A': %A" file ex) - 0 - - | Window fileToOpen -> - (*let display (window : Views.MainWindow) (img : IImage) = - let imgControl = window.Root.FindName("img") :?> Controls.Image - imgControl.Source <- BitmapSourceConvert.ToBitmapSource(img) - - let log (window : Views.MainWindow) (mess : string) = - let txtLog = window.Root.FindName("txtLog") :?> Controls.TextBlock - txtLog.Text <- txtLog.Text + mess + "\n"*) - - if debug then config.Debug <- DebugOn "." - GUI.Main.run config fileToOpen diff --git a/Parasitemia/Parasitemia/packages.config b/Parasitemia/Parasitemia/packages.config deleted file mode 100644 index 7f36e2b..0000000 --- a/Parasitemia/Parasitemia/packages.config +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Parasitemia/ParasitemiaCore/AssemblyInfo.fs b/Parasitemia/ParasitemiaCore/AssemblyInfo.fs new file mode 100644 index 0000000..0925faf --- /dev/null +++ b/Parasitemia/ParasitemiaCore/AssemblyInfo.fs @@ -0,0 +1,41 @@ +namespace ParasitemiaCore.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/ParasitemiaCore/Classifier.fs similarity index 99% rename from Parasitemia/Parasitemia/Classifier.fs rename to Parasitemia/ParasitemiaCore/Classifier.fs index 121a7f5..0bea084 100644 --- a/Parasitemia/Parasitemia/Classifier.fs +++ b/Parasitemia/ParasitemiaCore/Classifier.fs @@ -1,4 +1,4 @@ -module Classifier +module ParasitemiaCore.Classifier open System open System.Collections.Generic diff --git a/Parasitemia/Parasitemia/Config.fs b/Parasitemia/ParasitemiaCore/Config.fs similarity index 99% rename from Parasitemia/Parasitemia/Config.fs rename to Parasitemia/ParasitemiaCore/Config.fs index e7deb61..5fdcd0d 100644 --- a/Parasitemia/Parasitemia/Config.fs +++ b/Parasitemia/ParasitemiaCore/Config.fs @@ -1,4 +1,4 @@ -module Config +module ParasitemiaCore.Config open System diff --git a/Parasitemia/Parasitemia/Const.fs b/Parasitemia/ParasitemiaCore/Const.fs similarity index 50% rename from Parasitemia/Parasitemia/Const.fs rename to Parasitemia/ParasitemiaCore/Const.fs index 304d3a9..9da7009 100644 --- a/Parasitemia/Parasitemia/Const.fs +++ b/Parasitemia/ParasitemiaCore/Const.fs @@ -1,3 +1,3 @@ -module Const +module ParasitemiaCore.Const let PI = float32 System.Math.PI \ No newline at end of file diff --git a/Parasitemia/Parasitemia/EEOver.fs b/Parasitemia/ParasitemiaCore/EEOver.fs similarity index 99% rename from Parasitemia/Parasitemia/EEOver.fs rename to Parasitemia/ParasitemiaCore/EEOver.fs index 2bd13b2..a1358e9 100644 --- a/Parasitemia/Parasitemia/EEOver.fs +++ b/Parasitemia/ParasitemiaCore/EEOver.fs @@ -1,4 +1,4 @@ -module EEOver +module ParasitemiaCore.EEOver open System diff --git a/Parasitemia/Parasitemia/Ellipse.fs b/Parasitemia/ParasitemiaCore/Ellipse.fs similarity index 99% rename from Parasitemia/Parasitemia/Ellipse.fs rename to Parasitemia/ParasitemiaCore/Ellipse.fs index ac3c041..520d29d 100644 --- a/Parasitemia/Parasitemia/Ellipse.fs +++ b/Parasitemia/ParasitemiaCore/Ellipse.fs @@ -1,4 +1,4 @@ -module Ellipse +module ParasitemiaCore.Ellipse open System open System.Collections.Generic diff --git a/Parasitemia/Parasitemia/Granulometry.fs b/Parasitemia/ParasitemiaCore/Granulometry.fs similarity index 98% rename from Parasitemia/Parasitemia/Granulometry.fs rename to Parasitemia/ParasitemiaCore/Granulometry.fs index a1c2fd4..ed8bda6 100644 --- a/Parasitemia/Parasitemia/Granulometry.fs +++ b/Parasitemia/ParasitemiaCore/Granulometry.fs @@ -1,4 +1,4 @@ -module Granulometry +module ParasitemiaCore.Granulometry open System open System.Drawing diff --git a/Parasitemia/Parasitemia/Heap.fs b/Parasitemia/ParasitemiaCore/Heap.fs similarity index 98% rename from Parasitemia/Parasitemia/Heap.fs rename to Parasitemia/ParasitemiaCore/Heap.fs index 3d0879e..e4230eb 100644 --- a/Parasitemia/Parasitemia/Heap.fs +++ b/Parasitemia/ParasitemiaCore/Heap.fs @@ -1,4 +1,4 @@ -module Heap +module ParasitemiaCore.Heap open System.Collections.Generic diff --git a/Parasitemia/Parasitemia/ImgTools.fs b/Parasitemia/ParasitemiaCore/ImgTools.fs similarity index 99% rename from Parasitemia/Parasitemia/ImgTools.fs rename to Parasitemia/ParasitemiaCore/ImgTools.fs index 096fd94..dd06649 100644 --- a/Parasitemia/Parasitemia/ImgTools.fs +++ b/Parasitemia/ParasitemiaCore/ImgTools.fs @@ -1,4 +1,4 @@ -module ImgTools +module ParasitemiaCore.ImgTools open System open System.Drawing @@ -21,11 +21,9 @@ let normalizeAndConvert (img: Image) : Image = img.MinMax(min, max, minLocation, maxLocation) ((img.Convert() - (!min).[0]) / ((!max).[0] - (!min).[0]) * 255.0).Convert() - let saveImg (img: Image<'TColor, 'TDepth>) (filepath: string) = img.Save(filepath) - let saveMat (mat: Matrix<'TDepth>) (filepath: string) = use img = new Image(mat.Size) mat.CopyTo(img) diff --git a/Parasitemia/Parasitemia/KMeans.fs b/Parasitemia/ParasitemiaCore/KMeans.fs similarity index 98% rename from Parasitemia/Parasitemia/KMeans.fs rename to Parasitemia/ParasitemiaCore/KMeans.fs index 98352fe..86f1e3b 100644 --- a/Parasitemia/Parasitemia/KMeans.fs +++ b/Parasitemia/ParasitemiaCore/KMeans.fs @@ -1,4 +1,4 @@ -module KMeans +module ParasitemiaCore.KMeans open System.Collections.Generic open System.Drawing diff --git a/Parasitemia/Parasitemia/KMedians.fs b/Parasitemia/ParasitemiaCore/KMedians.fs similarity index 97% rename from Parasitemia/Parasitemia/KMedians.fs rename to Parasitemia/ParasitemiaCore/KMedians.fs index d005651..5819a66 100644 --- a/Parasitemia/Parasitemia/KMedians.fs +++ b/Parasitemia/ParasitemiaCore/KMedians.fs @@ -1,4 +1,4 @@ -module KMedians +module ParasitemiaCore.KMedians open System.Collections.Generic open System.Drawing diff --git a/Parasitemia/Parasitemia/KdTree.fs b/Parasitemia/ParasitemiaCore/KdTree.fs similarity index 99% rename from Parasitemia/Parasitemia/KdTree.fs rename to Parasitemia/ParasitemiaCore/KdTree.fs index 04a1152..94a3921 100644 --- a/Parasitemia/Parasitemia/KdTree.fs +++ b/Parasitemia/ParasitemiaCore/KdTree.fs @@ -1,4 +1,4 @@ -module KdTree +module ParasitemiaCore.KdTree open System diff --git a/Parasitemia/Parasitemia/MainAnalysis.fs b/Parasitemia/ParasitemiaCore/MainAnalysis.fs similarity index 97% rename from Parasitemia/Parasitemia/MainAnalysis.fs rename to Parasitemia/ParasitemiaCore/MainAnalysis.fs index 53429b2..846c067 100644 --- a/Parasitemia/Parasitemia/MainAnalysis.fs +++ b/Parasitemia/ParasitemiaCore/MainAnalysis.fs @@ -1,4 +1,4 @@ -module ImageAnalysis +module ParasitemiaCore.Analysis open System open System.Linq @@ -9,6 +9,8 @@ open FSharp.Collections.ParallelSeq open Emgu.CV open Emgu.CV.Structure +open Logger + open Utils open ImgTools open Config @@ -22,8 +24,8 @@ let doAnalysis (img: Image) (name: string) (config: Config) (reportPr | _ -> () let inline buildLogWithName (text: string) = sprintf "(%s) %s" name text - let logWithName = buildLogWithName >> log - let inline logTimeWithName (text: string) (f: unit -> 'a) : 'a = logTime (buildLogWithName text) f + let logWithName mess = Log.User(buildLogWithName mess) + let inline logTimeWithName (text: string) (f: unit -> 'a) : 'a = Log.LogWithTime((buildLogWithName text), Severity.USER, f) logWithName "Starting analysis ..." diff --git a/Parasitemia/Parasitemia/MatchingEllipses.fs b/Parasitemia/ParasitemiaCore/MatchingEllipses.fs similarity index 78% rename from Parasitemia/Parasitemia/MatchingEllipses.fs rename to Parasitemia/ParasitemiaCore/MatchingEllipses.fs index 3599bb5..b9b4b83 100644 --- a/Parasitemia/Parasitemia/MatchingEllipses.fs +++ b/Parasitemia/ParasitemiaCore/MatchingEllipses.fs @@ -1,4 +1,4 @@ -module MatchingEllipses +module ParasitemiaCore.MatchingEllipses open System open System.Linq @@ -29,7 +29,7 @@ type MatchingEllipses (radius: float32) = let ellipses = List() // All ellipses with a score below this are removed. - let matchingScoreThreshold = 1.f + let matchingScoreThreshold = 0.8f member this.Add (e: Ellipse) = ellipses.Add(EllipseScoreFlaggedKd(0.f, e)) @@ -60,7 +60,7 @@ type MatchingEllipses (radius: float32) = let areaOther = other.Ellipse.Area match EEOver.EEOverlapArea e.Ellipse other.Ellipse with | Some (overlapArea, _, _) -> - let matchingScore = (2.f * overlapArea / (areaE + areaOther)) ** 20.f + let matchingScore = (2.f * overlapArea / (areaE + areaOther)) ** 30.f if matchingScore <= 1.f // For approximation error. then other.AddMatchingScore(matchingScore) @@ -78,10 +78,19 @@ type MatchingEllipses (radius: float32) = KdTree.minY = e.Ellipse.Cy - e.Ellipse.A KdTree.maxY = e.Ellipse.Cy + e.Ellipse.A } for other in tree.Search window do - if not other.Removed && e.MatchingScore > other.MatchingScore && - distanceTwoPoints (PointD(e.Ellipse.Cx, e.Ellipse.Cy)) (PointD(other.Ellipse.Cx, other.Ellipse.Cy)) < 0.3f * e.Ellipse.B + if not other.Removed && e.MatchingScore > other.MatchingScore then - other.Removed <- true + // Case where ellipses are too close. + if distanceTwoPoints (PointD(e.Ellipse.Cx, e.Ellipse.Cy)) (PointD(other.Ellipse.Cx, other.Ellipse.Cy)) < 0.3f * e.Ellipse.B + then + other.Removed <- true + else + // Case where ellipses are overlapped. + match EEOver.EEOverlapArea e.Ellipse other.Ellipse with + | Some (overlapArea, _, _) when e.Ellipse.Area < 1.1f * overlapArea || other.Ellipse.Area < 1.1f * overlapArea -> + other.Removed <- true + | _ -> + () ellipses |> List.ofSeq diff --git a/Parasitemia/ParasitemiaCore/ParasitemiaCore.fsproj b/Parasitemia/ParasitemiaCore/ParasitemiaCore.fsproj new file mode 100644 index 0000000..1113511 --- /dev/null +++ b/Parasitemia/ParasitemiaCore/ParasitemiaCore.fsproj @@ -0,0 +1,117 @@ + + + + + Debug + AnyCPU + 2.0 + 0f8a85f4-9328-40c3-b8ff-44fb39ceb01f + Library + ParasitemiaCore + ParasitemiaCore + v4.5.2 + 4.4.0.0 + true + ParasitemiaCore + + + + true + full + false + false + bin\Debug\ + DEBUG;TRACE + 3 + bin\Debug\ParasitemiaCore.XML + + + pdbonly + true + true + bin\Release\ + TRACE + 3 + bin\Release\ParasitemiaCore.XML + + + 11 + + + + + $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets + + + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets + + + + + + + + + + + + + + + + + + + + + + + + + + + + ..\..\..\Emgu\emgucv-windows-universal 3.0.0.2157\bin\Emgu.CV.dll + + + ..\..\..\Emgu\emgucv-windows-universal 3.0.0.2157\bin\Emgu.Util.dll + + + ..\packages\FSharp.Collections.ParallelSeq.1.0.2\lib\net40\FSharp.Collections.ParallelSeq.dll + True + + + ..\packages\FSharp.Core.4.0.0.1\lib\net40\FSharp.Core.dll + True + + + ..\packages\MathNet.Numerics.3.10.0\lib\net40\MathNet.Numerics.dll + True + + + ..\packages\MathNet.Numerics.FSharp.3.10.0\lib\net40\MathNet.Numerics.FSharp.dll + True + + + + + + + + + + Logger + {a4f183ae-562a-4bad-88e6-658b4ce15dc3} + True + + + + \ No newline at end of file diff --git a/Parasitemia/Parasitemia/ParasitesMarker.fs b/Parasitemia/ParasitemiaCore/ParasitesMarker.fs similarity index 95% rename from Parasitemia/Parasitemia/ParasitesMarker.fs rename to Parasitemia/ParasitemiaCore/ParasitesMarker.fs index 671755c..22ea041 100644 --- a/Parasitemia/Parasitemia/ParasitesMarker.fs +++ b/Parasitemia/ParasitemiaCore/ParasitesMarker.fs @@ -1,10 +1,13 @@ -module ParasitesMarker +module ParasitemiaCore.ParasitesMarker open System.Drawing open System.Linq open Emgu.CV open Emgu.CV.Structure + +open Logger + open Utils type Result = { @@ -18,7 +21,7 @@ type Result = { // * 'Infection' corresponds to the parasite. It shouldn't contain thrombocytes. let findMa (green: Image) (filteredGreen: Image) (config: Config.Config) : Result * Image * Image = // We use the filtered image to find the dark stain. - let kmediansResults = logTime "Finding fg/bg (k-medians)" (fun () -> KMedians.kmedians filteredGreen) + let kmediansResults = Log.LogWithTime("Finding fg/bg (k-medians)", Severity.USER, (fun () -> KMedians.kmedians filteredGreen)) let { KMedians.fg = fg; KMedians.median_bg = median_bg; KMedians.median_fg = median_fg; KMedians.d_fg = d_fg } = kmediansResults let darkStain = d_fg.Cmp(median_bg * float config.Parameters.darkStainLevel, CvEnum.CmpType.GreaterThan) darkStain._And(filteredGreen.Cmp(median_fg, CvEnum.CmpType.LessThan)) diff --git a/Parasitemia/Parasitemia/Types.fs b/Parasitemia/ParasitemiaCore/Types.fs similarity index 98% rename from Parasitemia/Parasitemia/Types.fs rename to Parasitemia/ParasitemiaCore/Types.fs index 5db6bb6..ffe40b5 100644 --- a/Parasitemia/Parasitemia/Types.fs +++ b/Parasitemia/ParasitemiaCore/Types.fs @@ -1,4 +1,4 @@ -module Types +module ParasitemiaCore.Types open System open System.Drawing diff --git a/Parasitemia/Parasitemia/UnitsOfMeasure.fs b/Parasitemia/ParasitemiaCore/UnitsOfMeasure.fs similarity index 86% rename from Parasitemia/Parasitemia/UnitsOfMeasure.fs rename to Parasitemia/ParasitemiaCore/UnitsOfMeasure.fs index 6a3b745..81ce51f 100644 --- a/Parasitemia/Parasitemia/UnitsOfMeasure.fs +++ b/Parasitemia/ParasitemiaCore/UnitsOfMeasure.fs @@ -1,4 +1,4 @@ -module UnitsOfMeasure +module ParasitemiaCore.UnitsOfMeasure [] type px [] type μm diff --git a/Parasitemia/Parasitemia/Utils.fs b/Parasitemia/ParasitemiaCore/Utils.fs similarity index 80% rename from Parasitemia/Parasitemia/Utils.fs rename to Parasitemia/ParasitemiaCore/Utils.fs index 638f9f5..bf245aa 100644 --- a/Parasitemia/Parasitemia/Utils.fs +++ b/Parasitemia/ParasitemiaCore/Utils.fs @@ -1,6 +1,4 @@ -module Utils - -open System.Diagnostics +module ParasitemiaCore.Utils open Types @@ -9,17 +7,6 @@ let inline roundInt v = v |> round |> int let inline dprintfn fmt = Printf.ksprintf System.Diagnostics.Debug.WriteLine fmt -let mutable log : (string -> unit) = - fun m -> () - -let logTime (m: string) (f: unit -> 'a) : 'a = - let sw = Stopwatch() - sw.Start() - let res = f () - sw.Stop() - log <| sprintf "%s (time: %d ms)" m sw.ElapsedMilliseconds - res - let inline lineFromTwoPoints (p1: PointD) (p2: PointD) : 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) diff --git a/Parasitemia/ParasitemiaCore/packages.config b/Parasitemia/ParasitemiaCore/packages.config new file mode 100644 index 0000000..4d1f091 --- /dev/null +++ b/Parasitemia/ParasitemiaCore/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Parasitemia/Parasitemia/GUI/About.fs b/Parasitemia/ParasitemiaUI/About.fs similarity index 97% rename from Parasitemia/Parasitemia/GUI/About.fs rename to Parasitemia/ParasitemiaUI/About.fs index bc8f3e5..5f9ce81 100644 --- a/Parasitemia/Parasitemia/GUI/About.fs +++ b/Parasitemia/ParasitemiaUI/About.fs @@ -1,4 +1,4 @@ -module Parasitemia.GUI.About +module ParasitemiaUI.About open System open System.Windows diff --git a/Parasitemia/Parasitemia/GUI/Analysis.fs b/Parasitemia/ParasitemiaUI/Analysis.fs similarity index 89% rename from Parasitemia/Parasitemia/GUI/Analysis.fs rename to Parasitemia/ParasitemiaUI/Analysis.fs index 7d72e72..40fd303 100644 --- a/Parasitemia/Parasitemia/GUI/Analysis.fs +++ b/Parasitemia/ParasitemiaUI/Analysis.fs @@ -1,4 +1,4 @@ -module Parasitemia.GUI.Analysis +module ParasitemiaUI.Analysis open System open System.IO @@ -13,8 +13,9 @@ open Microsoft.Win32 // For the common dialogs. open Emgu.CV.WPF -open UnitsOfMeasure -open Config +open ParasitemiaCore.UnitsOfMeasure +open ParasitemiaCore.Config + open Types let showWindow (parent: Window) (state: State.State) : bool = @@ -33,10 +34,15 @@ let showWindow (parent: Window) (state: State.State) : bool = let textLog: TextBlock = ctrl "textLog" let scrollLog: ScrollViewer = ctrl "scrollLog" - Utils.log <- (fun mess -> window.Root.Dispatcher.Invoke(fun () -> - textLog.Inlines.Add(Documents.Run(mess)) - textLog.Inlines.Add(Documents.LineBreak()) - scrollLog.ScrollToBottom())) + let logListener = + { new Logger.IListener with + member this.NewEntry severity mess = + window.Root.Dispatcher.Invoke(fun () -> + textLog.Inlines.Add(Documents.Run(mess)) + textLog.Inlines.Add(Documents.LineBreak()) + scrollLog.ScrollToBottom()) } + + Logger.Log.AddListener(logListener) let minPPI = 1. let maxPPI = 10e6 @@ -104,7 +110,7 @@ let showWindow (parent: Window) (state: State.State) : bool = butClose.Content <- "Abort" async { let results = - ImageAnalysis.doMultipleAnalysis + ParasitemiaCore.Analysis.doMultipleAnalysis imagesToProcess (Some (fun progress -> window.Root.Dispatcher.Invoke(fun () -> progressBar.Value <- float progress))) @@ -120,7 +126,7 @@ let showWindow (parent: Window) (state: State.State) : bool = butClose.Content <- "Close" updateSourceImages ()) - Utils.log "All analyses terminated successfully" + Logger.Log.User("All analyses terminated successfully") atLeastOneAnalysisPerformed <- true analysisPerformed <- true) } |> Async.Start) @@ -129,8 +135,10 @@ let showWindow (parent: Window) (state: State.State) : bool = window.Root.ShowDialog() |> ignore + Logger.Log.RmListener(logListener) + lock monitor (fun () -> if not analysisPerformed then analysisCancelled <- true - atLeastOneAnalysisPerformed) \ No newline at end of file + atLeastOneAnalysisPerformed) diff --git a/Parasitemia/Parasitemia/App.config b/Parasitemia/ParasitemiaUI/App.config similarity index 100% rename from Parasitemia/Parasitemia/App.config rename to Parasitemia/ParasitemiaUI/App.config diff --git a/Parasitemia/Parasitemia/AssemblyInfo.fs b/Parasitemia/ParasitemiaUI/AssemblyInfo.fs similarity index 91% rename from Parasitemia/Parasitemia/AssemblyInfo.fs rename to Parasitemia/ParasitemiaUI/AssemblyInfo.fs index 1027e98..cdeb924 100644 --- a/Parasitemia/Parasitemia/AssemblyInfo.fs +++ b/Parasitemia/ParasitemiaUI/AssemblyInfo.fs @@ -1,4 +1,4 @@ -namespace Parasitemia.AssemblyInfo +namespace ParasitemiaUI.AssemblyInfo open System.Reflection open System.Runtime.CompilerServices @@ -7,11 +7,11 @@ 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. -[] +[] [] [] [] -[] +[] [] [] [] diff --git a/Parasitemia/Parasitemia/GUI/GUI.fs b/Parasitemia/ParasitemiaUI/GUI.fs similarity index 94% rename from Parasitemia/Parasitemia/GUI/GUI.fs rename to Parasitemia/ParasitemiaUI/GUI.fs index 950afc9..98b13ea 100644 --- a/Parasitemia/Parasitemia/GUI/GUI.fs +++ b/Parasitemia/ParasitemiaUI/GUI.fs @@ -1,4 +1,4 @@ -module Parasitemia.GUI.Main +module ParasitemiaUI.GUI open System open System.IO @@ -13,7 +13,10 @@ open Microsoft.Win32 // For the common dialogs. open Emgu.CV.WPF -open Config +open Logger + +open ParasitemiaCore.Config +open ParasitemiaCore.Utils open Types let run (defaultConfig: Config) (fileToOpen: string option) = @@ -59,10 +62,10 @@ let run (defaultConfig: Config) (fileToOpen: string option) = let extractRBCPreview (img: Emgu.CV.Image) (rbc: RBC) : Emgu.CV.Image = let rbcWidth = rbc.size.Width let rbcHeight = rbc.size.Height - let x = rbc.center.X - rbcWidth / 2. |> Utils.roundInt - let y = rbc.center.Y - rbcHeight / 2. |> Utils.roundInt - let w = Utils.roundInt rbcWidth - let h = Utils.roundInt rbcHeight + let x = rbc.center.X - rbcWidth / 2. |> roundInt + 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)))) @@ -235,16 +238,21 @@ let run (defaultConfig: Config) (fileToOpen: string option) = | _ -> () let saveCurrentDocument () = - if state.FilePath = "" - then - let dialog = SaveFileDialog(AddExtension = true, DefaultExt = PiaZ.extension, Filter = PiaZ.filter); - let res = dialog.ShowDialog() - if res.HasValue && res.Value + try + if state.FilePath = "" then - state.FilePath <- dialog.FileName + let dialog = SaveFileDialog(AddExtension = true, DefaultExt = PiaZ.extension, Filter = PiaZ.filter); + let res = dialog.ShowDialog() + if res.HasValue && res.Value + then + state.FilePath <- dialog.FileName + state.Save() + else state.Save() - else - state.Save() + with + | :? IOException as ex -> + Log.Error(ex.ToString()) + MessageBox.Show(sprintf "The document cannot be save in '%s'" state.FilePath, "Error saving the document", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore // Ask the use to save the current document if neccessary. let askSaveCurrent () = @@ -338,9 +346,16 @@ let run (defaultConfig: Config) (fileToOpen: string option) = let loadFile (filepath: string) = askSaveCurrent () - state.FilePath <- filepath - state.Load() - updateGUI () + let previousFilePath = state.FilePath + try + state.FilePath <- filepath + state.Load() + updateGUI () + with + | :? IOException as ex -> + Log.Error(ex.ToString()) + state.FilePath <- previousFilePath + MessageBox.Show(sprintf "The document cannot be loaded from '%s'" state.FilePath, "Error saving the document", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore txtPatient.LostFocus.AddHandler(fun obj args -> state.PatientID <- txtPatient.Text) diff --git a/Parasitemia/Parasitemia/Parasitemia.fsproj b/Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj similarity index 68% rename from Parasitemia/Parasitemia/Parasitemia.fsproj rename to Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj index e20ecb9..3b88791 100644 --- a/Parasitemia/Parasitemia/Parasitemia.fsproj +++ b/Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj @@ -7,12 +7,12 @@ 2.0 70838e65-f211-44fc-b28f-0ed1ca6e850f WinExe - Parasitemia - Parasitemia + ParasitemiaUI + ParasitemiaUI v4.5.2 true 4.4.0.0 - Parasitemia + ParasitemiaUI @@ -27,7 +27,7 @@ DEBUG;TRACE 3 x64 - bin\Debug\Parasitemia.XML + bin\Debug\ParasitemiaUI.XML false --folder "../../../Images/debug" --output "../../../Images/output" --debug @@ -39,7 +39,7 @@ DEBUG;TRACE 3 x64 - bin\Debug\Parasitemia.XML + bin\Debug\ParasitemiaUI.XML false --output "../../../Images/output" --debug bin\DebugGUI\ @@ -52,7 +52,7 @@ TRACE 3 AnyCPU - bin\Release\Parasitemia.XML + bin\Release\ParasitemiaUI.XML false --folder "../../../Images/release" --output "../../../Images/output" --debug @@ -74,69 +74,38 @@ - - - + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - + - - ..\packages\Castle.Core.3.3.3\lib\net45\Castle.Core.dll - True - ..\..\..\Emgu\emgucv-windows-universal 3.0.0.2157\bin\Emgu.CV.dll ..\..\..\Emgu\emgucv-windows-universal 3.0.0.2157\bin\Emgu.Util.dll - - ..\packages\FSharp.Collections.ParallelSeq.1.0.2\lib\net40\FSharp.Collections.ParallelSeq.dll - True - ..\packages\FSharp.Core.4.0.0.1\lib\net40\FSharp.Core.dll True @@ -153,18 +122,6 @@ ..\packages\FsXaml.Wpf.0.9.9\lib\net45\FsXaml.Wpf.TypeProvider.dll True - - ..\packages\log4net.2.0.5\lib\net45-full\log4net.dll - True - - - ..\packages\MathNet.Numerics.3.10.0\lib\net40\MathNet.Numerics.dll - True - - - ..\packages\MathNet.Numerics.FSharp.3.10.0\lib\net40\MathNet.Numerics.FSharp.dll - True - ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll @@ -191,6 +148,16 @@ + + Logger + {a4f183ae-562a-4bad-88e6-658b4ce15dc3} + True + + + ParasitemiaCore + {0f8a85f4-9328-40c3-b8ff-44fb39ceb01f} + True + WPF {314fd78e-870e-4794-bb16-ea4586f2abdb} diff --git a/Parasitemia/Parasitemia/GUI/PiaZ.fs b/Parasitemia/ParasitemiaUI/PiaZ.fs similarity index 80% rename from Parasitemia/Parasitemia/GUI/PiaZ.fs rename to Parasitemia/ParasitemiaUI/PiaZ.fs index d551b8d..d4ea46f 100644 --- a/Parasitemia/Parasitemia/GUI/PiaZ.fs +++ b/Parasitemia/ParasitemiaUI/PiaZ.fs @@ -1,5 +1,5 @@ -// ParasitemIA Zipped file format. -module Parasitemia.GUI.PiaZ +// ParasitemIA Zipped document format. +module ParasitemiaUI.PiaZ open System open System.Windows @@ -26,12 +26,12 @@ type JSONInformation = { type JSONSourceImage = { num: int RBCRadius: float32 // The RBC Radius found by granulometry. - parameters: Config.Parameters + parameters: ParasitemiaCore.Config.Parameters dateLastAnalysis: DateTime rbcs: RBC List } -type FileData = { +type DocumentData = { patientID: string images: SourceImage list } @@ -39,7 +39,13 @@ type FileData = { let mainEntryName = "info.json" let imageExtension = ".tiff" -let save (filePath: string) (data: FileData) = +/// +/// Save a document in a give file path. The file may already exist. +/// +/// +/// +/// If the file cannot be written +let save (filePath: string) (data: DocumentData) = use file = ZipFile.Open(filePath, ZipArchiveMode.Update) for e in List.ofSeq file.Entries do // 'ofSeq' to not iterate a collection currently modified. @@ -60,8 +66,12 @@ let save (filePath: string) (data: FileData) = use imgJSONFileWriter = new StreamWriter(imgJSONEntry.Open()) imgJSONFileWriter.Write(JsonConvert.SerializeObject({ num = srcImg.num; RBCRadius = srcImg.config.RBCRadius.Pixel; parameters = srcImg.config.Parameters; dateLastAnalysis = srcImg.dateLastAnalysis; rbcs = srcImg.rbcs })) - -let load (filePath: string) : FileData = +/// +/// Load document from a give file path. +/// +/// +/// If the file cannot be read +let load (filePath: string) : DocumentData = use file = ZipFile.Open(filePath, ZipArchiveMode.Read) let mainEntry = file.GetEntry(mainEntryName) @@ -78,7 +88,7 @@ let load (filePath: string) : FileData = let imgEntry = file.GetEntry(imgEntry.Name + ".json") use imgEntryFileReader = new StreamReader(imgEntry.Open()) let imgInfo = JsonConvert.DeserializeObject(imgEntryFileReader.ReadToEnd()) - let config = Config.Config(imgInfo.parameters) + let config = ParasitemiaCore.Config.Config(imgInfo.parameters) config.SetRBCRadius imgInfo.RBCRadius yield { num = imgNum config = config diff --git a/Parasitemia/ParasitemiaUI/Program.fs b/Parasitemia/ParasitemiaUI/Program.fs new file mode 100644 index 0000000..bbf20b3 --- /dev/null +++ b/Parasitemia/ParasitemiaUI/Program.fs @@ -0,0 +1,94 @@ +module ParasitemiaUI.Main + +open System +open System.IO +open System.Threading + +open Emgu.CV +open Emgu.CV.Structure + +open Logger + +open ParasitemiaCore.Utils +open ParasitemiaCore.Config + +type Input = + | File of string + | Dir of string + +type RunningMode = + | CmdLine of Input * string // A file or a directory to process and the output directory. + | Window of string option // An optional path to a file to open can be given in window mode. + +type Arguments = RunningMode * bool + +let parseArgs (args: string[]) : Arguments = + + let output = Array.tryFindIndex ((=) "--output") args + + let runningMode = + match Array.tryFindIndex ((=) "--folder") args, output with + | Some i, Some i_output when i < args.Length - 2 && i_output < args.Length - 2 -> + CmdLine ((Dir args.[i+1]), args.[i_output + 1]) + | _ -> + match Array.tryFindIndex ((=) "--file") args, output with + | 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) + + runningMode, Array.exists ((=) "--debug") args + +[] +[] +let main args = + try + Log.User("Starting of Parasitemia UI ...") + + let result = + match parseArgs args with + | mode, debug -> + let config = Config(defaultParameters) + + match mode with + | CmdLine (input, output) -> + if debug + then + config.Debug <- DebugOn output + + Directory.CreateDirectory output |> ignore + + use logFile = new StreamWriter(new FileStream(Path.Combine(output, "log.txt"), FileMode.Append, FileAccess.Write)) + Log.AddListener({ new IListener with member this.NewEntry mess severity = logFile.WriteLine(mess) }) + + Log.User (sprintf "=== New run : %A %A ===" DateTime.Now (if debug then "[DEBUG]" else "[RELEASE]")) + + let files = match input with + | 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("Whole analyze", Severity.USER, (fun () -> + let results = ParasitemiaCore.Analysis.doMultipleAnalysis images None + + for id, cells in results do + let config = images |> List.pick (fun (id', config', _) -> if id' = id then Some config' else None) + let total, infected = countCells cells + fprintf resultFile "File: %s %d %d %.2f (diameter: %A)\n" id total infected (100. * (float infected) / (float total)) config.RBCRadius)) + 0 + + | Window fileToOpen -> + if debug then config.Debug <- DebugOn "." + GUI.run config fileToOpen + + Log.User("Parasitemia UI closed") + result + + with + | _ as ex -> + Log.Fatal("Error: {0}", ex) + 1 \ No newline at end of file diff --git a/Parasitemia/Parasitemia/Resources/icon.ico b/Parasitemia/ParasitemiaUI/Resources/icon.ico similarity index 100% rename from Parasitemia/Parasitemia/Resources/icon.ico rename to Parasitemia/ParasitemiaUI/Resources/icon.ico diff --git a/Parasitemia/Parasitemia/GUI/State.fs b/Parasitemia/ParasitemiaUI/State.fs similarity index 84% rename from Parasitemia/Parasitemia/GUI/State.fs rename to Parasitemia/ParasitemiaUI/State.fs index 45272ca..5b89810 100644 --- a/Parasitemia/Parasitemia/GUI/State.fs +++ b/Parasitemia/ParasitemiaUI/State.fs @@ -1,4 +1,4 @@ -module Parasitemia.GUI.State +module ParasitemiaUI.State open System open System.Collections.Generic @@ -44,11 +44,19 @@ type State () = rbc.infected <- infected rbc.setManually <- not rbc.setManually + /// + /// Save the current state. 'FilePath' must have been defined. + /// + /// If the file cannot be saved member this.Save () = - let data = { PiaZ.FileData.patientID = this.PatientID; PiaZ.FileData.images = List.ofSeq sourceImages } + let data = { PiaZ.DocumentData.patientID = this.PatientID; PiaZ.DocumentData.images = List.ofSeq sourceImages } PiaZ.save this.FilePath data alteredSinceLastSave <- false + /// + /// Load the current state. 'FilePath' must have been defined. + /// + /// If the file cannot be laoded member this.Load () = let data = PiaZ.load this.FilePath this.PatientID <- data.patientID @@ -58,7 +66,7 @@ type State () = then this.CurrentImage <- Some sourceImages.[0] alteredSinceLastSave <- false - member this.AddSourceImage (filePath: string) (defaultConfig: Config.Config) : SourceImage = + member this.AddSourceImage (filePath: string) (defaultConfig: ParasitemiaCore.Config.Config) : SourceImage = let srcImg = { num = sourceImages.Count + 1; config = defaultConfig.Copy(); dateLastAnalysis = DateTime(0L); rbcs = []; img = new Image(filePath) } sourceImages.Add(srcImg) if sourceImages.Count = 1 @@ -81,7 +89,7 @@ type State () = // Re-numbered the images. sourceImages |> Seq.iteri (fun i srcImg -> srcImg.num <- i + 1) - member this.SetResult (imgNum: int) (cells: Cell list) = + member this.SetResult (imgNum: int) (cells: ParasitemiaCore.Types.Cell list) = let sourceImage = sourceImages.Find(fun srcImg -> srcImg.num = imgNum) let w = sourceImage.img.Width @@ -97,14 +105,14 @@ type State () = rbc.center.Y > center.Y - tolerance && rbc.center.Y < center.Y + tolerance) sourceImage.rbcs <- cells - |> List.filter (fun cell -> match cell.cellClass with HealthyRBC | InfectedRBC -> true | _ -> false ) + |> List.filter (fun cell -> match cell.cellClass with ParasitemiaCore.Types.HealthyRBC | ParasitemiaCore.Types.InfectedRBC -> true | _ -> false ) |> List.sortByDescending (fun cell -> cell.infectedArea, (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 infected, setManually = match getPreviousRBC center with | Some rbc -> rbc.infected, true - | _ -> cell.cellClass = InfectedRBC, false + | _ -> cell.cellClass = ParasitemiaCore.Types.InfectedRBC, false { num = i + 1 infected = infected diff --git a/Parasitemia/Parasitemia/GUI/Types.fs b/Parasitemia/ParasitemiaUI/Types.fs similarity index 81% rename from Parasitemia/Parasitemia/GUI/Types.fs rename to Parasitemia/ParasitemiaUI/Types.fs index c90fc0c..7d80294 100644 --- a/Parasitemia/Parasitemia/GUI/Types.fs +++ b/Parasitemia/ParasitemiaUI/Types.fs @@ -1,4 +1,4 @@ -module Parasitemia.GUI.Types +module ParasitemiaUI.Types open System open System.Windows @@ -18,7 +18,7 @@ type RBC = { type SourceImage = { mutable num: int - mutable config: Config.Config + mutable config: ParasitemiaCore.Config.Config mutable dateLastAnalysis: DateTime // UTC. img: Image mutable rbcs: RBC list } \ No newline at end of file diff --git a/Parasitemia/Parasitemia/GUI/AboutWindow.xaml b/Parasitemia/ParasitemiaUI/XAML/AboutWindow.xaml similarity index 100% rename from Parasitemia/Parasitemia/GUI/AboutWindow.xaml rename to Parasitemia/ParasitemiaUI/XAML/AboutWindow.xaml diff --git a/Parasitemia/ParasitemiaUI/XAML/AboutWindow.xaml.fs b/Parasitemia/ParasitemiaUI/XAML/AboutWindow.xaml.fs new file mode 100644 index 0000000..ed9130d --- /dev/null +++ b/Parasitemia/ParasitemiaUI/XAML/AboutWindow.xaml.fs @@ -0,0 +1,6 @@ +namespace ParasitemiaUI.Views + +open FsXaml + +type AboutWindow = XAML<"XAML/AboutWindow.xaml"> + diff --git a/Parasitemia/Parasitemia/GUI/AnalysisWindow.xaml b/Parasitemia/ParasitemiaUI/XAML/AnalysisWindow.xaml similarity index 100% rename from Parasitemia/Parasitemia/GUI/AnalysisWindow.xaml rename to Parasitemia/ParasitemiaUI/XAML/AnalysisWindow.xaml diff --git a/Parasitemia/ParasitemiaUI/XAML/AnalysisWindow.xaml.fs b/Parasitemia/ParasitemiaUI/XAML/AnalysisWindow.xaml.fs new file mode 100644 index 0000000..07d9573 --- /dev/null +++ b/Parasitemia/ParasitemiaUI/XAML/AnalysisWindow.xaml.fs @@ -0,0 +1,6 @@ +namespace ParasitemiaUI.Views + +open FsXaml + +type AnalysisWindow = XAML<"XAML/AnalysisWindow.xaml"> + diff --git a/Parasitemia/Parasitemia/GUI/ImageSourcePreview.xaml b/Parasitemia/ParasitemiaUI/XAML/ImageSourcePreview.xaml similarity index 100% rename from Parasitemia/Parasitemia/GUI/ImageSourcePreview.xaml rename to Parasitemia/ParasitemiaUI/XAML/ImageSourcePreview.xaml diff --git a/Parasitemia/Parasitemia/GUI/ImageSourcePreview.xaml.fs b/Parasitemia/ParasitemiaUI/XAML/ImageSourcePreview.xaml.fs similarity index 74% rename from Parasitemia/Parasitemia/GUI/ImageSourcePreview.xaml.fs rename to Parasitemia/ParasitemiaUI/XAML/ImageSourcePreview.xaml.fs index 425d436..6a3a1d5 100644 --- a/Parasitemia/Parasitemia/GUI/ImageSourcePreview.xaml.fs +++ b/Parasitemia/ParasitemiaUI/XAML/ImageSourcePreview.xaml.fs @@ -1,4 +1,4 @@ -namespace Parasitemia.GUI.Views +namespace ParasitemiaUI.Views open System open System.Windows @@ -8,7 +8,7 @@ open System.Windows.Input open FSharp.ViewModule open FsXaml -type ImageSourcePreview = XAML<"GUI/ImageSourcePreview.xaml", true> +type ImageSourcePreview = XAML<"XAML/ImageSourcePreview.xaml", true> (* type ImageSourcePreviewController() = inherit UserControlViewController() *) diff --git a/Parasitemia/Parasitemia/GUI/ImageSourceSelection.xaml b/Parasitemia/ParasitemiaUI/XAML/ImageSourceSelection.xaml similarity index 100% rename from Parasitemia/Parasitemia/GUI/ImageSourceSelection.xaml rename to Parasitemia/ParasitemiaUI/XAML/ImageSourceSelection.xaml diff --git a/Parasitemia/Parasitemia/GUI/ImageSourceSelection.xaml.fs b/Parasitemia/ParasitemiaUI/XAML/ImageSourceSelection.xaml.fs similarity index 73% rename from Parasitemia/Parasitemia/GUI/ImageSourceSelection.xaml.fs rename to Parasitemia/ParasitemiaUI/XAML/ImageSourceSelection.xaml.fs index 1ca5c66..4c91ed2 100644 --- a/Parasitemia/Parasitemia/GUI/ImageSourceSelection.xaml.fs +++ b/Parasitemia/ParasitemiaUI/XAML/ImageSourceSelection.xaml.fs @@ -1,4 +1,4 @@ -namespace Parasitemia.GUI.Views +namespace ParasitemiaUI.Views open System open System.Windows @@ -8,7 +8,7 @@ open System.Windows.Input open FSharp.ViewModule open FsXaml -type ImageSourceSelection = XAML<"GUI/ImageSourceSelection.xaml", true> +type ImageSourceSelection = XAML<"XAML/ImageSourceSelection.xaml", true> (* type ImageSourcePreviewController() = inherit UserControlViewController() *) diff --git a/Parasitemia/Parasitemia/GUI/MainWindow.xaml b/Parasitemia/ParasitemiaUI/XAML/MainWindow.xaml similarity index 100% rename from Parasitemia/Parasitemia/GUI/MainWindow.xaml rename to Parasitemia/ParasitemiaUI/XAML/MainWindow.xaml 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/Parasitemia/GUI/NumericUpDown.xaml b/Parasitemia/ParasitemiaUI/XAML/NumericUpDown.xaml similarity index 100% rename from Parasitemia/Parasitemia/GUI/NumericUpDown.xaml rename to Parasitemia/ParasitemiaUI/XAML/NumericUpDown.xaml diff --git a/Parasitemia/Parasitemia/GUI/NumericUpDown.xaml.fs b/Parasitemia/ParasitemiaUI/XAML/NumericUpDown.xaml.fs similarity index 70% rename from Parasitemia/Parasitemia/GUI/NumericUpDown.xaml.fs rename to Parasitemia/ParasitemiaUI/XAML/NumericUpDown.xaml.fs index 574ffd6..9f1394d 100644 --- a/Parasitemia/Parasitemia/GUI/NumericUpDown.xaml.fs +++ b/Parasitemia/ParasitemiaUI/XAML/NumericUpDown.xaml.fs @@ -1,4 +1,4 @@ -namespace Parasitemia.GUI.Views +namespace ParasitemiaUI.Views open System open System.Windows @@ -7,7 +7,7 @@ open System.Windows.Input open FsXaml -type NumericUpDown = XAML<"GUI/NumericUpDown.xaml", true> +type NumericUpDown = XAML<"XAML/NumericUpDown.xaml", true> type NumericUpDownEvents = Up | Down diff --git a/Parasitemia/Parasitemia/GUI/RBCFrame.xaml b/Parasitemia/ParasitemiaUI/XAML/RBCFrame.xaml similarity index 100% rename from Parasitemia/Parasitemia/GUI/RBCFrame.xaml rename to Parasitemia/ParasitemiaUI/XAML/RBCFrame.xaml diff --git a/Parasitemia/Parasitemia/GUI/RBCFrame.xaml.fs b/Parasitemia/ParasitemiaUI/XAML/RBCFrame.xaml.fs similarity index 59% rename from Parasitemia/Parasitemia/GUI/RBCFrame.xaml.fs rename to Parasitemia/ParasitemiaUI/XAML/RBCFrame.xaml.fs index 1ef4d31..672554b 100644 --- a/Parasitemia/Parasitemia/GUI/RBCFrame.xaml.fs +++ b/Parasitemia/ParasitemiaUI/XAML/RBCFrame.xaml.fs @@ -1,4 +1,4 @@ -namespace Parasitemia.GUI.Views +namespace ParasitemiaUI.Views open System open System.Windows @@ -8,4 +8,4 @@ open System.Windows.Input open FSharp.ViewModule open FsXaml -type RBCFrame = XAML<"GUI/RBCFrame.xaml", true> +type RBCFrame = XAML<"XAML/RBCFrame.xaml", true> diff --git a/Parasitemia/ParasitemiaUI/packages.config b/Parasitemia/ParasitemiaUI/packages.config new file mode 100644 index 0000000..1c4cea4 --- /dev/null +++ b/Parasitemia/ParasitemiaUI/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Parasitemia/Parasitemia/resources.rc b/Parasitemia/ParasitemiaUI/resources.rc similarity index 100% rename from Parasitemia/Parasitemia/resources.rc rename to Parasitemia/ParasitemiaUI/resources.rc diff --git a/Parasitemia/Parasitemia/resources.res b/Parasitemia/ParasitemiaUI/resources.res similarity index 100% rename from Parasitemia/Parasitemia/resources.res rename to Parasitemia/ParasitemiaUI/resources.res -- 2.45.2