您的位置:首页 > 其它

医学影像调窗技术

2015-07-30 10:56 323 查看
转自:/article/6019812.html

在年初的时候做过一个dicom格式文件解析,当时只是提了下。看着跟别人的显示出来也差不多 其实是我想太简单了。整理了下思路 这里提供正确的调窗代码。 医学影像 说得挺高科技的 其实在这个过程中本身没太复杂的图像处理技术。窗值处理就算是比较“高深”的了 也就是调窗。
网上都是啥基于 DCMTK的DICOM医学图像显示及其调窗方法研究 说得文绉绉的 没啥鸟用 ,dicom没你想象的那么复杂哈 咱这个全是自主代码 顶多看了点C++的源码 然后改成c#版本的 其实都一样的。

这中间有几个 步骤,
1 字节序转换
2 保留有效位,使用&进行位运算截取有效位
3 根据有无符号进行值转换
4 针对CT影像的窗值偏移处理
5 窗值映射 也就是映射到256级灰度(参考上一篇

而我原来的代码啥都没做 直接对两个字节的数据进行toUint16 然后就进行窗值映射,还有就是也没有进行预设窗值读取。那么这样做的后果是什么呢 。
我们先加上预设窗值读取,首先我们加上几个变量 进行影像显示的几个关键数据 图像的长 宽 默认窗值 颜色采样数 1为灰度3为彩色 数据存储位数 有效位数 最高位数,具体查看dicom标准。
变量声明 默认窗值读取代码 (预设窗宽tag 0028 1051 预设窗位tag 0028 1050):

public unsafe Bitmap convertTo8(BinaryReader streamdata, int colors, bool littleEdition, bool signed, short nHighBit,
int dataLen, float rescaleSlope, float rescaleIntercept, float windowCenter, float windowWidth, int width, int height)
{
Bitmap bmp = new Bitmap(width, height);
Graphics gg = Graphics.FromImage(bmp);
gg.Clear(Color.Green);
BitmapData bmpDatas = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
long numPixels = width * height;

if (colors == 3)//color Img
{
byte* p = (byte*)bmpDatas.Scan0;
int indx = 0;
for (int i = 0; i < bmp.Height; i++)
{
for (int j = 0; j < bmp.Width; j++)
{
p[indx + 2] = streamdata.ReadByte();
p[indx + 1] = streamdata.ReadByte();
p[indx] = streamdata.ReadByte();
indx += 3;
}
}
}
else if (colors == 1)//grayscale Img
{
byte* p = (byte*)bmpDatas.Scan0;
int nMin = ~(0xffff << (nHighBit + 1)), nMax = 0;
int indx = 0;//byteData index

for (int n = 0; n < numPixels; n++)//pixNum index
{
short nMask; nMask = (short)(0xffff << (nHighBit + 1));
short nSignBit;

byte[] pixData = null;
short pixValue = 0;

pixData = streamdata.ReadBytes(dataLen / 8 * colors);
if (nHighBit <= 15 && nHighBit > 7)
{
if (littleEdition == false)
Array.Reverse(pixData, 0, 2);

// 1. Clip the high bits.
if (signed == false)// Unsigned integer
{
pixValue = (short)((~nMask) & (BitConverter.ToInt16(pixData, 0)));
}
else
{
nSignBit = (short)(1 << nHighBit);
if (((BitConverter.ToInt16(pixData, 0)) & nSignBit) != 0)
pixValue = (short)(BitConverter.ToInt16(pixData, 0) | nMask);
else
pixValue = (short)((~nMask) & (BitConverter.ToInt16(pixData, 0)));
}
}
else if (nHighBit <= 7)
{
if (signed == false)// Unsigned integer
{
nMask = (short)(0xffff << (nHighBit + 1));
pixValue = (short)((~nMask) & (pixData[0]));
}
else
{
nMask = (short)(0xffff << (nHighBit + 1));
nSignBit = (short)(1 << nHighBit);
if (((pixData[0]) & nSignBit) != 0)
pixValue = (short)((short)pixData[0] | nMask);
else
pixValue = (short)((~nMask) & (pixData[0]));
}

}

// 2. Rescale if needed (especially for CT)
if ((rescaleSlope != 1.0f) || (rescaleIntercept != 0.0f))
{
float fValue = pixValue * rescaleSlope + rescaleIntercept;
pixValue = (short)fValue;
}

// 3. Window-level or rescale to 8-bit
if ((windowCenter != 0) || (windowWidth != 0))
{
float fSlope;
float fShift;
float fValue;

fShift = windowCenter - windowWidth / 2.0f;
fSlope = 255.0f / windowWidth;

fValue = ((pixValue) - fShift) * fSlope;
if (fValue < 0)
fValue = 0;
else if (fValue > 255)
fValue = 255;

p[indx++] = (byte)fValue;
p[indx++] = (byte)fValue;
p[indx++] = (byte)fValue;
}
else
{
// We will map the whole dynamic range.
float fSlope;
float fValue;

int i = 0;
// First compute the min and max.
if (n == 0)
nMin = nMax = pixValue;
else
{
if (pixValue < nMin)
nMin = pixValue;

if (pixValue > nMask)
nMask = pixValue;
}

// Calculate the scaling factor.
if (nMax != nMin)
fSlope = 255.0f / (nMax - nMin);
else
fSlope = 1.0f;

fValue = ((pixValue) - nMin) * fSlope;
if (fValue < 0)
fValue = 0;
else if (fValue > 255)
fValue = 255;

p[indx++] = (byte)fValue;
}
}
}

bmp.UnlockBits(bmpDatas);
//bmp.Dispose();
return bmp;
}


View Code
完整源码及测试数据下载 猛击此处
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: