static void Main(string[] args) { var sw = new Stopwatch(); sw.Start(); if (args.Length < 2) { System.Console.WriteLine("Open-Vistarsier requires at least 2 NiftiFiles as input."); System.Console.WriteLine("Usage: vt [prior nifti] [current nifti] [output-prefix](optional)"); return; } var prior = args[0]; var current = args[1]; var outputPrefix = ""; if (args.Length > 2) { outputPrefix = args[2]; } if (!Path.IsPathRooted(prior)) { prior = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, prior)); } if (!Path.IsPathRooted(current)) { current = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, current)); } // Create an MS pipeline. var pipeline = new MSPipeline( current, prior, current, true, true, true, new string[] { outputPrefix + "vt-increase.nii", outputPrefix + "vt-decrease.nii" }, outputPrefix + "vt-prior.nii"); var metrics = pipeline.Process(); // Hack(ish), there's all sorts of to-ing and fro-ing with BMP encoding going on under the hood. // If we don't do this step the nifti will blue itself. var nii = new NiftiFloat32().ReadNifti(outputPrefix + "vt-increase.nii"); for (int i = 0; i < nii.Voxels.Length; ++i) { nii.Voxels[i] = ToBgr((int)nii.Voxels[i]); } nii.WriteNifti(outputPrefix + "vt-increase.nii"); System.Console.WriteLine("Complete:"); System.Console.WriteLine($"Initial brain match : {metrics.BrainMatch}"); System.Console.WriteLine($"Union brain match : {metrics.CorrectedBrainMatch}"); System.Console.WriteLine($"Successful : {metrics.Passed}"); System.Console.WriteLine($"Time to complete : {sw.Elapsed}"); }
public void Reorient() { var nifti = new NiftiFloat32(); nifti.ReadNifti(_minimalNiftiPath); nifti.Reorient(nifti.Header.dim[2], nifti.Header.dim[3], nifti.Header.dim[1]); nifti.GetDimensions(SliceType.Axial, out var width, out var height, out var nSlices); // TODO: Check that this is the expected result. Assert.AreEqual(height, 10); Assert.AreEqual(width, 64); Assert.AreEqual(nSlices, 64); }
public void WriteRgb() { if (File.Exists(_rgbfile)) { File.Delete(_rgbfile); } var nifti = new NiftiFloat32(); nifti.ReadNifti(_minimalNiftiPath); // Read pixdim from sample file nifti.ReadNiftiHeader(_minimalNiftiHdrPath); // set dimensions for new file nifti.ConvertHeaderToRgb(); nifti.Header.cal_min = 0; nifti.Header.cal_max = uint.MaxValue * (3 / 4); // write voxels //var voxelsSize = nifti.Header.dim[1] * nifti.Header.dim[2] * nifti.Header.dim[3]; //var bytepix = nifti.Header.bitpix / 8; //nifti.VoxelsBytes = new byte[voxelsSize * bytepix]; const int r = 255; const int g = 51; const int b = 204; for (var x = 0; x < nifti.Header.dim[1]; x++) { for (var y = 0; y < nifti.Header.dim[2]; y++) { for (var z = 0; z < nifti.Header.dim[3]; z++) { nifti.SetPixelRgb(x, y, z, SliceType.Axial, r, g, b); } } } if (File.Exists(_rgbfile)) { File.Delete(_rgbfile); } nifti.WriteNifti(_rgbfile); Assert.IsTrue(File.Exists(_rgbfile)); //TODO: More meaningful asserts File.Delete(_rgbfile); }
public void DeepCopy() { NiftiFloat32 nifti = (NiftiFloat32) new NiftiFloat32().ReadNifti(_minimalNiftiPath); // TODO <<-- This is a nasty interface var niftiB = (NiftiFloat32)nifti.DeepCopy(); // Check voxel copy Assert.IsTrue(nifti.Voxels[0] == niftiB.Voxels[0]); // Check header copy Assert.IsTrue(nifti.Header.intent_code == niftiB.Header.intent_code); // Check that we're not just doing a shallow copy niftiB.Voxels[0] = nifti.Voxels[0] + 1; niftiB.Header.intent_code = (short)(nifti.Header.intent_code + 1); // Now they should be different... Assert.IsFalse(nifti.Voxels[0] == niftiB.Voxels[0]); Assert.IsFalse(nifti.Header.intent_code == niftiB.Header.intent_code); }
public void CompareTest() { // Create som Nifti objects to compare. var niftiA = new NiftiFloat32().ReadNifti(_minimalNiftiPath); var niftiB = new NiftiFloat32().ReadNifti(_minimalNiftiPath); // Mean and standard deviations are used to see if the change is significant, which we want it to be. (var mean, var stdDev) = niftiA.Voxels.Where(val => val > 10).MeanStandardDeviation(); var change = (float)stdDev; // Make sure the starting values aren't background. niftiA.Voxels[0] = (float)mean; niftiA.Voxels[1] = (float)mean; // Increase by 1 std deviation. niftiB.Voxels[0] = niftiA.Voxels[0] + change; // Decrease by 1 std deviation. niftiB.Voxels[1] = niftiA.Voxels[1] - change; // Do a heaps compare var decrease = MSCompare.CompareMSLesionDecrease(niftiB, niftiA); var increase = MSCompare.CompareMSLesionIncrease(niftiB, niftiA); var changed = Compare.GatedSubract(niftiB, niftiA); // Check that things are working as expected (within floating point error range) Assert.AreEqual(decrease.Voxels[0], 0, 0.00001, "Problem with CompareMSLesionDecrease"); Assert.AreEqual(decrease.Voxels[1], -change, 0.00001, "Problem with CompareMSLesionDecrease"); Assert.AreEqual(increase.Voxels[0], change, 0.00001, "Problem with CompareMSLesionIncrease"); Assert.AreEqual(increase.Voxels[1], 0, 0.00001, "Problem with CompareMSLesionIncrease"); Assert.AreEqual(changed.Voxels[1], -change, 0.00001, "Problem with CompareMSLesion"); Assert.AreEqual(changed.Voxels[0], change, 0.00001, "Problem with CompareMSLesion"); // Check that nothing else has changed. var diff = 0f; for (int i = 2; i < niftiA.Voxels.Length; ++i) { diff += Math.Abs(decrease.Voxels[i]); diff += Math.Abs(increase.Voxels[i]); diff += Math.Abs(changed.Voxels[i]); } Assert.IsTrue(diff == 0, "We've got extra differences which we shouldn't."); }
public void RegistrationTest() { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Assert.Inconclusive("Currently uses Windows Binaries"); } var NiftiA = new NiftiFloat32().ReadNifti(_lrMaskNiftiPath); var NiftiB = new NiftiFloat32().ReadNifti(_lrNiftiPath); // Only doing a couple of asserts here. If something horrible goes wrong, we're expecting an exception. // You can also check the console output to see what the tools reckon. _ = Registration.ANTSRegistration(NiftiA, NiftiB, (d, e) => Console.WriteLine(e.Data)); var outFile = Registration.ANTSApplyTransforms(_lrMaskNiftiPath, _lrNiftiPath, (d, e) => Console.WriteLine(e.Data)); Assert.IsTrue(File.Exists(outFile), "No out file for ANTSApplyTransforms"); _ = new NiftiFloat32().ReadNifti(outFile); _ = Registration.CMTKRegistration(NiftiA, NiftiB, (d, e) => Console.WriteLine(e.Data)); outFile = Registration.CMTKResliceUsingPrevious(_lrMaskNiftiPath, _lrNiftiPath, (d, e) => Console.WriteLine(e.Data)); Assert.IsTrue(File.Exists(outFile), "No out file for CMTKResliceUsingPrevious rego"); NiftiA.ReadNifti(outFile); }
public void WriteRgba() // TODO: This is currently unsupported (technically RGBA doesn't seem to be part // of the NifTI 1.1 format, so it will throw an error on the datatype { var nifti = new NiftiFloat32(); nifti.ReadNifti(_minimalNiftiPath); nifti.ConvertHeaderToRgba(); // Read pixdim from sample file // set dimensions for new file nifti.Header.dim[1] = 100; nifti.Header.dim[2] = 80; nifti.Header.dim[3] = 50; nifti.Header.cal_min = 0; nifti.Header.cal_max = uint.MaxValue; // write voxels var voxelsSize = nifti.Header.dim[1] * nifti.Header.dim[2] * nifti.Header.dim[3]; nifti.Voxels = new float[voxelsSize]; for (var i = 0; i < voxelsSize; i++) { nifti.Voxels[i] = 255; } if (File.Exists(_rgbafile)) { File.Delete(_rgbafile); } nifti.WriteNifti(_rgbafile); Assert.IsTrue(File.Exists(_rgbafile)); //TODO more meaningful asserts. File.Delete(_rgbafile); }
public void ReadNifti() { // Read the minimal nifti file. var nifti = new NiftiFloat32(); nifti.ReadNifti(_minimalNiftiPath); // Check that the dimensions are correct. nifti.GetDimensions(SliceType.Axial, out var width, out var height, out var nSlices); Assert.AreEqual(height, 64); Assert.AreEqual(width, 64); Assert.AreEqual(nSlices, 10); //// Read the minimal nifti file. //var nifti2 = new NiftiRgb48(); //nifti2.ReadNifti(_minimalNiftiPath); //// Check that the dimensions are correct. //nifti2.GetDimensions(SliceType.Axial, out var width2, out var height2, out var nSlices2); //Assert.AreEqual(height2, 64); //Assert.AreEqual(width2, 64); //Assert.AreEqual(nSlices2, 10); //Assert.IsFalse(nifti2.Voxels[10] == 0); }
/// <summary> /// Main method responsible for calling other methods to Extract Brain from current and prior series, Register the two, BFC and Normalize and then compare using a lookup table /// </summary> /// <param name="currentNii">current series nifti file path</param> /// <param name="priorNii">prior series nifti file path</param> /// <param name="referenceNii">reference series nifti file path (If exists, used for universal frame of reference)</param> /// <param name="extractBrain">to do skull stripping or not</param> /// <param name="register">to register or not</param> /// <param name="biasFieldCorrect">to perform bias field correction or not</param> /// <param name="resultNiis">end result output nifti files path</param> /// <param name="outPriorReslicedNii">resliced prior series nifti file path</param> public override MSMetrics Process() { // We consider this pipeline one-shot. if (IsComplete) { throw new IThinkSomethingWentWrongException("You've already processed this instance. Make a new one or check the metrics."); } // Quick sanity check that files exist. FileSystem.FilesExist(new[] { _currentNii, _priorNii }); // Setup our things... DataReceivedEventHandler dataout = (s, e) => { _log.Debug(e.Data); System.Console.WriteLine(e.Data); }; var stopwatch1 = new Stopwatch(); // BiasCorrection if (_biasFieldCorrect) { _log.Info("Starting bias correction..."); stopwatch1.Start(); var bias1 = Task.Run(() => { return(BiasCorrect(_currentNii, dataout)); }); var bias2 = Task.Run(() => { return(BiasCorrect(_priorNii, dataout)); }); //bias1.Wait(); //bias2.Wait(); _currentNii = bias1.Result; _priorNii = bias2.Result; _log.Info($@"..done. [{stopwatch1.Elapsed}]"); stopwatch1.Restart(); } var currentWithSkull = _currentNii; var priorWithSkull = _priorNii; // Brain Extraction _log.Info("Starting brain extraction..."); var brain1 = Task.Run(() => { return(SkullStrip(_currentNii, dataout)); }); var brain2 = Task.Run(() => { return(SkullStrip(_priorNii, dataout)); }); var brain3 = Task.Run(() => { return(SkullStrip(_referenceNii, dataout)); }); // TODO: This can be null. brain1.Wait(); brain2.Wait(); brain3.Wait(); _currentNii = brain1.Result; _priorNii = brain2.Result; _referenceNii = brain3.Result; _log.Info($@"..done. [{stopwatch1.Elapsed}]"); stopwatch1.Restart(); // Registration if (_register) { var refBrain = string.IsNullOrEmpty(_referenceNii) && File.Exists(_referenceNii) ? _referenceNii : _currentNii; // Registration _log.Info("Starting registration..."); _priorNii = Register(_priorNii, refBrain, dataout); if (!refBrain.Equals(_currentNii)) // If we're using a third file for registration... { _priorNii = Register(_currentNii, refBrain, dataout); } _log.Info($@"..done. [{stopwatch1.Elapsed}]"); stopwatch1.Restart(); //TODO fix this... //priorWithSkull = Reslicer(priorWithSkull, refBrain, dataout); } // Convert files to INifti, now that we're done with pre-processing. var currentNifti = new NiftiFloat32().ReadNifti(_currentNii); var priorNifti = new NiftiFloat32().ReadNifti(_priorNii); var currentNiftiWithSkull = new NiftiFloat32().ReadNifti(currentWithSkull); var priorNiftiWithSkull = new NiftiFloat32().ReadNifti(priorWithSkull); // Check brain extraction match... _log.Info($@"Checking brain extraction..."); stopwatch1.Restart(); CheckBrainExtractionMatch(currentNifti, priorNifti, currentNiftiWithSkull, priorNiftiWithSkull, Metrics); _log.Info($@"...done [{stopwatch1.Elapsed}]"); // Normalize stopwatch1.Restart(); _log.Info("Starting normalization..."); currentNifti = Normalization.ZNormalize(currentNifti, priorNifti); _log.Info($@"..done. [{stopwatch1.Elapsed}]"); stopwatch1.Restart(); // Compare _log.Info("Starting compare..."); var increaseTask = Task.Run(() => { return(MSCompare.CompareMSLesionIncrease(currentNifti, priorNifti)); }); var decreaseTask = Task.Run(() => { return(MSCompare.CompareMSLesionDecrease(currentNifti, priorNifti)); }); increaseTask.Wait(); decreaseTask.Wait(); var increaseNifti = increaseTask.Result; var decreaseNifti = decreaseTask.Result; _log.Info($@"..done. [{stopwatch1.Elapsed}]"); stopwatch1.Restart(); // Estimate edge ratio. The edge ratio is the ratio of change which is an edge vs. the change which is bounded. // A lower ratio implies that there are larger connected areas of change, which may be more clinically significant. // This is an estimate as the check is only over one dimension (to save time). _log.Info($@"Estimating edge ratio (lower implies more meaningful change)"); EstimateEdgeRatio(increaseNifti, decreaseNifti, Metrics); _log.Info($@"..done. [{stopwatch1.Elapsed}]"); stopwatch1.Restart(); // Write the prior resliced file. var priorToExport = _extractBrain ? priorNifti : priorNiftiWithSkull; priorToExport.WriteNifti(_outPriorReslicedNii); //Overlay increase and decrease values: _log.Info("Generating RGB overlays..."); var currentToExport = _extractBrain ? currentNifti : currentNiftiWithSkull; var overlayTask1 = Task.Run(() => { return(currentToExport.AddOverlay(increaseNifti)); }); var overlayTask2 = Task.Run(() => { return(currentToExport.AddOverlay(decreaseNifti)); }); overlayTask1.Wait(); overlayTask2.Wait(); var increaseNiftiRGB = overlayTask1.Result; var decreaseNiftiRGB = overlayTask2.Result; // Write files out to disk. var writeTask1 = Task.Run(() => { increaseNiftiRGB.WriteNifti(_resultNiis[0]); }); var writeTask2 = Task.Run(() => { decreaseNiftiRGB.WriteNifti(_resultNiis[1]); }); writeTask1.Wait(); writeTask2.Wait(); _log.Info($@"..done. [{stopwatch1.Elapsed}]"); Metrics.Histogram = new Histogram { Current = currentNifti, Prior = priorNifti, Increase = increaseNifti, Decrease = decreaseNifti }; Metrics.ResultsSlides = new System.Drawing.Bitmap[] { Metrics.Histogram.GenerateSlide() }; IsComplete = true; return(Metrics); }