DICOM: Comparison and Analysis of three open-source DICOM databases: "data loading" and dicom comparison and analysis
Background:
In the previous blog, DICOM: Sante DICOM Editor of DICOM universal editing tool introduces DICOM universal editing tool,"As long as the Sante DICOM Editor cannot open the data, the DICOM file format can basically be determined to be incorrect (the accuracy rate is 99.9999% ^_^ )". While sighing the powerful Sante DICOM Editor, I want to know how it is implemented at the underlying layer. Through daily use and reading software help manual, we can infer that the underlying dependent library is dcmtk, just like the compatibility problems encountered by many dicom open source libraries such as dcmtk, fo-DICOM, and dcm4che3,-- Dcmtk is the most compatible, followed by fo-dicom, and dcm4che3 is the worst..
Problem:
This article compares dcmtk3.6 and dcm4che3. x to parse the sameSpecial dicom file(Elements that contain non-standard VR) AnalysisCompatibility of dcmtk, dcm4che, and fo-dicom data loadingProblem.
The content of a special dicom file is as follows:
28 00 20 01 20 20 02 00 30 F8, The specific description is as follows:
When using dcmtk and fo-dicom to load data, no error occurs. For example, when loading data using dcmtk, the following prompt is displayed:
We can see that dcmtk has been successfully identified.Elements of non-standard VR).
Although there is no error in loading data using fo-dicomElements of non-standard VR)The following elements are not loaded successfully, as shown in:
An error occurs during dcm4che3 loading, as shown below:
Problem Analysis:
This problem occurs because dcm4che3 and fo-dicom are being parsed.For 20Non-standard VRUnrecognized. In the following section, we will analyze the source code of dcm4che3 and dcmtk to locate the problem and provide a solution (only the source code of dcm4che3.3.8 and dmctk3.6 are compared and analyzed here, the source code analysis of fo-dicom is to be supplemented after subsequent sorting ).
1. dcmtk3.6 source code:
Use dcmtk to compile the data loading test project. The simple sample code is as follows:
int main(){ OFLog::configure(OFLogger::TRACE_LOG_LEVEL); char* ifname = "c:\\1.dcm"; E_FileReadMode readMode = /*ERM_fileOnly*/ERM_autoDetect; E_TransferSyntax xfer = EXS_Unknown; Uint32 maxReadLength = DCM_MaxReadLength; bool loadIntoMemory = true; DcmFileFormat dfile; DcmObject *dset = &dfile; if (readMode == ERM_dataset) dset = dfile.getDataset(); OFCondition cond = dfile.loadFile(ifname, xfer, EGL_noChange, maxReadLength, readMode); if (cond.bad()) { return 1; } return 0;}
After one-step debugging, you can see that the process for dcmtk to load dicom files is as follows:
In the DcmItem classNon-standard VR ElementsThere are corresponding warning messages,
/* if the VR which was read is not a standard VR, print a warning */ if (!vr.isStandard()) { OFOStringStream oss; oss << "DcmItem: Non-standard VR '" << ((OFstatic_cast(unsigned char, vrstr[0]) < 32) ? ' ' : vrstr[0]) << ((OFstatic_cast(unsigned char, vrstr[1]) < 32) ? ' ' : vrstr[1]) << "' (" << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(2) << OFstatic_cast(unsigned int, vrstr[0] & 0xff) << "\\" << STD_NAMESPACE setw(2) << OFstatic_cast(unsigned int, vrstr[1] & 0xff) << ") encountered while parsing element " << newTag << OFStringStream_ends; OFSTRINGSTREAM_GETSTR(oss, tmpString) /* encoding of this data element might be wrong, try to correct it */ if (dcmAcceptUnexpectedImplicitEncoding.get()) { DCMDATA_WARN(tmpString << ", trying again with Implicit VR Little Endian"); /* put back read bytes to input stream ... */ inStream.putback(); bytesRead = 0; /* ... and retry with Implicit VR Little Endian transfer syntax */ return readTagAndLength(inStream, EXS_LittleEndianImplicit, tag, length, bytesRead); } else { DCMDATA_WARN(tmpString << ", assuming " << (vr.usesExtendedLengthEncoding() ? "4" : "2") << " byte length field"); } OFSTRINGSTREAM_FREESTR(tmpString) } /* set the VR which was read in the above created tag object. */ newTag.setVR(vr); /* increase counter by 2 */ bytesRead += 2;
After warningNon-standard VR ElementsThe process is as follows:
/* read the value in the length field. In some cases, it is 4 bytes wide, in other */ /* cases only 2 bytes (see DICOM standard part 5, section 7.1.1) */ if (xferSyn.isImplicitVR() || nxtobj == EVR_na) //note that delimitation items don't have a VR { inStream.read(&valueLength, 4); //length field is 4 bytes wide swapIfNecessary(gLocalByteOrder, byteOrder, &valueLength, 4, 4); bytesRead += 4; } else { //the transfer syntax is explicit VR DcmVR vr(newTag.getEVR()); if (vr.usesExtendedLengthEncoding()) { Uint16 reserved; inStream.read(&reserved, 2); // 2 reserved bytes inStream.read(&valueLength, 4); // length field is 4 bytes wide swapIfNecessary(gLocalByteOrder, byteOrder, &valueLength, 4, 4); bytesRead += 6; } else { Uint16 tmpValueLength; inStream.read(&tmpValueLength, 2); // length field is 2 bytes wide swapIfNecessary(gLocalByteOrder, byteOrder, &tmpValueLength, 2, 2); bytesRead += 2; valueLength = tmpValueLength; } }
According to the above Code,OfVR = 20, 20, Which is parsed as EVR_UNKNOWN2B by dcmtk, as described in the code comment:
/// Used internally for elements with unknown VR with 2-byte length field in explicit VR
EVR_UNKNOWN2B
DICOM Standard7.1.2 of PS5Non-standard VR is described as follows:
2. dcm4che3.3.8 source code:
Compare the source code of dcm4che3.3.8 and find thatOfVR = 20, 20, Directly marked as UN type by dcmtk,
public static VR valueOf(int code) { try { VR vr = VALUE_OF[indexOf(code)]; if (vr != null) return vr; } catch (IndexOutOfBoundsException e) {} LOG.warn("Unrecogniced VR code: {0}H - treat as UN", Integer.toHexString(code)); return UN; }
In dcm4che3, the UN type is defined
Here, the UN type is defined according to the DICOM3.0 standard label constraints of the VR = UN (unknown) type in the preceding section,That is, the VR field should be four bytes.. HoweverOfVR = 20, 20AfterValue LengthOnly two bytes02 00. Therefore, dcm4che3 is loadedThe length of an element is incorrectly parsed4163895298, That is, the hexadecimalF8 30 00 02, As shown in:
Solution:
So far, we have found dcm4che3 error parsing.OfVR = 20, 20Reasons for nonstandard VR elements.This non-standard VR cannot be processed as the VR. UN type in a unified manner, but the specific Length of its subsequent Value Length should be 2 or 4 for classification.(This issue will be further analyzed in subsequent blog posts. Please note that), There are two places to modify:
1. Correct parsing of Non-standard VR:
//VR.java,Line 110public static VR valueOf(int code) { try { VR vr = VALUE_OF[indexOf(code)]; if (vr != null) return vr; } catch (IndexOutOfBoundsException e) {} LOG.warn("Unrecogniced VR code: {0}H - treat as UN", Integer.toHexString(code)); //return UN; LOG.warn("zssure:to solve non-standard VR,Unrecogniced VR code: {0}H - treat as UN", Integer.toHexString(code)); return null;//zssure:to solve non-standard VR }
2. Read the VL of Non-standard VR correctly:
//DicomInputStream.java Line 386 public int readHeader() throws IOException { byte[] buf = buffer; tagPos = pos; readFully(buf, 0, 8); switch(tag = ByteUtils.bytesToTag(buf, 0, bigEndian)) { case Tag.Item: case Tag.ItemDelimitationItem: case Tag.SequenceDelimitationItem: vr = null; break; default: if (explicitVR) { vr = VR.valueOf(ByteUtils.bytesToVR(buf, 4)); //zssure:to solve non-standard VR //referred:dcmtk/dcitem.cc/readTagAndLength,Line 970 if(vr == null) { length = ByteUtils.bytesToUShort(buf, 6, bigEndian); return tag; } //zssure:end if (vr.headerLength() == 8) { length = ByteUtils.bytesToUShort(buf, 6, bigEndian); return tag; } readFully(buf, 4, 4); } else { vr = VR.UN; } } length = ByteUtils.bytesToInt(buf, 4, bigEndian); return tag; }
Download the test file:
The test data used in this article has been uploaded to the CSDN repository on Github. You can download the data by yourself. The data has been anonymized to protect patient privacy.
Download Non-standard VR test dcm file
Follow-up blog introduction:
1. view the Java stream operation in the dcm4che3. x database "Copy of the stream"
2. Eclipse automatically compiles dcm4che3. x source code
3. Comparison and Analysis of the three open-source DICOM libraries: "data loading" (continued)
By zssure@163.com
Time: 2015-09-05
Copyright Disclaimer: This article is an original zssure article. Please indicate the source for reprinting. It cannot be reproduced without your permission.