转:http://blog.csdn.net/zssureqh/article/details/8846337

背景介绍:

医学影像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*类型,增加了编程的复杂性)

下面给出部分代码:

  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. mImageValue+=mValueRecord;
  38. }
  39. ::WritePrivateProfileString(mImageModule.c_str(),mSOPInstanceUID.c_str(),mImageValue.c_str(),iniFileName);

3、数据库的写入:

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

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

对于MYSQL的C++操作,可以参见博文:http://www.cnblogs.com/justinzhang/archive/2011/09/23/2185963.html

DCMTK开源库的学习笔记4:利用ini配置文件对dcm影像进行归档的更多相关文章

  1. [Python ]小波变化库——Pywalvets 学习笔记

    [Python ]小波变化库——Pywalvets 学习笔记 2017年03月20日 14:04:35 SNII_629 阅读数:24776 标签: python库pywavelets小波变换 更多 ...

  2. 学习笔记:利用GDI+生成简单的验证码图片

    学习笔记:利用GDI+生成简单的验证码图片 /// <summary> /// 单击图片时切换图片 /// </summary> /// <param name=&quo ...

  3. 机器学习实战(Machine Learning in Action)学习笔记————09.利用PCA简化数据

    机器学习实战(Machine Learning in Action)学习笔记————09.利用PCA简化数据 关键字:PCA.主成分分析.降维作者:米仓山下时间:2018-11-15机器学习实战(Ma ...

  4. Spring MVC 学习笔记2 - 利用Spring Tool Suite创建一个web 项目

    Spring MVC 学习笔记2 - 利用Spring Tool Suite创建一个web 项目 Spring Tool Suite 是一个带有全套的Spring相关支持功能的Eclipse插件包. ...

  5. Solr 6.7学习笔记(02)-- 配置文件 managed-schema (schema.xml) -- 样例(6)

    managed-schema 样例: <?xml version="1.0" encoding="UTF-8" ?> <!-- License ...

  6. Solr 6.7学习笔记(02)-- 配置文件 managed-schema (schema.xml)(3)

         5. <fieldType> fieldType主要定义了一些字段类型,其name属性值用于前面<field>中的type属性的值.e.g. <fieldTyp ...

  7. Solr 6.7学习笔记(02)-- 配置文件 managed-schema (schema.xml) - filter(5)

    自定义fieldType时,通常还会用到filter.filter必须跟在tokenizer或其它filter之后.如: <fieldType> <analyzer> < ...

  8. Solr 6.7学习笔记(02)-- 配置文件 managed-schema (schema.xml)(1)

    刚学Solr(版本6.7.0),新建一个core时,提示要求schema.xml文件,我找了半天也没在源码包中找到名为schema.xml的文件.这个版本其实用的是managed-schema文件,没 ...

  9. php学习笔记:利用gd库生成图片,并实现随机验证码

    说明:一些基本的代码我都进行了注释,这里实现的验证码位数.需要用的字符串都可以再设置.有我的注释,大家应该很容易能看得懂. 基本思路: 1.用mt_rand()随机生成数字确定需要获取的字符串,对字符 ...

随机推荐

  1. linq入门系列导航

    写在前面 为什么突然想起来学学linq呢?还是源于在跟一个同事聊天的时候,说到他们正在弄得一个项目,在里面用到了linq to sql.突然想到距上次使用linq to sql是三年前的事情了.下班回 ...

  2. 团队作业 -- beta版本

    下一阶段需要改进完善的功能 1界面布局 2方块颜色调整 下一阶段新增的功能 1分数排行榜 2撤销上一步操作 需要改进的团队分工 无. 按要求加上一起进行编码任务 需要改进的工具流程 使用github进 ...

  3. 更新java对xml文件的操作

    //更新java在xml文件中操作的内容 public static void upda(Document doc) throws Exception{ //创建一个TransformerFactor ...

  4. Kettle_使用Pan.bat执行转换、Kitchen.bat执行作业

    参考资料:http://www.cnblogs.com/wxjnew/p/3620792.html 注意:使用bat文件执行速度比执行在spoon.bat中执行慢很多 一.使用Pan.bat执行转换 ...

  5. codevs 1082 线段树练习3 模板题

    #include<cstdio> #include<cstring> #include<algorithm> using namespace std; ],sum[ ...

  6. 【HTTP劫持和DNS劫持】腾讯的实际业务分析

        简单介绍一下HTTP劫持和DNS劫持的概念,也就是运营商通过某些方式篡改了用户正常访问的网页,插入广告或者其他一些杂七杂八的东西.       首先对运营商的劫持行为做一些分析,他们的目的无非 ...

  7. BZOJ-1002 轮状病毒 高精度加减+Kirchhoff矩阵数定理+递推

    1002: [FJOI2007]轮状病毒 Time Limit: 1 Sec Memory Limit: 162 MB Submit: 3543 Solved: 1953 [Submit][Statu ...

  8. 洛谷P1263 || 巴蜀2311 宫廷守卫

    题目描述 从前有一个王国,这个王国的城堡是一个矩形,被分为M×N个方格.一些方格是墙,而另一些是空地.这个王国的国王在城堡里设了一些陷阱,每个陷阱占据一块空地. 一天,国王决定在城堡里布置守卫,他希望 ...

  9. Erlang第三课 ---- 创建和使用module

    ----------------小技巧----------------------------- 因为这一课开始,我们要使用Erlang文件操作,所以,我们期待启动shell的时候,当前目录最好是是我 ...

  10. Weak is not weak,Strong is not strong

    问题 今天做浏览器Controller的时候,碰到了一个奇怪的问题:每次pop浏览器controller之后,等几秒,总会碰到类似下面的错误(其中的xxxController就是浏览器或继承他的子类C ...