4 DICOM imaging protocol coding implementation - Metadata group analysis

Keywords: Computer Vision image processing dicom

The following links are the shortcomings of this series of articles, which can be discussed in the comments area:
Series articles
  corresponding column of this article Explain DICOM protocol from zero - imaging protocol Articles in Analysis of DICOM imaging protocol and Implementation of DICOM imaging protocol , it is recommended to read the above two articles first to understand the underlying protocol of DICOM, which is helpful to understand the code implementation.

Last article DICOM imaging protocol coding implementation - file header analysis The implementation idea and overall code framework of DICOM parsing engine are explained and completed

  1. The code implementation of reading DICOM file into memory.
  2. Read file header

This article will continue to explain and implement the following code ideas:

  1. Read each DataElement in the metadata group according to the metadata group characteristics
  2. Read each DataElement in the data group according to the data group properties
  3. If PixelData is in compressed format, decompress it with the corresponding decompression algorithm

Dependency:

class DcmRead : public FileRead
{
public:
	DcmRead(bool iscompress = false);
	DcmRead(string path, bool iscompress = false);
	DcmRead(char *buffer, int len, bool iscompress = false);
	~DcmRead();
public:
	virtual bool ReadFile(string path) override;
	vector<ElementData> ParseFile(TagName tagname);
	ElementFormat GetTransferSyntax();
	//Get elements other than pixeldata
	ElementValue GetElement(TagName tagname);
	ElementData GetPixelData();
	DcmToBmpTag GetBMPInfo();
	void SaveBMP(string path);
	virtual void Clear() override;
	bool IsCompress();
public:
	
	DcmTags * GetDcmTags();
	DcmFile *GetDcmFile();
	void SetClassify(DcmFilePeriod *dcmclassify);
private:

	//First 128 zeros and 4 "dicms" of dicom file
	virtual void GetHead() override;
	//dicom file 0x0002 group, metadata group
	virtual void GetMetadata() override;
	//dicom file information group
	virtual void GetInfodata(void *dcmelementset) override;
	//dicom all elements
	//virtual void GetElements(void *dcmelementset) override;

private:
	
	//Compressed image data
	ElementData UnCompressPixel(vector<ElementData> pixelset);
	vector<ElementData> GetUnCompressPixel();
	void TransferUnCompress();
private:
	
	void MemCopy(int len, int &datalen, char **data);
	void TagClassify(int startindex,vector<DcmElement> *dcmelementset);
	//Judge whether it is metadata group 0x0002 (explicit small end)
	bool IsMetaData(char *data);
	//Determine whether it is SQ
	bool IsSQ(char * data);
	int GetDataLen(char *data);
	//Whether to obtain SQ sequence completely
	bool IsSQEnd();
	//Whether the function operates recursively
	bool IsEmbed();
	void SetVrDataLen(char *data);
	void DeletePartPointer(DcmElement dcmelement);
	void DeletePointer(DcmElement dcmelement);
	void Init(bool iscompress);
private:
	void SetBinding();
	bool(DcmRead::*CompareModel)(char *source, char *des);
	int(DcmRead::*ElementIntValue)(char *buffer, int len);

	bool BigCompareModel(char *source, char *des);
	bool LittleCompareModel(char *source, char *des);
	int BigElementLen(char *buffer, int len);
	int LittleElementLen(char *buffer, int len);
private:
	void GetDcmClassifyInfo();
	void FileClassify();
private:
	bool status;
	bool isbigmodel;
	bool isin;
	bool iscompress;
	DcmFile dcmFile;
	int offset;
	//SQ sequence nesting levels
	int embednum;
	int vrlen;
	int datalen;
	ElementFormat ef;
	CompressFormat cf;
	ElementValueType et;
	vector<DcmElement> sqelementset;
	//Pipe *jpeg2k;
	SocketClient *sc;

private:
	DcmTags *dcmtags;
	DcmFilePeriod *dcmclassify;
};

  as mentioned in the previous article, the characteristics of metadata group are:
  metadata group is a small end explicit format
  metadata group and data group are composed of DataElement structure in DICOM protocol
   DataElement consists of Tag, VR, Length and Value
   Tag consists of group and element. VR can be divided into explicit and implicit. The byte of Length is determined by explicit and implicit and VR types, and the Length of Value is even
The most important element in the metadata group is the transfer syntax (00020010) transfer syntax uid
According to the above characteristics, the GetMetadata() function is implemented from the DcmRead class

void DcmRead::GetMetadata()
{
	//Metadata is an explicit small end
	ef = ElementFormat::ExplicitLittle;
	int startindex = sizeof(dcmFile.Head) + sizeof(dcmFile.DcmFlag);

	TagClassify(startindex, &dcmFile.DcmElementSet);
	GetTransferSyntax();
}

   startindex is the offset address of the buffer memory pointer from the metadata group
  the main contents of the TagClassify function are:
In the   for loop, judge whether it is metadata group 0002, and execute the loop

memcpy(dcmelement.tag, buffer + offset, sizeof(dcmelement.tag));
SetVrDataLen(dcmelement.tag);
MemCopy(vrlen, dcmelement.vr.len, &dcmelement.vr.data);
MemCopy(datalen, dcmelement.datalen.len, &dcmelement.datalen.data);
int len = (this->*ElementIntValue)(dcmelement.datalen.data, dcmelement.datalen.len);
MemCopy(len, dcmelement.data.len, &dcmelement.data.data);

   explicitly, the VR length is judged according to 27 types

class ElementBytes
{
public:
	static const int ImplicitLenBytes = 4;//Implicit VR, VR length is 0 bytes, len length is 4 bytes
	static const int ExplicitLenComBytes = 2;//For explicit VR, the length of common type VR is 2 bytes, and the length of len is 2 bytes
	static const int ExplicitVrComBytes = 2;//The VR length of explicit VR, ob, ow, of, SQ, UT and UN is 4 bytes, and the len length is 4 bytes

	static const int ExplicitLenOtherBytes = 4;//The VR length of explicit VR, ob, ow, of, SQ, UT and UN is 4 bytes, and the len length is 4 bytes
	static const int ExplicitVrOtherBytes = 4;//The VR length of explicit VR, ob, ow, of, SQ, UT and UN is 4 bytes, and the len length is 4 bytes


};
void DcmRead::SetVrDataLen(char *data)
{
	if (!IsSQ(data))
	{
		switch (ef)
		{
		case ElementFormat::ImplicitLittle:

			vrlen = 0;
			datalen = ElementBytes::ImplicitLenBytes;
			break;
		case ElementFormat::ExplicitLittle:
		case ElementFormat::ExplicitBig:
		case ElementFormat::CompressPixel:

			char vrtype[2];
			memcpy(vrtype, buffer + offset, sizeof(vrtype));
			for (int j = 0; j < TagType::VrType.size(); j++)
			{
				if (memcmp(vrtype, TagType::VrType[j], sizeof(vrtype)) == 0)
				{
					vrlen = ElementBytes::ExplicitVrOtherBytes;
					datalen = ElementBytes::ExplicitLenOtherBytes;

					break;
				}
				if (j == TagType::VrType.size() - 1)
				{
					vrlen = ElementBytes::ExplicitVrComBytes;
					datalen = ElementBytes::ExplicitLenComBytes;
				}
			}

			break;

		default:
			break;
		}
	}
}

   after obtaining the Length of VR and Length, obtain the corresponding data from the buffer memory pointer and accumulate the offset address

void DcmRead::MemCopy(int len, int &datalen, char **data)
{
	datalen = len;
	*data = new char[datalen];
	memcpy(*data, buffer + offset, datalen);
	offset += datalen;
}

  small end, ElementIntValue binding small end conversion function

void DcmRead::SetBinding()
{
	if (ef == ElementFormat::ExplicitLittle || ef == ElementFormat::ImplicitLittle || ef == ElementFormat::CompressPixel)
	{
		CompareModel = &DcmRead::LittleCompareModel;
		ElementIntValue = &DcmRead::LittleElementLen;
	}
	else if (ef == ElementFormat::ExplicitBig)
	{
		CompareModel = &DcmRead::BigCompareModel;
		ElementIntValue = &DcmRead::BigElementLen;
	}
}
int DcmRead::LittleElementLen(char *buffer, int len)
{
	int data = 0;
	int flag = 0x000000FF;
	for (int i = 0; i < len; i++)
	{
		data |= ((buffer[i] << i * 8) & (flag << i * 8));
	}

	return data;
}

   so far, the metadata group parsing framework has been basically completed. In order to highlight the key points and the overall processing flow, some functions do not list the complete contents.
  the next article will explain the DICOM data set analysis framework.

Posted by ant peacocke on Wed, 17 Nov 2021 15:45:54 -0800