世界是連續的,影像卻是一格一格的
在前兩篇導覽中,你學到了數位影像到底是什麼:一個有限的像素網格,每個像素存著一小組數字,用來編碼亮度與顏色。但讓我們先走出那個網格一下。鏡頭聚焦到相機感光元件上的光,並不是由一個個小方格組成的,它是完美連續的——在像平面的每一個點上都有某個亮度,而這個亮度可以落在一段連續範圍中的任何數值,中間沒有任何空隙。大自然不會四捨五入,也不會對齊到網格。
於是這裡就有了一個真正的矛盾。光學影像在兩個不同的意義上都是連續函數。它在空間上是連續的:在你能指出的任意兩點之間,都還有無窮多個點,每個點都有自己的亮度。它在數值上也是連續的:單一一個點的亮度可以是 0.4137…、或 0.4138…、或介於其間的任何值,是一個有著無盡精度的實數。電腦無法儲存這兩種無窮。它的記憶體格子數量是有限的,而且每個格子只能存有限精度的數字。
把那個平滑的光學影像變成可儲存的陣列,這個過程叫做數位化(digitization),而整篇導覽的核心想法是:數位化其實是兩個分開的動作,各自對應一種連續性。第一個是取樣(sampling):決定我們實際上要在空間中的哪些位置量測亮度——也就是挑選網格點。第二個是量化(quantization):決定每一次量測能存成什麼數字——把亮度四捨五入到一份有限的允許階數清單中的某一階。取樣馴服空間;量化馴服數值。
取樣:在網格上讀取影像
取樣這個動作,是只在一組規則排列的離散點上量測連續的亮度——也就是疊在像平面上的一張想像網格的交叉點。我們不會、也無法在每個地方都記錄亮度。我們挑一張網格,然後在每個網格點讀出一個亮度值。每一次讀值就變成一個像素。在網格點之間發生的一切,根本沒有被量測到;那些只能用猜的,或者乾脆忽略。
一個連續漸層的場景上疊著一張網格;取樣點位於網格交叉點,並變成離散的像素。
有一個能清楚體會這件事的方法:想像你要繪製整個國家的氣溫分布圖,但你只能在官方氣象站讀溫度計。如果只有寥寥幾個站,你得到的是很粗糙的樣貌——這裡一團暖、那裡一團涼——而你會完全錯過某個沒有設站的山谷裡的低溫區。再加上幾百個站,細緻的局部變化就開始顯現出來。這些氣象站就是你的取樣點;越密的站網,就能捕捉到越多真實的變化。光學影像就是那個真實而平滑的溫度場;取樣就是那張氣象站的網。
這直接連到第一篇講的解析度。取樣點之間的間距就是解析度:網格越細代表間距越小,間距越小代表在同一個場景上取的樣本越多,樣本越多代表像素越多,像素越多代表越忠實地複製了原本的光。當有人說一張照片「解析度更高」,他們其實是在說「這個世界是用更密的網格取樣的」。把每個方向的間距減半,你取的樣本數就變成四倍——橫向兩倍、縱向也兩倍。
量化:把亮度四捨五入到固定階數
取樣告訴了我們要讀哪裡。但每一次讀到的還是一個有著無盡小數的實數,而記憶體格子裝不下無盡的小數。量化就是第二個動作:把每一個取樣到的亮度,四捨五入到一份有限的允許階數(levels)清單裡最接近的那一個值。想像在「所有可能亮度」這道平滑斜坡上鋪一座樓梯。真實亮度可以落在斜坡上的任何地方,但你只能回報它最接近哪一階。
這座樓梯有幾階?這正是第一篇講的位元深度。每個通道用 b 個位元,就會有 2^b 個不同的階:1 個位元給 2 階(非黑即白),8 個位元給 256 階(一般照片用的),以此類推。位元越多代表階數越多、每階越細,所以樓梯就越貼近那道平滑斜坡。讓我們把這個四捨五入規則寫精確。
把真實強度貼到最接近的可儲存階。
由左讀到右。I 是取樣交給我們的真實連續強度(比方說一個從 0% 到 100% 之間任意值的亮度)。Δ(讀作「delta」,即步階大小)是兩個相鄰可儲存階之間的間隔。我們先做除法 I/Δ,這在問「這個亮度有幾個完整步階高?」;接著把它四捨五入到最接近的整數,貼到某一個特定的階;然後再乘回 Δ,把那個階的編號變回一個實際的亮度 q,這就是我們存下來的東西。步階大小本身是 Δ = 範圍 / 2^b:把整個亮度範圍切成 2^b 個相等的步階,其中 b 是位元深度,所以 2^b 就是階數。位元越少 → Δ 越大 → 樓梯越粗。
# Worked example: quantize brightness (0-100%) to b = 2 bits -> 4 levels
range_ = 100.0
b = 2
delta = range_ / (2 ** b) # = 100 / 4 = 25% -> levels at 0, 25, 50, 75
readings = [12, 31, 58, 87] # true sampled brightnesses, in %
for I in readings:
q = round(I / delta) * delta # snap to nearest level
error = abs(I - q) # how much we lost by rounding
print(f"I={I:>3}% -> q={q:>3.0f}% error={error:>4.1f}%")
# I= 12% -> q= 0% error=12.0%
# I= 31% -> q= 25% error= 6.0%
# I= 58% -> q= 50% error= 8.0%
# I= 87% -> q= 75% error=12.0%
# Largest possible error = delta/2 = 12.5%注意例子裡每個誤差最多都是 12.5%,而那正好是 Δ/2 = 25%/2。這不是巧合:因為我們是四捨五入到最近的階,真實值與我們保留的那一階之間,距離永遠不會超過半階。這個最壞情況的差距 Δ/2 就是量化誤差(quantization error)。整條因果鏈現在很清楚了——位元越多 → 2^b 越大 → Δ 越小 → Δ/2 越小 → 誤差越小。如果我們用 8 個位元而不是 2 個,Δ 會是 100%/256 ≈ 0.39%,最壞誤差只有幾乎看不見的 0.2%。
取樣出錯時:鋸齒與混疊
現在回到空間。如果場景裡含有比取樣網格所能表現的還要細的細節——條紋擠得比取樣點之間的間距還密——會發生什麼事?你或許會希望這些細節單純地消失或變模糊。並不會。會發生的是更奇怪、更糟的事:那些記不下來的細節不會消失,反而偽裝成一個從來不存在、更粗的假圖案。這個冒牌圖案就叫做混疊假影(aliasing artifact),它正是取樣出錯最經典的方式。
你幾乎一定看過這個現象。電視上有人穿著細條紋或細格紋的襯衫,那塊布料突然爆出旋轉、閃爍的彩虹漣漪,還會隨著人移動而流動——那就是摩爾紋(moiré),一種混疊假影,因為襯衫的織紋比相機的像素網格還細。低解析度螢幕上的一條斜線或一個圓,變成一級級粗糙的「階梯」鋸齒——那是邊緣的混疊,有時叫做「鋸齒(jaggies)」,因為一條傾斜的邊緣帶有比單一像素所能擺放還細的細節。網格只能把邊緣放在整數像素的位置上,於是一道平滑的斜坡就變成了一段樓梯。
混疊不只發生在空間上。著名的馬車輪錯覺——電影裡旋轉的輪輻看起來變慢、停住、甚至倒著轉——正是同一個現象,只是發生在時間軸上。電影一秒只取樣這個世界 24 次。如果一根輪輻在兩個畫格之間幾乎移動了整整一個輪輻間距,那每張快照都會讓它停在「比上一根輪輻位置稍微差一點」的地方,於是你的大腦把這些畫格縫成了一個緩慢的倒退爬行。真實的快速運動被取樣得太慢,結果就偽裝成慢動作重新出現了。
奈奎斯特與抗鋸齒:解方
要把那句話變成規則,我們需要一個友善的詞:頻率(frequency)。在這裡,頻率單純就是指當你橫越空間移動時,亮度變化得有多快。晴朗的藍天變化得很慢——低空間頻率。細條紋襯衫或銳利的邊緣在很小的距離內就劇烈變化——高空間頻率。「細節」和「高頻」是同一件事的兩個名字。一個場景中最細的細節,對應的就是它最高的頻率。
這個大師級的想法就是奈奎斯特取樣原理(Nyquist sampling principle):要誠實地捕捉到某個最細擺動率以內的細節,你的取樣速度至少必須是那個擺動率的兩倍。任何比你的取樣所允許的極限還細的東西,都會發生混疊。用符號寫出來如下。
取樣速度要超過你想保留的最細細節的兩倍。
逐個符號來看:f_s 是取樣率——我們把網格點放得多密(比方說感光元件上每毫米幾個樣本)。f_max 是場景中實際存在的最高空間頻率——也就是最細、變化最快的細節。因子 2 是整件事的核心:你每個擺動的週期至少需要兩個樣本(一個抓波峰,一個抓緊接著的波谷),才能把這個擺動跟一條平直的線區分開來。等價地說,你的取樣間距必須小於最細細節週期的一半。每個週期抓不到兩個樣本,擺動就會塌縮成一個較慢的冒牌貨——正是上一節講的混疊。
但如果場景就是有著比你固定網格永遠無法滿足的細節呢——一件真有細密織紋的真襯衫,而你的感光元件又換不了?這就是那個優雅的解方:抗鋸齒(anti-aliasing)。在取樣之前,刻意用一個平滑(低通)濾波器把影像模糊掉,移除那些超過你網格能誠實捕捉範圍的頻率。「低通」意思是它讓低頻通過、壓制高頻。當那些捕捉不了的細節在取樣之前就已經被移除,就沒有任何東西能去偽裝成假圖案了。
# Why we blur BEFORE shrinking an image (downsampling) # Wrong: just throw away pixels -> high frequencies alias into moire small_bad = image[::4, ::4] # keep every 4th pixel, no blur # Right: low-pass (blur) first, removing detail the small grid can't hold, # THEN sample on the coarser grid blurred = gaussian_blur(image, sigma=2) small_good = blurred[::4, ::4] # small_good has no fake patterns; small_bad shimmers with aliasing
完整的數位化流程
讓我們把第一到第三篇的所有東西組裝成一條乾淨的鏈。平滑的光學影像抵達感光元件;我們依序馴服它的兩種無窮——先是空間,再是數值——而從另一端掉出來的,就是你第一天認識的那個有限的數字陣列。
- 連續光學影像:在空間中每一點上都有平滑的亮度,數值落在連續範圍裡——這是大自然的輸入,承載著無窮多的資訊。
- 抗鋸齒模糊:低通濾波器把比網格能誠實承載的還細的細節剝除掉,讓它無法以假圖案的形式重新出現(混疊的解方)。
- 在網格上取樣(取樣):只在離散的網格點上讀取亮度;間距決定了解析度。空間到此變成離散的。
- 量化每個樣本(量化):把每個讀值四捨五入到由位元深度決定的 2^b 個階中最接近的一個。數值到此也變成離散的。
- 完成的數位影像:一個有限的像素網格,每個像素是一小組有限的數字——可以儲存、顯示,並用來運算。
退一步看,讓人安心的全貌就完整了:前兩篇的每一個概念——作為單一格子的像素、表示有多少格的解析度、表示每格記得多細的位元深度,以及承載色相的顏色通道——正好就是這條流程的輸出。像素是取樣到的點。解析度是取樣間距。位元深度是量化階數的數量。那個數字網格不是攝影的起點;它是數位化的終點。