Cleaning and some little tweaks.
authorGreg Burri <greg.burri@gmail.com>
Fri, 29 Jan 2016 21:58:21 +0000 (22:58 +0100)
committerGreg Burri <greg.burri@gmail.com>
Fri, 29 Jan 2016 21:58:21 +0000 (22:58 +0100)
14 files changed:
Parasitemia/ParasitemiaCore/Analysis.fs
Parasitemia/ParasitemiaCore/Config.fs
Parasitemia/ParasitemiaCore/Ellipse.fs
Parasitemia/ParasitemiaCore/Granulometry.fs
Parasitemia/ParasitemiaCore/ImgTools/Edges.fs
Parasitemia/ParasitemiaCore/ParasitemiaCore.fsproj
Parasitemia/ParasitemiaUI/Analysis.fs
Parasitemia/ParasitemiaUI/DPICalculator.fs
Parasitemia/ParasitemiaUI/ParasitemiaUI.fsproj
Parasitemia/ParasitemiaUI/XAML/DPICalculatorWindow.xaml [deleted file]
Parasitemia/ParasitemiaUI/XAML/DPICalculatorWindow.xaml.fs [deleted file]
Parasitemia/ParasitemiaUI/XAML/ImageSourceSelection.xaml
Parasitemia/ParasitemiaUI/XAML/PPICalculatorWindow.xaml [new file with mode: 0644]
Parasitemia/ParasitemiaUI/XAML/PPICalculatorWindow.xaml.fs [new file with mode: 0644]

index bad0daf..7f58463 100644 (file)
@@ -67,7 +67,9 @@ let doAnalysis (img: Image<Bgr, byte>) (name: string) (config: Config) (reportPr
         let range =
             let delta = config.Parameters.granulometryRange * config.RBCRadiusByResolution.Pixel
             int <| config.RBCRadiusByResolution.Pixel - delta, int <| config.RBCRadiusByResolution.Pixel + delta
+
         let! radius = logTimeWithName "Granulometry (area)" (fun() -> reportWithVal 10 (Granulometry.findRadiusByAreaClosing img_RBC_filtered range |> float32))
+        //let! radius = logTimeWithName "Granulometry (morpho)" (fun() -> reportWithVal 10 (Granulometry.findRadiusByClosing img_RBC_filtered range 1. true |> float32))
         config.SetRBCRadius <| radius
 
         logWithName (sprintf "Found erythrocyte diameter: %A" config.RBCRadius)
@@ -119,8 +121,8 @@ let doAnalysis (img: Image<Bgr, byte>) (name: string) (config: Config) (reportPr
                 IO.saveImg parasites.parasite (buildFileName " - parasites - stain.png")
                 IO.saveImg parasites.nucleus (buildFileName " - parasites - infection.png")
 
-                let imgAllEllipses = img.Copy()
-                Drawing.drawEllipses imgAllEllipses matchingEllipses.Ellipses (Bgr(255.0, 255.0, 255.0)) 0.04
+                let imgAllEllipses = img_RBC_filtered.Copy()
+                Drawing.drawEllipses imgAllEllipses matchingEllipses.Ellipses (Gray(200.0)) 0.04
                 IO.saveImg imgAllEllipses (buildFileName " - ellipses - all.png")
 
                 let imgEllipses = img_RBC_filtered.Convert<Bgr, byte>()
index 385b7d4..5e3280c 100644 (file)
@@ -43,17 +43,17 @@ type Parameters = {
 
 let defaultParameters = {
     rbcDiameter = 7.5<μm>
-    resolution = 220.e3<ppi> // 220.e3<ppi> Correspond to 50X.
+    resolution = 230.e3<ppi> // 230.e3<ppi> Correspond to 50X.
 
     ratioAreaPaleCenter = 2.f / 5.f // The ratio between an RBC area and the area of the its pale center.
 
     granulometryRange = 0.5f
 
-    minRbcRadius = -0.3f
-    maxRbcRadius = 0.3f
+    minRbcRadius = -0.23f
+    maxRbcRadius = 0.23f
 
     LPFStandardDeviationParasite = 0.15<μm>
-    LPFStandardDeviationRBC = 0.2<μm>
+    LPFStandardDeviationRBC = 0.22<μm>
 
     factorNbPick = 1.0
 
index 4e48230..db23ece 100644 (file)
@@ -14,6 +14,9 @@ open Config
 open MatchingEllipses
 open Const
 
+// This is a ratio of the biggest radius.
+let minimumDistanceBetweenDrawnPoints = 0.6
+
 /// <summary>
 /// Try to build an ellipse from three points and two tangents passing by the first and the second point.
 /// 'Ellipse.A' is always equal or greater than Ellipse.B.
@@ -76,28 +79,17 @@ let ellipse (p1x: float) (p1y: float) (m1: float) (p2x: float) (p2y: float) (m2:
 
 
 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
+        if v1x > 0.f then -1.f else 1.f
     elif p1y < py
     then
-        if v1x < 0.f
-        then
-            rotation <- -1.f
+        if v1x < 0.f then -1.f else 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
+        if v1y < 0.f then -1.f else 1.f
+    else // p1x < px
+        if v1y > 0.f then -1.f else 1.f
 
 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
@@ -125,9 +117,9 @@ let private areVectorsValid (p1x: float32) (p1y: float32) (p2x: float32) (p2y: f
 
         if diff > PI || (diff < 0.f && diff > -PI)
         then
-            None
+            Some (m1, m2)
         else
-        Some (m1, m2)
+            None
 
 let find (edges: Matrix<byte>)
          (xGradient: Matrix<float32>)
@@ -145,7 +137,7 @@ let find (edges: Matrix<byte>)
 
     let radiusTolerance = (r2 - r1) * 0.2f
 
-    let squaredMinimumDistance = (float r2 / 1.5) ** 2.
+    let squaredMinimumDistance = (float config.RBCRadius.Pixel * minimumDistanceBetweenDrawnPoints) ** 2.
     let inline squaredDistance x1 y1 x2 y2 = (x1 - x2) ** 2. + (y1 - y2) ** 2.
 
     let h = edges.Height
index b325534..877df99 100644 (file)
@@ -1,6 +1,7 @@
 module ParasitemiaCore.Granulometry
 
 open System
+open System.IO
 open System.Drawing
 
 open Emgu.CV
@@ -24,7 +25,7 @@ let findRadiusByClosing (img: Image<Gray, 'TDepth>) (range: int * int) (scale: f
     let intensityImg = scaledImg.GetSum().Intensity
 
     // 's' must be odd.
-    let octagon (s: int) : Mat =
+    let octagon (s: int) : Matrix<byte> =
         if s % 2 = 0 then failwith "s must be odd"
         let m = new Matrix<byte>(Array2D.create s s 1uy)
         let r = (float s) / (Math.Sqrt 2. + 2.) |> roundInt
@@ -36,12 +37,12 @@ let findRadiusByClosing (img: Image<Gray, 'TDepth>) (range: int * int) (scale: f
                     m.[s - i - 1, j] <- 0uy
                     m.[i, s - j - 1] <- 0uy
                     m.[s - i - 1, s - j - 1] <- 0uy
-        m.Mat
+        m
 
     let mutable previous_n = Double.NaN
     for r in r1' .. r2' do
         let se = if useOctagon
-                 then octagon (2 * r - 1) // It doesnd't speed up the process.
+                 then (octagon (2 * r - 1)).Mat // It doesn't speed up the process.
                  else CvInvoke.GetStructuringElement(CvEnum.ElementShape.Ellipse, Size(2 * r, 2 * r), Point(-1, -1))
 
         use closed = scaledImg.MorphologyEx(CvEnum.MorphOp.Close, se, Point(-1, -1), 1, CvEnum.BorderType.Replicate, MCvScalar(0.0))
index b174ee9..a1b6033 100644 (file)
@@ -11,6 +11,10 @@ open Const
 open Histogram
 open Otsu
 
+// Sensibilities of the hysteresis search.
+let sensibilityHigh = 0.1f
+let sensibilityLow = 0.0f
+
 /// <summary>
 /// Find edges of an image by using the Canny approach.
 /// The thresholds are automatically defined with otsu on gradient magnitudes.
@@ -21,9 +25,9 @@ let find (img: Image<Gray, float32>) : Matrix<byte> * Matrix<float32> * Matrix<f
     let h = img.Height
 
     use sobelKernel =
-        new Matrix<float32>(array2D [[ 1.0f; 0.0f; -1.0f ]
-                                     [ 2.0f; 0.0f; -2.0f ]
-                                     [ 1.0f; 0.0f; -1.0f ]])
+        new Matrix<float32>(array2D [[ -1.0f; 0.0f; 1.0f ]
+                                     [ -2.0f; 0.0f; 2.0f ]
+                                     [ -1.0f; 0.0f; 1.0f ]])
 
     let xGradient = new Matrix<float32>(img.Size)
     let yGradient = new Matrix<float32>(img.Size)
@@ -32,11 +36,9 @@ let find (img: Image<Gray, float32>) : Matrix<byte> * Matrix<float32> * Matrix<f
 
     use magnitudes = new Matrix<float32>(xGradient.Size)
     use angles = new Matrix<float32>(xGradient.Size)
-    CvInvoke.CartToPolar(xGradient, yGradient, magnitudes, angles) // Compute the magnitudes and angles.
+    CvInvoke.CartToPolar(xGradient, yGradient, magnitudes, angles) // Compute the magnitudes and angles. The angles are between 0 and 2 * pi.
 
     let thresholdHigh, thresholdLow =
-        let sensibilityHigh = 0.1f
-        let sensibilityLow = 0.0f
         let threshold, _, _ = otsu (histogramMat magnitudes 300)
         threshold + (sensibilityHigh * threshold), threshold - (sensibilityLow * threshold)
 
@@ -49,14 +51,6 @@ let find (img: Image<Gray, float32>) : Matrix<byte> * Matrix<float32> * Matrix<f
     let xGradientData = xGradient.Data
     let yGradientData = yGradient.Data
 
-    for i in 0 .. h - 1 do
-        nmsData.[i, 0] <- 0uy
-        nmsData.[i, w - 1] <- 0uy
-
-    for j in 0 .. w - 1 do
-        nmsData.[0, j] <- 0uy
-        nmsData.[h - 1, j] <- 0uy
-
     for i in 1 .. h - 2 do
         for j in 1 .. w - 2 do
             let vx = xGradientData.[i, j]
@@ -93,6 +87,9 @@ let find (img: Image<Gray, float32>) : Matrix<byte> * Matrix<float32> * Matrix<f
 
     // suppressMConnections nms // It's not helpful for the rest of the process (ellipse detection).
 
+    IO.saveMat magnitudes "magnitudes.png"
+    IO.saveMat nms "nms.png"
+
     let edges = new Matrix<byte>(xGradient.Size)
     let edgesData = edges.Data
 
index 473f11d..1acdac0 100644 (file)
     <Compile Include="ImgTools\Histogram.fs" />
     <Compile Include="ImgTools\Otsu.fs" />
     <Compile Include="ImgTools\Drawing.fs" />
-    <Compile Include="ImgTools\Edges.fs" />
-    <Compile Include="ImgTools\Morpho.fs" />
     <Compile Include="ImgTools\IO.fs" />
     <Compile Include="ImgTools\ImgTools.fs" />
+    <Compile Include="ImgTools\Edges.fs" />
+    <Compile Include="ImgTools\Morpho.fs" />
     <Compile Include="Granulometry.fs" />
     <Compile Include="Config.fs" />
     <Compile Include="KMedians.fs" />
index 70d9ef0..5bb3500 100644 (file)
@@ -63,8 +63,8 @@ let showWindow (parent: Window) (state: State.State) : bool =
             imageSourceSelection.menuZoom50X.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- "230000")
             imageSourceSelection.menuZoom100X.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- "460000")
 
-            imageSourceSelection.butDPICalculator.Click.AddHandler(fun obj args ->
-                match DPICalculator.showWindow win.Root with
+            imageSourceSelection.butPPICalculator.Click.AddHandler(fun obj args ->
+                match PPICalculator.showWindow win.Root with
                 | Some resolution -> imageSourceSelection.txtResolution.Text <- resolution.ToString()
                 | None -> ())
 
index dbd7251..5e9c19b 100644 (file)
@@ -1,4 +1,4 @@
-module ParasitemiaUI.DPICalculator
+module ParasitemiaUI.PPICalculator
 
 open System
 open System.Windows
@@ -19,7 +19,7 @@ type SensorSize = {
         sprintf "%g mm × %g mm%s" this.w this.h (if this.txt = "" then "" else " (" + this.txt + ")")
 
 let showWindow (parent: Window) : int option =
-    let win = Views.DPICalculatorWindow()
+    let win = Views.PPICalculatorWindow()
     win.Root.Owner <- parent
     win.Root.Left <- parent.Left + parent.ActualWidth / 2. - win.Root.Width / 2.
     win.Root.Top <- parent.Top + parent.ActualHeight / 2. - win.Root.Height / 2.
index 2231bf4..a423093 100644 (file)
@@ -74,8 +74,8 @@
     <Compile Include="XAML\ImageSourceSelection.xaml.fs" />
     <Resource Include="XAML\RBCFrame.xaml" />
     <Compile Include="XAML\RBCFrame.xaml.fs" />
-    <Resource Include="XAML\DPICalculatorWindow.xaml" />
-    <Compile Include="XAML\DPICalculatorWindow.xaml.fs" />
+    <Resource Include="XAML\PPICalculatorWindow.xaml" />
+    <Compile Include="XAML\PPICalculatorWindow.xaml.fs" />
     <Resource Include="XAML\AnalysisWindow.xaml" />
     <Compile Include="XAML\AnalysisWindow.xaml.fs" />
     <Resource Include="XAML\AboutWindow.xaml" />
diff --git a/Parasitemia/ParasitemiaUI/XAML/DPICalculatorWindow.xaml b/Parasitemia/ParasitemiaUI/XAML/DPICalculatorWindow.xaml
deleted file mode 100644 (file)
index e731014..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
-        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        mc:Ignorable="d"
-        x:Name="DPICalculatorWindow" Height="200" Width="378.5" MinHeight="200" MinWidth="280" Title="About" Icon="pack://application:,,,/Resources/icon.ico">
-   <Grid Margin="3">
-      <Grid.ColumnDefinitions>
-         <ColumnDefinition Width="Auto"/>
-         <ColumnDefinition Width="1*"/>
-      </Grid.ColumnDefinitions>
-      <Grid.RowDefinitions>
-         <RowDefinition Height="Auto" />
-         <RowDefinition Height="Auto" />
-         <RowDefinition Height="Auto" />
-         <RowDefinition Height="20" />
-         <RowDefinition Height="Auto" />
-         <RowDefinition Height="1*" />
-      </Grid.RowDefinitions>
-      <Label Content="Sensor size" Grid.Row="0" Grid.Column="0" />
-      <ComboBox x:Name="cmbSensorSize" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" />
-
-      <Label Content="Sensor resolution [Megapixel]" Grid.Row="1" Grid.Column="0" />
-      <TextBox x:Name="txtSensorResolution" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" />
-
-      <Label Content="Zoom" Grid.Row="2" Grid.Column="0" />
-      <TextBox x:Name="txtZoom" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" />
-
-      <Label Content="Image resolution [DPI]" Grid.Row="4" Grid.Column="0" />
-      <TextBox x:Name="txtImageResolution" Grid.Row="4" Grid.Column="1" VerticalAlignment="Center" IsReadOnly="True" />
-
-      <Grid Grid.Column="0" Grid.Row="5" Grid.ColumnSpan="2">
-         <Grid.ColumnDefinitions>
-            <ColumnDefinition Width="1*"/>
-            <ColumnDefinition Width="Auto"/>
-         </Grid.ColumnDefinitions>
-         <Button x:Name="butCancel" Content="Cancel" HorizontalAlignment="Right" Margin="3" VerticalAlignment="Bottom" Width="75" Height="20" Grid.Column="0" />
-         <Button x:Name="butOK" Content="OK" HorizontalAlignment="Right" Margin="3" VerticalAlignment="Bottom" Width="75" Height="20" Grid.Column="1" />
-      </Grid>
-   </Grid>
-</Window>
\ No newline at end of file
diff --git a/Parasitemia/ParasitemiaUI/XAML/DPICalculatorWindow.xaml.fs b/Parasitemia/ParasitemiaUI/XAML/DPICalculatorWindow.xaml.fs
deleted file mode 100644 (file)
index ef9cbf4..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace ParasitemiaUI.Views
-
-open FsXaml
-
-type DPICalculatorWindow = XAML<"XAML/DPICalculatorWindow.xaml", true>
-
index 94ca3ed..83fa943 100644 (file)
@@ -67,7 +67,7 @@
                   </Style>
                </Button.Style>
             </Button>
-            <Button x:Name="butDPICalculator" Content="DPI calculator" Grid.Column="2" Margin="3" />
+            <Button x:Name="butPPICalculator" Content="PPI calculator" Grid.Column="2" Margin="3" />
          </Grid>
       </Grid>
    </Grid>
diff --git a/Parasitemia/ParasitemiaUI/XAML/PPICalculatorWindow.xaml b/Parasitemia/ParasitemiaUI/XAML/PPICalculatorWindow.xaml
new file mode 100644 (file)
index 0000000..d52a144
--- /dev/null
@@ -0,0 +1,41 @@
+<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        mc:Ignorable="d"
+        x:Name="PPICalculatorWindow" Height="200" Width="378.5" MinHeight="200" MinWidth="280" Title="About" Icon="pack://application:,,,/Resources/icon.ico">
+   <Grid Margin="3">
+      <Grid.ColumnDefinitions>
+         <ColumnDefinition Width="Auto"/>
+         <ColumnDefinition Width="1*"/>
+      </Grid.ColumnDefinitions>
+      <Grid.RowDefinitions>
+         <RowDefinition Height="Auto" />
+         <RowDefinition Height="Auto" />
+         <RowDefinition Height="Auto" />
+         <RowDefinition Height="20" />
+         <RowDefinition Height="Auto" />
+         <RowDefinition Height="1*" />
+      </Grid.RowDefinitions>
+      <Label Content="Sensor size" Grid.Row="0" Grid.Column="0" />
+      <ComboBox x:Name="cmbSensorSize" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" />
+
+      <Label Content="Sensor resolution [Megapixel]" Grid.Row="1" Grid.Column="0" />
+      <TextBox x:Name="txtSensorResolution" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" />
+
+      <Label Content="Zoom" Grid.Row="2" Grid.Column="0" />
+      <TextBox x:Name="txtZoom" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" />
+
+      <Label Content="Image resolution [PPI]" Grid.Row="4" Grid.Column="0" />
+      <TextBox x:Name="txtImageResolution" Grid.Row="4" Grid.Column="1" VerticalAlignment="Center" IsReadOnly="True" />
+
+      <Grid Grid.Column="0" Grid.Row="5" Grid.ColumnSpan="2">
+         <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="1*"/>
+            <ColumnDefinition Width="Auto"/>
+         </Grid.ColumnDefinitions>
+         <Button x:Name="butCancel" Content="Cancel" HorizontalAlignment="Right" Margin="3" VerticalAlignment="Bottom" Width="75" Height="20" Grid.Column="0" />
+         <Button x:Name="butOK" Content="OK" HorizontalAlignment="Right" Margin="3" VerticalAlignment="Bottom" Width="75" Height="20" Grid.Column="1" />
+      </Grid>
+   </Grid>
+</Window>
\ No newline at end of file
diff --git a/Parasitemia/ParasitemiaUI/XAML/PPICalculatorWindow.xaml.fs b/Parasitemia/ParasitemiaUI/XAML/PPICalculatorWindow.xaml.fs
new file mode 100644 (file)
index 0000000..acb0cd5
--- /dev/null
@@ -0,0 +1,6 @@
+namespace ParasitemiaUI.Views
+
+open FsXaml
+
+type PPICalculatorWindow = XAML<"XAML/PPICalculatorWindow.xaml", true>
+