DcmMetaInfo value retrieval

All other questions regarding DCMTK

Moderator: Moderator Team

Post Reply
Message
Author
l7aur
Posts: 2
Joined: Wed, 2025-01-08, 14:02
Location: Cluj-Napoca, Romania

DcmMetaInfo value retrieval

#1 Post by l7aur »

Hello :),
I am new to DICOM and DCMTK and I am looking to get a grasp on them. I am currently trying to implement a simple DICOM file visualizer that should work like this: I pass a path to the program and it prints in the terminal the contents of the file (similarly to what DCMTK's print method offers). The issue I am encountering is related to the value field.

I had great success in mocking the DcmDataset's print method: I managed to read all tags, their VR, VM, length and value and placed them in a custom multi-way tree (I am not sure if it is unnecessary), even if they were part of nested sequences. However, when I use the same approach for the DcmMetaInfo the methods that should retrieve the values (findAndGet...) fail.

What do I do wrong? Any suggestions regarding the logic of the program or its implementation are welcomed!
Thank you!

Here is what my program prints in the console:
findAndGetOFStringAray() failed for 'SourceApplicationEntityTitle'
findAndGetOFStringAray() failed for 'ImplementationVersionName'
findAndGetOFStringAray() failed for 'ImplementationClassUID'
findAndGetOFStringAray() failed for 'TransferSyntaxUID'
findAndGetOFStringAray() failed for 'MediaStorageSOPInstanceUID'
findAndGetOFStringAray() failed for 'MediaStorageSOPClassUID'
findAndGetOFStringAray() failed for 'FileMetaInformationGroupLength'
(????,????) ?? 0 0 not assigned root
>(0002,0000) UL 1 4 FileMetaInformationGroupLength init::value_not_retrieved
>(0002,0002) UI 1 26 MediaStorageSOPClassUID init::value_not_retrieved
>(0002,0003) UI 1 64 MediaStorageSOPInstanceUID init::value_not_retrieved
>(0002,0010) UI 1 18 TransferSyntaxUID init::value_not_retrieved
>(0002,0012) UI 1 28 ImplementationClassUID init::value_not_retrieved
>(0002,0013) SH 1 16 ImplementationVersionName init::value_not_retrieved
>(0002,0016) AE 1 12 SourceApplicationEntityTitle init::value_not_retrieved
Here is what the DcmMetaInfo's print method outputs:
# Dicom-Meta-Information-Header
# Used TransferSyntax: Little Endian Explicit
(0002,0000) UL 212 # 4, 1 FileMetaInformationGroupLength
(0002,0002) UI =SecondaryCaptureImageStorage # 26, 1 MediaStorageSOPClassUID
(0002,0003) UI [1.2.826.0.1.3680043.8.1055.1.20111103112244831.30826609.78057758] # 64, 1 MediaStorageSOPInstanceUID
(0002,0010) UI =LittleEndianImplicit # 18, 1 TransferSyntaxUID
(0002,0012) UI [1.2.826.0.1.3680043.8.1055.1] # 28, 1 ImplementationClassUID
(0002,0013) SH [dicomlibrary-100] # 16, 1 ImplementationVersionName
(0002,0016) AE [DICOMLIBRARY] # 12, 1 SourceApplicationEntityTitle
This is the main section from main.cpp:

Code: Select all

Reader reader("tests/online_test.dcm");
	if (reader.fopen() < 0) {
		std::cerr << "File not loaded!\n";
		return -1;
	}
	Tree* mi = reader.loadMetainfo();
	reader.retrieveValues(mi);
	mi->preOrderTraversalPrint();
Here are the methods from the Reader class (Note: Tree is actually a MultiWay Tree):

Code: Select all

Tree* Reader::load(DcmItem* container)
{
	Tree* t = new Tree();
	TreeNode* prev = t->getRoot();
	std::stack<TreeNode*> nodeStack;

	//[NOTE] the default level in traversal is 2
	int currentLevel = 2;
	nodeStack.push(t->getRoot());
	DcmStack s;
	while (container->nextObject(s, OFTrue).good()) {
		DcmObject* current = s.top();
		int level = s.card();
		
		//[NOTE] create node and insert in tree
		DcmTag tag = current->getTag();
		DcmVR vr = current->getVR();
		unsigned long vm = current->getVM();
		Uint32 length = current->getLength();
		OFString description = tag.getTagName();
		OFString value = "init::value_not_retrieved";
		TreeNode* node = new TreeNode(tag, vr, vm, length, description, value);

		//[NOTE] update node stack and level if required
		if (level == currentLevel) {
			t->insert(nodeStack.top(), node);
		}
		else if (level > currentLevel) {
			t->insert(prev, node);
			currentLevel++;
			nodeStack.push(prev);
		}
		else {
			while (nodeStack.size() > 1 && currentLevel > level) {
				nodeStack.pop();
				currentLevel--;
			}
			t->insert(nodeStack.top(), node);
		}
		prev = node;
	}
	return t;
}

void Reader::retrieveValues(Tree* tree)
{
	std::stack<TreeNode*> nodes{};
	for (auto& i : tree->getRoot()->children)
		nodes.push(i);
	while (!nodes.empty()) {
		TreeNode* currentNode = nodes.top();
		nodes.pop();
		retrieveValue(tree, currentNode);
		for (auto& i : currentNode->children)
			nodes.push(i);
	}
}

void Reader::retrieveValue(Tree* tree, TreeNode* node)
{
	DcmItem* item = findContainerOfNode(tree, node);
	if (item == nullptr) {
		std::cerr << "The container of the node with description \'" << node->description << "\' was not found!\n";
		return;
	}

	switch (node->vr.getEVR())
	{
	case EVR_AE:case EVR_AS:case EVR_AT:case EVR_CS:case EVR_DA:case EVR_DS:
	case EVR_DT:case EVR_FL:case EVR_FD:case EVR_IS:case EVR_LO:case EVR_LT:
	case EVR_OB:case EVR_OD:case EVR_OF:case EVR_OL:case EVR_OW:case EVR_PN:
	case EVR_SH:case EVR_SL:case EVR_SS:case EVR_ST:case EVR_TM:case EVR_UC:
	case EVR_UI:case EVR_UR:case EVR_US:case EVR_UT:case EVR_UN:case EVR_UL:
	case EVR_ox:case EVR_xs: {
		OFString value{};
		OFCondition status = item->findAndGetOFStringArray(node->tag, value);
		if (status.good())
			node->value = value;
		else
			std::cerr << "findAndGetOFStringAray() failed for \'" << node->description << "\'\n";
		return;
	}
	case EVR_SQ:case EVR_na:
		node->value = "";
		return;
	case EVR_lt:case EVR_up:case EVR_item:case EVR_metainfo:
	case EVR_dataset:case EVR_fileFormat:case EVR_dicomDir:case EVR_dirRecord:
	case EVR_pixelSQ:case EVR_pixelItem:case EVR_UNKNOWN:case EVR_OverlayData:
	case EVR_UNKNOWN2B:
		node->value = "!!not handled!!";
		break;
	default:
		break;
	}
}
Last edited by l7aur on Thu, 2025-01-09, 07:38, edited 2 times in total.
Laurentiu Grad

Marco Eichelberg
OFFIS DICOM Team
OFFIS DICOM Team
Posts: 1506
Joined: Tue, 2004-11-02, 17:22
Location: Oldenburg, Germany
Contact:

Re: DcmMetaInfo value retrieval

#2 Post by Marco Eichelberg »

I cannot say that I really understand what your code is doing. I suspect, though, that your function findContainerOfNode() returns a pointer to the wrong DcmItem instance.
Note that a DICOM file (class DcmFileFormat) contains two different DcmItem instances, one for the meta-header (i.e. all attributes starting with 0002), and one for all others.
You need to call DcmFileFormat::getMetaInfo() to access the meta-header, and DcmFileFormat::getDataset() to access the main dataset.

l7aur
Posts: 2
Joined: Wed, 2025-01-08, 14:02
Location: Cluj-Napoca, Romania

Re: DcmMetaInfo value retrieval

#3 Post by l7aur »

Thank you for your answer. You were right, it seems that I did not take into consideration that the method Reader::findContainerOfNode() will be used with both the DcmDataset and the DcmMetaInfo when I had designed it. I included a code snippet containing the body of the method and I highlighted the updates. Some minor adjustments had to be made in other methods as well. Thank you for your help!

Code: Select all

// the new signature of the method: DcmItem* Reader::findContainerOfNode(Tree* tree, TreeNode* node, DcmItem* initialContainer)
DcmItem* Reader::findContainerOfNode(Tree* tree, TreeNode* node) { 
	std::vector<TreeNode*> path = tree->findPathToRootFrom(node);
	if (path.empty()) {
		std::cerr << "No path found from root to \'" << node->description << '\'\n';
		return nullptr;
	}

	//[NOTE] remove virtual node root
	path.pop_back();

	//[NOTE] the node is a direct child of the root, the container is the dataset itself
	if (path.size() <= 1)
				// Wrong return, replaced by: return initialContainer;
				return dataset;
		
				// Wrong initialization, replaced by: DcmItem* item = initialContainer
				DcmItem* item = dataset;

	//[ASSUMPTION] sequence nodes are paired with item nodes
	//[UNTESTED] sequences that contain sequence delimiters (unspecified length)
	auto i = path.rbegin();
	TreeNode* sqNode = *(i++);
	TreeNode* itNode = *(i++);

	//[NOTE] findIndexOfChild() must return valid index because the node was stored in the path to the root
				// Wrong call, replaced by: OFCondition status = initialContainer->findAndGetSequenceItem(sqNode->tag, item, sqNode->findIndexOfChild(itNode));
				OFCondition status = dataset->findAndGetSequenceItem(sqNode->tag, item, sqNode->findIndexOfChild(itNode));
	while (i != path.rend() && i + 1 != path.rend()) {
		sqNode = *(i++);
		itNode = *(i++);
		status = item->findAndGetSequenceItem(sqNode->tag, item, sqNode->findIndexOfChild(itNode));
		if (status.bad()) {
			std::cerr << "The sequence parsing process failed for \'" << node->description << "\'\n";
			return nullptr;
		}
	}
	return item;
}
Some details about the code and the thought process: I created a Reader class that handles user's interaction with a DICOM file, i. e. opening it with DcmFileFormat::loadFile(), and storing pointers to its dataset & metainfo. Because both the DcmMetainfo and the DcmDataset have a tree-like structure, I designed a core Read::load() method whose body is supplied in the first post - it creates a multiway tree based on the last iteration algorithm presented here: https://support.dcmtk.org/redmine/proje ... ateDataset. As far as I have tested, it works correctly. Reader::loadMetainfo() is just a wrapper that takes the pointer to the file's metainfo which was set in Reader::fopen()

Code: Select all

Tree* Reader::loadMetainfo() {
	return load(metainfo);
}
Laurentiu Grad

Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot], Bing [Bot], Google [Bot] and 1 guest