let run (defaultConfig : Config) (fileToOpen : string option) =
let app = new Application ()
let win = MainWindow ()
+ win.Title <- Constants.APPLICATION_NAME
let state = State.State defaultConfig
let mutable currentScale = 1.
updateDocumentStatus ()
let loadFile (filepath : string) =
+ let displayLoadingErrorMessage (message : string) =
+ MessageBox.Show (sprintf "The document cannot be loaded from \"%s\": %s" filepath message, "Error loading the document", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore
+
askSaveCurrent ()
let previousFilePath = state.FilePath
try
state.Load ()
updateGUI ()
with
+ | PiaZ.VersionFileNewerException fileVersion ->
+ state.FilePath <- previousFilePath
+ displayLoadingErrorMessage $"File version ({fileVersion}) is newer than supported one ({PiaZ.CURRENT_FILE_VERSION}), you may update Parasitemia to the latest version"
+
| :? IOException as ex ->
Log.Error "%O" ex
state.FilePath <- previousFilePath
- MessageBox.Show (sprintf "The document cannot be loaded from \"%s\"" filepath, "Error loading the document", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore
+ displayLoadingErrorMessage "IO Error"
let askLoadFile () =
let dialog = OpenFileDialog (Filter = PiaZ.filter)
MessageBox.Show (sprintf "The results cannot be exported in \"%s\"" state.FilePath, "Error exporting the files", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore
let importImage () =
- let dialog = OpenFileDialog (Filter = "Image Files|*.png;*.jpg;*.tif;*.tiff", Multiselect = true)
+ let dialog = OpenFileDialog (Filter = "Image Files|*.png;*.jpg;.jpeg;.bmp;*.tif;*.tiff", Multiselect = true)
let res = dialog.ShowDialog ()
if res.HasValue && res.Value then
let noSourceImage = state.SourceImages.Count () = 0
images : SourceImage list
}
-let mainEntryName = "info.json"
-let imageExtension = ".tiff"
-let currentFileVersion = 2
+let MAIN_ENTRY_NAME = "info.json"
+let DEFAULT_IMAGE_EXTENSION = ".tiff"
+let JSON_EXTENSION = ".json"
+let CURRENT_FILE_VERSION = 3
/// <summary>
/// Save a document in a give file path. The file may already exist.
let save (filePath : string) (data : DocumentData) =
use file = ZipFile.Open (filePath, ZipArchiveMode.Update)
+ // We only delete JSON files and removed images.
for e in List.ofSeq file.Entries do // 'ofSeq' to not iterate a collection currently modified.
- e.Delete ()
+ if Path.GetExtension e.Name = JSON_EXTENSION || data.images |> List.exists (fun img -> img.OriginalName = e.Name) |> not then
+ e.Delete ()
// Main JSON file.
- let mainEntry = file.CreateEntry (mainEntryName, CompressionLevel.Fastest)
+ let mainEntry = file.CreateEntry (MAIN_ENTRY_NAME, CompressionLevel.Fastest)
use mainEntryWriter = new StreamWriter (mainEntry.Open ())
- mainEntryWriter.Write (JsonConvert.SerializeObject ({ patientID = data.patientID; fileVersion = currentFileVersion }))
+ mainEntryWriter.Write (JsonConvert.SerializeObject ({ patientID = data.patientID; fileVersion = CURRENT_FILE_VERSION }))
- // Write each images and the associated information.
+ // Write each images and the associated information as a JSON file.
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)
+ match srcImg.TempFile with
+ | Some imgTempFile ->
+ let imgEntry = file.CreateEntry (srcImg.OriginalName, CompressionLevel.NoCompression)
+ (File.Open (imgTempFile, FileMode.Open, FileAccess.Read)).CopyTo (imgEntry.Open ())
+ srcImg.TempFile <- None
- let imgJSONEntry = file.CreateEntry (imgFilename + ".json", CompressionLevel.Fastest)
+ | None -> ()
+
+ //let imgFilename = (string srcImg.Num) + DEFAULT_IMAGE_EXTENSION
+ //let imgEntry = file.CreateEntry (imgFilename, CompressionLevel.NoCompression)
+ //srcImg.Img.ToBitmap().Save (imgEntry.Open (), System.Drawing.Imaging.ImageFormat.Tiff)
+
+ let imgJSONEntry = file.CreateEntry (srcImg.OriginalName + JSON_EXTENSION, CompressionLevel.Fastest)
use imgJSONFileWriter = new StreamWriter (imgJSONEntry.Open ())
imgJSONFileWriter.Write (
JsonConvert.SerializeObject (
| _ -> ()
data
+exception VersionFileNewerException of int
+
/// <summary>
/// Load document from a give file path.
/// </summary>
/// <param name="filePath">Path to the PiaZ file</param>
/// <param name="defaultConfig"></param>
/// <exception cref="System.IOException">If the file cannot be read</exception>
+/// <exception cref="VersionFileNewerException">If the file version is newer than the current supported version</exception>
let load (filePath : string) (defaultConfig : ParasitemiaCore.Config.Config) : DocumentData =
use file = ZipFile.Open (filePath, ZipArchiveMode.Read)
- let mainEntry = file.GetEntry (mainEntryName)
+ let mainEntry = file.GetEntry (MAIN_ENTRY_NAME)
use mainEntryReader = new StreamReader (mainEntry.Open ())
let info = JsonConvert.DeserializeObject<JSONInformation> (mainEntryReader.ReadToEnd ())
- updateDocumentData info.fileVersion currentFileVersion
+ if info.fileVersion > CURRENT_FILE_VERSION then
+ raise <| VersionFileNewerException info.fileVersion
+
+ updateDocumentData info.fileVersion CURRENT_FILE_VERSION
{
patientID = info.patientID
images =
[
- let mutable imgNum = 0
for imgEntry in file.Entries do
- if imgEntry.Name.EndsWith (imageExtension) then
+ if imgEntry.Name.EndsWith JSON_EXTENSION |> not then
use bitmap = new System.Drawing.Bitmap (imgEntry.Open (), false)
let img = bitmap.ToImage<Bgr, byte> ()
- imgNum <- imgNum + 1
- let imgJSONEntry = file.GetEntry (imgEntry.Name + ".json")
+ let imgJSONEntry = file.GetEntry (imgEntry.Name + JSON_EXTENSION)
use imgJSONFileReader = new StreamReader (imgJSONEntry.Open ())
let imgInfo = JsonConvert.DeserializeObject<JSONSourceImage> (imgJSONFileReader.ReadToEnd ())
+ let imgNum = imgInfo.num
let config = defaultConfig.Copy ()
config.Parameters <-
config.SetRBCRadius imgInfo.RBCRadius
- SourceImage (imgNum, imgInfo.name, config, imgInfo.dateLastAnalysis, img, imgInfo.rbcs, HealthyRBCBrightness = imgInfo.healthyRBCBrightness, InfectedRBCBrightness = imgInfo.infectedRBCBrightness)
+ SourceImage (imgNum, imgInfo.name, imgEntry.Name, config, imgInfo.dateLastAnalysis, FromMemory img, imgInfo.rbcs, HealthyRBCBrightness = imgInfo.healthyRBCBrightness, InfectedRBCBrightness = imgInfo.infectedRBCBrightness)
]
+ |> List.sortBy (fun image -> image.Num)
}
\ No newline at end of file
namespace ParasitemiaUI
open System
+open System.IO
open System.Windows.Media
open Emgu.CV
open Types
-type SourceImage (num : int, name : string, config : ParasitemiaCore.Config.Config, dateLastAnalysis : DateTime, img : Image<Bgr, byte>, rbcs : RBC list) =
+type ImageData =
+ | FromMemory of Image<Bgr, byte>
+ | FromFile of string // This file will be stored in a temporary directory until the image is saved in a piaz file.
+
+type SourceImage (num : int, name : string, originalName : string, config : ParasitemiaCore.Config.Config, dateLastAnalysis : DateTime, imgData : ImageData, rbcs : RBC list) =
let mutable num = num
let mutable name = name
let mutable config = config
let mutable dateLastAnalysis = dateLastAnalysis // UTC.
- let img = img
+ let img =
+ match imgData with
+ | FromMemory i -> i
+ | FromFile path -> new Image<Bgr, byte> (path)
+
+ let mutable tempFile : string option =
+ match imgData with
+ | FromMemory _ -> None
+ | FromFile path ->
+ let tmpDirname = Guid.NewGuid ()
+ let filename = Path.GetFileName path
+
+ // Copy it to a temporary directory.
+ let tmpDir = Path.Combine (Path.GetTempPath (), string tmpDirname)
+ Directory.CreateDirectory tmpDir |> ignore
+ let tmpPath = Path.Combine (tmpDir, filename)
+
+ File.Copy (path, tmpPath)
+
+ Some tmpPath
+
let mutable rbcs = rbcs
let mutable healthyRBCBrightness = 1.f
let mutable infectedRBCBrightness = 1.f
member this.Name with get () = name and set value = name <- value
+ member this.OriginalName = originalName
+
member this.Config = config
member this.DateLastAnalysis with get () = dateLastAnalysis and set value = dateLastAnalysis <- value
member this.Img = img
+ member this.TempFile
+ with get () = tempFile
+ and set value = tempFile <- value // TODO: remove temp file if set to None
+
member this.RBCs
with get () = rbcs
and set value =