临西做网站哪里便宜,公司名称被大量网站收录,北京室内设计公司排名,柳州最新消息转自#xff1a;https://www.cnblogs.com/assassinx/p/3139505.html
在年初的时候做过一个dicom格式文件解析#xff0c;当时只是提了下。看着跟别人的显示出来也差不多 其实是我想太简单了。整理了下思路 这里提供正确的调窗代码。 医学影像 说得挺高科技的 其实在这个过程…转自https://www.cnblogs.com/assassinx/p/3139505.html
在年初的时候做过一个dicom格式文件解析当时只是提了下。看着跟别人的显示出来也差不多 其实是我想太简单了。整理了下思路 这里提供正确的调窗代码。 医学影像 说得挺高科技的 其实在这个过程中本身没太复杂的图像处理技术。窗值处理就算是比较“高深”的了 也就是调窗。 网上都是啥基于 DCMTK的DICOM医学图像显示及其调窗方法研究 说得文绉绉的 没啥鸟用 dicom没你想象的那么复杂哈 咱这个全是自主代码 顶多看了点C的源码 然后改成c#版本的 其实都一样的。
具体流程需要以下几个步骤 1 字节序转换 2 保留有效位使用进行位运算截取有效位 3 根据有无符号进行值转换 4 针对CT影像的窗值偏移处理 5 窗值映射 也就是映射到256级灰度参考上一篇
而我原来的代码啥都没做 直接对两个字节的数据进行toUint16 然后就进行窗值映射还有就是也没有进行预设窗值读取。那么这样做的后果是什么呢 。 我们先加上预设窗值读取首先我们加上几个变量 进行影像显示的几个关键数据 图像的长 宽 默认窗值 颜色采样数 1为灰度3为彩色 数据存储位数 有效位数 最高位数具体查看dicom标准。 变量声明 默认窗值读取代码 预设窗宽tag 0028 1051 预设窗位tag 0028 1050 1 if (fileName string.Empty)2 return false;3 4 int dataLen, validLen, hibit;//数据长度 有效位5 int imgNum;//帧数6 7 rows int.Parse(tags[0028,0010].Substring(5));8 cols int.Parse(tags[0028,0011].Substring(5));9
10 colors int.Parse(tags[0028,0002].Substring(5));
11 dataLen int.Parse(tags[0028,0100].Substring(5));//bits allocated
12 validLen int.Parse(tags[0028,0101].Substring(5));
13 bool signed int.Parse(tags[0028,0103].Substring(5)) 0 ? false : true;
14 hibit int.Parse(tags[0028,0102].Substring(5));
15 float rescaleSlop 1, rescaleinter 0;
16 if (tags.ContainsKey(0028,1052) tags.ContainsKey(0028,1053))
17 {
18 rescaleSlop float.Parse(tags[0028,1053].Substring(5));
19 rescaleinter float.Parse(tags[0028,1052].Substring(5));
20 }
21 //读取预设窗宽窗位
22 //预设窗值读取代码......
23 #region//读取预设窗宽窗位
24 if (windowWith 0 windowCenter 0)
25 {
26 Regex r new Regex(([0-9]));
27 if (tags.ContainsKey(0028,1051))
28 {
29 Match m r.Match(tags[0028,1051].Substring(5));
30
31 if (m.Success)
32 windowWith int.Parse(m.Value);
33 else
34 windowWith 1 validLen;
35 }
36 else
37 {
38 windowWith 1 validLen;
39 }
40
41 if (tags.ContainsKey(0028,1050))
42 {
43 Match m r.Match(tags[0028,1050].Substring(5));
44 if (m.Success)
45 windowCenter int.Parse(m.Value);//窗位
46 else
47 windowCenter windowWith / 2;
48 }
49 else
50 {
51 windowCenter windowWith / 2;
52 }
53 }
54
55 #endregion
虽然原理是正确的 但还是会产生乱七八糟的问题 始终跟别人标准的不一样 标准的窗值调整请参考这篇论文医学图像的调窗技术及DI 基本上照着他做就OK 只是有些地方没讲太明白。 那么我这篇文章基本上就是他经过代码实践后的翻版 参考了过后那么我们就要照标准的流程来处理 字节序转换 后截取有效位 然后根据有无符号进行值转换 还是原来的代码 中间加上这几句 1 if (isLitteEndian false)
2 Array.Reverse(pixData, 0, 2);
3
4 if (signed false)
5 gray BitConverter.ToUInt16(pixData, 0);
6 else
7 gray BitConverter.ToInt16(pixData, 0);
这么做了后我们发现 1.2.840.113619.2.81.290.23014.2902.1.6.20031230.260236.dcm 那幅图像显示对了
但是测试另一幅 还是不对 CT.dcm 这幅图像看上去是CR的图其实是CT序列图像里的一幅 ,因为是CT影像 所以要做值偏移处理 值值×斜率截距 这是高中学的称为HU 至于为什么要这样我也不知道 dicom标准规定的 如果是CT图像需要进行偏移处理则进行偏移处理 然后进行窗值映射。 1 //字节序翻转2 if (isLitteEndian false)3 Array.Reverse(pixData, 0, 2);4 //取值5 if (signed false)6 gray BitConverter.ToUInt16(pixData, 0);7 else8 gray BitConverter.ToInt16(pixData, 0);9 //特别针对CT图像 值值x斜率截距
10 if ((rescaleSlop ! 1.0f) || (rescaleinter ! 0.0f))
11 {
12 float fValue (float)gray * rescaleSlop rescaleinter;
13 gray (short)fValue;
14 } 所有的数据都读取完成后再setPixel 这种操作效率太低了。所以我们还得优化下代码 先lockbits 然后一边读取一边更新数据。 这是整理后的标准调窗代码有点多哈 慢慢看我说得挺简单 中间有各种复杂情况哈 请参考上面说的步骤及论文里的说明来 1 public unsafe Bitmap convertTo8(BinaryReader streamdata, int colors, bool littleEdition, bool signed, short nHighBit,2 int dataLen, float rescaleSlope, float rescaleIntercept, float windowCenter, float windowWidth, int width, int height)3 {4 Bitmap bmp new Bitmap(width, height);5 Graphics gg Graphics.FromImage(bmp);6 gg.Clear(Color.Green);7 BitmapData bmpDatas bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),8 System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);9 long numPixels width * height;10 11 if (colors 3)//color Img12 {13 byte* p (byte*)bmpDatas.Scan0;14 int indx 0;15 for (int i 0; i bmp.Height; i)16 {17 for (int j 0; j bmp.Width; j)18 {19 p[indx 2] streamdata.ReadByte();20 p[indx 1] streamdata.ReadByte();21 p[indx] streamdata.ReadByte();22 indx 3;23 }24 }25 }26 else if (colors 1)//grayscale Img27 {28 byte* p (byte*)bmpDatas.Scan0;29 int nMin ~(0xffff (nHighBit 1)), nMax 0;30 int indx 0;//byteData index31 32 for (int n 0; n numPixels; n)//pixNum index33 {34 short nMask; nMask (short)(0xffff (nHighBit 1));35 short nSignBit;36 37 byte[] pixData null;38 short pixValue 0;39 40 pixData streamdata.ReadBytes(dataLen / 8 * colors);41 if (nHighBit 15 nHighBit 7)42 {43 if (littleEdition false)44 Array.Reverse(pixData, 0, 2);45 46 // 1. Clip the high bits.47 if (signed false)// Unsigned integer48 {49 pixValue (short)((~nMask) (BitConverter.ToInt16(pixData, 0)));50 }51 else52 {53 nSignBit (short)(1 nHighBit);54 if (((BitConverter.ToInt16(pixData, 0)) nSignBit) ! 0)55 pixValue (short)(BitConverter.ToInt16(pixData, 0) | nMask);56 else57 pixValue (short)((~nMask) (BitConverter.ToInt16(pixData, 0)));58 }59 }60 else if (nHighBit 7)61 {62 if (signed false)// Unsigned integer63 {64 nMask (short)(0xffff (nHighBit 1));65 pixValue (short)((~nMask) (pixData[0]));66 }67 else68 {69 nMask (short)(0xffff (nHighBit 1));70 nSignBit (short)(1 nHighBit);71 if (((pixData[0]) nSignBit) ! 0)72 pixValue (short)((short)pixData[0] | nMask);73 else74 pixValue (short)((~nMask) (pixData[0]));75 }76 77 }78 79 // 2. Rescale if needed (especially for CT)80 if ((rescaleSlope ! 1.0f) || (rescaleIntercept ! 0.0f))81 {82 float fValue pixValue * rescaleSlope rescaleIntercept;83 pixValue (short)fValue;84 }85 86 // 3. Window-level or rescale to 8-bit87 if ((windowCenter ! 0) || (windowWidth ! 0))88 {89 float fSlope;90 float fShift;91 float fValue;92 93 fShift windowCenter - windowWidth / 2.0f;94 fSlope 255.0f / windowWidth;95 96 fValue ((pixValue) - fShift) * fSlope;97 if (fValue 0)98 fValue 0;99 else if (fValue 255)
100 fValue 255;
101
102
103 p[indx] (byte)fValue;
104 p[indx] (byte)fValue;
105 p[indx] (byte)fValue;
106 }
107 else
108 {
109 // We will map the whole dynamic range.
110 float fSlope;
111 float fValue;
112
113
114 int i 0;
115 // First compute the min and max.
116 if (n 0)
117 nMin nMax pixValue;
118 else
119 {
120 if (pixValue nMin)
121 nMin pixValue;
122
123 if (pixValue nMask)
124 nMask pixValue;
125 }
126
127 // Calculate the scaling factor.
128 if (nMax ! nMin)
129 fSlope 255.0f / (nMax - nMin);
130 else
131 fSlope 1.0f;
132
133 fValue ((pixValue) - nMin) * fSlope;
134 if (fValue 0)
135 fValue 0;
136 else if (fValue 255)
137 fValue 255;
138
139 p[indx] (byte)fValue;
140 }
141 }
142 }
143
144 bmp.UnlockBits(bmpDatas);
145 //bmp.Dispose();
146 return bmp;
147 }
完整源码及测试数据下载 猛击此处