Ejemplo n.º 1
0
        public void NormalizationTest()
        {
            // Setup a couple of niftis. (Reading from files just makes sure we have valid headers, etc.)
            var niftiA = new NiftiFloat32().ReadNifti(_minimalNiftiPath);
            var niftiB = new NiftiFloat32().ReadNifti(_minimalNiftiPath);

            // Lets use a random element in testing! Maybe it'll pass if we get lucky!
            var rng = new Random(1000);

            for (int i = 0; i < niftiB.Voxels.Length; ++i)
            {
                niftiB.Voxels[i] = rng.Next();
            }

            // Normalize without trimming any background.
            niftiB = Normalization.ZNormalize(niftiB, niftiA, float.NegativeInfinity);

            // See how we did.
            (var mean, var stdDev) = niftiA.Voxels.MeanStandardDeviation();
            Assert.AreEqual(niftiB.Voxels.Mean(), mean, 0.000001);
            Assert.AreEqual(niftiB.Voxels.StandardDeviation(), stdDev, 0.000001);

            niftiB = Normalization.RangeNormalize(niftiA, 0, 1);
            Assert.IsTrue(niftiB.Voxels.Max() <= 1);
            Assert.IsTrue(niftiB.Voxels.Min() >= 0);
            Assert.AreEqual(niftiB.Voxels.Mean(), 0.5, 0.1); // We would expect the mean to be roughly 0.5 given a random input.
        }
Ejemplo n.º 2
0
        private Histogram DoCompare(INifti <float> currentnii, INifti <float> priornii)
        {
            _log.Info("Starting normalization...");
            currentnii = Normalization.ZNormalize(currentnii, priornii);
            _log.Info($@"..done.");

            currentnii.RecalcHeaderMinMax();
            priornii.RecalcHeaderMinMax();

            var tasks     = new List <Task>();
            var histogram = new Histogram
            {
                Prior   = priornii,
                Current = currentnii
            };

            var cs = _recipe.CompareSettings;

            if (cs.CompareIncrease)
            {
                var t = Task.Run(() =>
                {
                    _log.Info("Comparing increased signal...");
                    var increase = Compare.GatedSubract(currentnii, priornii, cs.BackgroundThreshold, cs.MinRelevantStd, cs.MaxRelevantStd, cs.MinChange, cs.MaxChange);
                    for (int i = 0; i < increase.Voxels.Length; ++i)
                    {
                        increase.Voxels[i] = increase.Voxels[i] > 0 ? increase.Voxels[i] : 0;
                    }
                    increase.RecalcHeaderMinMax();
                    increase.ColorMap  = ColorMaps.RedScale();
                    histogram.Increase = increase;
                    var increaseOut    = currentnii.AddOverlay(increase);
                    var outpath        = _currentPath + ".increase.nii";
                    increaseOut.WriteNifti(outpath);
                    Metrics.ResultFiles.Add(new ResultFile()
                    {
                        FilePath = outpath, Description = "Increased Signal", Type = ResultType.CURRENT_PROCESSED
                    });
                });
                tasks.Add(t);
            }
            if (cs.CompareDecrease)
            {
                _log.Info("Comparing decreased signal...");
                // I know the code in these two branches looks similar but there's too many inputs to make a function that much simpler...
                var t = Task.Run(() =>
                {
                    var decrease = Compare.GatedSubract(currentnii, priornii, cs.BackgroundThreshold, cs.MinRelevantStd, cs.MaxRelevantStd, cs.MinChange, cs.MaxChange);
                    for (int i = 0; i < decrease.Voxels.Length; ++i)
                    {
                        decrease.Voxels[i] = decrease.Voxels[i] < 0 ? decrease.Voxels[i] : 0;
                    }
                    decrease.RecalcHeaderMinMax();
                    decrease.ColorMap = ColorMaps.ReverseGreenScale();

                    histogram.Decrease = decrease;
                    var decreaseOut    = currentnii.AddOverlay(decrease);
                    var outpath        = _currentPath + ".decrease.nii";
                    decreaseOut.WriteNifti(outpath);
                    Metrics.ResultFiles.Add(new ResultFile()
                    {
                        FilePath = outpath, Description = "Decreased Signal", Type = ResultType.CURRENT_PROCESSED
                    });
                });
                tasks.Add(t);
            }
            Task.WaitAll(tasks.ToArray());
            _log.Info("...done.");

            return(histogram);
        }
Ejemplo n.º 3
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);
        }