醫學影像“調窗”(window-leveling)的算法

圖像顯示和打印面臨的一個問題是:圖像的亮度和對比度能否充分突出關鍵部分。這裏所指的“關鍵部分”在 CT 裏的例子有軟組織、骨頭、腦組織、肺、腹部等等。

技術問題:
    顯示器往往只有 8-bit, 而數據有 12- 至 16-bits。
    如果將數據的 min 和 max 間 (dynamic range) 的之間轉換到 8-bit 0-255 去,過程是個有損轉換, 而且出來的圖像往往突出的是些噪音。

針對這些問題,研究人員先提出一些要求 (requirements),然後根據這些要求提出了一些算法。這些算法現在都很成熟。

要求一:充分利用 0-255 間的顯示有效值域
要求二:儘量減少值域壓縮帶來的損失
要求三:不能損失應該突出的組織部分

算法分析:
A.  16-bit 到 8-bit 直接轉換:

   computeMinMax(pixel_val, min, max); // 先算圖像的最大和最小值
   for (i = 0; i < nNumPixels; i++)
     disp_pixel_val[i] = (pixel_val[i] - min)*255.0/(double)(max - min);

這個算法必須有,對不少種類的圖像是很有效的:如 8-bit 圖像,MRI, ECT, CR 等等。

B. Window-leveling 算法: W/L 是專門爲 CT 設計的。原理很簡單:CT 圖像裏不同組織的密度 (用 Hounsfield 單位) 是在固定的值域, 與具體設備和成像軟件沒有關係。因此,要看頭顱時, 我們只需將頭顱的值域轉換到 0-255 就行了。

CT W/L 不講頭顱值域的 min 和 max, 而說 max - min (即 window_width) 和 (max+min)/2 (即 window_center)。

我們還可以用原來的公式,只是 min 和 max 的算法不一樣。

  // 先算圖像的最大和最小值
  min = (2*window_center - window_width)/2.0 + 0.5;
  max = (2*window_center + window_width)/2.0 + 0.5;
   for (i = 0; i < nNumPixels; i++)
     disp_pixel_val[i] = (pixel_val[i] - min)*255.0/(double)(max - min);

請注意,CT 圖像必須先轉換成 Hounsfield 值再做 window-level。 這個轉換包括將多餘高位 bits 變成 0 (clipping), 和用 recale slope 和 rescale intercept 來做單位轉換。

HU[i] = pixel_val[i]*rescale_slope + rescale_intercept

C.非線性轉換

我剛剛說的是將 min 和 max 間的數值線性轉換到 0-255 之間。 如果 max - min 出來是個很大的數值,比如說 25500, 那就說每 100 原始密度會壓縮成一個顯示灰度。 這樣的損失可能會很大。

因爲人眼對灰度地反應式是非線性的,非線性轉換可以解決一些問題。 常用算法有 log 和 gamma 兩種。gamma 比較好調 gamma 值,因此用得比較多。

for (i = 0; i < nNumPixels; i++)
    disp_pixel_val[i] = 255.0 * pow(pixel_value[i]/(max-min), 1.0/gamma);

D. 有效值域:CT 的 Window-level 有標準的定義,請參看 “Practical CT Techniques", by Wladyslaw Gedroyc and Sheila Rankin, Springer-Verlag。最常用到的有 WW = 400, WL = 40 (實用許多部位); WW = 100, WL = 36 (頭);WW = 3200, WL = 200 (骨頭),等等。

補充幾點:

o   在做任何轉換時要注意有效灰度域外的數值的處理。
   最好先用 int 而非 unsigned char 來算,再轉入矩陣,以避免 overflow 和 underflow。

   double dFactor = 255.0/(double)(max - min);
   int nPixelVal;

   for (i = 0; i < nNumPixels; i++)
   {

     nPixelVal = (int) ((pixel_val[i] - min)*dFactor);

     if (nPixelVal < 0)    
       disp_pixel_val[i] = 0;
     else if (nPixelVal > 255)
      disp_pixel_val[i] = 255;
     else
      disp_pixel_val[i] = nPixelVal;

   }
  

o   做 window-level 時要注意 min 和 max 之外原始數據的處理

   double dFactor, min, max;
   int nPixelVal;

   min = (2*window_center - window_width)/2.0 + 0.5;
   max = (2*window_center + window_width)/2.0 + 0.5;
   dFactor = 255.0/(double)(max - min);

   for (i = 0; i < nNumPixels; i++)
  {
     if (pixel_val[i] < min)
     {
       disp_pixel_val[i] = 0;
       continue;
     }

     if (pixel_val[i] > max)
     {
       disp_pixel_val[i] = 255;
       continue;
     }

     nPixelVal = (int)((pixel_val[i] - min)*dFactor);

     if (nPixelVal < 0)    
       disp_pixel_val[i] = 0;
     else if (nPixelVal > 255)
      disp_pixel_val[i] = 255;
     else
      disp_pixel_val[i] = nPixelVal;

  }

發佈了38 篇原創文章 · 獲贊 157 · 訪問量 38萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章