Ejemplo n.º 1
0
        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}");
        }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
        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);
        }
Ejemplo n.º 5
0
        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.");
        }
Ejemplo n.º 6
0
        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);
        }
Ejemplo n.º 7
0
        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);
        }
Ejemplo n.º 8
0
        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);
        }
Ejemplo n.º 9
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);
        }