diff --git a/Core/Code/IO/mitkDicomSeriesReader.cpp b/Core/Code/IO/mitkDicomSeriesReader.cpp index 6863c7a488..daac5f331e 100644 --- a/Core/Code/IO/mitkDicomSeriesReader.cpp +++ b/Core/Code/IO/mitkDicomSeriesReader.cpp @@ -1,1364 +1,1361 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // uncomment for learning more about the internal sorting mechanisms //#define MBILOG_ENABLE_DEBUG #include #include #include #include #include #include #include #include "mitkProperties.h" namespace mitk { typedef itk::GDCMSeriesFileNames DcmFileNamesGeneratorType; DicomSeriesReader::SliceGroupingAnalysisResult::SliceGroupingAnalysisResult() :m_GantryTilt(false) { } DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetBlockFilenames() { return m_GroupedFiles; } DicomSeriesReader::StringContainer DicomSeriesReader::SliceGroupingAnalysisResult::GetUnsortedFilenames() { return m_UnsortedFiles; } bool DicomSeriesReader::SliceGroupingAnalysisResult::ContainsGantryTilt() { return m_GantryTilt; } void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToSortedBlock(const std::string& filename) { m_GroupedFiles.push_back( filename ); } void DicomSeriesReader::SliceGroupingAnalysisResult::AddFileToUnsortedBlock(const std::string& filename) { m_UnsortedFiles.push_back( filename ); } void DicomSeriesReader::SliceGroupingAnalysisResult::FlagGantryTilt() { m_GantryTilt = true; } void DicomSeriesReader::SliceGroupingAnalysisResult::UndoPrematureGrouping() { assert( !m_GroupedFiles.empty() ); m_UnsortedFiles.insert( m_UnsortedFiles.begin(), m_GroupedFiles.back() ); m_GroupedFiles.pop_back(); } const DicomSeriesReader::TagToPropertyMapType& DicomSeriesReader::GetDICOMTagsToMITKPropertyMap() { static bool initialized = false; static TagToPropertyMapType dictionary; if (!initialized) { /* Selection criteria: - no sequences because we cannot represent that - nothing animal related (specied, breed registration number), MITK focusses on human medical image processing. - only general attributes so far When extending this, we should make use of a real dictionary (GDCM/DCMTK and lookup the tag names there) */ // Patient module dictionary["0010|0010"] = "dicom.patient.PatientsName"; dictionary["0010|0020"] = "dicom.patient.PatientID"; dictionary["0010|0030"] = "dicom.patient.PatientsBirthDate"; dictionary["0010|0040"] = "dicom.patient.PatientsSex"; dictionary["0010|0032"] = "dicom.patient.PatientsBirthTime"; dictionary["0010|1000"] = "dicom.patient.OtherPatientIDs"; dictionary["0010|1001"] = "dicom.patient.OtherPatientNames"; dictionary["0010|2160"] = "dicom.patient.EthnicGroup"; dictionary["0010|4000"] = "dicom.patient.PatientComments"; dictionary["0012|0062"] = "dicom.patient.PatientIdentityRemoved"; dictionary["0012|0063"] = "dicom.patient.DeIdentificationMethod"; // General Study module dictionary["0020|000d"] = "dicom.study.StudyInstanceUID"; dictionary["0008|0020"] = "dicom.study.StudyDate"; dictionary["0008|0030"] = "dicom.study.StudyTime"; dictionary["0008|0090"] = "dicom.study.ReferringPhysiciansName"; dictionary["0020|0010"] = "dicom.study.StudyID"; dictionary["0008|0050"] = "dicom.study.AccessionNumber"; dictionary["0008|1030"] = "dicom.study.StudyDescription"; dictionary["0008|1048"] = "dicom.study.PhysiciansOfRecord"; dictionary["0008|1060"] = "dicom.study.NameOfPhysicianReadingStudy"; // General Series module dictionary["0008|0060"] = "dicom.series.Modality"; dictionary["0020|000e"] = "dicom.series.SeriesInstanceUID"; dictionary["0020|0011"] = "dicom.series.SeriesNumber"; dictionary["0020|0060"] = "dicom.series.Laterality"; dictionary["0008|0021"] = "dicom.series.SeriesDate"; dictionary["0008|0031"] = "dicom.series.SeriesTime"; dictionary["0008|1050"] = "dicom.series.PerformingPhysiciansName"; dictionary["0018|1030"] = "dicom.series.ProtocolName"; dictionary["0008|103e"] = "dicom.series.SeriesDescription"; dictionary["0008|1070"] = "dicom.series.OperatorsName"; dictionary["0018|0015"] = "dicom.series.BodyPartExamined"; dictionary["0018|5100"] = "dicom.series.PatientPosition"; dictionary["0028|0108"] = "dicom.series.SmallestPixelValueInSeries"; dictionary["0028|0109"] = "dicom.series.LargestPixelValueInSeries"; // VOI LUT module dictionary["0028|1050"] = "dicom.voilut.WindowCenter"; dictionary["0028|1051"] = "dicom.voilut.WindowWidth"; dictionary["0028|1055"] = "dicom.voilut.WindowCenterAndWidthExplanation"; initialized = true; } return dictionary; } DataNode::Pointer DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback) { DataNode::Pointer node = DataNode::New(); if (DicomSeriesReader::LoadDicomSeries(filenames, *node, sort, check_4d, correctTilt, callback)) { if( filenames.empty() ) { return NULL; } return node; } else { return NULL; } } bool DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback) { if( filenames.empty() ) { MITK_WARN << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; node.SetData(NULL); return true; // this is not actually an error but the result is very simple } DcmIoType::Pointer io = DcmIoType::New(); try { if (io->CanReadFile(filenames.front().c_str())) { io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); switch (io->GetComponentType()) { case DcmIoType::UCHAR: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::CHAR: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::USHORT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::SHORT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::UINT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::INT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::ULONG: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::LONG: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::FLOAT: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; case DcmIoType::DOUBLE: DicomSeriesReader::LoadDicom(filenames, node, sort, check_4d, correctTilt, callback); break; default: MITK_ERROR << "Found unsupported DICOM pixel type: (enum value) " << io->GetComponentType(); } if (node.GetData()) { return true; } } } catch(itk::MemoryAllocationError& e) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch(std::exception& e) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch(...) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return false; } bool DicomSeriesReader::IsDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); return io->CanReadFile(filename.c_str()); } bool DicomSeriesReader::IsPhilips3DDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); if (io->CanReadFile(filename.c_str())) { //Look at header Tag 3001,0010 if it is "Philips3D" gdcm::Reader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); if (data_set.FindDataElement(gdcm::Tag(0x3001, 0x0010)) && (sf.ToString(gdcm::Tag(0x3001, 0x0010)) == "Philips3D ")) { return true; } } return false; } bool DicomSeriesReader::ReadPhilips3DDicom(const std::string &filename, mitk::Image::Pointer output_image) { // Now get PhilipsSpecific Tags gdcm::PixmapReader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); gdcm::Attribute<0x0028,0x0011> dimTagX; // coloumns || sagittal gdcm::Attribute<0x3001,0x1001, gdcm::VR::UL, gdcm::VM::VM1> dimTagZ; //I have no idea what is VM1. // (Philips specific) // transversal gdcm::Attribute<0x0028,0x0010> dimTagY; // rows || coronal gdcm::Attribute<0x0028,0x0008> dimTagT; // how many frames gdcm::Attribute<0x0018,0x602c> spaceTagX; // Spacing in X , unit is "physicalTagx" (usually centimeter) gdcm::Attribute<0x0018,0x602e> spaceTagY; gdcm::Attribute<0x3001,0x1003, gdcm::VR::FD, gdcm::VM::VM1> spaceTagZ; // (Philips specific) gdcm::Attribute<0x0018,0x6024> physicalTagX; // if 3, then spacing params are centimeter gdcm::Attribute<0x0018,0x6026> physicalTagY; gdcm::Attribute<0x3001,0x1002, gdcm::VR::US, gdcm::VM::VM1> physicalTagZ; // (Philips specific) dimTagX.Set(data_set); dimTagY.Set(data_set); dimTagZ.Set(data_set); dimTagT.Set(data_set); spaceTagX.Set(data_set); spaceTagY.Set(data_set); spaceTagZ.Set(data_set); physicalTagX.Set(data_set); physicalTagY.Set(data_set); physicalTagZ.Set(data_set); unsigned int dimX = dimTagX.GetValue(), dimY = dimTagY.GetValue(), dimZ = dimTagZ.GetValue(), dimT = dimTagT.GetValue(), physicalX = physicalTagX.GetValue(), physicalY = physicalTagY.GetValue(), physicalZ = physicalTagZ.GetValue(); float spaceX = spaceTagX.GetValue(), spaceY = spaceTagY.GetValue(), spaceZ = spaceTagZ.GetValue(); if (physicalX == 3) // spacing parameter in cm, have to convert it to mm. spaceX = spaceX * 10; if (physicalY == 3) // spacing parameter in cm, have to convert it to mm. spaceY = spaceY * 10; if (physicalZ == 3) // spacing parameter in cm, have to convert it to mm. spaceZ = spaceZ * 10; // Ok, got all necessary Tags! // Now read Pixeldata (7fe0,0010) X x Y x Z x T Elements const gdcm::Pixmap &pixels = reader.GetPixmap(); gdcm::RAWCodec codec; codec.SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2); codec.SetPixelFormat(pixels.GetPixelFormat()); codec.SetPlanarConfiguration(0); gdcm::DataElement out; codec.Decode(data_set.GetDataElement(gdcm::Tag(0x7fe0, 0x0010)), out); const gdcm::ByteValue *bv = out.GetByteValue(); const char *new_pixels = bv->GetPointer(); // Create MITK Image + Geometry typedef itk::Image ImageType; //Pixeltype might be different sometimes? Maybe read it out from header ImageType::RegionType myRegion; ImageType::SizeType mySize; ImageType::IndexType myIndex; ImageType::SpacingType mySpacing; ImageType::Pointer imageItk = ImageType::New(); mySpacing[0] = spaceX; mySpacing[1] = spaceY; mySpacing[2] = spaceZ; mySpacing[3] = 1; myIndex[0] = 0; myIndex[1] = 0; myIndex[2] = 0; myIndex[3] = 0; mySize[0] = dimX; mySize[1] = dimY; mySize[2] = dimZ; mySize[3] = dimT; myRegion.SetSize( mySize); myRegion.SetIndex( myIndex ); imageItk->SetSpacing(mySpacing); imageItk->SetRegions( myRegion); imageItk->Allocate(); imageItk->FillBuffer(0); itk::ImageRegionIterator iterator(imageItk, imageItk->GetLargestPossibleRegion()); iterator.GoToBegin(); unsigned long pixCount = 0; unsigned long planeSize = dimX*dimY; unsigned long planeCount = 0; unsigned long timeCount = 0; unsigned long numberOfSlices = dimZ; while (!iterator.IsAtEnd()) { unsigned long adressedPixel = pixCount + (numberOfSlices-1-planeCount)*planeSize // add offset to adress the first pixel of current plane + timeCount*numberOfSlices*planeSize; // add time offset iterator.Set( new_pixels[ adressedPixel ] ); pixCount++; ++iterator; if (pixCount == planeSize) { pixCount = 0; planeCount++; } if (planeCount == numberOfSlices) { planeCount = 0; timeCount++; } if (timeCount == dimT) { break; } } mitk::CastToMitkImage(imageItk, output_image); return true; // actually never returns false yet.. but exception possible } DicomSeriesReader::GantryTiltInformation::GantryTiltInformation() : m_ShiftUp(0.0) , m_ShiftRight(0.0) , m_ShiftNormal(0.0) , m_NumberOfSlicesApart(1) { } DicomSeriesReader::GantryTiltInformation::GantryTiltInformation( const Point3D& origin1, const Point3D& origin2, const Vector3D& right, const Vector3D& up, unsigned int numberOfSlicesApart) : m_ShiftUp(0.0) , m_ShiftRight(0.0) , m_ShiftNormal(0.0) , m_NumberOfSlicesApart(numberOfSlicesApart) { assert(numberOfSlicesApart); // determine if slice 1 (imagePosition1 and imageOrientation1) and slice 2 can be in one orthogonal slice stack: // calculate a line from origin 1, directed along the normal of slice (calculated as the cross product of orientation 1) // check if this line passes through origin 2 /* Determine if line (imagePosition2 + l * normal) contains imagePosition1. Done by calculating the distance of imagePosition1 from line (imagePosition2 + l *normal) E.g. http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html squared distance = | (pointAlongNormal - origin2) x (origin2 - origin1) | ^ 2 / |pointAlongNormal - origin2| ^ 2 ( x meaning the cross product ) */ Vector3D normal = itk::CrossProduct(right, up); Point3D pointAlongNormal = origin2 + normal; double numerator = itk::CrossProduct( pointAlongNormal - origin2 , origin2 - origin1 ).GetSquaredNorm(); double denominator = (pointAlongNormal - origin2).GetSquaredNorm(); double distance = sqrt(numerator / denominator); if ( distance > 0.001 ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { MITK_DEBUG << " Series seems to contain a tilted (or sheared) geometry"; MITK_DEBUG << " Distance of expected slice origin from actual slice origin: " << distance; MITK_DEBUG << " ==> storing this shift for later analysis:"; MITK_DEBUG << " v right: " << right; MITK_DEBUG << " v up: " << up; MITK_DEBUG << " v normal: " << normal; Point3D projectionRight = projectPointOnLine( origin1, origin2, right ); Point3D projectionUp = projectPointOnLine( origin1, origin2, up ); Point3D projectionNormal = projectPointOnLine( origin1, origin2, normal ); m_ShiftRight = (projectionRight - origin2).GetNorm(); m_ShiftUp = (projectionUp - origin2).GetNorm(); m_ShiftNormal = (projectionNormal - origin2).GetNorm(); MITK_DEBUG << " shift normal: " << m_ShiftNormal; MITK_DEBUG << " shift up: " << m_ShiftUp; MITK_DEBUG << " shift right: " << m_ShiftRight; MITK_DEBUG << " tilt angle (rad): " << tanh( m_ShiftUp / m_ShiftNormal ); MITK_DEBUG << " tilt angle (deg): " << tanh( m_ShiftUp / m_ShiftNormal ) * 180.0 / 3.1415926535; } } Point3D DicomSeriesReader::GantryTiltInformation::projectPointOnLine( Point3D p, Point3D lineOrigin, Vector3D lineDirection ) { /** See illustration at http://mo.mathematik.uni-stuttgart.de/inhalt/aussage/aussage472/ vector(lineOrigin,p) = normal * ( innerproduct((p - lineOrigin),normal) / squared-length(normal) ) */ Vector3D lineOriginToP = p - lineOrigin; ScalarType innerProduct = lineOriginToP * lineDirection; ScalarType factor = innerProduct / lineDirection.GetSquaredNorm(); Point3D projection = lineOrigin + factor * lineDirection; return projection; } ScalarType DicomSeriesReader::GantryTiltInformation::GetTiltCorrectedAdditionalSize() const { // this seems to be a bit too much sometimes, but better too much than cutting off parts of the image return int(m_ShiftUp + 1.0); // to next bigger int: plus 1, then cut off after point } ScalarType DicomSeriesReader::GantryTiltInformation::GetMatrixCoefficientForCorrectionInWorldCoordinates() const { // so many mm shifted per slice! return m_ShiftUp / static_cast(m_NumberOfSlicesApart); } ScalarType DicomSeriesReader::GantryTiltInformation::GetRealZSpacing() const { return m_ShiftNormal / static_cast(m_NumberOfSlicesApart); } bool DicomSeriesReader::GantryTiltInformation::IsSheared() const { return ( m_ShiftRight > 0.001 || m_ShiftUp > 0.001); } bool DicomSeriesReader::GantryTiltInformation::IsRegularGantryTilt() const { return ( m_ShiftRight < 0.001 && m_ShiftUp > 0.001); } std::string DicomSeriesReader::ConstCharStarToString(const char* s) { return s ? std::string(s) : std::string(); } Point3D DicomSeriesReader::DICOMStringToPoint3D(const std::string& s, bool& successful) { Point3D p; successful = true; std::istringstream originReader(s); std::string coordinate; unsigned int dim(0); - while( std::getline( originReader, coordinate, '\\' ) ) + while( std::getline( originReader, coordinate, '\\' ) && dim < 3) { - if (dim > 4) break; // otherwise we access invalid array index - - p[dim++] = atof(coordinate.c_str()); + p[dim++]= atof(coordinate.c_str()); } if (dim != 3) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << " instead of 3 values."; } return p; } void DicomSeriesReader::DICOMStringToOrientationVectors(const std::string& s, Vector3D& right, Vector3D& up, bool& successful) { successful = true; std::istringstream orientationReader(s); std::string coordinate; unsigned int dim(0); - while( std::getline( orientationReader, coordinate, '\\' ) ) + while( std::getline( orientationReader, coordinate, '\\' ) && dim < 6 ) { - if (dim > 6) break; // otherwise we access invalid array index - if (dim<3) { right[dim++] = atof(coordinate.c_str()); } else { up[dim++ - 3] = atof(coordinate.c_str()); } } if (dim != 6) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << " instead of 6 values."; } } DicomSeriesReader::SliceGroupingAnalysisResult DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const StringContainer& files, bool groupImagesWithGantryTilt, const gdcm::Scanner::MappingType& tagValueMappings_) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption() again SliceGroupingAnalysisResult result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; + thisOrigin.Fill(0.0f); Point3D lastOrigin; lastOrigin.Fill(0.0f); Point3D lastDifferentOrigin; lastDifferentOrigin.Fill(0.0f); bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")"; unsigned int fileIndex(0); for (StringContainer::const_iterator fileIter = files.begin(); fileIter != files.end(); ++fileIter, ++fileIndex) { bool fileFitsIntoPattern(false); std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that thisOriginString = ConstCharStarToString( tagValueMappings[fileIter->c_str()][tagImagePositionPatient] ); bool ignoredConversionError(-42); // hard to get here, no graceful way to react thisOrigin = DICOMStringToPoint3D( thisOriginString, ignoredConversionError ); MITK_DEBUG << " " << fileIndex << " " << *fileIter << " at " << thisOriginString << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if ( lastOriginInitialized && (thisOrigin == lastOrigin) ) { MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position result.AddFileToUnsortedBlock( *fileIter ); fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; // Here we calculate if this slice and the previous one are well aligned, // i.e. we test if the previous origin is on a line through the current // origin, directed into the normal direction of the current slice. // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, // which cannot be simply loaded into a single mitk::Image at the moment. // For this case, we flag this finding in the result and DicomSeriesReader // can correct for that later. Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors( tagValueMappings[fileIter->c_str()][tagImageOrientation], right, up, ignoredConversionError ); GantryTiltInformation tiltInfo( lastDifferentOrigin, thisOrigin, right, up, 1 ); if ( tiltInfo.IsSheared() ) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { /* optimistic approach, accepting gantry tilt: save file for later, check all further files */ // at this point we have TWO slices analyzed! if they are the only two files, we still split, because there is no third to verify our tilting assumption. // later with a third being available, we must check if the initial tilting vector is still valid. if yes, continue. // if NO, we need to split the already sorted part (result.first) and the currently analyzed file (*fileIter) // tell apart gantry tilt from overall skewedness // sort out irregularly sheared slices, that IS NOT tilting if ( groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt() ) { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } else { result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } } else { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction if (norm > toleratedError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 /* Optimistic approach: check if any of the remaining slices fits in */ result.AddFileToUnsortedBlock( *fileIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } // record current origin for reference in later iterations if ( !lastOriginInitialized || ( fileFitsIntoPattern && (thisOrigin != lastOrigin) ) ) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } if ( result.ContainsGantryTilt() ) { // check here how many files were grouped. // IF it was only two files AND we assume tiltedness (e.g. save "distance") // THEN we would want to also split the two previous files (simple) because // we don't have any reason to assume they belong together if ( result.GetBlockFilenames().size() == 2 ) { result.UndoPrematureGrouping(); } } return result; } DicomSeriesReader::UidFileNamesMap DicomSeriesReader::GetSeries(const StringContainer& files, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { return GetSeries(files, true, groupImagesWithGantryTilt, restrictions); } DicomSeriesReader::UidFileNamesMap DicomSeriesReader::GetSeries(const StringContainer& files, bool sortTo3DPlust, bool groupImagesWithGantryTilt, const StringContainer& /*restrictions*/) { /** assumption about this method: returns a map of uid-like-key --> list(filename) each entry should contain filenames that have images of same - series instance uid (automatically done by GDCMSeriesFileNames - 0020,0037 image orientation (patient) - 0028,0030 pixel spacing (x,y) - 0018,0050 slice thickness */ UidFileNamesMap groupsOfSimilarImages; // preliminary result, refined into the final result mapOf3DPlusTBlocks // use GDCM directly, itk::GDCMSeriesFileNames does not work with GDCM 2 // PART I: scan files for sorting relevant DICOM tags, // separate images that differ in any of those // attributes (they cannot possibly form a 3D block) // scan for relevant tags in dicom files gdcm::Scanner scanner; const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID scanner.AddTag( tagSeriesInstanceUID ); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation scanner.AddTag( tagImageOrientation ); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing scanner.AddTag( tagPixelSpacing ); const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness scanner.AddTag( tagSliceThickness ); const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows scanner.AddTag( tagNumberOfRows ); const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols scanner.AddTag( tagNumberOfColumns ); // additional tags read in this scan to allow later analysis // THESE tag are not used for initial separating of files const gdcm::Tag tagImagePositionPatient(0x0020,0x0032); // Image Position (Patient) scanner.AddTag( tagImagePositionPatient ); // TODO add further restrictions from arguments // let GDCM scan files if ( !scanner.Scan( files ) ) { MITK_ERROR << "gdcm::Scanner failed when scanning " << files.size() << " input files."; return groupsOfSimilarImages; } // assign files IDs that will separate them for loading into image blocks for (gdcm::Scanner::ConstIterator fileIter = scanner.Begin(); fileIter != scanner.End(); ++fileIter) { //MITK_DEBUG << "Scan file " << fileIter->first << std::endl; if ( std::string(fileIter->first).empty() ) continue; // TODO understand why Scanner has empty string entries if ( std::string(fileIter->first) == std::string("DICOMDIR") ) continue; // we const_cast here, because I could not use a map.at() function in CreateMoreUniqueSeriesIdentifier. // doing the same thing with find would make the code less readable. Since we forget the Scanner results // anyway after this function, we can simply tolerate empty map entries introduced by bad operator[] access std::string moreUniqueSeriesId = CreateMoreUniqueSeriesIdentifier( const_cast(fileIter->second) ); groupsOfSimilarImages [ moreUniqueSeriesId ].push_back( fileIter->first ); } // PART II: sort slices spatially for ( UidFileNamesMap::const_iterator groupIter = groupsOfSimilarImages.begin(); groupIter != groupsOfSimilarImages.end(); ++groupIter ) { try { groupsOfSimilarImages[ groupIter->first ] = SortSeriesSlices( groupIter->second ); // sort each slice group spatially } catch(...) { MITK_ERROR << "Catched something."; } } // PART III: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing), // separate into multiple blocks if necessary. // // Analysis performs the following steps: // * imitate itk::ImageSeriesReader: use the distance between the first two images as z-spacing // * check what images actually fulfill ITK's z-spacing assumption // * separate all images that fail the test into new blocks, re-iterate analysis for these blocks UidFileNamesMap mapOf3DPlusTBlocks; // final result of this function for ( UidFileNamesMap::const_iterator groupIter = groupsOfSimilarImages.begin(); groupIter != groupsOfSimilarImages.end(); ++groupIter ) { UidFileNamesMap mapOf3DBlocks; // intermediate result for only this group(!) std::map mapOf3DBlockAnalysisResults; StringContainer filesStillToAnalyze = groupIter->second; std::string groupUID = groupIter->first; unsigned int subgroup(0); MITK_DEBUG << "Analyze group " << groupUID; while (!filesStillToAnalyze.empty()) // repeat until all files are grouped somehow { SliceGroupingAnalysisResult analysisResult = AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze, groupImagesWithGantryTilt, scanner.GetMappings() ); // enhance the UID for additional groups std::stringstream newGroupUID; newGroupUID << groupUID << '.' << subgroup; mapOf3DBlocks[ newGroupUID.str() ] = analysisResult.GetBlockFilenames(); MITK_DEBUG << "Result: sorted 3D group " << newGroupUID.str() << " with " << mapOf3DBlocks[ newGroupUID.str() ].size() << " files"; ++subgroup; filesStillToAnalyze = analysisResult.GetUnsortedFilenames(); // remember what needs further analysis } // end of grouping, now post-process groups // PART IV: attempt to group blocks to 3D+t blocks if requested // inspect entries of mapOf3DBlocks // - if number of files is identical to previous entry, collect for 3D+t block // - as soon as number of files changes from previous entry, record collected blocks as 3D+t block, start a new one, continue // decide whether or not to group 3D blocks into 3D+t blocks where possible if ( !sortTo3DPlust ) { // copy 3D blocks to output // TODO avoid collisions (or prove impossibility) mapOf3DPlusTBlocks.insert( mapOf3DBlocks.begin(), mapOf3DBlocks.end() ); } else { // sort 3D+t (as described in "PART IV") MITK_DEBUG << "================================================================================"; MITK_DEBUG << "3D+t analysis:"; unsigned int numberOfFilesInPreviousBlock(0); std::string previousBlockKey; for ( UidFileNamesMap::const_iterator block3DIter = mapOf3DBlocks.begin(); block3DIter != mapOf3DBlocks.end(); ++block3DIter ) { unsigned int numberOfFilesInThisBlock = block3DIter->second.size(); std::string thisBlockKey = block3DIter->first; if (numberOfFilesInPreviousBlock == 0) { numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; mapOf3DPlusTBlocks[thisBlockKey].insert( mapOf3DPlusTBlocks[thisBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; previousBlockKey = thisBlockKey; } else { bool identicalOrigins; try { // check whether this and the previous block share a comon origin // TODO should be safe, but a little try/catch or other error handling wouldn't hurt const char *origin_value = scanner.GetValue( mapOf3DBlocks[thisBlockKey].front().c_str(), tagImagePositionPatient ), *previous_origin_value = scanner.GetValue( mapOf3DBlocks[previousBlockKey].front().c_str(), tagImagePositionPatient ), *destination_value = scanner.GetValue( mapOf3DBlocks[thisBlockKey].back().c_str(), tagImagePositionPatient ), *previous_destination_value = scanner.GetValue( mapOf3DBlocks[previousBlockKey].back().c_str(), tagImagePositionPatient ); if (!origin_value || !previous_origin_value || !destination_value || !previous_destination_value) { identicalOrigins = false; } else { std::string thisOriginString = ConstCharStarToString( origin_value ); std::string previousOriginString = ConstCharStarToString( previous_origin_value ); // also compare last origin, because this might differ if z-spacing is different std::string thisDestinationString = ConstCharStarToString( destination_value ); std::string previousDestinationString = ConstCharStarToString( previous_destination_value ); identicalOrigins = ( (thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString) ); } } catch(...) { identicalOrigins = false; } if (identicalOrigins && (numberOfFilesInPreviousBlock == numberOfFilesInThisBlock)) { // group with previous block mapOf3DPlusTBlocks[previousBlockKey].insert( mapOf3DPlusTBlocks[previousBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " --> group enhanced with another timestep"; } else { // start a new block mapOf3DPlusTBlocks[thisBlockKey].insert( mapOf3DPlusTBlocks[thisBlockKey].end(), block3DIter->second.begin(), block3DIter->second.end() ); MITK_DEBUG << " ==> group closed with " << mapOf3DPlusTBlocks[previousBlockKey].size() / numberOfFilesInPreviousBlock << " time steps"; previousBlockKey = thisBlockKey; MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; } } numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; } } } MITK_DEBUG << "================================================================================"; MITK_DEBUG << "Summary: "; for ( UidFileNamesMap::const_iterator groupIter = mapOf3DPlusTBlocks.begin(); groupIter != mapOf3DPlusTBlocks.end(); ++groupIter ) { MITK_DEBUG << " Image volume " << groupIter->first << " with " << groupIter->second.size() << " files"; } MITK_DEBUG << "Done. "; MITK_DEBUG << "================================================================================"; return mapOf3DPlusTBlocks; } DicomSeriesReader::UidFileNamesMap DicomSeriesReader::GetSeries(const std::string &dir, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { gdcm::Directory directoryLister; directoryLister.Load( dir.c_str(), false ); // non-recursive return GetSeries(directoryLister.GetFilenames(), groupImagesWithGantryTilt, restrictions); } std::string DicomSeriesReader::CreateSeriesIdentifierPart( gdcm::Scanner::TagToValue& tagValueMap, const gdcm::Tag& tag ) { std::string result; try { result = IDifyTagValue( tagValueMap[ tag ] ? tagValueMap[ tag ] : std::string("") ); } catch (std::exception& e) { MITK_WARN << "Could not access tag " << tag << ": " << e.what(); } return result; } std::string DicomSeriesReader::CreateMoreUniqueSeriesIdentifier( gdcm::Scanner::TagToValue& tagValueMap ) { const gdcm::Tag tagSeriesInstanceUID(0x0020,0x000e); // Series Instance UID const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols const char* tagSeriesInstanceUid = tagValueMap[tagSeriesInstanceUID]; if (!tagSeriesInstanceUid) { mitkThrow() << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously wrong with this image, so stopping here."; } std::string constructedID = tagSeriesInstanceUid; constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfRows ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagNumberOfColumns ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagPixelSpacing ); constructedID += CreateSeriesIdentifierPart( tagValueMap, tagSliceThickness ); // be a bit tolerant for orienatation, let only the first few digits matter (http://bugs.mitk.org/show_bug.cgi?id=12263) // NOT constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation ); try { bool conversionError(false); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); DICOMStringToOrientationVectors( tagValueMap[tagImageOrientation], right, up, conversionError ); //string newstring sprintf(simplifiedOrientationString, "%.3f\\%.3f\\%.3f\\%.3f\\%.3f\\%.3f", right[0], right[1], right[2], up[0], up[1], up[2]); std::ostringstream ss; ss.setf(std::ios::fixed, std::ios::floatfield); ss.precision(5); ss << right[0] << "\\" << right[1] << "\\" << right[2] << "\\" << up[0] << "\\" << up[1] << "\\" << up[2]; std::string simplifiedOrientationString(ss.str()); constructedID += IDifyTagValue( simplifiedOrientationString ); } catch (std::exception& e) { MITK_WARN << "Could not access tag " << tagImageOrientation << ": " << e.what(); } constructedID.resize( constructedID.length() - 1 ); // cut of trailing '.' return constructedID; } std::string DicomSeriesReader::IDifyTagValue(const std::string& value) { std::string IDifiedValue( value ); if (value.empty()) throw std::logic_error("IDifyTagValue() illegaly called with empty tag value"); // Eliminate non-alnum characters, including whitespace... // that may have been introduced by concats. for(std::size_t i=0; i= 'a' && IDifiedValue[i] <= 'z') || (IDifiedValue[i] >= '0' && IDifiedValue[i] <= '9') || (IDifiedValue[i] >= 'A' && IDifiedValue[i] <= 'Z'))) { IDifiedValue.erase(i, 1); } } IDifiedValue += "."; return IDifiedValue; } DicomSeriesReader::StringContainer DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { UidFileNamesMap allSeries = GetSeries(dir, groupImagesWithGantryTilt, restrictions); StringContainer resultingFileList; for ( UidFileNamesMap::const_iterator idIter = allSeries.begin(); idIter != allSeries.end(); ++idIter ) { if ( idIter->first.find( series_uid ) == 0 ) // this ID starts with given series_uid { resultingFileList.insert( resultingFileList.end(), idIter->second.begin(), idIter->second.end() ); // append } } return resultingFileList; } DicomSeriesReader::StringContainer DicomSeriesReader::SortSeriesSlices(const StringContainer &unsortedFilenames) { gdcm::Sorter sorter; sorter.SetSortFunction(DicomSeriesReader::GdcmSortFunction); try { sorter.Sort(unsortedFilenames); return sorter.GetFilenames(); } catch(std::logic_error&) { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } bool DicomSeriesReader::GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2) { // make sure we have Image Position and Orientation if ( ! ( ds1.FindDataElement(gdcm::Tag(0x0020,0x0032)) && ds1.FindDataElement(gdcm::Tag(0x0020,0x0037)) && ds2.FindDataElement(gdcm::Tag(0x0020,0x0032)) && ds2.FindDataElement(gdcm::Tag(0x0020,0x0037)) ) ) { MITK_WARN << "Dicom images are missing attributes for a meaningful sorting."; throw std::logic_error("Dicom images are missing attributes for a meaningful sorting."); } gdcm::Attribute<0x0020,0x0032> image_pos1; // Image Position (Patient) gdcm::Attribute<0x0020,0x0037> image_orientation1; // Image Orientation (Patient) image_pos1.Set(ds1); image_orientation1.Set(ds1); gdcm::Attribute<0x0020,0x0032> image_pos2; gdcm::Attribute<0x0020,0x0037> image_orientation2; image_pos2.Set(ds2); image_orientation2.Set(ds2); /* we tolerate very small differences in image orientation, since we got to know about acquisitions where these values change across a single series (7th decimal digit) (http://bugs.mitk.org/show_bug.cgi?id=12263) still, we want to check if our assumption of 'almost equal' orientations is valid */ for (unsigned int dim = 0; dim < 6; ++dim) { if ( fabs(image_orientation2[dim] - image_orientation1[dim]) > 0.0001 ) { MITK_ERROR << "Dicom images have different orientations."; throw std::logic_error("Dicom images have different orientations. Call GetSeries() first to separate images."); } } double normal[3]; normal[0] = image_orientation1[1] * image_orientation1[5] - image_orientation1[2] * image_orientation1[4]; normal[1] = image_orientation1[2] * image_orientation1[3] - image_orientation1[0] * image_orientation1[5]; normal[2] = image_orientation1[0] * image_orientation1[4] - image_orientation1[1] * image_orientation1[3]; double dist1 = 0.0, dist2 = 0.0; for (unsigned char i = 0u; i < 3u; ++i) { dist1 += normal[i] * image_pos1[i]; dist2 += normal[i] * image_pos2[i]; } if ( fabs(dist1 - dist2) < mitk::eps) { gdcm::Attribute<0x0008,0x0032> acq_time1; // Acquisition time (may be missing, so we check existence first) gdcm::Attribute<0x0008,0x0032> acq_time2; gdcm::Attribute<0x0020,0x0012> acq_number1; // Acquisition number (may also be missing, so we check existence first) gdcm::Attribute<0x0020,0x0012> acq_number2; if (ds1.FindDataElement(gdcm::Tag(0x0008,0x0032)) && ds2.FindDataElement(gdcm::Tag(0x0008,0x0032))) { acq_time1.Set(ds1); acq_time2.Set(ds2); return acq_time1 < acq_time2; } else if (ds1.FindDataElement(gdcm::Tag(0x0020,0x0012)) && ds2.FindDataElement(gdcm::Tag(0x0020,0x0012))) { acq_number1.Set(ds1); acq_number2.Set(ds2); return acq_number1 < acq_number2; } else { return true; } } else { // default: compare position return dist1 < dist2; } } std::string DicomSeriesReader::GetConfigurationString() { std::stringstream configuration; configuration << "MITK_USE_GDCMIO: "; configuration << "true"; configuration << "\n"; configuration << "GDCM_VERSION: "; #ifdef GDCM_MAJOR_VERSION configuration << GDCM_VERSION; #endif //configuration << "\n"; return configuration.str(); } void DicomSeriesReader::CopyMetaDataToImageProperties(StringContainer filenames, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, Image *image) { std::list imageBlock; imageBlock.push_back(filenames); CopyMetaDataToImageProperties(imageBlock, tagValueMappings_, io, image); } void DicomSeriesReader::CopyMetaDataToImageProperties( std::list imageBlock, const gdcm::Scanner::MappingType& tagValueMappings_, DcmIoType* io, Image* image) { if (!io || !image) return; StringLookupTable filesForSlices; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceNumberForSlices; gdcm::Scanner::MappingType& tagValueMappings = const_cast(tagValueMappings_); //DICOM tags which should be added to the image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number unsigned int timeStep(0); std::string propertyKeySliceLocation = "dicom.image.0020.1041"; std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; // tags for each image for ( std::list::iterator i = imageBlock.begin(); i != imageBlock.end(); i++, timeStep++ ) { const StringContainer& files = (*i); unsigned int slice(0); for ( StringContainer::const_iterator fIter = files.begin(); fIter != files.end(); ++fIter, ++slice ) { filesForSlices.SetTableValue( slice, *fIter ); gdcm::Scanner::TagToValue tagValueMapForFile = tagValueMappings[fIter->c_str()]; if(tagValueMapForFile.find(tagSliceLocation) != tagValueMapForFile.end()) sliceLocationForSlices.SetTableValue(slice, tagValueMapForFile[tagSliceLocation]); if(tagValueMapForFile.find(tagInstanceNumber) != tagValueMapForFile.end()) instanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagInstanceNumber]); if(tagValueMapForFile.find(tagSOPInstanceNumber) != tagValueMapForFile.end()) SOPInstanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagSOPInstanceNumber]); } image->SetProperty( "files", StringLookupTableProperty::New( filesForSlices ) ); //If more than one time step add postfix ".t" + timestep if(timeStep != 0) { propertyKeySliceLocation.append(".t" + timeStep); propertyKeyInstanceNumber.append(".t" + timeStep); propertyKeySOPInstanceNumber.append(".t" + timeStep); } image->SetProperty( propertyKeySliceLocation.c_str(), StringLookupTableProperty::New( sliceLocationForSlices ) ); image->SetProperty( propertyKeyInstanceNumber.c_str(), StringLookupTableProperty::New( instanceNumberForSlices ) ); image->SetProperty( propertyKeySOPInstanceNumber.c_str(), StringLookupTableProperty::New( SOPInstanceNumberForSlices ) ); } // Copy tags for series, study, patient level (leave interpretation to application). // These properties will be copied to the DataNode by DicomSeriesReader. // tags for the series (we just use the one that ITK copied to its dictionary (proably that of the last slice) const itk::MetaDataDictionary& dict = io->GetMetaDataDictionary(); const TagToPropertyMapType& propertyLookup = DicomSeriesReader::GetDICOMTagsToMITKPropertyMap(); itk::MetaDataDictionary::ConstIterator dictIter = dict.Begin(); while ( dictIter != dict.End() ) { //MITK_DEBUG << "Key " << dictIter->first; std::string value; if ( itk::ExposeMetaData( dict, dictIter->first, value ) ) { //MITK_DEBUG << "Value " << value; TagToPropertyMapType::const_iterator valuePosition = propertyLookup.find( dictIter->first ); if ( valuePosition != propertyLookup.end() ) { std::string propertyKey = valuePosition->second; //MITK_DEBUG << "--> " << propertyKey; image->SetProperty( propertyKey.c_str(), StringProperty::New(value) ); } } else { MITK_WARN << "Tag " << dictIter->first << " not read as string as expected. Ignoring..." ; } ++dictIter; } } } // end namespace mitk #include diff --git a/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsNetworkCreator.cpp b/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsNetworkCreator.cpp index 1f315932de..8f7541f354 100644 --- a/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsNetworkCreator.cpp +++ b/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsNetworkCreator.cpp @@ -1,757 +1,762 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkConnectomicsNetworkCreator.h" #include #include #include "mitkConnectomicsConstantsManager.h" // VTK #include #include #include mitk::ConnectomicsNetworkCreator::ConnectomicsNetworkCreator() : m_FiberBundle() , m_Segmentation() , m_ConNetwork( mitk::ConnectomicsNetwork::New() ) , idCounter(0) , m_LabelToVertexMap() , m_LabelToNodePropertyMap() , allowLoops( false ) { } mitk::ConnectomicsNetworkCreator::ConnectomicsNetworkCreator( mitk::Image::Pointer segmentation, mitk::FiberBundleX::Pointer fiberBundle ) : m_FiberBundle(fiberBundle) , m_Segmentation(segmentation) , m_ConNetwork( mitk::ConnectomicsNetwork::New() ) , idCounter(0) , m_LabelToVertexMap() , m_LabelToNodePropertyMap() , allowLoops( false ) { } mitk::ConnectomicsNetworkCreator::~ConnectomicsNetworkCreator() { } void mitk::ConnectomicsNetworkCreator::SetFiberBundle(mitk::FiberBundleX::Pointer fiberBundle) { m_FiberBundle = fiberBundle; } void mitk::ConnectomicsNetworkCreator::SetSegmentation(mitk::Image::Pointer segmentation) { m_Segmentation = segmentation; } itk::Point mitk::ConnectomicsNetworkCreator::GetItkPoint(double point[3]) { itk::Point itkPoint; itkPoint[0] = point[0]; itkPoint[1] = point[1]; itkPoint[2] = point[2]; return itkPoint; } void mitk::ConnectomicsNetworkCreator::CreateNetworkFromFibersAndSegmentation() { //empty graph m_ConNetwork->clear(); m_LabelToVertexMap.clear(); m_LabelToNodePropertyMap.clear(); vtkSmartPointer fiberPolyData = m_FiberBundle->GetFiberPolyData(); vtkSmartPointer vLines = fiberPolyData->GetLines(); vLines->InitTraversal(); int numFibers = m_FiberBundle->GetNumFibers(); for( int fiberID( 0 ); fiberID < numFibers; fiberID++ ) { vtkIdType numPointsInCell(0); vtkIdType* pointsInCell(NULL); vLines->GetNextCell ( numPointsInCell, pointsInCell ); TractType::Pointer singleTract = TractType::New(); for( int pointInCellID( 0 ); pointInCellID < numPointsInCell ; pointInCellID++) { // push back point PointType point = GetItkPoint( fiberPolyData->GetPoint( pointsInCell[ pointInCellID ] ) ); singleTract->InsertElement( singleTract->Size(), point ); } //MappingStrategy strategy = EndElementPosition; //MappingStrategy strategy = JustEndPointVerticesNoLabel; MappingStrategy strategy = EndElementPositionAvoidingWhiteMatter; if ( singleTract && ( singleTract->Size() > 0 ) ) { AddConnectionToNetwork( ReturnAssociatedVertexPairForLabelPair( ReturnLabelForFiberTract( singleTract, strategy ) ) ); } } + // Prune unconnected nodes + m_ConNetwork->PruneUnconnectedSingleNodes(); // provide network with geometry m_ConNetwork->SetGeometry( m_Segmentation->GetGeometry() ); m_ConNetwork->UpdateBounds(); m_ConNetwork->SetIsModified( true ); MBI_INFO << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_INFO_NETWORK_CREATED; } void mitk::ConnectomicsNetworkCreator::AddConnectionToNetwork(ConnectionType newConnection) { VertexType vertexA = newConnection.first; VertexType vertexB = newConnection.second; // if vertices A and B exist if( vertexA && vertexB) { // check for loops (if they are not allowed if( allowLoops || !( vertexA == vertexB ) ) { // If the connection already exists, increment weight, else create connection if ( m_ConNetwork->EdgeExists( vertexA, vertexB ) ) { m_ConNetwork->IncreaseEdgeWeight( vertexA, vertexB ); } else { m_ConNetwork->AddEdge( vertexA, vertexB ); } } } } mitk::ConnectomicsNetworkCreator::VertexType mitk::ConnectomicsNetworkCreator::ReturnAssociatedVertexForLabel( ImageLabelType label ) { // if label is not known, create entry if( ! ( m_LabelToVertexMap.count( label ) > 0 ) ) { VertexType newVertex = m_ConNetwork->AddVertex( idCounter ); idCounter++; SupplyVertexWithInformation(label, newVertex); m_LabelToVertexMap.insert( std::pair< ImageLabelType, VertexType >( label, newVertex ) ); } //return associated vertex return m_LabelToVertexMap.find( label )->second; } mitk::ConnectomicsNetworkCreator::ConnectionType mitk::ConnectomicsNetworkCreator::ReturnAssociatedVertexPairForLabelPair( ImageLabelPairType labelpair ) { //hand both labels through to the single label function ConnectionType connection( ReturnAssociatedVertexForLabel(labelpair.first), ReturnAssociatedVertexForLabel(labelpair.second) ); return connection; } mitk::ConnectomicsNetworkCreator::ImageLabelPairType mitk::ConnectomicsNetworkCreator::ReturnLabelForFiberTract( TractType::Pointer singleTract, mitk::ConnectomicsNetworkCreator::MappingStrategy strategy) { switch( strategy ) { case EndElementPosition: { return EndElementPositionLabel( singleTract ); } case PrecomputeAndDistance: { return PrecomputeVertexLocationsBySegmentation( singleTract ); } case JustEndPointVerticesNoLabel: { return JustEndPointVerticesNoLabelTest( singleTract ); } case EndElementPositionAvoidingWhiteMatter: { return EndElementPositionLabelAvoidingWhiteMatter( singleTract ); } } // To remove warnings, this code should never be reached MBI_ERROR << mitk::ConnectomicsConstantsManager::CONNECTOMICS_ERROR_INVALID_MAPPING; ImageLabelPairType nullPair( NULL, NULL ); return nullPair; } mitk::ConnectomicsNetworkCreator::ImageLabelPairType mitk::ConnectomicsNetworkCreator::EndElementPositionLabel( TractType::Pointer singleTract ) { ImageLabelPairType labelpair; {// Note: .fib image tracts are safed using index coordinates mitk::Point3D firstElementFiberCoord, lastElementFiberCoord; mitk::Point3D firstElementSegCoord, lastElementSegCoord; mitk::Index3D firstElementSegIndex, lastElementSegIndex; if( singleTract->front().Size() != 3 ) { MBI_ERROR << mitk::ConnectomicsConstantsManager::CONNECTOMICS_ERROR_INVALID_DIMENSION_NEED_3; } for( int index = 0; index < singleTract->front().Size(); index++ ) { firstElementFiberCoord.SetElement( index, singleTract->front().GetElement( index ) ); lastElementFiberCoord.SetElement( index, singleTract->back().GetElement( index ) ); } // convert from fiber index coordinates to segmentation index coordinates FiberToSegmentationCoords( firstElementFiberCoord, firstElementSegCoord ); FiberToSegmentationCoords( lastElementFiberCoord, lastElementSegCoord ); for( int index = 0; index < 3; index++ ) { firstElementSegIndex.SetElement( index, firstElementSegCoord.GetElement( index ) ); lastElementSegIndex.SetElement( index, lastElementSegCoord.GetElement( index ) ); } int firstLabel = m_Segmentation->GetPixelValueByIndex( firstElementSegIndex ); int lastLabel = m_Segmentation->GetPixelValueByIndex( lastElementSegIndex ); labelpair.first = firstLabel; labelpair.second = lastLabel; // Add property to property map if( ! ( m_LabelToNodePropertyMap.count( firstLabel ) > 0 ) ) { NetworkNode firstNode; firstNode.coordinates.resize( 3 ); for( unsigned int index = 0; index < firstNode.coordinates.size() ; index++ ) { firstNode.coordinates[ index ] = firstElementSegIndex[ index ] ; } firstNode.label = LabelToString( firstLabel ); m_LabelToNodePropertyMap.insert( std::pair< ImageLabelType, NetworkNode >( firstLabel, firstNode ) ); } if( ! ( m_LabelToNodePropertyMap.count( lastLabel ) > 0 ) ) { NetworkNode lastNode; lastNode.coordinates.resize( 3 ); for( unsigned int index = 0; index < lastNode.coordinates.size() ; index++ ) { lastNode.coordinates[ index ] = lastElementSegIndex[ index ] ; } lastNode.label = LabelToString( lastLabel ); m_LabelToNodePropertyMap.insert( std::pair< ImageLabelType, NetworkNode >( lastLabel, lastNode ) ); } } return labelpair; } mitk::ConnectomicsNetworkCreator::ImageLabelPairType mitk::ConnectomicsNetworkCreator::PrecomputeVertexLocationsBySegmentation( TractType::Pointer singleTract ) { ImageLabelPairType labelpair; return labelpair; } mitk::ConnectomicsNetworkCreator::ImageLabelPairType mitk::ConnectomicsNetworkCreator::EndElementPositionLabelAvoidingWhiteMatter( TractType::Pointer singleTract ) { ImageLabelPairType labelpair; {// Note: .fib image tracts are safed using index coordinates mitk::Point3D firstElementFiberCoord, lastElementFiberCoord; mitk::Point3D firstElementSegCoord, lastElementSegCoord; mitk::Index3D firstElementSegIndex, lastElementSegIndex; if( singleTract->front().Size() != 3 ) { MBI_ERROR << mitk::ConnectomicsConstantsManager::CONNECTOMICS_ERROR_INVALID_DIMENSION_NEED_3; } for( int index = 0; index < singleTract->front().Size(); index++ ) { firstElementFiberCoord.SetElement( index, singleTract->front().GetElement( index ) ); lastElementFiberCoord.SetElement( index, singleTract->back().GetElement( index ) ); } // convert from fiber index coordinates to segmentation index coordinates FiberToSegmentationCoords( firstElementFiberCoord, firstElementSegCoord ); FiberToSegmentationCoords( lastElementFiberCoord, lastElementSegCoord ); for( int index = 0; index < 3; index++ ) { firstElementSegIndex.SetElement( index, firstElementSegCoord.GetElement( index ) ); lastElementSegIndex.SetElement( index, lastElementSegCoord.GetElement( index ) ); } int firstLabel = m_Segmentation->GetPixelValueByIndex( firstElementSegIndex ); int lastLabel = m_Segmentation->GetPixelValueByIndex( lastElementSegIndex ); // Check whether the labels belong to the white matter (which means, that the fibers ended early) bool extendFront(false), extendEnd(false), retractFront(false), retractEnd(false); extendFront = !IsNonWhiteMatterLabel( firstLabel ); extendEnd = !IsNonWhiteMatterLabel( lastLabel ); retractFront = IsBackgroundLabel( firstLabel ); retractEnd = IsBackgroundLabel( lastLabel ); //if( extendFront || extendEnd ) //{ //MBI_INFO << "Before Start: " << firstLabel << " at " << firstElementSegIndex[ 0 ] << " " << firstElementSegIndex[ 1 ] << " " << firstElementSegIndex[ 2 ] << " End: " << lastLabel << " at " << lastElementSegIndex[ 0 ] << " " << lastElementSegIndex[ 1 ] << " " << lastElementSegIndex[ 2 ]; //} if ( extendFront ) { std::vector< int > indexVectorOfPointsToUse; //Use first two points for direction indexVectorOfPointsToUse.push_back( 1 ); indexVectorOfPointsToUse.push_back( 0 ); // label and coordinate temp storage int tempLabel( firstLabel ); mitk::Index3D tempIndex = firstElementSegIndex; LinearExtensionUntilGreyMatter( indexVectorOfPointsToUse, singleTract, tempLabel, tempIndex ); firstLabel = tempLabel; firstElementSegIndex = tempIndex; } if ( extendEnd ) { std::vector< int > indexVectorOfPointsToUse; //Use last two points for direction indexVectorOfPointsToUse.push_back( singleTract->Size() - 2 ); indexVectorOfPointsToUse.push_back( singleTract->Size() - 1 ); // label and coordinate temp storage int tempLabel( lastLabel ); mitk::Index3D tempIndex = lastElementSegIndex; LinearExtensionUntilGreyMatter( indexVectorOfPointsToUse, singleTract, tempLabel, tempIndex ); lastLabel = tempLabel; lastElementSegIndex = tempIndex; } if ( retractFront ) { // label and coordinate temp storage int tempLabel( firstLabel ); mitk::Index3D tempIndex = firstElementSegIndex; RetractionUntilBrainMatter( true, singleTract, tempLabel, tempIndex ); firstLabel = tempLabel; firstElementSegIndex = tempIndex; } if ( retractEnd ) { // label and coordinate temp storage int tempLabel( lastLabel ); mitk::Index3D tempIndex = lastElementSegIndex; RetractionUntilBrainMatter( false, singleTract, tempLabel, tempIndex ); lastLabel = tempLabel; lastElementSegIndex = tempIndex; } //if( extendFront || extendEnd ) //{ // MBI_INFO << "After Start: " << firstLabel << " at " << firstElementSegIndex[ 0 ] << " " << firstElementSegIndex[ 1 ] << " " << firstElementSegIndex[ 2 ] << " End: " << lastLabel << " at " << lastElementSegIndex[ 0 ] << " " << lastElementSegIndex[ 1 ] << " " << lastElementSegIndex[ 2 ]; //} labelpair.first = firstLabel; labelpair.second = lastLabel; // Add property to property map if( ! ( m_LabelToNodePropertyMap.count( firstLabel ) > 0 ) ) { NetworkNode firstNode; firstNode.coordinates.resize( 3 ); for( unsigned int index = 0; index < firstNode.coordinates.size() ; index++ ) { firstNode.coordinates[ index ] = firstElementSegIndex[ index ] ; } firstNode.label = LabelToString( firstLabel ); m_LabelToNodePropertyMap.insert( std::pair< ImageLabelType, NetworkNode >( firstLabel, firstNode ) ); } if( ! ( m_LabelToNodePropertyMap.count( lastLabel ) > 0 ) ) { NetworkNode lastNode; lastNode.coordinates.resize( 3 ); for( unsigned int index = 0; index < lastNode.coordinates.size() ; index++ ) { lastNode.coordinates[ index ] = lastElementSegIndex[ index ] ; } lastNode.label = LabelToString( lastLabel ); m_LabelToNodePropertyMap.insert( std::pair< ImageLabelType, NetworkNode >( lastLabel, lastNode ) ); } } return labelpair; } mitk::ConnectomicsNetworkCreator::ImageLabelPairType mitk::ConnectomicsNetworkCreator::JustEndPointVerticesNoLabelTest( TractType::Pointer singleTract ) { ImageLabelPairType labelpair; {// Note: .fib image tracts are safed using index coordinates mitk::Point3D firstElementFiberCoord, lastElementFiberCoord; mitk::Point3D firstElementSegCoord, lastElementSegCoord; mitk::Index3D firstElementSegIndex, lastElementSegIndex; if( singleTract->front().Size() != 3 ) { MBI_ERROR << mitk::ConnectomicsConstantsManager::CONNECTOMICS_ERROR_INVALID_DIMENSION_NEED_3; } for( int index = 0; index < singleTract->front().Size(); index++ ) { firstElementFiberCoord.SetElement( index, singleTract->front().GetElement( index ) ); lastElementFiberCoord.SetElement( index, singleTract->back().GetElement( index ) ); } // convert from fiber index coordinates to segmentation index coordinates FiberToSegmentationCoords( firstElementFiberCoord, firstElementSegCoord ); FiberToSegmentationCoords( lastElementFiberCoord, lastElementSegCoord ); for( int index = 0; index < 3; index++ ) { firstElementSegIndex.SetElement( index, firstElementSegCoord.GetElement( index ) ); lastElementSegIndex.SetElement( index, lastElementSegCoord.GetElement( index ) ); } int firstLabel = 1 * firstElementSegIndex[ 0 ] + 1000 * firstElementSegIndex[ 1 ] + 1000000 * firstElementSegIndex[ 2 ]; int lastLabel = 1 * firstElementSegIndex[ 0 ] + 1000 * firstElementSegIndex[ 1 ] + 1000000 * firstElementSegIndex[ 2 ]; labelpair.first = firstLabel; labelpair.second = lastLabel; // Add property to property map if( ! ( m_LabelToNodePropertyMap.count( firstLabel ) > 0 ) ) { NetworkNode firstNode; firstNode.coordinates.resize( 3 ); for( unsigned int index = 0; index < firstNode.coordinates.size() ; index++ ) { firstNode.coordinates[ index ] = firstElementSegIndex[ index ] ; } firstNode.label = LabelToString( firstLabel ); m_LabelToNodePropertyMap.insert( std::pair< ImageLabelType, NetworkNode >( firstLabel, firstNode ) ); } if( ! ( m_LabelToNodePropertyMap.count( lastLabel ) > 0 ) ) { NetworkNode lastNode; lastNode.coordinates.resize( 3 ); for( unsigned int index = 0; index < lastNode.coordinates.size() ; index++ ) { lastNode.coordinates[ index ] = lastElementSegIndex[ index ] ; } lastNode.label = LabelToString( lastLabel ); m_LabelToNodePropertyMap.insert( std::pair< ImageLabelType, NetworkNode >( lastLabel, lastNode ) ); } } return labelpair; } void mitk::ConnectomicsNetworkCreator::SupplyVertexWithInformation( ImageLabelType& label, VertexType& vertex ) { // supply a vertex with the additional information belonging to the label // TODO: Implement additional information acquisition m_ConNetwork->SetLabel( vertex, m_LabelToNodePropertyMap.find( label )->second.label ); m_ConNetwork->SetCoordinates( vertex, m_LabelToNodePropertyMap.find( label )->second.coordinates ); } std::string mitk::ConnectomicsNetworkCreator::LabelToString( ImageLabelType& label ) { int tempInt = (int) label; std::stringstream ss;//create a stringstream std::string tempString; ss << tempInt;//add number to the stream tempString = ss.str(); return tempString;//return a string with the contents of the stream } mitk::ConnectomicsNetwork::Pointer mitk::ConnectomicsNetworkCreator::GetNetwork() { return m_ConNetwork; } void mitk::ConnectomicsNetworkCreator::FiberToSegmentationCoords( mitk::Point3D& fiberCoord, mitk::Point3D& segCoord ) { mitk::Point3D tempPoint; // convert from fiber index coordinates to segmentation index coordinates m_FiberBundle->GetGeometry()->IndexToWorld( fiberCoord, tempPoint ); m_Segmentation->GetGeometry()->WorldToIndex( tempPoint, segCoord ); } void mitk::ConnectomicsNetworkCreator::SegmentationToFiberCoords( mitk::Point3D& segCoord, mitk::Point3D& fiberCoord ) { mitk::Point3D tempPoint; // convert from fiber index coordinates to segmentation index coordinates m_Segmentation->GetGeometry()->IndexToWorld( segCoord, tempPoint ); m_FiberBundle->GetGeometry()->WorldToIndex( tempPoint, fiberCoord ); } bool mitk::ConnectomicsNetworkCreator::IsNonWhiteMatterLabel( int labelInQuestion ) { bool isWhite( false ); isWhite = ( ( labelInQuestion == freesurfer_Left_Cerebral_White_Matter ) || ( labelInQuestion == freesurfer_Left_Cerebellum_White_Matter ) || ( labelInQuestion == freesurfer_Right_Cerebral_White_Matter ) || - ( labelInQuestion == freesurfer_Right_Cerebellum_White_Matter ) + ( labelInQuestion == freesurfer_Right_Cerebellum_White_Matter )|| + ( labelInQuestion == freesurfer_Left_Cerebellum_Cortex ) || + ( labelInQuestion == freesurfer_Right_Cerebellum_Cortex ) || + ( labelInQuestion == freesurfer_Brain_Stem ) ); return !isWhite; } bool mitk::ConnectomicsNetworkCreator::IsBackgroundLabel( int labelInQuestion ) { bool isBackground( false ); isBackground = ( labelInQuestion == 0 ); return isBackground; } void mitk::ConnectomicsNetworkCreator::LinearExtensionUntilGreyMatter( std::vector & indexVectorOfPointsToUse, TractType::Pointer singleTract, int & label, mitk::Index3D & mitkIndex ) { if( indexVectorOfPointsToUse.size() > singleTract->Size() ) { MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_MORE_POINTS_THAN_PRESENT; return; } if( indexVectorOfPointsToUse.size() < 2 ) { MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_ESTIMATING_LESS_THAN_2; return; } for( int index( 0 ); index < indexVectorOfPointsToUse.size(); index++ ) { if( indexVectorOfPointsToUse[ index ] > singleTract->Size() ) { MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_ESTIMATING_BEYOND_END; return; } if( indexVectorOfPointsToUse[ index ] < 0 ) { MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_ESTIMATING_BEYOND_START; return; } } mitk::Point3D startPoint, endPoint; std::vector< double > differenceVector; differenceVector.resize( singleTract->front().Size() ); { // which points to use, currently only last two //TODO correct using all points int endPointIndex = indexVectorOfPointsToUse.size() - 1; int startPointIndex = indexVectorOfPointsToUse.size() - 2; // convert to segmentation coords mitk::Point3D startFiber, endFiber; for( int index = 0; index < singleTract->front().Size(); index++ ) { endFiber.SetElement( index, singleTract->GetElement( indexVectorOfPointsToUse[ endPointIndex ] ).GetElement( index ) ); startFiber.SetElement( index, singleTract->GetElement( indexVectorOfPointsToUse[ startPointIndex ] ).GetElement( index ) ); } FiberToSegmentationCoords( endFiber, endPoint ); FiberToSegmentationCoords( startFiber, startPoint ); // calculate straight line for( int index = 0; index < singleTract->front().Size(); index++ ) { differenceVector[ index ] = endPoint.GetElement( index ) - startPoint.GetElement( index ); } // normalizing direction vector double length( 0.0 ); double sum( 0.0 ); for( int index = 0; index < differenceVector.size() ; index++ ) { sum = sum + differenceVector[ index ] * differenceVector[ index ]; } length = std::sqrt( sum ); for( int index = 0; index < differenceVector.size() ; index++ ) { differenceVector[ index ] = differenceVector[ index ] / length; } // follow line until first non white matter label mitk::Index3D tempIndex; int tempLabel( label ); bool keepOn( true ); for( int parameter( 0 ) ; keepOn ; parameter++ ) { if( parameter > 1000 ) { MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_DID_NOT_FIND_WHITE; break; } for( int index( 0 ); index < 3; index++ ) { tempIndex.SetElement( index, endPoint.GetElement( index ) + parameter * differenceVector[ index ] ); } tempLabel = m_Segmentation->GetPixelValueByIndex( tempIndex ); if( IsNonWhiteMatterLabel( tempLabel ) ) { if( tempLabel < 1 ) { keepOn = false; - MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_NOT_EXTEND_TO_WHITE; + //MBI_WARN << mitk::ConnectomicsConstantsManager::CONNECTOMICS_WARNING_NOT_EXTEND_TO_WHITE; } else { label = tempLabel; mitkIndex = tempIndex; keepOn = false; } } } } } void mitk::ConnectomicsNetworkCreator::RetractionUntilBrainMatter( bool retractFront, TractType::Pointer singleTract, int & label, mitk::Index3D & mitkIndex ) { int retractionStartIndex( singleTract->Size() - 1 ); int retractionStepIndexSize( -1 ); int retractionTerminationIndex( 0 ); if( retractFront ) { retractionStartIndex = 0; retractionStepIndexSize = 1; retractionTerminationIndex = singleTract->Size() - 1; } int currentRetractionIndex = retractionStartIndex; bool keepRetracting( true ); mitk::Point3D currentPoint, nextPoint; std::vector< double > differenceVector; differenceVector.resize( singleTract->front().Size() ); while( keepRetracting && ( currentRetractionIndex != retractionTerminationIndex ) ) { // convert to segmentation coords mitk::Point3D currentPointFiberCoord, nextPointFiberCoord; for( int index = 0; index < singleTract->front().Size(); index++ ) { currentPointFiberCoord.SetElement( index, singleTract->GetElement( currentRetractionIndex ).GetElement( index ) ); nextPointFiberCoord.SetElement( index, singleTract->GetElement( currentRetractionIndex + retractionStepIndexSize ).GetElement( index ) ); } FiberToSegmentationCoords( currentPointFiberCoord, currentPoint ); FiberToSegmentationCoords( nextPointFiberCoord, nextPoint ); // calculate straight line for( int index = 0; index < singleTract->front().Size(); index++ ) { differenceVector[ index ] = nextPoint.GetElement( index ) - currentPoint.GetElement( index ); } // calculate length of direction vector double length( 0.0 ); double sum( 0.0 ); for( int index = 0; index < differenceVector.size() ; index++ ) { sum = sum + differenceVector[ index ] * differenceVector[ index ]; } length = std::sqrt( sum ); // retract mitk::Index3D tempIndex; int tempLabel( label ); for( int parameter( 0 ) ; parameter < length ; parameter++ ) { for( int index( 0 ); index < 3; index++ ) { tempIndex.SetElement( index, currentPoint.GetElement( index ) + ( 1.0 + parameter ) / ( 1.0 + length ) * differenceVector[ index ] ); } tempLabel = m_Segmentation->GetPixelValueByIndex( tempIndex ); if( !IsBackgroundLabel( tempLabel ) ) { label = tempLabel; mitkIndex = tempIndex; return; } // hit next point without finding brain matter currentRetractionIndex = currentRetractionIndex + retractionStepIndexSize; if( ( currentRetractionIndex < 1 ) || ( currentRetractionIndex > ( singleTract->Size() - 2 ) ) ) { keepRetracting = false; } } } } diff --git a/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsNetworkCreator.h b/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsNetworkCreator.h index 8bb4d26f05..504fe4fd2f 100644 --- a/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsNetworkCreator.h +++ b/Modules/DiffusionImaging/Algorithms/Connectomics/mitkConnectomicsNetworkCreator.h @@ -1,191 +1,193 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkConnectomicsNetworkCreator_h #define mitkConnectomicsNetworkCreator_h #include #include #include #include "mitkCommon.h" #include "mitkImage.h" #include "mitkFiberBundleX.h" #include "mitkConnectomicsNetwork.h" #include "MitkDiffusionImagingExports.h" namespace mitk { class MitkDiffusionImaging_EXPORT ConnectomicsNetworkCreator : public itk::Object { public: /** Enum for different ways to create the mapping from fibers to network */ enum MappingStrategy { EndElementPosition, PrecomputeAndDistance, JustEndPointVerticesNoLabel, EndElementPositionAvoidingWhiteMatter }; /** Standard class typedefs. */ /** Method for creation through the object factory. */ mitkClassMacro(ConnectomicsNetworkCreator, itk::Object); itkNewMacro(Self); /** Types for the standardized Tract **/ typedef itk::Point PointType; typedef itk::VectorContainer TractType; typedef itk::VectorContainer< unsigned int, TractType::Pointer > TractContainerType; //init via smartpointer /** Types for Network **/ typedef mitk::ConnectomicsNetwork::VertexDescriptorType VertexType; typedef mitk::ConnectomicsNetwork::EdgeDescriptorType EdgeType; typedef mitk::ConnectomicsNetwork::NetworkNode NetworkNode; typedef std::pair< VertexType, VertexType > ConnectionType; /** Types for labels **/ typedef int ImageLabelType; typedef std::pair< ImageLabelType, ImageLabelType > ImageLabelPairType; - - - + /** Given a fiber bundle and a parcellation are set, this will create a network from both */ void CreateNetworkFromFibersAndSegmentation(); void SetFiberBundle(mitk::FiberBundleX::Pointer fiberBundle); void SetSegmentation(mitk::Image::Pointer segmentation); mitk::ConnectomicsNetwork::Pointer GetNetwork(); protected: //////////////////// Functions /////////////////////// ConnectomicsNetworkCreator(); ConnectomicsNetworkCreator( mitk::Image::Pointer segmentation, mitk::FiberBundleX::Pointer fiberBundle ); ~ConnectomicsNetworkCreator(); /** Add a connection to the network */ void AddConnectionToNetwork(ConnectionType newConnection); /** Determine if a label is already identified with a vertex, otherwise create a new one */ VertexType ReturnAssociatedVertexForLabel( ImageLabelType label ); /** Return the vertexes associated with a pair of labels */ ConnectionType ReturnAssociatedVertexPairForLabelPair( ImageLabelPairType labelpair ); /** Return the pair of labels which identify the areas connected by a single fiber */ ImageLabelPairType ReturnLabelForFiberTract( TractType::Pointer singleTract, MappingStrategy strategy ); /** Assign the additional information which should be part of the vertex */ void SupplyVertexWithInformation( ImageLabelType& label, VertexType& vertex ); /** Create a string from the label */ std::string LabelToString( ImageLabelType& label ); /** Check whether the label in question belongs to white matter according to the freesurfer table */ bool IsNonWhiteMatterLabel( int labelInQuestion ); /** Check whether the label in question belongs to background according to the freesurfer table */ bool IsBackgroundLabel( int labelInQuestion ); /** Extend a straight line through the given points and look for the first non white matter label It will try extend in the direction of the points in the vector so a vector {B,C} will result in extending from C in the direction C-B */ void LinearExtensionUntilGreyMatter( std::vector & indexVectorOfPointsToUse, TractType::Pointer singleTract, int & label, mitk::Index3D & mitkIndex ); /** Retract fiber until the first brain matter label is hit The bool parameter controls whether the front or the end is retracted */ void RetractionUntilBrainMatter( bool retractFront, TractType::Pointer singleTract, int & label, mitk::Index3D & mitkIndex ); /** Convert point to itk point */ itk::Point GetItkPoint(double point[3]); ///////// Mapping strategies ////////// /** Use the position of the end and starting element only to map to labels Map a fiber to a vertex by taking the value of the parcellation image at the same world coordinates as the last and first element of the tract.*/ ImageLabelPairType EndElementPositionLabel( TractType::Pointer singleTract ); /** Map by distance between elements and vertices depending on their volume First go through the parcellation and compute the coordinates of the future vertices. Assign a radius according on their volume. Then map an edge to a label by considering the nearest vertices and comparing the distance to them to their radii. */ ImageLabelPairType PrecomputeVertexLocationsBySegmentation( TractType::Pointer singleTract ); /** Use the position of the end and starting element only to map to labels Just take first and last position, no labelling, nothing */ ImageLabelPairType JustEndPointVerticesNoLabelTest( TractType::Pointer singleTract ); /** Use the position of the end and starting element unless it is in white matter, then search for nearby parcellation to map to labels Map a fiber to a vertex by taking the value of the parcellation image at the same world coordinates as the last and first element of the tract. If this happens to be white matter, then try to extend the fiber in a line and take the first non-white matter parcel, that is intersected. */ ImageLabelPairType EndElementPositionLabelAvoidingWhiteMatter( TractType::Pointer singleTract ); ///////// Conversions ////////// /** Convert fiber index to segmentation index coordinates */ void FiberToSegmentationCoords( mitk::Point3D& fiberCoord, mitk::Point3D& segCoord ); /** Convert segmentation index to fiber index coordinates */ void SegmentationToFiberCoords( mitk::Point3D& segCoord, mitk::Point3D& fiberCoord ); /////////////////////// Variables //////////////////////// mitk::FiberBundleX::Pointer m_FiberBundle; mitk::Image::Pointer m_Segmentation; // the graph itself mitk::ConnectomicsNetwork::Pointer m_ConNetwork; // the id counter int idCounter; // the map mapping labels to vertices std::map< ImageLabelType, VertexType > m_LabelToVertexMap; // mapping labels to additional information std::map< ImageLabelType, NetworkNode > m_LabelToNodePropertyMap; // toggles whether edges between a node and itself can exist bool allowLoops; //////////////////////// IDs //////////////////////////// // These IDs are the freesurfer ids used in parcellation static const int freesurfer_Left_Cerebral_White_Matter = 2; static const int freesurfer_Left_Cerebellum_White_Matter = 7; + static const int freesurfer_Left_Cerebellum_Cortex = 8; + static const int freesurfer_Brain_Stem = 16; static const int freesurfer_Right_Cerebral_White_Matter = 41; static const int freesurfer_Right_Cerebellum_White_Matter = 46; + static const int freesurfer_Right_Cerebellum_Cortex = 47; + }; }// end namespace mitk #endif // _mitkConnectomicsNetworkCreator_H_INCLUDED diff --git a/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.cpp b/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.cpp index 55d44dad68..b876f87f6e 100644 --- a/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.cpp +++ b/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.cpp @@ -1,503 +1,587 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkConnectomicsNetwork.h" #include /* Constructor and Destructor */ mitk::ConnectomicsNetwork::ConnectomicsNetwork() : m_IsModified( false ) { } mitk::ConnectomicsNetwork::~ConnectomicsNetwork() { } /* Wrapper methods */ bool mitk::ConnectomicsNetwork::EdgeExists( mitk::ConnectomicsNetwork::VertexDescriptorType vertexA, mitk::ConnectomicsNetwork::VertexDescriptorType vertexB ) const { return boost::edge(vertexA, vertexB, m_Network ).second; } void mitk::ConnectomicsNetwork::IncreaseEdgeWeight( mitk::ConnectomicsNetwork::VertexDescriptorType vertexA, mitk::ConnectomicsNetwork::VertexDescriptorType vertexB ) { m_Network[ boost::edge(vertexA, vertexB, m_Network ).first ].weight++; SetIsModified( true ); } void mitk::ConnectomicsNetwork::AddEdge( mitk::ConnectomicsNetwork::VertexDescriptorType vertexA, mitk::ConnectomicsNetwork::VertexDescriptorType vertexB ) { AddEdge(vertexA, vertexB, m_Network[ vertexA ].id, m_Network[ vertexB ].id ); } void mitk::ConnectomicsNetwork::AddEdge( mitk::ConnectomicsNetwork::VertexDescriptorType vertexA, mitk::ConnectomicsNetwork::VertexDescriptorType vertexB, int sourceID, int targetID, int weight ) { boost::add_edge( vertexA, vertexB, m_Network ); m_Network[ boost::edge(vertexA, vertexB, m_Network ).first ].sourceId = sourceID; m_Network[ boost::edge(vertexA, vertexB, m_Network ).first ].targetId = targetID; m_Network[ boost::edge(vertexA, vertexB, m_Network ).first ].weight = weight; m_Network[ boost::edge(vertexA, vertexB, m_Network ).first ].edge_weight = 1.0; SetIsModified( true ); } mitk::ConnectomicsNetwork::VertexDescriptorType mitk::ConnectomicsNetwork::AddVertex( int id ) { VertexDescriptorType vertex = boost::add_vertex( m_Network ); m_Network[vertex].id = id; SetIsModified( true ); return vertex; } void mitk::ConnectomicsNetwork::SetLabel( mitk::ConnectomicsNetwork::VertexDescriptorType vertex, std::string inLabel ) { m_Network[vertex].label = inLabel; SetIsModified( true ); } void mitk::ConnectomicsNetwork::SetCoordinates( mitk::ConnectomicsNetwork::VertexDescriptorType vertex, std::vector< float > inCoordinates ) { m_Network[vertex].coordinates = inCoordinates; SetIsModified( true ); } void mitk::ConnectomicsNetwork::clear() { m_Network.clear(); SetIsModified( true ); } /* Superclass methods, that need to be implemented */ void mitk::ConnectomicsNetwork::UpdateOutputInformation() { } void mitk::ConnectomicsNetwork::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::ConnectomicsNetwork::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::ConnectomicsNetwork::VerifyRequestedRegion() { return true; } void mitk::ConnectomicsNetwork::SetRequestedRegion( itk::DataObject *data ) { } std::vector< mitk::ConnectomicsNetwork::NetworkNode > mitk::ConnectomicsNetwork::GetVectorOfAllNodes() const { boost::graph_traits::vertex_iterator iterator, end; // sets iterator to start end end to end boost::tie(iterator, end) = boost::vertices( m_Network ); std::vector< NetworkNode > vectorOfNodes; for ( ; iterator != end; ++iterator) { NetworkNode tempNode; // the value of an iterator is a descriptor tempNode = m_Network[ *iterator ]; vectorOfNodes.push_back( tempNode ); } return vectorOfNodes; } std::vector< mitk::ConnectomicsNetwork::VertexDescriptorType > mitk::ConnectomicsNetwork::GetVectorOfAllVertexDescriptors() const { boost::graph_traits::vertex_iterator iterator, end; // sets iterator to start end end to end boost::tie(iterator, end) = boost::vertices( m_Network ); std::vector< VertexDescriptorType > vectorOfDescriptors; for ( ; iterator != end; ++iterator) { vectorOfDescriptors.push_back( *iterator ); } return vectorOfDescriptors; } std::vector< std::pair< std::pair< mitk::ConnectomicsNetwork::NetworkNode, mitk::ConnectomicsNetwork::NetworkNode > , mitk::ConnectomicsNetwork::NetworkEdge > > mitk::ConnectomicsNetwork::GetVectorOfAllEdges() const { boost::graph_traits::edge_iterator iterator, end; // sets iterator to start end end to end boost::tie(iterator, end) = boost::edges( m_Network ); std::vector< std::pair< std::pair< NetworkNode, NetworkNode > , NetworkEdge > > vectorOfEdges; for ( ; iterator != end; ++iterator) { NetworkNode sourceNode, targetNode; NetworkEdge tempEdge; // the value of an iterator is a descriptor tempEdge = m_Network[ *iterator ]; sourceNode = m_Network[ boost::source( *iterator, m_Network ) ]; targetNode = m_Network[ boost::target( *iterator, m_Network ) ]; std::pair< NetworkNode, NetworkNode > nodePair( sourceNode, targetNode ); std::pair< std::pair< NetworkNode, NetworkNode > , NetworkEdge > edgePair( nodePair, tempEdge); vectorOfEdges.push_back( edgePair ); } return vectorOfEdges; } int mitk::ConnectomicsNetwork::GetNumberOfVertices() const { return boost::num_vertices( m_Network ); } int mitk::ConnectomicsNetwork::GetNumberOfEdges() { return boost::num_edges( m_Network ); } int mitk::ConnectomicsNetwork::GetMaximumWeight() const { int maxWeight( 0 ); boost::graph_traits::edge_iterator iterator, end; // sets iterator to start end end to end boost::tie(iterator, end) = boost::edges( m_Network ); for ( ; iterator != end; ++iterator) { int tempWeight; // the value of an iterator is a descriptor tempWeight = m_Network[ *iterator ].weight; if( tempWeight > maxWeight ) { maxWeight = tempWeight; } } return maxWeight; } int mitk::ConnectomicsNetwork::GetNumberOfSelfLoops() { int noOfSelfLoops( 0 ); std::vector< std::pair< std::pair< NetworkNode, NetworkNode > , NetworkEdge > > edgeVector = GetVectorOfAllEdges(); for( int index = 0; index < edgeVector.size() ; index++ ) { double sourceX, sourceY, sourceZ, targetX, targetY, targetZ; sourceX = edgeVector[ index ].first.first.coordinates[0] ; sourceY = edgeVector[ index ].first.first.coordinates[1] ; sourceZ = edgeVector[ index ].first.first.coordinates[2] ; targetX = edgeVector[ index ].first.second.coordinates[0] ; targetY = edgeVector[ index ].first.second.coordinates[1] ; targetZ = edgeVector[ index ].first.second.coordinates[2] ; // if the coordinates are the same if( sourceX > ( targetX - 0.01 ) && sourceX < ( targetX + 0.01 ) && sourceY > ( targetY - 0.01 ) && sourceY < ( targetY + 0.01 ) && sourceZ > ( targetZ - 0.01 ) && sourceZ < ( targetZ + 0.01 ) ) { noOfSelfLoops++; } } return noOfSelfLoops; } double mitk::ConnectomicsNetwork::GetAverageDegree() { double vertices = (double) GetNumberOfVertices(); double edges = (double) GetNumberOfEdges(); return ( ( edges * 2.0 ) / vertices ); } double mitk::ConnectomicsNetwork::GetConnectionDensity() { double vertices = (double) GetNumberOfVertices(); double edges = (double) GetNumberOfEdges(); double numberOfPossibleEdges = vertices * ( vertices - 1 ) / 2 ; return ( edges / numberOfPossibleEdges ); } std::vector< int > mitk::ConnectomicsNetwork::GetDegreeOfNodes( ) const { std::vector< int > vectorOfDegree; boost::graph_traits::vertex_iterator iterator, end; // sets iterator to start end end to end boost::tie( iterator, end ) = boost::vertices( m_Network ); vectorOfDegree.resize( this->GetNumberOfVertices() ); for ( ; iterator != end; ++iterator) { // the value of an iterator is a descriptor vectorOfDegree[ m_Network[ *iterator ].id ] = GetVectorOfAdjacentNodes( *iterator ).size(); } return vectorOfDegree; } std::vector< mitk::ConnectomicsNetwork::VertexDescriptorType > mitk::ConnectomicsNetwork::GetVectorOfAdjacentNodes( mitk::ConnectomicsNetwork::VertexDescriptorType vertex ) const { std::vector< mitk::ConnectomicsNetwork::VertexDescriptorType > vectorOfAdjacentNodes; boost::graph_traits::adjacency_iterator adjIter, adjEnd; boost::tie( adjIter, adjEnd ) = boost::adjacent_vertices( vertex, m_Network); for ( ; adjIter != adjEnd; ++adjIter) { vectorOfAdjacentNodes.push_back( *adjIter ); } return vectorOfAdjacentNodes; } int mitk::ConnectomicsNetwork::GetMaximumDegree() const { int maximumDegree( 0 ); std::vector< int > vectorOfDegree = GetDegreeOfNodes(); for( int index( 0 ); index < vectorOfDegree.size(); ++index ) { if( maximumDegree < vectorOfDegree[ index ] ) { maximumDegree = vectorOfDegree[ index ]; } } return maximumDegree; } std::vector< double > mitk::ConnectomicsNetwork::GetLocalClusteringCoefficients( ) { std::vector< double > vectorOfClusteringCoefficients; typedef boost::graph_traits::vertex_iterator vertexIter; vectorOfClusteringCoefficients.resize( this->GetNumberOfVertices() ); std::pair vertexPair; //for every vertex calculate the clustering coefficient for (vertexPair = vertices(m_Network); vertexPair.first != vertexPair.second; ++vertexPair.first) { vectorOfClusteringCoefficients[ m_Network[ *vertexPair.first ].id ] = boost::clustering_coefficient(m_Network,*vertexPair.first) ; } return vectorOfClusteringCoefficients; } std::vector< double > mitk::ConnectomicsNetwork::GetClusteringCoefficientsByDegree( ) { std::vector< double > vectorOfClusteringCoefficients = GetLocalClusteringCoefficients(); std::vector< int > vectorOfDegree = GetDegreeOfNodes(); std::vector< double > vectorOfClusteringCoefficientsByDegree; vectorOfClusteringCoefficientsByDegree.resize( GetMaximumDegree() + 1, 0 ); // c_{mean}(k) = frac{1}_{N_{k}} sum_{i in Y(k)} c_{i} // where N_{k} is the number of vertices of degree k // Y(k) is the set of vertices of degree k // c_{i} is the local clustering coefficient of vertex i for( int degree( 0 ); degree < vectorOfClusteringCoefficientsByDegree.size(); ++degree ) { vectorOfClusteringCoefficientsByDegree[ degree ] = 0; int n_k( 0 ); for( int index( 0 ); index < vectorOfDegree.size(); ++index ) { if( degree == vectorOfDegree[ index ] ) {// if in Y( degree ) vectorOfClusteringCoefficientsByDegree[ degree ] += vectorOfClusteringCoefficients[ index ]; n_k++; } } if( n_k != 0 ) { vectorOfClusteringCoefficientsByDegree[ degree ] = vectorOfClusteringCoefficientsByDegree[ degree ] / n_k; } } return vectorOfClusteringCoefficientsByDegree; } double mitk::ConnectomicsNetwork::GetGlobalClusteringCoefficient( ) { double globalClusteringCoefficient( 0.0 ); std::vector< double > vectorOfClusteringCoefficientsByDegree = GetClusteringCoefficientsByDegree(); std::vector< int > vectorOfDegree = GetDegreeOfNodes(); std::vector< int > degreeDistribution; degreeDistribution.resize( vectorOfClusteringCoefficientsByDegree.size(), 0 ); int normalizationParameter( 0 ); for( int index( 0 ); index < vectorOfDegree.size(); ++index ) { degreeDistribution[ vectorOfDegree[ index ] ]++; normalizationParameter++; } // c_{mean} = sum_{k} P_{k} c_{mean}(k) // where P_{k} is the degree distribution // k is the degree for( int degree( 0 ); degree < degreeDistribution.size(); ++degree ) { globalClusteringCoefficient += degreeDistribution[ degree ] / ( (double) normalizationParameter) * vectorOfClusteringCoefficientsByDegree[ degree ]; } return globalClusteringCoefficient; } mitk::ConnectomicsNetwork::NetworkType* mitk::ConnectomicsNetwork::GetBoostGraph() { return &m_Network; } bool mitk::ConnectomicsNetwork::GetIsModified() const { return m_IsModified; } void mitk::ConnectomicsNetwork::SetIsModified( bool value) { m_IsModified = value; } mitk::ConnectomicsNetwork::NetworkNode mitk::ConnectomicsNetwork::GetNode( VertexDescriptorType vertex ) const { return m_Network[ vertex ]; } mitk::ConnectomicsNetwork::NetworkEdge mitk::ConnectomicsNetwork::GetEdge( VertexDescriptorType vertexA, VertexDescriptorType vertexB ) const { return m_Network[ boost::edge(vertexA, vertexB, m_Network ).first ]; } void mitk::ConnectomicsNetwork::UpdateBounds( ) { float min = itk::NumericTraits::min(); float max = itk::NumericTraits::max(); float bounds[] = {max, min, max, min, max, min}; std::vector< mitk::ConnectomicsNetwork::NetworkNode > nodeVector = this->GetVectorOfAllNodes(); if( nodeVector.size() == 0 ) { bounds[0] = 0; bounds[1] = 1; bounds[2] = 0; bounds[3] = 1; bounds[4] = 0; bounds[5] = 1; } // for each direction, make certain the point is in between for( int index(0), end(nodeVector.size()) ; index < end; index++ ) { for( int direction(0); direction < nodeVector.at( index ).coordinates.size(); direction++ ) { if( nodeVector.at( index ).coordinates.at(direction) < bounds[ 2 * direction ] ) { bounds[ 2 * direction ] = nodeVector.at( index ).coordinates.at(direction); } if( nodeVector.at( index ).coordinates.at(direction) > bounds[ 2 * direction + 1] ) { bounds[ 2 * direction + 1] = nodeVector.at( index ).coordinates.at(direction); } } } // provide some border margin for(int i=0; i<=4; i+=2) { bounds[i] -=10; } for(int i=1; i<=5; i+=2) { bounds[i] +=10; } this->GetGeometry()->SetFloatBounds(bounds); this->GetTimeSlicedGeometry()->UpdateInformation(); } + +void mitk::ConnectomicsNetwork::PruneUnconnectedSingleNodes() +{ + boost::graph_traits::vertex_iterator iterator, end; + + // set to true if iterators are invalidated by deleting a vertex + bool vertexHasBeenRemoved( true ); + + // if no vertex has been removed in the last loop, we are done + while( vertexHasBeenRemoved ) + { + vertexHasBeenRemoved = false; + // sets iterator to start and end to end + boost::tie(iterator, end) = boost::vertices( m_Network ); + + for ( ; iterator != end && !vertexHasBeenRemoved; ++iterator) + { + // If the node has no adjacent vertices it should be deleted + if( GetVectorOfAdjacentNodes( *iterator ).size() == 0 ) + { + vertexHasBeenRemoved = true; + // this invalidates all iterators + boost::remove_vertex( *iterator, m_Network ); + } + } + } + + UpdateIDs(); +} + +void mitk::ConnectomicsNetwork::UpdateIDs() +{ + boost::graph_traits::vertex_iterator v_i, v_end; + boost::graph_traits::edge_iterator e_i, e_end; + + // update node ids + boost::tie( v_i, v_end ) = boost::vertices( m_Network ); + + for ( ; v_i != v_end; ++v_i) + { + m_Network[*v_i].id = *v_i; + } + + // update edge information + boost::tie(e_i, e_end) = boost::edges( m_Network ); + + for ( ; e_i != e_end; ++e_i) + { + m_Network[ *e_i ].sourceId = m_Network[ boost::source( *e_i, m_Network ) ].id; + m_Network[ *e_i ].targetId = m_Network[ boost::target( *e_i, m_Network ) ].id; + } + this->SetIsModified( true ); +} + +void mitk::ConnectomicsNetwork::PruneEdgesBelowWeight( int targetWeight ) +{ + boost::graph_traits::edge_iterator iterator, end; + + // set to true if iterators are invalidated by deleting a vertex + bool edgeHasBeenRemoved( true ); + + // if no vertex has been removed in the last loop, we are done + while( edgeHasBeenRemoved ) + { + edgeHasBeenRemoved = false; + // sets iterator to start and end to end + boost::tie(iterator, end) = boost::edges( m_Network ); + + for ( ; iterator != end && !edgeHasBeenRemoved; ++iterator) + { + // If the node has no adjacent edges it should be deleted + if( m_Network[ *iterator ].weight < targetWeight ) + { + edgeHasBeenRemoved = true; + // this invalidates all iterators + boost::remove_edge( *iterator, m_Network ); + } + } + } + + // this will remove any nodes which, after deleting edges are now + // unconnected, also this calls UpdateIDs() + PruneUnconnectedSingleNodes(); +} diff --git a/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.h b/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.h index ef9e157f8d..c9425a50a8 100644 --- a/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.h +++ b/Modules/DiffusionImaging/IODataStructures/ConnectomicsNetwork/mitkConnectomicsNetwork.h @@ -1,178 +1,196 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_ConnectomicsNetwork_H #define _MITK_ConnectomicsNetwork_H #include "MitkDiffusionImagingExports.h" #include "mitkBaseData.h" #include namespace mitk { /** * \brief Base Class for Connectomics Networks */ class MitkDiffusionImaging_EXPORT ConnectomicsNetwork : public BaseData { public: /** Structs for the graph */ /** The Node */ struct NetworkNode { int id; std::string label; std::vector< float > coordinates; }; /** The Edge */ struct NetworkEdge { int sourceId; int targetId; int weight; // For now the number of times it was present double edge_weight; // For boost, currently set to 1 by default for unweighted calculations }; /** Typedefs **/ //typedef boost::adjacency_list< boost::listS, boost::listS, boost::undirectedS, NetworkNode, NetworkEdge > NetworkType; typedef boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, NetworkNode, NetworkEdge > NetworkType; typedef boost::graph_traits::vertex_descriptor VertexDescriptorType; typedef boost::graph_traits::edge_descriptor EdgeDescriptorType; // virtual methods that need to be implemented virtual void UpdateOutputInformation(); virtual void SetRequestedRegionToLargestPossibleRegion(); virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); virtual bool VerifyRequestedRegion(); virtual void SetRequestedRegion( itk::DataObject *data ); // Macros mitkClassMacro( ConnectomicsNetwork, BaseData ); itkNewMacro( Self ); ////////////////// Interface /////////////////// /** return whether an edge exists between the two given vertices */ bool EdgeExists( VertexDescriptorType vertexA, VertexDescriptorType vertexB ) const; /** increase the weight of an edge between the two given vertices */ void IncreaseEdgeWeight( VertexDescriptorType vertexA, VertexDescriptorType vertexB ); /** add an edge between two given vertices */ void AddEdge( VertexDescriptorType vertexA, VertexDescriptorType vertexB); /** add an edge between two given vertices ( with a specific weight ) */ void AddEdge( VertexDescriptorType vertexA, VertexDescriptorType vertexB, int sourceID, int targetID, int weight = 1 ); /** add a vertex with a specified id */ VertexDescriptorType AddVertex( int id); /** set the label of a vertex */ void SetLabel( VertexDescriptorType vertex, std::string inLabel ); /** set the coordinates of a vertex */ void SetCoordinates( VertexDescriptorType vertex, std::vector< float > inCoordinates ); /** clear the graph */ void clear(); /** return the node struct for a given node descriptor */ NetworkNode GetNode( VertexDescriptorType vertex ) const; /** return the edge struct for two given node descriptors */ NetworkEdge GetEdge( VertexDescriptorType vertexA, VertexDescriptorType vertexB ) const; /** get vector containing all the nodes of the network */ std::vector< NetworkNode > GetVectorOfAllNodes() const; /** get vector containing all the vertex descriptors of the network */ std::vector< VertexDescriptorType > GetVectorOfAllVertexDescriptors() const; /** get vector containing the descriptors of nodes adjacent to the vertex denoted by the given descriptor */ std::vector< VertexDescriptorType > GetVectorOfAdjacentNodes( VertexDescriptorType vertex ) const; /** get vector containing all the edges of the network and the connected nodes */ std::vector< std::pair< std::pair< NetworkNode, NetworkNode > , NetworkEdge > > GetVectorOfAllEdges() const; /** get overall number of vertices in the network */ int GetNumberOfVertices() const; /** get overall number of edges in the network */ int GetNumberOfEdges(); /** get number of vertices, that are connected to themselves */ int GetNumberOfSelfLoops(); /** get number of vertices, that are connected to themselves */ double GetAverageDegree(); /** get number of edges divided by number of possible edges */ double GetConnectionDensity(); /** Get the maximum weight of all edges */ int GetMaximumWeight() const; /** Get a vector in the format vector[ vertexID ] = degree */ std::vector< int > GetDegreeOfNodes( ) const; /** Get the maximum degree of all nodes */ int GetMaximumDegree() const; /** Get a vector in the format vector[ vertexID ] = clustering coefficient */ std::vector< double > GetLocalClusteringCoefficients( ); /** Get a vector in the format vector[ degree ] = average clustering coefficient */ std::vector< double > GetClusteringCoefficientsByDegree( ); /** Get the global clustering coefficient */ double GetGlobalClusteringCoefficient( ); /** Access boost graph directly */ NetworkType* GetBoostGraph(); /** Get the modified flag */ bool GetIsModified() const; /** Set the modified flag */ void SetIsModified( bool ); /** Update the bounds of the geometry to fit the network */ void UpdateBounds( ); + /** Remove nodes not connected to any other node */ + void PruneUnconnectedSingleNodes(); + + /** Remove edges below the specified weight + * + * targetWeight is the number of connecting fibers + * + * This will remove unconnected nodes after removal + */ + void PruneEdgesBelowWeight( int targetWeight ); + protected: ConnectomicsNetwork(); virtual ~ConnectomicsNetwork(); + /** This function will relabel all vertices and edges in a continuous manner + * + * Mainly important after removing vertices, to make sure that the ids run continuously from + * 0 to number of vertices - 1 and edge target and source ids match the corresponding node. + */ + void UpdateIDs(); + NetworkType m_Network; /// Flag which indicates whether the network has been modified since the last check /// /// mainly for rendering purposes bool m_IsModified; private: }; } // namespace mitk #endif /* _MITK_ConnectomicsNetwork_H */ diff --git a/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp b/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp index 7bd21ff93e..d6d89badbe 100644 --- a/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp +++ b/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.cpp @@ -1,1128 +1,1066 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __itkDiffusionMultiShellQballReconstructionImageFilter_cpp #define __itkDiffusionMultiShellQballReconstructionImageFilter_cpp #include #include #include #include #include #include #include #include #include #include #include "mitkDiffusionFunctionCollection.h" #include "itkPointShell.h" #include +#include + namespace itk { template< class T, class TG, class TO, int L, int NODF> DiffusionMultiShellQballReconstructionImageFilter ::DiffusionMultiShellQballReconstructionImageFilter() : m_GradientDirectionContainer(NULL), m_NumberOfGradientDirections(0), m_NumberOfBaselineImages(1), m_Threshold(NumericTraits< ReferencePixelType >::NonpositiveMin()), m_BValue(1.0), m_Lambda(0.0), m_IsHemisphericalArrangementOfGradientDirections(false), m_IsArithmeticProgession(false), - m_ReconstructionType(Mode_Standard1Shell) + m_ReconstructionType(Mode_Standard1Shell), + m_Interpolation_Flag(false), + m_Interpolation_SHT1_inv(0), + m_Interpolation_SHT2_inv(0), + m_Interpolation_SHT3_inv(0), + m_Interpolation_TARGET_SH(0) { // At least 1 inputs is necessary for a vector image. // For images added one at a time we need at least six this->SetNumberOfRequiredInputs( 1 ); } template void DiffusionMultiShellQballReconstructionImageFilter ::Normalize( OdfPixelType & out) { for(int i=0; i -double DiffusionMultiShellQballReconstructionImageFilter -::CalculateThreashold(const double value, const double delta) -{ - return (value<0)*(0.5*delta) + (value>=0 && value=delta && value<1-delta)*value+(value>=1-delta && value<1)*(1-0.5*delta-0.5*((1-value)*(1-value))/delta) - + (value>=1)*(1-0.5*delta); -} - template void DiffusionMultiShellQballReconstructionImageFilter -::Threshold(vnl_vector & vec, double delta) +::Projection1(vnl_vector & vec, double delta) { if (delta==0){ //Clip attenuation values. If att<0 => att=0, if att>1 => att=1 for (int i=0; i=0 && vec[i]<=1)*vec[i]+(vec[i]>1); } else{ //Use function from Aganj et al, MRM, 2010 for (int i=0; i< vec.size(); i++) vec[i]=CalculateThreashold(vec[i], delta); } } template -void DiffusionMultiShellQballReconstructionImageFilter -::Threshold(vnl_matrix & mat, double delta) +double DiffusionMultiShellQballReconstructionImageFilter +::CalculateThreashold(const double value, const double delta) { - if (delta==0){ //Clip attenuation values. If att<0 => att=0, if att>1 => att=1 - for (int i=0; i=0 && mat(i,j)<=1)*mat(i,j)+(mat(i,j)>1); - } - else{ //Use function from Aganj et al, MRM, 2010 - for (int i=0; i=0 && value=delta && value<1-delta)*value+(value>=1-delta && value<1)*(1-0.5*delta-0.5*((1-value)*(1-value))/delta) + + (value>=1)*(1-0.5*delta); } template void DiffusionMultiShellQballReconstructionImageFilter -::Projection1( vnl_matrix & E, double delta ) +::Projection2( vnl_vector & E1,vnl_vector & E2, vnl_vector & E3, double delta ) { const double sF = sqrt(5.0); - vnl_vector vOnes(E.rows()); + vnl_vector vOnes(m_MaxDirections); vOnes.fill(1.0); - vnl_matrix T0(E.rows(), E.cols()); - vnl_matrix C(E.rows(), 7); - vnl_matrix A(E.rows(), 7); - vnl_matrix B(E.rows(), 7); + vnl_matrix T0(m_MaxDirections, 3); + vnl_matrix C(m_MaxDirections, 7); + vnl_matrix A(m_MaxDirections, 7); + vnl_matrix B(m_MaxDirections, 7); - vnl_vector s0(E.rows()); - vnl_vector a0(E.rows()); - vnl_vector b0(E.rows()); - vnl_vector ta(E.rows()); - vnl_vector tb(E.rows()); - vnl_vector e(E.rows()); - vnl_vector m(E.rows()); - vnl_vector a(E.rows()); - vnl_vector b(E.rows()); + vnl_vector s0(m_MaxDirections); + vnl_vector a0(m_MaxDirections); + vnl_vector b0(m_MaxDirections); + vnl_vector ta(m_MaxDirections); + vnl_vector tb(m_MaxDirections); + vnl_vector e(m_MaxDirections); + vnl_vector m(m_MaxDirections); + vnl_vector a(m_MaxDirections); + vnl_vector b(m_MaxDirections); // logarithmierung aller werte in E - for(int i = 0 ; i < E.rows(); i++) + for(int i = 0 ; i < m_MaxDirections; i++) { - for(int j = 0 ; j < E.cols(); j++) - { - T0(i,j) = -log(E(i,j)); - } + T0(i,0) = -log(E1(i)); + T0(i,1) = -log(E2(i)); + T0(i,2) = -log(E3(i)); } - //T0 = -T0.apply(std::log); + // Summeiere Zeilenweise über alle Shells sum = E1+E2+E3 - for(int i = 0 ; i < E.rows(); i++) + for(int i = 0 ; i < m_MaxDirections; i++) { s0[i] = T0(i,0) + T0(i,1) + T0(i,2); } - - for(int i = 0; i < E.rows(); i ++) + for(int i = 0; i < m_MaxDirections; i ++) { // Alle Signal-Werte auf der Ersten shell E(N,0) normiert auf s0 - a0 = E(i,0) / s0[i]; + a0[i] = T0(i,0) / s0[i]; // Alle Signal-Werte auf der Zweiten shell E(N,1) normiert auf s0 - b0 = E(i,1) / s0[i]; + b0[i] = T0(i,1) / s0[i]; } ta = a0 * 3.0; tb = b0 * 3.0; e = tb - (ta * 2.0); m = (tb * 2.0 ) + ta; - for(int i = 0; i < E.rows(); i++) + for(int i = 0; i = 1-3*(sF+2)*delta; - C(i,2) = m[i] > 3 -3*sF*delta && -1+3*(2*sF+5)*delta < e[i] && e[i]<-3*sF*delta; - C(i,3) = m[i] >= 3-3*sF*delta && e[i] >= -3 *sF * delta; - C(i,4) = 2.5 + 1.5*(5+sF)*delta < m[i] && m[i] < 3-3*sF*delta && e[i] > -3*sF*delta; - C(i,5) = ta[i] <= 0.5+1.5 *(sF+1)*delta && m[i] <= 2.5 + 1.5 *(5+sF) * delta; - C(i,6) = !( C(i,0) || C(i,1) || C(i,2) || C(i,3) || C(i,4) || C(i,5) ); // ~ANY(C(i,[0-5] ),2) + C(i,0) = (tb[i] < 1+3*delta && 0.5+1.5*(sF+1)*delta < ta[i] && ta[i] < 1-3* (sF+2) *delta); + C(i,1) = (e[i] <= -1+3*(2*sF+5)*delta) && (ta[i]>=1-3*(sF+2)*delta); + C(i,2) = (m[i] > 3-3*sF*delta) && (-1+3*(2*sF+5)*delta= 3-3*sF*delta && e[i] >= -3 *sF * delta); + C(i,4) = (2.5 + 1.5*(5+sF)*delta < m[i] && m[i] < 3-3*sF*delta && e[i] > -3*sF*delta); + C(i,5) = (ta[i] <= 0.5+1.5 *(sF+1)*delta && m[i] <= 2.5 + 1.5 *(5+sF) * delta); + C(i,6) = !((bool) C(i,0) ||(bool) C(i,1) ||(bool) C(i,2) ||(bool) C(i,3) ||(bool) C(i,4) ||(bool) C(i,5) ); // ~ANY(C(i,[0-5] ),2) A(i,0)=(bool)C(i,0) * a0(i); A(i,1)=(bool)C(i,1) * (1.0/3.0-(sF+2)*delta); A(i,2)=(bool)C(i,2) * (0.2+0.8*a0(i)-0.4*b0(i)-delta/sF); A(i,3)=(bool)C(i,3) * (0.2+delta/sF); A(i,4)=(bool)C(i,4) * (0.2*a0(i)+0.4*b0(i)+2*delta/sF); A(i,5)=(bool)C(i,5) * (1.0/6.0+0.5*(sF+1)*delta); A(i,6)=(bool)C(i,6) * a0(i); B(i,0)=(bool)C(i,0) * (1.0/3.0+delta); B(i,1)=(bool)C(i,1) * (1.0/3.0+delta); B(i,2)=(bool)C(i,2) * (0.4-0.4*a0(i)+0.2*b0(i)-2*delta/sF); B(i,3)=(bool)C(i,3) * (0.4-3*delta/sF); B(i,4)=(bool)C(i,4) * (0.4*a0(i)+0.8*b0(i)-delta/sF); B(i,5)=(bool)C(i,5) * (1.0/3.0+delta); B(i,6)=(bool)C(i,6) * b0(i); } - - - for(int i = 0 ; i < E.rows(); i++) + for(int i = 0 ; i < m_MaxDirections; i++) { double sumA = 0; double sumB = 0; for(int j = 0 ; j < 7; j++) { sumA += A(i,j); sumB += B(i,j); } a[i] = sumA; b[i] = sumB; } - for(int i = 0; i < E.rows(); i++) + for(int i = 0; i < m_MaxDirections; i++) { - E(i,0) = exp(-(a[i]*s0[i])); - E(i,1) = exp(-(b[i]*s0[i])); - E(i,2) = exp(-((1-a[i]-b[i])*s0[i])); + E1(i) = exp(-(a[i]*s0[i])); + E2(i) = exp(-(b[i]*s0[i])); + E3(i) = exp(-((1-a[i]-b[i])*s0[i])); } } - - - template void DiffusionMultiShellQballReconstructionImageFilter -::Projection2( vnl_vector & A, vnl_vector & a, vnl_vector & b, double delta0) +::Projection3( vnl_vector & A, vnl_vector & a, vnl_vector & b, double delta0) { const double s6 = sqrt(6.0); const double s15 = s6/2.0; vnl_vector delta(a.size()); delta.fill(delta0); vnl_matrix AM(a.size(), 15); vnl_matrix aM(a.size(), 15); vnl_matrix bM(a.size(), 15); vnl_matrix B(a.size(), 15); AM.set_column(0, A); AM.set_column(1, A); AM.set_column(2, A); AM.set_column(3, delta); AM.set_column(4, (A+a-b - (delta*s6))/3.0); AM.set_column(5, delta); AM.set_column(6, delta); AM.set_column(7, delta); AM.set_column(8, A); AM.set_column(9, 0.2*(a*2+A-2*(s6+1)*delta)); AM.set_column(10,0.2*(b*(-2)+A+2-2*(s6+1)*delta)); AM.set_column(11, delta); AM.set_column(12, delta); AM.set_column(13, delta); AM.set_column(14, 0.5-(1+s15)*delta); aM.set_column(0, a); aM.set_column(1, a); aM.set_column(2, -delta + 1); aM.set_column(3, a); aM.set_column(4, (A*2+a*5+b+s6*delta)/6.0); aM.set_column(5, a); aM.set_column(6, -delta + 1); aM.set_column(7, 0.5*(a+b)+(1+s15)*delta); aM.set_column(8, -delta + 1); aM.set_column(9, 0.2*(a*4+A*2+(s6+1)*delta)); aM.set_column(10, -delta + 1); aM.set_column(11, (s6+3)*delta); aM.set_column(12, -delta + 1); aM.set_column(13, -delta + 1); aM.set_column(14, -delta + 1); bM.set_column(0, b); bM.set_column(1, delta); bM.set_column(2, b); bM.set_column(3, b); bM.set_column(4, (A*(-2)+a+b*5-s6*delta)/6.0); bM.set_column(5, delta); bM.set_column(6, b); bM.set_column(7, 0.5*(a+b)-(1+s15)*delta); bM.set_column(8, delta); bM.set_column(9, delta); bM.set_column(10, 0.2*(b*4-A*2+1-(s6+1)*delta)); bM.set_column(11, delta); bM.set_column(12, delta); bM.set_column(13, -delta*(s6+3) + 1); bM.set_column(14, delta); delta0 *= 0.99; - for(int i = 0 ; i < a.size(); i ++) - { - for(int j = 0 ; j < 15; j ++) - { - B(i,j) = delta0 < AM(i,j) && 2 * (AM(i,j) + delta0 * s15) < aM(i,j) - bM(i,j) && bM(i,j) > delta0 && aM(i,j) < 1- delta0; - } - } - vnl_matrix R2(a.size(), 15); - vnl_matrix A_(a.size(), 15); - vnl_matrix a_(a.size(), 15); - vnl_matrix b_(a.size(), 15); - - - - vnl_matrix OnesVecMat(1, 15); - OnesVecMat.fill(1.0); - - vnl_matrix AVecMat(a.size(), 1); - AVecMat.set_column(0,A); - - vnl_matrix aVecMat(a.size(), 1); - aVecMat.set_column(0,a); - - vnl_matrix bVecMat(a.size(), 1); - bVecMat.set_column(0,b); - - A_ = AM - (AVecMat * OnesVecMat); - a_ = aM - (aVecMat * OnesVecMat); - b_ = bM - (bVecMat * OnesVecMat); - - for(int i = 0 ; i < a.size(); i++) - for(int j = 0 ; j < 15; j++) - { - A_(i,j) *= A_(i,j); - a_(i,j) *= a_(i,j); - b_(i,j) *= b_(i,j); + std::vector I(a.size()); + + for (int i=0; idelta0 && aM(i,j)<1-delta0) + R2(i,j) = (AM(i,j)-A(i))*(AM(i,j)-A(i))+ (aM(i,j)-a(i))*(aM(i,j)-a(i))+(bM(i,j)-b(i))*(bM(i,j)-b(i)); + else + R2(i,j) = 1e20; } - - - R2 = A_ + a_ + b_; - - for(int i = 0 ; i < a.size(); i ++) - { - for(int j = 0 ; j < 15; j ++) - { - if(B(i,j) == 0) R2(i,j) = 1e20; - } - } - - std::vector indicies(a.size()); - - // suche den spalten-index der zu der kleinsten Zahl einer Zeile korrespondiert - for(int i = 0 ; i < a.size(); i++) - { unsigned int index = 0; double minvalue = 999; for(int j = 0 ; j < 15 ; j++) { if(R2(i,j) < minvalue){ minvalue = R2(i,j); index = j; } } - indicies[i] = index; + I[i] = index; } - for(int i = 0 ; i < a.size(); i++) - { - A[i] = AM(i,indicies[i]); - a[i] = aM(i,indicies[i]); - b[i] = bM(i,indicies[i]); + for (int i=0; i < A.size(); i++){ + A(i) = AM(i,(int)I[i]); + a(i) = aM(i,(int)I[i]); + b(i) = bM(i,(int)I[i]); } -} +} template void DiffusionMultiShellQballReconstructionImageFilter -::S_S0Normalization( vnl_vector & vec, typename NumericTraits::AccumulateType b0 ) +::S_S0Normalization( vnl_vector & vec, double S0 ) { - - double b0f = (double)b0; for(int i = 0; i < vec.size(); i++) { - if (b0f==0) - b0f = 0.01; - if(vec[i] >= b0f) - vec[i] = b0f - 0.001; - vec[i] /= b0f; + if (S0==0) + S0 = 0.01; + vec[i] /= S0; } } - -template -void DiffusionMultiShellQballReconstructionImageFilter -::S_S0Normalization( vnl_matrix & mat, typename NumericTraits::AccumulateType b0 ) -{ - double b0f = (double)b0; - for(int i = 0; i < mat.rows(); i++) - { - for( int j = 0; j < mat.cols(); j++ ){ - if (b0f==0) - b0f = 0.01; - if(mat(i,j) >= b0f) - mat(i,j) = b0f - 0.001; - mat(i,j) /= b0f; - } - } -} - - - template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::DoubleLogarithm(vnl_vector & vec) { for(int i = 0; i < vec.size(); i++) { vec[i] = log(-log(vec[i])); } } template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::SetGradientImage( GradientDirectionContainerType *gradientDirection , const GradientImagesType *gradientImage , float bvalue) { - this->m_BValue = bvalue; - this->m_GradientDirectionContainer = gradientDirection; - this->m_NumberOfBaselineImages = 0; - this->m_ReconstructionType = Mode_Standard1Shell; + m_BValue = bvalue; + m_GradientDirectionContainer = gradientDirection; + m_NumberOfBaselineImages = 0; + if(m_BValueMap.size() == 0){ itkWarningMacro(<< "DiffusionMultiShellQballReconstructionImageFilter.cpp : no GradientIndexMapAvalible"); GradientDirectionContainerType::ConstIterator gdcit; - for( gdcit = this->m_GradientDirectionContainer->Begin(); gdcit != this->m_GradientDirectionContainer->End(); ++gdcit) + for( gdcit = m_GradientDirectionContainer->Begin(); gdcit != m_GradientDirectionContainer->End(); ++gdcit) { double bValueKey = int(((m_BValue * gdcit.Value().two_norm() * gdcit.Value().two_norm())+7.5)/10)*10; m_BValueMap[bValueKey].push_back(gdcit.Index()); } } - if(m_BValueMap.find(0) == m_BValueMap.end()) { itkExceptionMacro(<< "DiffusionMultiShellQballReconstructionImageFilter.cpp : GradientIndxMap with no b-Zero indecies found: check input image"); } + + m_NumberOfBaselineImages = m_BValueMap[0].size(); + m_NumberOfGradientDirections = gradientDirection->Size() - m_NumberOfBaselineImages; + // ensure that the gradient image we received has as many components as + // the number of gradient directions + if( gradientImage->GetVectorLength() != m_NumberOfBaselineImages + m_NumberOfGradientDirections ) + { + itkExceptionMacro( << m_NumberOfGradientDirections << " gradients + " << m_NumberOfBaselineImages + << "baselines = " << m_NumberOfGradientDirections + m_NumberOfBaselineImages + << " directions specified but image has " << gradientImage->GetVectorLength() + << " components."); + } + + + ProcessObject::SetNthInput( 0, const_cast< GradientImagesType* >(gradientImage) ); + + std::string gradientImageClassName(ProcessObject::GetInput(0)->GetNameOfClass()); + if ( strcmp(gradientImageClassName.c_str(),"VectorImage") != 0 ) + itkExceptionMacro( << "There is only one Gradient image. I expect that to be a VectorImage. But its of type: " << gradientImageClassName ); + + + m_BZeroImage = BZeroImageType::New(); + typename GradientImagesType::Pointer img = static_cast< GradientImagesType * >( ProcessObject::GetInput(0) ); + m_BZeroImage->SetSpacing( img->GetSpacing() ); // Set the image spacing + m_BZeroImage->SetOrigin( img->GetOrigin() ); // Set the image origin + m_BZeroImage->SetDirection( img->GetDirection() ); // Set the image direction + m_BZeroImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); + m_BZeroImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); + m_BZeroImage->Allocate(); + +} + +template< class T, class TG, class TO, int L, int NODF> +void DiffusionMultiShellQballReconstructionImageFilter +::BeforeThreadedGenerateData() +{ + m_ReconstructionType = Mode_Standard1Shell; + if(m_BValueMap.size() == 4 ){ BValueMapIteraotr it = m_BValueMap.begin(); + it++; // skip b0 entry + const int b1 = it->first; + const int vecSize1 = it->second.size(); + IndiciesVector shell1 = it->second; it++; - const int b1 = (*it).first; - it++; - const int b2 = (*it).first; + const int b2 = it->first; + const int vecSize2 = it->second.size(); + IndiciesVector shell2 = it->second; it++; - const int b3 = (*it).first; + const int b3 = it->first; + const int vecSize3 = it->second.size(); + IndiciesVector shell3 = it->second; + // arithmetic progrssion if(b2 - b1 == b1 && b3 - b2 == b1 ) { + // check if Interpolation is needed + // if shells with different numbers of directions exist + m_Interpolation_Flag = false; + if(vecSize1 != vecSize2 || vecSize2 != vecSize3 || vecSize1 != vecSize3) + { + m_Interpolation_Flag = true; + MITK_INFO << "Shell interpolation: shells with different numbers of directions"; + }else // if each shell holds same numbers of directions, but the gradient direction differ more than one 1 degree + { + m_Interpolation_Flag = CheckForDifferingShellDirections(); + if(m_Interpolation_Flag) MITK_INFO << "Shell interpolation: gradient direction differ more than one 1 degree"; + } + m_ReconstructionType = Mode_Analytical3Shells; + + if(m_Interpolation_Flag) + { + IndiciesVector min_shell; + IndiciesVector max_shell; + int Interpolation_SHOrder = 10; + + //fewer directions + if (vecSize1 <= vecSize2 ) { min_shell = shell1;} + else { min_shell = shell2;} + if (min_shell.size() > vecSize3){ min_shell = shell3;} + + //most directions + if (vecSize1 >= vecSize2 ) { max_shell = shell1;} + else { max_shell = shell2;} + if (max_shell.size() < vecSize3){ max_shell = shell3;} + + m_MaxDirections = max_shell.size(); + + //SH-order determination + while( ((Interpolation_SHOrder+1)*(Interpolation_SHOrder+2)/2) > min_shell.size() && Interpolation_SHOrder > L ) + Interpolation_SHOrder -= 2 ; + + MITK_INFO << "Interpolation enabeled, using SH of order : " << Interpolation_SHOrder; + + // create target SH-Basis + vnl_matrix * Q = new vnl_matrix(3, max_shell.size()); + ComputeSphericalFromCartesian(Q, max_shell); + + int NumberOfCoeffs = (int)(Interpolation_SHOrder*Interpolation_SHOrder + Interpolation_SHOrder + 2.0)/2.0 + Interpolation_SHOrder; + m_Interpolation_TARGET_SH = new vnl_matrix(max_shell.size(), NumberOfCoeffs); + ComputeSphericalHarmonicsBasis(Q, m_Interpolation_TARGET_SH, Interpolation_SHOrder); + delete Q; + // end creat target SH-Basis + + // create measured-SHBasis + // Shell 1 + vnl_matrix * tempSHBasis; + vnl_matrix_inverse * temp; + + Q = new vnl_matrix(3, shell1.size()); + ComputeSphericalFromCartesian(Q, shell1); + + tempSHBasis = new vnl_matrix(shell1.size(), NumberOfCoeffs); + ComputeSphericalHarmonicsBasis(Q, tempSHBasis, Interpolation_SHOrder); + temp = new vnl_matrix_inverse((*tempSHBasis)); + + m_Interpolation_SHT1_inv = new vnl_matrix(temp->inverse()); + + delete Q; + delete temp; + delete tempSHBasis; + + // Shell 2 + Q = new vnl_matrix(3, shell2.size()); + ComputeSphericalFromCartesian(Q, shell2); + + tempSHBasis = new vnl_matrix(shell2.size(), NumberOfCoeffs); + ComputeSphericalHarmonicsBasis(Q, tempSHBasis, Interpolation_SHOrder); + temp = new vnl_matrix_inverse((*tempSHBasis)); + + m_Interpolation_SHT2_inv = new vnl_matrix(temp->inverse()); + + delete Q; + delete temp; + delete tempSHBasis; + + // Shell 3 + Q = new vnl_matrix(3, shell3.size()); + ComputeSphericalFromCartesian(Q, shell3); + + tempSHBasis = new vnl_matrix(shell3.size(), NumberOfCoeffs); + + ComputeSphericalHarmonicsBasis(Q, tempSHBasis, Interpolation_SHOrder); + + temp = new vnl_matrix_inverse((*tempSHBasis)); + + m_Interpolation_SHT3_inv = new vnl_matrix(temp->inverse()); + + delete Q; + delete temp; + delete tempSHBasis; + + ComputeReconstructionMatrix(max_shell); + return; + }else + { + ComputeReconstructionMatrix(shell1); + } } } if(m_BValueMap.size() > 2 && m_ReconstructionType != Mode_Analytical3Shells) { m_ReconstructionType = Mode_NumericalNShells; } - - this->m_NumberOfBaselineImages = m_BValueMap[0].size(); - this->m_NumberOfGradientDirections = gradientDirection->Size() - this->m_NumberOfBaselineImages; - - // ensure that the gradient image we received has as many components as - // the number of gradient directions - if( gradientImage->GetVectorLength() != this->m_NumberOfBaselineImages + m_NumberOfGradientDirections ) - { - itkExceptionMacro( << m_NumberOfGradientDirections << " gradients + " << this->m_NumberOfBaselineImages - << "baselines = " << m_NumberOfGradientDirections + this->m_NumberOfBaselineImages - << " directions specified but image has " << gradientImage->GetVectorLength() - << " components."); + if(m_BValueMap.size() == 2){ + BValueMapIteraotr it = m_BValueMap.begin(); + it++; // skip b0 entry + IndiciesVector shell = it->second; + ComputeReconstructionMatrix(shell); } - this->ProcessObject::SetNthInput( 0, const_cast< GradientImagesType* >(gradientImage) ); } + template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter -::BeforeThreadedGenerateData() +::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, int NumberOfThreads) { itk::TimeProbe clock; clock.Start(); - - if( m_NumberOfGradientDirections < (((L+1)*(L+2))/2) /* && m_NumberOfGradientDirections < 6 */ ) + switch(m_ReconstructionType) { - itkExceptionMacro( << "At least " << ((L+1)*(L+2))/2 << " gradient directions are required" ); + case Mode_Standard1Shell: + StandardOneShellReconstruction(outputRegionForThread); + break; + case Mode_Analytical3Shells: + AnalyticalThreeShellReconstruction(outputRegionForThread); + break; + case Mode_NumericalNShells: + break; } - // Input must be an itk::VectorImage. - std::string gradientImageClassName(this->ProcessObject::GetInput(0)->GetNameOfClass()); - - if ( strcmp(gradientImageClassName.c_str(),"VectorImage") != 0 ) - itkExceptionMacro( << "There is only one Gradient image. I expect that to be a VectorImage. But its of type: " << gradientImageClassName ); - - m_BZeroImage = BZeroImageType::New(); - typename GradientImagesType::Pointer img = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); - m_BZeroImage->SetSpacing( img->GetSpacing() ); // Set the image spacing - m_BZeroImage->SetOrigin( img->GetOrigin() ); // Set the image origin - m_BZeroImage->SetDirection( img->GetDirection() ); // Set the image direction - m_BZeroImage->SetLargestPossibleRegion( img->GetLargestPossibleRegion()); - m_BZeroImage->SetBufferedRegion( img->GetLargestPossibleRegion() ); - m_BZeroImage->Allocate(); - - this->ComputeReconstructionMatrix(); - clock.Stop(); - MITK_INFO << "Before GenerateData : " << clock.GetTotal(); - + MITK_INFO << "Reconstruction in : " << clock.GetTotal() << " s"; } - template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::StandardOneShellReconstruction(const OutputImageRegionType& outputRegionForThread) { // Get output image pointer - typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); + typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(ProcessObject::GetOutput(0)); + // Get input gradient image pointer + typename GradientImagesType::Pointer gradientImagePointer = static_cast< GradientImagesType * >( ProcessObject::GetInput(0) ); // ImageRegionIterator for the output image ImageRegionIterator< OutputImageType > oit(outputImage, outputRegionForThread); oit.GoToBegin(); // ImageRegionIterator for the BZero (output) image - ImageRegionConstIterator< BZeroImageType > bzeroImageIterator(m_BZeroImage, outputRegionForThread); - bzeroImageIterator.GoToBegin(); - - IndiciesVector SignalIndicies = m_BValueMap[1]; - - // if the gradient directiosn aragement is hemispherical, duplicate all gradient directions - // alone, interested in the value, the direction can be neglected - if(m_IsHemisphericalArrangementOfGradientDirections){ - int NumbersOfGradientIndicies = SignalIndicies.size(); - for (int i = 0 ; i < NumbersOfGradientIndicies; i++) - SignalIndicies.push_back(SignalIndicies[i]); - } - - - // Get input gradient image pointer - typename GradientImagesType::Pointer gradientImagePointer = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); + ImageRegionIterator< BZeroImageType > bzeroIterator(m_BZeroImage, outputRegionForThread); + bzeroIterator.GoToBegin(); // Const ImageRegionIterator for input gradient image typedef ImageRegionConstIterator< GradientImagesType > GradientIteratorType; GradientIteratorType git(gradientImagePointer, outputRegionForThread ); git.GoToBegin(); + BValueMapIteraotr it = m_BValueMap.begin(); + it++; // skip b0 entry + IndiciesVector SignalIndicies = it->second; + IndiciesVector BZeroIndicies = m_BValueMap[0]; + + int NumbersOfGradientIndicies = SignalIndicies.size(); + typedef typename GradientImagesType::PixelType GradientVectorType; // iterate overall voxels of the gradient image region while( ! git.IsAtEnd() ) { GradientVectorType b = git.Get(); // ODF Vector OdfPixelType odf(0.0); + double b0average = 0; + const int b0size = BZeroIndicies.size(); + for(unsigned int i = 0; i SignalVector(m_NumberOfGradientDirections); - if( (bzeroImageIterator.Get() != 0) && (bzeroImageIterator.Get() >= m_Threshold) ) + vnl_vector SignalVector(NumbersOfGradientIndicies); + if( (b0average != 0) && (b0average >= m_Threshold) ) { for( unsigned int i = 0; i< SignalIndicies.size(); i++ ) { SignalVector[i] = static_cast(b[SignalIndicies[i]]); } // apply threashold an generate ln(-ln(E)) signal // Replace SignalVector with PreNormalized SignalVector - S_S0Normalization(SignalVector, bzeroImageIterator.Get()); - DoubleLogarithm(SignalVector); + S_S0Normalization(SignalVector, b0average); + Projection1(SignalVector); - // ODF coeffs-vector - vnl_vector coeffs(m_NumberCoefficients); + DoubleLogarithm(SignalVector); // approximate ODF coeffs - coeffs = ( (*m_CoeffReconstructionMatrix) * SignalVector ); + vnl_vector coeffs = ( (*m_CoeffReconstructionMatrix) * SignalVector ); coeffs[0] = 1.0/(2.0*sqrt(QBALL_ANAL_RECON_PI)); odf = element_cast(( (*m_ODFSphericalHarmonicBasisMatrix) * coeffs )).data_block(); odf *= (QBALL_ANAL_RECON_PI*4/NODF); } // set ODF to ODF-Image oit.Set( odf ); ++oit; - ++bzeroImageIterator; ++git; } MITK_INFO << "One Thread finished reconstruction"; } -#include -//#include -//#include - - template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::NumericalNShellReconstruction(const OutputImageRegionType& outputRegionForThread) { // vnl_levenberg_marquardt LMOptimizer = new vnl_levenberg_marquardt(); } -template< class T, class TG, class TO, int L, int NODF> -void DiffusionMultiShellQballReconstructionImageFilter -::GenerateAveragedBZeroImage(const OutputImageRegionType& outputRegionForThread) -{ - typedef typename GradientImagesType::PixelType GradientVectorType; - - ImageRegionIterator< BZeroImageType > bzeroIterator(m_BZeroImage, outputRegionForThread); - bzeroIterator.GoToBegin(); - - IndiciesVector BZeroIndicies = m_BValueMap[0]; - - typename GradientImagesType::Pointer gradientImagePointer = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); - - // Const ImageRegionIterator for input gradient image - typedef ImageRegionConstIterator< GradientImagesType > GradientIteratorType; - GradientIteratorType git(gradientImagePointer, outputRegionForThread ); - git.GoToBegin(); - - - while( ! git.IsAtEnd() ) - { - GradientVectorType b = git.Get(); - - // compute the average bzero signal - typename NumericTraits::AccumulateType b0 = NumericTraits::Zero; - for(unsigned int i = 0; i < BZeroIndicies.size(); ++i) - { - b0 += b[BZeroIndicies[i]]; - } - b0 /= BZeroIndicies.size(); - - bzeroIterator.Set(b0); - ++bzeroIterator; - ++git; - } -} template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::AnalyticalThreeShellReconstruction(const OutputImageRegionType& outputRegionForThread) { - - typedef typename GradientImagesType::PixelType GradientVectorType; - // Input Gradient Image and Output ODF Image - typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); - typename GradientImagesType::Pointer gradientImagePointer = static_cast< GradientImagesType * >( this->ProcessObject::GetInput(0) ); - + typedef typename GradientImagesType::PixelType GradientVectorType; + typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(ProcessObject::GetOutput(0)); + typename GradientImagesType::Pointer gradientImagePointer = static_cast< GradientImagesType * >( ProcessObject::GetInput(0) ); // Define Image iterators ImageRegionIterator< OutputImageType > odfOutputImageIterator(outputImage, outputRegionForThread); - ImageRegionConstIterator< BZeroImageType > bzeroImageIterator(m_BZeroImage, outputRegionForThread); ImageRegionConstIterator< GradientImagesType > gradientInputImageIterator(gradientImagePointer, outputRegionForThread ); + ImageRegionIterator< BZeroImageType > bzeroIterator(m_BZeroImage, outputRegionForThread); // All iterators seht to Begin of the specific OutputRegion + bzeroIterator.GoToBegin(); odfOutputImageIterator.GoToBegin(); - bzeroImageIterator.GoToBegin(); gradientInputImageIterator.GoToBegin(); // Get Shell Indicies for all non-BZero Gradients // it MUST be a arithmetic progression eg.: 1000, 2000, 3000 BValueMapIteraotr it = m_BValueMap.begin(); it++; // it = b-value = 1000 - IndiciesVector Shell1Indiecies = (*it).second; + IndiciesVector Shell1Indiecies = it->second; it++; // it = b-value = 2000 - IndiciesVector Shell2Indiecies = (*it).second; + IndiciesVector Shell2Indiecies = it->second; it++; // it = b-value = 3000 - IndiciesVector Shell3Indiecies = (*it).second; + IndiciesVector Shell3Indiecies = it->second; + IndiciesVector BZeroIndicies = m_BValueMap[0]; + if(!m_Interpolation_Flag) + { + m_MaxDirections = Shell1Indiecies.size(); + }// else: m_MaxDirection is set in BeforeThreadedGenerateData - // if input data is a hemispherical arragement, duplicate eache gradient for each shell - if(m_IsHemisphericalArrangementOfGradientDirections){ - int NumbersOfGradientIndicies = Shell1Indiecies.size(); - for (int i = 0 ; i < NumbersOfGradientIndicies; i++){ - Shell1Indiecies.push_back(Shell1Indiecies[i]); - Shell2Indiecies.push_back(Shell2Indiecies[i]); - Shell3Indiecies.push_back(Shell3Indiecies[i]); - } - } + // Nx3 Signal Matrix with E(0) = Shell 1, E(1) = Shell 2, E(2) = Shell 3 + vnl_vector< double > E1(m_MaxDirections); + vnl_vector< double > E2(m_MaxDirections); + vnl_vector< double > E3(m_MaxDirections); - // Nx3 Signal Vector with E(0) = Shell 1, E(1) = Shell 2, E(2) = Shell 3 - vnl_matrix< double > * E = new vnl_matrix(Shell1Indiecies.size(), 3); + vnl_vector AlphaValues(m_MaxDirections); + vnl_vector BetaValues(m_MaxDirections); + vnl_vector LAValues(m_MaxDirections); + vnl_vector PValues(m_MaxDirections); - vnl_vector * AlphaValues = new vnl_vector(Shell1Indiecies.size()); - vnl_vector * BetaValues = new vnl_vector(Shell1Indiecies.size()); - vnl_vector * LAValues = new vnl_vector(Shell1Indiecies.size()); - vnl_vector * PValues = new vnl_vector(Shell1Indiecies.size()); + vnl_vector DataShell1(Shell1Indiecies.size()); + vnl_vector DataShell2(Shell2Indiecies.size()); + vnl_vector DataShell3(Shell3Indiecies.size()); OdfPixelType odf(0.0); + double P2,A,B2,B,P,alpha,beta,lambda, ER1, ER2; + + // iterate overall voxels of the gradient image region while( ! gradientInputImageIterator.IsAtEnd() ) { - if( (bzeroImageIterator.Get() != 0) && (bzeroImageIterator.Get() >= m_Threshold) ) + + GradientVectorType b = gradientInputImageIterator.Get(); + + // calculate for each shell the corresponding b0-averages + double shell1b0Norm =0; + double shell2b0Norm =0; + double shell3b0Norm =0; + double b0average = 0; + const int b0size = BZeroIndicies.size(); + for(unsigned int i = 0; i = b0size / 3 && i < (b0size / 3)*2) shell2b0Norm += b[BZeroIndicies[i]]; + if(i >= (b0size / 3) * 2) shell3b0Norm += b[BZeroIndicies[i]]; + } + shell1b0Norm /= (BZeroIndicies.size()/3); + shell2b0Norm /= (BZeroIndicies.size()/3); + shell3b0Norm /= (BZeroIndicies.size()/3); + b0average = (shell1b0Norm + shell2b0Norm+ shell3b0Norm)/3; + bzeroIterator.Set(b0average); + ++bzeroIterator; + + if( (b0average != 0) && ( b0average >= m_Threshold) ) { // Get the Signal-Value for each Shell at each direction (specified in the ShellIndicies Vector .. this direction corresponse to this shell...) - GradientVectorType b = gradientInputImageIterator.Get(); + + ///fsl fix --------------------------------------------------- for(int i = 0 ; i < Shell1Indiecies.size(); i++) - { - E->put(i,0, static_cast(b[Shell1Indiecies[i]])); - E->put(i,1, static_cast(b[Shell2Indiecies[i]])); - E->put(i,2, static_cast(b[Shell3Indiecies[i]])); - } + DataShell1[i] = static_cast(b[Shell1Indiecies[i]]); + for(int i = 0 ; i < Shell2Indiecies.size(); i++) + DataShell2[i] = static_cast(b[Shell2Indiecies[i]]); + for(int i = 0 ; i < Shell3Indiecies.size(); i++) + DataShell3[i] = static_cast(b[Shell2Indiecies[i]]); - //Approximated-Signal by SH fit - using the specific shell directions and values - // approximated Signal : S = SHBasis * Coeffs - // with Coeffs : C = (B_T * B + lambda * L) ^ -1 * B_T * OS - // OS := Original-Signal - E->set_column(1, (*m_SHBasisMatrix) * ((*m_SignalReonstructionMatrix) * (E->get_column(1)))); - E->set_column(2, (*m_SHBasisMatrix) * ((*m_SignalReonstructionMatrix) * (E->get_column(2)))); + // Normalize the Signal: Si/S0 + S_S0Normalization(DataShell1, shell1b0Norm); + S_S0Normalization(DataShell2, shell2b0Norm); + S_S0Normalization(DataShell3, shell2b0Norm); + //fsl fix -------------------------------------------ende-- + + /* correct version + for(int i = 0 ; i < Shell1Indiecies.size(); i++) + DataShell1[i] = static_cast(b[Shell1Indiecies[i]]); + for(int i = 0 ; i < Shell2Indiecies.size(); i++) + DataShell2[i] = static_cast(b[Shell2Indiecies[i]]); + for(int i = 0 ; i < Shell3Indiecies.size(); i++) + DataShell3[i] = static_cast(b[Shell3Indiecies[i]]); // Normalize the Signal: Si/S0 - S_S0Normalization(*E,bzeroImageIterator.Get()); + S_S0Normalization(DataShell1, shell1b0Norm); + S_S0Normalization(DataShell2, shell2b0Norm); + S_S0Normalization(DataShell3, shell3b0Norm); + */ - //Implements Eq. [19] and Fig. 4. - Threshold(*E); + if(m_Interpolation_Flag) + { + E1 = ((*m_Interpolation_TARGET_SH) * (*m_Interpolation_SHT1_inv) * (DataShell1)); + E2 = ((*m_Interpolation_TARGET_SH) * (*m_Interpolation_SHT2_inv) * (DataShell2)); + E3 = ((*m_Interpolation_TARGET_SH) * (*m_Interpolation_SHT3_inv) * (DataShell3)); + }else{ + E1 = (DataShell1); + E2 = (DataShell2); + E3 = (DataShell3); + } + //Implements Eq. [19] and Fig. 4. + Projection1(E1); + Projection1(E2); + Projection1(E3); //inqualities [31]. Taking the lograithm of th first tree inqualities //convert the quadratic inqualities to linear ones. - Projection1(*E); - - double E1, E2, E3, P2,A,B2,B,P,alpha,beta,lambda, ER1, ER2; + Projection2(E1,E2,E3); - for( unsigned int i = 0; i< Shell1Indiecies.size(); i++ ) + for( unsigned int i = 0; i< m_MaxDirections; i++ ) { + double e1 = E1.get(i); + double e2 = E2.get(i); + double e3 = E3.get(i); - E1 = E->get(i,0); - E2 = E->get(i,1); - E3 = E->get(i,2); - - P2 = E2-E1*E1; - A = (E3 -E1*E2) / ( 2* P2); - B2 = A * A -(E1 * E3 - E2 * E2) /P2; + P2 = e2-e1*e1; + A = (e3 -e1*e2) / ( 2* P2); + B2 = A * A -(e1 * e3 - e2 * e2) /P2; B = 0; if(B2 > 0) B = sqrt(B2); - P = 0; if(P2 > 0) P = sqrt(P2); alpha = A + B; beta = A - B; + PValues.put(i, P); + AlphaValues.put(i, alpha); + BetaValues.put(i, beta); - lambda = 0.5 + 0.5 * std::sqrt(1 - std::pow((P * 2 ) / (alpha - beta), 2));; - ER1 = std::fabs(lambda * (alpha - beta) + (beta - E1 )) - + std::fabs(lambda * (std::pow(alpha, 2) - std::pow(beta, 2)) + (std::pow(beta, 2) - E2 )) - + std::fabs(lambda * (std::pow(alpha, 3) - std::pow(beta, 3)) + (std::pow(beta, 3) - E3 )); - ER2 = std::fabs((lambda-1) * (alpha - beta) + (beta - E1 )) - + std::fabs((lambda-1) * (std::pow(alpha, 2) - std::pow(beta, 2)) + (std::pow(beta, 2) - E2 )) - + std::fabs((lambda-1) * (std::pow(alpha, 3) - std::pow(beta, 3)) + (std::pow(beta, 3) - E3 )); + } + Projection3(PValues, AlphaValues, BetaValues); - PValues->put(i, P); - AlphaValues->put(i, alpha); - BetaValues->put(i, beta); - LAValues->put(i,(lambda * (ER1 < ER2)) + ((1-lambda) * (ER1 >= ER2))); + for(int i = 0 ; i < m_MaxDirections; i++) + { + const double fac = (PValues[i] * 2 ) / (AlphaValues[i] - BetaValues[i]); + lambda = 0.5 + 0.5 * std::sqrt(1 - fac * fac);; + ER1 = std::fabs(lambda * (AlphaValues[i] - BetaValues[i]) + (BetaValues[i] - E1.get(i) )) + + std::fabs(lambda * (AlphaValues[i] * AlphaValues[i] - BetaValues[i] * BetaValues[i]) + (BetaValues[i] * BetaValues[i] - E2.get(i) )) + + std::fabs(lambda * (AlphaValues[i] * AlphaValues[i] * AlphaValues[i] - BetaValues[i] * BetaValues[i] * BetaValues[i]) + (BetaValues[i] * BetaValues[i] * BetaValues[i] - E3.get(i) )); + ER2 = std::fabs((1-lambda) * (AlphaValues[i] - BetaValues[i]) + (BetaValues[i] - E1.get(i) )) + + std::fabs((1-lambda) * (AlphaValues[i] * AlphaValues[i] - BetaValues[i] * BetaValues[i]) + (BetaValues[i] * BetaValues[i] - E2.get(i) )) + + std::fabs((1-lambda) * (AlphaValues[i] * AlphaValues[i] * AlphaValues[i] - BetaValues[i] * BetaValues[i] * BetaValues[i]) + (BetaValues[i] * BetaValues[i] * BetaValues[i] - E3.get(i))); + if(ER1 < ER2) + LAValues.put(i, lambda); + else + LAValues.put(i, 1-lambda); } - Projection2(*PValues, *AlphaValues, *BetaValues); - //Threshold(*AlphaValues); - //Threshold(*BetaValues); + DoubleLogarithm(AlphaValues); + DoubleLogarithm(BetaValues); - DoubleLogarithm(*AlphaValues); - DoubleLogarithm(*BetaValues); + vnl_vector SignalVector(element_product((LAValues) , (AlphaValues)-(BetaValues)) + (BetaValues)); - vnl_vector SignalVector(element_product((*LAValues) , (*AlphaValues)-(*BetaValues)) + (*BetaValues)); vnl_vector coeffs((*m_CoeffReconstructionMatrix) *SignalVector ); // the first coeff is a fix value coeffs[0] = 1.0/(2.0*sqrt(QBALL_ANAL_RECON_PI)); // Cast the Signal-Type from double to float for the ODF-Image odf = element_cast( (*m_ODFSphericalHarmonicBasisMatrix) * coeffs ).data_block(); - odf *= (QBALL_ANAL_RECON_PI*4/NODF); - //Normalize(odf); + odf *= ((QBALL_ANAL_RECON_PI*4)/NODF); } // set ODF to ODF-Image odfOutputImageIterator.Set( odf ); ++odfOutputImageIterator; - // iterate - ++bzeroImageIterator; ++gradientInputImageIterator; } - - - MITK_INFO << "THREAD FINISHED"; - delete E; - delete AlphaValues; - delete BetaValues; - delete PValues; - delete LAValues; } -template< class T, class TG, class TO, int L, int NODF> -vnl_vector DiffusionMultiShellQballReconstructionImageFilter -::AnalyticalThreeShellParameterEstimation(const IndiciesVector * shell1Indicies,const IndiciesVector * shell2Indicies ,const IndiciesVector * shell3Indicies, vnl_vector) -{ - - - -} template< class T, class TG, class TO, int L, int NODF> -void DiffusionMultiShellQballReconstructionImageFilter -::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, int NumberOfThreads) +void DiffusionMultiShellQballReconstructionImageFilter:: +ComputeSphericalHarmonicsBasis(vnl_matrix * QBallReference, vnl_matrix *SHBasisOutput, int LOrder , vnl_matrix* LaplaciaBaltramiOutput, vnl_vector* SHOrderAssociation, vnl_matrix* SHEigenvalues) { - itk::TimeProbe clock; - GenerateAveragedBZeroImage(outputRegionForThread); + // MITK_INFO << *QBallReference; - clock.Start(); - switch(m_ReconstructionType) - { - case Mode_Standard1Shell: - StandardOneShellReconstruction(outputRegionForThread); - break; - case Mode_Analytical3Shells: - AnalyticalThreeShellReconstruction(outputRegionForThread); - break; - case Mode_NumericalNShells: - break; - } - clock.Stop(); - MITK_INFO << "Reconstruction in : " << clock.GetTotal() << " TU"; - - - -} - -template< class T, class TG, class TO, int L, int NODF> -void DiffusionMultiShellQballReconstructionImageFilter:: -ComputeSphericalHarmonicsBasis(vnl_matrix * QBallReference, vnl_matrix *SHBasisOutput, vnl_matrix* LaplaciaBaltramiOutput, vnl_vector* SHOrderAssociation, vnl_matrix* SHEigenvalues ) -{ for(unsigned int i=0; i< (*SHBasisOutput).rows(); i++) { - for(int k = 0; k <= L; k += 2) + for(int k = 0; k <= LOrder; k += 2) { for(int m =- k; m <= k; m++) { int j = ( k * k + k + 2 ) / 2 + m - 1; // Compute SHBasisFunctions - double phi = (*QBallReference)(0,i); - double th = (*QBallReference)(1,i); - (*SHBasisOutput)(i,j) = mitk::sh::Yj(m,k,th,phi); + if(QBallReference){ + double phi = (*QBallReference)(0,i); + double th = (*QBallReference)(1,i); + (*SHBasisOutput)(i,j) = mitk::sh::Yj(m,k,th,phi); + } // Laplacian Baltrami Order Association if(LaplaciaBaltramiOutput) (*LaplaciaBaltramiOutput)(j,j) = k*k*(k + 1)*(k+1); // SHEigenvalues with order Accosiation kj if(SHEigenvalues) (*SHEigenvalues)(j,j) = -k* (k+1); // Order Association if(SHOrderAssociation) (*SHOrderAssociation)[j] = k; } } } } -template< class T, class TG, class TO, int L, int NODF> -void DiffusionMultiShellQballReconstructionImageFilter -::ComputeFunkRadonTransformationMatrix(vnl_vector* SHOrderAssociationReference, vnl_matrix* FRTMatrixOutput ) -{ - for(int i=0; i -bool DiffusionMultiShellQballReconstructionImageFilter -::CheckHemisphericalArrangementOfGradientDirections() -{ - // handle acquisition schemes where only half of the spherical - // shell is sampled by the gradient directions. In this case, - // each gradient direction is duplicated in negative direction. - vnl_vector centerMass(3); - centerMass.fill(0.0); - int count = 0; - GradientDirectionContainerType::ConstIterator gdcit1; - for( gdcit1 = this->m_GradientDirectionContainer->Begin(); gdcit1 != this->m_GradientDirectionContainer->End(); ++gdcit1) - { - if(gdcit1.Value().one_norm() > 0.0) - { - centerMass += gdcit1.Value(); - count ++; - } - } - centerMass /= count; - if(centerMass.two_norm() > 0.1) - { - return false; - } - return true; -} - template< class T, class TG, class TO, int L, int NOdfDirections> void DiffusionMultiShellQballReconstructionImageFilter -::ComputeReconstructionMatrix() +::ComputeReconstructionMatrix(IndiciesVector const & refVector) { typedef std::auto_ptr< vnl_matrix< double> > MatrixDoublePtr; typedef std::auto_ptr< vnl_vector< int > > VectorIntPtr; typedef std::auto_ptr< vnl_matrix_inverse< double > > InverseMatrixDoublePtr; - std::map >::const_iterator it = (m_BValueMap.begin()); - it++; - const std::vector gradientIndiciesVector = (*it).second; - - int numberOfGradientDirections = gradientIndiciesVector.size(); + int numberOfGradientDirections = refVector.size(); if( numberOfGradientDirections < (((L+1)*(L+2))/2) || numberOfGradientDirections < 6 ) { itkExceptionMacro( << "At least (L+1)(L+2)/2 gradient directions for each shell are required; current : " << numberOfGradientDirections ); } - CheckDuplicateDiffusionGradients(); - // check if gradient directions are arrangement as a hemisphere(true) or sphere(false) - m_IsHemisphericalArrangementOfGradientDirections = CheckHemisphericalArrangementOfGradientDirections(); - if(m_IsHemisphericalArrangementOfGradientDirections) numberOfGradientDirections *= 2; - - - MatrixDoublePtr Q(new vnl_matrix(3, numberOfGradientDirections)); - Q->fill(0.0); - // Cartesian to spherical coordinates - { - int j = 0; - - for(int i = 0; i < gradientIndiciesVector.size(); i++) - { - - double x = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(0); - double y = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(1); - double z = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(2); - double cart[3]; - mitk::sh::Cart2Sph(x,y,z,cart); - (*Q)(0,j) = cart[0]; - (*Q)(1,j) = cart[1]; - (*Q)(2,j++) = cart[2]; - - } - if(m_IsHemisphericalArrangementOfGradientDirections) - { - for(int i = 0; i < gradientIndiciesVector.size(); i++) - { - double x = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(0); - double y = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(1); - double z = this->m_GradientDirectionContainer->ElementAt(gradientIndiciesVector[i]).get(2); - double cart[3]; - mitk::sh::Cart2Sph(x,y,z,cart); - (*Q)(0,j) = cart[0]; - (*Q)(1,j) = cart[1]; - (*Q)(2,j++) = cart[2]; - } - } - } - const int LOrder = L; - m_NumberCoefficients = (int)(LOrder*LOrder + LOrder + 2.0)/2.0 + LOrder; - MITK_INFO << m_NumberCoefficients; - m_SHBasisMatrix = new vnl_matrix(numberOfGradientDirections,m_NumberCoefficients); - m_SHBasisMatrix->fill(0.0); - VectorIntPtr SHOrderAssociation(new vnl_vector(m_NumberCoefficients)); + int NumberOfCoeffs = (int)(LOrder*LOrder + LOrder + 2.0)/2.0 + LOrder; + MITK_INFO << NumberOfCoeffs; + MatrixDoublePtr SHBasisMatrix(new vnl_matrix(numberOfGradientDirections,NumberOfCoeffs)); + SHBasisMatrix->fill(0.0); + VectorIntPtr SHOrderAssociation(new vnl_vector(NumberOfCoeffs)); SHOrderAssociation->fill(0.0); - MatrixDoublePtr LaplacianBaltrami(new vnl_matrix(m_NumberCoefficients,m_NumberCoefficients)); + MatrixDoublePtr LaplacianBaltrami(new vnl_matrix(NumberOfCoeffs,NumberOfCoeffs)); LaplacianBaltrami->fill(0.0); - MatrixDoublePtr FRTMatrix(new vnl_matrix(m_NumberCoefficients,m_NumberCoefficients)); + MatrixDoublePtr FRTMatrix(new vnl_matrix(NumberOfCoeffs,NumberOfCoeffs)); FRTMatrix->fill(0.0); - MatrixDoublePtr SHEigenvalues(new vnl_matrix(m_NumberCoefficients,m_NumberCoefficients)); + MatrixDoublePtr SHEigenvalues(new vnl_matrix(NumberOfCoeffs,NumberOfCoeffs)); SHEigenvalues->fill(0.0); + MatrixDoublePtr Q(new vnl_matrix(3, numberOfGradientDirections)); + // Convert Cartesian to Spherical Coordinates refVector -> Q + ComputeSphericalFromCartesian(Q.get(), refVector); // SHBasis-Matrix + LaplacianBaltrami-Matrix + SHOrderAssociationVector - ComputeSphericalHarmonicsBasis(Q.get() ,m_SHBasisMatrix, LaplacianBaltrami.get(), SHOrderAssociation.get(), SHEigenvalues.get()); + ComputeSphericalHarmonicsBasis(Q.get() ,SHBasisMatrix.get() , LOrder , LaplacianBaltrami.get(), SHOrderAssociation.get(), SHEigenvalues.get()); // Compute FunkRadon Transformation Matrix Associated to SHBasis Order lj - ComputeFunkRadonTransformationMatrix(SHOrderAssociation.get() ,FRTMatrix.get()); + for(int i=0; i(((m_SHBasisMatrix->transpose()) * (*m_SHBasisMatrix)) + (m_Lambda * (*LaplacianBaltrami)))); + MatrixDoublePtr temp(new vnl_matrix(((SHBasisMatrix->transpose()) * (*SHBasisMatrix)) + (m_Lambda * (*LaplacianBaltrami)))); InverseMatrixDoublePtr pseudo_inv(new vnl_matrix_inverse((*temp))); - MatrixDoublePtr inverse(new vnl_matrix(m_NumberCoefficients,m_NumberCoefficients)); - inverse->fill(0.0); + MatrixDoublePtr inverse(new vnl_matrix(NumberOfCoeffs,NumberOfCoeffs)); (*inverse) = pseudo_inv->inverse(); - // ODF Factor ( missing 1/4PI ?? ) - double factor = (1.0/(16.0*QBALL_ANAL_RECON_PI*QBALL_ANAL_RECON_PI)); - - m_SignalReonstructionMatrix = new vnl_matrix((*inverse) * (m_SHBasisMatrix->transpose())); + const double factor = (1.0/(16.0*QBALL_ANAL_RECON_PI*QBALL_ANAL_RECON_PI)); + MatrixDoublePtr SignalReonstructionMatrix (new vnl_matrix((*inverse) * (SHBasisMatrix->transpose()))); + m_CoeffReconstructionMatrix = new vnl_matrix(( factor * ((*FRTMatrix) * ((*SHEigenvalues) * (*SignalReonstructionMatrix))) )); - m_CoeffReconstructionMatrix = new vnl_matrix(( factor * ((*FRTMatrix) * ((*SHEigenvalues) * (*m_SignalReonstructionMatrix))) )); - - // this code goes to the image adapter coeffs->odfs later + // SH Basis for ODF-reconstruction vnl_matrix_fixed* U = itk::PointShell >::DistributePointShell(); - - m_ODFSphericalHarmonicBasisMatrix = new vnl_matrix(NOdfDirections,m_NumberCoefficients); - m_ODFSphericalHarmonicBasisMatrix->fill(0.0); - for(int i=0; i( U->as_matrix() )); + m_ODFSphericalHarmonicBasisMatrix = new vnl_matrix(NOdfDirections,NumberOfCoeffs); + ComputeSphericalHarmonicsBasis(tempPtr.get(), m_ODFSphericalHarmonicBasisMatrix, LOrder); } -template< class T, class TG, class TO, int L, int NODF> -template< class VNLType > -void DiffusionMultiShellQballReconstructionImageFilter -::printMatrix( VNLType * mat ) +template< class T, class TG, class TO, int L, int NOdfDirections> +void DiffusionMultiShellQballReconstructionImageFilter +::ComputeSphericalFromCartesian(vnl_matrix * Q, IndiciesVector const & refShell) { - - std::stringstream stream; - - for(int i = 0 ; i < mat->rows(); i++) + for(int i = 0; i < refShell.size(); i++) { - stream.str(""); - for(int j = 0; j < mat->cols(); j++) - { - stream << (*mat)(i,j) << " "; - } - + double x = m_GradientDirectionContainer->ElementAt(refShell[i]).normalize().get(0); + double y = m_GradientDirectionContainer->ElementAt(refShell[i]).normalize().get(1); + double z = m_GradientDirectionContainer->ElementAt(refShell[i]).normalize().get(2); + double cart[3]; + mitk::sh::Cart2Sph(x,y,z,cart); + (*Q)(0,i) = cart[0]; + (*Q)(1,i) = cart[1]; + (*Q)(2,i) = cart[2]; } - MITK_INFO << stream.str(); } + template< class T, class TG, class TO, int L, int NODF> bool DiffusionMultiShellQballReconstructionImageFilter ::CheckDuplicateDiffusionGradients() { bool value = false; BValueMapIteraotr mapIterator = m_BValueMap.begin(); + mapIterator++; while(mapIterator != m_BValueMap.end()) { std::vector::const_iterator it1 = mapIterator->second.begin(); std::vector::const_iterator it2 = mapIterator->second.begin(); for(; it1 != mapIterator->second.end(); ++it1) { for(; it2 != mapIterator->second.end(); ++it2) { if(m_GradientDirectionContainer->ElementAt(*it1) == m_GradientDirectionContainer->ElementAt(*it2) && it1 != it2) { itkWarningMacro( << "Some of the Diffusion Gradients equal each other. Corresponding image data should be averaged before calling this filter." ); value = true; } } } ++mapIterator; } return value; } +// corresponding directions between shells (e.g. dir1_shell1 vs dir1_shell2) differ more than 1 degree. +template< class T, class TG, class TO, int L, int NODF> +bool DiffusionMultiShellQballReconstructionImageFilter +::CheckForDifferingShellDirections() +{ + bool interp_flag = false; + + BValueMapIteraotr mapIterator = m_BValueMap.begin(); + mapIterator++; + std::vector shell1 = mapIterator->second; + mapIterator++; + std::vector shell2 = mapIterator->second; + mapIterator++; + std::vector shell3 = mapIterator->second; + + for (int i=0; i< shell1.size(); i++) + if (fabs(dot(m_GradientDirectionContainer->ElementAt(shell1[i]), m_GradientDirectionContainer->ElementAt(shell2[i]))) <= 0.9998) {interp_flag=true; break;} + for (int i=0; i< shell1.size(); i++) + if (fabs(dot(m_GradientDirectionContainer->ElementAt(shell1[i]), m_GradientDirectionContainer->ElementAt(shell3[i]))) <= 0.9998) {interp_flag=true; break;} + for (int i=0; i< shell1.size(); i++) + if (fabs(dot(m_GradientDirectionContainer->ElementAt(shell2[i]), m_GradientDirectionContainer->ElementAt(shell3[i]))) <= 0.9998) {interp_flag=true; break;} + return interp_flag; +} + + template< class T, class TG, class TO, int L, int NODF> void DiffusionMultiShellQballReconstructionImageFilter ::PrintSelf(std::ostream& os, Indent indent) const { std::locale C("C"); std::locale originalLocale = os.getloc(); os.imbue(C); Superclass::PrintSelf(os,indent); //os << indent << "OdfReconstructionMatrix: " << m_ReconstructionMatrix << std::endl; if ( m_GradientDirectionContainer ) { os << indent << "GradientDirectionContainer: " << m_GradientDirectionContainer << std::endl; } else { os << indent << "GradientDirectionContainer: (Gradient directions not set)" << std::endl; } os << indent << "NumberOfGradientDirections: " << m_NumberOfGradientDirections << std::endl; os << indent << "NumberOfBaselineImages: " << m_NumberOfBaselineImages << std::endl; os << indent << "Threshold for reference B0 image: " << m_Threshold << std::endl; os << indent << "BValue: " << m_BValue << std::endl; os.imbue( originalLocale ); } } #endif // __itkDiffusionMultiShellQballReconstructionImageFilter_cpp diff --git a/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.h b/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.h index fb7a499f38..a54d102e7e 100644 --- a/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.h +++ b/Modules/DiffusionImaging/Reconstruction/itkDiffusionMultiShellQballReconstructionImageFilter.h @@ -1,229 +1,240 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __itkDiffusionMultiShellQballReconstructionImageFilter_h_ #define __itkDiffusionMultiShellQballReconstructionImageFilter_h_ #include "itkImageToImageFilter.h" #include "vnl/vnl_vector_fixed.h" #include "vnl/vnl_matrix.h" #include "vnl/algo/vnl_svd.h" #include "itkVectorContainer.h" #include "itkVectorImage.h" #include namespace itk{ /** \class DiffusionMultiShellQballReconstructionImageFilter Aganj_2010 */ template< class TReferenceImagePixelType, class TGradientImagePixelType, class TOdfPixelType, int NOrderL, int NrOdfDirections> class DiffusionMultiShellQballReconstructionImageFilter : public ImageToImageFilter< Image< TReferenceImagePixelType, 3 >, Image< Vector< TOdfPixelType, NrOdfDirections >, 3 > > { public: typedef DiffusionMultiShellQballReconstructionImageFilter Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef ImageToImageFilter< Image< TReferenceImagePixelType, 3>, Image< Vector< TOdfPixelType, NrOdfDirections >, 3 > > Superclass; typedef TReferenceImagePixelType ReferencePixelType; typedef TGradientImagePixelType GradientPixelType; typedef Vector< TOdfPixelType, NrOdfDirections > OdfPixelType; typedef typename Superclass::InputImageType ReferenceImageType; typedef Image< OdfPixelType, 3 > OdfImageType; typedef OdfImageType OutputImageType; typedef TOdfPixelType BZeroPixelType; typedef Image< BZeroPixelType, 3 > BZeroImageType; typedef typename Superclass::OutputImageRegionType OutputImageRegionType; /** Typedef defining one (of the many) gradient images. */ typedef Image< GradientPixelType, 3 > GradientImageType; /** An alternative typedef defining one (of the many) gradient images. * It will be assumed that the vectorImage has the same dimension as the * Reference image and a vector length parameter of \c n (number of * gradient directions)*/ typedef VectorImage< GradientPixelType, 3 > GradientImagesType; /** Holds the ODF reconstruction matrix */ typedef vnl_matrix< TOdfPixelType >* OdfReconstructionMatrixType; typedef vnl_matrix< double > * CoefficientMatrixType; /** Holds each magnetic field gradient used to acquire one DWImage */ typedef vnl_vector_fixed< double, 3 > GradientDirectionType; /** Container to hold gradient directions of the 'n' DW measurements */ typedef VectorContainer< unsigned int, GradientDirectionType > GradientDirectionContainerType; typedef std::map > BValueMap; typedef std::map >::iterator BValueMapIteraotr; typedef std::vector IndiciesVector; // --------------------------------------------------------------------------------------------// /** Method for creation through the object factory. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(DiffusionMultiShellQballReconstructionImageFilter, ImageToImageFilter); /** set method to add gradient directions and its corresponding * image. The image here is a VectorImage. The user is expected to pass the * gradient directions in a container. The ith element of the container * corresponds to the gradient direction of the ith component image the * VectorImage. For the baseline image, a vector of all zeros * should be set.*/ void SetGradientImage( GradientDirectionContainerType *, const GradientImagesType *image , float bvalue);//, std::vector listOfUserSelctedBValues ); /** Get reference image */ virtual ReferenceImageType * GetReferenceImage() { return ( static_cast< ReferenceImageType *>(this->ProcessObject::GetInput(0)) ); } /** Return the gradient direction. idx is 0 based */ virtual GradientDirectionType GetGradientDirection( unsigned int idx) const { if( idx >= m_GradientDirectionContainer->Size() ) { itkExceptionMacro( << "Gradient direction " << idx << "does not exist" ); } return m_GradientDirectionContainer->ElementAt( idx+1 ); } void Normalize(OdfPixelType & odf ); - void S_S0Normalization( vnl_vector & vec, typename NumericTraits::AccumulateType b0 = 0 ); - void S_S0Normalization( vnl_matrix & mat, typename NumericTraits::AccumulateType b0 = 0 ); + void S_S0Normalization( vnl_vector & vec, double b0 = 0 ); void DoubleLogarithm(vnl_vector & vec); - void Threshold(vnl_vector & vec, double delta = 0.01); - void Threshold(vnl_matrix & mat, double delta = 0.01); + void Projection1(vnl_vector & vec, double delta = 0.01); double CalculateThreashold(const double value, const double delta); - void Projection1( vnl_matrix & mat, double delta = 0.01); - void Projection2( vnl_vector & A, vnl_vector & alpha, vnl_vector & beta, double delta = 0.01); + void Projection2( vnl_vector & E1, vnl_vector & E2, vnl_vector & E3, double delta = 0.01); + void Projection3( vnl_vector & A, vnl_vector & alpha, vnl_vector & beta, double delta = 0.01); /** Threshold on the reference image data. The output ODF will be a null * pdf for pixels in the reference image that have a value less than this * threshold. */ itkSetMacro( Threshold, ReferencePixelType ); itkGetMacro( Threshold, ReferencePixelType ); itkGetMacro( BZeroImage, typename BZeroImageType::Pointer); //itkGetMacro( ODFSumImage, typename BlaImage::Pointer); itkSetMacro( Lambda, double ); itkGetMacro( Lambda, double ); itkGetConstReferenceMacro( BValue, TOdfPixelType); void SetBValueMap(BValueMap map){this->m_BValueMap = map;} protected: DiffusionMultiShellQballReconstructionImageFilter(); - ~DiffusionMultiShellQballReconstructionImageFilter() {}; + ~DiffusionMultiShellQballReconstructionImageFilter() { }; void PrintSelf(std::ostream& os, Indent indent) const; - void ComputeReconstructionMatrix(); + void ComputeReconstructionMatrix(IndiciesVector const & refVector); + void ComputeODFSHBasis(); bool CheckDuplicateDiffusionGradients(); - void ComputeSphericalHarmonicsBasis(vnl_matrix* QBallReference, vnl_matrix* SHBasisOutput, vnl_matrix* LaplaciaBaltramiOutput, vnl_vector* SHOrderAssociation , vnl_matrix * SHEigenvalues); - void ComputeFunkRadonTransformationMatrix(vnl_vector* SHOrderAssociationReference, vnl_matrix* FRTMatrixOutput ); - bool CheckHemisphericalArrangementOfGradientDirections(); + bool CheckForDifferingShellDirections(); + void ComputeSphericalHarmonicsBasis(vnl_matrix* QBallReference, vnl_matrix* SHBasisOutput, int Lorder , vnl_matrix* LaplaciaBaltramiOutput =0 , vnl_vector* SHOrderAssociation =0 , vnl_matrix * SHEigenvalues =0); + //void ComputeFunkRadonTransformationMatrix(vnl_vector* SHOrderAssociationReference, vnl_matrix* FRTMatrixOutput ); + //bool CheckHemisphericalArrangementOfGradientDirections(); void BeforeThreadedGenerateData(); void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, int NumberOfThreads ); - vnl_vector AnalyticalThreeShellParameterEstimation(const IndiciesVector * shell1, const IndiciesVector * shell2, const IndiciesVector * shell3, vnl_vector b); void StandardOneShellReconstruction(const OutputImageRegionType& outputRegionForThread); void AnalyticalThreeShellReconstruction(const OutputImageRegionType& outputRegionForThread); void NumericalNShellReconstruction(const OutputImageRegionType& outputRegionForThread); void GenerateAveragedBZeroImage(const OutputImageRegionType& outputRegionForThread); private: enum ReconstructionType { Mode_Analytical3Shells, Mode_NumericalNShells, Mode_Standard1Shell }; + // Interpolation + bool m_Interpolation_Flag; + CoefficientMatrixType m_Interpolation_SHT1_inv; + CoefficientMatrixType m_Interpolation_SHT2_inv; + CoefficientMatrixType m_Interpolation_SHT3_inv; + CoefficientMatrixType m_Interpolation_TARGET_SH; + int m_MaxDirections; //CoefficientMatrixType m_ReconstructionMatrix; CoefficientMatrixType m_CoeffReconstructionMatrix; CoefficientMatrixType m_ODFSphericalHarmonicBasisMatrix; - CoefficientMatrixType m_SignalReonstructionMatrix; - CoefficientMatrixType m_SHBasisMatrix; + //CoefficientMatrixType m_SignalReonstructionMatrix; + //CoefficientMatrixType m_SHBasisMatrix; /** container to hold gradient directions */ GradientDirectionContainerType::Pointer m_GradientDirectionContainer; /** Number of gradient measurements */ unsigned int m_NumberOfGradientDirections; /** Number of baseline images */ unsigned int m_NumberOfBaselineImages; /** Threshold on the reference image data */ ReferencePixelType m_Threshold; /** LeBihan's b-value for normalizing tensors */ float m_BValue; typename BZeroImageType::Pointer m_BZeroImage; BValueMap m_BValueMap; double m_Lambda; bool m_IsHemisphericalArrangementOfGradientDirections; bool m_IsArithmeticProgession; - int m_NumberCoefficients; + //int m_NumberCoefficients; ReconstructionType m_ReconstructionType; - - template< class VNLType > - void printMatrix( VNLType * mat ); - //------------------------- VNL-function ------------------------------------ - template - static vnl_vector< WntValue> element_cast (vnl_vector< CurrentValue> const& v1) + vnl_vector< WntValue> element_cast (vnl_vector< CurrentValue> const& v1) { vnl_vector result(v1.size()); for(int i = 0 ; i < v1.size(); i++) result[i] = static_cast< WntValue>(v1[i]); return result; } + template + double dot (vnl_vector_fixed< type ,3> const& v1, vnl_vector_fixed< type ,3 > const& v2 ) + { + double result = (v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]) / (v1.two_norm() * v2.two_norm()); + return result ; + } + + void ComputeSphericalFromCartesian(vnl_matrix * Q, const IndiciesVector & refShell); + + }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkDiffusionMultiShellQballReconstructionImageFilter.cpp" #endif #endif //__itkDiffusionMultiShellQballReconstructionImageFilter_h_ diff --git a/Modules/DiffusionImaging/Rendering/mitkConnectomicsNetworkMapper3D.cpp b/Modules/DiffusionImaging/Rendering/mitkConnectomicsNetworkMapper3D.cpp index c54e3431ca..0c91da3715 100644 --- a/Modules/DiffusionImaging/Rendering/mitkConnectomicsNetworkMapper3D.cpp +++ b/Modules/DiffusionImaging/Rendering/mitkConnectomicsNetworkMapper3D.cpp @@ -1,180 +1,181 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkConnectomicsNetworkMapper3D.h" mitk::ConnectomicsNetworkMapper3D::ConnectomicsNetworkMapper3D() { //TODO: implement m_NetworkAssembly = vtkPropAssembly::New(); } mitk::ConnectomicsNetworkMapper3D:: ~ConnectomicsNetworkMapper3D() { //TODO: implement m_NetworkAssembly->Delete(); } void mitk::ConnectomicsNetworkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer* renderer) { //TODO: implement if( this->GetInput() == NULL ) { return; } if( this->GetInput()->GetIsModified( ) ) { GenerateData(); } } void mitk::ConnectomicsNetworkMapper3D::GenerateData() { - //TODO: implement + m_NetworkAssembly->Delete(); + m_NetworkAssembly = vtkPropAssembly::New(); - // Here is the part where a graph is given and converted to points and connections between points... + // Here is the part where a graph is given and converted to points and connections between points... std::vector< mitk::ConnectomicsNetwork::NetworkNode > vectorOfNodes = this->GetInput()->GetVectorOfAllNodes(); std::vector< std::pair< std::pair< mitk::ConnectomicsNetwork::NetworkNode, mitk::ConnectomicsNetwork::NetworkNode > , mitk::ConnectomicsNetwork::NetworkEdge > > vectorOfEdges = this->GetInput()->GetVectorOfAllEdges(); mitk::Point3D tempWorldPoint, tempCNFGeometryPoint; //////////////////////Create Spheres///////////////////////// for(unsigned int i = 0; i < vectorOfNodes.size(); i++) { vtkSmartPointer sphereSource = vtkSmartPointer::New(); for(unsigned int dimension = 0; dimension < 3; dimension++) { tempCNFGeometryPoint.SetElement( dimension , vectorOfNodes[i].coordinates[dimension] ); } this->GetData()->GetGeometry()->IndexToWorld( tempCNFGeometryPoint, tempWorldPoint ); sphereSource->SetCenter( tempWorldPoint[0] , tempWorldPoint[1], tempWorldPoint[2] ); sphereSource->SetRadius(1.0); vtkSmartPointer mapper = vtkSmartPointer::New(); mapper->SetInput(sphereSource->GetOutput()); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); m_NetworkAssembly->AddPart(actor); } //////////////////////Create Tubes///////////////////////// for(unsigned int i = 0; i < vectorOfEdges.size(); i++) { vtkSmartPointer lineSource = vtkSmartPointer::New(); for(unsigned int dimension = 0; dimension < 3; dimension++) { tempCNFGeometryPoint[ dimension ] = vectorOfEdges[i].first.first.coordinates[dimension]; } this->GetData()->GetGeometry()->IndexToWorld( tempCNFGeometryPoint, tempWorldPoint ); lineSource->SetPoint1(tempWorldPoint[0], tempWorldPoint[1],tempWorldPoint[2] ); for(unsigned int dimension = 0; dimension < 3; dimension++) { tempCNFGeometryPoint[ dimension ] = vectorOfEdges[i].first.second.coordinates[dimension]; } this->GetData()->GetGeometry()->IndexToWorld( tempCNFGeometryPoint, tempWorldPoint ); lineSource->SetPoint2(tempWorldPoint[0], tempWorldPoint[1], tempWorldPoint[2] ); vtkSmartPointer tubes = vtkSmartPointer::New(); tubes->SetInput( lineSource->GetOutput() ); tubes->SetNumberOfSides( 12 ); double radiusFactor = 1.0 + ((double) vectorOfEdges[i].second.weight) / 10.0 ; tubes->SetRadius( std::log10( radiusFactor ) ); vtkSmartPointer mapper2 = vtkSmartPointer::New(); mapper2->SetInput( tubes->GetOutput() ); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper2); double maxWeight = (double) this->GetInput()->GetMaximumWeight(); double colourFactor = vectorOfEdges[i].second.weight / maxWeight; actor->GetProperty()->SetColor( colourFactor, colourFactor, colourFactor); m_NetworkAssembly->AddPart(actor); } (static_cast ( GetData() ) )->SetIsModified( false ); //this->UpdateVtkObjects(); } const mitk::ConnectomicsNetwork* mitk::ConnectomicsNetworkMapper3D::GetInput() { return static_cast ( GetData() ); } void mitk::ConnectomicsNetworkMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer , bool overwrite) { //TODO: implement // hand it to the superclass for base default properties Superclass::SetDefaultProperties(node, renderer, overwrite); } void mitk::ConnectomicsNetworkMapper3D::ApplyProperties(mitk::BaseRenderer* renderer) { //TODO: implement } void mitk::ConnectomicsNetworkMapper3D::SetVtkMapperImmediateModeRendering(vtkMapper *mapper) { //TODO: implement } void mitk::ConnectomicsNetworkMapper3D::UpdateVtkObjects() { //TODO: implement } vtkProp* mitk::ConnectomicsNetworkMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { return m_NetworkAssembly; } diff --git a/Modules/Segmentation/Algorithms/mitkReduceContourSetFilter.cpp b/Modules/Segmentation/Algorithms/mitkReduceContourSetFilter.cpp index 53c110e531..1ad1d9bd76 100644 --- a/Modules/Segmentation/Algorithms/mitkReduceContourSetFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkReduceContourSetFilter.cpp @@ -1,491 +1,490 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkReduceContourSetFilter.h" mitk::ReduceContourSetFilter::ReduceContourSetFilter() { m_MaxSegmentLenght = 0; m_StepSize = 10; m_Tolerance = -1; m_ReductionType = DOUGLAS_PEUCKER; m_MaxSpacing = -1; m_MinSpacing = -1; this->m_UseProgressBar = false; this->m_ProgressStepSize = 1; } mitk::ReduceContourSetFilter::~ReduceContourSetFilter() { } void mitk::ReduceContourSetFilter::GenerateData() { unsigned int numberOfInputs = this->GetNumberOfInputs(); unsigned int numberOfOutputs (0); - this->CreateOutputsForAllInputs(numberOfInputs); vtkSmartPointer newPolyData; vtkSmartPointer newPolygons; vtkSmartPointer newPoints; //For the purpose of evaluation // unsigned int numberOfPointsBefore (0); // unsigned int numberOfPointsAfter (0); for(unsigned int i = 0; i < numberOfInputs; i++) { mitk::Surface* currentSurface = const_cast( this->GetInput(i) ); vtkSmartPointer polyData = currentSurface->GetVtkPolyData(); newPolyData = vtkPolyData::New(); newPolygons = vtkCellArray::New(); newPoints = vtkPoints::New(); vtkSmartPointer existingPolys = polyData->GetPolys(); vtkSmartPointer existingPoints = polyData->GetPoints(); existingPolys->InitTraversal(); vtkIdType* cell (NULL); vtkIdType cellSize (0); for( existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);) { bool incorporatePolygon = this->CheckForIntersection(cell,cellSize,existingPoints, /*numberOfIntersections, intersectionPoints, */i); if ( !incorporatePolygon ) continue; vtkSmartPointer newPolygon = vtkPolygon::New(); if(m_ReductionType == NTH_POINT) { this->ReduceNumberOfPointsByNthPoint(cellSize, cell, existingPoints, newPolygon, newPoints); if (newPolygon->GetPointIds()->GetNumberOfIds() != 0) { newPolygons->InsertNextCell(newPolygon); } } else if (m_ReductionType == DOUGLAS_PEUCKER) { this->ReduceNumberOfPointsByDouglasPeucker(cellSize, cell, existingPoints, newPolygon, newPoints); if (newPolygon->GetPointIds()->GetNumberOfIds() > 3) { newPolygons->InsertNextCell(newPolygon); } } //Again for evaluation // numberOfPointsBefore += cellSize; // numberOfPointsAfter += newPolygon->GetPointIds()->GetNumberOfIds(); } if (newPolygons->GetNumberOfCells() != 0) { newPolyData->SetPolys(newPolygons); newPolyData->SetPoints(newPoints); newPolyData->BuildLinks(); Surface::Pointer surface = this->GetOutput(numberOfOutputs); surface->SetVtkPolyData(newPolyData); numberOfOutputs++; } } // MITK_INFO<<"Points before: "<SetNumberOfOutputs(numberOfOutputs); //Setting progressbar if (this->m_UseProgressBar) mitk::ProgressBar::GetInstance()->Progress(this->m_ProgressStepSize); } void mitk::ReduceContourSetFilter::ReduceNumberOfPointsByNthPoint (vtkIdType cellSize, vtkIdType* cell, vtkPoints* points, vtkPolygon* reducedPolygon, vtkPoints* reducedPoints) { unsigned int newNumberOfPoints (0); unsigned int mod = cellSize%m_StepSize; if(mod == 0) { newNumberOfPoints = cellSize/m_StepSize; } else { newNumberOfPoints = ( (cellSize-mod)/m_StepSize )+1; } if (newNumberOfPoints <= 3) { return; } reducedPolygon->GetPointIds()->SetNumberOfIds(newNumberOfPoints); reducedPolygon->GetPoints()->SetNumberOfPoints(newNumberOfPoints); for (unsigned int i = 0; i < cellSize; i++) { if (i%m_StepSize == 0) { double point[3]; points->GetPoint(cell[i], point); vtkIdType id = reducedPoints->InsertNextPoint(point); reducedPolygon->GetPointIds()->SetId(i/m_StepSize, id); } } vtkIdType id = cell[0]; double point[3]; points->GetPoint(id, point); id = reducedPoints->InsertNextPoint(point); reducedPolygon->GetPointIds()->SetId(newNumberOfPoints-1, id); } void mitk::ReduceContourSetFilter::ReduceNumberOfPointsByDouglasPeucker(vtkIdType cellSize, vtkIdType* cell, vtkPoints* points, vtkPolygon* reducedPolygon, vtkPoints* reducedPoints) { //If the cell is too small to obtain a reduced polygon with the given stepsize return if (cellSize <= m_StepSize*3)return; /* What we do now is (see the Douglas Peucker Algorithm): 1. Divide the current contour in two line segments (start - middle; middle - end), put them into the stack 2. Fetch first line segment and create the following vectors: - v1 = (start;end) - v2 = (start;currentPoint) -> for each point of the current line segment! 3. Calculate the distance from the currentPoint to v1: a. Determine the length of the orthogonal projection of v2 to v1 by: l = v2 * (normalized v1) b. There a three possibilities for the distance then: d = sqrt(lenght(v2)^2 - l^2) if l > 0 and l < length(v1) d = lenght(v2-v1) if l > 0 and l > lenght(v1) d = length(v2) if l < 0 because v2 is then pointing in a different direction than v1 4. Memorize the point with the biggest distance and create two new line segments with it at the end of the iteration and put it into the stack 5. If the distance value D <= m_Tolerance, then add the start and end index and the corresponding points to the reduced ones */ //First of all set tolerance if none is specified if(m_Tolerance < 0) { if(m_MaxSpacing > 0) { m_Tolerance = m_MinSpacing; } else { m_Tolerance = 1.5; } } std::stack lineSegments; //1. Divide in line segments LineSegment ls2; ls2.StartIndex = cell[cellSize/2]; ls2.EndIndex = cell[cellSize-1]; lineSegments.push(ls2); LineSegment ls1; ls1.StartIndex = cell[0]; ls1.EndIndex = cell[cellSize/2]; lineSegments.push(ls1); LineSegment currentSegment; double v1[3]; double v2[3]; double tempV[3]; double lenghtV1; double currentMaxDistance (0); vtkIdType currentMaxDistanceIndex (0); double l; double d; vtkIdType pointId (0); //Add the start index to the reduced points. From now on just the end indices will be added pointId = reducedPoints->InsertNextPoint(points->GetPoint(cell[0])); reducedPolygon->GetPointIds()->InsertNextId(pointId); while (!lineSegments.empty()) { currentSegment = lineSegments.top(); lineSegments.pop(); //2. Create vectors points->GetPoint(currentSegment.EndIndex, tempV); points->GetPoint(currentSegment.StartIndex, v1); v1[0] = tempV[0]-v1[0]; v1[1] = tempV[1]-v1[1]; v1[2] = tempV[2]-v1[2]; lenghtV1 = vtkMath::Norm(v1); vtkMath::Normalize(v1); int range = currentSegment.EndIndex - currentSegment.StartIndex; for (int i = 1; i < abs(range); ++i) { points->GetPoint(currentSegment.StartIndex+i, tempV); points->GetPoint(currentSegment.StartIndex, v2); v2[0] = tempV[0]-v2[0]; v2[1] = tempV[1]-v2[1]; v2[2] = tempV[2]-v2[2]; //3. Calculate the distance l = vtkMath::Dot(v2, v1); d = vtkMath::Norm(v2); if (l > 0 && l < lenghtV1) { d = sqrt((d*d-l*l)); } else if (l > 0 && l > lenghtV1) { tempV[0] = lenghtV1*v1[0] - v2[0]; tempV[1] = lenghtV1*v1[1] - v2[1]; tempV[2] = lenghtV1*v1[2] - v2[2]; d = vtkMath::Norm(tempV); } //4. Memorize maximum distance if (d > currentMaxDistance) { currentMaxDistance = d; currentMaxDistanceIndex = currentSegment.StartIndex+i; } } //4. & 5. if (currentMaxDistance <= m_Tolerance) { //double temp[3]; int segmentLenght = currentSegment.EndIndex - currentSegment.StartIndex; if (segmentLenght > (int)m_MaxSegmentLenght) { m_MaxSegmentLenght = (unsigned int)segmentLenght; } // MITK_INFO<<"Lenght: "< 25) { unsigned int newLenght(segmentLenght); while (newLenght > 25) { newLenght = newLenght*0.5; } unsigned int divisions = abs(segmentLenght)/newLenght; // MITK_INFO<<"Divisions: "<InsertNextPoint(points->GetPoint(currentSegment.StartIndex + newLenght*i)); reducedPolygon->GetPointIds()->InsertNextId(pointId); } } // MITK_INFO<<"Inserting END: "<InsertNextPoint(points->GetPoint(currentSegment.EndIndex)); reducedPolygon->GetPointIds()->InsertNextId(pointId); } else { ls2.StartIndex = currentMaxDistanceIndex; ls2.EndIndex = currentSegment.EndIndex; lineSegments.push(ls2); ls1.StartIndex = currentSegment.StartIndex; ls1.EndIndex = currentMaxDistanceIndex; lineSegments.push(ls1); } currentMaxDistance = 0; } } bool mitk::ReduceContourSetFilter::CheckForIntersection (vtkIdType* currentCell, vtkIdType currentCellSize, vtkPoints* currentPoints,/* vtkIdType numberOfIntersections, vtkIdType* intersectionPoints,*/ unsigned int currentInputIndex) { /* If we check the current cell for intersections then we have to consider three possibilies: 1. There is another cell among all the other input surfaces which intersects the current polygon: - That means we have to save the intersection points because these points should not be eliminated 2. There current polygon exists just because of an intersection of another polygon with the current plane defined by the current polygon - That means the current polygon should not be incorporated and all of its points should be eliminated 3. There is no intersection - That mean we can just reduce the current polygons points without considering any intersections */ for (unsigned int i = 0; i < this->GetNumberOfInputs(); i++) { //Don't check for intersection with the polygon itself if (i == currentInputIndex) continue; //Get the next polydata to check for intersection vtkSmartPointer poly = const_cast( this->GetInput(i) )->GetVtkPolyData(); vtkSmartPointer polygonArray = poly->GetPolys(); polygonArray->InitTraversal(); vtkIdType anotherInputPolygonSize (0); vtkIdType* anotherInputPolygonIDs(NULL); /* The procedure is: - Create the equation of the plane, defined by the points of next input - Calculate the distance of each point of the current polygon to the plane - If the maximum distance is not bigger than 1.5 of the maximum spacing AND the minimal distance is not bigger than 0.5 of the minimum spacing then the current contour is an intersection contour */ for( polygonArray->InitTraversal(); polygonArray->GetNextCell(anotherInputPolygonSize, anotherInputPolygonIDs);) { //Choosing three plane points to calculate the plane vectors double p1[3]; double p2[3]; double p3[3]; //The plane vectors double v1[3]; double v2[3] = { 0 }; //The plane normal double normal[3]; //Create first Vector poly->GetPoint(anotherInputPolygonIDs[0], p1); poly->GetPoint(anotherInputPolygonIDs[1], p2); v1[0] = p2[0]-p1[0]; v1[1] = p2[1]-p1[1]; v1[2] = p2[2]-p1[2]; //Find 3rd point for 2nd vector (The angle between the two plane vectors should be bigger than 30 degrees) double maxDistance (0); double minDistance (10000); for (unsigned int j = 2; j < anotherInputPolygonSize; j++) { poly->GetPoint(anotherInputPolygonIDs[j], p3); v2[0] = p3[0]-p1[0]; v2[1] = p3[1]-p1[1]; v2[2] = p3[2]-p1[2]; //Calculate the angle between the two vector for the current point double dotV1V2 = vtkMath::Dot(v1,v2); double absV1 = sqrt(vtkMath::Dot(v1,v1)); double absV2 = sqrt(vtkMath::Dot(v2,v2)); double cosV1V2 = dotV1V2/(absV1*absV2); double arccos = acos(cosV1V2); double degree = vtkMath::DegreesFromRadians(arccos); //If angle is bigger than 30 degrees break if (degree > 30) break; }//for (to find 3rd point) //Calculate normal of the plane by taking the cross product of the two vectors vtkMath::Cross(v1,v2,normal); vtkMath::Normalize(normal); //Determine position of the plane double lambda = vtkMath::Dot(normal, p1); /* Calculate the distance to the plane for each point of the current polygon If the distance is zero then save the currentPoint as intersection point */ for (unsigned int k = 0; k < currentCellSize; k++) { double currentPoint[3]; currentPoints->GetPoint(currentCell[k], currentPoint); double tempPoint[3]; tempPoint[0] = normal[0]*currentPoint[0]; tempPoint[1] = normal[1]*currentPoint[1]; tempPoint[2] = normal[2]*currentPoint[2]; double temp = tempPoint[0]+tempPoint[1]+tempPoint[2]-lambda; double distance = fabs(temp); if (distance > maxDistance) { maxDistance = distance; } if (distance < minDistance) { minDistance = distance; } }//for (to calculate distance and intersections with currentPolygon) if (maxDistance < 1.5*m_MaxSpacing && minDistance < 0.5*m_MinSpacing) { return false; } //Because we are considering the plane defined by the acual input polygon only one iteration is sufficient //We do not need to consider each cell of the plane break; }//for (to traverse through all cells of actualInputPolyData) }//for (to iterate through all inputs) return true; } void mitk::ReduceContourSetFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } void mitk::ReduceContourSetFilter::Reset() { for (unsigned int i = 0; i < this->GetNumberOfInputs(); i++) { this->PopBackInput(); } this->SetNumberOfInputs(0); this->SetNumberOfOutputs(0); } void mitk::ReduceContourSetFilter::SetUseProgressBar(bool status) { this->m_UseProgressBar = status; } void mitk::ReduceContourSetFilter::SetProgressStepSize(unsigned int stepSize) { this->m_ProgressStepSize = stepSize; } diff --git a/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.cpp b/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.cpp index d5c38b90c6..cbcd37ae2d 100644 --- a/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.cpp +++ b/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.cpp @@ -1,220 +1,225 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurfaceInterpolationController.h" mitk::SurfaceInterpolationController::SurfaceInterpolationController() : m_CurrentContourListID (0) { m_ReduceFilter = ReduceContourSetFilter::New(); m_NormalsFilter = ComputeContourSetNormalsFilter::New(); m_InterpolateSurfaceFilter = CreateDistanceImageFromSurfaceFilter::New(); m_ReduceFilter->SetUseProgressBar(true); m_NormalsFilter->SetUseProgressBar(true); m_InterpolateSurfaceFilter->SetUseProgressBar(true); m_Contours = Surface::New(); m_PolyData = vtkSmartPointer::New(); m_PolyData->SetPoints(vtkPoints::New()); m_InterpolationResult = 0; } mitk::SurfaceInterpolationController::~SurfaceInterpolationController() { for (unsigned int i = 0; i < m_ListOfContourLists.size(); ++i) { for (unsigned int j = 0; j < m_ListOfContourLists.at(i).size(); ++j) { delete(m_ListOfContourLists.at(i).at(j).position); } } } mitk::SurfaceInterpolationController* mitk::SurfaceInterpolationController::GetInstance() { static mitk::SurfaceInterpolationController* m_Instance; if ( m_Instance == 0) { m_Instance = new SurfaceInterpolationController(); } return m_Instance; } void mitk::SurfaceInterpolationController::AddNewContour (mitk::Surface::Pointer newContour ,RestorePlanePositionOperation* op) { AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform = op->GetTransform(); mitk::Vector3D direction = op->GetDirectionVector(); int pos (-1); for (unsigned int i = 0; i < m_ListOfContourLists.at(m_CurrentContourListID).size(); i++) { itk::Matrix diffM = transform->GetMatrix()-m_ListOfContourLists.at(m_CurrentContourListID).at(i).position->GetTransform()->GetMatrix(); bool isSameMatrix(true); for (unsigned int j = 0; j < 3; j++) { if (fabs(diffM[j][0]) > 0.0001 && fabs(diffM[j][1]) > 0.0001 && fabs(diffM[j][2]) > 0.0001) { isSameMatrix = false; break; } } itk::Vector diffV = m_ListOfContourLists.at(m_CurrentContourListID).at(i).position->GetTransform()->GetOffset()-transform->GetOffset(); if ( isSameMatrix && m_ListOfContourLists.at(m_CurrentContourListID).at(i).position->GetPos() == op->GetPos() && (fabs(diffV[0]) < 0.0001 && fabs(diffV[1]) < 0.0001 && fabs(diffV[2]) < 0.0001) ) { pos = i; break; } } if (pos == -1) { //MITK_INFO<<"New Contour"; mitk::RestorePlanePositionOperation* newOp = new mitk::RestorePlanePositionOperation (OpRESTOREPLANEPOSITION, op->GetWidth(), op->GetHeight(), op->GetSpacing(), op->GetPos(), direction, transform); ContourPositionPair newData; newData.contour = newContour; newData.position = newOp; m_ReduceFilter->SetInput(m_ListOfContourLists.at(m_CurrentContourListID).size(), newContour); - m_NormalsFilter->SetInput(m_ListOfContourLists.at(m_CurrentContourListID).size(), m_ReduceFilter->GetOutput(m_ListOfContourLists.at(m_CurrentContourListID).size())); - m_InterpolateSurfaceFilter->SetInput(m_ListOfContourLists.at(m_CurrentContourListID).size(), m_NormalsFilter->GetOutput(m_ListOfContourLists.at(m_CurrentContourListID).size())); m_ListOfContourLists.at(m_CurrentContourListID).push_back(newData); } else { //MITK_INFO<<"Modified Contour"; m_ListOfContourLists.at(m_CurrentContourListID).at(pos).contour = newContour; m_ReduceFilter->SetInput(pos, newContour); - m_NormalsFilter->SetInput(pos, m_ReduceFilter->GetOutput(pos)); - m_InterpolateSurfaceFilter->SetInput(pos, m_NormalsFilter->GetOutput(pos)); + } + + m_ReduceFilter->Update(); + m_CurrentNumberOfReducedContours = m_ReduceFilter->GetNumberOfOutputs(); + + for (unsigned int i = 0; i < m_CurrentNumberOfReducedContours; i++) + { + m_NormalsFilter->SetInput(i, m_ReduceFilter->GetOutput(i)); + m_InterpolateSurfaceFilter->SetInput(i, m_NormalsFilter->GetOutput(i)); } this->Modified(); } void mitk::SurfaceInterpolationController::Interpolate() { - if (m_ListOfContourLists.at(m_CurrentContourListID).size() < 2) + if (m_CurrentNumberOfReducedContours< 2) return; //Setting up progress bar mitk::ProgressBar::GetInstance()->AddStepsToDo(8); m_InterpolateSurfaceFilter->Update(); Image::Pointer distanceImage = m_InterpolateSurfaceFilter->GetOutput(); vtkSmartPointer mcFilter = vtkMarchingCubes::New(); mcFilter->SetInput(distanceImage->GetVtkImageData()); mcFilter->SetValue(0,0); mcFilter->Update(); m_InterpolationResult = 0; m_InterpolationResult = mitk::Surface::New(); m_InterpolationResult->SetVtkPolyData(mcFilter->GetOutput()); m_InterpolationResult->GetGeometry()->SetOrigin(distanceImage->GetGeometry()->GetOrigin()); vtkSmartPointer polyDataAppender = vtkSmartPointer::New(); for (unsigned int i = 0; i < m_ReduceFilter->GetNumberOfOutputs(); i++) { polyDataAppender->AddInput(m_ReduceFilter->GetOutput(i)->GetVtkPolyData()); } polyDataAppender->Update(); m_Contours->SetVtkPolyData(polyDataAppender->GetOutput()); //Last progress step mitk::ProgressBar::GetInstance()->Progress(8); m_InterpolationResult->DisconnectPipeline(); } mitk::Surface::Pointer mitk::SurfaceInterpolationController::GetInterpolationResult() { return m_InterpolationResult; } mitk::Surface* mitk::SurfaceInterpolationController::GetContoursAsSurface() { return m_Contours; } void mitk::SurfaceInterpolationController::SetDataStorage(DataStorage &ds) { m_DataStorage = &ds; } unsigned int mitk::SurfaceInterpolationController::CreateNewContourList() { unsigned int newID = m_ListOfContourLists.size(); ContourPositionPairList newList; m_ListOfContourLists.push_back(newList); this->SetCurrentListID(newID); m_InterpolationResult = 0; return m_CurrentContourListID; } void mitk::SurfaceInterpolationController::SetCurrentListID ( unsigned int ID ) { if (ID == m_CurrentContourListID ) return; m_CurrentContourListID = ID; m_ReduceFilter->Reset(); m_NormalsFilter->Reset(); m_InterpolateSurfaceFilter->Reset(); for (unsigned int i = 0; i < m_ListOfContourLists.at(m_CurrentContourListID).size(); i++) { m_ReduceFilter->SetInput(i, m_ListOfContourLists.at(m_CurrentContourListID).at(i).contour); m_NormalsFilter->SetInput(i,m_ReduceFilter->GetOutput(i)); m_InterpolateSurfaceFilter->SetInput(i,m_NormalsFilter->GetOutput(i)); } } void mitk::SurfaceInterpolationController::SetMinSpacing(double minSpacing) { m_ReduceFilter->SetMinSpacing(minSpacing); } void mitk::SurfaceInterpolationController::SetMaxSpacing(double maxSpacing) { m_ReduceFilter->SetMaxSpacing(maxSpacing); m_NormalsFilter->SetMaxSpacing(maxSpacing); } void mitk::SurfaceInterpolationController::SetDistanceImageVolume(unsigned int distImgVolume) { m_InterpolateSurfaceFilter->SetDistanceImageVolume(distImgVolume); } void mitk::SurfaceInterpolationController::SetWorkingImage(Image* workingImage) { m_NormalsFilter->SetSegmentationBinaryImage(workingImage); } mitk::Image* mitk::SurfaceInterpolationController::GetImage() { return m_InterpolateSurfaceFilter->GetOutput(); } diff --git a/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.h b/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.h index a2de3d9558..7f8507c28b 100644 --- a/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.h +++ b/Modules/Segmentation/Controllers/mitkSurfaceInterpolationController.h @@ -1,131 +1,133 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkSurfaceInterpolationController_h_Included #define mitkSurfaceInterpolationController_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkSurface.h" #include "mitkInteractionConst.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkCreateDistanceImageFromSurfaceFilter.h" #include "mitkReduceContourSetFilter.h" #include "mitkComputeContourSetNormalsFilter.h" #include "mitkDataNode.h" #include "mitkDataStorage.h" #include "mitkWeakPointer.h" #include "vtkPolygon.h" #include "vtkPoints.h" #include "vtkCellArray.h" #include "vtkPolyData.h" #include "vtkSmartPointer.h" #include "vtkAppendPolyData.h" #include "vtkMarchingCubes.h" #include "vtkImageData.h" #include "mitkVtkRepresentationProperty.h" #include "vtkProperty.h" #include "mitkProgressBar.h" namespace mitk { class Segmentation_EXPORT SurfaceInterpolationController : public itk::Object { public: mitkClassMacro(SurfaceInterpolationController, itk::Object); itkNewMacro(Self); static SurfaceInterpolationController* GetInstance(); /* * Adds a new extracted contour to the list */ void AddNewContour(Surface::Pointer newContour, RestorePlanePositionOperation *op); /* * Interpolates the 3D surface from the given extracted contours */ void Interpolate (); mitk::Surface::Pointer GetInterpolationResult(); void SetMinSpacing(double minSpacing); void SetMaxSpacing(double maxSpacing); void SetDistanceImageVolume(unsigned int distImageVolume); void SetWorkingImage(Image* workingImage); Surface* GetContoursAsSurface(); void SetDataStorage(DataStorage &ds); unsigned int CreateNewContourList(); void SetCurrentListID (unsigned int ID); mitk::Image* GetImage(); protected: SurfaceInterpolationController(); ~SurfaceInterpolationController(); private: struct ContourPositionPair { Surface::Pointer contour; RestorePlanePositionOperation* position; }; typedef std::vector ContourPositionPairList; ContourPositionPairList::iterator m_Iterator; ReduceContourSetFilter::Pointer m_ReduceFilter; ComputeContourSetNormalsFilter::Pointer m_NormalsFilter; CreateDistanceImageFromSurfaceFilter::Pointer m_InterpolateSurfaceFilter; double m_MinSpacing; double m_MaxSpacing; const Image* m_WorkingImage; Surface::Pointer m_Contours; vtkSmartPointer m_PolyData; unsigned int m_DistImageVolume; mitk::WeakPointer m_DataStorage; std::vector m_ListOfContourLists; unsigned int m_CurrentContourListID; mitk::Surface::Pointer m_InterpolationResult; + + unsigned int m_CurrentNumberOfReducedContours; }; } #endif diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.cpp index 36c7ca2d3a..bbfadb486d 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.cpp @@ -1,677 +1,711 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // ####### Blueberry includes ####### #include #include // ####### Qmitk includes ####### #include "QmitkBrainNetworkAnalysisView.h" #include "QmitkStdMultiWidget.h" // ####### Qt includes ####### #include // ####### ITK includes ####### #include // ####### MITK includes ####### #include #include "mitkConnectomicsSyntheticNetworkGenerator.h" #include "mitkConnectomicsSimulatedAnnealingManager.h" #include "mitkConnectomicsSimulatedAnnealingPermutationModularity.h" #include "mitkConnectomicsSimulatedAnnealingCostFunctionModularity.h" // Includes for image casting between ITK and MITK #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" const std::string QmitkBrainNetworkAnalysisView::VIEW_ID = "org.mitk.views.brainnetworkanalysis"; QmitkBrainNetworkAnalysisView::QmitkBrainNetworkAnalysisView() : QmitkFunctionality() , m_Controls( 0 ) , m_MultiWidget( NULL ) , m_ConnectomicsNetworkCreator( mitk::ConnectomicsNetworkCreator::New() ) , m_demomode( false ) , m_currentIndex( 0 ) { } QmitkBrainNetworkAnalysisView::~QmitkBrainNetworkAnalysisView() { } void QmitkBrainNetworkAnalysisView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkBrainNetworkAnalysisViewControls; m_Controls->setupUi( parent ); QObject::connect( m_Controls->convertToRGBAImagePushButton, SIGNAL(clicked()), this, SLOT(OnConvertToRGBAImagePushButtonClicked()) ); QObject::connect( m_Controls->networkifyPushButton, SIGNAL(clicked()), this, SLOT(OnNetworkifyPushButtonClicked()) ); QObject::connect( m_Controls->syntheticNetworkCreationPushButton, SIGNAL(clicked()), this, SLOT(OnSyntheticNetworkCreationPushButtonClicked()) ); - QObject::connect( (QObject*)( m_Controls->syntheticNetworkComboBox ), SIGNAL(currentIndexChanged (int)), this, SLOT(OnSyntheticNetworkComboBoxCurrentIndexChanged(int)) ); - QObject::connect( (QObject*)( m_Controls->modularizePushButton ), SIGNAL(clicked()), this, SLOT(OnModularizePushButtonClicked()) ); + QObject::connect( (QObject*)( m_Controls->syntheticNetworkComboBox ), SIGNAL(currentIndexChanged (int)), this, SLOT(OnSyntheticNetworkComboBoxCurrentIndexChanged(int)) ); + QObject::connect( (QObject*)( m_Controls->modularizePushButton ), SIGNAL(clicked()), this, SLOT(OnModularizePushButtonClicked()) ); + QObject::connect( (QObject*)( m_Controls->prunePushButton ), SIGNAL(clicked()), this, SLOT(OnPrunePushButtonClicked()) ); } // GUI is different for developer and demo mode m_demomode = false; if( m_demomode ) { this->m_Controls->convertToRGBAImagePushButton->hide(); this->m_Controls->networkifyPushButton->show(); this->m_Controls->networkifyPushButton->setText( "Create Network" ); this->m_Controls->modularizePushButton->hide(); + this->m_Controls->pruneOptionsGroupBox->hide(); this->m_Controls->syntheticNetworkOptionsGroupBox->show(); //--------------------------- fill comboBox--------------------------- this->m_Controls->syntheticNetworkComboBox->insertItem(0,"Regular lattice"); this->m_Controls->syntheticNetworkComboBox->insertItem(1,"Heterogenic sphere"); this->m_Controls->syntheticNetworkComboBox->insertItem(2,"Random network"); } else { this->m_Controls->convertToRGBAImagePushButton->show(); this->m_Controls->networkifyPushButton->show(); this->m_Controls->networkifyPushButton->setText( "Networkify" ); this->m_Controls->modularizePushButton->show(); + this->m_Controls->pruneOptionsGroupBox->show(); this->m_Controls->syntheticNetworkOptionsGroupBox->show(); //--------------------------- fill comboBox--------------------------- this->m_Controls->syntheticNetworkComboBox->insertItem(0,"Regular lattice"); this->m_Controls->syntheticNetworkComboBox->insertItem(1,"Heterogenic sphere"); this->m_Controls->syntheticNetworkComboBox->insertItem(2,"Random network"); this->m_Controls->syntheticNetworkComboBox->insertItem(3,"Scale free network"); this->m_Controls->syntheticNetworkComboBox->insertItem(4,"Small world network"); } this->WipeDisplay(); } void QmitkBrainNetworkAnalysisView::StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget) { m_MultiWidget = &stdMultiWidget; } void QmitkBrainNetworkAnalysisView::StdMultiWidgetNotAvailable() { m_MultiWidget = NULL; } void QmitkBrainNetworkAnalysisView::WipeDisplay() { m_Controls->lblWarning->setVisible( true ); m_Controls->inputImageOneNameLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->inputImageOneNameLabel->setVisible( false ); m_Controls->inputImageOneLabel->setVisible( false ); m_Controls->inputImageTwoNameLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->inputImageTwoNameLabel->setVisible( false ); m_Controls->inputImageTwoLabel->setVisible( false ); m_Controls->numberOfVerticesLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->numberOfEdgesLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->numberOfSelfLoopsLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->averageDegreeLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->connectionDensityLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->efficiencyLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->globalClusteringLabel->setText( mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_DASH ); m_Controls->betweennessNetworkHistogramCanvas->SetHistogram( NULL ); m_Controls->degreeNetworkHistogramCanvas->SetHistogram( NULL ); m_Controls->shortestPathNetworkHistogramCanvas->SetHistogram( NULL ); m_Controls->betweennessNetworkHistogramCanvas->update(); m_Controls->degreeNetworkHistogramCanvas->update(); m_Controls->shortestPathNetworkHistogramCanvas->update(); m_Controls->betweennessNetworkHistogramCanvas->Clear(); m_Controls->degreeNetworkHistogramCanvas->Clear(); m_Controls->shortestPathNetworkHistogramCanvas->Clear(); m_Controls->betweennessNetworkHistogramCanvas->Replot(); m_Controls->degreeNetworkHistogramCanvas->Replot(); m_Controls->shortestPathNetworkHistogramCanvas->Replot(); } void QmitkBrainNetworkAnalysisView::OnSelectionChanged( std::vector nodes ) { this->WipeDisplay(); // Valid options are either // 1 image (parcellation) // // 1 image (parcellation) // 1 fiber bundle // // 1 network if( nodes.size() > 2 ) { return; } bool alreadyFiberBundleSelected( false ), alreadyImageSelected( false ), currentFormatUnknown( true ); // iterate all selected objects, adjust warning visibility for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; currentFormatUnknown = true; if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { currentFormatUnknown = false; if( alreadyImageSelected ) { this->WipeDisplay(); return; } alreadyImageSelected = true; m_Controls->lblWarning->setVisible( false ); m_Controls->inputImageOneNameLabel->setText(node->GetName().c_str()); m_Controls->inputImageOneNameLabel->setVisible( true ); m_Controls->inputImageOneLabel->setVisible( true ); } if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { currentFormatUnknown = false; // a fiber bundle has to be in conjunction with a parcellation if( nodes.size() != 2 || alreadyFiberBundleSelected ) { this->WipeDisplay(); return; } alreadyFiberBundleSelected = true; m_Controls->lblWarning->setVisible( false ); m_Controls->inputImageTwoNameLabel->setText(node->GetName().c_str()); m_Controls->inputImageTwoNameLabel->setVisible( true ); m_Controls->inputImageTwoLabel->setVisible( true ); } { // network section mitk::ConnectomicsNetwork* network = dynamic_cast( node->GetData() ); if( node.IsNotNull() && network ) { currentFormatUnknown = false; if( nodes.size() != 1 ) { // only valid option is a single network this->WipeDisplay(); return; } m_Controls->lblWarning->setVisible( false ); m_Controls->inputImageOneNameLabel->setText(node->GetName().c_str()); m_Controls->inputImageOneNameLabel->setVisible( true ); m_Controls->inputImageOneLabel->setVisible( true ); int noVertices = network->GetNumberOfVertices(); int noEdges = network->GetNumberOfEdges(); int noSelfLoops = network->GetNumberOfSelfLoops(); double averageDegree = network->GetAverageDegree(); double connectionDensity = network->GetConnectionDensity(); double globalClustering = network->GetGlobalClusteringCoefficient(); m_Controls->numberOfVerticesLabel->setText( QString::number( noVertices ) ); m_Controls->numberOfEdgesLabel->setText( QString::number( noEdges ) ); m_Controls->numberOfSelfLoopsLabel->setText( QString::number( noSelfLoops ) ); m_Controls->averageDegreeLabel->setText( QString::number( averageDegree ) ); m_Controls->connectionDensityLabel->setText( QString::number( connectionDensity ) ); m_Controls->globalClusteringLabel->setText( QString::number( globalClustering ) ); mitk::ConnectomicsNetwork::Pointer connectomicsNetwork( network ); mitk::ConnectomicsHistogramsContainer *histogramContainer = histogramCache[ connectomicsNetwork ]; if(histogramContainer) { m_Controls->betweennessNetworkHistogramCanvas->SetHistogram( histogramContainer->GetBetweennessHistogram() ); m_Controls->degreeNetworkHistogramCanvas->SetHistogram( histogramContainer->GetDegreeHistogram() ); m_Controls->shortestPathNetworkHistogramCanvas->SetHistogram( histogramContainer->GetShortestPathHistogram() ); m_Controls->betweennessNetworkHistogramCanvas->DrawProfiles(); m_Controls->degreeNetworkHistogramCanvas->DrawProfiles(); m_Controls->shortestPathNetworkHistogramCanvas->DrawProfiles(); double efficiency = histogramContainer->GetShortestPathHistogram()->GetEfficiency(); m_Controls->efficiencyLabel->setText( QString::number( efficiency ) ); } } } // end network section if ( currentFormatUnknown ) { this->WipeDisplay(); return; } } // end for loop } void QmitkBrainNetworkAnalysisView::OnSyntheticNetworkComboBoxCurrentIndexChanged(int currentIndex) { m_currentIndex = currentIndex; switch (m_currentIndex) { case 0: this->m_Controls->parameterOneLabel->setText( "Nodes per side" ); this->m_Controls->parameterTwoLabel->setText( "Internode distance" ); this->m_Controls->parameterOneSpinBox->setEnabled( true ); this->m_Controls->parameterOneSpinBox->setValue( 5 ); this->m_Controls->parameterTwoDoubleSpinBox->setEnabled( true ); this->m_Controls->parameterTwoDoubleSpinBox->setMaximum( 999.999 ); this->m_Controls->parameterTwoDoubleSpinBox->setValue( 10.0 ); break; case 1: this->m_Controls->parameterOneLabel->setText( "Number of nodes" ); this->m_Controls->parameterTwoLabel->setText( "Radius" ); this->m_Controls->parameterOneSpinBox->setEnabled( true ); this->m_Controls->parameterOneSpinBox->setValue( 1000 ); this->m_Controls->parameterTwoDoubleSpinBox->setEnabled( true ); this->m_Controls->parameterTwoDoubleSpinBox->setMaximum( 999.999 ); this->m_Controls->parameterTwoDoubleSpinBox->setValue( 50.0 ); break; case 2: this->m_Controls->parameterOneLabel->setText( "Number of nodes" ); this->m_Controls->parameterTwoLabel->setText( "Edge percentage" ); this->m_Controls->parameterOneSpinBox->setEnabled( true ); this->m_Controls->parameterOneSpinBox->setValue( 100 ); this->m_Controls->parameterTwoDoubleSpinBox->setEnabled( true ); this->m_Controls->parameterTwoDoubleSpinBox->setMaximum( 1.0 ); this->m_Controls->parameterTwoDoubleSpinBox->setValue( 0.5 ); break; case 3: //GenerateSyntheticScaleFreeNetwork( network, 1000 ); break; case 4: //GenerateSyntheticSmallWorldNetwork( network, 1000 ); break; default: this->m_Controls->parameterOneLabel->setText( "Parameter 1" ); this->m_Controls->parameterTwoLabel->setText( "Paramater 2" ); this->m_Controls->parameterOneSpinBox->setEnabled( false ); this->m_Controls->parameterOneSpinBox->setValue( 0 ); this->m_Controls->parameterTwoDoubleSpinBox->setEnabled( false ); this->m_Controls->parameterTwoDoubleSpinBox->setValue( 0.0 ); } } void QmitkBrainNetworkAnalysisView::OnSyntheticNetworkCreationPushButtonClicked() { // warn if trying to create a very big network // big network is a network with > 5000 nodes (estimate) // this might fill up the memory to the point it freezes int numberOfNodes( 0 ); switch (m_currentIndex) { case 0: numberOfNodes = this->m_Controls->parameterOneSpinBox->value() * this->m_Controls->parameterOneSpinBox->value() * this->m_Controls->parameterOneSpinBox->value(); break; case 1: numberOfNodes = this->m_Controls->parameterOneSpinBox->value(); break; case 2: numberOfNodes = this->m_Controls->parameterOneSpinBox->value(); break; case 3: // not implemented yet break; case 4: // not implemented yet break; default: break; } if( numberOfNodes > 5000 ) { QMessageBox msgBox; msgBox.setText("Trying to generate very large network."); msgBox.setIcon( QMessageBox::Warning ); msgBox.setInformativeText("You are trying to generate a network with more than 5000 nodes, this is very resource intensive and might lead to program instability. Proceed with network generation?"); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); int ret = msgBox.exec(); switch (ret) { case QMessageBox::Yes: // continue break; case QMessageBox::No: // stop return; break; default: // should never be reached break; } } // proceed mitk::ConnectomicsSyntheticNetworkGenerator::Pointer generator = mitk::ConnectomicsSyntheticNetworkGenerator::New(); mitk::DataNode::Pointer networkNode = mitk::DataNode::New(); int parameterOne = this->m_Controls->parameterOneSpinBox->value(); double parameterTwo = this->m_Controls->parameterTwoDoubleSpinBox->value(); - ////add network to datastorage + //add network to datastorage networkNode->SetData( generator->CreateSyntheticNetwork( m_currentIndex, parameterOne, parameterTwo ) ); networkNode->SetName( mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_DEFAULT_CNF_NAME ); if( generator->WasGenerationSuccessfull() ) { this->GetDefaultDataStorage()->Add( networkNode ); } else { MITK_WARN << "Problem occured during synthetic network generation."; } return; } void QmitkBrainNetworkAnalysisView::OnConvertToRGBAImagePushButtonClicked() { std::vector nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataNode* node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information( NULL, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_CONNECTOMICS_CREATION, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_SELECTION_WARNING); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData* data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image* image = dynamic_cast( data ); if (image) { std::stringstream message; std::string name; message << mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_PERFORMING_IMAGE_PROCESSING_FOR_IMAGE; if (node->GetName(name)) { // a property called "name" was found for this DataNode message << "'" << name << "'"; } message << "."; MITK_INFO << message.str(); // Convert to RGBA AccessByItk( image, TurnIntoRGBA ); this->GetDefaultDataStorage()->GetNamedNode( mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_DEFAULT_RGBA_NAME )->GetData()->SetGeometry( node->GetData()->GetGeometry() ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } template < typename TPixel, unsigned int VImageDimension > void QmitkBrainNetworkAnalysisView::TurnIntoRGBA( itk::Image* inputImage) { typedef itk::RGBAPixel< unsigned char > RGBAPixelType; typedef itk::Image< TPixel, VImageDimension > TemplateImageType; typedef itk::Image< RGBAPixelType, VImageDimension > RGBAImageType; itk::ImageRegionIterator it_inputImage(inputImage, inputImage->GetLargestPossibleRegion()); TPixel minimumValue, maximumValue; it_inputImage.GoToBegin(); maximumValue = minimumValue = it_inputImage.Value(); for(it_inputImage.GoToBegin(); !it_inputImage.IsAtEnd(); ++it_inputImage) { if ( it_inputImage.Value() < minimumValue ) { minimumValue = it_inputImage.Value(); } else { if ( it_inputImage.Value() > maximumValue ) { maximumValue = it_inputImage.Value(); } } } int range = int ( maximumValue - minimumValue ); //needs to be castable to int int offset = int ( minimumValue ); if ( range < 0 ) //error { return; } std::vector< unsigned int > histogram; histogram.resize( range + 1, 0 ); for(it_inputImage.GoToBegin(); !it_inputImage.IsAtEnd(); ++it_inputImage) { histogram[ int ( it_inputImage.Value() ) - offset ] += 1; } int gapCounter = 0; //this variable will be used to count the empty labels //stores how much has to be subtracted from the image to remove gaps std::vector< TPixel > subtractionStorage; subtractionStorage.resize( range + 1, 0 ); for( int index = 0; index <= range; index++ ) { if( histogram[ index ] == 0 ) { gapCounter++; //if the label is empty, increase gapCounter } else { subtractionStorage[ index ] = TPixel ( gapCounter ); } } //remove gaps from label image for(it_inputImage.GoToBegin(); !it_inputImage.IsAtEnd(); ++it_inputImage) { it_inputImage.Value() = it_inputImage.Value() - subtractionStorage[ int ( it_inputImage.Value() ) ]; } // create colour vector std::vector< RGBAPixelType > lookupTable; { RGBAPixelType backgroundColour; for( int elementIndex = 0; elementIndex < 4; ++elementIndex ) { backgroundColour.SetElement( elementIndex, 0 ); } lookupTable.push_back( backgroundColour ); for(int colourNumber = 0; colourNumber < range ; ++colourNumber) { RGBAPixelType colour; for( int elementIndex = 0; elementIndex < 3; ++elementIndex ) { colour.SetElement( elementIndex, rand() % 256 ); } colour.SetAlpha( 255 ); lookupTable.push_back( colour ); } } // create RGBA image typename RGBAImageType::Pointer rgbaImage = RGBAImageType::New(); rgbaImage->SetRegions(inputImage->GetLargestPossibleRegion().GetSize()); rgbaImage->SetSpacing(inputImage->GetSpacing()); rgbaImage->SetOrigin(inputImage->GetOrigin()); rgbaImage->Allocate(); //fill with appropriate colours itk::ImageRegionIterator it_rgbaImage(rgbaImage, rgbaImage->GetLargestPossibleRegion()); for(it_inputImage.GoToBegin(), it_rgbaImage.GoToBegin(); !it_inputImage.IsAtEnd(); ++it_inputImage, ++it_rgbaImage) { it_rgbaImage.Value() = lookupTable[ int ( it_inputImage.Value() ) ]; } mitk::Image::Pointer mitkRGBAImage = mitk::ImportItkImage( rgbaImage ); mitk::DataNode::Pointer rgbaImageNode = mitk::DataNode::New(); rgbaImageNode->SetData(mitkRGBAImage); rgbaImageNode->SetProperty(mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_NAME, mitk::StringProperty::New(mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_DEFAULT_RGBA_NAME)); rgbaImageNode->SetBoolProperty( mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_VOLUMERENDERING, true); this->GetDefaultDataStorage()->Add( rgbaImageNode ); } void QmitkBrainNetworkAnalysisView::OnNetworkifyPushButtonClicked() { std::vector nodes = this->GetDataManagerSelection(); if ( nodes.empty() ) { QMessageBox::information( NULL, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_CONNECTOMICS_CREATION, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_SELECTION_WARNING); return; } if (! ( nodes.size() == 2 ) ) { QMessageBox::information( NULL, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_CONNECTOMICS_CREATION, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_SELECTION_WARNING); return; } mitk::DataNode* firstNode = nodes.front(); mitk::DataNode* secondNode = nodes.at(1); if (!firstNode) { // Nothing selected. Inform the user and return QMessageBox::information( NULL, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_CONNECTOMICS_CREATION, mitk::ConnectomicsConstantsManager::CONNECTOMICS_GUI_SELECTION_WARNING); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData* firstData = firstNode->GetData(); mitk::BaseData* secondData = secondNode->GetData(); if (firstData && secondData) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image* image = dynamic_cast( firstData ); mitk::FiberBundleX* fiberBundle = dynamic_cast( secondData ); // check whether order was switched if (! (image && fiberBundle) ) { image = dynamic_cast( secondData ); fiberBundle = dynamic_cast( firstData ); } if (image && fiberBundle) { m_ConnectomicsNetworkCreator->SetSegmentation( image ); m_ConnectomicsNetworkCreator->SetFiberBundle( fiberBundle ); m_ConnectomicsNetworkCreator->CreateNetworkFromFibersAndSegmentation(); mitk::DataNode::Pointer networkNode = mitk::DataNode::New(); - ////add network to datastorage + //add network to datastorage networkNode->SetData( m_ConnectomicsNetworkCreator->GetNetwork() ); networkNode->SetName( mitk::ConnectomicsConstantsManager::CONNECTOMICS_PROPERTY_DEFAULT_CNF_NAME ); this->GetDefaultDataStorage()->Add( networkNode ); } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkBrainNetworkAnalysisView::OnModularizePushButtonClicked() { std::vector nodes = this->GetDataManagerSelection(); if ( nodes.empty() ) { QMessageBox::information( NULL, "Modularization calculation", "Please select exactly one network."); return; } for( std::vector::iterator it = nodes.begin(); it != nodes.end(); ++it ) { mitk::DataNode::Pointer node = *it; if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { return; } { mitk::ConnectomicsNetwork* network = dynamic_cast( node->GetData() ); if( node.IsNotNull() && network ) { typedef mitk::ConnectomicsSimulatedAnnealingPermutationModularity::ToModuleMapType MappingType; int depthOfModuleRecursive( 2 ); double startTemperature( 2.0 ); double stepSize( 4.0 ); mitk::ConnectomicsNetwork::Pointer connectomicsNetwork( network ); mitk::ConnectomicsSimulatedAnnealingManager::Pointer manager = mitk::ConnectomicsSimulatedAnnealingManager::New(); mitk::ConnectomicsSimulatedAnnealingPermutationModularity::Pointer permutation = mitk::ConnectomicsSimulatedAnnealingPermutationModularity::New(); mitk::ConnectomicsSimulatedAnnealingCostFunctionModularity::Pointer costFunction = mitk::ConnectomicsSimulatedAnnealingCostFunctionModularity::New(); permutation->SetCostFunction( costFunction.GetPointer() ); permutation->SetNetwork( connectomicsNetwork ); permutation->SetDepth( depthOfModuleRecursive ); permutation->SetStepSize( stepSize ); manager->SetPermutation( permutation.GetPointer() ); manager->RunSimulatedAnnealing( startTemperature, stepSize ); MappingType mapping = permutation->GetMapping(); MappingType::iterator iter = mapping.begin(); MappingType::iterator end = mapping.end(); int loop( 0 ); while( iter != end ) { MBI_DEBUG << "Vertex " << iter->first << " belongs to module " << iter->second ; MBI_INFO << "Vertex " << iter->first << " belongs to module " << iter->second ; iter++; } MBI_DEBUG << "Overall number of modules is " << permutation->getNumberOfModules( &mapping ) ; MBI_DEBUG << "Cost is " << costFunction->Evaluate( network, &mapping ) ; MBI_INFO << "Overall number of modules is " << permutation->getNumberOfModules( &mapping ) ; MBI_INFO << "Cost is " << costFunction->Evaluate( network, &mapping ) ; return; } } } } + +void QmitkBrainNetworkAnalysisView::OnPrunePushButtonClicked() +{ + std::vector nodes = this->GetDataManagerSelection(); + if ( nodes.empty() ) + { + QMessageBox::information( NULL, "Network pruning", "Please select one or more network."); + return; + } + + for( std::vector::iterator it = nodes.begin(); + it != nodes.end(); + ++it ) + { + mitk::DataNode::Pointer node = *it; + + if( node.IsNotNull() && dynamic_cast(node->GetData()) ) + { + return; + } + + { + mitk::ConnectomicsNetwork* network = dynamic_cast( node->GetData() ); + if( node.IsNotNull() && network ) + { + // Edge pruning will also do node pruning + network->PruneEdgesBelowWeight( this->m_Controls->pruneEdgeWeightSpinBox->value() ); + } + } + } +} diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.h b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.h index ef6158c2bc..712604a641 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisView.h @@ -1,115 +1,118 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkBrainNetworkAnalysisView_h #define QmitkBrainNetworkAnalysisView_h #include #include #include "ui_QmitkBrainNetworkAnalysisViewControls.h" #include "mitkConnectomicsNetworkCreator.h" #include "mitkConnectomicsNetworkMapper3D.h" #include "mitkConnectomicsHistogramCache.h" // ####### ITK includes ####### #include /*! \brief QmitkBrainNetworkAnalysisView This bundle provides GUI for the brain network analysis algorithms. \sa QmitkFunctionality \ingroup Functionalities */ class QmitkBrainNetworkAnalysisView : public QmitkFunctionality { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; QmitkBrainNetworkAnalysisView(); virtual ~QmitkBrainNetworkAnalysisView(); virtual void CreateQtPartControl(QWidget *parent); virtual void StdMultiWidgetAvailable (QmitkStdMultiWidget &stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); protected slots: /// \brief Called when the user clicks the GUI button void OnConvertToRGBAImagePushButtonClicked(); /// \brief Align two images by copying the geometry void OnNetworkifyPushButtonClicked(); /// \brief Create synthetic networks void OnSyntheticNetworkCreationPushButtonClicked(); /// \brief Adjust parameters depending on synthetic network type void OnSyntheticNetworkComboBoxCurrentIndexChanged(int currentIndex); /// \brief Create modularization of network void OnModularizePushButtonClicked(); + /// \brief Prune network + void OnPrunePushButtonClicked(); + protected: // ####### Functions ####### /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( std::vector nodes ); /// \brief Converts an image into a RGBA image template < typename TPixel, unsigned int VImageDimension > void TurnIntoRGBA( itk::Image* inputImage); /// \brief Wipe display and empty statistics void WipeDisplay(); // ####### Variables ####### Ui::QmitkBrainNetworkAnalysisViewControls* m_Controls; QmitkStdMultiWidget* m_MultiWidget; mitk::ConnectomicsNetworkCreator::Pointer m_ConnectomicsNetworkCreator; mitk::ConnectomicsNetworkMapper3D::Pointer m_NetworkMapper; /// Cache for histograms mitk::ConnectomicsHistogramCache histogramCache; // Demo/Developer mode toggle bool m_demomode; // The selected synthetic network type int m_currentIndex; }; #endif // _QMITKBRAINNETWORKANALYSISVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisViewControls.ui index 497e043298..7f749cfcca 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/Connectomics/QmitkBrainNetworkAnalysisViewControls.ui @@ -1,363 +1,393 @@ QmitkBrainNetworkAnalysisViewControls 0 0 227 - 1012 + 1045 0 0 QmitkTemplate Data QLabel { color: rgb(255, 0, 0) } Please select data! Image 1: - Image 2: - Convert the selected image to RGBA format Convert to RGBA Create a network from a parcellation and a fiber image Networkify Create Synthetic Networks Divide in Modules + + + + Prune Options + + + + + + Prune Network + + + + + + + + + Prune edge weight below + + + + + + + + + + + Qt::Vertical 20 40 true Synthetic Network Options Parameter 1 false 9999 Parameter 2 false 3 999.899999999999977 Network Statistics # of vertices: # of edges: - - # of self loops: - average degree - connection density - efficiency - global clustering - Histograms 0 0 50 150 50 150 50 150 Qt::Vertical 20 40 QmitkNetworkHistogramCanvas QWidget
internal/Connectomics/QmitkNetworkHistogramCanvas.h
1