背景
前段時(shí)間項(xiàng)目組開發(fā)了一款在線影像文件質(zhì)控的web工具推励,當(dāng)時(shí)研究員提出了這樣一個(gè)需求腾务,當(dāng)點(diǎn)擊圖像的某一個(gè)點(diǎn)時(shí)捂龄,將這個(gè)點(diǎn)不同時(shí)間上的(MRI掃描是持續(xù)一段時(shí)間的揩慕,掃描相同部位時(shí),根據(jù)掃描頻率就會(huì)產(chǎn)生多張圖像)灰度值用折線圖描繪出來侮攀。要想完成這個(gè)需求史侣,首先就要弄清楚灰度值是什么。
理解含義
從百度百科上可以看出灰度通俗的講就是用一個(gè)數(shù)字描述的黑白程度魏身,值越小圖片越暗。這樣的理解對(duì)應(yīng)想知道概念的人基本足夠了蚪腐,但是對(duì)于咱們coding的工程師箭昵,還需要進(jìn)一步了解。
先來看看常見的的一些概念
RGB
:我們生活中通過三原色深淺組成不同的顏色回季,前端開發(fā)中常用RGB來指定色彩家制,R正林、G、B 都是 0-255颤殴, 比如 (255,255,255) 表示白色觅廓,用16進(jìn)制表示255就是ff, 所以255,255,255 可以表示為 ffffff, 在css樣式表中寫為 #ffffff。
像素
:圖片我們都知道是由多個(gè)像素組成的涵但,是圖像的最小單元杈绸,如果這個(gè)單元里只有一個(gè)bit大小,也就是只有0和1兩個(gè)值矮瘟,由這種像素組成的圖像稱二值圖像瞳脓,顯然它可以表示一個(gè)黑白圖。這個(gè)單位里也可以存一個(gè)16位二進(jìn)制數(shù)澈侠,24位..., 可以想象存儲(chǔ)的越大劫侧,一個(gè)像素能表達(dá)的顏色就越豐富,整體圖像呈現(xiàn)就越精細(xì)哨啃,當(dāng)然圖片占用的空間也就越大烧栋。
1位圖像(即二值圖像)只能表示黑白,那二位圖像就可以表示4種顏色(00,01,10,11拳球,VGA)审姓,依次類推,4位(16色醇坝,VGA)邑跪,8位(256色),16位(增強(qiáng)色)呼猪,24位和32位(真彩色)等画畅。
色彩圖像:以RGB圖像為例,RGB圖像每一個(gè)像素的顏色值(由RGB三原色表示)直接存放在圖像矩陣中宋距,由于每一像素的顏色需由R轴踱、G、B三個(gè)分量來表示谚赎,M淫僻、N分別表示圖像的行列數(shù),三個(gè)M x N的二維矩陣分別表示各個(gè)像素的R壶唤、G雳灵、B三個(gè)顏色分量,假設(shè)我們?cè)诶L圖的時(shí)候只取第一個(gè)矩陣闸盔,那么繪制出來的就是紅色分量的圖片悯辙。
灰度圖像:每個(gè)像素只有一個(gè)采樣顏色,這類圖像通常顯示為從最暗黑色到最亮的白色的灰度,盡管理論上這個(gè)采樣可以任何顏色的不同深淺躲撰,甚至可以是不同亮度上的不同顏色针贬。
灰度級(jí)
: 灰度級(jí)表明圖像中不同灰度的最大數(shù)量÷5埃灰度級(jí)越大桦他,圖像的亮度范圍越大。二值圖像一個(gè)像素只能存0和1谆棱,它的灰度級(jí)就為2快压,8位圖一個(gè)像素表示0-255共256個(gè)灰度值,所有它的灰度級(jí)為256础锐, 那么存儲(chǔ)一張512*512,256級(jí)灰度的圖片需要的空間就為 512*512*8=2,097,152 bit ≈ 1.7 Mb
嗓节。
圖像的灰度化
:
圖像的灰度化是讓像素點(diǎn)矩陣中的每一個(gè)像素點(diǎn)都滿足關(guān)系:R=G=B,此時(shí)的這個(gè)值叫做灰度值皆警。如RGB(100,100,100)就代表灰度值為100,RGB(50,50,50)代表灰度值為50拦宣。
RGB彩色圖像一般灰度化處理的方法:
- 浮點(diǎn)算法:
Gray=R*0.3+G*0.59+B*0.11
- 整數(shù)方法:
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中的像素?cái)?shù)據(jù)鸵隧,DICOM標(biāo)準(zhǔn)中定義PixelData(7FE0,0010)標(biāo)簽表示像素?cái)?shù)據(jù),以下使用dcm4che演示獲取像素方法
File dcm = new File("xxx.dcm");
DicomInputStream dis = new DicomInputStream(dcm);
Attributes dataSet = dis.readDataset(-1, -1);
byte[] pixelData = dataSet.getBytes(Tag.PixelData);
獲取到像素?cái)?shù)據(jù)之后意推,還是無法確定某一點(diǎn)的灰度值豆瘫,要實(shí)現(xiàn)這樣的需要,還需要知道DICOM標(biāo)準(zhǔn)中定義的下面幾個(gè)Tag
- Photometric Interpretation 該字段常見的值有MONOCHROME1菊值、MONOCHROME2外驱、PALETTE COLOR、RGB
- MONOCHROME1和MONOCHROME2 表示單通道灰度圖像腻窒,只是兩者對(duì)黑色和白色的映射相反而已昵宇;
- PALETTE COLOR 就是BMP中提到的調(diào)色板圖;
- RGB是常見的R(紅)儿子、G(綠)瓦哎、B(藍(lán))三通道彩色圖像
- Rows(0028,0010) 圖像行數(shù), 即高度
- Columns(0028,0011) 圖像列數(shù)柔逼,即寬度
- BitsAllocated(0028,0100) 圖像數(shù)據(jù)存儲(chǔ)位數(shù)蒋譬,一般是8位和16位
- PixelRepresentation(0028,0103) 是否是帶符號(hào),0是無符號(hào)愉适,1是有符號(hào)
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);
輸出結(jié)果:
MONOCHROME2
16
0
rows: 900
columns: 900
pixelData length: 1620000
從結(jié)果可以看出本例使用的是一張16位灰度圖犯助,像素為 900*900
。16位圖一個(gè)像素使用兩個(gè)字節(jié)(1字節(jié)為8bit)维咸,所以900*900 總字節(jié)長(zhǎng)度 = 900*900*2
= 1620000
,與上面打印的結(jié)果也是一致的剂买。假如現(xiàn)在要取第10行第2列的像素值扑媚,那么數(shù)組下標(biāo)計(jì)算如下:
# 第一行第二列即 row=10,column=2
index1 = ((row-1)*900 + column-1) * 2 = 16202
index2 = index1 + 1 = 16203
byte[] pixelData = dataSet.getBytes(Tag.PixelData);
# 16位像素兩個(gè)字節(jié), (java 默認(rèn)都是BigEndian,所以第二個(gè)字節(jié)是高位)
short pixel = (short) ((pixelData[36202] & 0xff) | ((pixelData[36203] & 0xff) << 8));
System.out.println(pixel);
由于是灰度圖像,取到的像素里的內(nèi)容就是灰度值雷恃。至此,要實(shí)現(xiàn)最開始需求费坊,只需要完成如下幾步即可:
- 獲取用戶的鼠標(biāo)焦點(diǎn)
- 通過一系列換算將焦點(diǎn)轉(zhuǎn)為像素位置
- 獲取每個(gè)時(shí)間點(diǎn)圖片相同位置的灰度值
- 利用繪圖軟件繪出折線圖
大概思路是這樣的倒槐,本文主要探討了灰度值的一種獲取方式,部分內(nèi)容參考了以下文章: