예제 #1
0
        /// <summary>
        /// Starts threads for frontal texture creation.
        /// </summary>
        /// <param name="groupState">synchronized Threadstate used to observe progress of one or multiple threads.</param>
        /// <param name="processed">synchronized queue which will be filled with each image index, that is ready.</param>
        /// <param name="data">pixel intensity values in a 3D Array mapped to a 1D Array.</param>
        /// <param name="files">all the DICOM files.</param>
        /// <param name="target">target jagged array, which the result will be written to.</param>
        /// <param name="windowWidth">Option to set custom windowWidth, Double.MinValue to not use it</param>
        /// <param name="windowCenter">Option to set custom windowCenter, Double.MinValue to not use it</param>
        /// <param name="threadCount">Amount of Threads to use</param>
        private void StartCreatingFrontTextures(ThreadGroupState groupState, ConcurrentQueue <int> processed, int[] data, IReadOnlyList <DiFile> files, IList <Color32[]> target,
                                                double windowWidth, double windowCenter, int threadCount)
        {
            int spacing = Height / threadCount;

            for (var i = 0; i < threadCount; ++i)
            {
                groupState.Register();
                var startIndex = i * spacing;
                var endIndex   = startIndex + spacing;

                if (i + 1 == threadCount)
                {
                    endIndex = Height;
                }

                var t = new Thread(() => CreateFrontTextures(groupState, processed, data, Width, Height, files,
                                                             target,
                                                             windowWidth, windowCenter, startIndex, endIndex))
                {
                    IsBackground = true
                };
                t.Start();
            }
        }
예제 #2
0
        /// <summary>
        /// Start coroutine for parsing of files.
        /// </summary>
        public ThreadGroupState StartParsingFiles(string folderPath)
        {
            ThreadGroupState state = new ThreadGroupState();

            StartCoroutine(InitFiles(folderPath, state));
            return(state);
        }
예제 #3
0
 /// <summary>
 /// Allows a Unity coroutine to wait for every working thread to finish.
 /// </summary>
 /// <param name="threadGroupState">Thread safe thread-state used to observe progress of one or multiple threads.</param>
 /// <returns>IEnumerator for usage as a coroutine</returns>
 private static IEnumerator WaitForThreads(ThreadGroupState threadGroupState)
 {
     while (threadGroupState.Working > 0)
     {
         yield return(null);
     }
 }
예제 #4
0
        /// <summary>
        /// Fills the target array with 3D data while applying basic preprocessing.
        /// </summary>
        /// <param name="groupState">synchronized Threadstate used to observe progress of one or multiple threads.</param>
        /// <param name="files">all the DICOM files.</param>
        /// <param name="width">width of a DICOM slice.</param>
        /// <param name="height">height of a DICOM slice.</param>
        /// <param name="target">1D array receiving the 3D data.</param>
        /// <param name="start">Start index used to determine partition of images to be computed</param>
        /// <param name="end">End index used to determine upper bound of partition of images to be computed</param>
        private static void PreProcess(ThreadGroupState groupState, IReadOnlyList <DiFile> files, int width, int height,
                                       IList <int> target, int start, int end)
        {
            var storedBytes = new byte[4];

            for (var layer = start; layer < end; ++layer)
            {
                var  currentDiFile = files[layer];
                var  pixelData     = currentDiFile.RemoveElement(0x7FE0, 0x0010);
                uint mask          = ~(0xFFFFFFFF << (currentDiFile.GetHighBit() + 1));
                int  allocated     = currentDiFile.GetBitsAllocated() / 8;

                var baseOffset = layer * width * height;

                using (var pixels = new MemoryStream(pixelData.GetValues()))
                {
                    for (var y = 0; y < height; ++y)
                    {
                        for (var x = 0; x < width; ++x)
                        {
                            //get current Int value
                            pixels.Read(storedBytes, 0, allocated);
                            var value      = BitConverter.ToInt32(storedBytes, 0);
                            var currentPix = GetPixelIntensity((int)(value & mask), currentDiFile);
                            target[baseOffset + y * width + x] = currentPix;
                        }
                    }
                }

                groupState.IncrementProgress();
                Thread.Sleep(10);
            }

            groupState.Done();
        }
예제 #5
0
        /// <summary>
        /// Starts threads for volume texture creation.
        /// </summary>
        /// <param name="groupState">synchronized Threadstate used to observe progress of one or multiple threads.</param>
        /// <param name="files">all the DICOM files.</param>
        /// <param name="data">pixel intensity values in a 3D Array mapped to a 1D Array.</param>
        /// <param name="target">target jagged array, which the result will be written to.</param>
        /// <param name="windowWidth">Option to set custom windowWidth, Double.MinValue to not use it</param>
        /// <param name="windowCenter">Option to set custom windowCenter, Double.MinValue to not use it</param>
        /// <param name="threadCount">Amount of Threads to use</param>
        private void StartCreatingVolume(ThreadGroupState groupState, IReadOnlyList <DiFile> files, IReadOnlyList <int> data, IList <Color32> target, double windowWidth, double windowCenter, int threadCount)
        {
#if PRINT_USAGE
            Debug.Log(Time.time +
                      $" : Started Creating Volume with Window (Center {WindowCenter}, Width {WindowWidth})");
#endif

            var spacing = files.Count / threadCount;

            for (var i = 0; i < threadCount; ++i)
            {
                groupState.Register();
                var startIndex = i * spacing;
                var endIndex   = startIndex + spacing;

                if (i + 1 == threadCount)
                {
                    endIndex = files.Count;
                }

                var t = new Thread(() => CreateVolume(groupState, data, files, Width, Height, target, windowWidth,
                                                      windowCenter, startIndex, endIndex))
                {
                    IsBackground = true
                };
                t.Start();
            }
        }
예제 #6
0
        /// <summary>
        /// Starts coroutine for preprocessing DICOM pixeldata
        /// </summary>
        public ThreadGroupState StartPreprocessData()
        {
            ThreadGroupState state = new ThreadGroupState {
                TotalProgress = DicomFiles.Length
            };

            PreprocessData(state);
            return(state);
        }
예제 #7
0
        /// <summary>
        /// Unity coroutine for loading the selected folder of files.
        /// </summary>
        /// <param name="folderPath">Path of the folder containing the DICOM files</param>
        /// <param name="threadGroupState">Thread safe thread-state used to observe progress of one or multiple threads.</param>
        /// <returns>IEnumerator for usage as a coroutine</returns>
        private IEnumerator InitFiles(string folderPath, ThreadGroupState threadGroupState)
        {
            threadGroupState.Register();
            //string[] filePaths = Directory.GetFiles(folderPath);

            //filePaths = Array.FindAll(filePaths, HasNoExtension);
            var fileNames = GetFiles(folderPath);

            DicomFiles = new DiFile[fileNames.Count];
            threadGroupState.TotalProgress = fileNames.Count;

            yield return(null);

            var zeroBased = true;

            foreach (var path in fileNames)
            {
                var diFile = new DiFile();
                diFile.InitFromFile(path);

                if (zeroBased && diFile.GetImageNumber() == DicomFiles.Length)
                {
                    ShiftLeft(DicomFiles);
                    zeroBased = false;
                }

                if (zeroBased)
                {
                    DicomFiles[diFile.GetImageNumber()] = diFile;
                }
                else
                {
                    DicomFiles[diFile.GetImageNumber() - 1] = diFile;
                }

                threadGroupState.IncrementProgress();
                yield return(null);
            }

            Width  = DicomFiles[0].GetImageWidth();
            Height = DicomFiles[0].GetImageHeight();

            _data = new int[DicomFiles.Length * Width * Height];

            VolumeTexture = null;

            WindowCenterPresets = DicomFiles[0].GetElement(0x0028, 0x1050)?.GetDoubles() ?? new[] { double.MinValue };
            WindowWidthPresets  = DicomFiles[0].GetElement(0x0028, 0x1051)?.GetDoubles() ?? new[] { double.MinValue };

            WindowCenter = WindowCenterPresets[0];
            WindowWidth  = WindowWidthPresets[0];

            MinPixelIntensity = (int)(DicomFiles[0].GetElement(0x0028, 0x1052)?.GetDouble() ?? 0d);
            MaxPixelIntensity = (int)((DicomFiles[0].GetElement(0x0028, 0x1053)?.GetDouble() ?? 1d) * (Math.Pow(2, DicomFiles[0].GetBitsStored()) - 1) + MinPixelIntensity);

            threadGroupState.Done();
        }
예제 #8
0
        /// <summary>
        /// Start coroutine for creating 2D textures.
        /// </summary>
        public ThreadGroupState StartCreatingTextures()
        {
            ThreadGroupState state = new ThreadGroupState {
                TotalProgress = DicomFiles.Length + Width + Height
            };

            StartCoroutine(CreateTextures(state));
            return(state);
        }
예제 #9
0
        /// <summary>
        /// Starts coroutine for creating the 3D texture
        /// </summary>
        public ThreadGroupState StartCreatingVolume()
        {
            ThreadGroupState state = new ThreadGroupState {
                TotalProgress = DicomFiles.Length
            };

            StartCoroutine(CreateVolume(state));

            return(state);
        }
예제 #10
0
        /// <summary>
        /// Adds a workload to be completed.
        /// </summary>
        /// <param name="threadGroupState">State of the workload</param>
        /// <param name="description">Displayed description of the workload</param>
        /// <param name="onFinished">Callback for completed work</param>
        public void AddWorkload(ThreadGroupState threadGroupState, string description, Action onFinished)
        {
            _currentWorkloads.Add(new Tuple <ThreadGroupState, string, Action>(threadGroupState, description, onFinished));

            if (_currentWorkloads.Count == 1)
            {
                MainMenu.ProgressHandler.TaskDescription = description;
                MainMenu.ProgressHandler.Value           = 0;
            }

            MainMenu.ProgressHandler.Max += threadGroupState.TotalProgress;
        }
예제 #11
0
        /// <summary>
        /// Fills the target color array with the pixels for all saggital images in range from start to end (excluding end).
        /// </summary>
        /// <param name="groupState">synchronized Threadstate used to observe progress of one or multiple threads.</param>
        /// <param name="processed">synchronized queue which will be filled with each image index, that is ready.</param>
        /// <param name="data">pixel intensity values in a 3D Array mapped to a 1D Array.</param>
        /// <param name="width">width of a transversal image.</param>
        /// <param name="height">height of a transversal image.</param>
        /// <param name="files">all the DICOM files.</param>
        /// <param name="target">target jagged array, which the result will be written to.</param>
        /// <param name="windowWidth">Option to set custom windowWidth, Double.MinValue to not use it</param>
        /// <param name="windowCenter">Option to set custom windowCenter, Double.MinValue to not use it</param>
        /// <param name="start">Start index used to determine partition of images to be computed</param>
        /// <param name="end">End index used to determine upper bound of partition of images to be computed</param>
        private static void CreateSagTextures(ThreadGroupState groupState, ConcurrentQueue <int> processed, int[] data, int width, int height, IReadOnlyList <DiFile> files, IList <Color32[]> target,
                                              double windowWidth, double windowCenter, int start, int end)
        {
            for (var x = start; x < end; ++x)
            {
                target[x] = new Color32[height * files.Count];
                FillPixelsSagittal(x, data, width, height, files, target[x], TransferFunction.Identity, windowWidth, windowCenter);
                processed.Enqueue(x);
                Thread.Sleep(5);
            }

            groupState.Done();
        }
예제 #12
0
        /// <summary>
        /// Fills the target color array with the pixels for all transversal images in range from start to end (excluding end).
        /// </summary>
        /// <param name="groupState">synchronized Threadstate used to observe progress of one or multiple threads.</param>
        /// <param name="processed">synchronized queue which will be filled with each image index, that is ready.</param>
        /// <param name="data">pixel intensity values in a 3D Array mapped to a 1D Array.</param>
        /// <param name="width">width of a transversal image.</param>
        /// <param name="height">height of a transversal image.</param>
        /// <param name="files">all the DICOM files.</param>
        /// <param name="target">target jagged array, which the result will be written to.</param>
        /// <param name="windowWidth">Option to set custom windowWidth, Double.MinValue to not use it</param>
        /// <param name="windowCenter">Option to set custom windowCenter, Double.MinValue to not use it</param>
        /// <param name="start">Start index used to determine partition of images to be computed</param>
        /// <param name="end">End index used to determine upper bound of partition of images to be computed</param>
        private static void CreateTransTextures(ThreadGroupState groupState, ConcurrentQueue <int> processed, int[] data, int width, int height, IReadOnlyList <DiFile> files,
                                                IList <Color32[]> target, double windowWidth, double windowCenter, int start, int end)
        {
            for (var layer = start; layer < end; ++layer)
            {
                target[layer] = new Color32[width * height];
                FillPixelsTransversal(layer, data, width, height, files, target[layer], TransferFunction.Identity, windowWidth, windowCenter);
                processed.Enqueue(layer);
                Thread.Sleep(5);
            }

            groupState.Done();
        }
예제 #13
0
        /// <summary>
        /// Unity coroutine used to create the 3D texture using multiple threads.
        /// </summary>
        /// <param name="threadGroupState">Thread safe thread-state used to observe progress of one or multiple threads.</param>
        /// <returns>IEnumerator for usage as a coroutine</returns>
        private IEnumerator CreateVolume(ThreadGroupState threadGroupState)
        {
            VolumeTexture = new Texture3D(Width, Height, DicomFiles.Length, TextureFormat.ARGB32, false);

            var cols = new Color32[Width * Height * DicomFiles.Length];

            StartCreatingVolume(threadGroupState, DicomFiles, _data, cols, WindowWidth, WindowCenter, 6);

            yield return(WaitForThreads(threadGroupState));

            VolumeTexture.SetPixels32(cols);
            VolumeTexture.Apply();
        }
예제 #14
0
        /// <summary>
        /// Unity coroutine used to create all textures using multiple threads.
        /// </summary>
        /// <param name="threadGroupState">Thread safe thread-state used to observe progress of one or multiple threads.</param>
        /// <returns>IEnumerator for usage as a coroutine</returns>
        private IEnumerator CreateTextures(ThreadGroupState threadGroupState)
        {
#if PRINT_USAGE
            Debug.Log(Time.time +
                      $" : Started Creating Textures with Window (Center {WindowCenter}, Width {WindowWidth})");
#endif

            _transversalTexture2Ds = new Texture2D[DicomFiles.Length];
            _frontalTexture2Ds     = new Texture2D[Height];
            _sagittalTexture2Ds    = new Texture2D[Width];

            var transTextureColors = new Color32[DicomFiles.Length][];
            var frontTextureColors = new Color32[Height][];
            var sagTextureColors   = new Color32[Width][];

            var transProgress = new ConcurrentQueue <int>();
            var frontProgress = new ConcurrentQueue <int>();
            var sagProgress   = new ConcurrentQueue <int>();

            StartCreatingTransTextures(threadGroupState, transProgress, _data, DicomFiles, transTextureColors, WindowWidth, WindowCenter, 2);
            StartCreatingFrontTextures(threadGroupState, frontProgress, _data, DicomFiles, frontTextureColors, WindowWidth, WindowCenter, 2);
            StartCreatingSagTextures(threadGroupState, sagProgress, _data, DicomFiles, sagTextureColors, WindowWidth, WindowCenter, 2);

            while (threadGroupState.Working > 0 || !(transProgress.IsEmpty && frontProgress.IsEmpty && sagProgress.IsEmpty))
            {
                int current;
                if (transProgress.TryDequeue(out current))
                {
                    CreateTexture2D(Width, Height, transTextureColors, _transversalTexture2Ds, current);
                    OnTextureUpdate.Invoke(SliceType.Transversal, current);
                    threadGroupState.IncrementProgress();
                }

                if (frontProgress.TryDequeue(out current))
                {
                    CreateTexture2D(Width, DicomFiles.Length, frontTextureColors, _frontalTexture2Ds, current);
                    OnTextureUpdate.Invoke(SliceType.Frontal, current);
                    threadGroupState.IncrementProgress();
                }

                if (sagProgress.TryDequeue(out current))
                {
                    CreateTexture2D(Height, DicomFiles.Length, sagTextureColors, _sagittalTexture2Ds, current);
                    OnTextureUpdate.Invoke(SliceType.Sagittal, current);
                    threadGroupState.IncrementProgress();
                }

                yield return(null);
            }
        }
예제 #15
0
        /// <summary>
        /// Starts one or more Threads for preprocessing.
        /// </summary>
        /// <param name="groupState">synchronized Threadstate used to observe progress of one or multiple threads.</param>
        /// <param name="files">all the DICOM files.</param>
        /// <param name="target">1D array receiving the 3D data.</param>
        /// <param name="threadCount">Amount of Threads to use.</param>
        private void StartPreProcessing(ThreadGroupState groupState, IReadOnlyList <DiFile> files, int[] target, int threadCount)
        {
            int spacing = files.Count / threadCount;

            for (var i = 0; i < threadCount; ++i)
            {
                var startIndex = i * spacing;
                var endIndex   = startIndex + spacing;

                if (i + 1 == threadCount)
                {
                    endIndex = files.Count;
                }

                groupState.Register();
                var t = new Thread(() => PreProcess(groupState, files, Width, Height, target, startIndex, endIndex))
                {
                    IsBackground = true
                };
                t.Start();
            }
        }
예제 #16
0
        /// <summary>
        /// Fills the given 3D color array using the given 3D pixel intensity array of same size.
        /// </summary>
        /// <param name="groupState">synchronized Threadstate used to observe progress of one or multiple threads.</param>
        /// <param name="data">pixel intensity values in a 3D Array mapped to a 1D Array.</param>
        /// <param name="dicomFiles">all the DICOM files.</param>
        /// <param name="width">width of a transversal image.</param>
        /// <param name="height">height of a transversal image.</param>
        /// <param name="target">§D color array mapped to 1D Array.</param>
        /// <param name="windowWidth">Option to set custom windowWidth, Double.MinValue to not use it</param>
        /// <param name="windowCenter">Option to set custom windowCenter, Double.MinValue to not use it</param>
        /// <param name="start">Start index used to determine partition of images to be computed</param>
        /// <param name="end">End index used to determine upper bound of partition of images to be computed</param>
        private void CreateVolume(ThreadGroupState groupState, IReadOnlyList <int> data, IReadOnlyList <DiFile> dicomFiles, int width, int height,
                                  IList <Color32> target, double windowWidth, double windowCenter, int start, int end)
        {
            var idx = start * width * height;

            for (var z = start; z < end; ++z)
            {
                var idxPartZ = z * width * height;
                for (var y = 0; y < height; ++y)
                {
                    var idxPart = idxPartZ + y * width;
                    for (var x = 0; x < width; ++x, ++idx)
                    {
                        target[idx] = TransferFunction.DYN_ALPHA(GetRGBValue(data[idxPart + x], dicomFiles[z], windowWidth, windowCenter));
                    }
                }

                Thread.Sleep(5);
                groupState.IncrementProgress();
            }

            groupState.Done();
        }
예제 #17
0
파일: Segment.cs 프로젝트: mrcdnk/dicomholo
 /// <summary>
 /// Creates an empty Segment with uninitialized data array.
 /// </summary>
 /// <param name="segmentColor">Color Number used inside the shader.</param>
 public Segment(Color segmentColor)
 {
     SegmentColor     = segmentColor;
     _currentWorkload = new ThreadGroupState();
 }
예제 #18
0
 /// <summary>
 /// Unity coroutine used to preprocess the DICOM pixel data using multiple threads.
 /// </summary>
 /// <param name="threadGroupState"></param>
 /// <returns>IEnumerator for usage as a coroutine</returns>
 private void PreprocessData(ThreadGroupState threadGroupState)
 {
     StartPreProcessing(threadGroupState, DicomFiles, _data, 12);
 }