// ===============================================================
    // DICOM Loading:

    /*! Starts loading the given DICOM, if available.
     * \note If slice is negative, this loads the entire volume!
     * \return true if loading process is started, false if the loader is currently busy. */
    public bool startLoading(DICOMSeries toLoad, int slice = 0)
    {
        if (!isBusy && toLoad != null)
        {
            // Lock:
            isBusy = true;

            seriesToLoad = toLoad;
            sliceToLoad  = slice;
            ThreadUtil t = new ThreadUtil(load, loadCallback);
            t.Run();

            // Let event system know what we're currently doing:
            PatientEventSystem.triggerEvent(PatientEventSystem.Event.LOADING_AddLoadingJob,
                                            "Loading DICOM series " + toLoad.seriesUID);

            if (slice >= 0)
            {
                PatientEventSystem.triggerEvent(PatientEventSystem.Event.DICOM_StartLoadingSlice);
            }
            else
            {
                PatientEventSystem.triggerEvent(PatientEventSystem.Event.DICOM_StartLoadingVolume);
            }

            return(true);
        }
        else
        {
            return(false);
        }
    }
Ejemplo n.º 2
0
 public void selectedNewVolumetric(DICOMSeries series)
 {
     if (DICOMLoader.instance.currentDICOMVolume != null &&
         DICOMLoader.instance.currentDICOMVolume.seriesInfo == series)
     {
         DICOMLoader.instance.unloadVolume();
     }
     else
     {
         DICOMLoader.instance.startLoadingVolume(series);
     }
 }
Ejemplo n.º 3
0
    public void selectedNewDicom(DICOMSeries series)
    {
        // Check if we previously loaded an image from this series. If so, figure out what
        // the last shown layer was:
        int previousLayer = DicomImage.GetComponent <DicomDisplayImage> ().savedLayerForSeriesUID(series.seriesUID);

        // Load this layer:
        DICOMLoader.instance.startLoading(series, previousLayer);

        StatusText.gameObject.SetActive(true);
        StatusText.text = "Loading DICOM ...";
        ImageScreen.SetActive(true);
        ListScreen.SetActive(false);
        //DicomImage.gameObject.SetActive (false);
    }
Ejemplo n.º 4
0
    public DICOM3D(DICOMSeries seriesInfo) : base(seriesInfo)
    {
        dimensions = 3;

        Image firstSlice = seriesInfo.firstSlice;
        Image lastSlice  = seriesInfo.lastSlice;

        VectorDouble o1 = firstSlice.GetOrigin();

        if (o1.Count < 3)
        {
            throw(new System.Exception("Invalid origins found in first image."));
        }
        origin = new Vector3((float)o1 [0], (float)o1 [1], (float)o1 [2]);

        // Load the pixel spacing:
        // NOTE: It seems that the the first value is the spacing between rows (i.e. y direction),
        //		the second value is the spacing between columns (i.e. x direction).
        //		I was not able to verify this so far, since all test dicoms we had have the same spacing in
        //		x and y direction...
        VectorDouble spacing = firstSlice.GetSpacing();

        if (spacing.Count < 2)
        {
            throw(new System.Exception("Invalid pixel spacing found in images."));
        }
        pixelSpacing = new Vector2((float)spacing [1], (float)spacing [0]);

        // DICOMSeries already calculated these values, so get them from there:
        directionCosineX = seriesInfo.directionCosineX;
        directionCosineY = seriesInfo.directionCosineY;

        // Generate the transformation matrices which can later be used to translate pixels to
        // 3D positions and vice versa.
        setupTransformationMatrices();

        loadVolumeData();


        Vector3 corner1       = transformPixelToPatientPos(Vector2.zero, 0f);
        Vector2 imgDimensions = new Vector2(firstSlice.GetWidth(), firstSlice.GetHeight());
        Vector3 corner2       = transformPixelToPatientPos(imgDimensions, seriesInfo.numberOfSlices - 1);

        //boundingBox = new Bounds ((max - min) / 2 + min, (max - min));
        boundingBox = new Bounds((corner2 - corner1) / 2 + corner1, (corner2 - corner1));
        Debug.Log("bounding Box: " + boundingBox.center + " " + boundingBox.size);
    }
Ejemplo n.º 5
0
    void eventNewDicomList(object obj = null)
    {
        eventClear();           // Clear the list

        // Make sure a patient is loaded:
        Patient p = Patient.getLoadedPatient();

        if (p == null)
        {
            Text newEntryText = ListEntryButton.transform.GetComponentInChildren <Text> ();
            newEntryText.text = "No patient loaded.";
            ListEntryButton.SetActive(true);
            return;
        }
        // Make sure at least one series was found:
        List <DICOMSeries> series = DICOMLoader.instance.availableSeries;

        if (series.Count <= 0)
        {
            Text newEntryText = ListEntryButton.transform.GetComponentInChildren <Text> ();
            newEntryText.text = "No series found.";
            ListEntryButton.SetActive(true);
            return;
        }

        // Deactivate default button:
        ListEntryButton.SetActive(false);

        foreach (DICOMSeries s in series)
        {
            //customNames.Add (p.getDICOMNameForSeriesUID (uid));
            GameObject newEntry = Instantiate(ListEntryButton) as GameObject;
            newEntry.SetActive(true);

            Text newEntryText = newEntry.transform.GetComponentInChildren <Text> ();
            newEntryText.text = s.getDescription();
            newEntry.transform.SetParent(ListEntryButton.transform.parent, false);

            // Make the button load the DICOM:
            Button      newButton = newEntry.GetComponent <Button> ();
            DICOMSeries captured  = s;
            newButton.onClick.AddListener(() => selectedNewDicom(captured));
        }

        DicomImage.gameObject.SetActive(false);
    }
Ejemplo n.º 6
0
    /*! Constructor, loads the DICOM image data from file.
     * The constructor starts the loading of pixel data from the files (filenames are
     * taken from the seriesInfo). If slice is zero or positive, only the single file
     * will be read. If slice is negative, the entire volume (i.e. all files - and thus
     * all slices) will be read.
     * \note Since the constructor does so much work, the Object should be created in
     *      a background thread and then passed to the main thread. */
    public DICOM(DICOMSeries seriesInfo, int slice = -1)
    {
        // Remember, we will need it later:
        this.seriesInfo = seriesInfo;
        this.slice      = slice;

        if (slice == -1)                // load the entire volume:
        {
            dimensions = 3;
            loadVolumeData();
        }
        else
        {
            // Make sure that 'slice' is a valid slice number:
            slice      = Mathf.Clamp(slice, 0, seriesInfo.filenames.Count - 1);
            dimensions = 2;
            loadImageData(slice);
        }
    }
Ejemplo n.º 7
0
    // ===============================================================
    // DICOM Loading:

    /*! Starts loading the given DICOM, if available.
     * \note If slice is negative, this loads the entire volume!
     * \return true if loading process is started, false if the loader is currently busy. */
    public bool startLoading(DICOMSeries toLoad, int slice = 0)
    {
        if (!isBusy && toLoad != null)
        {
            // Lock:
            isBusy = true;

            seriesToLoad = toLoad;
            sliceToLoad  = slice;
            ThreadUtil t = new ThreadUtil(load, loadCallback);
            t.Run();

            // Let event system know what we're currently doing:
            PatientEventSystem.triggerEvent(PatientEventSystem.Event.LOADING_AddLoadingJob,
                                            "DICOM directory parsing");

            return(true);
        }
        else
        {
            return(false);
        }
    }
    /*! Searches the directoryToLoad for DICOMs. Called in thread!*/
    public void parseDirectory(object sender, DoWorkEventArgs e)
    {
        // Parse the directory and return the seriesUIDs of all the DICOM series:
        try {
            availableSeries.Clear();

            Debug.Log("[DICOM] Searching directory: " + directoryToLoad);
            VectorString series = ImageSeriesReader.GetGDCMSeriesIDs(directoryToLoad);
            if (series.Count > 0)
            {
                foreach (string s in series)
                {
                    DICOMSeries info = new DICOMSeries(directoryToLoad, s);
                    availableSeries.Add(info);
                }
            }

            // Debug log:
            string str = "[DICOM] Found " + series.Count + " series.";
            Debug.Log(str);
        } catch (System.Exception err) {
            Debug.LogError("Error while trying to parse DICOM directory: " + err.Message);
        }
    }
 /*! Starts loading the given DICOM volume, if available.
  * \return true if loading process is started, false if the loader is currently busy. */
 public bool startLoadingVolume(DICOMSeries toLoad)
 {
     return(startLoading(toLoad, -1));
 }
Ejemplo n.º 10
0
        public VolumeDataset ImportDICOMSeries(DICOMSeries series)
        {
            List <DICOMSliceFile> files = series.dicomFiles;

            // Check if the series is missing the slice location tag
            bool needsCalcLoc = false;

            foreach (DICOMSliceFile file in files)
            {
                needsCalcLoc |= file.missingLocation;
            }

            // Calculate slice location from "Image Position" (0020,0032)
            if (needsCalcLoc)
            {
                CalcSliceLocFromPos(files);
            }

            // Sort files by slice location
            files.Sort((DICOMSliceFile a, DICOMSliceFile b) => { return(a.location.CompareTo(b.location)); });

            Debug.Log($"Importing {files.Count} DICOM slices");

            if (files.Count <= 1)
            {
                Debug.LogError("Insufficient number of slices.");
                return(null);
            }

            // Create dataset
            VolumeDataset dataset = new VolumeDataset();

            dataset.datasetName = Path.GetFileName(datasetName);
            dataset.dimX        = files[0].file.PixelData.Columns;
            dataset.dimY        = files[0].file.PixelData.Rows;
            dataset.dimZ        = files.Count;

            int dimension = dataset.dimX * dataset.dimY * dataset.dimZ;

            dataset.data = new float[dimension];

            for (int iSlice = 0; iSlice < files.Count; iSlice++)
            {
                DICOMSliceFile slice     = files[iSlice];
                PixelData      pixelData = slice.file.PixelData;
                int[]          pixelArr  = ToPixelArray(pixelData);
                if (pixelArr == null) // This should not happen
                {
                    pixelArr = new int[pixelData.Rows * pixelData.Columns];
                }

                for (int iRow = 0; iRow < pixelData.Rows; iRow++)
                {
                    for (int iCol = 0; iCol < pixelData.Columns; iCol++)
                    {
                        int pixelIndex = (iRow * pixelData.Columns) + iCol;
                        int dataIndex  = (iSlice * pixelData.Columns * pixelData.Rows) + (iRow * pixelData.Columns) + iCol;

                        int   pixelValue      = pixelArr[pixelIndex];
                        float hounsfieldValue = pixelValue * slice.slope + slice.intercept;

                        dataset.data[dataIndex] = Mathf.Clamp(hounsfieldValue, -1024.0f, 3071.0f);
                    }
                }
            }

            if (files[0].pixelSpacing > 0.0f)
            {
                dataset.scaleX = files[0].pixelSpacing * dataset.dimX;
                dataset.scaleY = files[0].pixelSpacing * dataset.dimY;
                dataset.scaleZ = Mathf.Abs(files[files.Count - 1].location - files[0].location);
            }

            dataset.FixDimensions();

            return(dataset);
        }
Ejemplo n.º 11
0
 /*! Constructor, loads the DICOM image data from file.
  * The constructor starts the loading of pixel data from the files (filenames are
  * taken from the seriesInfo). If slice is zero or positive, only the single file
  * will be read. If slice is negative, the entire volume (i.e. all files - and thus
  * all slices) will be read.
  * \note Since the constructor does so much work, the Object should be created in
  *      a background thread and then passed to the main thread. */
 public DICOM(DICOMSeries seriesInfo)
 {
     // Remember, we will need it later:
     this.seriesInfo = seriesInfo;
 }
Ejemplo n.º 12
0
    public DICOM2D(DICOMSeries seriesInfo, int slice) : base(seriesInfo)
    {
        dimensions = 2;
        slice      = Mathf.Clamp(slice, 0, seriesInfo.filenames.Count - 1);
        this.slice = slice;

        VectorString fileNames = seriesInfo.filenames;

        // Read the DICOM image:
        image = SimpleITK.ReadImage(fileNames[slice]);
        loadImageData(image);

        VectorDouble o1 = image.GetOrigin();

        if (o1.Count < 3)
        {
            throw(new System.Exception("Invalid origins found in first image."));
        }
        origin = new Vector3((float)o1 [0], (float)o1 [1], (float)o1 [2]);

        // Load the direction cosines:
        // ITK stores the direction cosines in a matrix with row-major-ordering. The weird indexing is because
        // we need the first and second column (0,3,6 for X and 1,4,7 for Y)
        VectorDouble direction = image.GetDirection();

        if (direction.Count < 6)
        {
            throw(new System.Exception("Invalid direction cosines found in images."));
        }
        directionCosineX = new Vector3((float)direction [0], (float)direction [3], (float)direction [6]);
        directionCosineY = new Vector3((float)direction [1], (float)direction [4], (float)direction [7]);

        sliceNormal = Vector3.Cross(directionCosineX, directionCosineY);

        // Calculate which direction the normal is facing to determine the orienation (Transverse,
        // Coronal or Saggital).
        float absX = Mathf.Abs(sliceNormal.x);
        float absY = Mathf.Abs(sliceNormal.y);
        float absZ = Mathf.Abs(sliceNormal.z);

        if (absX > absY && absX > absZ)
        {
            sliceOrientation = SliceOrientation.Saggital;
        }
        else if (absY > absX && absY > absZ)
        {
            sliceOrientation = SliceOrientation.Coronal;
        }
        else if (absZ > absX && absZ > absY)
        {
            sliceOrientation = SliceOrientation.Transverse;
        }
        else
        {
            sliceOrientation = SliceOrientation.Unknown;
        }

        // Load the pixel spacing:
        // NOTE: It seems that the the first value is the spacing between rows (i.e. y direction),
        //		the second value is the spacing between columns (i.e. x direction).
        //		I was not able to verify this so far, since all test dicoms we had have the same spacing in
        //		x and y direction...
        VectorDouble spacing = image.GetSpacing();

        if (spacing.Count < 2)
        {
            throw(new System.Exception("Invalid pixel spacing found in images."));
        }
        pixelSpacing = new Vector2((float)spacing [1], (float)spacing [0]);

        // Generate the transformation matrices which can later be used to translate pixels to
        // 3D positions and vice versa.
        setupTransformationMatrices();
    }
Ejemplo n.º 13
0
    void eventNewDicomList(object obj = null)
    {
        eventClear();           // Clear the list

        // Make sure a patient is loaded:
        Patient p = Patient.getLoadedPatient();

        if (p == null)
        {
            Text newEntryText = ListEntry.transform.GetComponentInChildren <Text> ();
            newEntryText.text = "No patient loaded.";
            ListEntry.SetActive(true);
            return;
        }
        // Make sure at least one series was found:
        List <DICOMSeries> series = DICOMLoader.instance.availableSeries;

        if (series.Count <= 0)
        {
            Text newEntryText = ListEntry.transform.GetComponentInChildren <Text> ();
            newEntryText.text = "No series found.";
            ListEntry.SetActive(true);
            return;
        }

        // Deactivate default button:
        ListEntry.SetActive(false);

        foreach (DICOMSeries s in series)
        {
            //customNames.Add (p.getDICOMNameForSeriesUID (uid));
            GameObject newEntry = Instantiate(ListEntry) as GameObject;
            newEntry.SetActive(true);

            Text newEntryText = newEntry.transform.GetComponentInChildren <Text> ();
            newEntryText.text = s.getDescription();
            newEntry.transform.SetParent(ListEntry.transform.parent, false);

            // Keep a reference to this series:
            DICOMSeries captured = s;

            // Make the button load the DICOM:
            GameObject newButton = newEntry.transform.Find("EntryButton").gameObject;
            newButton.GetComponent <Button> ().onClick.AddListener(() => selectedNewDicom(captured));

            // Make the volume button display the DICOM as volume:
            GameObject volumetricButton = newEntry.transform.Find("VolumetricButton").gameObject;
            // For now, only allow Transverse volumes:
            if (captured.sliceOrientation == SliceOrientation.Transverse && captured.isConsecutiveVolume)
            {
                volumetricButton.GetComponent <Button> ().onClick.AddListener(() => selectedNewVolumetric(captured));
                volumetricButton.SetActive(true);
            }
            else
            {
                volumetricButton.SetActive(false);
            }
        }

        DicomImage.gameObject.SetActive(false);
    }