【转】Dicom文件解析!!!!!!

转自:https://blog.csdn.net/leaf6094189/article/details/8510325

Dicom全称是医学数字图像与通讯,这里讲的暂不涉及通讯那方面的问题 只讲*.dcm 也就是diocm格式文件的读取,读取本身是没啥难度的 无非就是字节码数据流处理。只不过确实比较繁琐。
好了 正题

分析


整体结构,先是128字节所谓的导言部分,说俗点就是没啥意义的破数据 跳过就是了,然后是dataElement,采取依次排列的方式,就是一个dataElement接一个dataElement的方式排到文件结尾。通俗的讲dataElement就是指tag,也就是Dicom标准里定义的数据字典。tag是4个字节表示的 前两字节是组号后两字节是偏移号 比如0008,0018。所有dataElement在文件中都是按tag排序的 比如0002,0001  0002,0002  0003,0011
文件整体结构如下:

再把论文里的这图贴上来,因为总结的比较好。单个dataElement的结构如下:

显示VR:VR为OB OW OF UT SQ UN的数据元素结构

组号

元素号

VR

预留

值长度

数据元素值

2

2

2

2(0x00,0x00)

4

由数据长度决定

显示VR:VR为普通类型时的数据元素结构(少了预留那一行)

组号

元素号

VR

值长度

数据元素值

2

2

2

4

由数据长度决定

隐式VR 时元素结构

组号

元素号

值长度

数据元素值

2

2

4

由数据长度决定

 


要问VR是啥东东 ,值表示法 啥叫值表示法啊 俺不懂 int string short ushort 懂不 就是这个意思,Dicom标准真坑爹 非要整个怪怪的概念。
VR总共27个 跟c#值类型对应关系我都写好了:

 

 1 string getVF(string VR, byte[] VF)2 {3     string VFStr = string.Empty;4     switch (VR)5     {6         case "SS":7             VFStr = BitConverter.ToInt16(VF, 0).ToString();8             break;9         case "US":
10             VFStr = BitConverter.ToUInt16(VF, 0).ToString();
11 
12             break;
13         case "SL":
14             VFStr = BitConverter.ToInt32(VF, 0).ToString();
15 
16             break;
17         case "UL":
18             VFStr = BitConverter.ToUInt32(VF, 0).ToString();
19 
20             break;
21         case "AT":
22             VFStr = BitConverter.ToUInt16(VF, 0).ToString();
23 
24             break;
25         case "FL":
26             VFStr = BitConverter.ToSingle(VF, 0).ToString();
27 
28             break;
29         case "FD":
30             VFStr = BitConverter.ToDouble(VF, 0).ToString();
31 
32             break;
33         case "OB":
34             VFStr = BitConverter.ToString(VF, 0);
35             break;
36         case "OW":
37             VFStr = BitConverter.ToString(VF, 0);
38             break;
39         case "SQ":
40             VFStr = BitConverter.ToString(VF, 0);
41             break;
42         case "OF":
43             VFStr = BitConverter.ToString(VF, 0);
44             break;
45         case "UT":
46             VFStr = BitConverter.ToString(VF, 0);
47             break;
48         case "UN":
49             VFStr = Encoding.Default.GetString(VF);
50             break;
51         default:
52             VFStr = Encoding.Default.GetString(VF);
53             break;
54     }
55     return VFStr;
56 }

 

找个dicom文件在十六进制编辑器下瞧瞧 给你整明白:

所有dataElement从前到后按tag又可简单分段:

文件元dataElement不受传输语法影响 总是以显示VR方式表示  因为它里面就定义了传输语法
普通dataElement受传输语法影响 显示VR表示方式还是隐式VR表示方式
像素数据dataElement最重要也是最大的一个数据项 其实存储的就是图像数据



有几个特殊的tag很重要 前面说过了tag就是dicom里定义的字典。文件元dataElement 和跟像素数据相关的dataElement 都很重要,其他的很多 如果全部照顾完的话估计得写上千行switch语句吧,所以没有必要一般我们一般只抓取关键的tag。并且在隐式语法下要确定VR也必须根据字典来确定
关键的tag如下:

 

  1 string getVR(string tag)2 {3     switch (tag)4     {5         case "0002,0000"://文件元信息长度6             return "UL";7             break;8         case "0002,0010"://传输语法9             return "UI";10             break;11         case "0002,0013"://文件生成程序的标题12             return "SH";13             break;14         case "0008,0005"://文本编码15             return "CS";16             break;17         case "0008,0008":18             return "CS";19             break;20         case "0008,1032"://成像时间21             return "SQ";22             break;23         case "0008,1111":24             return "SQ";25             break;26         case "0008,0020"://检查日期27             return "DA";28             break;29         case "0008,0060"://成像仪器30             return "CS";31             break;32         case "0008,0070"://成像仪厂商33             return "LO";34             break;35         case "0008,0080":36             return "LO";37             break;38         case "0010,0010"://病人姓名39             return "PN";40             break;41         case "0010,0020"://病人id42             return "LO";43             break;44         case "0010,0030"://病人生日45             return "DA";46             break;47         case "0018,0060"://电压48             return "DS";49             break;50         case "0018,1030"://协议名51             return "LO";52             break;53         case "0018,1151":54             return "IS";55             break;56         case "0020,0010"://检查ID57             return "SH";58             break;59         case "0020,0011"://序列60             return "IS";61             break;62         case "0020,0012"://成像编号63             return "IS";64             break;65         case "0020,0013"://影像编号66             return "IS";67             break;68         case "0028,0002"://像素采样1为灰度3为彩色69             return "US";70             break;71         case "0028,0004"://图像模式MONOCHROME2为灰度72             return "CS";73             break;74         case "0028,0010"://row高75             return "US";76             break;77         case "0028,0011"://col宽78             return "US";79             break;80         case "0028,0100"://单个采样数据长度81             return "US";82             break;83         case "0028,0101"://实际长度84             return "US";85             break;86         case "0028,0102"://采样最大值87             return "US";88             break;89         case "0028,1050"://窗位90             return "DS";91             break;92         case "0028,1051"://窗宽93             return "DS";94             break;95         case "0028,1052":96             return "DS";97             break;98         case "0028,1053":99             return "DS";
100             break;
101         case "0040,0008"://文件夹标签
102             return "SQ";
103             break;
104         case "0040,0260"://文件夹标签
105             return "SQ";
106             break;
107         case "0040,0275"://文件夹标签
108             return "SQ";
109             break;
110         case "7fe0,0010"://像素数据开始处
111             return "OW";
112             break;
113         default:
114             return "UN";
115             break;
116     }
117 }

其中,最关键的两个tag:
1)0002,0010
普通tag的读取方式 little字节序还是big字节序  隐式VR还是显示VR。由它的值决定

 

 1 switch (VFStr)2 {3     case "1.2.840.10008.1.2.1\0"://显示little4         isLitteEndian = true;5         isExplicitVR = true;6         break;7     case "1.2.840.10008.1.2.2\0"://显示big8         isLitteEndian = false;9         isExplicitVR = true;
10         break;
11     case "1.2.840.10008.1.2\0"://隐式little
12         isLitteEndian = true;
13         isExplicitVR = false;
14         break;
15     default:
16         break;
17 }

2)7fe0,0010
像素数据开始处

整理

根据以上的分析相信解析一个dicom格式文件的过程已经很清晰了吧
第一步:跳过128字节导言部分,并读取"DICM"4个字符 以确认是dicom格式文件
第二步:读取第一部分 也就是非常重要的文件元dataElement 。读取所有0002开头的tag 并根据0002,0010的值确定传输语法。文件元tag部分的数据元素都是以显示VR的方式表示的 读取它的值 也就是字节码处理 别告诉我说你不会字节码处理哈。传输语法 说得那么官方,你就忽悠吧 其实就确定两个东西而已 
1.字节序 这个基本上都是little字节序。举个例子吧十进制数 35280 用十六进制表示是0xff00 但是存储到文件中你用十六进制编辑器打开你看到的是这个样子00ff 这就是little字节序。平常我们用的x86PC在windows下都是little字节序 包括AMD的CPU。别太较真 较真的话这个问题又可以写篇博客了。
2.确定从0002以后的dataElement的VR是显示还是隐式。说来说去0002,0010的值就 那么固定几个 并且只能是那么几个 这些都在那个北美放射学会定义的dicom标准的第六章 有说明 :

1.2.840.10008.1.2Implicit VR Little Endian: Default Transfer Syntax for DICOMTransfer Syntax
1.2.840.10008.1.2.1Explicit VR Little EndianTransfer Syntax
1.2.840.10008.1.2.2Explicit VR Big EndianTransfer Syntax

上面的那段代码其实就是这个表格的实现,讲到这里你会觉得多么的坑爹啊 是的dicom面向对象的破概念非常烦的。
第三步:读取普通tag 直到搜寻到7fe0,0010,这个是最大的,用来存储图像数据的 dataElement。我们在前一步已经把VR是显示还是隐式确定 通过前面的图 ,也就是字节码处理而已无任何压力。显式情况下只需要根据VR 和Len 确定数据类型 和 数据长度,然后直接读取就可以了。隐式情况下这破玩艺儿有点烦,只能根据tag 字典确定它是什么VR,推测出数据类型后才能读取。关于这个字典也在dicom标准的第六章。上面倒数第二段的代码已经把重要的字典都列了出来。
第四步:读取灰度像素数据并调窗,然后以GDI的方式显示出来。 说实话开始我还以为dicom这种号称医学什么影像的专家制定出来的标准 读取像素数据应该有难度吧 结果没想到这么的傻瓜。直接按像素从左到右从上到下 一行行依次扫描。两个字节表示1个像素普通Dicom格式存储的是16位的灰度图像,其实有效数据只有12位,除去0 所以最高值是2047。比如CT值 从-1000到+1000,空气的密度为-1000 水的密度为0 金属的密度为+1000 总共的值为2000


调窗技术:
即把12级灰度的数据 通过调节窗宽窗位并让他在RGB模式下显示出来。还技术呢 说实话这个也是没什么技术含量的所谓的技术,两句代码给你整明白。
调节窗宽窗位到底什么意思,12位的数据那么它总共有2047个等级的灰度 没有显示设备可以体现两千多级的明暗度 就算有我们肉眼也无法分辨更无法诊断。我们要诊断是要提取关键密度值的数据 在医院放射科呆久了你一定经常听医生讲什么骨窗 肺窗 之类的词儿,这就是指的这个“窗”。比如有病人骨折了打了钢板我们想看金属部分来诊断 那么我们应该抓取CT值从800到1000 密度的像素 也就是灰度值 然后把它放到RGB模式下显示,低于800的不论值大小都显示黑色 高于1000的不论值大小都显示白色。
通过以上例子那么这个范围1000-800=200 这个200表示窗宽,800+(200/2)这个表示窗位
一句话,从2047个等级的灰度里选取一个范围放到0~255的灰度环境里显示。

怎样把12位灰度影射到8位灰度显示出来呢,还怎么显示 上面方法都给说明了基本上算半成品了。联想到角度制弧度制,设要求的8位灰度值为x 已知的12位灰度值为y那么:x/255=y/2047 那么x=255y/2047 原理不多讲 等比中项十字相乘法 这个是初中的知识哈。初中没读过的童鞋飘过。。。

原理过程讲完了

代码走起

 

  1 class DicomHandler2     {3         string fileName = "";4         Dictionary<string, string> tags = new Dictionary<string, string>();//dicom文件中的标签5         BinaryReader dicomFile;//dicom文件流6 7         //文件元信息8         public Bitmap gdiImg;//转换后的gdi图像9         UInt32 fileHeadLen;//文件头长度10         long fileHeadOffset;//文件数据开始位置11         UInt32 pixDatalen;//像素数据长度12         long pixDataOffset = 0;//像素数据开始位置13         bool isLitteEndian = true;//是否小字节序(小端在前 、大端在前)14         bool isExplicitVR = true;//有无VR15 16         //像素信息17         int colors;//颜色数 RGB为3 黑白为118         public int windowWith = 2048, windowCenter = 2048 / 2;//窗宽窗位19         int rows, cols;20         public void readAndShow(TextBox textBox1)21         {22             if (fileName == string.Empty)23                 return;24             dicomFile = new BinaryReader(File.OpenRead(fileName));25 26             //跳过128字节导言部分27             dicomFile.BaseStream.Seek(128, SeekOrigin.Begin);28 29             if (new string(dicomFile.ReadChars(4)) != "DICM")30             {31                 MessageBox.Show("没有dicom标识头,文件格式错误");32                 return;33             }34 35 36             tagRead();37 38             IDictionaryEnumerator enor = tags.GetEnumerator();39             while (enor.MoveNext())40             {41                 if (enor.Key.ToString().Length > 9)42                 {43                     textBox1.Text += enor.Key.ToString() + "\r\n";44                     textBox1.Text += enor.Value.ToString().Replace('\0', ' ');45                 }46                 else47                     textBox1.Text += enor.Key.ToString() + enor.Value.ToString().Replace('\0', ' ') + "\r\n";48             }49             dicomFile.Close();50         }51         public  DicomHandler(string _filename)52         {53             fileName = _filename;54         }55 56         public void saveAs(string filename)57         {58             switch (filename.Substring(filename.LastIndexOf('.')))59             {60                 case ".jpg":61                     gdiImg.Save(filename, System.Drawing.Imaging.ImageFormat.Jpeg);62                     break;63                 case ".bmp":64                     gdiImg.Save(filename, System.Drawing.Imaging.ImageFormat.Bmp);65                     break;66                 case ".png":67                     gdiImg.Save(filename, System.Drawing.Imaging.ImageFormat.Png);68                     break;69                 default:70                     break;71             }72         }73         public bool getImg( )//获取图像 在图像数据偏移量已经确定的情况下74         {75             if (fileName == string.Empty)76                 return false;77             78             int dataLen, validLen;//数据长度 有效位79             int imgNum;//帧数80 81             rows = int.Parse(tags["0028,0010"].Substring(5));82             cols = int.Parse(tags["0028,0011"].Substring(5));83 84             colors = int.Parse(tags["0028,0002"].Substring(5));85             dataLen = int.Parse(tags["0028,0100"].Substring(5));86             validLen = int.Parse(tags["0028,0101"].Substring(5));87 88             gdiImg = new Bitmap(cols, rows);89 90             BinaryReader dicomFile = new BinaryReader(File.OpenRead(fileName));91 92             dicomFile.BaseStream.Seek(pixDataOffset, SeekOrigin.Begin);93 94             long reads = 0;95             for (int i = 0; i < gdiImg.Height; i++)96             {97                 for (int j = 0; j < gdiImg.Width; j++)98                 {99                     if (reads >= pixDatalen)
100                         break;
101                     byte[] pixData = dicomFile.ReadBytes(dataLen / 8 * colors);
102                     reads += pixData.Length;
103 
104                     Color c = Color.Empty;
105                     if (colors == 1)
106                     {
107                         int grayGDI;
108 
109                         double gray = BitConverter.ToUInt16(pixData, 0);
110                         //调窗代码,就这么几句而已 
111                         //1先确定窗口范围 2映射到8位灰度
112                         int grayStart = (windowCenter - windowWith / 2);
113                         int grayEnd = (windowCenter + windowWith / 2);
114 
115                         if (gray < grayStart)
116                             grayGDI = 0;
117                         else if (gray > grayEnd)
118                             grayGDI = 255;
119                         else
120                         {
121                             grayGDI = (int)((gray - grayStart) * 255 / windowWith);
122                         }
123 
124                         if (grayGDI > 255)
125                             grayGDI = 255;
126                         else if (grayGDI < 0)
127                             grayGDI = 0;
128                         c = Color.FromArgb(grayGDI, grayGDI, grayGDI);
129                     }
130                     else if (colors == 3)
131                     {
132                         c = Color.FromArgb(pixData[0], pixData[1], pixData[2]);
133                     }
134 
135                     gdiImg.SetPixel(j, i, c);
136                 }
137             }
138 
139             dicomFile.Close();
140             return true;
141         }
142         void tagRead()//不断读取所有tag 及其值 直到碰到图像数据 (7fe0 0010 )
143         {
144             bool enDir = false;
145             int leve = 0;
146             StringBuilder folderData = new StringBuilder();//该死的文件夹标签
147             string folderTag = "";
148             while (dicomFile.BaseStream.Position + 6 < dicomFile.BaseStream.Length)
149             {
150                 //读取tag
151                 string tag = dicomFile.ReadUInt16().ToString("x4") + "," +
152                 dicomFile.ReadUInt16().ToString("x4");
153 
154                 string VR = string.Empty;
155                 UInt32 Len = 0;
156                 //读取VR跟Len
157                 //对OB OW SQ 要做特殊处理 先置两个字节0 然后4字节值长度
158                 //------------------------------------------------------这些都是在读取VR一步被阻断的情况
159                 if (tag.Substring(0, 4) == "0002")//文件头 特殊情况
160                 {
161                     VR = new string(dicomFile.ReadChars(2));
162 
163                     if (VR == "OB" || VR == "OW" || VR == "SQ" || VR == "OF" || VR == "UT" || VR == "UN")
164                     {
165                         dicomFile.BaseStream.Seek(2, SeekOrigin.Current);
166                         Len = dicomFile.ReadUInt32();
167                     }
168                     else
169                         Len = dicomFile.ReadUInt16();
170                 }
171                 else if (tag == "fffe,e000" || tag == "fffe,e00d" || tag == "fffe,e0dd")//文件夹标签
172                 {
173                     VR = "**";
174                     Len = dicomFile.ReadUInt32();
175                 }
176                 else if (isExplicitVR == true)//有无VR的情况
177                 {
178                     VR = new string(dicomFile.ReadChars(2));
179 
180                     if (VR == "OB" || VR == "OW" || VR == "SQ" || VR == "OF" || VR == "UT" || VR == "UN")
181                     {
182                         dicomFile.BaseStream.Seek(2, SeekOrigin.Current);
183                         Len = dicomFile.ReadUInt32();
184                     }
185                     else
186                         Len = dicomFile.ReadUInt16();
187                 }
188                 else if (isExplicitVR == false)
189                 {
190                     VR = getVR(tag);//无显示VR时根据tag一个一个去找 真烦啊。
191                     Len = dicomFile.ReadUInt32();
192                 }
193                 //判断是否应该读取VF 以何种方式读取VF
194                 //-------------------------------------------------------这些都是在读取VF一步被阻断的情况
195                 byte[] VF = { 0x00 };
196 
197                 if (tag == "7fe0,0010")//图像数据开始了
198                 {
199                     pixDatalen = Len;
200                     pixDataOffset = dicomFile.BaseStream.Position;
201                     dicomFile.BaseStream.Seek(Len, SeekOrigin.Current);
202                     VR = "UL";
203                     VF = BitConverter.GetBytes(Len);
204                 }
205                 else if ((VR == "SQ" && Len == UInt32.MaxValue) || (tag == "fffe,e000" && Len == UInt32.MaxValue))//靠 遇到文件夹开始标签了
206                 {
207                     if (enDir == false)
208                     {
209                         enDir = true;
210                         folderData.Remove(0, folderData.Length);
211                         folderTag = tag;
212                     }
213                     else
214                     {
215                         leve++;//VF不赋值
216                     }
217                 }
218                 else if ((tag == "fffe,e00d" && Len == UInt32.MinValue) || (tag == "fffe,e0dd" && Len == UInt32.MinValue))//文件夹结束标签
219                 {
220                     if (enDir == true)
221                     {
222                         enDir = false;
223                     }
224                     else
225                     {
226                         leve--;
227                     }
228                 }
229                 else
230                     VF = dicomFile.ReadBytes((int)Len);
231 
232                 string VFStr;
233 
234                 VFStr = getVF(VR, VF);
235 
236                 //----------------------------------------------------------------针对特殊的tag的值的处理
237                 //特别针对文件头信息处理
238                 if (tag == "0002,0000")
239                 {
240                     fileHeadLen = Len;
241                     fileHeadOffset = dicomFile.BaseStream.Position;
242                 }
243                 else if (tag == "0002,0010")//传输语法 关系到后面的数据读取
244                 {
245                     switch (VFStr)
246                     {
247                         case "1.2.840.10008.1.2.1\0"://显示little
248                             isLitteEndian = true;
249                             isExplicitVR = true;
250                             break;
251                         case "1.2.840.10008.1.2.2\0"://显示big
252                             isLitteEndian = false;
253                             isExplicitVR = true;
254                             break;
255                         case "1.2.840.10008.1.2\0"://隐式little
256                             isLitteEndian = true;
257                             isExplicitVR = false;
258                             break;
259                         default:
260                             break;
261                     }
262                 }
263                 for (int i = 1; i <= leve; i++)
264                     tag = "--" + tag;
265                 //------------------------------------数据搜集代码
266                 if ((VR == "SQ" && Len == UInt32.MaxValue) || (tag == "fffe,e000" && Len == UInt32.MaxValue) || leve > 0)//文件夹标签代码
267                 {
268                     folderData.AppendLine(tag + "(" + VR + "):" + VFStr);
269                 }
270                 else if (((tag == "fffe,e00d" && Len == UInt32.MinValue) || (tag == "fffe,e0dd" && Len == UInt32.MinValue)) && leve == 0)//文件夹结束标签
271                 {
272                     folderData.AppendLine(tag + "(" + VR + "):" + VFStr);
273                     tags.Add(folderTag + "SQ", folderData.ToString());
274                 }
275                 else
276                     tags.Add(tag, "(" + VR + "):" + VFStr);
277             }
278         }
279 }

好了收工。
测试下成果

 

 1 if (openFileDialog1.ShowDialog() != DialogResult.OK)2     return;3 4 string fileName = openFileDialog1.FileName;5 6 handler = new DicomHandler(fileName);7 8 handler.readAndShow(textBox1);9 
10 this.Text = "DicomViewer-" + openFileDialog1.FileName;
11 
12 
13 backgroundWorker1.RunWorkerAsync();

这里处理gdi位图的时候直接用的setPix 处理速度比较慢所以用了backgroundWorker,实际应用中请使用内存缓冲跟指针的方式
否则效率低了是得不到客户的认可的哦,gdi位图操作可使用lockBits加指针的方式 ,12位的灰度像素数据可以第一次读取后缓存到内存中 以方便后面调窗的快速读取
优化这点代码也不难哈 对指针什么的熟点就行了,前几章都有。

这是ezDicom 经过公认测试的软件 我们来跟他对比一下,打开 
调窗测试,我们注意到两个东西 在没有窗宽窗位时 默认窗宽是2047+1即2048  窗位是2048/2即1024
直观的感受是调窗宽像在调图像对比度 ,调窗位像在调图像亮度。
窗宽为255的时候图像是最瑞丽的 因为255其实就是8位图像的默认窗宽。
注意窗位那里有小小区别,ez窗位显示的是根据1024那里为0开始偏移 而我的程序是根据窗宽中间值没有偏移
没有偏移的情况稍微符合逻辑点吧。
但是可以看到原理是一样的 结果是一样的。


源码下载测试dcm文件: 猛击此处

本文来自:http://www.cnblogs.com/assassinx/archive/2013/01/09/dicomViewer.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/436110.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VS2008 JS脚本调试总是调试旧代码 真不知道怎么回事?谁能帮帮我呀!

如图,[dynamic]标记的是调试的元文件 xml1.htm[dynamic]和xml.js[dynamic] 我更改后的文件是xml1.htm和xml.js,见图1 2 调试的文件有debugger 更改后的文件我把debugger注了 js脚本中我把变量给改了见3 4 ,所以在浏览新的xml1.htm时,竟然报对象找不到,真实受不了了!!! 为什么我…

【转】C#开发PACS医学影像处理系统(一):开发背景和功能预览

转自&#xff1a;https://www.cnblogs.com/Uncle-Joker/p/13646949.html 本系列文章将从以下模块和大家分享和讨论使用C#开发医学软件PACS和RIS系统&#xff0c; 国内相关资料比较少&#xff0c;也借此机会丰富一下医学软件开发生态&#xff0c;讨论技术难点&#xff0c;希望…

《WF编程》系列之30 - 基本活动:错误处理

《WF编程》系列之30 - 基本活动:错误处理 4.3 错误处理 Fault,故障,现在官方已经将其翻译为错误,那么以后的随笔中我也就采用官方的翻译吧. 错误处理也属于流程控制的一部分,这一节我来介绍一下有关错误处理的活动.错误是指在工作流执行期间发生的异常.我们可以使用错误处理程…

oracle错误27101,ORA-27101ORA-01034错误解决

Oracle已经启动&#xff0c;连接sqlplus后&#xff0c;进行查询&#xff0c;出现下面错误ORA-01034: ORACLE not availableORA-27101: shared memory realm doesOracle已经启动&#xff0c;连接sqlplus后&#xff0c;进行查询&#xff0c;出现下面错误ORA-01034: ORACLE not av…

NOD32升级ID获取器For流星无语更新了

NOD32升级ID获取器For流星无语更新了一下,现在可以直接把用户名密码写入注册表了,连复制/粘贴操作都可以免了...转载于:https://www.cnblogs.com/lxwy/archive/2008/09/05/4420722.html

【转】【C#】使用fo-dicom完成BMP,JPG,PNG图片转换为DICOM文件

转自&#xff1a;https://developer.aliyun.com/article/672065 最近研究了一下DICOM和BMP文件转换的问题&#xff0c;也是很头大。度娘了很久&#xff0c;也在CSDN等论坛看到一些断断续续的文件&#xff0c;最主要的是代码只是片断&#xff0c;不是完整的实现。头大了。 首先…

C++设计模式之二 AbstractFactory模式

设计模式的目的就是尽量减少“变化”对程序的影响&#xff0c;尤其是对客户程序的影响。AbstractFactory模式作为创建型模式的一种&#xff0c;解决的就是“new”在变化中可能引起的问题。 先来看看new有何种不好&#xff0c;举个创建汽车的车门的例子&#xff1a; 很自然的一…

改写DataCogs在MOSS列表中实现三级联动字段

项目中有需求需要实现列表中3级的字段联动。参照小熊的[分享]修复DataCogs二级联动FieldControl支持中文 &#xff0c;改造一下&#xff0c;实现了3级联动字段。另外&#xff0c;还可以实现一个列表中同时有两个&#xff0c;或两个以上互相独立的联动字段&#xff0c; 详细的字…

【转】01.Dicom 学习笔记-DICOM C-Store 消息服务

转自&#xff1a;https://www.jianshu.com/p/bab6a85d3486 引言 之前总体介绍了 DICOM 的消息服务&#xff0c;可以参考这篇博文&#xff0c;但是有关每个服务的详细信息没有讲解&#xff0c;本文就结合开源 DICOM 库 fo-dicom 详细介绍一下 C-Store 服务。 名词简介 在正式讲…

【转】02.Dicom 学习笔记-DICOM C-Find 消息服务

引言 前篇介绍了 DICOM C-Store 消息服务&#xff0c;本文结合开源 DICOM 库 fo-dicom 详细介绍一下 C-Find 服务。 C-Find 消息服务 C-Find 服务是一个查询服务&#xff0c;用于一个 DIMSE-service-user 在同等的DIMSE-service-user 上查询复合 SOP 实例的属性满足查询条件给出…

【转】03.Dicom 学习笔记-DICOM C-Get 消息服务

转自&#xff1a;https://www.jianshu.com/p/c7f5b9fa597c 引言 前篇介绍了 DICOM C-Find 消息服务&#xff0c;本文结合开源 DICOM 库 fo-dicom 详细介绍一下 C-Get 服务。 C-Get 消息服务 C-Get 服务主要用于获取影像&#xff0c;用于一个 DIMSE-service-user 在同等的DIMSE…

php dao类设计,DAO数据访问对象设计 - GoFrame官网 - 类似PHP-Laravel, Java-SpringBoot的Go企业级开发框架...

关于DAO数据访问对象设计其实是关于GoFrame框架工程化实践中比较重要一块设计。DAO设计结合GoFrame的ORM组件性能和易用性都很强&#xff0c;可以极大提高开发和维护效率。看完本章节内容之后&#xff0c;小伙伴们应该能够理解并体会到使用DAO数据库访问对象设计的优点。一、现…

再谈“我是怎么招聘程序员的”

我以前写过一篇“我是怎么招聘程序员的”的文章&#xff08;在CSDN那里有很多人进行了回复&#xff09;。今天&#xff0c;我想再谈谈关于招聘和面试这方面的东西&#xff0c;主要是以下这些原因&#xff1a; 近半年来我在进行了大量的招聘工作&#xff0c;对面试有一些新的体…

【转】04.Dicom 学习笔记-DICOM C-Move 消息服务

引言 前篇介绍了 DICOM C-Get 消息服务&#xff0c;本文结合开源 DICOM 库 fo-dicom 详细介绍一下 C-Move 服务。 C-Move 消息服务 C-Move 服务可以用来获取影像和转存影像&#xff0c;用于一个 DIMSE-service-user 在同等的 DIMSE-service-user 上查询复合 SOP 实例的属性满足…

【转】05.Dicom 学习笔记-DICOM C-Echo 消息服务

引言 经过前面几篇的介绍&#xff0c;DIMSE-C 消息服务这块已经讲解了差不多了&#xff0c;还剩最后一个 C-Echo 消息服务&#xff0c;这个服务相对前面的4个服务来说更简单一些&#xff0c;本文结合开源 DICOM 库 fo-dicom 详细介绍一下 C-Echo 服务。 C-Echo 消息服务 首先来…

哪些设计模式最值得学习

最近又在首页看到几篇设计模式相关的学习随笔。回想起来&#xff0c;这几年在园子里发布的有关设计模式的随笔都有一个共同的特点。那就是Factory和Singleton居多&#xff0c;如果是系列的&#xff0c;也往往是从这两个模式开始的。由于能够坚持把《设计模式》中所有模式都写完…

【转】000.DICOM:DICOM标准学习路线图(初稿)!!!!!!!!!!!!

转自&#xff1a;https://zssure.blog.csdn.net/article/details/49231303 题记&#xff1a; DICOM医学图像处理专栏撰写已有两个年头&#xff0c;积累了近百篇文章。 起初 只是用于记录自己科研、工作中遇到的疑难问题&#xff0c;专注于图像处理&#xff08;主要是医学图像…

对比 SQL Server 2005 和 Oracle

在 Microsoft Windows Server 上运行的 SQL Server 2005&#xff0c;为企业级关系数据库和分析解决方案提供了一个平台&#xff0c;在安全、可用性、与 Visual Studio 的整合度&#xff0c;从小企业到大企业的可扩展性以及低费用方面胜过了Oracle 10g。探索下列信息以发现在 这…

【转】pacs定位线_C#开发PACS医学影像处理系统(十五):Dicom影像交叉定位线算法

转自&#xff1a;https://www.cnblogs.com/Uncle-Joker/p/13686618.html 1.定位线概念&#xff1a;某个方位的影像在另一个方向的影像上的投影相交线&#xff0c;例如横断面(从头到脚的方向)在矢状面(从左手到右手)上的影像投影面交线。 举个例子&#xff1a;右边的是MR(核磁共…

C++设计模式之 简单工厂模式讲解(历史上最简单明白的例子)

工作之余&#xff0c;在看资料过程中发现一个极易理解的简单工厂模式的例子&#xff0c;自己亲自试练一番,感觉对这个设计模式不熟悉的朋友&#xff0c; 一看马上就知道是什么回事了。 简单工厂模式根据提供给它的数据&#xff0c;返回几个可能类中的一个类的实例。通常它返的…