r2168: *** empty log message ***
[ctsim.git] / libctsim / ctndicom.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **      Name:           ctndicomp.cpp
5 **  Purpose:      Interface to CTN Dicom classes
6 **      Programmer:   Kevin Rosenberg
7 **      Date Started: March 2001
8 **
9 **  This is part of the CTSim program
10 **  Copyright (c) 1983-2001 Kevin Rosenberg
11 **
12 **  $Id: ctndicom.cpp,v 1.15 2002/06/27 01:48:26 kevin Exp $
13 **
14 **  This program is free software; you can redistribute it and/or modify
15 **  it under the terms of the GNU General Public License (version 2) as
16 **  published by the Free Software Foundation.
17 **
18 **  This program is distributed in the hope that it will be useful,
19 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
20 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 **  GNU General Public License for more details.
22 **
23 **  You should have received a copy of the GNU General Public License
24 **  along with this program; if not, write to the Free Software
25 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 ******************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #ifdef HAVE_CTN_DICOM
33
34 #include "ctndicom.h" 
35 #include "imagefile.h"
36 #include "projections.h"
37
38
39 DicomImporter::DicomImporter (const char* const pszFile)
40   : m_strFilename(pszFile), m_bFail(false), m_iContents(DICOM_CONTENTS_INVALID), 
41     m_pImageFile(NULL), m_pProjections(NULL), m_pFile(NULL)
42 {
43   unsigned long lOptions = DCM_ORDERLITTLEENDIAN | DCM_FORMATCONVERSION;
44   DCM_Debug (FALSE);
45
46   CONDITION cond = DCM_OpenFile (pszFile, lOptions, &m_pFile);
47   if (cond != DCM_NORMAL) {
48     m_bFail = true;
49     char textbuf [2048]; 
50     CONDITION cond2 = COND_TopCondition (&cond, textbuf, sizeof(textbuf));
51     cond2 = DCM_NORMAL; // testing
52     if (cond2 != DCM_NORMAL) {
53       m_strFailMessage = "DCM_OpenFile failure: ";
54       m_strFailMessage += m_strFilename;
55     } else
56       m_strFailMessage = textbuf;
57
58     return;
59   }
60
61   unsigned short iNRows, iNCols, iBitsAllocated, iBitsStored, iHighBit, iPixRep;
62   DCM_ELEMENT aElemRequired[] = {
63     {DCM_IMGROWS, DCM_US, "", 1, sizeof(iNRows), reinterpret_cast<char*>(&iNRows),},
64     {DCM_IMGCOLUMNS, DCM_US, "", 1, sizeof(iNCols), reinterpret_cast<char*>(&iNCols),},
65     {DCM_IMGBITSALLOCATED, DCM_US, "", 1, sizeof(iBitsAllocated), reinterpret_cast<char*>(&iBitsAllocated),},
66     {DCM_IMGBITSSTORED, DCM_US, "", 1, sizeof(iBitsStored), reinterpret_cast<char*>(&iBitsStored),},
67     {DCM_IMGHIGHBIT, DCM_US, "", 1, sizeof(iHighBit), reinterpret_cast<char*>(&iHighBit),},
68     {DCM_IMGPIXELREPRESENTATION, DCM_US, "", 1, sizeof(iPixRep), reinterpret_cast<char*>(&iPixRep),},
69   };
70   int nElemRequired = sizeof (aElemRequired) / sizeof(DCM_ELEMENT);
71
72   if (DCM_ParseObject (&m_pFile, aElemRequired, nElemRequired, NULL, 0, NULL) == DCM_NORMAL) {
73     loadImage (iNRows, iNCols, iBitsAllocated, iBitsStored, iHighBit, iPixRep);
74     return;
75   }
76   U32 lRtnLength;
77   DCM_TAG somatomTag = DCM_MAKETAG(TAG_GROUP_SOMATOM, TAG_MEMBER_SOMATOM_DATA);
78   if (DCM_GetElementSize (&m_pFile, somatomTag, &lRtnLength) == DCM_NORMAL)
79     loadProjections();
80 }
81
82 void
83 DicomImporter::loadImage(unsigned short iNRows, unsigned short iNCols, unsigned short iBitsAllocated, 
84                          unsigned short iBitsStored, unsigned short iHighBit, unsigned short iPixRep)
85 {
86   U32 lRtnLength;
87   unsigned short iSamplesPerPixel, iPlanarConfig;
88   
89   DCM_ELEMENT elemPlanarConfig = {DCM_IMGPLANARCONFIGURATION, DCM_US, "", 1, sizeof(iPlanarConfig), 
90                       reinterpret_cast<char*>(&iPlanarConfig)};
91   DCM_ELEMENT elemSamplesPerPixel = {DCM_IMGSAMPLESPERPIXEL, DCM_US, "", 1, 
92                       sizeof(iSamplesPerPixel), reinterpret_cast<char*>(&iSamplesPerPixel)};
93
94   if (DCM_ParseObject (&m_pFile, &elemSamplesPerPixel, 1, NULL, 0, NULL) != DCM_NORMAL)
95     iSamplesPerPixel = 1;  // default value
96
97   if (iSamplesPerPixel > 1) {
98     void* ctx = NULL;
99     if (DCM_GetElementValue (&m_pFile, &elemPlanarConfig, &lRtnLength, &ctx) != DCM_NORMAL) {
100       m_bFail = true;
101       m_strFailMessage = "Planar Configuration not specified when iSamplesPerPixel > 1";
102     }
103   }
104
105   char szRescaleSlope[17];
106   char szRescaleIntercept[17];
107   double dRescaleSlope = 1;
108   double dRescaleIntercept = 0;
109   DCM_ELEMENT elemRescaleSlope = {DCM_IMGRESCALESLOPE, DCM_DS, "", 1, strlen(szRescaleSlope), szRescaleSlope};
110   DCM_ELEMENT elemRescaleIntercept = {DCM_IMGRESCALEINTERCEPT, DCM_DS, "", 1, strlen(szRescaleIntercept), szRescaleIntercept};
111   if (DCM_ParseObject (&m_pFile, &elemRescaleSlope, 1, NULL, 0, NULL) == DCM_NORMAL) {
112     if (sscanf (szRescaleSlope, "%lf", &dRescaleSlope) != 1)
113       sys_error (ERR_SEVERE, "Error parsing rescale slope");
114   }
115   if (DCM_ParseObject (&m_pFile, &elemRescaleIntercept, 1, NULL, 0, NULL) == DCM_NORMAL) {
116     if (sscanf (szRescaleIntercept, "%lf", &dRescaleIntercept) != 1)
117       sys_error (ERR_SEVERE, "Error parsing rescale intercept");
118   }
119
120   DCM_ELEMENT elemPixelData = {DCM_PXLPIXELDATA, DCM_OT, "", 1, 0, NULL};
121   // Get the actual  pixel data (the only other required element)
122   if (DCM_GetElementSize (&m_pFile, elemPixelData.tag, &lRtnLength) != DCM_NORMAL) {
123     m_bFail = true;
124     m_strFailMessage = "Can't get pixel data size";
125     return;
126   }
127
128   // Check the size of the pixel data to make sure we have the correct amount...
129   unsigned long lRealLength = lRtnLength;
130   unsigned long lCheckLengthInBytes = iNRows * iNCols * iSamplesPerPixel;
131   if (iBitsAllocated == 16)
132     lCheckLengthInBytes *= 2;
133   if (lCheckLengthInBytes > lRealLength) {
134     m_bFail = true;
135     m_strFailMessage = "Too little pixel data supplied";
136     return;
137   }
138   // Now allocate the memory to hold the pixel data and get it from the DICOM file...
139
140   unsigned char* pRawPixels = new unsigned char [lCheckLengthInBytes];
141   elemPixelData.length = lCheckLengthInBytes;
142   elemPixelData.d.ot = pRawPixels;
143   
144   void* ctx = NULL;
145   CONDITION cond = DCM_GetElementValue (&m_pFile, &elemPixelData, &lRtnLength, &ctx);
146   if ((cond != DCM_NORMAL) && (cond != DCM_GETINCOMPLETE)) {
147     m_bFail = true;
148     m_strFailMessage = "Can't read pixel data";
149     delete pRawPixels;
150     return;
151   }
152   if ((lCheckLengthInBytes < lRealLength) && (cond != DCM_GETINCOMPLETE)) {
153     m_bFail = true;
154     m_strFailMessage = "Should have gooten incomplete message reading pixel data";
155     delete pRawPixels;
156     return;
157   }
158
159   m_pImageFile = new ImageFile (iNCols, iNRows);
160   ImageFileArray v = m_pImageFile->getArray();
161   double dScale = 1 << iBitsStored;
162   unsigned int iMaskLength = iBitsStored;
163   if (iMaskLength > 8)
164     iMaskLength -= 8;
165   unsigned int iMask = (1 << iMaskLength) - 1;
166   for (int iy = 0; iy < iNRows; iy++) {
167     for (int ix = 0; ix < iNCols; ix++) {
168       if (iBitsAllocated == 8)  {
169         unsigned char cV = pRawPixels[iy * iNRows + ix];
170         v[ix][iy] = (cV & iMask) / dScale;
171       } else if (iBitsAllocated == 16) {
172         unsigned long lBase = (iy * iNRows + ix) * 2;
173         unsigned char cV1 = pRawPixels[lBase];
174         unsigned char cV2 = pRawPixels[lBase+1] & iMask;
175         int iV = cV1 + (cV2 << 8);
176         v[ix][iNRows - 1 - iy] = iV * dRescaleSlope + dRescaleIntercept;
177       }
178     }
179   }
180   m_iContents = DICOM_CONTENTS_IMAGE;
181 }
182
183 void
184 DicomImporter::loadProjections()
185 {
186   U32 lRtnLength;
187   void* ctx = NULL;
188
189   unsigned short iNViews, iNDets;
190   DCM_ELEMENT aElemRequired[] = {
191     {DCM_IMGROWS, DCM_US, "", 1, sizeof(iNViews), reinterpret_cast<char*>(&iNViews)},
192     {DCM_IMGCOLUMNS, DCM_US, "", 1, sizeof(iNDets), reinterpret_cast<char*>(&iNDets)},
193   };
194   int nElemRequired = sizeof (aElemRequired) / sizeof(DCM_ELEMENT);
195
196   if (DCM_ParseObject (&m_pFile, aElemRequired, nElemRequired, NULL, 0, NULL) != DCM_NORMAL) {
197     m_bFail = true;
198     m_strFailMessage = "Unable to read header for projections";
199     return;
200   }
201
202   DCM_TAG somatomTag = DCM_MAKETAG(TAG_GROUP_SOMATOM, TAG_MEMBER_SOMATOM_DATA);
203   DCM_ELEMENT elemProjections = {somatomTag, DCM_UN, "", 1, 0, NULL};
204   if (DCM_GetElementSize (&m_pFile, elemProjections.tag, &lRtnLength) != DCM_NORMAL) {
205     m_bFail = true;
206     m_strFailMessage = "Can't find projection data";
207     return;
208   }
209
210   unsigned char* pRawProjections = new unsigned char [lRtnLength];
211   elemProjections.length = lRtnLength;
212   elemProjections.d.ot = pRawProjections;
213   
214   ctx = NULL;
215   CONDITION cond = DCM_GetElementValue (&m_pFile, &elemProjections, &lRtnLength, &ctx);
216   if ((cond != DCM_NORMAL) && (cond != DCM_GETINCOMPLETE)) {
217     m_bFail = true;
218     m_strFailMessage = "Can't read projections data";
219     delete pRawProjections;
220     return;
221   }
222   m_iContents = DICOM_CONTENTS_PROJECTIONS;
223   m_pProjections = new Projections;
224   if (! m_pProjections->initFromSomatomAR_STAR (iNViews, iNDets, pRawProjections, lRtnLength)) {
225     m_bFail = true;
226     m_strFailMessage = "Error converting raw projection data";
227     delete m_pProjections;
228     m_pProjections = NULL;
229   }
230
231   delete pRawProjections;
232 }
233
234
235 DicomImporter::~DicomImporter()
236 {
237   if (m_pFile)
238     DCM_CloseObject (&m_pFile);
239 }
240
241
242 DicomExporter::DicomExporter (ImageFile* pImageFile)
243 : m_pImageFile(pImageFile), m_pObject(NULL)
244 {
245   DCM_Debug (FALSE);
246   if (! pImageFile) {
247     m_bFail = true;
248     m_strFailMessage = "Initialized DicomExported with NULL imagefile";
249     return;
250   }
251   m_bFail = ! createDicomObject();
252 }
253
254 DicomExporter::~DicomExporter ()
255 {
256   if (m_pObject)
257     DCM_CloseObject (&m_pObject);
258 }
259
260 bool
261 DicomExporter::writeFile (const char* const pszFilename)
262 {
263   if (! m_pObject)
264     return false;
265
266   m_strFilename = pszFilename;
267
268   CONDITION cond = DCM_WriteFile (&m_pObject, DCM_ORDERLITTLEENDIAN, pszFilename);
269   if (cond != DCM_NORMAL) {
270     m_bFail = true;
271     m_strFailMessage = "Error writing DICOM file ";
272     m_strFailMessage += pszFilename;
273     return false;
274   }
275
276   return true;
277 }
278
279 bool
280 DicomExporter::createDicomObject()
281 {
282   CONDITION cond = DCM_CreateObject (&m_pObject, 0);
283   if (cond != DCM_NORMAL) {
284     m_bFail = true;
285     m_strFailMessage = "Error creating DICOM object";
286     return false;
287   }
288
289   double dMin, dMax;
290   m_pImageFile->getMinMax (dMin, dMax);
291   double dWidth = dMax - dMin;
292   if (dWidth == 0.)
293     dWidth = 1E-7;
294   double dScale = 65535. / dWidth;
295
296   double dRescaleIntercept = dMin;
297   double dRescaleSlope = 1 / dScale;
298   char szRescaleIntercept[17];
299   char szRescaleSlope[17];
300   snprintf (szRescaleIntercept, sizeof(szRescaleIntercept), "%e", dRescaleIntercept);
301   snprintf (szRescaleSlope, sizeof(szRescaleIntercept), "%e", dRescaleSlope);
302
303   char szCTSimRoot[] = "1.2.826.0.1.3680043.2.284.";
304   char szModality[] = "CT";
305   char szSOPClassUID[65] = "1.2.840.10008.5.4.1.1.2";
306   char szImgPhotometricInterp[] = "MONOCHROME2";
307   char szPixelSpacing[33] = "0\\0";
308   char szRelImageOrientationPatient[100] = "1\\0\\0\\0\\1\\0";
309   char szRelImagePositionPatient[49] = "0\\0\\0";
310   char szAcqKvp[] = "0";
311   char szRelAcquisitionNumber[] = "1";
312   char szRelImageNumber[] = "1";
313   char szIDSOPInstanceUID[65] = "";
314   char szIDManufacturer[] = "CTSim";
315   char szRelPositionRefIndicator[] = "0";
316   char szRelFrameOfReferenceUID[65] = "";
317   char szRelSeriesNumber[] = "1";
318   char szIDAccessionNumber[] = "0";
319   char szRelStudyID[] = "1";
320   char szIDReferringPhysician[] = "NONE";
321   char szIDStudyTime[] = "000000.0";
322   char szIDStudyDate[] = "00000000";
323   char szRelStudyInstanceUID[65] = "";
324   char szPatSex[] = "O";
325   char szPatBirthdate[] = "0000000";
326   char szPatID[] = "NONE";
327   char szPatName[] = "NONE";
328   char szIDImageType[] = "ORIGINAL";
329   char szIDManufacturerModel[65] = "";
330
331   std::ostringstream osPatComments;
332   m_pImageFile->printLabelsBrief (osPatComments);
333   size_t sizePatComments = osPatComments.str().length();
334   char* pszPatComments = new char [sizePatComments+1];
335   strncpy (pszPatComments, osPatComments.str().c_str(), sizePatComments);
336
337   snprintf (szIDSOPInstanceUID, sizeof(szIDSOPInstanceUID), "%s.2.1.6.1", szCTSimRoot);
338   snprintf (szRelStudyInstanceUID, sizeof(szRelStudyInstanceUID), "%s.2.1.6.1.1", szCTSimRoot);
339   snprintf (szRelFrameOfReferenceUID, sizeof(szRelFrameOfReferenceUID), "%s.99", szCTSimRoot);
340 #ifdef VERSION
341   snprintf (szIDManufacturerModel, sizeof(szIDManufacturerModel), "VERSION %s", VERSION);
342 #endif
343   snprintf (szPixelSpacing, sizeof(szPixelSpacing), "%e\\%e", m_pImageFile->axisIncrementX(), m_pImageFile->axisIncrementY());
344   double minX, maxX, minY, maxY;
345   if (m_pImageFile->getAxisExtent(minX, maxX, minY, maxY)) {
346     minX += m_pImageFile->axisIncrementX() / 2;
347     minY += m_pImageFile->axisIncrementY() / 2;
348     snprintf(szRelImagePositionPatient, sizeof(szRelImagePositionPatient), "%e\\%e\\0", minX, minY);
349   }
350
351   unsigned short iNRows = m_pImageFile->ny();
352   unsigned short iNCols = m_pImageFile->nx();
353   unsigned short iBitsAllocated = 16;
354   unsigned short iBitsStored = 16;
355   unsigned short iHighBit = 15;
356   unsigned short iPixRep = 0;
357   unsigned short iSamplesPerPixel = 1;
358   DCM_ELEMENT aElemRequired[] = {
359     {DCM_IMGROWS, DCM_US, "", 1, sizeof(iNRows), reinterpret_cast<char*>(&iNRows)},
360     {DCM_IMGCOLUMNS, DCM_US, "", 1, sizeof(iNCols), reinterpret_cast<char*>(&iNCols)},
361     {DCM_IMGBITSALLOCATED, DCM_US, "", 1, sizeof(iBitsAllocated), reinterpret_cast<char*>(&iBitsAllocated)},
362     {DCM_IMGBITSSTORED, DCM_US, "", 1, sizeof(iBitsStored), reinterpret_cast<char*>(&iBitsStored)},
363     {DCM_IMGHIGHBIT, DCM_US, "", 1, sizeof(iHighBit), reinterpret_cast<char*>(&iHighBit)},
364     {DCM_IMGPIXELREPRESENTATION, DCM_US, "", 1, sizeof(iPixRep), reinterpret_cast<char*>(&iPixRep)},
365     {DCM_IMGSAMPLESPERPIXEL, DCM_US, "", 1, sizeof(iSamplesPerPixel), reinterpret_cast<char*>(&iSamplesPerPixel)},
366     {DCM_IMGRESCALESLOPE, DCM_DS, "", 1, strlen(szRescaleSlope), szRescaleSlope},
367     {DCM_IMGRESCALEINTERCEPT, DCM_DS, "", 1, strlen(szRescaleIntercept), szRescaleIntercept},
368     {DCM_IMGPHOTOMETRICINTERP, DCM_CS, "", 1, strlen(szImgPhotometricInterp), szImgPhotometricInterp},
369     {DCM_IMGPIXELSPACING, DCM_DS, "", 1, strlen(szPixelSpacing), szPixelSpacing},
370     {DCM_RELIMAGEORIENTATIONPATIENT, DCM_DS, "", 1, strlen(szRelImageOrientationPatient), szRelImageOrientationPatient},
371     {DCM_RELIMAGEPOSITIONPATIENT, DCM_DS, "", 1, strlen(szRelImagePositionPatient), szRelImagePositionPatient},
372     {DCM_ACQKVP, DCM_DS, "", 1, strlen(szAcqKvp), szAcqKvp},
373     {DCM_RELACQUISITIONNUMBER, DCM_IS, "", 1, strlen(szRelAcquisitionNumber), szRelAcquisitionNumber},
374     {DCM_ACQSLICETHICKNESS, DCM_DS, "", 1, strlen(szRelAcquisitionNumber), szRelAcquisitionNumber},
375     {DCM_RELIMAGENUMBER, DCM_IS, "", 1, strlen(szRelImageNumber), szRelImageNumber},
376     {DCM_IDSOPINSTANCEUID, DCM_UI, "", 1, strlen(szIDSOPInstanceUID), szIDSOPInstanceUID},
377     {DCM_IDMANUFACTURER, DCM_LO, "", 1, strlen(szIDManufacturer), szIDManufacturer},
378     {DCM_RELPOSITIONREFINDICATOR, DCM_LO, "", 1, strlen(szRelPositionRefIndicator), szRelPositionRefIndicator},
379     {DCM_RELFRAMEOFREFERENCEUID, DCM_UI, "", 1, strlen(szRelFrameOfReferenceUID), szRelFrameOfReferenceUID},
380     {DCM_RELSERIESNUMBER, DCM_IS, "", 1, strlen(szRelSeriesNumber), szRelSeriesNumber},
381     {DCM_RELSERIESINSTANCEUID, DCM_UI, "", 1, strlen(szIDAccessionNumber), szIDAccessionNumber},
382     {DCM_IDACCESSIONNUMBER, DCM_SH, "", 1, strlen(szIDAccessionNumber), szIDAccessionNumber},
383     {DCM_RELSTUDYID, DCM_SH, "", 1, strlen(szRelStudyID), szRelStudyID},
384     {DCM_IDREFERRINGPHYSICIAN, DCM_PN, "", 1, strlen(szIDReferringPhysician), szIDReferringPhysician},
385     {DCM_IDSTUDYTIME, DCM_TM, "", 1, strlen(szIDStudyTime), szIDStudyTime},
386     {DCM_IDSTUDYDATE, DCM_DA, "", 1, strlen(szIDStudyDate), szIDStudyDate},
387     {DCM_RELSTUDYINSTANCEUID, DCM_UI, "", 1, strlen(szRelStudyInstanceUID), szRelStudyInstanceUID},
388     {DCM_PATSEX, DCM_CS, "", 1, strlen(szPatSex), szPatSex},
389     {DCM_PATBIRTHDATE, DCM_DA, "", 1, strlen(szPatBirthdate), szPatBirthdate},
390     {DCM_PATID, DCM_LO, "", 1, strlen(szPatID), szPatID},
391     {DCM_PATNAME, DCM_PN, "", 1, strlen(szPatName), szPatName},
392     {DCM_IDIMAGETYPE, DCM_CS, "", 1, strlen(szIDImageType), szIDImageType},
393     {DCM_IDMODALITY, DCM_CS, "", 1, strlen(szModality), szModality},
394     {DCM_IDSOPCLASSUID, DCM_UI, "", 1, strlen(szSOPClassUID), szSOPClassUID},
395     {DCM_IDMANUFACTURERMODEL, DCM_LO, "", 1, strlen(szIDManufacturerModel), szIDManufacturerModel},
396     {DCM_PATCOMMENTS, DCM_LT, "", 1, strlen(pszPatComments), pszPatComments},
397   };
398   int nElemRequired = sizeof (aElemRequired) / sizeof(DCM_ELEMENT);
399
400   int iUpdateCount;
401   cond = DCM_ModifyElements (&m_pObject, aElemRequired, nElemRequired, NULL, 0, &iUpdateCount);
402
403   DCM_ELEMENT elemPixelData = {DCM_PXLPIXELDATA, DCM_OT, "", 1, 0, NULL};
404
405   unsigned long lRealLength = 2 * m_pImageFile->nx() * m_pImageFile->ny();
406
407   unsigned char* pRawPixels = new unsigned char [lRealLength];
408   elemPixelData.length = lRealLength;
409   elemPixelData.d.ot = pRawPixels;
410   
411   ImageFileArray v = m_pImageFile->getArray();
412   for (int iy = 0; iy < iNRows; iy++) {
413     for (int ix = 0; ix < iNCols; ix++) {
414         unsigned long lBase = (iy * iNRows + ix) * 2;
415         unsigned int iValue = nearest<int>(dScale * (v[ix][iNRows - 1 - iy] - dMin));
416         pRawPixels[lBase] = iValue & 0xFF;
417         pRawPixels[lBase+1] = (iValue & 0xFF00) >> 8;
418     }
419   }
420   cond = DCM_ModifyElements (&m_pObject, &elemPixelData, 1, NULL, 0, &iUpdateCount);
421   delete pRawPixels;
422
423   delete pszPatComments;
424
425   if (cond != DCM_NORMAL || iUpdateCount != 1) {
426     m_bFail = true;
427     m_strFailMessage = "Error modifying pixel data";
428     return false;
429   }
430
431   return true;
432 }
433
434 #endif // HAVE_CTN_DICOM
435
436