跳轉到

2.1 亮度對比與直方圖#

概念說明#

亮度 (brightness) 調整是把所有像素加上(或乘上)同一偏移,使影像整體變亮或變暗。對比 (contrast) 調整則是放大像素值之間的差距,讓亮的地方更亮、暗的地方更暗。

它們都可以用一個「對映函式」s = T(r) 來描述,其中 r 是輸入像素值、s 是輸出值:

  • 亮度偏移:s = r + c(c 為常數,需截到 0~255 的範圍內)
  • 對比拉伸:s = α·(r−128) + 128(α>1 拉大對比,α<1 壓縮對比)
  • 一般線性拉伸:s = (r − r_min) × 255 / (r_max − r_min),把最小值映到 0、最大值映到 255,可解決影像明明內容不暗但整體偏灰的問題(像是相機自動曝光選錯了範圍)。
  • 反相(負片):s = 255 − r

直方圖 (histogram) 統計每個灰階值在影像中出現的次數。對 8-bit 影像,直方圖是 256 個數字的陣列。直方圖像是影像的「簽名」:

  • 峰集中在暗側 → 影像偏暗(曝光不足)
  • 峰集中在亮側 → 影像偏亮(過曝)
  • 全部擠在中間窄帶 → 對比不足
  • 佔滿整個 0~255 → 動態範圍運用良好

直方圖在數位影像處理中的地位,就像統計學的機率密度函數——把直方圖除以總像素數,就是像素值為 v 的機率估計 p(v)。這個「把影像看成隨機分布」的觀點,是下一節直方圖等化的理論基礎。

演算法步驟:計算直方圖#

  1. 把影像展平成一維。
  2. 逐像素累加統計:可用 np.bincount,把值 v 的計數加 1。
  3. 輸出 256 維陣列。可再除以像素總數得到機率。

Python 程式碼範例#

import numpy as np
from PIL import Image

img = np.array(Image.open("gray.png"))          # 用 1.2 節產生的灰階影像
print("影像尺寸:", img.shape)

# 直方圖
h, _ = np.histogram(img, bins=256, range=(0, 256))
print("第一個峰值位置:", h.argmax(), "次數:", h.max())

# 畫出簡單的直方圖字元版
top = h.max()
for v in range(0, 256, 8):                       # 每 8 個灰階畫一行
    bar = '#' * int(60 * h[v] / top) if top > 0 else ''
    print(f"{v:3d} {bar}")

# 亮度偏移 +60
bright = np.clip(img.astype(np.int32) + 60, 0, 255).astype(np.uint8)

# 對比拉伸 alpha=1.6(以 128 為中心)
contrast = (1.6*(img.astype(np.int32)-128) + 128)
contrast = np.clip(contrast, 0, 255).astype(np.uint8)

# 線性拉伸到滿範圍
mn, mx = img.min(), img.max()
stretch = ((img - mn) * 255.0 / (mx - mn)).astype(np.uint8)

Image.fromarray(bright).save("bright.png")
Image.fromarray(contrast).save("contrast.png")
Image.fromarray(stretch).save("stretch.png")

# 用直方圖檢驗效應
for name, im in [("原圖", img), ("加亮", bright), ("拉伸", stretch)]:
    hh, _ = np.histogram(im, bins=256, range=(0, 256))
    nz = hh > 0
    print(f"{name}: 有效灰階 {nz.sum()} 個, "
          f"最小 {im.min()}, 最大 {im.max()}, 平均 {im.mean():.1f}")

執行後可觀察:加亮影像的直方圖整體右移、上限處出現「堆積」(clip 掉了超出範圍的值);對比拉伸使直方圖兩端推得離中點更遠;線性拉伸後的最小、最大值正好是 0 與 255。

複雜度分析#

逐像素點運算:O(N)。直方圖統計:亦可 O(N)(每像素一次陣列累加),實務上 bincount 用 C 實作快很多。直方圖本身只佔 256 個整數,空間 O(1)。

練習#

  1. 對一張暗影像做亮度 +50 與線性拉伸,比較何者保住了資訊?說明何時偏移會造成「亮度飽和、細節永久損失」。
  2. 寫一個 gamma 校正函式 s = 255×(r/255)^γ,畫出 γ=0.5 與 2.0 的對映曲線(用文字或數值輸出)。
  3. 計算你自己的照片縮圖的直方圖,判斷它是偏亮、偏暗或對比不足,並選擇合適的修正。