Skip to content

Commit

Permalink
Version 0.5.4
Browse files Browse the repository at this point in the history
=============

- Fixed bug in statcmp that might result in incorrect calculations for images where the border is not black.

- Fixed bug in definition of arithmetic operators ".-", "-.", "./", "/."

- The percentiles operator now takes one more parameter to decide in which way to take into account the number of elements equal to a voxel (previously such information was discarded).
  • Loading branch information
vincenzoml committed Jan 30, 2019
1 parent 2a17f2a commit ed59eeb
Show file tree
Hide file tree
Showing 11 changed files with 209 additions and 173 deletions.
30 changes: 15 additions & 15 deletions src/ErrorMsg.fs
Expand Up @@ -24,11 +24,12 @@ do debugFlag <- true
#endif
let isDebug() = debugFlag

type Logger private (streamWriter : System.IO.StreamWriter) =
let stopWatch = System.Diagnostics.Stopwatch.StartNew()
let mutable destinations = [streamWriter] // this is a placeholder for permitting more simultaneous destinations.
type Logger private () =
static let streamWriter = new StreamWriter(System.Console.OpenStandardOutput())
static let stopWatch = System.Diagnostics.Stopwatch.StartNew()
static let mutable destinations = [streamWriter] // this is a placeholder for permitting more simultaneous destinations.

let print prefix (string : string) =
static let print prefix (string : string) =
let printer destination =
lock destination
(fun () ->
Expand All @@ -37,16 +38,15 @@ type Logger private (streamWriter : System.IO.StreamWriter) =
prefix
(string.Replace("\n","\n "))
destination.Flush())
List.iter printer destinations

new() = Logger(new StreamWriter(System.Console.OpenStandardOutput()))
new(filename : string) = Logger(new StreamWriter(filename))

member __.Debug s = print "info" s
member __.DebugOnly s = if isDebug() then print "dbug" s
member __.Warning s = print "warn" s
member __.Failure s = print "fail" s
member __.Result name value = print "user" (sprintf "%s=%A" name value)
member this.DebugExn (exn : exn) = this.Debug <| if isDebug() then exn.ToString() else exn.Message
List.iter printer destinations

// private new() = Logger(new StreamWriter(System.Console.OpenStandardOutput()))
// private new(filename : string) = Logger(new StreamWriter(filename))

static member Debug s = print "info" s
static member DebugOnly s = if isDebug() then print "dbug" s
static member Warning s = print "warn" s
static member Failure s = print "fail" s
static member Result name value = print "user" (sprintf "%s=%A" name value)
static member DebugExn (exn : exn) = Logger.Debug <| if isDebug() then exn.ToString() else exn.Message

28 changes: 14 additions & 14 deletions src/Interpreter.fs
Expand Up @@ -52,7 +52,7 @@ type private DVal = Form of Formula | Fun of string list * Expression * Env

and private Env = Map<string,DVal>

type Interpreter(model : IModel, checker : ModelChecker, logger : ErrorMsg.Logger) =
type Interpreter(model : IModel, checker : ModelChecker) =
let defaultLibDir : string =
System.IO.Path.GetDirectoryName (System.Reflection.Assembly.GetExecutingAssembly().Location)

Expand Down Expand Up @@ -105,14 +105,14 @@ type Interpreter(model : IModel, checker : ModelChecker, logger : ErrorMsg.Logge
match syn with
| ModelLoad(ide,filename) :: rest ->
let filename = getPath inputdir filename confinetoRead
logger.DebugOnly <| sprintf "ModelLoad \"%s\"" filename
let v = model.Load logger filename
ErrorMsg.Logger.DebugOnly <| sprintf "ModelLoad \"%s\"" filename
let v = model.Load filename
evaluate (env.Add(ide,Form (checker.FormulaFactory.CreateConst (v,TModel)))) parsedImports rest jobs
| Declaration (ide,fargs,body) :: rest ->
logger.DebugOnly <| sprintf "Declaration \"%s\"" ide
ErrorMsg.Logger.DebugOnly <| sprintf "Declaration \"%s\"" ide
evaluate (env.Add(ide,Fun (fargs,body,env))) parsedImports rest jobs
| ModelSave(pos,filename,expression) :: rest -> // TODO Use interpreterexception also in load and import
logger.DebugOnly <| sprintf "ModelSave \"%s\"" filename
ErrorMsg.Logger.DebugOnly <| sprintf "ModelSave \"%s\"" filename
let formula = translateExpression [] model checker.FormulaFactory checker.OperatorFactory env expression
let typ = formula.Operator.Rettype
if model.CanSave typ filename then
Expand All @@ -121,18 +121,18 @@ type Interpreter(model : IModel, checker : ModelChecker, logger : ErrorMsg.Logge
let filename = getPath outputdir filename confinetoWrite
let dirname = System.IO.Path.GetDirectoryName filename
ignore <| Directory.CreateDirectory(dirname)
model.Save logger filename res }
model.Save filename res }
evaluate env parsedImports rest (j::jobs)
else raise <| InterpreterException(StackTrace(["save",pos]),CantSaveException(typ,filename))
| Print(pos,filename,expression) :: rest -> // TODO Use interpreterexception also in load and import
logger.DebugOnly <| sprintf "Print \"%s\"" filename
ErrorMsg.Logger.DebugOnly <| sprintf "Print \"%s\"" filename
let formula = translateExpression [] model checker.FormulaFactory checker.OperatorFactory env expression
let typ = formula.Operator.Rettype
match typ with
| (TModel | TNumber | TBool | TString) ->
let j =
job { let! res = checker.Get formula
logger.Result filename res }
ErrorMsg.Logger.Result filename res }
evaluate env parsedImports rest (j::jobs)
| _ -> raise <| InterpreterException(StackTrace(["print",pos]),CantPrintException(typ))
| Import fname :: rest ->
Expand All @@ -147,21 +147,21 @@ type Interpreter(model : IModel, checker : ModelChecker, logger : ErrorMsg.Logge
then try2
else raise <| ImportNotFoundException(fname,libdir)
else raise <| ImportNotFoundException(fname,libdir)
logger.DebugOnly <| sprintf "Import \"%s\"" fname
logger.Debug <| sprintf "Importing file \"%s\"" path
ErrorMsg.Logger.DebugOnly <| sprintf "Import \"%s\"" fname
ErrorMsg.Logger.Debug <| sprintf "Importing file \"%s\"" path
if not (parsedImports.Contains(path)) then
let parsed = parseImport path
evaluate env (parsedImports.Add path) (parsed@rest) jobs
else evaluate env parsedImports rest jobs
| [] -> List.rev jobs
job { logger.Debug "Parsing input..."
job { ErrorMsg.Logger.Debug "Parsing input..."
let p = parseProgram filename s
logger.Debug "Preparing computation..."
ErrorMsg.Logger.Debug "Preparing computation..."
let jobs = evaluate (emptyEnv()) (Set.empty) p []
logger.Debug "Starting computation..."
ErrorMsg.Logger.Debug "Starting computation..."
do! checker.Check
do! Util.Concurrent.conIgnore (Array.ofList jobs)
logger.Debug "... done." }
ErrorMsg.Logger.Debug "... done." }
let batchHopac job =
match Hopac.run (Job.catch job) with
| Choice1Of2 () -> ()
Expand Down
12 changes: 8 additions & 4 deletions src/LogicFragments.fs
Expand Up @@ -2,7 +2,7 @@
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
//
//
// A copy of the license is available in the file "Apache_License.txt".
// You may obtain a copy of the License at
//
Expand Down Expand Up @@ -66,6 +66,10 @@ type IQuantitativeModel<'Value when 'Value : equality> =
abstract member Mask : 'Value -> 'Value -> Job<'Value>
[<OperatorAttribute("avg",[|"valuation(number)";"valuation(bool)"|],"number","avg(img,bimg) is the average of the values of img at voxels that are true in bimg")>]
abstract member Avg : 'Value -> 'Value -> Job<float>
[<OperatorAttribute("./",[|"number";"valuation(number)"|],"valuation(number)","divides each voxel by a constant")>]
abstract member DivSV : float -> 'Value -> Job<'Value>
[<OperatorAttribute(".-",[|"number";"valuation(number)"|],"valuation(number)","subtracts a constant from each voxel")>]
abstract member SubSV : float -> 'Value -> Job<'Value>
[<OperatorAttribute("/.",[|"valuation(number)";"number"|],"valuation(number)","divides each voxel by a constant")>]
abstract member DivVS : 'Value -> float -> Job<'Value>
[<OperatorAttribute("-.",[|"valuation(number)";"number"|],"valuation(number)","subtracts a constant from each voxel")>]
Expand All @@ -86,7 +90,7 @@ type ISpatialModel<'Value when 'Value : equality> =

type IStatisticalModel<'Value when 'Value : equality> =
inherit ILogicModel<'Value>
[<OperatorAttribute("crossCorrelation",[|"number";"valuation(number)";"valuation(number)";"valuation(bool)";"number";"number";"number"|],"valuation(number)","similarity via statistical cross-correlation (see academic papers or extended documentation)")>]
[<OperatorAttribute("crossCorrelation",[|"number";"valuation(number)";"valuation(number)";"valuation(bool)";"number";"number";"number"|],"valuation(number)","crossCorrelation(radius,local,target,mask,min,max,nbins) computes similarity scores via statistical cross-correlation (see academic papers or extended documentation)")>]
abstract member CrossCorrelation : float -> 'Value -> 'Value -> 'Value -> float -> float -> float -> Job<'Value>

type IBoundedModel<'Value when 'Value : equality> =
Expand All @@ -110,8 +114,8 @@ type IImageModel<'Value when 'Value : equality> =
abstract member Volume : 'Value -> Job<float>
[<OperatorAttribute("maxvol","valuation(bool)","valuation(bool)","The connected component of the given image with maximum volume (if more components have the same maximum volume, their union is returned)")>]
abstract member MaxVol : 'Value -> Job<'Value>
[<OperatorAttribute("percentiles",[|"valuation(number)";"valuation(bool)"|],"valuation(number)","Each voxel in percentiles(img,bimg) is the percentile, between 0 and 1, of its value in img, considering only voxels that are true in bimg (voxels that are false in bimg are assigned value 0)")>]
abstract member Percentiles : 'Value -> 'Value -> Job<'Value>
[<OperatorAttribute("percentiles",[|"valuation(number)";"valuation(bool)";"number"|],"valuation(number)","Each voxel in percentiles(img,bimg,k) is the percentile rank, between 0 and 1, of its value in img, considering only voxels that are true in bimg (voxels that are false in bimg are assigned value 0); the rank is not rounded, and it is corrected with k * n where n is the number of voxels equal to the considered one.")>]
abstract member Percentiles : 'Value -> 'Value -> float -> Job<'Value>
[<OperatorAttribute("rgb",[|"valuation(number)";"valuation(number)";"valuation(number)"|],"model","Creates a RGB image given the red, green, and blue components")>]
abstract member RGB : 'Value -> 'Value -> 'Value -> Job<'Value>
[<OperatorAttribute("rgba",[|"valuation(number)";"valuation(number)";"valuation(number)";"valuation(number)"|],"model","Creates a RGBA image given the red, green, blue, and alpha components")>]
Expand Down
6 changes: 5 additions & 1 deletion src/Makefile
Expand Up @@ -46,7 +46,7 @@ restore:
publish-internal:
TERM=xterm DOTNET_CLI_TELEMETRY_OPTOUT=1 dotnet publish -c release -r $(RID)
mkdir -p $(RELEASE)
cp -a bin/release/netcoreapp2.1/$(RID)/publish/* $(RELEASE)
cp -a bin/release/netcoreapp2.2/$(RID)/publish/* $(RELEASE)
cp -a BINARY_LICENSE.txt $(RELEASE)
cd ../releases && rm -f $(RELEASENAME).zip && zip -r $(RELEASENAME).zip $(RELEASENAME)

Expand All @@ -63,3 +63,7 @@ release-win:
make publish-internal RID=win-x64

release: release-linux release-osx release-win

install-linux: release-linux
#TODO: this is temporary; should be made portable
echo sudo "cd /opt && $(PWD)/$(RELEASE)linux-x64.zip"
4 changes: 2 additions & 2 deletions src/Model.fs
Expand Up @@ -22,6 +22,6 @@ open Hopac
[<AbstractClass>]
type IModel() =
inherit Coreops()
abstract member Load : ErrorMsg.Logger -> string -> obj
abstract member Save : ErrorMsg.Logger -> string -> obj -> unit
abstract member Load : string -> obj
abstract member Save : string -> obj -> unit
abstract member CanSave : Type -> string -> bool
13 changes: 6 additions & 7 deletions src/Program.fs
Expand Up @@ -16,7 +16,7 @@

module VoxLogicA.Main
open System.Reflection
open itk.simple
open VoxLogicA

exception CommandLineException
with override __.Message = "Invalid arguments. Usage:\nVoxLogicA <FILENAME>"
Expand All @@ -35,16 +35,15 @@ let main (argv : string array) =
let name = Assembly.GetEntryAssembly().GetName()
let version = name.Version
let informationalVersion = ((Assembly.GetEntryAssembly().GetCustomAttributes(typeof<AssemblyInformationalVersionAttribute>, false).[0]) :?> AssemblyInformationalVersionAttribute).InformationalVersion
let logger = ErrorMsg.Logger ()
logger.Debug (sprintf "%s %s" name.Name informationalVersion)
ErrorMsg.Logger.Debug (sprintf "%s %s" name.Name informationalVersion)
if version.Revision <> 0 then
logger.Warning (sprintf "You are using a PRERELEASE version of VoxLogicA. The most recent stable release is %d.%d.%d." version.Major version.Minor version.Build)
ErrorMsg.Logger.Warning (sprintf "You are using a PRERELEASE version of VoxLogicA. The most recent stable release is %d.%d.%d." version.Major version.Minor version.Build)
try
let model = SITKModel() :> IModel
let checker = ModelChecker(model)
match parseCmdLine argv with
| Filename filename ->
let interpreter = Interpreter(model,checker,logger)
let interpreter = Interpreter(model,checker)
interpreter.Batch
interpreter.DefaultLibDir
(System.IO.Path.GetFullPath ".")
Expand All @@ -64,6 +63,6 @@ let main (argv : string array) =
printfn "Command line error. Try the \"--help\" command line switch."
1
| e ->
logger.DebugExn e
logger.Failure "exiting."
ErrorMsg.Logger.DebugExn e
ErrorMsg.Logger.Failure "exiting."
1
20 changes: 11 additions & 9 deletions src/SITKModel.fs
Expand Up @@ -40,10 +40,10 @@ type SITKModel() =
match t with
| (TValuation(_)|TModel) when List.exists (f.EndsWith : string -> bool) supported_extensions -> true
| _ -> false
override __.Save (logger : ErrorMsg.Logger) filename v =
saveImage filename (v :?> Image) logger
override __.Load (logger : ErrorMsg.Logger) s =
let img = loadImage s logger
override __.Save filename v =
saveImage filename (v :?> Image)
override __.Load s =
let img = loadImage s
match baseImg with
| None ->
baseImg <- Some img
Expand All @@ -59,10 +59,10 @@ type SITKModel() =
&& img.GetDimension() = img1.GetDimension()
then
if img.GetNumberOfComponentsPerPixel() = img1.GetNumberOfComponentsPerPixel() then
logger.Warning (sprintf "Image \"%s\" has different physical space, but same logical structure than previously loaded images; physical space corrected." s)
ErrorMsg.Logger.Warning (sprintf "Image \"%s\" has different physical space, but same logical structure than previously loaded images; physical space corrected." s)
changePhysicalSpace(img,img1) :> obj
else
logger.Warning (sprintf "Image \"%s\"correcting physical space with different number of components is not currently supported; going to exit." s)
ErrorMsg.Logger.Warning (sprintf "Image \"%s\"correcting physical space with different number of components is not currently supported; going to exit." s)
raise (DifferentPhysicalAndLogicalSpaceException s) //TODO: fix this, converting when possible.
else raise (DifferentPhysicalAndLogicalSpaceException s)

Expand All @@ -80,7 +80,7 @@ type SITKModel() =
member __.RGBA (imgr : Image) (imgg : Image) (imgb : Image) (imga : Image) = job { return rgba imgr imgg imgb imga }
member __.Volume img = lift volume img
member __.MaxVol img = lift maxvol img
member __.Percentiles img mask = lift2 percentiles img mask
member __.Percentiles img mask correction = job { return percentiles img mask correction }

interface IBooleanModel<Image> with
member __.And img1 img2 = lift2 logand img1 img2
Expand Down Expand Up @@ -112,11 +112,13 @@ type SITKModel() =
member __.MultiplyVV img1 img2 = lift2 mult img1 img2
member __.Mask (img : Image) (maskImg : Image) = lift2 mask img maskImg
member __.Avg (img : Image) (maskImg : Image) = lift2 avg img maskImg
member __.DivVS (img : Image) k = job { return SimpleITK.Divide(img,k) }
member __.AddVS (img : Image) k = job { return SimpleITK.Add(img,k) }
member __.MulVS (img : Image) k = job { return SimpleITK.Multiply(img,k) }
member __.SubVS (img : Image) k = job { return SimpleITK.Subtract(img,k) }

member __.DivVS (img : Image) k = job { return SimpleITK.Divide(img,k) }
member __.SubSV k (img : Image) = job { return SimpleITK.Subtract(k,img) }
member __.DivSV k (img : Image) = job { return SimpleITK.Divide(k,img) }

interface IStatisticalModel<Image> with
member __.CrossCorrelation rho a b fb m1 m2 k = crosscorrelation rho a b fb m1 m2 k

0 comments on commit ed59eeb

Please sign in to comment.