Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions Common/ContrastXML.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*=========================================================================

Program: ITK-SNAP
Language: C++

This file was created as part of the added functionality of the custom windowing.
We have added the option to set a custom contrast window preset using Contrast Menu.

This file is mostly based on Common\Registry.cxx

=========================================================================*/
#include "ContrastXML.h"
#include "itkXMLFile.h"

#include "itksys/SystemTools.hxx"
#include "IRISException.h"

#if defined(_MSC_VER)
#pragma warning ( disable : 4786 )
#endif
#include <Registry.cxx>

using namespace std;


int ContrastXMLFileReader::CanReadFile(const char* name)
{
return GenericXMLFileReader::CanReadFile(name);
}

std::vector<std::shared_ptr<PresetStruct>> ContrastXMLFileReader::getPresetStack() const
{
return m_PresetStack;
}

void ContrastXMLFileReader::StartElement(const char* name, const char** atts)
{
// If this is the root-level element <contrast>, it can be ignored
if (strcmpi(name, "contrast") == 0)
{
return;
}

// Read the attributes into a map
typedef std::map<std::string, std::string> AttributeMap;
typedef AttributeMap::const_iterator AttrIter;
AttributeMap attrmap;

for (int i = 0; atts[i] != nullptr && atts[i + 1] != nullptr; i += 2)
attrmap[itksys::SystemTools::LowerCase(atts[i])] = atts[i + 1];

// Process tags
if (strcmpi(name, "preset") == 0)
{
const AttrIter itKey = attrmap.find("key");
if (itKey == attrmap.end())
throw IRISException("Missing 'key' attribute to <preset> element");

const auto preset_name = itKey->second;

// Create a new preset and place it on the stack
m_PresetStack.push_back(std::make_shared<PresetStruct>(PresetStruct{preset_name}));
}
else if (strcmpi(name, "contrastProperty") == 0)
{
const AttrIter itKey = attrmap.find("key");
if (itKey == attrmap.end())
throw IRISException("Missing 'key' attribute to <contrastProperty> element");

const AttrIter itValue = attrmap.find("value");
if (itValue == attrmap.end())
throw IRISException("Missing 'value' attribute to <contrastProperty> element");

const auto property_name = itKey->second;
const auto property_value = itValue->second;

if(property_name == "Window")
{
m_PresetStack.back()->window = stod(property_value);
}
if(property_name == "Level")
{
m_PresetStack.back()->level = stod(property_value);
}
}
else
throw IRISException("Unknown XML element <%s>", name);
}

void ContrastXMLFileReader::EndElement(const char* name)
{
}

void ContrastXMLFileReader::CharacterDataHandler(const char* inData, int inLength)
{
}


void
Contrast
::Clear()
{
m_PresetMap.clear();
}

std::vector<std::string>
Contrast::GetPresetNames()
{
// get all keys from m_PresetMap and sort if needed
std::vector<std::string> presets;
for (const auto& map_entry : m_PresetMap)
{
presets.push_back(map_entry.first); // have to get all keys
}

return presets;
}

double Contrast::GetLevel(const std::string& tissue)
{
return m_PresetMap.at(tissue).level;
}

double Contrast::GetWindow(const std::string& tissue)
{
return m_PresetMap.at(tissue).window;
}


Contrast
::Contrast()
= default;

Contrast::
~Contrast()
= default;

void Contrast::ReadFromCustomContrastXMLFile(const char* pathname, SmartPtr<ContrastXMLFileReader>& reader)
{
reader->SetOutputObject(this);
reader->SetFilename(pathname);

reader->GenerateOutputInformation();

// check if presets are populated
for (const auto& preset : reader->getPresetStack())
{
m_PresetMap.insert(std::pair<std::string, PresetStruct>(preset->preset_name, *preset));
}
}
101 changes: 101 additions & 0 deletions Common/ContrastXML.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*=========================================================================

Program: ITK-SNAP
Language: C++

This file was created as part of the added functionality of the custom windowing.
We have added the option to set a custom contrast window preset using Contrast Menu.

This file is mostly based on Common\Registry.h

=========================================================================*/
#ifndef __Contrast_h_
#define __Contrast_h_

#ifdef _MSC_VER
# pragma warning(disable:4786) // '255' characters in the debug information
#endif //_MSC_VER

#include <list>
#include <map>
#include <string>
#include <cstdarg>
#include <iomanip>

#include <itksys/SystemTools.hxx>
#include "itkXMLFile.h"

#include "IRISException.h"

struct PresetStruct
{
std::string preset_name;
double level;
double window;
};

class Contrast;

/** Reader for XML files */
class ContrastXMLFileReader : public itk::XMLReader<Contrast>
{
public:

irisITKObjectMacro(ContrastXMLFileReader, itk::XMLReader<Contrast>)
int CanReadFile(const char* name) ITK_OVERRIDE;

std::vector<std::shared_ptr<PresetStruct>> getPresetStack() const;

protected:

ContrastXMLFileReader()
= default;

void StartElement(const char* name, const char** atts) ITK_OVERRIDE;
void EndElement(const char* name) ITK_OVERRIDE;
void CharacterDataHandler(const char* inData, int inLength) ITK_OVERRIDE;

static int strcmpi(const char* str1, const char* str2)
{
return itksys::SystemTools::Strucmp(str1, str2);
}

// The preset stack
std::vector<std::shared_ptr<PresetStruct>> m_PresetStack;
};

/**
* \class Contrast
* \brief A tree of key-value pair maps
*/
class Contrast final
{
public:
/** Constructor initializes an empty contrast */
Contrast();

/** Destructor */
virtual ~Contrast();

/** Get a list of preset names*/
std::vector<std::string> GetPresetNames();

/** Get a level value*/
double GetLevel(const std::string& tissue);

/** Get a window value*/
double GetWindow(const std::string& tissue);

/** Empty the contents of the contrast */
void Clear();

/** Read from XML file */
void ReadFromCustomContrastXMLFile(const char* pathname, SmartPtr<ContrastXMLFileReader>& reader);

private:

/** A hash table for the presets */
std::map<std::string, PresetStruct> m_PresetMap;
};

#endif
23 changes: 15 additions & 8 deletions Common/Registry.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@

using namespace std;

namespace GenericXMLFileReader
{
static int CanReadFile(const char *name)
{
if ( !itksys::SystemTools::FileExists(name)
|| itksys::SystemTools::FileIsDirectory(name)
|| itksys::SystemTools::FileLength(name) == 0 )
{
return 0;
}

return 1;
}
}


/** Reader for XML files */
Expand Down Expand Up @@ -79,14 +93,7 @@ class RegistryXMLFileReader : public itk::XMLReader<Registry>

int RegistryXMLFileReader::CanReadFile(const char *name)
{
if ( !itksys::SystemTools::FileExists(name)
|| itksys::SystemTools::FileIsDirectory(name)
|| itksys::SystemTools::FileLength(name) == 0 )
{
return 0;
}

return 1;
return GenericXMLFileReader::CanReadFile(name);
}

void RegistryXMLFileReader::StartElement(const char *name, const char **atts)
Expand Down
38 changes: 38 additions & 0 deletions ContrastSettings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE contrast [
<!ELEMENT contrast (contrastProperty*,preset*)>
<!ELEMENT preset (contrastProperty*,preset*)>
<!ELEMENT contrastProperty EMPTY>
<!ATTLIST preset key CDATA #REQUIRED>
<!ATTLIST contrastProperty key CDATA #REQUIRED>
<!ATTLIST contrastProperty value CDATA #REQUIRED>
]>
<!--
The contrast enhancement values are specifically prepared for CT analysis.
They are directly taken from: https://radiopaedia.org/articles/windowing-ct.

To add a new preset, complete the file with the structure as in the examples below.
At "preset" change "key" to the name of the preset to be added, and at "contrastProperty" set custom "value".
-->
<contrast>
<preset key="Abdomen-SoftTissues" >
<contrastProperty key="Window" value="400" />
<contrastProperty key="Level" value="50" />
</preset>
<preset key="Brain" >
<contrastProperty key="Window" value="80" />
<contrastProperty key="Level" value="40" />
</preset>
<preset key="Lungs" >
<contrastProperty key="Window" value="1500" />
<contrastProperty key="Level" value="-600" />
</preset>
<preset key="Mediastinum" >
<contrastProperty key="Window" value="350" />
<contrastProperty key="Level" value="50" />
</preset>
<preset key="Spine-Bone" >
<contrastProperty key="Window" value="1800" />
<contrastProperty key="Level" value="400" />
</preset>
</contrast>
7 changes: 7 additions & 0 deletions GUI/Model/GlobalUIModel.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,12 @@ void GlobalUIModel::ResetContrastAllLayers()
}
}

void GlobalUIModel::AdjustContrastAllLayers(double level, double window)
{
IntensityCurveModel *curve_model = GetIntensityCurveModel();
curve_model->OnAdjustCustomContrast(level, window);
}

void GlobalUIModel::ToggleOverlayVisibility()
{
// Are we in tiled mode or in stack mode?
Expand Down Expand Up @@ -510,6 +516,7 @@ void GlobalUIModel::LoadUserPreferences()
m_AppearanceSettings->LoadFromRegistry(
si->Folder("UserInterface.Appearance"));


// Read the default behaviors
dbs->ReadFromRegistry(
si->Folder("UserInterface.DefaultBehavior"));
Expand Down
5 changes: 4 additions & 1 deletion GUI/Model/GlobalUIModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,9 +326,12 @@ class GlobalUIModel : public AbstractModel
/** Auto-adjust contrast in all image layers */
void AutoContrastAllLayers();

/** Auto-adjust contrast in all image layers */
/** Reset contrast in all image layers */
void ResetContrastAllLayers();

/** Adjust custom contrast in all image layers */
void AdjustContrastAllLayers(double level, double window);

/** A function for switching between segmentation layers when there multiple */
void CycleSelectedSegmentationLayer(int direction);

Expand Down
9 changes: 9 additions & 0 deletions GUI/Model/IntensityCurveModel.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,15 @@ void IntensityCurveModel::OnAutoFitWindow()
dmp->AutoFitContrast();
}

void IntensityCurveModel::OnAdjustCustomContrast(double custom_level, double custom_window)
{
int index_level = 2;
int index_window = 3;

SetIntensityRangeIndexedValue(index_level, custom_level);
SetIntensityRangeIndexedValue(index_window, custom_window);
}

bool
IntensityCurveModel
::GetHistogramBinSizeValueAndRange(
Expand Down
1 change: 1 addition & 0 deletions GUI/Model/IntensityCurveModel.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ class IntensityCurveModel : public IntensityCurveModelBase
IntensityRangePropertyType index) const;

void OnAutoFitWindow();
void OnAdjustCustomContrast(double custom_level, double custom_window);
protected:

IntensityCurveModel();
Expand Down
1 change: 1 addition & 0 deletions GUI/Qt/Windows/MainControlPanel.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ MainControlPanel::MainControlPanel(MainImageWindow *parent) :
void MainControlPanel::SetModel(GlobalUIModel *model)
{
m_Model = model;

ui->pageCursorInspector->SetModel(m_Model->GetCursorInspectionModel());
ui->pageZoomInspector->SetModel(m_Model);
ui->pageDisplayInspector->SetModel(m_Model->GetDisplayLayoutModel());
Expand Down
Loading