日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

DICOM医学图像处理:DICOM存储操作之“多幅BMP图像数据存入DCM文件”

發布時間:2025/3/20 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DICOM医学图像处理:DICOM存储操作之“多幅BMP图像数据存入DCM文件” 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景:

? ? ? ? 本專欄“DICOM醫學圖像處理”受眾較窄,起初只想作為自己學習積累和工作經驗的簡單整理。前幾天無聊瀏覽了一下,發現閱讀量兩極化嚴重,主要集中在“關于BMP(JPG)與DCM格式轉換”和“DICOM 通訊協議”,尤其是許久前的第一篇博文DCMTK開源庫的學習筆記1:將DCM文件保存成BMP文件或數據流(即數組)。因此在2014年底前打算寫幾篇關于DCM格式轉換的文章,此次主要聚焦“如何將BMP、JPG等常規圖像保存成DCM文件”,以DCMTK庫為基礎,給出簡單的實例。

? ? ? ? 這幾篇博文采用倒敘的方式,先給出可直接運行的源碼,然后重點講解其中易犯的錯誤,最后是知識點補充。

利用DCMTK實現Multi-BMP存入DCM:

? ? ? ? 源碼以DCMTK為基礎,思路參照DCMTK的img2dcm工具包,依賴庫包含:netapi32.lib; wsock32.lib; ofstd.lib; oflog.lib; dcmimgle.lib; ijg8.lib; ijg12.lib; ijg16.lib; dcmdata.lib; dcmimage.lib; dcmjpeg.lib; dcmnet.lib; zlib.lib;libi2d.lib;(【注】:libi2d.lib庫是用于導入BMP文件的)

源碼如下:

?

[csharp]?view plaincopy print?
  • //?DcmPixelDataTest.cpp?:?定義控制臺應用程序的入口點。??
  • //??
  • ?
  • #include?"stdafx.h"??
  • #include?"dcmtk/config/osconfig.h"??
  • #include?"dcmtk/dcmdata/dctk.h"??
  • #include?"dcmtk/dcmdata/dcistrmf.h"??
  • #include?"dcmtk/dcmdata/libi2d/i2dbmps.h"??
  • #include?"DicomUtils.h"??
  • #include?<direct.h>??
  • ??
  • int?_tmain(int?argc,?_TCHAR*?argv[])??
  • {??
  • ????OFCondition?status;??
  • ??
  • ????DcmFileFormat?fileformat;??
  • ????DcmDataset*?mydatasete=fileformat.getDataset();??
  • ????DicomUtils::AddDicomElements((DcmDataset*&)mydatasete);??
  • ????Uint16?rows,cols,samplePerPixel,bitsAlloc,bitsStored,highBit,pixelRpr,planConf,pixAspectH,pixAspectV;??
  • ????OFString?photoMetrInt;??
  • ????Uint32?length;??
  • ????E_TransferSyntax?ts;??
  • ????char*?mydata=new?char[1024*1024*10];??
  • ????memset(mydata,0,sizeof(char)*1024*1024*10);??
  • ????char*?tmpData=mydata;??
  • ????char?curDir[100];??
  • ????getcwd(curDir,100);??
  • ????//循環添加4張圖片??
  • ????for(int?i=0;i<4;++i)??
  • ????{??
  • ????????OFString?num;??
  • ????????char?numtmp[100];??
  • ????????memset(numtmp,0,sizeof(char)*100);??
  • ????????sprintf(numtmp,"%s\\test\\%d.bmp",curDir,i+1);??
  • ????????OFString?filename=OFString(numtmp);??
  • ????????I2DBmpSource*?bmpSource=new?I2DBmpSource();??
  • ????????bmpSource->setImageFile(filename);??
  • ??
  • ????????char*?pixData=NULL;??
  • ????????bmpSource->readPixelData(rows,cols,samplePerPixel,photoMetrInt,bitsAlloc,bitsStored,highBit,pixelRpr,planConf,pixAspectH,pixAspectV,pixData,length,ts);??
  • ????????memcpy(tmpData,pixData,length);??
  • ????????tmpData+=length;??
  • ??
  • ????????delete?bmpSource;??
  • ????};??
  • ??
  • ????mydatasete->putAndInsertUint16(DCM_SamplesPerPixel,samplePerPixel);??
  • ????mydatasete->putAndInsertString(DCM_NumberOfFrames,"4");??
  • ????mydatasete->putAndInsertUint16(DCM_Rows,rows);??
  • ????mydatasete->putAndInsertUint16(DCM_Columns,cols);??
  • ????mydatasete->putAndInsertUint16(DCM_BitsAllocated,bitsAlloc);??
  • ????mydatasete->putAndInsertUint16(DCM_BitsStored,bitsStored);??
  • ????mydatasete->putAndInsertUint16(DCM_HighBit,highBit);??
  • ????mydatasete->putAndInsertUint8Array(DCM_PixelData,(Uint8*)mydata,4*length);??
  • ????mydatasete->putAndInsertOFStringArray(DCM_PhotometricInterpretation,photoMetrInt);??
  • ????//mydatasete->putAndInsertString(DCM_PlanarConfiguration,"1");??
  • ????status=fileformat.saveFile("c:\\Multibmp2dcmtest.dcm",ts);??
  • ????if(status.bad())??
  • ????{??
  • ????????std::cout<<"Error:("<<status.text()<<")\n";??
  • ????}??
  • ????return?0;??
  • }??

  • 代碼中的DicomUtils類是一個方法類,提供了一個靜態方法AddDicomElement構造DICOM基本元素,代碼如下:

    ?

    [csharp]?view plaincopy print?
  • #include?"DicomUtils.h"??
  • ??
  • ??
  • DicomUtils::DicomUtils(void)??
  • {??
  • }??
  • ??
  • ??
  • DicomUtils::~DicomUtils(void)??
  • {??
  • }??
  • void?DicomUtils::AddDicomElements(DcmDataset*&?dataset)??
  • {??
  • ????//構建測試數據??
  • ??
  • ????/*??添加患者信息??*/??
  • ????dataset->putAndInsertUint16(DCM_AccessionNumber,0);??
  • ????dataset->putAndInsertString(DCM_PatientName,"zssure",true);??
  • ????dataset->putAndInsertString(DCM_PatientID,"2234");??
  • ????dataset->putAndInsertString(DCM_PatientBirthDate,"20141221");??
  • ????dataset->putAndInsertString(DCM_PatientSex,"M");??
  • ??
  • ????/*??添加Study信息???*/??
  • ????dataset->putAndInsertString(DCM_StudyDate,"20141221");??
  • ????dataset->putAndInsertString(DCM_StudyTime,"195411");??
  • ????char?uid[100];??
  • ????dcmGenerateUniqueIdentifier(uid,SITE_STUDY_UID_ROOT);??
  • ????dataset->putAndInsertString(DCM_StudyInstanceUID,uid);??
  • ????dataset->putAndInsertString(DCM_StudyID,"1111");??
  • ??
  • ??
  • ????/*??添加Series信息??*/??
  • ????dataset->putAndInsertString(DCM_SeriesDate,"20141221");??
  • ????dataset->putAndInsertString(DCM_SeriesTime,"195411");??
  • ????memset(uid,0,sizeof(char)*100);??
  • ????dcmGenerateUniqueIdentifier(uid,SITE_SERIES_UID_ROOT);??
  • ????dataset->putAndInsertString(DCM_SeriesInstanceUID,uid);??
  • ????/*??添加Image信息???*/??
  • ????dataset->putAndInsertString(DCM_ImageType,"ORIGINAL\\PRIMARY\\AXIAL");??
  • ????dataset->putAndInsertString(DCM_ContentDate,"20141221");??
  • ????dataset->putAndInsertString(DCM_ContentTime,"200700");??
  • ????dataset->putAndInsertString(DCM_InstanceNumber,"1");??
  • ????dataset->putAndInsertString(DCM_SamplesPerPixel,"1");??
  • ????dataset->putAndInsertString(DCM_PhotometricInterpretation,"MONOCHROME2");??
  • ????dataset->putAndInsertString(DCM_PixelSpacing,"0.3\\0.3");??
  • ????dataset->putAndInsertString(DCM_BitsAllocated,"16");??
  • ????dataset->putAndInsertString(DCM_BitsStored,"16");??
  • ????dataset->putAndInsertString(DCM_HighBit,"15");??
  • ????dataset->putAndInsertString(DCM_WindowCenter,"600");??
  • ????dataset->putAndInsertString(DCM_WindowWidth,"800");??
  • ????dataset->putAndInsertString(DCM_RescaleIntercept,"0");??
  • ????dataset->putAndInsertString(DCM_RescaleSlope,"1");??
  • ??
  • ??
  • }??

  • 問題分析:

    1)文件格式錯誤:

    ? ? ? ? 我在方法類DicomUtils中默認添加的SamplePerPixel標簽值為1,如果最終讀取完像素數據(即readPixelData函數調用完)未重新寫入BMP相應的SamplePerPixel字段,會引發文件格式錯誤,在SanteSoft DICOM Editor軟件中打開彈出如下錯誤:

    ? ? ? ? 利用DCMTK自帶的dcmdump.exe工具分析結果如下:

    ? ? ? ? 由此可以看出像素數據讀取失敗。

    2)圖像信息顯示錯誤:



    ? ? ? ? 上圖是正確圖像,下圖是由于Photometric Interpretation字段寫入錯誤導致的,靜態類DicomUtils中默認的Photometric Interpretation值為MONOCHROME2,修改為I2DBmpSource中readPixelData函數返回的photoMetrInt參數后圖像數據顯示正確,如下圖所示:

    ?

    3)圖像色彩顯示錯誤:

    ? ? ? ? 在靜態類DicomUtils中并未添加Planar Configuration字段,因此DCMTK自動填充該字段為0,如果我們修改為1,會出現圖像色彩錯誤,如下圖:


    BMP格式:

    ? ? ? ? 關于BMP格式介紹的博文很多,可參考http://blog.csdn.net/zhandoushi1982/article/details/5196017或者http://blog.csdn.net/gwwgle/article/details/4775396。BMP文件數據主要有以下幾部分組成:1)文件頭,即結構BITMAPFILEHEADER, *PBITMAPFILEHEADER,類似于DCM中的DcmMetaInfo;2)圖像描述信息塊,該部分記錄了圖像信息塊的大小、圖像的寬度、高度、圖像通道數(即Plane)、像素位數(即后面DICOM標準中的SamplesPerPixel)、圖像壓縮方式、圖像數據區大小等等;3)顏色表,即調色板。該部分與DICOM標準中的COLOR PALETTE,隨著像素位數不同顏色表大小也不同,當像素位數為24或更大,即SamplesPerPixel=3時,像素數據本身就可以代表顏色,因此不需要顏色表;4)圖像數據區,即文件中存儲的真正的像素信息。【注】:這里有一個坐標轉換,標準的BMP文件像素存儲順序是由左到右、由下到上,即坐標原點為圖像左下角;而DICOM標準存儲順序為從左到右,從上到下,坐標原點為圖像左上角,因此在自己讀取時需要進行反轉。

    獲取BMP圖像信息方法:

    1)直接讀取二進制

    ? ? ? ? 了解了BMP文件的具體格式,可以利用常用的二進制操作方式,直接從文件中提取像素數據。這種代碼網上也很多,可參考:http://www.jb51.net/article/56274.htm。

    2)DCMTK庫

    ? ? ? ? DCMTK庫中的I2DBmpSource類是專門用來解析BMP文件的,并且提供了BMP到DICOM數據格式的轉換。具體的使用可參照我上面的實例,也可參考DCMTK給出的img2dcm工具包源碼。?

    3)CxImage第三方庫

    ? ? ? ? CxImage是一款免費的、優秀的圖像操作類庫,可以快捷的存取、顯示、轉換各種圖像,例如BMP、GIF、ICO、TGA、JPEG、PCX、PNG、TIFF、MNG、RAS等等;CxImage使用簡單,文檔詳細,只有一個API接口文件;支持Windows、Linux和Unix等多平臺,支持32位和64位。

    ? ? ? ? 關于CxImage的復雜使用,可參見CodeProject中大神的博文:http://www.codeproject.com/Articles/1300/CxImage。

    ? ? ? ? 另外我在博文DCMTK開源庫的學習筆記1:將DCM文件保存成BMP文件或數據流(即數組)中給出的源碼是結合了CxImage和DCMTK兩種開源庫,這也是常見的一種組合方式,具體細節可參考我的GitHub上的源碼。

    DICOM文件格式:

    1)Samples Per Pixel:

    ? ? ? ? 標簽為(0028,0002),具體的介紹在DICOM3.0標準第3部分的附錄C7.6.3.1。含義表示【the number of separate planes in this image】,就像PhotoShop中的通道,每個通道表示一種顏色(除了RGB三個通道以外,也會存在第四個通透性通道)。對于灰度圖像(monochrome或gray)和顏色表圖像(palette,就是BMP格式中介紹的有調色板的BMP文件),該標簽值為1,RGB圖像或其他色彩模式圖像,該標簽值為3。本實例中使用的BMP圖像是RGB格式的,因此SamplePerPixel=3,起初的文件格式錯誤就是由于該字段設置為1所致。

    2)Photometric Interpretation:

    ? ? ? ? 標簽為(0028,0004),具體介紹在DICOM3.0標準第3部分的附錄C7.6.3.1.2。該字段常見的值有MONOCHROME1、MONOCHROME2、PALETTE COLOR、RGB,其中MONOCHROME1和MONOCHROME2表示單通道灰度圖像,只是兩者對黑色和白色的映射相反而已;PALETTE COLOR就是BMP中提到的調色板圖像,此時需要SamplesPerPixel字段為1,;RGB是常見的R(紅)、G(綠)、B(藍)三通道彩色圖像,此時SamplesPerPixel字段值為3,這就是我們實例中使用的圖像。除此以外DICOM3.0標準中還給出了YBR_FULL、HSV、ARGB、CMYK等方式,此處就不詳細介紹了。

    3)Planar Configuration:

    ? ? ? ? 標簽為(0028,0006),具體介紹在DICOM3.0標準第3部分附錄C7.6.3.1.3。當Samples Per Pixel字段的值大于1時,Planar Configuration字段規定了實際像素信息的存儲方式,具體如下:

    ?

    最終結果:

    ? ? ? ? 博文中實例代碼最終在C盤根目錄生成Multibmp2dcmtest.dcm文件,利用Sante DICOM Editor打開可以順利看到文件中包含了我們插入的四張bmp圖像,如下圖:


    ?

    ? ? ? ? 至此將多幅BMP圖像寫入DCM文件的任務順利完成了,其實將多幅圖像寫入DCM文件與寫入單幅BMP圖像是完全相同的,只需要將多張BMP圖像(此時要求每張BMP圖像的寬度和高度相同)像素數據首尾相接的寫入DCM中的PixelData標簽下,即(7FE0,0010);此時將NumberofFrames標簽賦值為圖像張數,DCM文件編輯器就可自動識別提取各張圖像。

    源碼:

    百度網盤:http://pan.baidu.com/s/1dDrhHlR

    GitHub:https://github.com/zssure-thu/CSDN/tree/master

    ?

    總結

    以上是生活随笔為你收集整理的DICOM医学图像处理:DICOM存储操作之“多幅BMP图像数据存入DCM文件”的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。