let setLogDirectory (dir: string) =
lock monitor (fun () ->
logDir <- dir
- absoluteDir <- Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), logDir)
+ absoluteDir <- Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), logDir)
if stream <> null
then
Drawing.drawCells imgCells' true cells
IO.saveImg imgCells' (buildFileName " - cells - full.png")
- let filteredRBCMaxima = gaussianFilter img_RBC config.LPFStandardDeviationRBC
+ (* let filteredRBCMaxima = gaussianFilter img_RBC config.LPFStandardDeviationRBC
for m in findMaxima filteredRBCMaxima do
Drawing.drawPoints filteredRBCMaxima m 255.f
- IO.saveImg filteredRBCMaxima (buildFileName " - filtered - maxima.png")
+ IO.saveImg filteredRBCMaxima (buildFileName " - filtered - maxima.png") *)
IO.saveImg imgWhitoutParasite (buildFileName " - filtered closed stain.png")
IO.saveImg imgWithoutNucleus (buildFileName " - filtered closed infection.png")
IO.saveImg img_RBC_filtered (buildFileName " - source - RBC.png")
IO.saveImg img_parasites_filtered (buildFileName " - source - parasites.png")
- IO.saveImg (normalize img_float.[2] 255.) (buildFileName " - source - red.png")
- IO.saveImg (normalize img_float.[1] 255.) (buildFileName " - source - green.png")
- IO.saveImg (normalize img_float.[0] 255.) (buildFileName " - source - blue.png")
+ IO.saveImg img_float.[2] (buildFileName " - source - red.png")
+ IO.saveImg img_float.[1] (buildFileName " - source - green.png")
+ IO.saveImg img_float.[0] (buildFileName " - source - blue.png")
| _ -> ()
return cells }
darkStainPixels <- darkStainPixels + 1
if float darkStainPixels > config.Parameters.maxDarkStainRatio * (float nbElement) then Some e else None)
+
+ // We do not change the state during the process to avoid to have peculiar neighbors which change the behavior of 'pixelOwnedByE'.
|> List.iter (fun e -> e.State <- CellState.Peculiar)
// 5) Define pixels associated to each ellipse and create the cells.
imageSourceSelection.lblDateLastAnalysis.Content <- if srcImg.dateLastAnalysis.Ticks = 0L then "<Never>" else srcImg.dateLastAnalysis.ToString()
imageSourceSelection.txtResolution.Text <- if srcImg.dateLastAnalysis.Ticks = 0L then "" else srcImg.config.Parameters.resolution.ToString()
- imageSourceSelection.menuZoom50X.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- "230000")
- imageSourceSelection.menuZoom100X.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- "460000")
+
+ for ppi in Utils.predefinedPPI do
+ let menu = MenuItem()
+ menu.Header <- ppi.ToString()
+ menu.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- ppi.ppi.ToString())
+ imageSourceSelection.predefinedValuesMenu.Items.Add(menu) |> ignore
imageSourceSelection.butPPICalculator.Click.AddHandler(fun obj args ->
match PPICalculator.showWindow win.Root with
open System.Windows.Controls
open System.Diagnostics
-open ParasitemiaCore.UnitsOfMeasure
open ParasitemiaCore.Types
+open ParasitemiaCore.UnitsOfMeasure
-type SensorSize = {
- w: float<mm>
- h: float<mm>
- txt: string } with
- override this.ToString () =
- sprintf "%g mm × %g mm%s" this.w this.h (if this.txt = "" then "" else " (" + this.txt + ")")
+open Types
let showWindow (parent: Window) : int option =
let win = Views.PPICalculatorWindow()
win.Root.Left <- parent.Left + parent.ActualWidth / 2. - win.Root.Width / 2.
win.Root.Top <- parent.Top + parent.ActualHeight / 2. - win.Root.Height / 2.
- let sensorSizes = [
- { w = 5.76<mm>; h = 4.29<mm>; txt = "1/2.5″" }
- { w = 7.6<mm>; h = 5.7<mm>; txt = "1/1.7″" }
- { w = 8.6<mm>; h = 6.6<mm>; txt = "2/3″" }
- { w = 13.2<mm>; h = 8.8<mm>; txt = "1″" }
- { w = 17.3<mm>; h = 13.<mm>; txt = "" }
- { w = 20.7<mm>; h = 13.8<mm>; txt = "" }
- { w = 22.2<mm>; h = 14.8<mm>; txt = "" }
- { w = 23.6<mm>; h = 15.7<mm>; txt = "" }
- { w = 28.7<mm>; h = 19.<mm>; txt = "" }
- { w = 28.7<mm>; h = 19.<mm>; txt = "" } ]
-
- for size in sensorSizes do
+ for size in Utils.sensorSizes do
win.cmbSensorSize.Items.Add(size) |> ignore
win.cmbSensorSize.SelectedIndex <- 0
Log.Error(ex.ToString())
MessageBox.Show(sprintf "The results cannot be exported in \"%s\"" state.FilePath, "Error exporting the files", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore
- win.txtPatient.TextChanged.AddHandler(fun obj args -> state.PatientID <- win.txtPatient.Text)
-
- win.menuExit.Click.AddHandler(fun obj args -> win.Root.Close())
- win.menuSave.Click.AddHandler(fun obj args -> saveCurrentDocument ())
- win.menuSaveAs.Click.AddHandler(fun obj args -> saveCurrentDocumentAsNewFile ())
- win.menuOpen.Click.AddHandler(fun obj args -> askLoadFile ())
- win.menuNew.Click.AddHandler(fun obj args -> newFile ())
- win.menuExportResults.Click.AddHandler(fun obj args -> exportResults ())
-
- win.menuAddSourceImage.Click.AddHandler(fun obj args ->
+ let importImage () =
let dialog = OpenFileDialog(Filter = "Image Files|*.png;*.jpg;*.tif;*.tiff", Multiselect = true)
let res = dialog.ShowDialog()
if res.HasValue && res.Value
if noSourceImage
then
- updateCurrentImage ())
+ updateCurrentImage ()
+
+ win.txtPatient.TextChanged.AddHandler(fun obj args -> state.PatientID <- win.txtPatient.Text)
+
+ win.menuExit.Click.AddHandler(fun obj args -> win.Root.Close())
+ win.menuSave.Click.AddHandler(fun obj args -> saveCurrentDocument ())
+ win.menuSaveAs.Click.AddHandler(fun obj args -> saveCurrentDocumentAsNewFile ())
+ win.menuOpen.Click.AddHandler(fun obj args -> askLoadFile ())
+ win.menuNew.Click.AddHandler(fun obj args -> newFile ())
+ win.menuExportResults.Click.AddHandler(fun obj args -> exportResults ())
+
+ win.menuAddSourceImage.Click.AddHandler(fun obj args -> importImage ())
win.menuAnalysis.SubmenuOpened.AddHandler(fun obj args -> win.menuStartAnalysis.IsEnabled <- state.SourceImages.Count() > 0)
FSharp.ViewModule.FunCommand((fun obj -> exportResults ()), (fun obj -> true)),
Input.KeyGesture(Input.Key.E, Input.ModifierKeys.Control))) |> ignore
+ // Import an image.
+ win.Root.InputBindings.Add(
+ Input.KeyBinding(
+ FSharp.ViewModule.FunCommand((fun obj -> importImage ()), (fun obj -> true)),
+ Input.KeyGesture(Input.Key.A, Input.ModifierKeys.Control))) |> ignore
+
// Viewport preview.
win.scrollViewCurrentImage.ScrollChanged.AddHandler(fun obj args -> updateViewportPreview ())
open Newtonsoft.Json
+open ParasitemiaCore.UnitsOfMeasure
+
let healthyRBColor = Color.FromRgb(255uy, 255uy, 0uy) // Yellow-green.
let infectedRBColor = Color.FromRgb(255uy, 0uy, 40uy) // Red with a bit of blue.
member this.InfectedRBCColor: SolidColorBrush =
let mutable color = infectedRBColor * this.infectedRBCBrightness
color.A <- 255uy;
- SolidColorBrush(color)
\ No newline at end of file
+ SolidColorBrush(color)
+
+type PredefinedPPI = {
+ ppi: int<ppi>
+ label: string } with
+ override this.ToString() =
+ sprintf "%s: %d" this.label this.ppi
+
+type SensorSize = {
+ w: float<mm>
+ h: float<mm>
+ label: string } with
+ override this.ToString () =
+ sprintf "%g mm × %g mm%s" this.w this.h (if this.label = "" then "" else " (" + this.label + ")")
+
+let defaultPredefinedPPI = [
+ { ppi = 230000<ppi>; label = "50×" }
+ { ppi = 460000<ppi>; label = "100×" } ]
+
+let defaultSensorSizes = [
+ { w = 3.2<mm>; h = 2.4<mm>; label = "1/4″" }
+ { w = 4.8<mm>; h = 3.6<mm>; label = "1/3″" }
+ { w = 5.76<mm>; h = 4.29<mm>; label = "1/2.5″" }
+ { w = 6.4<mm>; h = 4.8<mm>; label = "1/2″" }
+ { w = 7.18<mm>; h = 5.32<mm>; label = "1/1.8″" }
+ { w = 7.6<mm>; h = 5.7<mm>; label = "1/1.7″" }
+ { w = 8.8<mm>; h = 6.6<mm>; label = "2/3″" }
+ { w = 13.2<mm>; h = 8.8<mm>; label = "1″" } ]
+
+
module ParasitemiaUI.Utils
+open System.IO
+
+open Newtonsoft.Json
+open Newtonsoft.Json.Converters
+
+open Types
+
let listAsStr (s: 'a seq) =
s |> Seq.fold (fun acc obj -> acc + (if acc = "" then "" else ", ") + obj.ToString()) ""
""
else
let percent = 100. * (float nb) / (float nbTotal)
- sprintf "%.1f %% (%d / %d)" percent nb nbTotal
\ No newline at end of file
+ sprintf "%.1f %% (%d / %d)" percent nb nbTotal
+
+let roamingDir =
+ Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), "Parasitemia")
+
+let predefinedPPIFilename = "predefined-ppi.json"
+let predefinedPPIFilepath = Path.Combine(roamingDir, predefinedPPIFilename)
+
+let sensorSizesFilename = "sensor-sizes.json"
+let sensorSizesFilepath = Path.Combine(roamingDir, sensorSizesFilename)
+
+let private savePredefinedPPIToFile (predefinedPPI: PredefinedPPI list) =
+ try
+ use file = new StreamWriter(predefinedPPIFilepath)
+ file.Write(JsonConvert.SerializeObject(predefinedPPI, JsonSerializerSettings(Formatting = Formatting.Indented)))
+ with
+ ex ->
+ Logger.Log.Error("Unable to save predefined PPI to file \"{0}\": {1}", predefinedPPIFilepath, ex)
+
+let private saveSensorSizesToFile (sensorSizes: SensorSize list) =
+ try
+ use file = new StreamWriter(sensorSizesFilepath)
+ file.Write(JsonConvert.SerializeObject(sensorSizes, JsonSerializerSettings(Formatting = Formatting.Indented)))
+ with
+ ex ->
+ Logger.Log.Error("Unable to save sensor sizes to file \"{0}\": {1}", sensorSizesFilepath, ex)
+
+let predefinedPPI : PredefinedPPI list =
+ try
+ use file = new StreamReader(predefinedPPIFilepath)
+ JsonConvert.DeserializeObject<PredefinedPPI list>(file.ReadToEnd())
+ with
+ | ex ->
+ savePredefinedPPIToFile defaultPredefinedPPI
+ defaultPredefinedPPI
+
+let sensorSizes : SensorSize list =
+ try
+ use file = new StreamReader(sensorSizesFilepath)
+ JsonConvert.DeserializeObject<SensorSize list>(file.ReadToEnd())
+ with
+ | ex ->
+ saveSensorSizesToFile defaultSensorSizes
+ defaultSensorSizes
<TextBox x:Name="txtResolution" Margin="3" Text="" Grid.Column="0" />
<Button x:Name="butDefaultResolutions" Content="Predefined values" Grid.Column="1" Margin="3">
<Button.ContextMenu>
- <ContextMenu>
- <MenuItem x:Name="menuZoom50X" Header="50X: 230'000 PPI" />
- <MenuItem x:Name="menuZoom100X" Header="100X: 460'000 PPI" />
+ <ContextMenu x:Name="predefinedValuesMenu">
+ <!-- MenuItem x:Name="menuZoom50X" Header="50X: 230'000 PPI" />
+ <MenuItem x:Name="menuZoom100X" Header="100X: 460'000 PPI" / -->
</ContextMenu>
</Button.ContextMenu>
<Button.Style>
<MenuItem x:Name="menuSave" Header="_Save" InputGestureText="Ctrl+S" />
<MenuItem x:Name="menuSaveAs" Header="Save _As..." InputGestureText="Ctrl+Shift+S" />
<Separator />
+ <MenuItem x:Name="menuAddSourceImage" Header="_Import Images..." InputGestureText="Ctrl+A" />
+ <Separator />
<MenuItem x:Name="menuExportResults" Header="E_xport Results As Text..." InputGestureText="Ctrl+E" />
<Separator />
<MenuItem x:Name="menuExit" Header="_Exit" />
</MenuItem>
- <MenuItem Header="_Images">
- <MenuItem x:Name="menuAddSourceImage" Header="_Add a Source Image" />
- </MenuItem>
<MenuItem x:Name="menuAnalysis" Header="_Analysis">
<MenuItem x:Name="menuStartAnalysis" Header="_Show Analysis Window" />
</MenuItem>