129e6a6bae522b0e7d3af94893b9669ea8364254
[master-thesis.git] / Parasitemia / ParasitemiaUI / State.fs
1 module ParasitemiaUI.State
2
3 open System
4 open System.Collections.Generic
5 open System.Windows
6
7 open Emgu.CV
8 open Emgu.CV.Structure
9
10 open Types
11
12 type State (defaultConfig : ParasitemiaCore.Config.Config) =
13 let sourceImages = List<SourceImage> ()
14 let mutable alteredSinceLastSave = false
15 let mutable patientID = ""
16
17 member this.AlteredSinceLastSave = alteredSinceLastSave
18 member val CurrentImage : SourceImage option = None with get, set
19 member val FilePath : string = "" with get, set
20
21 member this.PatientID
22 with get () : string = patientID
23 and set id =
24 if id <> patientID then
25 alteredSinceLastSave <- true
26 patientID <- id
27
28 member this.GlobalParasitemia : int * int =
29 sourceImages
30 |> Seq.fold (
31 fun (nbTotal, nbTotalInfected) srcImg ->
32 let nb, nbInfected = srcImg.ImageParasitemia
33 nbTotal + nb, nbTotalInfected + nbInfected
34 ) (0, 0)
35
36 member this.SetAsInfected (rbc : RBC) (infected : bool) =
37 if infected <> rbc.infected then
38 alteredSinceLastSave <- true
39 rbc.infected <- infected
40 rbc.setManually <- not rbc.setManually
41
42 /// <summary>
43 /// Save the current state. 'FilePath' must have been defined.
44 /// </summary>
45 /// <exception cref="System.IOException">If the file cannot be saved</exception>
46 member this.Save () =
47 let data = { PiaZ.DocumentData.patientID = this.PatientID; PiaZ.DocumentData.images = List.ofSeq sourceImages }
48 PiaZ.save this.FilePath data
49 alteredSinceLastSave <- false
50
51 /// <summary>
52 /// Load the current state. 'FilePath' must have been defined.
53 /// </summary>
54 /// <exception cref="System.IOException">If the file cannot be loaded</exception>
55 member this.Load () =
56 let data = PiaZ.load this.FilePath defaultConfig
57 this.PatientID <- data.patientID
58 sourceImages.Clear ()
59 sourceImages.InsertRange (0, data.images)
60 this.CurrentImage <- if sourceImages.Count > 0 then Some sourceImages.[0] else None
61 alteredSinceLastSave <- false
62
63 /// <summary>
64 /// </summary>
65 /// <exception cref="System.IOException">If the image cannot be read</exception>
66 member this.AddSourceImage (filePath : string) (defaultConfig : ParasitemiaCore.Config.Config) : SourceImage =
67 let srcImg = SourceImage (sourceImages.Count + 1, System.IO.FileInfo(filePath).Name, defaultConfig.Copy (), DateTime (0L), new Image<Bgr, byte> (filePath), [])
68 sourceImages.Add srcImg
69 if sourceImages.Count = 1 then
70 this.CurrentImage <- Some sourceImages.[0]
71 alteredSinceLastSave <- true
72 srcImg
73
74 member this.RemoveSourceImage (srcImg : SourceImage) =
75 let isCurrent =
76 match this.CurrentImage with
77 | Some srcImg' -> srcImg = srcImg'
78 | _ -> false
79
80 if sourceImages.Remove srcImg then
81 alteredSinceLastSave <- true
82 if isCurrent then
83 this.CurrentImage <- if sourceImages.Count > 0 then Some sourceImages.[0] else None
84 // Re-numbered the images.
85 sourceImages |> Seq.iteri (fun i srcImg -> srcImg.Num <- i + 1)
86
87 member this.SetName (srcImg : SourceImage) (name : string) =
88 if name <> srcImg.Name then
89 srcImg.Name <- name
90 alteredSinceLastSave <- true
91
92 member this.SetResult (imgNum : int) (result : ParasitemiaCore.Types.AnalysisResult) =
93 let sourceImage = sourceImages.Find (fun srcImg -> srcImg.Num = imgNum)
94
95 let w = sourceImage.Img.Width
96 let h = sourceImage.Img.Height
97
98 sourceImage.DateLastAnalysis <- DateTime.UtcNow
99
100 // To match with previously manually altered RBC.
101 let manuallyAlteredPreviousRBCS = sourceImage.RBCs |> List.filter (fun rbc -> rbc.setManually)
102 let tolerance = (float sourceImage.Config.RBCRadius.Pixel) * 0.5 // +/-.
103 let getPreviousManuallyAlteredRBC (center : Point) : RBC option =
104 manuallyAlteredPreviousRBCS
105 |> List.tryFind (
106 fun rbc ->
107 rbc.center.X > center.X - tolerance &&
108 rbc.center.X < center.X + tolerance &&
109 rbc.center.Y > center.Y - tolerance &&
110 rbc.center.Y < center.Y + tolerance
111 )
112
113 sourceImage.RBCs <-
114 result.Cells
115 |> List.filter (fun cell -> match cell.cellClass with ParasitemiaCore.Types.HealthyRBC | ParasitemiaCore.Types.InfectedRBC -> true | _ -> false )
116 |> List.sortByDescending (fun cell -> cell.nucleusArea, (w - cell.center.X) + (h - cell.center.Y))
117 |> List.mapi (
118 fun i cell ->
119 let center = Point (float cell.center.X, float cell.center.Y)
120 let infected, setManually =
121 let infected = cell.cellClass = ParasitemiaCore.Types.InfectedRBC
122 match getPreviousManuallyAlteredRBC center with
123 | Some rbc when rbc.infected <> infected -> rbc.infected, true // If it has been previously manually changed and now match the result, the manually flag is removed.
124 | _ -> infected, false
125 {
126 num = i + 1
127 infected = infected
128 setManually = setManually
129 center = center
130 size = Size (float cell.elements.Width, float cell.elements.Height)
131 infectedArea = cell.nucleusArea
132 }
133 )
134
135 alteredSinceLastSave <- true
136
137 member this.SourceImages : SourceImage seq =
138 sourceImages :> SourceImage seq
139
140 member this.Reset () =
141 this.PatientID <- ""
142 this.FilePath <- ""
143 this.CurrentImage <- None
144 sourceImages.Clear ()
145 alteredSinceLastSave <- false