天天看點

OpenCV—python 自動色彩均衡(ACE)

文章目錄

  • ​​一、ACE算法理論​​
  • ​​1.1 ACE算法​​
  • ​​二、快速ACE算法​​
  • ​​2.1 原理​​
  • ​​2.2 代碼示範​​

一、ACE算法理論

圖像對比度增強的算法在很多場合都有用處,特别是在醫學圖像中,這是因為在衆多疾病的診斷中,醫學圖像的視覺檢查時很有必要的。醫學圖像由于本身及成像條件的限制,圖像的對比度很低。是以,在這個方面已經開展了很多的研究。這種增強算法一般都遵循一定的視覺原則。衆所周知,人眼對高頻信号(邊緣處等)比較敏感。雖然細節資訊往往是高頻信号,但是他們時常嵌入在大量的低頻背景信号中,進而使得其視覺可見性降低。是以适當的提高高頻部分能夠提高視覺效果并有利于診斷。

ACE在圖像處理方面可以有兩種,

一種是:Automatic Color Equalization,即自動彩色均衡。​【論文與代碼】 一種是:Adaptive Contrast Enhancement,即自适應對比度增強

高動态範圍圖像是指在一幅圖像中,既有明亮的區域又有陰影區域,為了使細節清晰,需要滿足以下幾點:

  1. 對動态範圍具有一定的壓縮能力;
  2. 對亮暗區域的細節有一定的顯示能力;
  3. 滿足上面條件基礎上不破壞圖像的清晰度。

對于高動态範圍處理,基于人眼視覺系統(HSV)在顔色連續和亮度連續方面得到較好的滿足。該算法考慮了圖像中顔色和亮度的空間位置關系,進行局部的自适應濾波,實作具有局部和非線性特征的圖像亮度,色彩與對比度調整,同時滿足灰階世界理論和白斑點假設。

算法原理

Rizzi等依據Retinex理論提出了自動顔色均衡算法,該算法考慮了圖像中顔色和亮度的空間位置關系,進行局部特性的自适應濾波,實作具有局部和非線性特征的圖像亮度與色彩調整和對比度調整,同時滿足灰色世界理論假設和白色斑點假設。

1.1 ACE算法
  1. 獲得空域重構圖像

    對圖像進行色彩/空域調整,完成圖像的色差矯正,得到空域重構圖像:

  1. 其中是中間結果,為2個點的亮度差, 表示距離度量函數, 為亮度表現函數,需要是奇函數,這一步可以适應局部圖像對比度, 可以放大較小的差異,并豐富大的差異,根據局部内容擴充或者壓縮動态範圍。一般的, 為:
  2. 對校正後的圖像進行動态擴充。ACE算法是對單一色道進行的,對于彩色圖檔需要對每一個色道分别處理。一種簡單的線性擴充可以表示為:

    其中:斜率為:,

    還可以将其映射到的空間中:

自動彩色均衡算法改進:

式(1)算法複雜度較高,對于一副像素數為N的圖像,需要執行O(N2)級次非線性映射計算,圖像尺寸越大,耗時越多,是以針對式(1)産生了許多加速改進算法。例如:LLLUT加速政策,使用快速傅裡葉變換替換卷積,将ACE轉換為對規範直方圖均衡化的一種平滑和局部修正的方法,并給出了求解最優模型:

對于改進方法,可以考慮的因素:

  1. 其他的坡度函數,多項式函數逼近;
  2. 除了
  3. 在求和的過程中,y 可以限制在一個小視窗中;

多項式逼近展示:

OpenCV—python 自動色彩均衡(ACE)

二、快速ACE算法

2.1 原理

ACE的增強效果普遍與retinex好。需要注意的是,ACE中目前像素是與整個圖像的其他像素做差分比較,計算複雜度非常非常高,這也是限制它應用的最主要原因。

本文主要基于兩個假設:

(1)對一副圖像ACE增強後得到輸出Y,如果對Y再進行一次ACE增強,輸出仍然是Y本身;

(2)對一副圖像的ACE增強結果進行尺寸縮放得到Y,對Y進行ACE增強,輸出仍然是Y本身。

如果上面假設成立,我們就可以對圖像進行縮放得到 ,對 的ACE增強結果進行尺度放大(與I尺寸一樣)得到,那麼 和 是非常接近的,我們隻需要在Y1基礎上進一步處理即可。

這裡就又引申了兩個細節問題:

  1. 如何快速的求的ACE增強結果?

    其實很簡單,對它再次縮放得到,求

  2. 如何在基礎上進一步處理得到?

    因為是在整個圖像域進行差分比較運算,與近處鄰域像素的比較構成了的細節資訊,與遠處像素的比較構成了的全局背景資訊,那麼我們合理假設,和的全局背景資訊相同,隻更新細節資訊即可,也就是,我們需要在基礎上加上中鄰近像素的差分結果,并減去中鄰近像素的差分結果就是最終的輸出。

為叙述友善,這裡假設後面的圖像都是歸一化到 [0,1] 之間的浮點數圖像。

ACE算法的計算公式為:

是權重參數,離中心點像素越遠w值越小,可以直接取值歐氏距離。

是相對對比度調節參數,非線性的,簡單取如下計算方法:

2.2 代碼示範
import cv2
import numpy as np
import math
 
def stretchImage(data, s=0.005, bins = 2000):    #線性拉伸,去掉最大最小0.5%的像素值,然後線性拉伸至[0,1]
    ht = np.histogram(data, bins);
    d = np.cumsum(ht[0])/float(data.size)
    lmin = 0; lmax=bins-1
    while lmin<bins:
        if d[lmin]>=s:
            break
        lmin+=1
    while lmax>=0:
        if d[lmax]<=1-s:
            break
        lmax-=1
    return np.clip((data-ht[1][lmin])/(ht[1][lmax]-ht[1][lmin]), 0,1)
 
g_para = {}
def getPara(radius = 5):                        #根據半徑計算權重參數矩陣
    global g_para
    m = g_para.get(radius, None)
    if m is not None:
        return m
    size = radius*2+1
    m = np.zeros((size, size))
    for h in range(-radius, radius+1):
        for w in range(-radius, radius+1):
            if h==0 and w==0:
                continue
            m[radius+h, radius+w] = 1.0/math.sqrt(h**2+w**2)
    m /= m.sum()
    g_para[radius] = m
    return m
 
def zmIce(I, ratio=4, radius=300):                     #正常的ACE實作
    para = getPara(radius)
    height,width = I.shape
    zh,zw = [0]*radius + range(height) + [height-1]*radius, [0]*radius + range(width)  + [width -1]*radius
    Z = I[np.ix_(zh, zw)]
    res = np.zeros(I.shape)
    for h in range(radius*2+1):
        for w in range(radius*2+1):
            if para[h][w] == 0:
                continue
            res += (para[h][w] * np.clip((I-Z[h:h+height, w:w+width])*ratio, -1, 1))
    return res
 
def zmIceFast(I, ratio, radius):                #單通道ACE快速增強實作
    height, width = I.shape[:2]
    if min(height, width) <=2:
        return np.zeros(I.shape)+0.5
    Rs = cv2.resize(I, ((width+1)/2, (height+1)/2))
    Rf = zmIceFast(Rs, ratio, radius)             #遞歸調用
    Rf = cv2.resize(Rf, (width, height))
    Rs = cv2.resize(Rs, (width, height))
 
    return Rf+zmIce(I,ratio, radius)-zmIce(Rs,ratio,radius)    
            
def zmIceColor(I, ratio=4, radius=3):               #rgb三通道分别增強,ratio是對比度增強因子,radius是卷積模闆半徑
    res = np.zeros(I.shape)
    for k in range(3):
        res[:,:,k] = stretchImage(zmIceFast(I[:,:,k], ratio, radius))
    return res
 
if __name__ == '__main__':
    m = zmIceColor(cv2.imread('p4.bmp')/255.0)*255
    cv2.imwrite('zmIce.jpg', m)