济南企业网站开发,郴州人为什么不像湖南人,搬瓦工搭建wordpress,新闻门户网站什么意思转自#xff1a;https://blog.csdn.net/zssureqh/article/details/8846337
背景介绍#xff1a;
医学影像PACS工作站的服务端需要对大量的dcm文件进行归档#xff0c;写入数据库处理。由于医学图像的特殊性#xff0c;每一个患者#xff08;即所谓的Patient#xff09;…转自https://blog.csdn.net/zssureqh/article/details/8846337
背景介绍
医学影像PACS工作站的服务端需要对大量的dcm文件进行归档写入数据库处理。由于医学图像的特殊性每一个患者即所谓的Patient每做一次检查即Study都至少会产生一组图像序列即Series而每一组图像序列下会包含大量的dcm文件例如做一次心脏CTA的诊断完整的一个心脏断层扫描序列大约有200幅图像。DICOM3.0协议中对每一幅影像是按照特定的三个UID唯一标示符来进行标记的分别是StudyInstanceUID、SeriesInstanceUID、SOPInstanceUID。其中StudyInstanceUID代表了唯一的一次检查StudySeriesInstanceUID代表了相应检查下的唯一序列Series、SOPInstanceUID代表了唯一检查下的唯一序列下的唯一图像。通常PACS工作站都是利用这三个UID来对dcm文件进行归档处理。
归档的设计
1、基本的归档结构是 第一级StudyInstanceUID 存储同一患者的影像数据 第二级SeriesInstanceUID 存储同一次检查下的影像数据 第三级SOPInstanceUID 存储同一个序列下的影像数据
了解了大致的归档结构后现在应该考虑怎样将dcm记录写入到数据库中最直观的想法就是将每一个dcm文件都记录在数据库中这样当需要读取指定的dcm文件时通过给定的三个UID直接在数据库中查询就能够得到。但是如此一来数据库的容量会急剧增加同一患者在数据库中存在着大量的冗余记录。因为患者的影像数据是按照上述的三级目录来归档的大量的相关的影像数据存储在服务器的同一个目录下对于同一个序列的图像可以直接在第二级目录中利用SOPInstanceUID来进行检索而不需要进行数据库的查询。但是当二级目录下的文件数量较大时检索文件件中的文件同样需要耗费大量的时间那么怎样可以提高检索效率呢答案就是配置文件。由于在归档的时候我们已经读取过每个dcm文件的三个UID那么可以将归档时候读取的UID信息写入到相应的INI配置文件中并存储到相应的图像序列目录下。那么在检索图像时通过前两级UID可以快速在数据库中查询到影像数据的归档目录当进入到指定的归档目录后利用归档时生成的INI文件可以快速的检索到指定的dcm文件另外如果归档时将一些常用的dcm文件信息一同写入到INI配置文件中如图像的宽度、高度、患者姓名、出生年月、窗宽/窗位等在后续的一些图像处理中同样能够节约时间提高效率。
2、INI配置文件的生成
INI配置文件的格式就不细讲了CSDN中已有很多详细讲解的博文请大家自行参阅。这次详细讲解一下利用dcmtk开源库来提取相应的dcm文件信息并写入到ini配置文件中的方法。
DCMTK开源库是一个很好的医学影像开发基础库其很好的实现了DICOM3.0标准且类的继承体系简单明了与DICOM3.0标准一一对应参考博文“dcmtk开源库的继承体系与DICOM3.0标准的对应关系”。这里我们只用到了dcmtk中的DcmItem类该类派生自DcmObject基础类其含有ElementList成员变量存储了DICOM3.0标准中规定的一系列的数据元Data Element基本结构如下图所示 注DcmItem类就是Dataset数据集的子类。其内部包含了数据元序列即ElementList数据成员。
通过阅读关于DcmItem类的源码总结归纳了以下几种dcmtk开源库给出的操作dcm文件相应数据元的函数findAndGet 函数、findOrCreate函数、findAndXXX函数、putAndInsert函数以及insertXXX函数如下图 至此我们可以利用findAndGet函数类来提取dcm文件中的相关信息结合WindowsAPI函数来进行INI配置文件的归档。由于INI配置文件就是文本文件因此我们选用了DcmItem中的findAndGetString函数来提取dcm文件中的数据元利用findAndGetString函数能够直接得到字符串格式const char*的数据元另外结合WritePrivateProfileString函数来生成INI配置文件。注此处findAndGetString函数正好与WritePrivateProfileString函数的格式匹配如果采用findAndGet的其他函数如findAndGetSin32就需要利用itoa等函数将整型转换成const char*类型增加了编程的复杂性
下面给出部分代码 DcmTagKey THU_DCM_ELEMENTS[]{DCM_InstanceNubmber, DCM_Rows,DCM_Columns,DCM_PatientName};//定义需要写入到ini文件中的dcm数据元标签数组int numsizeof(THU_DCM_ELEMNTS)/sizeof(DcmTagKey);OFString mImageValue;OFString mGap(|);//INI配置文件中各个数据元之间的间隔符OFString mImageModule(ImageModule\\);//配置文件的节名称OFString mSOPInstanceUID;OFString mSeriesInstanceUID;DcmDataset *pDatasetmDcmFile-getDataset();DcmMetaInfo *pMetaInfomDcmFile-getMetaInfo();pDataset-findAndGetOFString(DCM_SeriesInstanceUID,mSeriesInstanceUID);pDataset-findAndGetOFString(DCM_SOPInstanceUID,mSOPInstanceUID);mImageModulemSeriesInstanceUID;for(int i0;inum;i){OFString mValueRecord;DcmElement *element;if(THU_DCM_ELEMNTS[i].getGroup()0x0002)// to determine if the THU_DCM_ELEMENTS[i] is MetaInfo{//the element belongs to DatasetpDataset-findAndGetOFStringArray(THU_DCM_ELEMNTS[i],mValueRecord);mValueRecordmGap;}else{//the element belongs to MetaInfoif(THU_DCM_ELEMNTS[i].getGroup()0x0000 THU_DCM_ELEMNTS[i].getElement()0x0000){mValueRecordmGap;}else{pMetaInfo-findAndGetOFStringArray(THU_DCM_ELEMNTS[i],mValueRecord);mValueRecordmGap;}}mImageValuemValueRecord;}::WritePrivateProfileString(mImageModule.c_str(),mSOPInstanceUID.c_str(),mImageValue.c_str(),iniFileName);
3、数据库的写入
数据库写入的方式与INI配置文件生成基本相似只要稍微了解C数据库编程的人员就很容易仿照上述INI配置文件的生成过程来完成数据库写入的部分此处就不细讲了只给出简单的部分代码 DcmFileFormat fileformat;TCHAR FilePath[MAX_PATH];OFCondition oc fileformat.loadFile(FilePath);DcmDataset *pDatasetfileformat.getDataset();char query[1000];memset(query,0,sizeof(char)*1000);lstrcat(query,_T(insert into patient values ());const char *tString;pDataset-findAndGetString(DCM_InstanceNumber,tString);lstrcat(query,tString);lstrcat(query,_T(,));pDataset-findAndGetString(DCM_PatientName,tString);lstrcat(query,_T(\));lstrcat(query,tString);lstrcat(query,_T(\));lstrcat(query,_T(,));pDataset-findAndGetString(DCM_PatientID,tString);lstrcat(query,tString);lstrcat(query,_T(,));pDataset-findAndGetString(DCM_PatientBirthDate,tString);lstrcat(query,tString);lstrcat(query,_T(,));pDataset-findAndGetString(DCM_PatientSex,tString);lstrcat(query,\);lstrcat(query,tString);lstrcat(query,\);lstrcat(query,,);pDataset-findAndGetString(DCM_PatientAge,tString);char temp[100];memcpy(temp,tString,lstrlen(tString));temp[lstrlen(tString)]_T(\0);for(int i0;istrlen(temp);i)if(temp[i]_T(Y))temp[i]_T(\0);lstrcat(query,temp);lstrcat(query,,);lstrcat(query,_T(2013))); //mysql数据库的写入MYSQL* con;conmysql_init((MYSQL*)0);if(con!NULL mysql_real_connect(con,host,user,passwd,db,port,unix_socket,client_flag)){if(!mysql_select_db(con,db)){::printf(Selcet successfully the database!\n);con-reconnect1;int rtmysql_real_query(mysql,query,strlen(query));if(rt){::printf(Error making insert!!!\n);}}}
对于MYSQL的C操作可以参见博文 http://www.cnblogs.com/justinzhang/archive/2011/09/23/2185963.html