曝光不足導致照片灰度分布集中在低亮度區間,細節淹沒在暗部。本文介紹基於直方圖均衡化的自適應增強演算法,將灰度動態範圍拉伸至全域,顯著改善視覺效果,附完整 DIB 處理實作代碼。
曝光不足時,感光顆粒分解不完全,導致像素灰度值集中在 0~100 區間 (8-bit 灰度制)。直方圖均衡化通過累積分布函數 (CDF) 將原始灰度映射至均勻分布:
增強轉換方程:
$$s_k = T(r_k) = (L-1) \sum_{j=0}^k p_r(r_j) = (L-1) \sum_{j=0}^k \frac{n_j}{N}$$
此轉換單調遞增,保證灰度順序不變,且輸出動態範圍固定為 [0, 255]。
LPSTR lpDIB = (LPSTR)::GlobalLock((HGLOBAL)hDIB);
LPSTR lpBits = m_clsDIB.FindDIBBits(lpDIB);
LONG w = m_clsDIB.DIBWidth(lpDIB);
LONG h = m_clsDIB.DIBHeight(lpDIB);
DWORD nR[256]={0}, nG[256]={0}, nB[256]={0};
for (int y=0; y<h; y++) {
for (int x=0; x<w*3; x+=3) {
nR[*(BYTE*)(lpBits + y*w*3 + x)]++;
nG[*(BYTE*)(lpBits + y*w*3 + x + 1)]++;
nB[*(BYTE*)(lpBits + y*w*3 + x + 2)]++;
}
}
float pR[256], pG[256], pB[256];
for (int i=0; i<256; i++) {
pR[i] = nR[i] / (float)(w*h);
pG[i] = nG[i] / (float)(w*h);
pB[i] = nB[i] / (float)(w*h);
}
BYTE mapR[256], mapG[256], mapB[256];
float cdfR=0, cdfG=0, cdfB=0;
for (int i=0; i<256; i++) {
cdfR += pR[i]; mapR[i] = (BYTE)(255.0f * cdfR + 0.5f);
cdfG += pG[i]; mapG[i] = (BYTE)(255.0f * cdfG + 0.5f);
cdfB += pB[i]; mapB[i] = (BYTE)(255.0f * cdfB + 0.5f);
}
for (int y=0; y<h; y++) {
for (int x=0; x<w*3; x+=3) {
lpBits[y*w*3 + x] = mapR[lpBits[y*w*3 + x]];
lpBits[y*w*3 + x + 1] = mapG[lpBits[y*w*3 + x + 1]];
lpBits[y*w*3 + x + 2] = mapB[lpBits[y*w*3 + x + 2]];
}
}

圖一 原始曝光不足照片

圖二 原始灰度直方圖 (集中於 0~100)

圖三 修復後照片與直方圖 (灰度均勻分布 40~255)
本演算法在 Windows 98 + VC6 環境驗證通過,核心邏輯無平台相依性,易於移植至現代 C++、C#、Python (OpenCV equalizeHist) 等環境。