Ejemplo n.º 1
0
/* Helper function to write a DICOM file using C++ */
static int write_dcm_cpp(const char *path, const Image *const im,
        const Dcm_meta *const meta, const float max_val) {

#define BUF_LEN 1024
        char buf[BUF_LEN];

        // Ensure the image is monochromatic
        if (im->nc != 1) {
                SIFT3D_ERR("write_dcm_cpp: image %s has %d channels. "
                        "Currently only signle-channel images are supported.\n",
                         path, im->nc);
                return SIFT3D_FAILURE;
        }

        // If no metadata was provided, initialize default metadata
        Dcm_meta meta_new;
        set_meta_defaults(meta, &meta_new);

        // Create a new fileformat object
        DcmFileFormat fileFormat;

        // Set the file type to derived
        DcmDataset *const dataset = fileFormat.getDataset();
        OFCondition status = dataset->putAndInsertString(DCM_ImageType, 
                                                         "DERIVED");
        if (status.bad()) {
                std::cerr << "write_dcm_cpp: Failed to set the image type" <<
                        std::endl;
                return SIFT3D_FAILURE;
        }

        // Set the class UID
        dataset->putAndInsertString(DCM_SOPClassUID, 
                UID_CTImageStorage);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the SOPClassUID\n");
                return SIFT3D_FAILURE;
        }

        // Set the photometric interpretation
        const char *photoInterp;
        if (im->nc == 1) {
                photoInterp = "MONOCHROME2";
        } else if (im->nc == 3) {
                photoInterp = "RGB";
        } else {
                SIFT3D_ERR("write_dcm_cpp: failed to determine the "
                        "photometric representation for %d channels \n", 
                        im->nc);
                return SIFT3D_FAILURE;
        }
        dataset->putAndInsertString(DCM_PhotometricInterpretation,
                photoInterp);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the photometric "
                        "interpretation \n");
                return SIFT3D_FAILURE;
        }

        // Set the pixel representation to unsigned
        dataset->putAndInsertUint16(DCM_PixelRepresentation, 0);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the pixel "
                        "representation \n");
                return SIFT3D_FAILURE;
        }

        // Set the number of channels (Samples Per Pixel) and set the planar
        // configuration to interlaced pixels
        assert(SIFT3D_IM_GET_IDX(im, 0, 0, 0, 1) == 
                SIFT3D_IM_GET_IDX(im, 0, 0, 0, 0) + 1);
        snprintf(buf, BUF_LEN, "%d", im->nc);
        dataset->putAndInsertString(DCM_SamplesPerPixel, buf);
        dataset->putAndInsertString(DCM_PlanarConfiguration, "0");

        // Set the bits allocated and stored, in big endian format 
        const unsigned int dcm_high_bit = dcm_bit_width - 1;
        dataset->putAndInsertUint16(DCM_BitsAllocated, dcm_bit_width);
        dataset->putAndInsertUint16(DCM_BitsStored, dcm_bit_width);
        dataset->putAndInsertUint16(DCM_HighBit, dcm_high_bit);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the bit widths \n");
                return SIFT3D_FAILURE;
        }

        // Set the patient name
        status = dataset->putAndInsertString(DCM_PatientName, 
                meta_new.patient_name);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the patient name\n");
                return SIFT3D_FAILURE;
        }

        // Set the patient ID
        status = dataset->putAndInsertString(DCM_PatientID,
                meta_new.patient_id);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the patient ID \n");
                return SIFT3D_FAILURE;
        }

        // Set the study UID
        status = dataset->putAndInsertString(DCM_StudyInstanceUID,
                meta_new.study_uid);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the "
                        "StudyInstanceUID \n");
                return SIFT3D_FAILURE;
        }

        // Set the series UID
        status = dataset->putAndInsertString(DCM_SeriesInstanceUID,
                meta_new.series_uid);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the "
                        "SeriesInstanceUID \n");
                return SIFT3D_FAILURE;
        }

        // Set the series description
        status = dataset->putAndInsertString(DCM_SeriesDescription,
                meta_new.series_descrip);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the series "
                        "description \n");
                return SIFT3D_FAILURE;
        }

        // Set the instance UID
        status = dataset->putAndInsertString(DCM_SOPInstanceUID, 
                meta_new.instance_uid);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: failed to set the "
                        "SOPInstanceUID \n");
                return SIFT3D_FAILURE;
        }

        // Set the dimensions
        OFCondition xstatus = dataset->putAndInsertUint16(DCM_Rows, im->ny); 
        OFCondition ystatus = dataset->putAndInsertUint16(DCM_Columns, im->nx);
        snprintf(buf, BUF_LEN, "%d", im->nz);
        OFCondition zstatus = dataset->putAndInsertString(DCM_NumberOfFrames,
                buf);
        if (xstatus.bad() || ystatus.bad() || zstatus.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the dimensions \n");
                return SIFT3D_FAILURE;
        }

        // Set the instance number
        snprintf(buf, BUF_LEN, "%u", meta_new.instance_num);
        status = dataset->putAndInsertString(DCM_InstanceNumber, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the instance "
                        "number \n");
                return SIFT3D_FAILURE;
        }

        // Set the ImagePositionPatient vector
        const double imPosX = static_cast<double>(im->nx - 1) * im->ux;
        const double imPosY = static_cast<double>(im->ny - 1) * im->uy;
        const double imPosZ = static_cast<double>(meta_new.instance_num) * 
                              im->uz;
        snprintf(buf, BUF_LEN, "%f\\%f\\%f", imPosX, imPosY, imPosZ);
        status = dataset->putAndInsertString(DCM_ImagePositionPatient, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the "
                        "ImagePositionPatient vector \n");
                return SIFT3D_FAILURE;
        }

        // Set the slice location
        snprintf(buf, BUF_LEN, "%f", imPosZ);
        status = dataset->putAndInsertString(DCM_SliceLocation, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the slice "
                        "location \n");
                return SIFT3D_FAILURE;
        }

        // Set the pixel spacing
        snprintf(buf, BUF_LEN, "%f\\%f", im->ux, im->uy);
        status = dataset->putAndInsertString(DCM_PixelSpacing, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the pixel "
                        "spacing \n");
                return SIFT3D_FAILURE;
        }

        // Set the aspect ratio
        snprintf(buf, BUF_LEN, "%f\\%f", im->ux, im->uy);
        status = dataset->putAndInsertString(DCM_PixelAspectRatio, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the pixel aspect "
                        "aspect ratio \n");
                return SIFT3D_FAILURE;
        }

        // Set the slice thickness
        snprintf(buf, BUF_LEN, "%f", im->uz);
        status = dataset->putAndInsertString(DCM_SliceThickness, buf);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: Failed to set the slice "
                                "thickness \n");
                return SIFT3D_FAILURE;
        }

        // Count the number of pixels in the image
        unsigned long numPixels = im->dims[0];
        for (int i = 1; i < IM_NDIMS; i++) {
                numPixels *= im->dims[i];
        }

        // Get the image scaling factor
        const float dcm_max_val = static_cast<float>(1 << dcm_bit_width) - 1.0f;
        const float im_max = max_val < 0.0f ? im_max_abs(im) : max_val;
        const float scale = im_max == 0.0f ? 1.0f : dcm_max_val / im_max;

        // Render the data to an 8-bit unsigned integer array
        assert(dcm_bit_width == 8);
        assert(fabsf(dcm_max_val - 255.0f) < FLT_EPSILON);
        uint8_t *pixelData = new uint8_t[numPixels];
        int x, y, z, c;
        SIFT3D_IM_LOOP_START_C(im, x, y, z, c)

                const float vox = SIFT3D_IM_GET_VOX(im, x, y, z, c);

                if (vox < 0.0f) {
                        SIFT3D_ERR("write_dcm_cpp: Image cannot be "
                                "negative \n");
                        return SIFT3D_FAILURE;
                }

                pixelData[c + x + y * im->nx + z * im->nx * im->ny] =
                        static_cast<uint8_t>(vox * scale);
        SIFT3D_IM_LOOP_END_C

        // Write the data
        status = dataset->putAndInsertUint8Array(DCM_PixelData, pixelData, 
                numPixels);
        delete[] pixelData;
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: failed to set the pixel data \n");
                return SIFT3D_FAILURE;
        }

        // Choose the encoding format
#if 0
        DJEncoderRegistration::registerCodecs();
        const E_TransferSyntax xfer = EXS_JPEGProcess14SV1TransferSyntax;
        DJ_RPLossless rp_lossless;
        status = dataset->chooseRepresentation(xfer, &rp_lossless);
#else
        const E_TransferSyntax xfer = EXS_LittleEndianExplicit;
        dataset->chooseRepresentation(xfer, NULL);
#endif
        if (!dataset->canWriteXfer(xfer)) {
                SIFT3D_ERR("write_dcm_cpp: Failed to choose the encoding "
                        "format \n");
                return SIFT3D_FAILURE;
        }

        // Force the media storage UIDs to be re-generated by removing them
        dataset->remove(DCM_MediaStorageSOPClassUID);
        dataset->remove(DCM_MediaStorageSOPInstanceUID);

        // Save the file
        status = fileFormat.saveFile(path, xfer);
        if (status.bad()) {
                SIFT3D_ERR("write_dcm_cpp: failed to write file %s (%s) \n",
                        path, status.text());
                return SIFT3D_FAILURE;
        }

        return SIFT3D_SUCCESS;
#undef BUF_LEN
}
Ejemplo n.º 2
0
/* Helper funciton to read a directory of DICOM files using C++ */
static int read_dcm_dir_cpp(const char *path, Image *const im) {

        struct stat st;
        DIR *dir;
        struct dirent *ent;
        int i, nx, ny, nz, nc, num_files, off_z;

        // Verify that the directory exists
	if (stat(path, &st)) {
                SIFT3D_ERR("read_dcm_dir_cpp: cannot find file %s \n", path);
                return SIFT3D_FAILURE;
	} else if (!S_ISDIR(st.st_mode)) {
                SIFT3D_ERR("read_dcm_dir_cpp: file %s is not a directory \n",
                        path);
                return SIFT3D_FAILURE;
	}

        // Open the directory
        if ((dir = opendir(path)) == NULL) {
                SIFT3D_ERR("read_dcm_dir_cpp: unexpected error opening "
                        "directory %s \n", path);
                return SIFT3D_FAILURE;
        }

        // Get all of the .dcm files in the directory
        std::vector<Dicom> dicoms;
        while ((ent = readdir(dir)) != NULL) {

                // Form the full file path
                std::string fullfile(std::string(path) + sepStr + ent->d_name);

                // Check if it is a DICOM file 
                if (im_get_format(fullfile.c_str()) != DICOM)
                        continue;

                // Read the file
                Dicom dicom(fullfile);
                if (!dicom.isValid()) {
                        closedir(dir);
                        return SIFT3D_FAILURE;
                }

                // Add the file to the list
                dicoms.push_back(dicom);
        }

        // Release the directory
        closedir(dir);
        
        // Get the number of files
        num_files = dicoms.size();

        // Verify that dicom files were found
        if (num_files == 0) {
                SIFT3D_ERR("read_dcm_dir_cpp: no DICOM files found in %s \n",
                        path);
                return SIFT3D_FAILURE;
        }

        // Check that the files are from the same series
        const Dicom &first = dicoms[0];
        for (int i = 1; i < num_files; i++) {

                const Dicom &dicom = dicoms[i];

                if (!first.eqSeries(dicom)) {
                        SIFT3D_ERR("read_dcm_dir_cpp: file %s is from a "
                                "different series than file %s \n", 
                                dicom.name().c_str(), first.name().c_str());
                        return SIFT3D_FAILURE;
                }
        }

        // Initialize the output dimensions
        nx = first.getNx();
        ny = first.getNy();
        nc = first.getNc();

        // Verify the dimensions of the other files, counting the total
        // series z-dimension
        nz = 0;
        for (i = 0; i < num_files; i++) {

                // Get a slice
                const Dicom &dicom = dicoms[i];        

                // Verify the dimensions
                if (dicom.getNx() != nx || dicom.getNy() != ny || 
                        dicom.getNc() != nc) {
                        SIFT3D_ERR("read_dcm_dir_cpp: slice %s "
                                "(%d, %d, %d) does not match the "
                                "dimensions of slice %s (%d, %d, %d) \n",
                                dicom.name().c_str(), dicom.getNx(), 
                                dicom.getNy(), dicom.getNc(), 
                                first.name().c_str(), nx, ny, nc);
                        return SIFT3D_FAILURE;
                }

                // Count the z-dimension
                nz += dicom.getNz();
        }

        // Resize the output
        im->nx = nx;
        im->ny = ny;
        im->nz = nz;
        im->nc = nc;
        im->ux = first.getUx(); 
        im->uy = first.getUy();
        im->uz = first.getUz();
        im_default_stride(im);
        if (im_resize(im))
                return SIFT3D_FAILURE;

        // Sort the slices by z position
        std::sort(dicoms.begin(), dicoms.end()); 

        // Allocate a temporary image for the slices
        Image slice;
        init_im(&slice);

        // Read the image data
        off_z = 0;
        for (i = 0; i < num_files; i++) {

                int x, y, z, c;

                const char *slicename = dicoms[i].name().c_str();

                // Read the slice 
                if (read_dcm(slicename, &slice)) {
                        im_free(&slice);
                        return SIFT3D_FAILURE;
                }

                // Copy the data to the volume
                SIFT3D_IM_LOOP_START_C(&slice, x, y, z, c)

                        SIFT3D_IM_GET_VOX(im, x, y, z + off_z, c) =
                                SIFT3D_IM_GET_VOX(&slice, x, y, z, c);

                SIFT3D_IM_LOOP_END_C

                off_z += slice.nz;
        }
        assert(off_z == nz);
        im_free(&slice);

        return SIFT3D_SUCCESS;
} 
Ejemplo n.º 3
0
/* Load the data from a DICOM file */
Dicom::Dicom(std::string path) : filename(path), valid(false) {

        // Load the image as a DcmFileFormat 
        DcmFileFormat fileFormat;
        OFCondition status = fileFormat.loadFile(path.c_str());
        if (status.bad()) {
                SIFT3D_ERR("Dicom.Dicom: failed to read DICOM file %s (%s)\n",
                        path.c_str(), status.text());
                return;
        }

        // Get the dataset
        DcmDataset *const data = fileFormat.getDataset();

        // Get the series UID 
        const char *seriesUIDStr;
        status = data->findAndGetString(DCM_SeriesInstanceUID, seriesUIDStr);
        if (status.bad() || seriesUIDStr == NULL) {
                SIFT3D_ERR("Dicom.Dicom: failed to get SeriesInstanceUID "
                        "from file %s (%s)\n", path.c_str(), status.text());
                return;
        }
        seriesUID = std::string(seriesUIDStr); 

#if 0
        // Read the patient position
        const char *patientPosStr;
        status = data->findAndGetString(DCM_PatientPosition, patientPosStr);
        if (status.bad() || patientPosStr == NULL) {
                std::cerr << "Dicom.Dicom: failed to get PatientPosition " <<
                        "from file " << path << " (" << status.text() << ")" <<
                        std::endl;
                return;
        }

        // Interpret the patient position to give the sign of the z axis
        double zSign;
        switch (patientPosStr[0]) {
        case 'H':
                zSign = -1.0;
                break;
        case 'F':
                zSign = 1.0;
                break;
        default:
                std::cerr << "Dicom.Dicom: unrecognized patient position: " <<
                        patientPosStr << std::endl;
                return;
        }
#else
        //TODO: Is this needed?
        const double zSign = 1.0;
#endif

        // Read the image position patient vector
        const char *imPosPatientStr;
        status = data->findAndGetString(DCM_ImagePositionPatient, 
                imPosPatientStr);
        if (status.bad() || imPosPatientStr == NULL) {
                SIFT3D_ERR("Dicom.Dicom: failed to get ImagePositionPatient "
                        "from file %s (%s)\n", path.c_str(), status.text());
                return;
        }

        // Parse the image position patient vector to get the z coordinate
        double imPosZ;
        if (sscanf(imPosPatientStr, "%*f\\%*f\\%lf", &imPosZ) != 1) {
                SIFT3D_ERR("Dicom.Dicom: failed to parse "
                        "ImagePositionPatient tag %s from file %s\n", 
                        imPosPatientStr, path.c_str());
                return;
        }

        // Compute the z-location of the upper-left corner, in feet-first 
        // coordinates
        z = zSign * imPosZ;

        // Load the DicomImage object
        DicomImage dicomImage(path.c_str());
        if (dicomImage.getStatus() != EIS_Normal) {
                SIFT3D_ERR("Dicom.Dicom: failed to open image %s (%s)\n",
                        path.c_str(), 
                        DicomImage::getString(dicomImage.getStatus()));
                return;
        }

        // Check for color images
        if (!dicomImage.isMonochrome()) {
                SIFT3D_ERR("Dicom.Dicom: reading of color DICOM images is "
                        "not supported at this time \n");
                return;
        }
        nc = 1;

        // Read the dimensions
        nx = dicomImage.getWidth();
        ny = dicomImage.getHeight();
        nz = dicomImage.getFrameCount();
        if (nx < 1 || ny < 1 || nz < 1) {
                SIFT3D_ERR("Dicom.Dicom: invalid dimensions for file %s "
                        "(%d, %d, %d)\n", path.c_str(), nx, ny, nz);
                return;
        }

        // Read the pixel spacing
        const char *pixelSpacingStr;
        status = data->findAndGetString(DCM_PixelSpacing, pixelSpacingStr);
        if (status.bad()) {
                SIFT3D_ERR("Dicom.Dicom: failed to get pixel spacing from "
                        "file %s (%s)\n", path.c_str(), status.text());
                return;
        }
        if (sscanf(pixelSpacingStr, "%lf\\%lf", &ux, &uy) != 2) {
                SIFT3D_ERR("Dicom.Dicom: unable to parse pixel spacing from "
                        "file %s \n", path.c_str());
                return;
        }
        if (ux <= 0.0 || uy <= 0.0) {
                SIFT3D_ERR("Dicom.Dicom: file %s has invalid pixel spacing "
                        "[%f, %f]\n", path.c_str(), ux, uy);
                return;
        }

        // Read the slice thickness 
        Float64 sliceThickness;
        status = data->findAndGetFloat64(DCM_SliceThickness, sliceThickness);
        if (!status.good()) {
                SIFT3D_ERR("Dicom.Dicom: failed to get slice thickness from "
                        "file %s (%s)\n", path.c_str(), status.text());
                return;
        }

        // Convert to double 
        uz = sliceThickness;
        if (uz <= 0.0) {
                SIFT3D_ERR("Dicom.Dicom: file %s has invalid slice "
                        "thickness: %f \n", path.c_str(), uz);
                return;
        }
        
        // Set the window 
        dicomImage.setMinMaxWindow();

        valid = true;
}
Ejemplo n.º 4
0
/* Helper function to read a DICOM file using C++ */
static int read_dcm_cpp(const char *path, Image *const im) {

        // Read the image metadata
        Dicom dicom(path);
        if (!dicom.isValid())
                return SIFT3D_FAILURE;

        // Load the DicomImage object
        DicomImage dicomImage(path);
        if (dicomImage.getStatus() != EIS_Normal) {
                SIFT3D_ERR("read_dcm_cpp: failed to open image %s (%s)\n",
                        path, DicomImage::getString(dicomImage.getStatus()));
                return SIFT3D_FAILURE;
        }

        // Initialize the image fields
        im->nx = dicom.getNx();
        im->ny = dicom.getNy();
        im->nz = dicom.getNz();
        im->nc = dicom.getNc();
        im->ux = dicom.getUx();
        im->uy = dicom.getUy();
        im->uz = dicom.getUz();

        // Resize the output
        im_default_stride(im);
        if (im_resize(im))
                return SIFT3D_FAILURE;

        // Get the bit depth of the image
        const int bufNBits = 32;
        const int depth = dicomImage.getDepth();
        if (depth > bufNBits) {
                SIFT3D_ERR("read_dcm_cpp: buffer is insufficiently wide "
                        "for %d-bit data of image %s \n", depth, path);
                return SIFT3D_FAILURE;
        }

        // Get the number of bits by which we need to shift the 32-bit data, to
        // recover the original resolution (DICOM uses Big-endian encoding)
        const uint32_t shift = isLittleEndian() ? 
                static_cast<uint32_t>(bufNBits - depth) : 0;

        // Read each frame
        for (int i = 0; i < im->nz; i++) { 

                // Get a pointer to the data, rendered as a 32-bit int
                const uint32_t *const frameData = 
                        static_cast<const uint32_t *const>(
                                dicomImage.getOutputData(
                                        static_cast<int>(bufNBits), i));
                if (frameData == NULL) {
                        SIFT3D_ERR("read_dcm_cpp: could not get data from "
                                "image %s frame %d (%s)\n", path, i, 
                                DicomImage::getString(dicomImage.getStatus()));
                        return SIFT3D_FAILURE;
                }

                // Copy the frame
                const int x_start = 0;
                const int y_start = 0;
                const int z_start = i;
                const int x_end = im->nx - 1;
                const int y_end = im->ny - 1;
                const int z_end = z_start;
                int x, y, z;
                SIFT3D_IM_LOOP_LIMITED_START(im, x, y, z, x_start, x_end,
                        y_start, y_end, z_start, z_end)

                        // Get the voxel and shift it to match the original
                        // magnitude 
                        const uint32_t vox =
                                frameData[x + y * im->nx] >> shift;

                        // Convert to float and write to the output image
                        SIFT3D_IM_GET_VOX(im, x, y, z, 0) =
                                static_cast<float>(vox);

                SIFT3D_IM_LOOP_END
        }

        return SIFT3D_SUCCESS;
}
Ejemplo n.º 5
0
/* Print an error message */
static void err_msg(const char *msg) {
        SIFT3D_ERR("kpSift3D: %s \n"
                "Use \"kpSift3D --help\" for more information. \n", msg);
}