// Released under the OSGPL license, as part of the OpenSceneGraph distribution. // // ReaderWriter for sgi's .rgb format. // specification can be found at http://local.wasp.uwa.edu.au/~pbourke/dataformats/sgirgb/sgiversion.html #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_DCMTK #ifndef _WIN32 #define HAVE_CONFIG_H #endif #include #include #include #include #include #endif #ifdef USE_ITK #include #include #include #include #include #include #include #endif #include #include #include #include class ReaderWriterDICOM : public osgDB::ReaderWriter { public: ReaderWriterDICOM() { supportsExtension("mag","dicom image format"); supportsExtension("ph","dicom image format"); supportsExtension("ima","dicom image format"); supportsExtension("dic","dicom image format"); supportsExtension("dcm","dicom image format"); supportsExtension("dicom","dicom image format"); // supportsExtension("*","dicom image format"); } std::ostream& warning() const { return osg::notify(osg::WARN); } std::ostream& notice() const { return osg::notify(osg::NOTICE); } std::ostream& info() const { return osg::notify(osg::INFO); } template T* readData(std::istream& fin, unsigned int length, unsigned int& numComponents) const { numComponents = length/sizeof(T); T* data = new T[numComponents]; fin.read((char*)data, numComponents*sizeof(T)); // read over any padding length -= numComponents*sizeof(T); while(fin && length>0) { fin.get(); --length; } return data; } template void printData(std::ostream& out, T* data, unsigned int numComponents) const { if (sizeof(T)==1) { for(unsigned int i=0; i32) out< Files; bool getDicomFilesInDirectory(const std::string& path, Files& files) const { osgDB::DirectoryContents contents = osgDB::getDirectoryContents(path); std::sort(contents.begin(), contents.end()); for(osgDB::DirectoryContents::iterator itr = contents.begin(); itr != contents.end(); ++itr) { if ((*itr).empty()) continue; if ((*itr)[0]=='.') { osg::notify(osg::INFO)<<"Ignoring tempory file "<<*itr< tile = new osgVolume::VolumeTile; tile->setVolumeTechnique(new osgVolume::RayTracedTechnique()); osg::ref_ptr layer= new osgVolume::ImageLayer(result.getImage()); layer->rescaleToZeroToOneRange(); osgVolume::SwitchProperty* sp = new osgVolume::SwitchProperty; sp->setActiveProperty(0); float alphaFunc = 0.1f; osgVolume::AlphaFuncProperty* ap = new osgVolume::AlphaFuncProperty(alphaFunc); osgVolume::SampleDensityProperty* sd = new osgVolume::SampleDensityProperty(0.005); osgVolume::TransparencyProperty* tp = new osgVolume::TransparencyProperty(1.0); { // Standard osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty; cp->addProperty(ap); cp->addProperty(sd); cp->addProperty(tp); sp->addProperty(cp); } { // Light osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty; cp->addProperty(ap); cp->addProperty(sd); cp->addProperty(tp); cp->addProperty(new osgVolume::LightingProperty); sp->addProperty(cp); } { // Isosurface osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty; cp->addProperty(sd); cp->addProperty(tp); cp->addProperty(new osgVolume::IsoSurfaceProperty(alphaFunc)); sp->addProperty(cp); } { // MaximumIntensityProjection osgVolume::CompositeProperty* cp = new osgVolume::CompositeProperty; cp->addProperty(ap); cp->addProperty(sd); cp->addProperty(tp); cp->addProperty(new osgVolume::MaximumIntensityProjectionProperty); sp->addProperty(cp); } layer->addProperty(sp); tile->setLayer(layer.get()); // get matrix providing size of texels (in mm) osgVolume::ImageDetails* details = dynamic_cast(result.getImage()->getUserData()); osg::RefMatrix* matrix = details ? details->getMatrix() : 0; if (details) { layer->setTexelOffset(details->getTexelOffset()); layer->setTexelScale(details->getTexelScale()); } if (matrix) { osgVolume::Locator* locator = new osgVolume::Locator(*matrix); tile->setLocator(locator); layer->setLocator(locator); // result.getImage()->setUserData(0); info()<<"Locator "<<*matrix< > Images; Images images; for(Files::iterator itr = files.begin(); itr != files.end(); ++itr) { ReadResult result = readSingleITKImage(*itr, options); if (result.success()) images.push_back(result.getImage()); else return result; } if (images.empty()) return ReadResult::ERROR_IN_READING_FILE; if (images.size()==1) return images[0].get(); typedef std::map > DistanceImageMap; typedef std::map OrientationDistanceImageMap; OrientationDistanceImageMap orientationDistanceImageMap; for(Images::iterator itr = images.begin(); itr != images.end(); ++itr) { osg::Image* image = itr->get(); osgVolume::ImageDetails* details = dynamic_cast(image->getUserData()); osg::RefMatrix* matrix = details ? details->getMatrix() : 0; if (matrix) { osg::Vec3 p0 = osg::Vec3(0.0, 0.0, 0.0) * (*matrix); osg::Vec3 p1 = osg::Vec3(0.0, 0.0, 1.0) * (*matrix); osg::Vec3 direction = p1-p0; direction.normalize(); float distance = p0 * direction; info()<<" direction="<GetOutput(); ImageType::RegionType region = inputImage->GetBufferedRegion(); ImageType::SizeType size = region.GetSize(); ImageType::IndexType start = region.GetIndex(); //inputImage->GetSpacing(); //inputImage->GetOrigin(); unsigned int width = size[0]; unsigned int height = size[1]; unsigned int depth = size[2]; osg::RefMatrix* matrix = new osg::RefMatrix; info()<<"width = "< details = new osgVolume::ImageDetails; details->setMatrix(new osg::RefMatrix); osg::ref_ptr image; unsigned int imageNum = 0; EP_Representation pixelRep = EPR_Uint8; int numPlanes = 0; GLenum pixelFormat = 0; GLenum dataType = 0; unsigned int pixelSize = 0; typedef std::list FileInfoList; FileInfoList fileInfoList; typedef std::map DistanceFileInfoMap; typedef std::map OrientationFileInfoMap; OrientationFileInfoMap orientationFileInfoMap; unsigned int totalNumSlices = 0; for(Files::iterator itr = files.begin(); itr != files.end(); ++itr) { DcmFileFormat fileformat; OFCondition status = fileformat.loadFile((*itr).c_str()); if(!status.good()) return ReadResult::ERROR_IN_READING_FILE; FileInfo fileInfo; fileInfo.filename = *itr; double pixelSize_y = 1.0; double pixelSize_x = 1.0; double sliceThickness = 1.0; double imagePositionPatient[3] = {0, 0, 0}; double imageOrientationPatient[6] = {1.0, 0.0, 0.0, 0.0, 1.0, 0.0 }; Uint16 numOfSlices = 1; // code for reading the intercept and scale that is required to convert to Hounsfield units. bool rescaling = false; double rescaleIntercept = 0.0; double rescaleSlope = 1.0; const char *classUID = NULL; if (fileformat.getDataset()->findAndGetString(DCM_SOPClassUID, classUID).good()) { info()<<" classUID = "<findAndGetFloat64(DCM_RescaleIntercept, rescaleIntercept).good(); rescaling &= fileformat.getDataset()->findAndGetFloat64(DCM_RescaleSlope, rescaleSlope).good(); if (rescaling) { fileInfo.rescaleIntercept = rescaleIntercept; fileInfo.rescaleSlope = rescaleSlope; info()<<" rescaleIntercept = "<second; if (dfim.empty()) return 0; double totalDistance = 0.0; if (dfim.size()>1) { totalDistance = fabs(dfim.rbegin()->first - dfim.begin()->first); } else { totalDistance = dfim.begin()->second.sliceThickness * double(dfim.begin()->second.numSlices); } info()<<"Total Distance including ends "<second; std::auto_ptr dcmImage(new DicomImage(fileInfo.filename.c_str())); if (dcmImage.get()) { if (dcmImage->getStatus()==EIS_Normal) { // get the pixel data const DiPixel* pixelData = dcmImage->getInterData(); if(!pixelData) { warning()<<"Error: no data in DicomImage object."< imageAdapter = new osg::Image; EP_Representation curr_pixelRep; int curr_numPlanes; GLenum curr_pixelFormat; GLenum curr_dataType; unsigned int curr_pixelSize; // create the new image convertPixelTypes(pixelData, curr_pixelRep, curr_numPlanes, curr_dataType, curr_pixelFormat, curr_pixelSize); imageAdapter->setImage(dcmImage->getWidth(), dcmImage->getHeight(), dcmImage->getFrameCount(), curr_pixelFormat, curr_pixelFormat, curr_dataType, (unsigned char*)(pixelData->getData()), osg::Image::NO_DELETE); if (!image) { pixelRep = curr_pixelRep; numPlanes = curr_numPlanes; dataType = curr_dataType; pixelFormat = curr_pixelFormat; pixelSize = curr_pixelSize; osg::RefMatrix* matrix = details->getMatrix(); (*matrix)(0,0) = fileInfo.matrix(0,0); (*matrix)(1,0) = fileInfo.matrix(1,0); (*matrix)(2,0) = fileInfo.matrix(2,0); (*matrix)(0,1) = fileInfo.matrix(0,1); (*matrix)(1,1) = fileInfo.matrix(1,1); (*matrix)(2,1) = fileInfo.matrix(2,1); (*matrix)(0,2) = fileInfo.matrix(0,2) * averageThickness; (*matrix)(1,2) = fileInfo.matrix(1,2) * averageThickness; (*matrix)(2,2) = fileInfo.matrix(2,2) * averageThickness; // note from Robert Osfield, testing various dicom files I have found that the rescaleIntercept // for CT data doesn't look to be applicable as an straight value offset, so we'll ignore for now. // details->setTexelOffset(fileInfo.rescaleIntercept); double s = fileInfo.rescaleSlope; switch(dataType) { case(GL_BYTE): s *= 128.0; break; case(GL_UNSIGNED_BYTE): s *= 255.0; break; case(GL_SHORT): s *= 32768.0; break; case(GL_UNSIGNED_SHORT): s *= 65535.0; break; case(GL_INT): s *= 2147483648.0; break; case(GL_UNSIGNED_INT): s *= 4294967295.0; break; default: break; } details->setTexelScale(osg::Vec4(s,s,s,s)); image = new osg::Image; image->setUserData(details.get()); image->setFileName(fileName.c_str()); image->allocateImage(dcmImage->getWidth(), dcmImage->getHeight(), totalNumSlices, pixelFormat, dataType); matrix->preMult(osg::Matrix::scale(double(image->s()), double(image->t()), double(image->r()))); info()<<"Image dimensions = "<s()<<", "<t()<<", "<r()<<" pixelFormat=0x"<getPlanes()>numPlanes || pixelData->getRepresentation()>pixelRep) { info()<<"Need to reallocated "<s()<<", "<t()<<", "<r()< previous_image = image; // create the new image convertPixelTypes(pixelData, pixelRep, numPlanes, dataType, pixelFormat, pixelSize); image = new osg::Image; image->setUserData(previous_image->getUserData()); image->setFileName(fileName.c_str()); image->allocateImage(dcmImage->getWidth(), dcmImage->getHeight(), totalNumSlices, pixelFormat, dataType); osg::copyImage(previous_image.get(), 0,0,0, previous_image->s(), previous_image->t(), imageNum, image.get(), 0, 0, 0, false); } osg::copyImage(imageAdapter.get(), 0,0,0, imageAdapter->s(), imageAdapter->t(), imageAdapter->r(), image.get(), 0, 0, imageNum, false); imageNum += dcmImage->getFrameCount(); } else { warning()<<"Error in reading dicom file "<