博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DCMTK开源库的学习笔记4:利用ini配置文件对dcm影像进行归档
阅读量:5876 次
发布时间:2019-06-19

本文共 4908 字,大约阅读时间需要 16 分钟。

转:

背景介绍:

医学影像PACS工作站的服务端需要对大量的dcm文件进行归档,写入数据库处理。由于医学图像的特殊性,每一个患者(即所谓的Patient)每做一次检查(即Study)都至少会产生一组图像序列(即Series),而每一组图像序列下会包含大量的dcm文件(例如做一次心脏CTA的诊断,完整的一个心脏断层扫描序列大约有200幅图像)。DICOM3.0协议中对每一幅影像是按照特定的三个UID(唯一标示符)来进行标记的,分别是StudyInstanceUID、SeriesInstanceUID、SOPInstanceUID。其中StudyInstanceUID代表了唯一的一次检查(Study),SeriesInstanceUID代表了相应检查下的唯一序列(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*类型,增加了编程的复杂性)

下面给出部分代码:

 

[cpp] 
 
  1.               DcmTagKey THU_DCM_ELEMENTS[]=  
  2. {DCM_InstanceNubmber, DCM_Rows,DCM_Columns,DCM_PatientName};//定义需要写入到ini文件中的dcm数据元标签数组  
  3. int num=sizeof(THU_DCM_ELEMNTS)/sizeof(DcmTagKey);  
  4. OFString mImageValue;  
  5. OFString mGap("|");//INI配置文件中各个数据元之间的间隔符  
  6. OFString mImageModule("ImageModule\\");//配置文件的节名称  
  7. OFString mSOPInstanceUID;  
  8. OFString mSeriesInstanceUID;  
  9. DcmDataset *pDataset=mDcmFile->getDataset();  
  10. DcmMetaInfo *pMetaInfo=mDcmFile->getMetaInfo();  
  11. pDataset->findAndGetOFString(DCM_SeriesInstanceUID,mSeriesInstanceUID);  
  12. pDataset->findAndGetOFString(DCM_SOPInstanceUID,mSOPInstanceUID);  
  13. mImageModule+=mSeriesInstanceUID;  
  14. for(int i=0;i<num;++i)  
  15. {  
  16.     OFString mValueRecord;  
  17.     DcmElement *element;  
  18.     if(THU_DCM_ELEMNTS[i].getGroup()>0x0002)// to determine if the THU_DCM_ELEMENTS[i] is MetaInfo  
  19.     {  
  20.         //the element belongs to Dataset  
  21.         pDataset->findAndGetOFStringArray(THU_DCM_ELEMNTS[i],mValueRecord);  
  22.         mValueRecord+=mGap;  
  23.     }  
  24.     else  
  25.     {  
  26.         //the element belongs to MetaInfo  
  27.         if(THU_DCM_ELEMNTS[i].getGroup()==0x0000 && THU_DCM_ELEMNTS[i].getElement()==0x0000)  
  28.         {  
  29.             mValueRecord=mGap;  
  30.         }  
  31.         else  
  32.         {  
  33.             pMetaInfo->findAndGetOFStringArray(THU_DCM_ELEMNTS[i],mValueRecord);  
  34.             mValueRecord+=mGap;  
  35.         }  
  36.   
  37.     }  
  38.     mImageValue+=mValueRecord;  
  39. }  
  40. ::WritePrivateProfileString(mImageModule.c_str(),mSOPInstanceUID.c_str(),mImageValue.c_str(),iniFileName);  

 

 

3、数据库的写入:

 

数据库写入的方式与INI配置文件生成基本相似,只要稍微了解C++数据库编程的人员,就很容易仿照上述INI配置文件的生成过程来完成数据库写入的部分,此处就不细讲了,只给出简单的部分代码:

 

[cpp] 
 
  1. DcmFileFormat fileformat;  
  2. TCHAR FilePath[MAX_PATH];  
  3. OFCondition oc = fileformat.loadFile(FilePath);  
  4. DcmDataset *pDataset=fileformat.getDataset();  
  5. char query[1000];  
  6. memset(query,0,sizeof(char)*1000);  
  7. lstrcat(query,_T("insert into patient values ("));  
  8.   
  9. const char *tString;  
  10. pDataset->findAndGetString(DCM_InstanceNumber,tString);  
  11. lstrcat(query,tString);  
  12. lstrcat(query,_T(","));  
  13. pDataset->findAndGetString(DCM_PatientName,tString);  
  14. lstrcat(query,_T("\""));  
  15. lstrcat(query,tString);  
  16. lstrcat(query,_T("\""));  
  17. lstrcat(query,_T(","));  
  18. pDataset->findAndGetString(DCM_PatientID,tString);  
  19. lstrcat(query,tString);  
  20. lstrcat(query,_T(","));  
  21. pDataset->findAndGetString(DCM_PatientBirthDate,tString);  
  22. lstrcat(query,tString);  
  23. lstrcat(query,_T(","));  
  24. pDataset->findAndGetString(DCM_PatientSex,tString);  
  25. lstrcat(query,"\"");  
  26. lstrcat(query,tString);  
  27. lstrcat(query,"\"");  
  28. lstrcat(query,",");  
  29. pDataset->findAndGetString(DCM_PatientAge,tString);  
  30. char temp[100];  
  31. memcpy(temp,tString,lstrlen(tString));  
  32. temp[lstrlen(tString)]=_T('\0');  
  33. for(int i=0;i<strlen(temp);++i)  
  34.     if(temp[i]==_T('Y'))  
  35.         temp[i]=_T('\0');  
  36. lstrcat(query,temp);  
  37. lstrcat(query,",");  
  38. lstrcat(query,_T("2013)"));       
  39. n style="white-space:pre">              </span>//mysql数据库的写入  
  40. MYSQL* con;  
  41. con=mysql_init((MYSQL*)0);  
  42. if(con!=NULL && mysql_real_connect(con,host,user,passwd,db,port,unix_socket,client_flag))  
  43. {  
  44.     if(!mysql_select_db(con,db))  
  45.     {  
  46.         ::printf("Selcet successfully the database!\n");  
  47.   
  48.         con->reconnect=1;  
  49.         int rt=mysql_real_query(mysql,query,strlen(query));  
  50.         if(rt)  
  51.         {  
  52.             ::printf("Error making insert!!!\n");  
  53.         }  
  54.   
  55.     }  
  56.   
  57. }  

对于MYSQL的C++操作,可以参见博文:

转载地址:http://ikkix.baihongyu.com/

你可能感兴趣的文章
【转】SQL SERVER 2005 数据库状态为“可疑”的解决方法
查看>>
Revit API 创建带箭头的标注
查看>>
jetty启动报错Unsupported major.minor version 51.0
查看>>
Xamarin.Android开发实践(七)
查看>>
彩色图像上执行Mean Shift迭代搜索目标 ,维加权直方图 + 巴氏系数 + Mean Shift迭代...
查看>>
深入理解JavaScript系列
查看>>
strtol 函数用法
查看>>
eclipse内存溢出设置
查看>>
搭建jenkins环境(linux操作系统)
查看>>
VS 2015 GIT操作使用说明
查看>>
上海办理房产税变更
查看>>
每天一个linux命令(52):scp命令
查看>>
CMOS Sensor Interface(CSI)
查看>>
linq中的contains条件
查看>>
HDU 5590 ZYB's Biology 水题
查看>>
memcached 分布式聚类算法
查看>>
言未及之而言,谓之躁;言及之而不言,谓之隐;未见颜色而言,谓之瞽(gǔ)...
查看>>
MYSQL查询一周内的数据(最近7天的)
查看>>
Redis的缓存策略和主键失效机制
查看>>
禁止body滚动允许div滚动防微信露底
查看>>