连载:徒手写一个DICOM阅图软件(003)之图像坐标系转换

 本文主要介绍“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. 结果

这样,结果图像就能显示在窗口正中间了。

系列的下一篇:

连载:徒手写一个DICOM阅图软件(004)之图像的操作-CSDN博客

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

优视魔方

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值