Add a logger assembly and split the main assembly in two : the UI and the parasitemia...
[master-thesis.git] / Parasitemia / ParasitemiaUI / PiaZ.fs
diff --git a/Parasitemia/ParasitemiaUI/PiaZ.fs b/Parasitemia/ParasitemiaUI/PiaZ.fs
new file mode 100644 (file)
index 0000000..d4ea46f
--- /dev/null
@@ -0,0 +1,97 @@
+// ParasitemIA Zipped document format.
+module ParasitemiaUI.PiaZ
+
+open System
+open System.Windows
+open System.IO
+open System.IO.Compression
+
+open Emgu.CV
+open Emgu.CV.Structure
+
+open Newtonsoft.Json
+open Newtonsoft.Json.Converters
+
+open Types
+
+let extension = ".piaz"
+let filter = "PIA|*.piaz"
+
+// Information associated to a document.
+type JSONInformation = {
+    patientID: string
+}
+
+// Information associated to each images.
+type JSONSourceImage = {
+    num: int
+    RBCRadius: float32 // The RBC Radius found by granulometry.
+    parameters: ParasitemiaCore.Config.Parameters
+    dateLastAnalysis: DateTime
+    rbcs: RBC List
+}
+
+type DocumentData = {
+    patientID: string
+    images: SourceImage list
+}
+
+let mainEntryName = "info.json"
+let imageExtension = ".tiff"
+
+/// <summary>
+/// Save a document in a give file path. The file may already exist.
+/// </summary>
+/// <param name="filePath"></param>
+/// <param name="data"></param>
+/// <exception cref="System.IOException">If the file cannot be written</exception>
+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.
+        e.Delete()
+
+    // Main JSON file.
+    let mainEntry = file.CreateEntry(mainEntryName, CompressionLevel.Fastest)
+    use mainEntryWriter = new StreamWriter(mainEntry.Open())
+    mainEntryWriter.Write(JsonConvert.SerializeObject({ JSONInformation.patientID = data.patientID }))
+
+    // Write each images and the associated information.
+    for srcImg in data.images do
+        let imgFilename = (string srcImg.num) + imageExtension
+        let imgEntry = file.CreateEntry(imgFilename, CompressionLevel.NoCompression) // FIXME: It seems a compression is applied to this file despite of the 'NoCompression' flag.
+        srcImg.img.ToBitmap().Save(imgEntry.Open(), System.Drawing.Imaging.ImageFormat.Tiff)
+
+        let imgJSONEntry = file.CreateEntry(imgFilename + ".json", CompressionLevel.Fastest)
+        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 }))
+
+/// <summary>
+/// Load document from a give file path.
+/// </summary>
+/// <param name="filePath"></param>
+/// <exception cref="System.IOException">If the file cannot be read</exception>
+let load (filePath: string) : DocumentData =
+    use file = ZipFile.Open(filePath, ZipArchiveMode.Read)
+
+    let mainEntry = file.GetEntry(mainEntryName)
+    use mainEntryReader = new StreamReader(mainEntry.Open())
+    let info = JsonConvert.DeserializeObject<JSONInformation>(mainEntryReader.ReadToEnd())
+
+    { patientID = info.patientID
+      images = [ let mutable imgNum = 0
+                 for imgEntry in file.Entries do
+                    if imgEntry.Name.EndsWith(imageExtension)
+                    then
+                        let img = new Image<Bgr, byte>(new System.Drawing.Bitmap(imgEntry.Open(), false)) // FIXME: Should we dispose the bitmap?
+                        imgNum <- imgNum + 1
+                        let imgEntry = file.GetEntry(imgEntry.Name + ".json")
+                        use imgEntryFileReader = new StreamReader(imgEntry.Open())
+                        let imgInfo = JsonConvert.DeserializeObject<JSONSourceImage>(imgEntryFileReader.ReadToEnd())
+                        let config = ParasitemiaCore.Config.Config(imgInfo.parameters)
+                        config.SetRBCRadius imgInfo.RBCRadius
+                        yield { num = imgNum
+                                config = config
+                                dateLastAnalysis = imgInfo.dateLastAnalysis
+                                img = img
+                                rbcs = imgInfo.rbcs } ] }
\ No newline at end of file