医学影像调窗技术
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):
View Code
完整源码及测试数据下载 猛击此处
在年初的时候做过一个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
完整源码及测试数据下载 猛击此处
相关文章推荐
- [Ptrace]Linux内存替换(二)信息获取
- 详解Java程序并发的Wait-Notify机制
- android 记录和恢复ListView滚动的位置 四种方法
- C语言关于补码的解释及误区
- HDUOJ 水果
- 批量修改mysql 截取字符串
- mui.fire()用法
- iOS UITableView使用详解
- Spock Proxy
- TIF、JPG图片手动添加地理坐标的方法
- 封装自己的ajax函数
- 数据库(表结构)设计技巧及注意事项
- HDU 1022 Train Problem I
- CentOS7 + owncloud8.1.0 搭建企业私有云(基础服务)
- Git学习教程
- jQuery新浪微博表情插件教程
- jsp页面charset与head中charset的区别
- 设计模式-单例模式
- Java中如何实现程序国际化
- Android5.0——Linux内核存储协议栈图