圖像增強、銳化,利用 Python-OpenCV 幫你實現 4 種方法!

圖像增強目的使得模糊圖片變得更加清晰、圖片模糊的原因是因爲像素灰度差值變化不大,如片各區域產生視覺效果似乎都是一樣的, 沒有較爲突出的地方,看起來不清晰的感覺

解決這個問題的最直接簡單辦法,放大像素灰度值差值、使圖像中的細節更加清晰。

目前較爲常用的幾個方法:伽馬變換、線性變換、分段線性變換、直方圖均衡化,對於圖像對比度增強,都能取得不錯的效果!

本文將對每種方法 簡單介紹一下,並藉助於 Python 、OpenCV 進行代碼實現,提前說一下哈,下面處理的圖像對象都是單通道灰度圖,不是三通道彩色圖!

1,線性變換

線性變換的原理是對所有像素值乘上一個擴張因子 factorfactor,像素值大的變得越大,像素值小的變得越小,從而達到圖像增強的效果,這裏利用 Numpy 的數組進行操作;
O(x,y)=I(x,y)factorO(x,y)I(x,y) O(x,y) = I(x,y)*factor\\ O(x,y)爲輸出像素值、I(x,y)爲輸入像素值;

需要注意的是,像素值最大爲255,因此在數組相乘之後需要進行數值截斷操作,最終代碼如下:

def line_trans_img(img,coffient):
    if len(img.shape) == 3:
        img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    out = 2*img
    #像素截斷;;;
    out[out>255] = 255
    out = np.around(out)
    return out

這裏 factorfactor 設置爲 2 ,變換結果如下,會看到強光處出現失真效果

line.png

這裏對排列圖片做一下說明,從左到右依次爲 原圖灰度圖、原圖灰度直方圖、處理之後的灰度圖、處理之後的灰度直方圖,以下的圖片排列方式相同

2,伽馬變換

伽馬變換對像素值做的是冪次方變換,主要是圖像的灰度級發生改變,轉換的原理公式爲:
O(x,y)=I(x,y)γO(x,y)I(x,y) O(x,y) = I(x,y)^\gamma\\ O(x,y)、I(x,y)定義與前面一致;
參數 γ\gamma 的設定 可以參照下面:

Snipaste_2020-05-26_20-18-17.png

  • γ>1\gamma>1 時,會減小灰度級較高的地方,增大灰度級較低的地方;
  • γ<1\gamma<1 時,會增大灰度級較高的地方,減小灰度級較低的地方;
def gama_transfer(img,power1):
    if len(img.shape) == 3:
         img= cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    img = 255*np.power(img/255,power1)
    img = np.around(img)
    img[img>255] = 255
    out_img = img.astype(np.uint8)
    return out_img

這裏 Gamma 分別取 1.5,0.5,結果如下:

gamma1.5.png

gamma0.5.png

結果來看,相對來說 γ>1\gamma >1 對圖像增強的結果會更好一點

3,分段線性分割

分段線性分割,提前把圖像的灰度級分爲幾部分,然後對每一部分的像素值做不同的線性變換,像素值基本變換原理:
$$
O(x,y) = \left{
\begin{aligned}
a_1* I(x,y) + b_1 & & 0<I(x,y)<H\
a_2* I(x,y) + b_2 & & H<=I(x,y)<v\
a_3* I(x,y) + b_3 & & v<=I(x,y)\

\end{aligned}
\right.
$$

這裏寫的代碼總感覺效率特別慢(逐像素改變),知道改進方法的小夥伴們望告知:

def seg_augment_img(img,start,c1,end,c2,b2,c3,b3):
    if len(img.shape) == 3:
         img= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

    out_img = np.zeros(img.shape)
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            if img[i][j] <start:
                out_img[i][j] = img[i][j]*c1
            elif img[i][j] < end:
                out_img[i][j] = img[i][j] *c2 + b2
            else:
                out_img[i][j] = img[i][j] * c3 +b3

    out_img[out_img>255] = 255
    out = np.around(out_img)
    out = out.astype(np.uint8)
    return out

函數中的參數分別爲 50,0.5,150,3.6,-310,0.238,194,結果如下:

seg_line.png

4,直方圖均衡化

每個灰度圖像都有自己的灰度直方圖,均衡化的原理是,先根據灰度直方圖計算累加灰度直方圖,根據灰度圖與累加灰度圖的映射關係關聯輸入圖像與輸出圖圖像的映射關係

映射關係原理如下:
O=k=0phistI(k)hw2561h,wOhist(k)k O = \frac{\sum_{k=0}^{p}hist_{I}(k)}{h*w}*256 -1\\ h,w表示圖像的高和寬\\ O表示輸出像素、hist(k)表示圖像灰度圖中像素爲k的個數;\\

因此,這裏幾個重要部分:1,計算出灰度直方圖;2,計算累加灰度直方圖;3,根據 1 和 2 得到映射關係,最終輸出灰度像素值;

def get_imghist(img):
    # 判斷圖像是否爲三通道;
    if len(img.shape) == 3:
        img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    # 無 Mask,256個bins,取值範圍爲[0,255]
    hist = cv2.calcHist([img],[0],None,[256],[0,255])
    return hist
    

def cal_equalhist(img):
    if len(img.shape) == 3:
         img= cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    h,w = img.shape[:2]
    grathist = get_imghist(img)

    zerosumMoment = np.zeros([256],np.uint32)
    for p in range(256):
        if p ==0:
            zerosumMoment[p] = grathist[0]
        else:
            zerosumMoment[p] = zerosumMoment[p-1] +grathist[p]

    output_q = np.zeros([256],np.uint8)
    cofficient = 256.0/(h*w)
    for p in range(256):
        q = cofficient *float(zerosumMoment[p]) - 1
        if q >= 0:
            output_q[p] = math.floor(q)
        else:
            output_q[p] = 0

    equalhistimage = np.zeros(img.shape,np.uint8)
    for i in range(h):
        for j in range(w):
            equalhistimage[i][j] = output_q[img[i][j]]

    # 第二種方法,opencv 庫函數自帶一種:
    #equalhistimage = cv2.equalizeHist(img)
    return equalhistimage

結果如下,看起來還是不錯的!(這裏圖片失真是因爲燈光的原因)

equalhist.png

小總結

根據以上幾個增強方法來看,針對於本案例選取的圖像,線性增強方法相對效果並不太好,可能會適用於其它的種類圖像,而 Gamma轉換 和直方圖均衡化取得相對不錯的結果

但圖像增強、銳化沒有最優方法,每種方法都有自己的特點,需要根據自己選擇合適的

最後還是要提醒一下感興趣的小夥伴們,記得跟着敲一下代碼,加深一下應用原理!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章