dcm文件编程

来源:互联网 发布:网站在线seo免费外链 编辑:程序博客网 时间:2024/05/20 07:14

原帖地址:http://blog.sina.com.cn/s/blog_6109582e0101fmm6.html


dcmtk程序包简介

该文简单列出了dcmtk程序包的简介,包括主要接口类的简单说明,可用工具以及一些例子。下一步工作准备详细分析每个程序包中的接口类功能,并结合源码和dicom文档分析其实现过程。

一、Config程序包

-config目录下的文档:

--config.txt:

指出你编辑的任何.h .c .cc文件首先必须包含该目录下的头文件#include "osconfig.h"

--dirstruc.txt:

给出了dcmtk项目的项目目录结构,这个用cmake会自动生成

--envvars.txt:

这个文件比较重要,它指出了一些运行时环境变量,这些变量可能会影响dcmtk的工具和库的使用,这些变量包括:

---DCMDICTPATH:影响dcmdata

    On Win32 platforms, a built-in dictionary is used by default. If the DCMDICTPATH environment variable is set, the applications will attempt to load _additional_ DICOM data dictionaries specified in the DCMDICTPATH environment variable instead. The DCMDICTPATH environment variable has the same format as the shell PATH variable in that a semicolon (";") separates entries. The data dictionary code will attempt to load each file specified in the DCMDICTPATH environment variable.

    See also: documentation in dcmdata/docs/datadict.txt

---TCP_BUFFER_LENGTH:影响dcmnet

    By default, DCMTK uses a TCP send and receive buffer length of 32K. If the environment variable TCP_BUFFER_LENGTH is set, it specified an override for the TCP buffer length. The value is specified in bytes, not in Kbytes.

---TCP_NODELAY:影响dcmnet

    If this environment variable contains a non-zero number, the Nagle algorithm will not be disabled for TCP transport connections. Also see documentation for macro DONT_DISABLE_NAGLE_ALGORITHM in config/docs/macros.txt

---TMPDIR:影响dcmnet

    Affects the implementation of the tempnam() emulation on platforms where tempnam() is not defined. See tempnam(3S) main page for a description.

--macros.txt:

这个文件也特别重要,它给出了很多编译时的宏,这些宏可能会影响dcmtk的工具和库的使用。大部分的宏可以用来激活一些实验性的或很少需要的特性,另外有一些是用来取消某些功能。要尽量谨慎使用。详细见文档。

--modules.txt:

这个文件讲述如何自己配置各个模块,不需要掌握。

-configinclude目录下的文件

--osconfig.h:这个文件是必须包含在所有.h .c文件中的,其中指出在win32环境下包含"dcmtk/config/cfwin32.h"文件

--cfwin32.h:包含了大量的宏定义。***如果需要查找某个宏的定义,可到这个文件中查找***

二、ofstd程序包

-ofstd:作为一般目的的类库。

这个模块包含了一般目的的类库,这些类所描述的对象概念并非在Dicom标准中特有。它们广泛的在toolkit中使用。主要包含下面的类

--OFCommandLine:处理命令行参数,头文件在ofcmdln.h***详情需要结合具体的代码来理解***

--OFCondition:描述条件码的一般类。头文件在ofcond.h***详情需要结合具体的代码来理解***

--OFConsole:是一个singleton(孤立)***不明白***。提供线程安全的对标准输出流和错误流的访问。允许以多线程的方式同时创建输出。***多线程的东西不太明白,它的作用应该是在多线程中实现指定线程的输出流进行输出操作***

--OFList:是一个双向链表模板类,接口是STL list类中的一个子集。头文件在oflist.h***不明白为什么不直接用标准库中的list,兼容性应该更好啊***

--OFStack:是一个堆栈模板类,接口是STL stack类中的一个子集。头文件在ofstack.h***不明白为什么不直接用标准库中的stack,兼容性应该更好啊***

--OFStandard:包含大量帮助函数组成的类,用来包含大量全局帮助函数。注意全部都是静态函数。其中的一些函数实现调用了windows API函数,如fileexists()。头文件在ofstd.h***如果需要一些全局函数,可以到这里了找一找。***

--OFString:一个简单的string类,实现了std::string的一个子集,没有iteratortrait,在速度上也没有优化。头文件在ofstring.h***不明白为什么不直接用标准库中的string,兼容性应该更好啊***

--除了上述的主要类以外,还包含了大量辅助类,用来支撑上述类的功能。***详情需要结合具体的代码来理解***

三、dcmdata程序包

-dcmdata:一个数据编码/解码库和可用的工具

这个模块包含了一些类来管理Dicom数据结构和文件。同时它也提供了对DICOMDIR文件的支持以满足Dicom storage media(存储介质)的需要。

主要的接口类有:

--DcmFileFormata class handling the DICOM file format (with meta header) 。头文件在dcfilefo.h***详情在单独的文档中分析***

--DcmDataseta class handling the DICOM dataset format (files without meta header) 。头文件在dcdatset.h***详情在单独的文档中分析***

--DcmItema class representing a collection of DICOM elements。头文件在dcitem.h***详情在单独的文档中分析***

--DcmElementabstract base class for all DICOM elements。头文件在dcelem.h***详情在单独的文档中分析***。它的派生类包括:DcmAttributeTag\DcmByteString\DcmFloatingPointDouble\DcmFloatingPointSingle\DcmOtherByteOtherWord\DcmSequenceOfItems\DcmSignedLong\DcmSignedShort\DcmUnsignedLong\DcmUnsignedShort

工具:这个模块包含了下面的命令行工具:

dcm2xml: Convert DICOM file and data set to XML

dcmconv: Convert DICOM file encoding

dcmcrle: Encode DICOM file to RLE transfer syntax

dcmdrle: Decode RLE-compressed DICOM file

dcmdump: Dump DICOM file and data set

dcmftest: Test if file uses DICOM part 10 format

dcmgpdir: Create a general purpose DICOMDIR

dcmodify: Modify DICOM files

dump2dcm: Convert ASCII dump to DICOM file

xml2dcm: Convert XML document to DICOM file or data set

***暂时不对命令行工具进行详细的分析***

举例:

--调入一个DICOM文件,输出病人姓名

DcmFileFormat fileformat;

OFCondition status = fileformat.loadFile("test.dcm");

if (status.good())

{OFString patientsName;

If (fileformat.getDataset()->findAndGetOFString(DCM_PatientsName, patientsName).good())

   cout << "Patient's Name: " << patientsName << endl;

} else

    cerr << "Error: cannot access Patient's Name!" << endl;

} else

cerr << "Error: cannot read DICOM file (" << status.text() << ")" << endl;

--创建一个DICOM dataset数据集,并保存为文件

char uid[100];

DcmFileFormat fileformat;

DcmDataset *dataset = fileformat.getDataset();

dataset->putAndInsertString(DCM_SOPClassUID, UID_SecondaryCaptureImageStorage);

dataset->putAndInsertString(DCM_SOPInstanceUID, dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT));

dataset->putAndInsertString(DCM_PatientsName, "Doe^John");

 

dataset->putAndInsertUint8Array(DCM_PixelData, pixelData, pixelLength);

OFCondition status = fileformat.saveFile("test.dcm", EXS_LittleEndianExplicit);

if (status.bad())

cerr << "Error: cannot write DICOM file (" << status.text() << ")" << endl;

--如何为多个文件创建一般目的的DICOMDIR

DicomDirInterface dicomdir;

OFCondition status = dicomdir.createNewDicomDir();

if (status.good())

{while ( )

    dicomdir.addDicomFile( );

status = dicomdir.writeDicomDir();

if (status.bad())

    cerr << "Error: cannot write DICOMDIR (" << status.text() << ")" << endl;

} else

cerr << "Error: cannot create DICOMDIR (" << status.text() << ")" << endl;

四、dcmimgle程序包

dcmimgle是一个图像处理库和可用的工具模块,它包括了对DICOM单色图像的访问和显示。对颜色图像的支持由dcmimage模块提供,对JPEG压缩图像的支持由dcmjpeg模块支持。

主要接口类:

--DicomImage: dcmimgle/dcmimage模块提供接口类。主要目的是图像显示。在dcmimage.h中定义。

--DiDisplayFunction: Class to handle hardcopy and softcopy device characteristics file and manage display LUTs (for calibration). didispfn.h中定义。

可用工具:

--dcmdspfn: Export standard display curves to a text file

--dcod2lum: Convert hardcopy characteristic curve file to softcopy format

--dconvlum: Convert VeriLUM files to DCMTK display files

举例:

--载入一幅DICOM单帧单色图像,并显示其像素数据。

DicomImage *image = new DicomImage("test.dcm");

if (image != NULL)

{if (image->getStatus() == EIS_Normal)

 

 if (image->isMonochrome())

    {

      image->setMinMaxWindow();

      Uint8 *pixelData = (Uint8 *)(image->getOutputData(8 ));

      if (pixelData != NULL)

      {

       

      }

    }

} else

    cerr << "Error: cannot load DICOM image (" << DicomImage::getString(image->getStatus()) << ")" << endl;

}

delete image;

五、dcmimage程序包

dcmimage模块为dcmimgle模块提供对彩色图像的支持。对单色图像的支持由dcmimgle提供,对JPEG压缩图像的支持由dcmjpeg模块支持。

主要接口类:

--DicomImage: dcmimgle中已介绍。

工具:

--dcm2pnm: Convert DICOM images to PPM/PGM, PNG, TIFF or BMP

--dcmquant: Convert DICOM color images to palette color

--dcmscale: Scale DICOM images

举例:

--载入一幅DICOM单帧图像(单色或彩色),并显示其像素数据。

#include "diregist.h"  

 

DicomImage *image = new DicomImage("test.dcm");

if (image != NULL)

{

if (image->getStatus() == EIS_Normal)

{

    Uint8 *pixelData = (Uint8 *)(image->getOutputData(8 ));

    if (pixelData != NULL)

    {

     

    }

} else

    cerr << "Error: cannot load DICOM image (" << DicomImage::getString(image->getStatus()) << ")" << endl;

}

delete image;

六、dcmjpeg程序包

dcmjpeg提供了一个压缩/解压缩库以及可用工具。该模块包含一些类,可将DICOM图像对象在非压缩和JPEG压缩表示(传输协议)之间转换。无失真和有失真JPEG处理都被支持。这个模块实现了一族codec(编码解码器,由DcmCodec类派生而来),可以将这些codeccodec list中注册,codec list是由dcmdata模块保存的。

主要接口类:

--DJEncoderRegistration: 一个singleton(孤立)类,为所有支持的JPEG处理注册编码器。在djencode.h中定义。

--DJDecoderRegistration: 一个singleton(孤立)类,为所有支持的JPEG处理注册解码器。在djdecode.h中定义。

--DJCodecEncoder: JPEG编码器的一个抽象codec类。This abstract class contains most of the application logic needed for a dcmdata codec object that implements a JPEG encoder using the DJEncoder interface to the underlying JPEG implementation. This class only supports compression, it neither implements decoding nor transcoding. djcodece.h中定义。

--DJCodecDecoder: JPEG解码器的一个抽象codec类。This abstract class contains most of the application logic needed for a dcmdata codec object that implements a JPEG decoder using the DJDecoder interface to the underlying JPEG implementation. This class only supports decompression, it neither implements encoding nor transcoding.

工具:

dcmcjpeg: Encode DICOM file to JPEG transfer syntax

dcmdjpeg: Decode JPEG-compressed DICOM file

dcmj2pnm: Convert DICOM images to PGM, PPM, BMP, TIFF or JPEG

dcmmkdir: Create a DICOMDIR file

举例:

--用无失真JPEG压缩一幅DICOM图像文件。

DJEncoderRegistration::registerCodecs(); // register JPEG codecs

DcmFileFormat fileformat;

if (fileformat.loadFile("test.dcm").good())

{

DcmDataset *dataset = fileformat.getDataset();

DcmItem *metaInfo = fileformat.getMetaInfo();

DJ_RPLossless params; // codec parameters, we use the defaults

// this causes the lossless JPEG version of the dataset to be created

dataset->chooseRepresentation(EXS_JPEGProcess14SV1TransferSyntax, &params);

// check if everything went well

if (dataset->canWriteXfer(EXS_JPEGProcess14SV1TransferSyntax))

   // force the meta-header UIDs to be re-generated when storing the file

    // since the UIDs in the data set may have changed

    delete metaInfo->remove(DCM_MediaStorageSOPClassUID);

    delete metaInfo->remove(DCM_MediaStorageSOPInstanceUID);

    // store in lossless JPEG format

    fileformat.saveFile("test_jpeg.dcm", EXS_JPEGProcess14SV1TransferSyntax);

}

 DJEncoderRegistration::cleanup(); // deregister JPEG codecs

--解压缩一幅JPEG压缩的DICOM图像文件。

DJDecoderRegistration::registerCodecs(); // register JPEG codecs

DcmFileFormat fileformat;

if (fileformat.loadFile("test_jpeg.dcm").good())

{

DcmDataset *dataset = fileformat.getDataset();

// decompress data set if compressed

dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);

// check if everything went well

if (dataset->canWriteXfer(EXS_LittleEndianExplicit))

{

    fileformat.saveFile("test_decompressed.dcm", EXS_LittleEndianExplicit);

}

  DJDecoderRegistration::cleanup(); // deregister JPEG codecs

七、dcmnet程序包

dcmnet是一个网络库及可用工具。该模块包含了实现DICOM网络通信的所有函数集,即:DICOM上层有限状态机(DICOM Upper Layer Finite State Machine),关联控制服务元素(Association Control Service Element, ACSE)以及DICOM消息服务元素(DICOM Message Service Element, DIMSE)。

主要接口:该模块的主要接口包括在文件assoc.hdimse.h中定义的大量结构和函数。

--assoc.h: 这个文件包含程序,为DICOM应用提供关联管理。它维护描述活动关联的结构,提供对关联特定信息的访问。也提供程序帮助关联协议association negotiation(presentation contexts, abstract syntaxes, transfer syntaxes, maximum PDU length, and other extended negotiation)。该包使用了DICOM上层机制接收/发送关联请求/响应。每一个活动的关联由T_ASC_Association结构表示,包含了所有相关的信息。模块前缀ASC_

--dimse.h: 这个文件包含程序,为DICOM应用提供dimse层的服务。

工具:

--echoscu: DICOM verification (C-ECHO) SCU

--findscu: DICOM query (C-FIND) SCU

--movescu: DICOM retrieve (C-MOVE) SCU

--storescp: DICOM storage (C-STORE) SCP

--storescu: DICOM storage (C-STORE) SCU

--termscu: DICOM termination SCU

举例:

--一个简单的Echo SCU(Verification Service Class SCU)。大多数错误处理代码省去了,在Win32上的特定代码如WinSock初始化也省去了。

T_ASC_Network *net; // network struct, contains DICOM upper layer FSM etc.

ASC_initializeNetwork(NET_REQUESTOR, 0, 1000 , &net);

T_ASC_Parameters *params; // parameters of association request

ASC_createAssociationParameters(&params, ASC_DEFAULTMAXPDU);

// set calling and called AE titles

ASC_setAPTitles(params, "ECHOSCU", "ANY-SCP", NULL);

// the DICOM server accepts connections at server.nowhere.com port 104

ASC_setPresentationAddresses(params, "localhost", "server.nowhere.com:104");

// list of transfer syntaxes, only a single entry here

const char* ts[] = { UID_LittleEndianImplicitTransferSyntax };

// add presentation context to association request

ASC_addPresentationContext(params, 1, UID_VerificationSOPClass, ts, 1);

// request DICOM association

T_ASC_Association *assoc;

if (ASC_requestAssociation(net, params, &assoc).good())

{

if (ASC_countAcceptedPresentationContexts(params) == 1)

   // the remote SCP has accepted the Verification Service Class

    DIC_US // generate next message ID

    DIC_US status; // DIMSE status of C-ECHO-RSP will be stored here

    DcmDataset *sd = NULL; // status detail will be stored here

    // send C-ECHO-RQ and handle response

    DIMSE_echoUser(assoc, id, DIMSE_BLOCKING, 0, &status, &sd);

    delete sd; // we don't care about status detail

}

}

ASC_releaseAssociation(assoc); // release association

ASC_destroyAssociation(&assoc); // delete assoc structure

ASC_dropNetwork(&net); // delete net structure

八、dcmpstat程序包

dcmpstat: 一个描述状态(presentation state)库和可用工具。This module contains classes that implement a high-level API for the DICOM Softcopy Grayscale Presentation State Storage SOP Class. It also contains various support classes that are used by DICOMscope, a free DICOM viewer that has been developed as a demonstrator for presentation states. See http://dicom.offis.de/dscope

主要接口:

--DVPresentationState: 一个灰度软拷贝描述状态。这个类管理着一个描述状态对象的数据结构。描述状态可以创建、读、写和更改。在dvpstat.h中定义。

--DVInterface: 这个接口类是用来帮助软拷贝描述状态浏览器工作的。这个类管理着数据库机制,允许开始和停止网络交互,并访问图像和描述状态。dviface.h中定义。

--DVPSStoredPrint: the representation of a Stored Print object。在文件dvpssp.h中定义。

工具:

dcmmkcrv: Add 2D curve data to image

dcmmklut: Create DICOM look-up tables

dcmp2pgm: Read DICOM image and presentation state and render bitmap

dcmprscp: DICOM basic grayscale print management SCP

dcmprscu: Print spooler for presentation state viewer

dcmpschk: Checking tool for presentation states

dcmpsmk: Create DICOM grayscale softcopy presentation state

dcmpsprt: Read DICOM images and presentation states and render print job

dcmpsrcv: Network receive for presentation state viewer

dcmpssnd: Network send for presentation state viewer

举例:

--给一幅DICOM图像创建一个缺省的描述状态

DcmFileFormat infile;

DcmFileFormat outfile;

if (infile.loadFile("image.dcm").good())

{

DVPresentationState pstate; // presentation state handler

if (pstate.createFromImage(*infile.getDataset()).good())

{

    // serialize presentation state into DICOM data set structure

    if (pstate.write(*outfile.getDataset(), OFFalse).good())

    {

      // and write to file

      outfile.saveFile("gsps.dcm", EXS_LittleEndianExplicit);    

    }

}

}

--应用一个描述状态中的灰度变换管道给一幅DICOM图像

DcmFileFormat imagefile;

DcmFileFormat gspsfile;

if (imagefile.loadFile("image.dcm").good() &&

    gspsfile.loadFile("gsps.dcm").good())

{

DVPresentationState pstate; // presentation state handler

if (pstate.read(*gspsfile.getDataset()).good()) // parse gsps object

{

    // attach presentation state to image data

    if (pstate.attachImage(&imagefile, OFFalse).good())

    {

      const void *pixel; // pointer to pixel data, one byte per pixel

      unsigned long width;   // width of image bitmap

      unsigned long height; // height of image bitmap

      if (pstate.getPixelData(pixel, width, height).good())

      {

       

      }

      pstate.detachImage(); // release connection between GSPS and image

    }

}

}

九、dcmsign程序包

dcmsign是一个数字签名库和可用工具。这个模块包含了一些类,以创建DICOM数据集中的数字签名,并验证和删除签名。这个模块需要扩展的OpenSSL库的支持。

主要接口:

--DcmSignature: this class provides the main interface to the dcmsign module - it allows to create, examine and verify digital signatures in DICOM datasets or items. The methods in this class do not handle digital signatures embedded in sequence items within the dataset, other than providing helper functions that allow to locate and attach the sub-items separately. dcsignat.h中定义。

--SiSecurityProfile: 所有安全框架的抽象基类。abstract base class for all security profiles.sisprof.h文件中定义。

--SiCertificate: a class representing X.509 public key certificates. sicert.h文件中定义。

--SiPrivateKey: a class representing a private key. siprivat.h文件中定义。

--SiMAC: a base class for all classes that implement hash functions. simac.h文件中定义。

工具:

dcmsign: Sign and Verify DICOM Files

举例:

--验证一个DICOM文件中的所有签名。

DcmFileFormat fileformat;

if (fileformat.loadFile("test.dcm").good())

{

int counter = 0;          // counts the signatures in the DICOM file

int corrupt_counter = 0; // counts signatures that failed verification

DcmDataset *dataset = fileformat.getDataset();

DcmStack stack;           // stores current location within file

DcmSignature signer;      // signature handler

DcmItem *sigItem = DcmSignature::findFirstSignatureItem(*dataset, stack);

while (sigItem) // browse through items that contain digital signatures

   signer.attach(sigItem); // each item may contain multiple signatures

    for (unsigned long l=0; l < signer.numberOfSignatures(); ++l)

    {

      if (signer.selectSignature(l).good())

      {

        ++counter;

        if (signer.verifyCurrent().bad()) // verify signature

           corrupt_counter++;

      }

    }

    signer.detach();

    sigItem = DcmSignature::findNextSignatureItem(*dataset, stack);

}

if (counter == 0)

      cerr << "no signatures found in dataset." << endl;

else

      cerr << counter << " signatures verified in dataset, "

           << corrupt_counter << " corrupted." << endl;

}

--给一个DICOM文件增加签名。

DcmFileFormat fileformat;

if (fileformat.loadFile("test.dcm").good())

{

DcmDataset *dataset = fileformat.getDataset();

SiCreatorProfile profile; // select the "RSA Creator Profile"

SiRIPEMD160 mac;           // use RIPEMD160 as MAC algorithm

DcmSignature signer;       // signature handler

SiCertificate cert;        // our certificate

if (cert.loadCertificate("certificate.pem", X509_FILETYPE_PEM).bad())

{

    cerr << "unable to load certificate" << endl;

    return;

}

SiPrivateKey key; // private key, must be unencrypted here

if (key.loadPrivateKey("privkey.pem", X509_FILETYPE_PEM).bad())

{

    cerr << "unable to load private key" << endl;

    return;

}

signer.attach(dataset); // connect handler to data set

if (signer.createSignature(key, cert, mac, profile).good())

{

    fileformat.saveFile("test_signed.dcm"); // write back

}

}

十、dcmsr程序包

dcmsr是一个结构化报表库和可用工具。这个模块包括一些类来读、写、创建、修改、访问、打印和显示DICOM结构化报表文档。所支持的SOP类列表由DSRTypes::E_DocumentType提供。

主要接口:

--DSRDocument: Interface class for 'dcmsr' (DICOM Structured Reporting Documents). This class supports reading, writing, creation, printing and rendering of DICOM SR documents (according to DICOM PS 3.x-2004, formerly known as Supplement 23). The list of supported SOP classes is available in file "dsrtypes.h". dsrdoc.h中定义。

--DSRDocumentTree: 管理SR文档树的类。在dsrdoctr.h中定义。

--DSRContentItem: Interface class for content items. This class allows to access the document tree nodes without using any pointers. dsrcitem.h中定义。

--DSRCodedEntryValue: Class for coded entry values. dsrcodvl.h中定义。

工具:

dsr2html: Render DICOM SR file and data set to HTML

dsr2xml: Convert DICOM SR file and data set to XML

dsrdump: Dump DICOM SR file and data set

xml2dsr: Convert DICOM SR file and data set to XML

举例:

--载入一个DICOM结构化报表,并以HTML格式显示其内容。

DcmFileFormat fileformat;

OFCondition status = fileformat.loadFile("test.dcm");

if (status.good())

{

DSRDocument document;

status = document.read(*fileformat.getDataset());

if (status.good())

{

    status = document.renderHTML(cout);

    if (status.bad())

      cerr << "Error: cannot render SR document (" << status.text() << ")" << endl;

} else

    cerr << "Error: cannot parse SR document (" << status.text() << ")" << endl;

} else

cerr << "Error: cannot read DICOM file (" << status.text() << ")" << endl;

--创建一个DICOM结构化报告,并将其存为文件。

DSRDocument document;

document.setPatientsName("Doe^John");

 

document.getTree().addContentItem(DSRTypes::RT_isRoot, DSRTypes::VT_Container);

document.getTree().getCurrentContentItem().setConceptName(DSRCodedEntryValue());

document.getTree().addContentItem(DSRTypes::RT_hasObsContext, DSRTypes::VT_Code, DSRTypes::AM_belowCurrent);

 

DcmFileFormat fileformat;

OFCondition status = document.write(*fileformat.getDataset())

if (status.good())

{

status = fileformat.saveFile("test.dcm", EXS_LittleEndianExplicit);

if (status.bad())

    cerr << "Error: cannot write DICOM file (" << status.text() << ")" << endl;

} else

cerr << "Error: cannot write SR document (" << status.text() << ")" << endl;

--读取文档树的属性,并直接修改。

DSRDocument document(DSRTypes::DT_KeyObjectDoc);

 

document.getTree().addContentItem(DSRTypes::RT_isRoot, DSRTypes::VT_Container);

DSRCodedEntryValue *codePtr = document.getTree().getCurrentContentItem().getConceptNamePtr();

if (codePtr != NULL)

codePtr->setCode("113000", "DCM", "Of Interest");

 

document.getTree().addContentItem(DSRTypes::RT_contains, DSRTypes::VT_Image);

DSRImageReferenceValue *imagePtr = document.getTree().getCurrentContentItem().getImageReferencePtr();

if (imagePtr != NULL)

{

imagePtr->setValue(DSRImageReferenceValue(UID_UltrasoundMultiframeImageStorage, ));

imagePtr->setPresentationState(DSRCompositeReferenceValue(UID_GrayscaleSoftcopyPresentationStateStorage, ));

imagePtr->getFrameList().addItem(2);

imagePtr->getFrameList().addItem(5);

}

 

十一、dcmtls程序包

dcmtls是网络库的安全扩展This module contains classes that implement DICOM network communication tunneled through a Transport Layer Security (TLS) connection, conforming to the DICOM "Security Enhancements One" extension (formerly Supplement 31). This module requires the external OpenSSL library.

主要接口:

--DcmTLSTransportLayer: factory class which creates secure TLS transport layer connections and maintains the parameters common to all TLS transport connections in one application (e.g. the pool of trusted certificates, the key and certificate to be used for authentication and the list of ciphersuite to be used for association negotiation.)。在tlslayer.h文件中定义。

--DcmTLSConnection: this class represents a TLS (Transport Layer Security) V1 based secure transport connection.

举例:

--TLS的关联请求应用

T_ASC_Network *net;        // network initialization code not shown,

T_ASC_Parameters *params; // we just assume these pointers to be valid

// create TLS object that initializes the random generator through a file

// "random.dat" containing random data (1 kByte is sufficient).

DcmTLSTransportLayer *tLayer = new DcmTLSTransportLayer(

DICOM_APPLICATION_REQUESTOR, "random.dat");

if (TCS_ok != tLayer->setPrivateKeyFile("privkey.pem", SSL_FILETYPE_PEM))

{

cerr << "unable to load private key" << endl;

return;

}

if (TCS_ok != tLayer->setCertificateFile("certificate.pem", SSL_FILETYPE_PEM))

{

cerr << "unable to load certificate" << endl;

return;

}

// enable the TLS_RSA_WITH_3DES_EDE_CBC_SHA ciphersuite

tLayer->setCipherSuites(SSL3_TXT_RSA_DES_192_CBC3_SHA);

// accept any certificate from the remote site (not recommended)

tLayer->setCertificateVerification(DCV_ignoreCertificate);

// register and activate TLS layer

ASC_setTransportLayer(net, tLayer, 1);

ASC_setTransportLayerType(params, 1);

十二、dcmwlm程序包

dcmwlm是一个设备工作表(Modality Worklist)数据库服务器。这个模块包含类,作为一个SCP,为实现DICOM Modality Worklist Management Service的应用提供支持。基于这些类的SCP可以从C-Find-RSP返回消息中找到相关信息。

主要接口:

--WlmActivityManager: This class encapsulates data structures and operations for basic worklist management service class providers. wlmactmg.h文件中定义。

--WlmDataSource: This class encapsulates data structures and operations for connecting to an arbitrary data source in the framework of the DICOM basic worklist management service. wlds.h文件中定义。

--WlmDataSourceFileSystem: This class encapsulates data structures and operations for connecting to a file-based data source in the framework of the DICOM basic worklist management service. wldsfs.h文件中定义。

--WlmFileSystemInteractionManager: This class encapsulates data structures and operations for managing data base interaction in the framework of the DICOM basic worklist management service.wlfsim.h文件中定义。

工具:

wlmscpfs: DICOM Basic Worklist Management SCP (based on data files)

举例:

For an example of how to use the main interface classes of this module, see file 'wlmscpfs.cc' (containing the main function of the corresponding tool) and file 'wlcefs.cc' (making use of the WlmActivityManager class that manages all activities a corresponding SCP has to manage).

十三、dcmqrdb程序包

dcmqrdb是一个图像数据库服务器This module contains a simple image archive that manages a number of storage areas and allows images to be stored in these storage areas using the DICOM Storage Service Class. It also allows image attributes to be queried and images to be retrieved using the DICOM Query/Retrieve Service Class.

工具:

dcmqridx: Register a DICOM image file in an image database index file

dcmqrscp: DICOM image archive (central test node)

dcmqrti: The Terminal Initiator Telnet Client Program

文件:下面文件提供进一步信息。

--dcmqrcnf.txt:

http://blog.csdn.net/diqiucun666/archive/2008/09/02/2866908.aspx

基于DCMTKDICOM相关程序编写攻略

前言:

       由于现在的医学影像设备的图像存储和传输正在逐渐向DICOM标准靠拢,在我们进行医学图像处理的过程中,经常需要自己编写和DICOM格式的图像相关的各种程序模块,以完成自己处理功能。如果从头开始理解DICOM的协议,然后完全自己编写这些代码来实现这些协议,是一件工程浩大的事情。德国offis公司开发的DCMTK,为我们提供了实现DICOM协议的一个平台,使得我们可以在它的基础上轻松的完成自己的主要工作,而不必把太多的精力放在实现DICOM协议的细节问题上。本文以WINDOWSXP+VC6.0为开发平台,从DCMTK的下载、调试、使用等方面介绍基于DCMTKDICOM相关程序编写过程。

三、基于DCMTKDICOM相关程序

       1、建立自己的应用工程并将其加入DCMTK的工作空间(Project>Insert Project To WorkSpace)。

       2、在自己的工程中加入需要使用的DCMTK静态库和头文件。

       按照正确的顺序填写链接库文件(对于链接库来说顺序也是很重要的!) DCMTK库有下面这些依赖关系:

       dcmdata -ofstd

       dcmimage -dcmimgle, dcmdata, ofstd

       dcmimgle -dcmdata, ofstd

       dcmjpeg -ijg8, ijg12, ijg16, dcmimage, dcmimgle, dcmdata, ofstd

       dcmnet -dcmdata, ofstd

       dcmpstat -dcmimage, dcmimgle, dcmsign, dcmsr, imagectn, dcmtls, dcmnet, dcmdata,   ofstd

       dcmsign -dcmdata, ofstd

       dcmsr -dcmdata, ofstd

       dcmtls -dcmnet, dcmdata, ofstd

       dcmwlm -dcmnet, dcmdata, ofstd

       imagectn -dcmnet, dcmdata, ofstd

       3、编译自己的工程。

4、不成功的可能有如下一些问题。

       1)对于单文档工程,运行时提示:

       generated debug assertion

       if (!ProcessShellCommand(cmdInfo))

       return FALSE;

       -- File: docsingl.cpp

       Line: 215

原因:缺少AFX_IDS_UNTITLED的字符串

解决:在资源文件的STRING TABLE中加入 ID= AFX_IDS_UNTITLED的键值。

       2)编辑时提示:

fatal error C1189: #error :  Please use the /MD switch for _AFXDLL builds

原因:

解决:在Project>Settings>Preprocessor definitions中去掉 _AFXDLL项。

       3)编译时提示:

LIBCMTD.lib(dbgheap.obj):error LNK2005: _malloc already defined in msvcrtd.lib(MSVCRTD.dll)

原因:由于VC在编译时试图从两个不同的库中写入同一个函数代码,只要忽略这些相冲突的默认库就可以解决。

解决:在Project>Settings>Link>Category>Input—>Ignore Liberies中加入

相应的msvcprtd.lib,msvcrtd.lib等。

另一种解决:保证自己的编译环境为:

       Aproject->settings->c/c++->category->code generationuse run-time library debug multithreaded.

       B) Project>Settings>Preprocessor definitions中去掉 _AFXDLL项。

       C)如果需要,在资源文件的STRING TABLE中加入 ID= AFX_IDS_UNTITLED的键值。

       D)这样就不需要忽略那些库了。

还有可以:

后来尝试了很多方法,经过摸索,最后得出解决方法:

Preprocessor中定义_AFXDLL

如果它提示:fatal error C1189: #error : Please use the /MD switch for _AFXDLL builds

就这样改:

C/C++->Code Generation->Multithread DLL (即实现/MD选项)

       4)编译时提示:

dcmdata.lib(dcuid.obj) : error LNK2001: unresolved external symbol _Netbios@4

原因:缺少所需的链接库文件

解决:在自己的工程中加入需要使用的DCMTK静态库和头文件,并注意顺序。

       5)编译时提示:

fatal error LNK1181: cannot open input file。。。。。。。 Debug.obj

原因:类似错误,可能是由于目录中含有太长的中文名字。

解决:可以将中文的目录名改为英文就OK

       6)运行时提示:

原因:

解决:在Project>Settings>Preprocessor definitions中去掉 _AFXDLL项。

       7)如果将已经make好的dcmtk目录复制到其它目录,则需要重新make,否则会出现一大堆路径错误

四、一个实际的例子

       我们经常使用的DICOM主要是读和写DICOM文件。下面以读写DICOM为例DCMTK的使用方法。

五、其它一些相关的问题

       1、把DCMTK当作静态库使用:DCMTK本身提供的是静态库即Lib,在链接进自己的工程时会将全部的函数加入,因此造成可执行文件很大,而且不便于升级;如果需要使用DCMTK作为动态库链接,需要自己建立相应的Dll工程,然后把原来的程序文件加进来,再写一个导出函数的文件,这些导出函数保持固定。这样其它地方使用的时候不会因为动态库程序升级又重新编译。

 

编译DCMTK

也许是DCMTK的开发人员认为DCMTK是一个专用的库,没有必要做成动态链接库。也许是DCMTK的开发人员认为DCMTK需要跨平台,做成动态链接库就平台相关了,违背了跨平台的本意。
   
 所以,如此两个也许只有,DCMTK经过编译之后总是得不到DLL文件。DCMTK到底能不能编译成DLL来使用呢?反正我是失败了。你来试试。不过在编译DCMTK的过程中也解决了不少棘手问题。不感独享。
   
 我用的编译环境是VC8(就是VS2005)里面的VC。我们编译的目的是使DCMTK能和我们现在使用的MFC协作良好。我们学习都是螺旋渐进式学习的(提出结论,然后推翻结论,得出新结论),我们也这样。

结论一:
   
 使用VC编写程序,总是离不开字符串,需要使用字符串CString。而VC呢?又支持各种不同的字符串编码。那么CMake创建的DCMTK项目,里面默认的什么字符串编码呢?好象VC8向导创建的项目,默认就是Unicode,而不是MBCS哦。正好相反,需要注意。CMake创建的DCMTK项目里面都使用的MBCS字符集。那么以后使用VC编写DCMTK应用程序的时候,还是使用MBCS字符集吧。毕竟DCMTK太大了,修改起来不太方便。以后,我们就不讨论Unicode版本的应用程序啦,只考虑使用MBCS的应用程序。

结论二:
    DCMTK
只能编译成静态连接库。
   
 为什么呢?因为我们按照默认的办法编译DCMTK之后,找不到任何DLL哦。全部是一大堆的LIB文件和.H文件。

结论三:
    VC
书写的程序都需要使用C语言运行库(里面实现了标准的C语言函数哦,比如IOSTREAM等东西)

结论四:
    C
语言运行库的引如方式有两种。与MFC的用法一样,有动态链接的方式和静态链接的方式。也就是编译参数/MT /MTd /MD /MDd负责控制啦。
    /MT
负责链接发行版的C语言运行库(静态链接)
    /MTd
负责链接调试版的C语言运行库(静态链接)
    /MD
负责链接发行版的C语言运行库(动态链接)
    /MDd
负责链接调试版的C语言运行库(动态链接)
   
   
 我们都知道动态链接MFC的时候,应用程序运行时会要求提供MFC对应的DLL
    VC6
的需要MFC4.2DLL。调试版的程序会自动引入MFC42D.DLL。发行版的会自动引入MFC42.DLLMFC42U.DLL呢?(那是Unicode版本的MFC,前面都说啦,不讨论了)VC8提供的DLL形式的MFC又是另外一个模样,叫做MFC80.DLL

结论五:
  DCMTK使用的是静态链接方式链接语言运行库。使用默认的DCMTK编译参数,生成的LIB文件。在使用的时候,需要注意以下问题。
    ①
需要设置项目的字符类型为MBCS,可以判断有没有定义文本宏_MBCS
    ②
需要设置项目使用语言运行库的方法为静态链接,可以判断有没有定义文本宏(_MDd,_MD,_MT,MTt)
    ③
需要静态链接MFC,可以判断有没有定义文本宏(_AFXDLL)
    ④
需要链接很多库,可以通过在源文件中编写代码实现, 提高重用度
         #pragma comment(lib,"ofstd")
         #pragma comment(lib,"dcmdata")
         #pragma comment(lib,"dcmtls")
         #pragma comment(lib,"dcmnet")
         #pragma comment(lib,"dcmqrdb")
         #pragma comment(lib,"dcmimgle")
         #pragma comment(lib,"dcmimage")
         #pragma comment(lib,"dcmjpeg")
         #pragma comment(lib,"ijg8")
         #pragma comment(lib,"ijg12")
         #pragma comment(lib,"ijg16")
         #pragma comment(lib,"dcmdsig")
         #pragma comment(lib,"dcmsr")
         #pragma comment(lib,"dcmpstat")
         #pragma comment(lib,"dcmwlm")
         #pragma comment(lib,"netapi32")
         #pragma comment(lib,"wsock32")
 

结论六:
   
 按照结论五编写出来的应用程序是很大的。但是独立性和完整性都最好。我写了个简单的Demo。调试版的大小是4.05M,发行版的大小是1.10M。但是复制到什么地方都可以运行,根本不需要其它额外的动态连接库。

结论七:
   
 有时必须调整DCMTK链接语言运行库的方式(变静态链接为动态链接)
   
 比如,要写扩展MFCDLL时,就必须采用动态链接的方式使用MFCVC要求,采用动态链接的方式使用MFC就必须采用用动态链接的方式使用语言库。如果DCMTK采用静态方式链接语言库,使用DCMTK的程序采用动态方式链接,那么就会出现语言库中的符号符号重复定义的情况。怎么办呢?能不能在最后链接的时候在统一指定语言库的使用方式呢?这个我测试了,好象不行。哪位大侠搞定了通知我下。我采用的办法是重新编译DCMTK。修改其语言库链接方式。如何操作呢?
   
 替换所有CMakeLists.txt中的/MT/MD (根据经验其实也就根目录下的CMakeLists.txt中存在)。重新编译DCMTK。这样一来,我们的程序就可以采用动态链接的方式使用MFC了。
   
 这次编译出来的程序,大小还可以。我把需要用到的功能用MFC扩展的DLL进行了包装。调试版本的DCMTK.DLL大小为1.92M;发行版本的DCMTK.DLl大小为884K;调试版本的Demo.EXE文件大小为152K;发行版本的Demo.EXE大小为48K。其实DCMTK.DLL的维护完全可以安排一个专人负责。一般调试完毕之后,变化是很少的。如此以来,调试程序的时候,编译速度快多了
转载自:http://blog.csdn.net/jackmacro/archive/2010/02/27/5332665.aspx

 

有关DCMTK的基础知识

来自:PACS专家网  (http://www.pacser.net/index.asp)
作者:jayition由于我对worklist的了解只是初步的,所以在我说的有错误之处还要大家多多指点;worklist我把它译为工作列表,它的作用是方便检查医师,减少检查医师的输入;病人信息在登记过之后就会产生一个列表,是所有没有经过拍片的病人信息列表,并提供一个服务,供设备来调用、当病人拿着一个登记号之类的编码到相对应的拍片室之后,拍片医师会将一小部分信息输入(例如登记号)、
设备根据登记号向SCP提出请求(C-FIND),
SCP
根据请求内容查找列表,将相应的信息返回给设备。
设备读取病人信息-》拍片-》。。。。。。(后面的事与工作列表服务关系就不太大了)
以上这些是它的大概流程!

由于我主要是用DCMTK进行研究,所以说明一下它现在所支持的一些信息:
在请求中,它支持以下字段(我用数据库来理解DICOM的,所以我习惯叫它们字段)
(0008,0050) AccessionNumber
 工作流号
(0008,0090) ReferringPhysiciansName
 提交人(医师)名字
(0010,0010) PatientsName
 病人姓名
(0010,0020) PatientID
 病人ID
(0010,0040) PatientsSex
 病人性别
(0032,1032) RequestingPhysician
 请求医师
(0038,0010) AdmissionID
 允许进入ID
(0040,0100) ScheduledProcedureStepSequence
 安排程序序列
(0008,0060) > Modality
 设备样式(设备名:如CTCR等)
(0040,0001) > ScheduledStationAETitle
 预定工作站的应用实体名
(0040,0002) > ScheduledProcedureStepStartDate
 预定的操作日期
(0040,0003) > ScheduledProcedureStepStartTime
 预定的操作时间
(0040,0006) > ScheduledPerformingPhysiciansName
 检查医生姓名
(0040,1001) RequestedProcedureID
 被请求的程序ID
(0040,1003) RequestedProcedurePriority
 被请求过程的优先权

支持以下返回信息:
(0008,0005) SpecificCharacterSet

(0008,0050) AccessionNumber
(0008,0080) InstitutionName
(0008,0081) InstitutionAddress
(0008,0090) ReferringPhysiciansName
(0008,1080) AdmittingDiagnosesDescription

(0008,1110) ReferencedStudySequence
(0008,1150) > ReferencedSOPClassUID
(0008,1155) > ReferencedSOPInstanceUID
(0008,1120) ReferencedPatientSequence
(0008,1150) > ReferencedSOPClassUID
(0008,1155) > ReferencedSOPInstanceUID
(0010,0010) PatientsName
(0010,0020) PatientID
(0010,0030) PatientsBirthDate
(0010,0040) PatientsSex
(0010,1000) OtherPatientIDs
(0010,1001) OtherPatientNames
(0010,1020) PatientsSize
(0010,1030) PatientsWeight
(0010,1040) PatientsAddress
(0010,1080) MilitaryRank
(0010,2000) MedicalAlerts
(0010,2110) ContrastAllergies
(0010,2160) EthnicGroup
(0010,21a0) SmokingStatus
(0010,21b0) AdditionalPatientHistory
(0010,21c0) PregnancyStatus
(0010,21d0) LastMenstrualDate
(0010,4000) PatientComments
(0020,000d) StudyInstanceUID
(0032,1032) RequestingPhysician
(0032,1033) RequestingService
(0032,1060) RequestedProcedureDescription
(0032,1064) RequestedProcedureCodeSequence
(0008,0100) > CodeValue
(0008,0102) > CodingSchemeDesignator
(0008,0103) > CodingSchemeVersion
(0008,0104) > CodeMeaning
(0038,0010) AdmissionID
(0038,0011) IssuerOfAdmissionID
(0038,0050) SpecialNeeds
(0038,0300) CurrentPatientLocation
(0038,0500) PatientState
(0040,0100) ScheduledProcedureStepSequence
(0008,0060) > Modality
(0032,1070) > RequestedContrastAgent
(0040,0001) > ScheduledStationAETitle

(0040,0002) > ScheduledProcedureStepStartDate
(0040,0003) > ScheduledProcedureStepStartTime
(0040,0004) > ScheduledProcedureStepEndDate
(0040,0005) > ScheduledProcedureStepEndTime

(0040,0006) > ScheduledPerformingPhysiciansName
(0040,0007) > ScheduledProcedureStepDescription
(0040,0008) > ScheduledProtocolCodeSequence
(0008,0100) > > CodeValue
(0008,0102) > > CodingSchemeDesignator
(0008,0103) > > CodingSchemeVersion
(0008,0104) > > CodeMeaning
(0040,0009) > ScheduledProcedureStepID
(0040,0010) > ScheduledStationName
(0040,0011) > ScheduledProcedureStepLocation
(0040,0012) > PreMedication
(0040,0020) > ScheduledProcedureStepStatus
(0040,0400) > CommentsOnTheScheduledProcedureStep
(0040,1001) RequestedProcedureID
(0040,1002) ReasonForTheRequestedProcedure
(0040,1003) RequestedProcedurePriority
(0040,1004) PatientTransportArrangements
(0040,1005) RequestedProcedureLocation
(0040,1008) ConfidentialityCode
(0040,1009) ReportingPriority
(0040,1010) NamesOfIntendedRecipientsOfResults
(0040,1400) RequestedProcedureComments
(0040,2001) ReasonForTheImagingServiceRequest
(0040,2004) IssueDateOfImagingServiceRequest
(0040,2005) IssueTimeOfImagingServiceRequest
(0040,2008) OrderEnteredBy
(0040,2009) OrderEnterersLocation
(0040,2010) OrderCallbackPhoneNumber
(0040,2016) PlacerOrderNumberImagingServiceRequest
(0040,2017) FillerOrderNumberImagingServiceRequest
(0040,2400) ImagingServiceRequestComments
(0040,3001) ConfidentialityConstraintOnPatientDataDescription

好的,下面为了更好地理解worklist服务,如果有一个环境来大概看一下!所以我用DCMTK中的例子程序来看这个过程,(由于我也没有看见过正真的环境,经过mouse及各位网友指点我就先把我理解的写出来了!如有错误望指点!)

在设备端可以理解为一个findscu程序(是DCMTK中的例程。)而响应这个程序的服务是wlmscpfs--相当于服务器端!findscu发送一个请求到服务器端wlmscpfswlmscpfs通过文件数据服务器(暂时这么叫,也可以叫文件数据库,因为它的每一条记录对应于一个文件!)查找,并返回结果!
接下来,为了更清晰这个结构,我将编译好的dump2dcm.exefindscu.exewlmscpfs.exe,还有dcmtk目录下的\\dcmwlm\\wlistdbdcmwlm\\wlistqry拷到一个自已定义的目录下,这里假设是d:\\dcmtk,再按这两个文件夹里面的readme的要求,将原来的dump文件转化为dcm格式,扩展名分别为wldcm,这个说明里已经讲清楚了!
然后我先跟大家作好约定吧!我存放文件的目录为:d:\\dcmtk目录下有:findscu.exewlmscpfs.exe文件;
d:\\dcmtk\\wlistdb
d:\\dcmtk\\wlistqry下面分别为wldcm文件。
打开两个命令行对话框:
1
。一个里面输:wlmscpfs -v -dfp "d:\\dcmtk\\wlistdb" 123
注释:-v是显示详细情况的,-dfp是指定文件数据库(也就是wl文件)路径的!123是端口号!
2.
另一个里面输:findscu -v --call OFFIS -k 0010,0010="ABC" -k 0010,0020="00001" -k 0010,0030 127.0.0.1 123
注释:相同的参数就不多说了,--call是用来指定AE Title的,而在这个程序里面AE Title指的是d:\\dcmtk\\wlistdb目录下的一个目录名,而您可以看一下,里面是一个OFFIS目录,如果目录改名,AE Title也要跟着改!-k是用于指定关键字的:比如 -k 0010,0010="ABC"表示要查找病人姓名(tag0010,0010)为ABC的病人,而您一定会注意到-k 0010,0030后面没有参数,那表示这条记录需要返回!(这是我的理解!)而127.0.0.1wlmscpfs所在机器的IP地址,而123wlmscpfs开的端口也就是在wlmscpfs -v -dfp "d:\\dcmtk\\wlistdb" 123中的123
这样,只要您在关键字中输入正确的关键字,就会返回正确结果!
如果想知道库里面有哪些信息,可以用文本编辑器打开d:\\dcmtk\\wlistdb文件夹中的dump文件,里面是相关wl文件的内容,当然,你也可以用dcm文件查看器看一下wl文件的内容,因为它就是dcm文件!

http://blog.sina.com.cn/s/blog_60efd3f50100e63s.html

DICOM文件格式与编程 (2009-07-06 10:23:47)   标签:杂谈 

任何图像文件格式无非是由两个部分组成:存参数的 header 和图点数据(pixel data)
BMP
 JPEGTIFF 之类的格式的 header 只描述图像的基本参数:如几行、几列、每点用了几位、有没有压缩、调色板等等。Header 往往是固定长度的。
而医疗影像还要许多其它参数,如病人基本资料、检验基本资料、系列资料、位置资料等等。而且每种Modality 和每种 image 所需要的内容不一样。因此,一般的图像格式不能使用。

一、DICOM  4 个内容层次
1
 Patient (病人)
2
 Study   (检验)
3
 Series   (系列)
4
 Image   (图像)
尽管头几层的内容在很多图像里是相同的,但它们在每个图像文件里都要有。
每一层叫一个Information EntityIE(从relational database schema 设计引用而来)。每一层又细分成Module。每个Module里面的最小单元叫做一个attributeelement
现在举个例子:CR 图像 (DICOM Part 3, A.2.3, Table A.2-1)
1. Patient IE:
   a. Patient Module (
参考 C.7.1.1)
2. Study IE:
   a. Study Module    (
参考 C.7.2.1)
   b. Patient Study Module (
参考 C.7.2.2)
3. Series IE:
   a. General Series (
参考 C.7.3.1)
   b. CR Series (
参考C.8.1..1)
   c. General Equipment (
参考 C.7.5.1)
4. Image IE:
   a. Genrral Image (C.7.6.1)
   b. Image Pixel (C.7.6.3)
   c. Contrast/bolus (C.7.6.4)
   d. CR Image (C.8.1.2)
    ...
   i. SOP Common (C.12.1)
将这些 modules (tables) 里的所有 elements 都找出来就做成了一个 CR 图像的架构。
要注意的是这些 module 有些是一定要的(modatory) 有些是用户选用的 (user)。到了每个 module attribute/element 表又有分五类 Type 1, 1C, 2, 2C  3 Type 1 是一定要的,2 也是一定要的但是内容可以是空的。Type 3 则可要可不要。所以浓缩一下,一个 CR 图像里的元素(elements) 也不是太多。把这些表格展开后,这些 elements 组成一个 dataset
那么,写到一个文件里或通过网路传送又是个什么格式呢?这就要看 Part 5。一个元素(element) 的结构是:
1. group tag: 16-bit
2. element tag: 16-bit
3. length (or VR/length): 32-bit
4. data (bytes of length)

DicomElem CRDataSet [] =
{
{ 0x0008, 0x0005, "CS", 10, "ISO_IR 100"},
{ 0x0008, 0x0008, "CS", 16
 "ORIGINAL\\PRIMARY"},
...
{0x0010, 0x0010, "PN", 16, "My^Test^Image^^ "}

{0x0010, 0x0020, "SH", 6, "123456"}

...
{0,0,"",0,""}
};

void WriteCDImage(FILE *fp)
{
DicomElem elem = CRDataSet[0];
unsigned long int lComboTag;
int nCols, nRows;
unsigned char *pPixelData
unsigned long int lPixelLength;

pPixelData = LoadBMPImgeData("MyImage.bmp", nCols, nRows, lPixelLength);

while(CRDataSet[i].group_tag)
{
     lComboTag = (CRDataSet[i].group_tag << 16) | (CRDataSet[i].element_tag<<16);
    switch(lComboTag)
     {

       case 0x00280010:
           *((short int *)CRDataSet[i].data) = nCols;
        break;
       case 0x00280011:
           *((short int *)CRDataSet[i].data) = nRows;
       break;
       ...
     }
    
   // Write group and element tag
   fwrite(&lComboTag, 1, sizeof(long), fp);

   // Write VR
   fwrite(CRDataSet[i].VR, 1, 2, fp);

   if (lComboTag != 0x7fe00010)
     {
        fwrite(CRDataSet[i].length, 1, sizeof(short), fp);
        fwrite(CRDataSet[i].data, 1, CRDataSet.length, fp);
      }
   else
       {
          fwrite("\0\0", 1, 2, fp); // Two blank bytes after VR
          fwrite(&lPixelLength, 1, sizeof(long), fp); // Length
         fwrite(pPixelData, 1, lPixelLength, fp);  
       }

i++;

}

}

unsigned char *LoadBMPImgeData(char *fileName, int &nCols, int &nRows, unsigned long &lPixelLength)
{
....
}

细节自己去写。

二、DICOM文件读写最难的是两件事情
    DICOM Sequence
    DICOM Pixel Data
Sequence
  C 里的类比是一个 structure  array,是结构套结构,所以读起来难。更麻烦的是 Sequence还可以不定义长度, 即长度是 -1。要靠你自己去找 (FFFE, E0DD) 来决定 Sequence 是否结束。
Array
 里面的每个 structure就是 DICOM Sequence 里的一个 ItemItem 的开头是一个特定 element (FFFE, E000) 如果 Item 的长度是 -1 要靠找到 (FFFE, E00D) 来决定 Item 的结束。
Pixel Data (7fe0, 0010)
 是一个特殊的 DICOM element。总是在所有元素的最后面。它与 Sequence 有两个相似的地方:长度区总是 32- ( explicit VR 的情况下要在 VR 区后面填两个 bytes,然后再加 4-bytes 的长度。
   
 如图像是压缩的,每幅图用一个 item 来存。 第一个 item 是个 offset table。每幅图的 offset 是一个dword (4 bytes),第一幅图的 offset  0

三、DICOM file 细节问题
    element
  data 长度一定要是一个偶数。
   
 要注意 big endian  little endian 的区别。软件要自动判断机器的 endian  DICOM 文件的transfer syntax。常见的:Intel PC  little endian Sun  big endian
如果机器的 endian 和数据的 endian 不对的话要做 byte swap。要做 byte swap 的有所有的根数字有关的binary data,  VR = SS, US, SL, UL, FL, FD, OW, AT 等等。DICOM group tag  element tag 也要分别做两个 byte  swapItem 指的是 DICOM Sequence  Item

0x7fe0, 0x0010, "OB", ' '' ', 0xffffffff // tag, VR, 长度
0xFFFE, 0x0000, 0xffffffff // Offset item begins
0x00000000 // Offset of first image = 0
0x00001000 // Offset of second image = 4096 (for example)
...
0xFFFE, 0xE00D // End of offset item
0xFFFE, 0x0000, 0x00001000   // first image item and length = 4096 (for example)
... // (4096 bytes)
0xFFFE, 0xE00D // end of first image
...
0xFFFE, 0xE0DD // End o of pixel data sequence

对应每一个用到的 element DICOM 标准 Part 6 都定义了一个 group tag  element tag。比如说:
patient name: 0x0010, 0x0010
patient ID:    0x0010, 0x0020
...
VR
说的是element格式,比如说patinet name VRPN。格式是last_name^first_name^middle_name^prefix^surfix。那么我的英文名字就是: Wang^JB^^Dr.^
往外写时要做几个事情:
1.
 要把所有元素按 group tage  element tag 理一遍 (sort)。从小排到大。
2.
 如果是写 DICOM 介质的 DICOM file 还先写 128 bytes preamble (一般是空白), "DICM",  group 2 Meta header。(讲到 Part 10 时再细说)
3.
 如果 dataset 里面含有 Sequence elements, sequence 里面每一个 Item 又是一个 dataset
有了这些知识,你就可以开始写一个小小的 BMP  DICOM 的转化程序。关键资料:
Modality (0008, 0060): SC
Photometric Interpretation (0028, 0004): RGB
SOP Class UID: 1.2.840.10008.1.5.4.1.1.7
最简单的办法是写一个 structure 然后一个 array
typedef struct DicomElem
{
short int group_tag,
short int element_tag,
char VR[4],
int length,
char data[128]
} DicomElem;

  工程文件编译好之后程序会生成很多例子程序可以用来参考。举个例子:运行dcmdata\apps\Debug\dcmdump.exe这个程序。这是一个解析dicom文件的程序。如果想开发dicom文件可以参照这个程序。 
   
 调用库的时候要注意几个问题: 
    1.
工程设置/cc++/preprocessor/附加包含路径。这里设置头文件 
    2.
工程设置/link/input/对象库模块。这里设置库文件。 
    3.
工程设置/link/project option/。这里设置库文件路径。 
    4.
工程设置/cc++/code generation/Use run-time library 
    5.
以上几项需要和例子程序中的设置保持一致。

注意事項︰
1. DCMTK
 的設定中,預設是採用 "Multithreaded" runtime options (/MT and /MTd) 的設定,因此若新專案要使用上述方法編譯之函式庫,則新專案的 runtime options 設定必須一致。
2.
  VC 中,fstream 的用法有2種,一為 一為內建於 std 中的 DCMTK預設使用std 的函式庫,因此使用的新專案也必須要一致。是否使用 std  fstream 可透過C:\DCMTK\dcmtk-3.5.4\config\include\dcmtk\config\cfwin32.h 檔案中的 USE_STD_CXX_INCLUDES 定義來決定。
dcmtk使用介绍 
   
 由于dcmtk不能在mfc下使用。所以需要生成库之后再调用。 
   
 这里给出两个程序。 
    1.
利用dcmtk生成的静态库。 
    2.
调用静态库生成了一个读dicom目录文件的程序。 
整个程序都是参考dcmtk里的例子dcmdump.exe做的 
dcm文件编程http://www.vchelp.net/ASP/cdf_pic/200410/reply_1_747225.rar

DCMTK的常见问题论坛:http://forum.dcmtk.org/


0 0
原创粉丝点击