DICOM入門(三)——解析DCM文件

talk is cheap show me the code —— Linus Torvalds

本章將結(jié)合開源項目IMBRA講解如何解析一個DCM文件

dcm文件結(jié)構(gòu).jpg

文件開頭會有128字節(jié)的導(dǎo)言,這部分數(shù)據(jù)沒有內(nèi)容。接著是4字節(jié)DICOM文件標識,存儲這"DICM"。然后緊接著就是dicom數(shù)據(jù)數(shù)據(jù)元素了

打開一個DCM文件.jpg
    try
    {
        pStream->read(oldDicomSignature, 8);
    }
    catch(StreamEOFError&)
    {
        IMEBRA_THROW(CodecWrongFormatError, "detected a wrong format");
    }

    // Skip the first 128 bytes (8 already skipped)
    ///////////////////////////////////////////////////////////
    pStream->seekForward(120);

    // Read the DICOM signature (DICM)
    ///////////////////////////////////////////////////////////
    std::uint8_t dicomSignature[4];
    pStream->read(dicomSignature, 4);

    // Check the DICM signature
    ///////////////////////////////////////////////////////////
    const char* checkSignature="DICM";
    if(::memcmp(dicomSignature, checkSignature, 4) != 0)
    {
        bFailed=true;
    }

舊版本的DCM文件會在開頭8個字節(jié)驗證簽名,在讀完128字節(jié)后,讀取4個字節(jié)驗證DICOM標識。

        pStream->read((std::uint8_t*)&tagId, sizeof(tagId));
        //將轉(zhuǎn)化成大端格式 或者小端格式
        pStream->adjustEndian((std::uint8_t*)&tagId, sizeof(tagId), endianType);
        (*pReadSubItemLength) += (std::uint32_t)sizeof(tagId);

        // Check for EOF
        ///////////////////////////////////////////////////////////
        if(pStream->endReached())
        {
            break;
        }

        // Check the byte order
        ///////////////////////////////////////////////////////////
        if(bFirstTag && tagId==0x0200)
        {
            // Reverse the last adjust
            pStream->adjustEndian((std::uint8_t*)&tagId, sizeof(tagId), endianType);

            // Fix the byte adjustment
            endianType=streamController::highByteEndian;

            // Redo the byte adjustment
            pStream->adjustEndian((std::uint8_t*)&tagId, sizeof(tagId), endianType);
        }

接下來開始讀取數(shù)據(jù)元素了,現(xiàn)讀取兩個字節(jié)的tagId,并且驗證大小端。

        if(tagId!=0x0002 && bCheckTransferSyntax)
        {
            // Reverse the last adjust
            pStream->adjustEndian((std::uint8_t*)&tagId, sizeof(tagId), endianType);

            std::string transferSyntax = pDataSet->getString(
                        0x0002,
                        0x0,
                        0x0010,
                        0,
                        0,
                        endianType == streamController::lowByteEndian ? "1.2.840.10008.1.2.1" : "1.2.840.10008.1.2.2");

            if(transferSyntax == "1.2.840.10008.1.2.2")
                endianType = streamController::highByteEndian;
            if(transferSyntax == "1.2.840.10008.1.2")
                bExplicitDataType=false;

            bCheckTransferSyntax=false;

            // Redo the byte adjustment
            pStream->adjustEndian((std::uint8_t*)&tagId, sizeof(tagId), endianType);
        }

dicom的數(shù)據(jù)元素的tagId是從大到小的讀取的,而最小的tagId就是從0x0002,當(dāng)tagId不是0x0002說明已經(jīng)讀取完所以關(guān)于0x0002的tagId。從中找出(0002,0010)的tag,這個tag設(shè)置當(dāng)前數(shù)據(jù)是否大端格式,顯示VR編碼還是隱式VR編碼。"1.2.840.10008.1.2.2"表示大端格式,1.2.840.10008.1.2表示隱式編碼。

隱式編碼.jpeg
隱式編碼實例.jpeg
顯式編碼(用于 OB、OW、OF、SQ、UT、UN).jpeg
顯式編碼實例.jpeg
顯式編碼(用于 OB、OW、OF、SQ、UT、UN).jpeg
顯式編碼實例.jpeg
        if(bExplicitDataType && tagId!=0xfffe)
        {
            // Get the tag's type
            ///////////////////////////////////////////////////////////
            std::string tagTypeString((size_t)2, ' ');

            pStream->read((std::uint8_t*)&(tagTypeString[0]), 2);
            (*pReadSubItemLength) += 2;

            // Get the tag's length
            ///////////////////////////////////////////////////////////
            pStream->read((std::uint8_t*)&tagLengthWord, sizeof(tagLengthWord));
            pStream->adjustEndian((std::uint8_t*)&tagLengthWord, sizeof(tagLengthWord), endianType);
            (*pReadSubItemLength) += (std::uint32_t)sizeof(tagLengthWord);

            // The data type is valid
            ///////////////////////////////////////////////////////////
            try
            {
                tagType = dicomDictionary::getDicomDictionary()->stringDataTypeToEnum(tagTypeString);
                tagLengthDWord=(std::uint32_t)tagLengthWord;
                wordSize = dicomDictionary::getDicomDictionary()->getWordSize(tagType);
                if(dicomDictionary::getDicomDictionary()->getLongLength(tagType))
                {
                    pStream->read((std::uint8_t*)&tagLengthDWord, sizeof(tagLengthDWord));
                    pStream->adjustEndian((std::uint8_t*)&tagLengthDWord, sizeof(tagLengthDWord), endianType);
                    (*pReadSubItemLength) += (std::uint32_t)sizeof(tagLengthDWord);
                }
            }
            catch(const DictionaryUnknownDataTypeError&)
            {
                // The data type is not valid. Switch to implicit data type
                ///////////////////////////////////////////////////////////
                bExplicitDataType = false;
                if(endianType == streamController::lowByteEndian)
                    tagLengthDWord=(((std::uint32_t)tagLengthWord)<<16) | ((std::uint32_t)tagTypeString[0]) | (((std::uint32_t)tagTypeString[1])<<8);
                else
                    tagLengthDWord=(std::uint32_t)tagLengthWord | (((std::uint32_t)tagTypeString[0])<<24) | (((std::uint32_t)tagTypeString[1])<<16);
            }


        } // End of the explicit data type read block

顯示編碼解析 取先獲取VR 再獲取長度 最后獲取值

if((!bExplicitDataType || tagId==0xfffe))
        {
            // Group length. Data type is always UL
            ///////////////////////////////////////////////////////////
            if(tagSubId == 0)
            {
                tagType = tagVR_t::UL;
            }
            else
            {
                try
                {
                    tagType = dicomDictionary::getDicomDictionary()->getTagType(tagId, tagSubId);
                }
                catch(const DictionaryUnknownTagError&)
                {
                    tagType = tagVR_t::UN;
                }
                wordSize = dicomDictionary::getDicomDictionary()->getWordSize(tagType);
            }
        }

隱式編碼解析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容