二值影像

Gaowaly發表於2024-07-28

一、 二值影像概念

1. 二值影像含義
二值影像(Binary Image)是指將影像上的每一個畫素只有兩種可能的取值或灰度等級狀態,人們經常用黑白、B&W、單色影像表示二值影像。

二值影像是指在影像中,灰度等級只有兩種,也就是說,影像中的任何畫素點的灰度值均為0或者255,分別代表黑色和白色。

2. 二值影像特點
1)資料量小;
2)處理速度快,成本低,實用性強;
3)能定義幾何學的各種概念。
3. 二值影像優缺點
二值影像一般用來描述字元影像。
優點:佔用空間少
缺點:當表示人物,風景的影像時,二值影像只能展示其邊緣資訊,影像內部的紋理特徵表現不明顯。這時候要使用紋理特徵更為豐富的灰度影像。

4. 二值影像處理流程

二、二值影像的連線性和距離

1、鄰域

對於任意畫素(x,y),把畫素的集合{(x+p,y+q)}(p、q是一對適當的整數)叫做畫素(x,y)的鄰域。即畫素(x,y)附近的畫素形成的區域。

鄰域是指某畫素p的周圍畫素,p與他們間的歐式距離不超過“根號2”即對角線的距離。如果q在p的某種鄰域中,則p,q為某種鄰接。

  • 1.1、 4-鄰域

    即對於畫素(x,y),上下左右4個畫素稱為4-鄰域,即以下元素:(x-1,y)(x,y-1)(x+1,y)(x,y+1)。

  • 2.2、 D-鄰域

    即對於畫素(x,y),其中(max-1>x>1,max-1>y>1)。其D-鄰域為以下元素:(x-1,y-1)(x+1,y-1)(x-1,y+1)(x+1,y+1)。

  • 3、 8-鄰域

    即對於畫素(x,y),其中(max-1>x>1,max-1>y>1),上下左右4個元素以及4個對角線畫素,稱為8-鄰域。為以下元素:(x-1,y-1)(x-1,y)(x-1,y+1)(x,y-1)(x,y+1)(x+1,y-1)(x+1,y)(x+1,y+1)

2、 二值影像的鄰接


4-鄰接:如果q點在p點的4-鄰域集合中,則p,q為4-鄰接。即互為4-鄰域的兩個畫素叫4-鄰接。


8-鄰接:如果q點在p點的8-鄰域集合中,則p,q為8-鄰接。即互為8-鄰域的兩個畫素叫8-鄰接。


D-鄰接:如果q點在p點的D-鄰域集合中,則p,q為D-鄰接


m-鄰接:如果q在p的4-鄰域中,或者q在p的D-鄰域中,且q的4-鄰域與p的4-鄰域交集為空,則p,q為m-鄰接。


ლ(′◉❥◉`ლ)
m鄰域的目的是消除8-鄰域的二義性。

二義性


在某種情況下,如果鄰域中存在多種通路。

某點1到點3存在兩種通路,1->p->3,1->p->2->3。所以點1和點3關係不明確。如果使用m鄰接,點3將不會是點p的m-鄰接,因為點p和點3的4-鄰域存在交集點2。

3、連通性

對於集合S存在一條通路的條件是,通路的畫素的某種排列,相鄰畫素滿足某種鄰接關係。例如點p到點q之間有A1,A2,A3…An個畫素點,且相鄰畫素點都滿足某種鄰接。則p和q之間存在通路。如果通路首尾相連,則稱閉合通路。S集合中的一點p只存在一條通路,則稱為一個連通分量,如果S只有一個連通分量,則稱為一個連通集。

對於R為一個影像子集,如果R連通的,則稱R為一個區域。對於所有不連線的K個區域,其並集Rk構成了影像的前景,Rk的補集稱為背景。

4、畫素的連線

在二值影像中,具有兩個相同數值的畫素a 1 a_1a1a 2 a_2a2,若所有與它們具有相同值的畫素,能夠在4-/8-鄰域內構成一個從a 1 a_1a1a 2 a_2a2的鄰接的畫素序列,則我們把畫素a 1 a_1a1a 2 a_2a2叫做4-/8-連線。其畫素序列叫4-/8-路徑。

5、連線成分

在研究一個二值影像連線成分的場合,若1畫素的連線成分用4-/8-連線,而0畫素連線成分 ,不用相反的8-/4-連線就會產生矛盾。在下圖中,如果假設各個1畫素用8一連線,因此0畫素和1畫素應採用互反的連線形式。

三、差異

  1. 二值影像(binary image),即影像上的每一個畫素只有兩種可能的取值或灰度等級狀態,人們經常用黑白、B&W、單色影像表示二值影像。
  2. 灰度影像(gray image) 是每個畫素只有一個取樣顏色的影像,這類影像通常顯示為從最暗黑色到最亮的白色的灰度,儘管理論上這個取樣可以任何顏色的不同深淺,甚至可以是不同亮度上的不同顏色。灰度影像與黑白影像不同,在計算機影像領域中黑白影像只有黑色與白色兩種顏色;但是,灰度影像在黑色與白色之間還有許多級的顏色深度。灰度影像經常是在單個電磁波頻譜如可見光內測量每個畫素的亮度得到的,用於顯示的灰度影像通常用每個取樣畫素8位的非線性尺度來儲存,這樣可以有256級灰度(如果用16位,則有65536級)。
  3. 彩色影像 ,每個畫素通常是由紅(R)、綠(G)、藍(B)三個分量來表示的,分量介於(0,255)。 (cmyk:分別是由蘭、洋紅、黃和黑色四個分量來表示的)

四、影像二值化

影像二值化就是將影像上的畫素點的灰度值設定為0或255,也就是將整個影像呈現出明顯的黑白效果的過程。影像的二值化使影像中資料量大為減少,從而能凸顯出目標的輪廓。

要得到二值化影像,首先要把影像灰度化,然後將256個亮度等級的灰度影像透過適當的閾值選取而獲得仍然可以反映影像整體和區域性特徵的二值化影像。所有灰度大於或等於閾值的畫素被判定為屬於特定物體,其灰度值為255,否則這些畫素點被排除在物體區域以外,灰度值為0,表示背景或者例外的物體區域。

2. 影像二值化方法及Python實現

比較常用的二值化方法有:簡單二值法,平均值法,雙峰法和OTSU法等。

2.1 簡單二值法

將影像灰度化後,我們選擇127(灰度值範圍的一半)作為閾值,即將畫素值大於127的畫素值全部設為255,小於127的全部設為0。

def Easy_Binarization(srcImg_path):
    img = cv.imread(srcImg_path)
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    img_gray[img_gray>127] = 255
    img_gray[img_gray<=127] = 0
    plt.imshow(img_gray, cmap='gray')
    plt.title('Easy_Binarization')
    plt.show()

2.2 平均值法

為了應對每張圖片的灰度值大不相同,閾值取為影像本身的平均值。

def Mean_Binarization(srcImg_path):
    img = cv.imread(srcImg_path)
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    threshold = np.mean(img_gray)
    print(threshold)
    img_gray[img_gray>threshold] = 255
    img_gray[img_gray<=threshold] = 0
    plt.imshow(img_gray, cmap='gray')
    plt.title('Mean_Binarization')
    plt.show()

實驗中該方法計算的閾值為123。

2.3 雙峰法

直方圖是影像的重要特質,它可以幫助我們分析影像中的灰度變化。因此,如果物體與背景的灰度值對比明顯,直方圖就會包含雙峰,它們分別為影像的前景和背景,而它們之間的谷底即為邊緣附近相對較少數目的畫素點,一般來講,這個最小值就為最優二值化的分界點,透過這個點可以把前景和背景很好地分開。

def Hist_Binarization(srcImg_path):
    img = cv.imread(srcImg_path)
    img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    hist = img_gray.flatten()
    plt.subplot(121)
    plt.hist(hist,256)

    cnt_hist = Counter(hist)
    print(cnt_hist)
    begin,end = cnt_hist.most_common(2)[0][0],cnt_hist.most_common(2)[1][0]
    if begin > end:
        begin, end = end, begin
    print(f'{begin}: {end}')

    cnt = np.iinfo(np.int16).max
    threshold = 0
    for i in range(begin,end+1):
        if cnt_hist[i]<cnt:
            cnt = cnt_hist[i]
            threshold = i
    print(f'{threshold}: {cnt}')
    img_gray[img_gray>threshold] = 255
    img_gray[img_gray<=threshold] = 0
    plt.subplot(122)
    plt.imshow(img_gray, cmap='gray')
    plt.show()

實驗中該方法得到的雙峰為(145,154),閾值為150。

2.4 OTSU法

雙峰法具有明顯的缺陷,因為直方圖是不連續的,有非常多尖峰和抖動,要找到準確的極值點十分困難。日本工程師大津展之為這個波谷找到了一個合適的數學表達,並於1979年發表。這個二值化方法稱為大津演算法(Otsu’s method)。

OTSU法也稱作最大類間方差法,因為按照大津法求得的閾值進行影像二值化分割後,前景與背景影像的類間方差最大。它被認為是影像分割中閾值選取的最佳演算法,計算簡單,不受影像亮度和對比度的影響,因此在數字影像處理上得到了廣泛的應用。它是按影像的灰度特性,將影像分成背景和前景兩部分。因方差是灰度分佈均勻性的一種度量,背景和前景之間的類間方差越大,說明構成影像的兩部分的差別越大,當部分前景錯分為背景或部分背景錯分為前景都會導致類間差別變小。因此,使類間方差最大的分割意味著錯分機率最小。

具體計算閾值方法如下:
設閾值為t, 將原圖轉化成灰度圖後,將其高與寬存於h,w,並將小於閾值的灰度值儲存在前景front中,大於等於閾值的存在背景back中。如下所示:

# 閾值:t
h, w = img.shape[:2]
front = img[img < t]
back = img[img >= t]

顯然,前景與背景的長度和應與h, w的乘積相等,即:

len(front) + len(back) == h * w.

設前景畫素數量佔總畫素數量的比重為frontP,背景畫素數量佔總畫素數量的比重為backP,前景和背景的灰度平均值分別為frontMean和backMean,總平均灰度值為m,則方差公式可寫成:

又因為:

上式可化簡為:

實驗程式碼如下:

def Otsu(srcImg_path):
    img = cv.imread(srcImg_path)
    img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    h, w = img.shape[:2]
    threshold_t = 0
    max_g = 0
    
    for t in range(255):
        front = img[img < t]
        back = img[img >= t]
        front_p = len(front) / (h * w)
        back_p = len(back) / (h * w)
        front_mean = np.mean(front) if len(front) > 0 else 0.
        back_mean = np.mean(back) if len(back) > 0 else 0.
        
        g = front_p * back_p * ((front_mean - back_mean)**2)
        if g > max_g:
            max_g = g
            threshold_t = t
    print(f"threshold = {threshold_t}")

    img[img < threshold_t] = 0
    img[img >= threshold_t] = 255
    plt.imshow(img, cmap='gray')
    plt.title('Otsu')
    plt.show()

二值化影像如下:

對比以上四種方法可以發現,Otsu方法得到的二值化影像細節更多,影像更細膩。

3. opencv-python中二值化方法的應用

在OpenCV中,分為簡單的閾值分割與自適應閾值分割。

3.1 簡單閾值分割(Simple Thresholding)

函式原型如下:

retval, dst = cv.threshold(src, thresh, maxval, type[, dst])

引數說明:
第一個引數src為原圖,需要注意的是輸入的影像需為灰度圖。
第二個引數thresh即為閾值,用於對畫素值的分類(一般定義為127)。
第三個引數maxval是最大值,即超過閾值後所定義的值(255)。
第四個引數type,在Simple Thresholding中一共有五種不同的方式: cv.THRESH_BINARY、cv.THRESH_BINARY_INV 、cv.THRESH_TRUNC 、cv.THRESH_TOZERO、cv.THRESH_TOZERO_INV

測試程式碼如下:

img = cv.imread('lenna.jpg')
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
img_gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
ret,thresh1 = cv.threshold(img_gray,127,255,cv.THRESH_BINARY)
ret,thresh2 = cv.threshold(img_gray,127,255,cv.THRESH_BINARY_INV)
ret,thresh3 = cv.threshold(img_gray,127,255,cv.THRESH_TRUNC)
ret,thresh4 = cv.threshold(img_gray,127,255,cv.THRESH_TOZERO)
ret,thresh5 = cv.threshold(img_gray,127,255,cv.THRESH_TOZERO_INV)

titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]

for i in range(6):
    plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.axis('off')
plt.show()

下圖是5種方式的實際效果:

3.2 自適應閾值分割(Adaptive Thresholding)

函式原型如下:

dst = cv.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst])

其中src, maxValue和thresholdType與Simple Thresholding相同。

在自適應閾值分割中,adaptive method(閾值的計算方式)有兩種:

  • cv.ADAPTIVE_THRESH_MEAN_C: 鄰域面積(blockSize * blockSize)的平均值並減去C.
  • cv.ADAPTIVE_THRESH_GAUSSIAN_C: 鄰域面積的高斯加權總和然後減去C.

下面是它們的實際效果(對於thresholdType在這裡選擇cv.THRESH_BINARY),測試程式碼如下:

img = cv.imread('lenna.jpg')
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)
img_gray = cv.cvtColor(img, cv.COLOR_RGB2GRAY)
ret,th1 = cv.threshold(img_gray,127,255,cv.THRESH_BINARY)
th2 = cv.adaptiveThreshold(img_gray,255,cv.ADAPTIVE_THRESH_MEAN_C,\
            cv.THRESH_BINARY,11,2)
th3 = cv.adaptiveThreshold(img_gray,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\
            cv.THRESH_BINARY,11,2)

titles = ['Original Image', 'Simple Thresholding',
            'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]

for i in range(4):
    plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.axis('off')
plt.show()

效果如下:

發現沒有像之前otsu那樣輸出一張類似的圖片,但是,它將影像中的邊框描繪了出來,實際應用中這樣的方式更適合處理文字形式的圖片。

4. 原始碼倉庫地址

🌼影像處理、機器學習的常用演算法彙總

相關文章