diff --git a/Core/Code/DataManagement/mitkLevelWindow.cpp b/Core/Code/DataManagement/mitkLevelWindow.cpp index f6d7c6385b..56e6fb5017 100644 --- a/Core/Code/DataManagement/mitkLevelWindow.cpp +++ b/Core/Code/DataManagement/mitkLevelWindow.cpp @@ -1,442 +1,443 @@ /*=================================================================== 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 "mitkLevelWindow.h" #include "mitkImageSliceSelector.h" #include "mitkImageStatisticsHolder.h" #include void mitk::LevelWindow::EnsureConsistency() { // Check if total range is ok { if ( m_RangeMin > m_RangeMax ) std::swap(m_RangeMin,m_RangeMax); if (m_RangeMin == m_RangeMax ) m_RangeMin = m_RangeMax - 1; } // Check if current window is ok { if ( m_LowerWindowBound > m_UpperWindowBound ) std::swap(m_LowerWindowBound,m_UpperWindowBound); if ( m_LowerWindowBound < m_RangeMin ) m_LowerWindowBound = m_RangeMin; if ( m_UpperWindowBound < m_RangeMin ) m_UpperWindowBound = m_RangeMin; if ( m_LowerWindowBound > m_RangeMax ) m_LowerWindowBound = m_RangeMax; if ( m_UpperWindowBound > m_RangeMax ) m_UpperWindowBound = m_RangeMax; if (m_LowerWindowBound == m_UpperWindowBound ) { if(m_LowerWindowBound == m_RangeMin ) m_UpperWindowBound++; else m_LowerWindowBound--; } } } mitk::LevelWindow::LevelWindow(mitk::ScalarType level, mitk::ScalarType window) : m_LowerWindowBound( level - window / 2.0 ), m_UpperWindowBound( level + window / 2.0 ), m_RangeMin( -2048.0 ), m_RangeMax( 4096.0 ), m_DefaultLowerBound( -2048.0 ), m_DefaultUpperBound( 4096.0 ), m_Fixed( false ) { SetDefaultLevelWindow(level, window); + SetLevelWindow(level, window, true); } mitk::LevelWindow::LevelWindow(const mitk::LevelWindow& levWin) : m_LowerWindowBound( levWin.GetLowerWindowBound() ) , m_UpperWindowBound( levWin.GetUpperWindowBound() ) , m_RangeMin( levWin.GetRangeMin() ) , m_RangeMax( levWin.GetRangeMax() ) , m_DefaultLowerBound( levWin.GetDefaultLowerBound() ) , m_DefaultUpperBound( levWin.GetDefaultUpperBound() ) , m_Fixed( levWin.GetFixed() ) { } mitk::LevelWindow::~LevelWindow() { } mitk::ScalarType mitk::LevelWindow::GetLevel() const { return (m_UpperWindowBound-m_LowerWindowBound) / 2.0 + m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetWindow() const { return (m_UpperWindowBound-m_LowerWindowBound); } mitk::ScalarType mitk::LevelWindow::GetDefaultLevel() const { return ((m_DefaultUpperBound+m_DefaultLowerBound)/2.0); } mitk::ScalarType mitk::LevelWindow::GetDefaultWindow() const { return ((m_DefaultUpperBound-m_DefaultLowerBound)); } void mitk::LevelWindow::ResetDefaultLevelWindow() { SetLevelWindow(GetDefaultLevel(), GetDefaultWindow()); } mitk::ScalarType mitk::LevelWindow::GetLowerWindowBound() const { return m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetUpperWindowBound() const { return m_UpperWindowBound; } void mitk::LevelWindow::SetDefaultLevelWindow(mitk::ScalarType level, mitk::ScalarType window) { SetDefaultBoundaries((level-(window/2.0)), (level+(window/2.0))); } void mitk::LevelWindow::SetLevelWindow(mitk::ScalarType level, mitk::ScalarType window, bool expandRangesIfNecessary) { SetWindowBounds( (level-(window/2.0)), (level+(window/2.0)), expandRangesIfNecessary ); } void mitk::LevelWindow::SetWindowBounds(mitk::ScalarType lowerBound, mitk::ScalarType upperBound, bool expandRangesIfNecessary) { if ( IsFixed() ) return; m_LowerWindowBound = lowerBound; m_UpperWindowBound = upperBound; if (expandRangesIfNecessary) { /* if caller is sure he wants exactly that level/window, we make sure the limits match */ if (m_LowerWindowBound > m_UpperWindowBound) std::swap(m_LowerWindowBound, m_UpperWindowBound); if ( m_LowerWindowBound < m_RangeMin ) { m_RangeMin = m_LowerWindowBound; } if ( m_UpperWindowBound > m_RangeMax ) { m_RangeMax = m_UpperWindowBound; } } EnsureConsistency(); } void mitk::LevelWindow::SetRangeMinMax(mitk::ScalarType min, mitk::ScalarType max) { if ( IsFixed() ) return; m_RangeMin = min; m_RangeMax = max; EnsureConsistency(); } void mitk::LevelWindow::SetDefaultBoundaries(mitk::ScalarType low, mitk::ScalarType up) { if ( IsFixed() ) return; m_DefaultLowerBound = low; m_DefaultUpperBound = up; // Check if default window is ok { if ( m_DefaultLowerBound > m_DefaultUpperBound ) std::swap(m_DefaultLowerBound,m_DefaultUpperBound); if (m_DefaultLowerBound == m_DefaultUpperBound ) m_DefaultLowerBound--; } EnsureConsistency(); } void mitk::LevelWindow::SetToMaxWindowSize() { SetWindowBounds( m_RangeMin , m_RangeMax ); } mitk::ScalarType mitk::LevelWindow::GetRangeMin() const { return m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetRangeMax() const { return m_RangeMax; } mitk::ScalarType mitk::LevelWindow::GetRange() const { return m_RangeMax - m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetDefaultUpperBound() const { return m_DefaultUpperBound; } mitk::ScalarType mitk::LevelWindow::GetDefaultLowerBound() const { return m_DefaultLowerBound; } void mitk::LevelWindow::ResetDefaultRangeMinMax() { SetRangeMinMax(m_DefaultLowerBound, m_DefaultUpperBound); } /*! This method initializes a mitk::LevelWindow from an mitk::Image. The algorithm is as follows: Default to taking the central image slice for quick analysis. Compute the smallest (minValue), second smallest (min2ndValue), second largest (max2ndValue), and largest (maxValue) data value by traversing the pixel values only once. In the same scan it also computes the count of minValue values and maxValue values. After that a basic histogram with specific information about the extrems is complete. If minValue == maxValue, the center slice is uniform and the above scan is repeated for the complete image, not just one slice Next, special cases of images with only 1, 2 or 3 distinct data values have hand assigned level window ranges. Next the level window is set relative to the inner range IR = lengthOf([min2ndValue, max2ndValue]) For count(minValue) > 20% the smallest values are frequent and should be distinct from the min2ndValue and larger values (minValue may be std:min, may signify something special) hence the lower end of the level window is set to min2ndValue - 0.5 * IR For count(minValue) <= 20% the smallest values are not so important and can blend with the next ones => min(level window) = min2ndValue And analog for max(level window): count(max2ndValue) > 20%: max(level window) = max2ndValue + 0.5 * IR count(max2ndValue) < 20%: max(level window) = max2ndValue In both 20%+ cases the level window bounds are clamped to the [minValue, maxValue] range In consequence the level window maximizes contrast with minimal amount of computation and does do useful things if the data contains std::min or std:max values or has only 1 or 2 or 3 data values. */ void mitk::LevelWindow::SetAuto(const mitk::Image* image, bool /*tryPicTags*/, bool guessByCentralSlice) { if ( IsFixed() ) return; if ( image == NULL || !image->IsInitialized() ) return; const mitk::Image* wholeImage = image; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); if ( guessByCentralSlice ) { sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2)/2); sliceSelector->SetTimeNr(image->GetDimension(3)/2); sliceSelector->SetChannelNr(image->GetDimension(4)/2); sliceSelector->Update(); image = sliceSelector->GetOutput(); if ( image == NULL || !image->IsInitialized() ) return; minValue = image->GetStatistics()->GetScalarValueMin(); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); if ( minValue == maxValue ) { // guessByCentralSlice seems to have failed, lets look at all data image = wholeImage; minValue = image->GetStatistics()->GetScalarValueMin(); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } } else { const_cast(image)->Update(); minValue = image->GetStatistics()->GetScalarValueMin(0); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); for (unsigned int i = 1; i < image->GetDimension(3); ++i) { ScalarType minValueTemp = image->GetStatistics()->GetScalarValueMin(i); if (minValue > minValueTemp) minValue = minValueTemp; ScalarType maxValueTemp = image->GetStatistics()->GetScalarValueMaxNoRecompute(i); if (maxValue < maxValueTemp) maxValue = maxValueTemp; ScalarType min2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(i); if (min2ndValue > min2ndValueTemp) min2ndValue = min2ndValueTemp; ScalarType max2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(i); if (max2ndValue > max2ndValueTemp) max2ndValue = max2ndValueTemp; } } // Fix for bug# 344 Level Window wird bei Eris Cut bildern nicht richtig gesetzt if ( image->GetPixelType().GetPixelType()==itk::ImageIOBase::SCALAR && image->GetPixelType().GetComponentType() == itk::ImageIOBase::INT && image->GetPixelType().GetBpe() >= 8) { // the windows compiler complains about ambiguos 'pow' call, therefore static casting to (double, int) if (minValue == -( pow( (double) 2.0, static_cast(image->GetPixelType().GetBpe()/2) ) ) ) { minValue = min2ndValue; } } // End fix //// uniform image if ( minValue == maxValue ) { minValue = maxValue-1; } else { //Due to bug #8690 level window now is no longer of fixed range by default but the range adapts according to levelwindow interaction //This is done because the range should be a little bit larger from the beginning so that the scale doesn't start to resize right from the beginning double additionalRange = 0.15*(maxValue-minValue); minValue -= additionalRange; maxValue += additionalRange; } SetRangeMinMax(minValue, maxValue); SetDefaultBoundaries(minValue, maxValue); /* if ( tryPicTags ) // level and window will be set by informations provided directly by the mitkIpPicDescriptor { if ( SetAutoByPicTags(const_cast(image)->GetPic()) ) { return; } } */ unsigned int numPixelsInDataset = image->GetDimensions()[0]; for ( unsigned int k=0; kGetDimension(); ++k ) numPixelsInDataset *= image->GetDimensions()[k]; unsigned int minCount = image->GetStatistics()->GetCountOfMinValuedVoxelsNoRecompute(); unsigned int maxCount = image->GetStatistics()->GetCountOfMaxValuedVoxelsNoRecompute(); float minCountFraction = minCount/float(numPixelsInDataset); float maxCountFraction = maxCount/float(numPixelsInDataset); //// binary image if ( min2ndValue == maxValue ) { // noop; full range is fine } //// triple value image, put middle value in center of gray level ramp else if ( min2ndValue == max2ndValue ) { ScalarType minDelta = std::min(min2ndValue-minValue, maxValue-min2ndValue); minValue = min2ndValue - minDelta; maxValue = min2ndValue + minDelta; } // now we can assume more than three distict scalar values else { ScalarType innerRange = max2ndValue - min2ndValue; if ( minCountFraction > 0.2 ) //// lots of min values -> make different from rest, but not miles away { ScalarType halfInnerRangeGapMinValue = min2ndValue - innerRange/2.0; minValue = std::max(minValue, halfInnerRangeGapMinValue); } else //// few min values -> focus on innerRange { minValue = min2ndValue; } if ( maxCountFraction > 0.2 ) //// lots of max values -> make different from rest { ScalarType halfInnerRangeGapMaxValue = max2ndValue + innerRange/2.0; maxValue = std::min(maxValue, halfInnerRangeGapMaxValue); } else //// few max values -> focus on innerRange { maxValue = max2ndValue; } } SetWindowBounds(minValue, maxValue); SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue); } void mitk::LevelWindow::SetFixed( bool fixed ) { m_Fixed = fixed; } bool mitk::LevelWindow::GetFixed() const { return m_Fixed; } bool mitk::LevelWindow::IsFixed() const { return m_Fixed; } bool mitk::LevelWindow::operator==(const mitk::LevelWindow& levWin) const { if ( m_RangeMin == levWin.GetRangeMin() && m_RangeMax == levWin.GetRangeMax() && m_LowerWindowBound == levWin.GetLowerWindowBound() && m_UpperWindowBound == levWin.GetUpperWindowBound() && m_DefaultLowerBound == levWin.GetDefaultLowerBound() && m_DefaultUpperBound == levWin.GetDefaultUpperBound() && m_Fixed == levWin.IsFixed() ) { return true; } else { return false; } } bool mitk::LevelWindow::operator!=(const mitk::LevelWindow& levWin) const { return ! ( (*this) == levWin); } mitk::LevelWindow& mitk::LevelWindow::operator=(const mitk::LevelWindow& levWin) { if (this == &levWin) { return *this; } else { m_RangeMin = levWin.GetRangeMin(); m_RangeMax = levWin.GetRangeMax(); m_LowerWindowBound= levWin.GetLowerWindowBound(); m_UpperWindowBound= levWin.GetUpperWindowBound(); m_DefaultLowerBound = levWin.GetDefaultLowerBound(); m_DefaultUpperBound = levWin.GetDefaultUpperBound(); m_Fixed = levWin.GetFixed(); return *this; } } diff --git a/Core/Code/Testing/mitkLevelWindowTest.cpp b/Core/Code/Testing/mitkLevelWindowTest.cpp index 212ed1c12b..0cf189e25b 100644 --- a/Core/Code/Testing/mitkLevelWindowTest.cpp +++ b/Core/Code/Testing/mitkLevelWindowTest.cpp @@ -1,960 +1,971 @@ /*=================================================================== 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 "mitkLevelWindow.h" #include /* * Reseting the Levelwindow to default values: * i.e. Range = -1000..1000, Level = 0 and Window = 500 */ void resetLevelWindow(mitk::LevelWindow &lw) { //Default window bounds lw.SetRangeMinMax(-10000,10000); lw.SetLevelWindow(0,500,false); if (lw.GetRangeMin() != -10000 || lw.GetRangeMax() != 10000 || lw.GetLevel() != 0 || lw.GetWindow() != 500) { std::cout << "[Failed] To reset Levelwindow"<GetDefaultWindow(); if (!(defaultWindow == 500)) { std::cout<GetDefaultLevel(); if (!(defaultLevel == 256)) { std::cout<<"[FAILED]"<GetWindow(); if (!(window == 500)) { std::cout<<"[FAILED]"<GetLowerWindowBound() == 6)) { std::cout<<"[FAILED]"<GetUpperWindowBound() == 506)) { std::cout<<"[FAILED]"<GetLevel(); if (!(level == 256)) { std::cout<<"[FAILED]"<SetLevelWindow(20, 100); if (!(levWin->GetLevel() == 20)) { std::cout<<"[FAILED]"<GetWindow() == 100)) { std::cout<<"[FAILED]"<SetLevelWindow(levWin->GetDefaultLevel(), levWin->GetDefaultWindow()); if (!(levWin->GetLevel() == 256) && !(levWin->GetWindow() == 500)) { std::cout<<"[FAILED]"<SetDefaultLevelWindow(20, 200); if (!(levWin->GetDefaultLevel() == 20) && !(levWin->GetDefaultWindow() == 200)) { std::cout<<"[FAILED]"<SetLevelWindow(100, 50); levWin->ResetDefaultLevelWindow(); //double a = levWin->GetLevel(); //double d = levWin->GetWindow(); if (!((levWin->GetLevel() == 20) &&(levWin->GetWindow() == 200))) { std::cout<<"[FAILED]"<SetWindowBounds(0, 2); if (!((levWin->GetLowerWindowBound() == 0) && (levWin->GetUpperWindowBound() == 2) && (levWin->GetLevel() == 1) && (levWin->GetWindow() == 2))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(2000, 2000); if (!(levWin->GetRangeMin() == 1999 && levWin->GetRangeMax() == 2000)) { std::cout<<"[FAILED]"< rangemax"; levWin->SetRangeMinMax(2100, 2000); if (!(levWin->GetRangeMin() == 2000 && levWin->GetRangeMax() == 2100)) { std::cout<<"[FAILED]"<SetRangeMinMax(-1000, 2000); std::cout<<"[PASSED]"<GetRangeMin() == -1000)) { std::cout<<"[FAILED]"<GetRangeMax() == 2000)) { std::cout<<"[FAILED]"<GetRangeMax() - levWin->GetRangeMin()) == levWin->GetRange())) { std::cout<<"[FAILED]"<SetDefaultBoundaries(2000, 2000); if (!(levWin->GetDefaultLowerBound() == 1999 && levWin->GetDefaultUpperBound() == 2000)) { std::cout<<"[FAILED]"< rangemax"; levWin->SetDefaultBoundaries(2100, 2000); if (!(levWin->GetDefaultLowerBound() == 2000 && levWin->GetDefaultUpperBound() == 2100)) { std::cout<<"[FAILED]"<SetDefaultBoundaries(-2000, 8000); std::cout<<"[PASSED]"<GetDefaultLowerBound() == -2000)) { std::cout<<"[FAILED]"<GetDefaultUpperBound() == 8000)) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); if (!((levWin->GetRangeMin() == levWin->GetDefaultLowerBound()) && (levWin->GetRangeMax() == levWin->GetDefaultUpperBound()))) { std::cout<<"[FAILED]"< maxRange "; levWin->SetRangeMinMax(2000, 1000); if (!((levWin->GetRangeMin() == 1000) && (levWin->GetRangeMax() == 2000))) { std::cout<<"[FAILED]"<SetRangeMinMax(2000, -1000); if (!((levWin->GetRangeMin() == -1000) && (levWin->GetRangeMax() == 2000))) { std::cout<<"[FAILED]"<SetRangeMinMax(-2000, -3000); if (!((levWin->GetRangeMin() == -3000) && (levWin->GetRangeMax() == -2000))) { std::cout<<"[FAILED]"<SetRangeMinMax(0, -1000); if (!((levWin->GetRangeMin() == -1000) && (levWin->GetRangeMax() == 0))) { std::cout<<"[FAILED]"<SetRangeMinMax(2000, 0); if (!((levWin->GetRangeMin() == 0) && (levWin->GetRangeMax() == 2000))) { std::cout<<"[FAILED]"<SetRangeMinMax(-10000, 10000); std::cout<<"[PASSED]"< defaultMaxRange "; levWin->SetDefaultBoundaries(2000, 1000); if (!((levWin->GetDefaultLowerBound() == 1000) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout<<"[FAILED]"<SetDefaultBoundaries(2000, -1000); if (!((levWin->GetDefaultLowerBound() == -1000) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout<<"[FAILED]"<SetDefaultBoundaries(-2000, -3000); if (!((levWin->GetDefaultLowerBound() == -3000) && (levWin->GetDefaultUpperBound() == -2000))) { std::cout<<"[FAILED]"<SetDefaultBoundaries(0, -1000); if (!((levWin->GetDefaultLowerBound() == -1000) && (levWin->GetDefaultUpperBound() == 0))) { std::cout<<"[FAILED]"<SetDefaultBoundaries(2000, 0); if (!((levWin->GetDefaultLowerBound() == 0) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout<<"[FAILED]"<SetDefaultBoundaries(-10000, 10000); std::cout<<"[PASSED]"< max "; levWin->SetWindowBounds(2000, 1000); if (!((levWin->GetLowerWindowBound() == 1000) && (levWin->GetUpperWindowBound() == 2000))) { std::cout<<"[FAILED]"<SetWindowBounds(2000, -1000); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 2000))) { std::cout<<"[FAILED]"<SetWindowBounds(-2000, -3000); if (!((levWin->GetLowerWindowBound() == -3000) && (levWin->GetUpperWindowBound() == -2000))) { std::cout<<"[FAILED]"<SetWindowBounds(0, -1000); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 0))) { std::cout<<"[FAILED]"<SetWindowBounds(2000, 0); if (!((levWin->GetLowerWindowBound() == 0) && (levWin->GetUpperWindowBound() == 2000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange, minmax < minrange, minmaxrange, min < minrange & max > minrange // max < minrange & min > minrange, min > maxrange & max < maxrange, min < minrange & max > maxrange // min > maxrange & max < minrange std::cout << "Testing mitk::LevelWindow max > min > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(11000, 12000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<GetLowerWindowBound()<<","<GetUpperWindowBound()<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< min > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(11000, 12000); if (!((levWin->GetLowerWindowBound() == 11000) && (levWin->GetUpperWindowBound() == 12000))) { std::cout<<"[FAILED]"<GetLowerWindowBound()<<","<GetUpperWindowBound()<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< max > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(12000, 11000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< max > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(12000, 11000); if (!((levWin->GetLowerWindowBound() == 11000) && (levWin->GetUpperWindowBound() == 12000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-12000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-12000, -11000); if (!((levWin->GetLowerWindowBound() == -12000) && (levWin->GetUpperWindowBound() == -11000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-11000, -12000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-11000, -12000); if (!((levWin->GetLowerWindowBound() == -12000) && (levWin->GetUpperWindowBound() == -11000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = FALSE"; levWin->SetWindowBounds(9999, 12000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = TRUE"; levWin->SetWindowBounds(9999, 12000); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 12000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< minrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, -9999, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< minrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, -9999); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, 11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, 11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == 11000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< min = max > minrange autoexpand = FALSE"; levWin->SetWindowBounds(5000, 5000, false); if (!((levWin->GetLowerWindowBound() == 4999) && (levWin->GetUpperWindowBound() == 5000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< min = max > minrange autoexpand = TRUE"; levWin->SetWindowBounds(5000, 5000); if (!((levWin->GetLowerWindowBound() == 4999) && (levWin->GetUpperWindowBound() == 5000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-10000, -10000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-10000, -10000); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(10000, 10000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(10000, 10000); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = FALSE"; levWin->SetWindowBounds(11000, 11000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange autoexpand = TRUE"; levWin->SetWindowBounds(11000, 11000); if (!((levWin->GetLowerWindowBound() == 10999) && (levWin->GetUpperWindowBound() == 11000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-11000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetWindowBounds(-11000, -11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -10999))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< min > minrange > max autoexpand = FALSE"; levWin->SetWindowBounds(-9000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< min > minrange > max autoexpand = TRUE"; levWin->SetWindowBounds(-9000, -11000, true); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -9000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange > minrange > max autoexpand = FALSE"; levWin->SetWindowBounds(11000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == 10000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange > minrange > max autoexpand = TRUE"; levWin->SetWindowBounds(11000, -11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == 11000))) { std::cout<<"[FAILED]"<ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(-20000, -15000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange & maxrange < min < max "; levWin->ResetDefaultLevelWindow(); levWin->SetRangeMinMax(-15000, -20000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(-80, 1000); levWin->SetWindowBounds(-1000,110, false); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 110))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(-80, 1000); levWin->SetWindowBounds(-1000,110); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 110))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(1000,-80); levWin->SetWindowBounds(-1000,110, false ); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 110))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(1000,-80); levWin->SetWindowBounds(-1000,110 ); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 110))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(20, 110); if (!((levWin->GetLowerWindowBound() == 20) && (levWin->GetUpperWindowBound() == 110))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxRange & min < maxrange < max "; levWin->SetWindowBounds(-90,1000); levWin->SetRangeMinMax(100, -80); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 100))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxRange & min < minrange < maxrange SetRangeMinMax(20, 100); if (!((levWin->GetLowerWindowBound() == 20) && (levWin->GetUpperWindowBound() == 100))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(20000, 15000); if (!((levWin->GetLowerWindowBound() == 15000) && (levWin->GetUpperWindowBound() == 15001))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"< maxrange & min < max < minrange "; levWin->SetRangeMinMax(20000, 15000); if (!((levWin->GetLowerWindowBound() == 15000) && (levWin->GetUpperWindowBound() == 15001))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<SetRangeMinMax(-20000, -15000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout<<"[FAILED]"<ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout<<"[PASSED]"<DebugOn(); image->Initialize( mitk::MakePixelType(), 3, dim); int *p = (int*)image->GetData(); int size = dim[0]*dim[1]*dim[2]; int i; for(i=0; iGetRange() == levelwindow.GetRange())) { std::cout<<"[FAILED]"< mitk::DICOMImageBlockDescriptor ::DICOMImageBlockDescriptor() :m_ReaderImplementationLevel(SOPClassUnknown) ,m_PropertyList(PropertyList::New()) ,m_TagCache(NULL) ,m_PropertiesOutOfDate(true) { } mitk::DICOMImageBlockDescriptor ::~DICOMImageBlockDescriptor() { } mitk::DICOMImageBlockDescriptor ::DICOMImageBlockDescriptor(const DICOMImageBlockDescriptor& other) :m_ImageFrameList( other.m_ImageFrameList ) ,m_MitkImage( other.m_MitkImage ) ,m_SliceIsLoaded( other.m_SliceIsLoaded ) ,m_ReaderImplementationLevel( other.m_ReaderImplementationLevel ) ,m_TiltInformation( other.m_TiltInformation ) ,m_PropertyList( other.m_PropertyList->Clone() ) ,m_TagCache( other.m_TagCache ) ,m_PropertiesOutOfDate( other.m_PropertiesOutOfDate ) { if (m_MitkImage) { m_MitkImage = m_MitkImage->Clone(); } } mitk::DICOMImageBlockDescriptor& mitk::DICOMImageBlockDescriptor ::operator=(const DICOMImageBlockDescriptor& other) { if (this != &other) { m_ImageFrameList = other.m_ImageFrameList; m_MitkImage = other.m_MitkImage; m_SliceIsLoaded = other.m_SliceIsLoaded; m_ReaderImplementationLevel = other.m_ReaderImplementationLevel; m_TiltInformation = other.m_TiltInformation; if (other.m_PropertyList) { m_PropertyList = other.m_PropertyList->Clone(); } if (other.m_MitkImage) { m_MitkImage = other.m_MitkImage->Clone(); } m_TagCache = other.m_TagCache; m_PropertiesOutOfDate = other.m_PropertiesOutOfDate; } return *this; } void mitk::DICOMImageBlockDescriptor ::SetTiltInformation(const GantryTiltInformation& info) { m_TiltInformation = info; } const mitk::GantryTiltInformation mitk::DICOMImageBlockDescriptor ::GetTiltInformation() const { return m_TiltInformation; } void mitk::DICOMImageBlockDescriptor ::SetImageFrameList(const DICOMImageFrameList& framelist) { m_ImageFrameList = framelist; m_SliceIsLoaded.resize(framelist.size()); m_SliceIsLoaded.assign(framelist.size(), false); m_PropertiesOutOfDate = true; } const mitk::DICOMImageFrameList& mitk::DICOMImageBlockDescriptor ::GetImageFrameList() const { return m_ImageFrameList; } void mitk::DICOMImageBlockDescriptor ::SetMitkImage(Image::Pointer image) { if (m_MitkImage != image) { if (m_TagCache.IsNull()) { MITK_ERROR << "Unable to describe MITK image with properties without a tag-cache object!"; m_MitkImage = NULL; return; } if (m_ImageFrameList.empty()) { MITK_ERROR << "Unable to describe MITK image with properties without a frame list!"; m_MitkImage = NULL; return; } // Should verify that the image matches m_ImageFrameList and m_TagCache // however, this is hard to do without re-analyzing all // TODO we should at least make sure that the number of frames is identical (plus rows/columns, orientation) // without gantry tilt correction, we can also check image origin m_MitkImage = this->DescribeImageWithProperties( this->FixupSpacing(image) ); } } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor ::GetMitkImage() const { return m_MitkImage; } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor ::FixupSpacing(Image* mitkImage) { if (mitkImage) { Vector3D imageSpacing = mitkImage->GetGeometry()->GetSpacing(); ScalarType desiredSpacingX = imageSpacing[0]; ScalarType desiredSpacingY = imageSpacing[1]; this->GetDesiredMITKImagePixelSpacing( desiredSpacingX, desiredSpacingY ); // prefer pixel spacing over imager pixel spacing MITK_DEBUG << "Loaded image with spacing " << imageSpacing[0] << ", " << imageSpacing[1]; MITK_DEBUG << "Found correct spacing info " << desiredSpacingX << ", " << desiredSpacingY; imageSpacing[0] = desiredSpacingX; imageSpacing[1] = desiredSpacingY; mitkImage->GetGeometry()->SetSpacing( imageSpacing ); } return mitkImage; } void mitk::DICOMImageBlockDescriptor ::SetSliceIsLoaded(unsigned int index, bool isLoaded) { if (index < m_SliceIsLoaded.size()) { m_SliceIsLoaded[index] = isLoaded; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } bool mitk::DICOMImageBlockDescriptor ::IsSliceLoaded(unsigned int index) const { if (index < m_SliceIsLoaded.size()) { return m_SliceIsLoaded[index]; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } bool mitk::DICOMImageBlockDescriptor ::AllSlicesAreLoaded() const { bool allLoaded = true; for (BoolList::const_iterator iter = m_SliceIsLoaded.begin(); iter != m_SliceIsLoaded.end(); ++iter) { allLoaded &= *iter; } return allLoaded; } /* PS defined IPS defined PS==IPS 0 0 --> UNKNOWN spacing, loader will invent 0 1 --> spacing as at detector surface 1 0 --> spacing as in patient 1 1 0 --> detector surface spacing CORRECTED for geometrical magnifications: spacing as in patient 1 1 1 --> detector surface spacing NOT corrected for geometrical magnifications: spacing as at detector */ mitk::PixelSpacingInterpretation mitk::DICOMImageBlockDescriptor ::GetPixelSpacingInterpretation() const { if ( m_ImageFrameList.empty() || m_TagCache.IsNull() ) { MITK_ERROR << "Invalid call to GetPixelSpacingInterpretation. Need to have initialized tag-cache!"; return SpacingUnknown; } std::string pixelSpacing = this->GetPixelSpacing(); std::string imagerPixelSpacing = this->GetImagerPixelSpacing(); if (pixelSpacing.empty()) { if (imagerPixelSpacing.empty()) { return SpacingUnknown; } else { return SpacingAtDetector; } } else // Pixel Spacing defined { if (imagerPixelSpacing.empty()) { return SpacingInPatient; } else if (pixelSpacing != imagerPixelSpacing) { return SpacingInPatient; } else { return SpacingAtDetector; } } } std::string mitk::DICOMImageBlockDescriptor ::GetPixelSpacing() const { if ( m_ImageFrameList.empty() || m_TagCache.IsNull() ) { MITK_ERROR << "Invalid call to GetPixelSpacing. Need to have initialized tag-cache!"; return std::string(""); } static const DICOMTag tagPixelSpacing(0x0028,0x0030); return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagPixelSpacing ); } std::string mitk::DICOMImageBlockDescriptor ::GetImagerPixelSpacing() const { if ( m_ImageFrameList.empty() || m_TagCache.IsNull() ) { MITK_ERROR << "Invalid call to GetImagerPixelSpacing. Need to have initialized tag-cache!"; return std::string(""); } static const DICOMTag tagImagerPixelSpacing(0x0018,0x1164); return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagImagerPixelSpacing ); } void mitk::DICOMImageBlockDescriptor ::GetDesiredMITKImagePixelSpacing( ScalarType& spacingX, ScalarType& spacingY) const { std::string pixelSpacing = this->GetPixelSpacing(); // preference for "in patient" pixel spacing if ( !DICOMStringToSpacing( pixelSpacing, spacingX, spacingY ) ) { std::string imagerPixelSpacing = this->GetImagerPixelSpacing(); // fallback to "on detector" spacing if ( !DICOMStringToSpacing( imagerPixelSpacing, spacingX, spacingY ) ) { // last resort: invent something spacingX = spacingY = 1.0; } } } void mitk::DICOMImageBlockDescriptor ::SetProperty(const std::string& key, BaseProperty* value) { m_PropertyList->SetProperty(key, value); } mitk::BaseProperty* mitk::DICOMImageBlockDescriptor ::GetProperty(const std::string& key) const { this->UpdateImageDescribingProperties(); return m_PropertyList->GetProperty(key); } std::string mitk::DICOMImageBlockDescriptor ::GetPropertyAsString(const std::string& key) const { this->UpdateImageDescribingProperties(); mitk::BaseProperty::Pointer property = m_PropertyList->GetProperty(key); if (property.IsNotNull()) { return property->GetValueAsString(); } else { return std::string(""); } } void mitk::DICOMImageBlockDescriptor ::SetFlag(const std::string& key, bool value) { m_PropertyList->ReplaceProperty(key, BoolProperty::New(value)); } bool mitk::DICOMImageBlockDescriptor ::GetFlag(const std::string& key, bool defaultValue) const { this->UpdateImageDescribingProperties(); BoolProperty::ConstPointer boolProp = dynamic_cast( this->GetProperty(key) ); if (boolProp.IsNotNull()) { return boolProp->GetValue(); } else { return defaultValue; } } void mitk::DICOMImageBlockDescriptor ::SetIntProperty(const std::string& key, int value) { m_PropertyList->ReplaceProperty(key, IntProperty::New(value)); } int mitk::DICOMImageBlockDescriptor ::GetIntProperty(const std::string& key, int defaultValue) const { this->UpdateImageDescribingProperties(); IntProperty::ConstPointer intProp = dynamic_cast( this->GetProperty(key) ); if (intProp.IsNotNull()) { return intProp->GetValue(); } else { return defaultValue; } } double mitk::DICOMImageBlockDescriptor ::stringtodouble(const std::string& str) const { double d; std::string trimmedstring(str); trimmedstring = trimmedstring.erase(trimmedstring.find_last_not_of(" \n\r\t")+1); std::istringstream converter(trimmedstring); if ( !trimmedstring.empty() && (converter >> d) && converter.eof() ) { return d; } else { throw std::invalid_argument("Argument is not a convertable number"); } } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor ::DescribeImageWithProperties(Image* mitkImage) { // TODO: this is a collection of properties that have been provided by the // legacy DicomSeriesReader. // We should at some point clean up this collection and name them in a more // consistent way! if (!mitkImage) return mitkImage; // first part: add some tags that describe individual slices // these propeties are defined at analysis time (see UpdateImageDescribingProperties()) std::string propertyKeySliceLocation = "dicom.image.0020.1041"; std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; std::string propertyKeySOPInstanceUID = "dicom.image.0008.0018"; mitkImage->SetProperty( propertyKeySliceLocation.c_str(), this->GetProperty("sliceLocationForSlices") ); mitkImage->SetProperty( propertyKeyInstanceNumber.c_str(), this->GetProperty("instanceNumberForSlices") ); mitkImage->SetProperty( propertyKeySOPInstanceUID.c_str(), this->GetProperty("SOPInstanceUIDForSlices") ); // second part: add properties that describe the whole image block mitkImage->SetProperty("dicomseriesreader.SOPClassUID", StringProperty::New( this->GetSOPClassUID() ) ); mitkImage->SetProperty("dicomseriesreader.SOPClass", StringProperty::New( this->GetSOPClassUIDAsName() )); mitkImage->SetProperty("dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New( PixelSpacingInterpretationToString( this->GetPixelSpacingInterpretation() )) ); mitkImage->SetProperty("dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New( this->GetPixelSpacingInterpretation() )); mitkImage->SetProperty("dicomseriesreader.ReaderImplementationLevelString", StringProperty::New( ReaderImplementationLevelToString( m_ReaderImplementationLevel ) )); mitkImage->SetProperty("dicomseriesreader.ReaderImplementationLevel", GenericProperty::New( m_ReaderImplementationLevel )); mitkImage->SetProperty("dicomseriesreader.GantyTiltCorrected", BoolProperty::New( this->GetTiltInformation().IsRegularGantryTilt() )); mitkImage->SetProperty("dicomseriesreader.3D+t", BoolProperty::New( this->GetFlag("3D+t",false) )); // level window std::string windowCenter = this->GetPropertyAsString("windowCenter"); std::string windowWidth = this->GetPropertyAsString("windowWidth"); try { double level = stringtodouble( windowCenter ); double window = stringtodouble( windowWidth ); - mitkImage->SetProperty("levelwindow", LevelWindowProperty::New( LevelWindow(level,window) ) ); + mitkImage->SetProperty("levelwindow", LevelWindowProperty::New(LevelWindow(level,window)) ); } catch (...) { // nothing, no levelwindow to be predicted... } mitkImage->SetProperty("dicom.pixel.PhotometricInterpretation", this->GetProperty("photometricInterpretation") ); mitkImage->SetProperty("dicom.image.imagetype", this->GetProperty("imagetype") ); mitkImage->SetProperty("dicom.study.StudyDescription", this->GetProperty("studyDescription") ); mitkImage->SetProperty("dicom.series.SeriesDescription", this->GetProperty("seriesDescription") ); // third part: get something from ImageIO. BUT this needs to be created elsewhere. or not at all! return mitkImage; } void mitk::DICOMImageBlockDescriptor ::SetReaderImplementationLevel(const ReaderImplementationLevel& level) { m_ReaderImplementationLevel = level; } mitk::ReaderImplementationLevel mitk::DICOMImageBlockDescriptor ::GetReaderImplementationLevel() const { return m_ReaderImplementationLevel; } std::string mitk::DICOMImageBlockDescriptor ::GetSOPClassUID() const { if ( !m_ImageFrameList.empty() && m_TagCache.IsNotNull() ) { static const DICOMTag tagSOPClassUID(0x0008,0x0016); return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagSOPClassUID ); } else { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUID(). Need to have initialized tag-cache!"; return std::string(""); } } std::string mitk::DICOMImageBlockDescriptor ::GetSOPClassUIDAsName() const { if ( !m_ImageFrameList.empty() && m_TagCache.IsNotNull() ) { gdcm::UIDs uidKnowledge; uidKnowledge.SetFromUID( this->GetSOPClassUID().c_str() ); const char* name = uidKnowledge.GetName(); if (name) { return std::string(name); } else { return std::string(""); } } else { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUIDAsName(). Need to have initialized tag-cache!"; return std::string(""); } } void mitk::DICOMImageBlockDescriptor ::SetTagCache(DICOMTagCache* privateCache) { // this must only be used during loading and never afterwards m_TagCache = privateCache; } #define printPropertyRange(label, property_name) \ { \ std::string first = this->GetPropertyAsString( #property_name "First"); \ std::string last = this->GetPropertyAsString( #property_name "Last"); \ if (!first.empty() || !last.empty()) \ { \ if (first == last) \ { \ os << " " label ": '" << first << "'" << std::endl; \ } \ else \ { \ os << " " label ": '" << first << "' - '" << last << "'" << std::endl; \ } \ } \ } #define printProperty(label, property_name) \ { \ std::string first = this->GetPropertyAsString( #property_name ); \ if (!first.empty()) \ { \ os << " " label ": '" << first << "'" << std::endl; \ } \ } #define printBool(label, commands) \ { \ os << " " label ": '" << (commands?"yes":"no") << "'" << std::endl; \ } void mitk::DICOMImageBlockDescriptor ::Print(std::ostream& os, bool filenameDetails) const { os << " Number of Frames: '" << m_ImageFrameList.size() << "'" << std::endl; os << " SOP class: '" << this->GetSOPClassUIDAsName() << "'" << std::endl; printProperty("Series Number", seriesNumber); printProperty("Study Description", studyDescription); printProperty("Series Description", seriesDescription); printProperty("Modality", modality); printProperty("Sequence Name", sequenceName); printPropertyRange("Slice Location", sliceLocation); printPropertyRange("Acquisition Number", acquisitionNumber); printPropertyRange("Instance Number", instanceNumber); printPropertyRange("Image Position", imagePositionPatient); printProperty("Image Orientation", orientation); os << " Pixel spacing interpretation: '" << PixelSpacingInterpretationToString(this->GetPixelSpacingInterpretation()) << "'" << std::endl; printBool("Gantry Tilt", this->GetTiltInformation().IsRegularGantryTilt()) //printBool("3D+t", this->GetFlag("3D+t",false)) //os << " MITK image loaded: '" << (this->GetMitkImage().IsNotNull() ? "yes" : "no") << "'" << std::endl; if (filenameDetails) { os << " Files in this image block:" << std::endl; for (DICOMImageFrameList::const_iterator frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end(); ++frameIter) { os << " " << (*frameIter)->Filename; if ((*frameIter)->FrameNo > 0) { os << ", " << (*frameIter)->FrameNo; } os << std::endl; } } } #define storeTagValueToProperty(tag_name, tag_g, tag_e) \ { \ const DICOMTag t(tag_g, tag_e); \ std::string tagValue = m_TagCache->GetTagValue( firstFrame, t ); \ const_cast(this)->SetProperty(#tag_name, StringProperty::New( tagValue ) ); \ } #define storeTagValueRangeToProperty(tag_name, tag_g, tag_e) \ { \ const DICOMTag t(tag_g, tag_e); \ std::string tagValueFirst = m_TagCache->GetTagValue( firstFrame, t ); \ std::string tagValueLast = m_TagCache->GetTagValue( lastFrame, t ); \ const_cast(this)->SetProperty(#tag_name "First", StringProperty::New( tagValueFirst ) ); \ const_cast(this)->SetProperty(#tag_name "Last", StringProperty::New( tagValueLast ) ); \ } void mitk::DICOMImageBlockDescriptor ::UpdateImageDescribingProperties() const { if (!m_PropertiesOutOfDate) return; if (!m_ImageFrameList.empty()) { if (m_TagCache.IsNull()) { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::UpdateImageDescribingProperties(). Need to have initialized tag-cache!"; return; } DICOMImageFrameInfo::Pointer firstFrame = m_ImageFrameList.front(); DICOMImageFrameInfo::Pointer lastFrame = m_ImageFrameList.back(); // see macros above storeTagValueToProperty(seriesNumber,0x0020,0x0011) storeTagValueToProperty(studyDescription,0x0008,0x1030) storeTagValueToProperty(seriesDescription,0x0008,0x103e) storeTagValueToProperty(modality,0x0008,0x0060) storeTagValueToProperty(sequenceName,0x0018,0x0024) storeTagValueToProperty(orientation,0x0020,0x0037) storeTagValueRangeToProperty(sliceLocation,0x0020,0x1041) storeTagValueRangeToProperty(acquisitionNumber,0x0020,0x0012) storeTagValueRangeToProperty(instanceNumber,0x0020,0x0013) storeTagValueRangeToProperty(imagePositionPatient,0x0020,0x0032) storeTagValueToProperty(windowCenter,0x0028,0x1050) storeTagValueToProperty(windowWidth,0x0028,0x1051) storeTagValueToProperty(imageType,0x0008,0x0008) storeTagValueToProperty(photometricInterpretation,0x0028,0x0004) // some per-image attributes // frames are just numbered starting from 0. timestep 1 (the second time-step) has frames starting at (number-of-frames-per-timestep) std::string propertyKeySliceLocation = "dicom.image.0020.1041"; std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceUIDForSlices; const DICOMTag tagSliceLocation(0x0020,0x1041); const DICOMTag tagInstanceNumber(0x0020,0x0013); const DICOMTag tagSOPInstanceNumber(0x0008,0x0018); unsigned int slice(0); for (DICOMImageFrameList::const_iterator frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end(); ++slice, ++frameIter) { std::string sliceLocation = m_TagCache->GetTagValue( *frameIter, tagSliceLocation ); sliceLocationForSlices.SetTableValue(slice, sliceLocation); std::string instanceNumber = m_TagCache->GetTagValue( *frameIter, tagInstanceNumber ); instanceNumberForSlices.SetTableValue(slice, instanceNumber); std::string sopInstanceUID = m_TagCache->GetTagValue( *frameIter, tagSOPInstanceNumber ); SOPInstanceUIDForSlices.SetTableValue(slice, sopInstanceUID); MITK_DEBUG << "Tag info for slice " << slice << ": SL '" << sliceLocation << "' IN '" << instanceNumber << "' SOP instance UID '" << sopInstanceUID << "'"; // add property or properties with proper names const_cast(this)->SetProperty( "sliceLocationForSlices", StringLookupTableProperty::New( sliceLocationForSlices ) ); const_cast(this)->SetProperty( "instanceNumberForSlices", StringLookupTableProperty::New( instanceNumberForSlices ) ); const_cast(this)->SetProperty( "SOPInstanceUIDForSlices", StringLookupTableProperty::New( SOPInstanceUIDForSlices ) ); } m_PropertiesOutOfDate = false; } }