本文主要介绍“DICOM图像的加载与显示”,为【徒手写一个DICOM阅图软件】的第3篇。
本文为了简明体现整个操作过程,因此程序框架及软件设计并不规范,所以代码仅供参考。
上一篇介绍了一个DICOM文件从加载到显示,见:连载:徒手写一个DICOM阅图软件(002)-CSDN博客
0. 准备
从上次结果来看,图像是可以显示了,
上图图像显示区域不在正中(已经超出去了),这非常影响体验,因此本篇主要实现将图像在窗口正中显示。因此图像看起来应该这样:
1. 像素转换逻辑
正向的转换逻辑为:
原始DICOM图像一般为512*512像素,软件窗口假设为400*300(如上图左),因此我们需要将原始图像从512*512缩放到300*300大小,然后将图像的中心移动到窗口中心(200,150)。
但实际上我常用反向的逻辑:
在窗口内某个像素,它应该显示DICOM文件中的某个像素。
因此我们只需要完成从图像坐标系到窗口坐标系的转换即可。
void ShowWork::DrawInPixel( )
{
short* pPixelBuffer = mData.GetPixelBuffer( ); // DICOM原始像素
if ( pPixelBuffer == NULL )
return;
int pixelWidth = mData.GetWidth( ); // DICOM图像的宽度
int pixelHeight = mData.GetHeight( ); // DICOM图像的高度
float a, b;
mData.GetWLFactor( a, b ); // 窗宽窗位转换系数
int w = mShowSize[0]; // 显示尺寸
int h = mShowSize[1];
M3DMatrix44f ScreenToDicom; // 从窗口到图像的转换矩阵
m3dInvertMatrix44( ScreenToDicom, mDicomToScreen );
M3DVector3f v1, v2;
UCHAR c = 0;
for ( int y = 0; y < h; y++ )
{
for ( int x = 0; x < w; x++ )
{
m3dLoadVector3( v1, float( x ), float( y ), 0 ); // 当前窗口位置
m3dTransformVector3( v2, v1, ScreenToDicom ); // 计算出
// 测试是否越界
if ( v2[0] < 0 || v2[0] >= pixelWidth ||
v2[1] < 0 || v2[1] >= pixelHeight )
{
mShowPixel[x + y * w ] = 0; // 如果越界,则置0
continue;
}
// 未越界,则取出CT值
int posX = int( v2[0] + 0.5f );
int posY = int( v2[1] + 0.5f );
short pixel = pPixelBuffer[ posX + posY * pixelWidth];
float v = pixel * a + b; // 将原始数据进行转换,y=x*a+b
if ( v < 0.0f )
c = 0;
else if ( v > 255.0f )
c = 255;
else
c = UCHAR( v );
mShowPixel[x + y * w] = RGB( c, c, c ); // 显示像素为32bit,因为灰度,所以3通道颜色一样。
}
}
}
2. 计算转换矩阵
至于转换矩阵,这里用矩阵来做非常简单,制作转换矩阵分3步:
1. 将图像中心平移到原点。
2. 将图像进行缩放到适合窗口大小。
3. 将图像移动到窗口中心。
void ShowWork::InitDicomToScreenMatrix( )
{
int dcmWidth = mData.GetWidth( ); // DICOM图像的宽度
int dcmHeight = mData.GetHeight( ); // DICOM图像的高度
int wndWidth = mShowSize[0]; // 窗口的宽
int wndHeight = mShowSize[1]; // 窗口的高
// 先将图像中心移动到原点
M3DVector3f imageCenter = { dcmWidth*0.5f, dcmHeight*0.5f, 0.0f };
M3DMatrix44f m1, m2, m3;
m3dTranslationMatrix44( m1, -imageCenter[0], -imageCenter[1], -imageCenter[2] ); // 平移矩阵
// 将图像缩放到窗口能合适显示的大小
float scaleWidth = float( wndWidth ) / float( dcmWidth );
float scaleHeight = float( wndHeight ) / float( dcmHeight );
float s = scaleWidth < scaleHeight ? scaleWidth : scaleHeight; // 宽度和高度选一个系数小的
m3dScaleMatrix44( m2, s, s, s ); // 缩放矩阵
m3dMatrixMultiply44( m3, m2, m1 ); // m1 x m2 => m3
// 再将图像移动窗口的中心
M3DVector3f wndCenter = { wndWidth*0.5f, wndHeight*0.5f, 0.0f };
m3dTranslationMatrix44( m1, wndCenter[0], wndCenter[1], wndCenter[2] ); // 平移矩阵
m3dMatrixMultiply44( mDicomToScreen, m1, m3 ); // m3 x m1 => mFitMatrix
}
3. 结果
这样,结果图像就能显示在窗口正中间了。
系列的下一篇: