DICOM图像中灰度理解

作者: rdgbrain | 来源:发表于2021-04-29 10:31 被阅读0次

    背景

    前段时间项目组开发了一款在线影像文件质控的web工具,当时研究员提出了这样一个需求,当点击图像的某一个点时,将这个点不同时间上的(MRI扫描是持续一段时间的,扫描相同部位时,根据扫描频率就会产生多张图像)灰度值用折线图描绘出来。要想完成这个需求,首先就要弄清楚灰度值是什么。

    理解含义

    百度百科

    从百度百科上可以看出灰度通俗的讲就是用一个数字描述的黑白程度,值越小图片越暗。这样的理解对应想知道概念的人基本足够了,但是对于咱们coding的工程师,还需要进一步了解。

    先来看看常见的的一些概念

    RGB:我们生活中通过三原色深浅组成不同的颜色,前端开发中常用RGB来指定色彩,R、G、B 都是 0-255, 比如 (255,255,255) 表示白色,用16进制表示255就是ff, 所以255,255,255 可以表示为 ffffff, 在css样式表中写为 #ffffff。
    像素:图片我们都知道是由多个像素组成的,是图像的最小单元,如果这个单元里只有一个bit大小,也就是只有0和1两个值,由这种像素组成的图像称二值图像,显然它可以表示一个黑白图。这个单位里也可以存一个16位二进制数,24位..., 可以想象存储的越大,一个像素能表达的颜色就越丰富,整体图像呈现就越精细,当然图片占用的空间也就越大。

    1位图像(即二值图像)只能表示黑白,那二位图像就可以表示4种颜色(00,01,10,11,VGA),依次类推,4位(16色,VGA),8位(256色),16位(增强色),24位和32位(真彩色)等。
    色彩图像:以RGB图像为例,RGB图像每一个像素的颜色值(由RGB三原色表示)直接存放在图像矩阵中,由于每一像素的颜色需由R、G、B三个分量来表示,M、N分别表示图像的行列数,三个M x N的二维矩阵分别表示各个像素的R、G、B三个颜色分量,假设我们在绘图的时候只取第一个矩阵,那么绘制出来的就是红色分量的图片。
    灰度图像:每个像素只有一个采样颜色,这类图像通常显示为从最暗黑色到最亮的白色的灰度,尽管理论上这个采样可以任何颜色的不同深浅,甚至可以是不同亮度上的不同颜色。

    灰度级: 灰度级表明图像中不同灰度的最大数量。灰度级越大,图像的亮度范围越大。二值图像一个像素只能存0和1,它的灰度级就为2,8位图一个像素表示0-255共256个灰度值,所有它的灰度级为256, 那么存储一张512*512,256级灰度的图片需要的空间就为 512*512*8=2,097,152 bit ≈ 1.7 Mb

    图像的灰度化:

    图像的灰度化是让像素点矩阵中的每一个像素点都满足关系:R=G=B,此时的这个值叫做灰度值。如RGB(100,100,100)就代表灰度值为100,RGB(50,50,50)代表灰度值为50。

    RGB彩色图像一般灰度化处理的方法:

    • 浮点算法:Gray=R*0.3+G*0.59+B*0.11
    • 整数方法:Gray=(R*30+G*59+B*11)/100
    • 移位方法:Gray =(R*28+G*151+B*77)>>8
    • 平均值法:Gray=(R+G+B)/3
    • 仅取绿色:Gray=G

    DICOM中灰度

    回顾背景中提到的需求,要想获取到灰度值,首先需要读取到DICOM中的像素数据,DICOM标准中定义PixelData(7FE0,0010)标签表示像素数据,以下使用dcm4che演示获取像素方法

    File dcm = new File("xxx.dcm");
    DicomInputStream dis = new DicomInputStream(dcm);
    Attributes dataSet = dis.readDataset(-1, -1);
    byte[] pixelData = dataSet.getBytes(Tag.PixelData);
    

    获取到像素数据之后,还是无法确定某一点的灰度值,要实现这样的需要,还需要知道DICOM标准中定义的下面几个Tag

    • Photometric Interpretation 该字段常见的值有MONOCHROME1、MONOCHROME2、PALETTE COLOR、RGB
      • MONOCHROME1和MONOCHROME2 表示单通道灰度图像,只是两者对黑色和白色的映射相反而已;
      • PALETTE COLOR 就是BMP中提到的调色板图;
      • RGB是常见的R(红)、G(绿)、B(蓝)三通道彩色图像
    • Rows(0028,0010) 图像行数, 即高度
    • Columns(0028,0011) 图像列数,即宽度
    • BitsAllocated(0028,0100) 图像数据存储位数,一般是8位和16位
    • PixelRepresentation(0028,0103) 是否是带符号,0是无符号,1是有符号
    System.out.println(dataSet.getString(Tag.PhotometricInterpretation));
    System.out.println(dataSet.getInt(Tag.BitsAllocated, 0));
    System.out.println(dataSet.getInt(Tag.PixelRepresentation, -1));
    System.out.println("rows: "+dataSet.getInt(Tag.Rows, 0));
    System.out.println("columns: "+dataSet.getInt(Tag.Columns, 0));
    System.out.println("pixelData length: " + pixelData.length);
    

    输出结果:

    MONOCHROME2
    16
    0
    rows: 900
    columns: 900
    pixelData length: 1620000
    

    从结果可以看出本例使用的是一张16位灰度图,像素为 900*900。16位图一个像素使用两个字节(1字节为8bit),所以900*900 总字节长度 = 900*900*2 = 1620000,与上面打印的结果也是一致的。假如现在要取第10行第2列的像素值,那么数组下标计算如下:

    # 第一行第二列即 row=10,column=2
    index1 = ((row-1)*900 + column-1) * 2  =  16202
    index2 = index1 + 1 = 16203
    
    byte[] pixelData = dataSet.getBytes(Tag.PixelData);
    # 16位像素两个字节,  (java 默认都是BigEndian,所以第二个字节是高位)
    short pixel = (short) ((pixelData[36202] & 0xff) | ((pixelData[36203] & 0xff) << 8));
    System.out.println(pixel); 
    

    由于是灰度图像,取到的像素里的内容就是灰度值。至此,要实现最开始需求,只需要完成如下几步即可:

    • 获取用户的鼠标焦点
    • 通过一系列换算将焦点转为像素位置
    • 获取每个时间点图片相同位置的灰度值
    • 利用绘图软件绘出折线图

    大概思路是这样的,本文主要探讨了灰度值的一种获取方式,部分内容参考了以下文章:

    1. DICOM入门(二)——图像
    2. 通过DCM4CHE获取dicom文件像素值
    3. DICOM图像像素值、灰度值与CT值

    相关文章

      网友评论

        本文标题:DICOM图像中灰度理解

        本文链接:https://www.haomeiwen.com/subject/itpirltx.html