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檔案的)
原始碼如下:
// 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基本元素,程式碼如下:
#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
後續博文介紹:
多幅JPEG影象資料存入DCM檔案
fo-dicom搭建簡單的PACS Server服務端
時間:2014-12-24
相關文章
- DICOM醫學檔案的解析
- iOS 影象處理 - 影象拼接iOS
- C#開發PACS醫學影像處理系統(十九):Dicom影像放大鏡C#
- C#開發PACS醫學影像處理系統(六):載入Dicom影像C#
- python PIL 影象處理操作Python
- 影象處理之影象增強
- Python-OpenCV 處理影象(三):影象畫素點操作PythonOpenCV
- 使用HTML5 IndexDB儲存影象和檔案HTMLIndex
- 醫學影象資料格式和格式轉換
- UIImage 影象處理UI
- Bayer影象處理
- [Python影象處理] 六.影象縮放、影象旋轉、影象翻轉與影象平移Python
- [Python影象處理] 八.影象腐蝕與影象膨脹Python
- 本blog的醫學影象處理演算法彙總演算法
- Python-OpenCV 處理影象(七):影象灰度化處理PythonOpenCV
- Python-OpenCV 處理影象(八):影象二值化處理PythonOpenCV
- 前端影象處理指南前端
- c#影象處理C#
- C#開發PACS醫學影像處理系統(十五):Dicom影像交叉定位線演算法C#演算法
- Python-OpenCV 處理影象(一):基本操作PythonOpenCV
- fMRI預處理之DICOM格式轉NII格式——SPM12批次碼
- Javascript影象處理之虛擬邊緣JavaScript
- MATLAB數字影象處理(二)影象增強Matlab
- C++影象處理 -- 影象黑白調整應用C++
- Linux 上的科學影象處理Linux
- 數字影象處理DIP
- 柵格影象的處理
- 讀取BMP影象每一畫素點RGB資料
- [Python影象處理] 五.影象融合、加法運算及影象型別轉換Python型別
- 24位BMP檔案儲存介紹
- 什麼是 DICOM
- [Python影象處理] 七.影象閾值化處理及演算法對比Python演算法
- IOS資料儲存之檔案沙盒儲存iOS
- Python資料視覺化影象庫MatPlotLib基本影象操作Python視覺化
- 影象中的畫素處理
- 第十四章 處理影象
- GPU 加速下的影象處理GPU
- 資料儲存--檔案儲存