圖像處理100題習題與代碼(1~10)

題目原地址:https://github.com/gzr2017/ImageProcessing100Wen
我的github地址:https://github.com/LeonG7/image_100question

前言:

這是圖像處理100題的題目記錄以及我自己寫的參考代碼,感謝@gzr2017提供中文翻譯。

所有代碼由jupyter book軟件導出,如需可執行源代碼,請在我的github下載。

如有任何問題或錯誤,歡迎在評論區討論和指正


圖像處理1~10題

讀取圖片:

import cv2
import matplotlib
from matplotlib import pyplot as plt
import numpy as np
%matplotlib inline

#如果沒有安裝opencv的話,這裏可以替換成matplotlib讀取圖片
img = cv2.imread("imori.jpg")
img_noise = cv2.imread("imori_noise.jpg")
#使用matplotlib輸出圖片,所以要將BGR格式預先轉爲RBG格式
img = img[:,:,[2,1,0]]
img_noise = img_noise[:,:,[2,1,0]]

1.通道交換

將紅色通道和藍色通道交換,交換通道維的第一列和第三列即可。

imshow = plt.imshow(img)
plt.show()

img1 = img.copy()
img1 = img[:,:,[2,1,0]]
imshow = plt.imshow(img1)


2.灰度化(Grayscale)

通過公式
Y=0.2126 R+0.7152 G+0.0722 B Y = 0.2126\ R + 0.7152\ G + 0.0722\ B
計算得出單通道Y

matplotlib中的cmap = gray

#灰度化
def gray(img):
    newimg = img[:,:,0]*0.2126 + img[:,:,1]*0.7152 + img[:,:,2]*0.0722
    return newimg
imshow = plt.imshow(img)
plt.show()

img2 = gray(img)
imshow = plt.imshow(img2,cmap='gray')


3.二值化(Thresholding)

把圖像進行二值化吧。

計算出灰度化的值之後,對像素點進行判斷:
y={0(ify<128)255(else) y= \begin{cases} 0& (\text{if}\quad y < 128) \\ 255& (\text{else}) \end{cases}

二值化是將圖像使用黑和白兩種顏色表示的方法。

#二值化
def thre(img,t=128):
    img[img<t] = 0
    img[img>=t] = 255
    return img
imshow = plt.imshow(img)
plt.show()
    
img3 = gray(img)
img3 =thre(img3)
imshow = plt.imshow(img3,cmap='gray')


4.大津二值化算法(Otsu’s Method)

使用大津算法來二值化圖像吧。

大津算法,也被稱作最大類間方差法,是一種可以自動確定二值化中閾值的算法。

類內方差類間方差的比值計算得來:

  • 小於閾值tt的類記作00,大於閾值tt的類記作11
  • w0w_0w1w_1是被閾值tt分開的兩個類中的像素數佔總像素數的比率(滿足w0+w1=1w_0+w_1=1);
  • S02{S_0}^2S12{S_1}^2是這兩個類中像素值的方差;
  • M0M_0M1M_1是這兩個類的像素值的平均值;

即:

  • 類內方差:Sw2=w0 S02+w1 S12{S_w}^2=w_0\ {S_0}^2+w_1\ {S_1}^2
  • 類間方差:Sb2=w0 (M0Mt)2+w1 (M1Mt)2=w0 w1 (M0M1)2{S_b}^2 = w_0 \ (M_0 - M_t)^2 + w_1\ (M_1 - M_t)^2 = w_0\ w_1\ (M_0 - M_1) ^2
  • 圖像所有像素的方差:St2=Sw2+Sb2=常數{S_t}^2 = {S_w}^2 + {S_b}^2 = \text{常數}

根據以上的式子,我們用以下的式子計算分離度XX

X=Sb2Sw2=Sb2St2Sb2 X = \frac{{S_b}^2}{{S_w}^2} = \frac{{S_b}^2}{{S_t}^2 - {S_b}^2}

也就是說:
argmaxt X=argmaxt Sb2 \arg\max\limits_{t}\ X=\arg\max\limits_{t}\ {S_b}^2
換言之,如果使Sb2=w0 w1 (M0M1)2{S_b}^2={w_0}\ {w_1}\ (M_0 - M_1)^2最大,就可以得到最好的二值化閾值tt

求出一個閾值,使得類間方差最大化

#計算最大類間方差
def oturs(img):
    t = 0
    sd = 0
    for i in range(255):
        newsd = sfunc(img,i)
        if newsd > sd:
            sd = newsd
            t = i
    return t
#計算類間方差
def sfunc(img,t):
    w = img > t
    w1 = w.sum()/w.size
    w0 = 1-w1
    if w1==0 or w0==0:
        return 0
    m1 = np.mean(img[w])
    m0 = np.mean(img[~w])
    return w0*w1*(m0-m1)*(m0-m1)
imshow = plt.imshow(img)
plt.show()

img4 = img.copy()
img4 = gray(img4)
t = oturs(img4)
img4 = thre(img4,t)
imshow = plt.imshow(img4,cmap='gray')


5.HSV變換

將使用HSV\text{HSV}表示色彩的圖像的色相反轉吧!

HSV\text{HSV}即使用**色相(Hue)、飽和度(Saturation)、明度(Value)**來表示色彩的一種方式。

  • 色相:將顏色使用00^{\circ}360360^{\circ}表示,就是平常所說的顏色名稱,如紅色、藍色。色相與數值按下表對應:

    青色 藍色 品紅
    00^{\circ} 6060^{\circ} 120120^{\circ} 180180^{\circ} 240240^{\circ} 300300^{\circ} 360360^{\circ}
  • 飽和度:是指色彩的純度,飽和度越低則顏色越黯淡(0S<10\leq S < 1);

  • 明度:即顏色的明暗程度。數值越高越接近白色,數值越低越接近黑色(0V<10\leq V < 1);

RGB\text{RGB}色彩表示轉換到HSV\text{HSV}色彩表示通過以下方式計算:

RGB\text{RGB}的取值範圍爲[0,1][0, 1],令:
Max=max(R,G,B)Min=min(R,G,B) \text{Max}=\max(R,G,B)\\ \text{Min}=\min(R,G,B)
色相:
H={0(if Min=Max)60 GRMaxMin+60(if Min=B)60 BGMaxMin+180(if Min=R)60 RBMaxMin+300(if Min=G) H=\begin{cases} 0&(\text{if}\ \text{Min}=\text{Max})\\ 60\ \frac{G-R}{\text{Max}-\text{Min}}+60&(\text{if}\ \text{Min}=B)\\ 60\ \frac{B-G}{\text{Max}-\text{Min}}+180&(\text{if}\ \text{Min}=R)\\ 60\ \frac{R-B}{\text{Max}-\text{Min}}+300&(\text{if}\ \text{Min}=G) \end{cases}
飽和度:
S=MaxMin S=\text{Max}-\text{Min}
明度:
V=Max V=\text{Max}
HSV\text{HSV}色彩表示轉換到RGB\text{RGB}色彩表示通過以下方式計算:
C=SH=H60X=C (1Hmod  21)(R,G,B)=(VC) (1,1,1)+{(0,0,0)(if H is undefined)(C,X,0)(if0H<1)(X,C,0)(if1H<2)(0,C,X)(if2H<3)(0,X,C)(if3H<4)(X,0,C)(if4H<5)(C,0,X)(if5H<6) C = S\\ H' = \frac{H}{60}\\ X = C\ (1 - |H' \mod 2 - 1|)\\ (R,G,B)=(V-C)\ (1,1,1)+\begin{cases} (0, 0, 0)& (\text{if H is undefined})\\ (C, X, 0)& (\text{if}\quad 0 \leq H' < 1)\\ (X, C, 0)& (\text{if}\quad 1 \leq H' < 2)\\ (0, C, X)& (\text{if}\quad 2 \leq H' < 3)\\ (0, X, C)& (\text{if}\quad 3 \leq H' < 4)\\ (X, 0, C)& (\text{if}\quad 4 \leq H' < 5)\\ (C, 0, X)& (\text{if}\quad 5 \leq H' < 6) \end{cases}
請將色相反轉(色相值加180180),然後再用RGB\text{RGB}色彩空間表示圖片。

將RGB圖片轉爲HSV,即色相、飽和度、明度

然後將色相反轉,再轉回RGB輸出

def rgb2hsv(img):
    #標準化
    img = img/255
    #將img拉成 [128*128,3] 方便計算
    old_shape = img.shape
    img = img.reshape(-1,3)
    
    R,G,B = img[:,0],img[:,1],img[:,2]
    max = np.max(img,axis=1)
    min = np.min(img,axis=1)
    hsv = np.zeros_like(img)
    
    #計算色相
    h_b = min == B
    h_r = min == R
    h_g = min == G
    hsv[h_b,0] = 60*(G[h_b]-R[h_b])/(max[h_b]-min[h_b])+60
    hsv[h_r,0] = 60*(B[h_r]-G[h_r])/(max[h_r]-min[h_r])+180
    hsv[h_g,0] = 60*(R[h_g]-B[h_g])/(max[h_g]-min[h_g])+300
    #飽和度
    hsv[:,1] = max-min
    #明度
    hsv[:,2] = max
    
    return hsv.reshape(old_shape)
def hsv2rgb(img):
    #色相是環形的,所以要模360°
    img[:,:,0] = np.mod(img[:,:,0],360)
    
    old_shape = img.shape
    img = img.reshape(-1,3)
    c = img[:,1]
    h = img[:,0]/60
    x = c*(1-np.abs(np.mod(h,2)-1))
    v = img[:,2]

    #計算rgb
    #先建立v-c的基礎數組,v-c*(1,1,1)
    v_c = (v-c).reshape(-1,1)
    rgb = np.repeat(v_c,3,axis=1)
    
    #劃分條件,組合起來加到初始數組上
    #這裏將true和false視爲1和0,相乘就是取並集
    h0 = (h>=0)*(h<1)
    zero = np.zeros(h0.sum())
    rgb[h0] += np.vstack((c[h0],x[h0],zero)).T
    h1 = (h>=1)*(h<2)
    zero = np.zeros(h1.sum())
    rgb[h1] += np.vstack((x[h1],c[h1],zero)).T
    h2 = (h>=2)*(h<3)
    zero = np.zeros(h2.sum())
    rgb[h2] += np.vstack((zero,c[h2],x[h2])).T
    h3 = (h>=3)*(h<4)
    zero = np.zeros(h3.sum())
    rgb[h3] += np.vstack((zero,x[h3],c[h3])).T
    h4 = (h>=4)*(h<5)
    zero = np.zeros(h4.sum())
    rgb[h4] += np.vstack((x[h4],zero,c[h4])).T
    h5 = (h>=5)*(h<6)
    zero = np.zeros(h5.sum())
    rgb[h5] += np.vstack((c[h5],zero,x[h5])).T
    return rgb.reshape(old_shape)
imshow = plt.imshow(img)
plt.show()

img5 = img.copy()
img_hsv = rgb2hsv(img5)

img_hsv[:,:,0]+=180

img5 = hsv2rgb(img_hsv)
imshow = plt.imshow(img5)


6.減色處理

我們將圖像的值由2563256^3壓縮至434^3,即將RGB\text{RGB}的值只取{32,96,160,224}\{32, 96, 160, 224\}。這被稱作色彩量化。色彩的值按照下面的方式定義:
val={32(0var<64)96(64var<128)160(128var<192)224(192var<256) \text{val}= \begin{cases} 32& (0 \leq \text{var} < 64)\\ 96& (64\leq \text{var}<128)\\ 160&(128\leq \text{var}<192)\\ 224&(192\leq \text{var}<256) \end{cases}

將RGB的【0~255】範圍離散化至【32 , 96 , 160 , 224】

def dcolor(img):
    img[img<64] = 32
    img[(img>=64)*(img<128)] = 96
    img[(img>=128)*(img<192)] = 160
    img[(img>=192)*(img<256)] = 224
    return img
imshow = plt.imshow(img)
plt.show()

img6 = img.copy()
img6 = dcolor(img)
imshow = plt.imshow(img6)


7.平均池化(Average Pooling)

將圖片按照固定大小網格分割,網格內的像素值取網格內所有像素的平均值。

我們將這種把圖片使用均等大小網格分割,並求網格內代表值的操作稱爲池化(Pooling)

池化操作是**卷積神經網絡(Convolutional Neural Network)**中重要的圖像處理方式。平均池化按照下式定義:
v=1R i=1R vi v=\frac{1}{|R|}\ \sum\limits_{i=1}^R\ v_i
請把大小爲128×128128\times128imori.jpg使用8×88\times8的網格做平均池化。

一定範圍內的像素點全部轉爲該範圍的平均值。

def pooling(img,length,method):
    for i in range(int(img.shape[0]/length)):
        for j in range(int(img.shape[1]/length)):
            area = img[i*length:(i+1)*length,j*length:(j+1)*length]
            if(method=="avg"):
                area[:,:,0] = area[:,:,0].mean()
                area[:,:,1] = area[:,:,1].mean()
                area[:,:,2] = area[:,:,2].mean()
            if(method=="max"):
                area[:,:,0] = area[:,:,0].max()
                area[:,:,1] = area[:,:,1].max()
                area[:,:,2] = area[:,:,2].max()
    return img
imshow = plt.imshow(img)
plt.show()

img7 = img.copy()
img7 = pooling(img7,8,"avg")
imshow = plt.imshow(img7)


8.最大池化(Max Pooling)

網格內的值不取平均值,而是取網格內的最大值進行池化操作。

一定範圍內的像素點全部轉爲該範圍的最大值

imshow = plt.imshow(img)
plt.show()

img8 = img.copy()
img8 = pooling(img8,8,"max")
imshow = plt.imshow(img8)


9.高斯濾波

使用高斯濾波器(3×33\times3大小,標準差σ=1.3\sigma=1.3)來對imori_noise.jpg進行降噪處理吧!

高斯濾波器是一種可以使圖像平滑的濾波器,用於去除噪聲。可用於去除噪聲的濾波器還有中值濾波器(參見問題十),平滑濾波器(參見問題十一)、LoG濾波器(參見問題十九)。

高斯濾波器將中心像素周圍的像素按照高斯分佈加權平均進行平滑化。這樣的(二維)權值通常被稱爲卷積核(kernel)或者濾波器(filter)

但是,由於圖像的長寬可能不是濾波器大小的整數倍,因此我們需要在圖像的邊緣補00。這種方法稱作Zero Padding。並且權值gg(卷積核)要進行歸一化操作 g=1\sum\ g = 1)。

按下面的高斯分佈公式計算權值:
g(x,y,σ)=12 π σ2 ex2+y22 σ2 g(x,y,\sigma)=\frac{1}{2\ \pi\ \sigma^2}\ e^{-\frac{x^2+y^2}{2\ \sigma^2}}

標準差σ=1.3\sigma=1.388-近鄰高斯濾波器如下:
K=116 [121242121] K=\frac{1}{16}\ \left[ \begin{matrix} 1 & 2 & 1 \\ 2 & 4 & 2 \\ 1 & 2 & 1 \end{matrix} \right]

1.先設定一個高斯核,高斯核的值由二維高斯函數計算得出

2.高斯核進行標準化,也就是九宮格的和爲1

3.對於每一個像素點,以它爲中心取和高斯核相同大小的方塊(九宮格)

4.將高斯核和方塊相乘後累加,得出的值就是這個新的中心像素點的高斯濾波值

def padding(img,s=1):
    #先加行
    rows = np.zeros((s,img.shape[1],img.shape[2]),dtype="uint8")
    #再加列,這時候列長已經擴充 2*s
    columns = np.zeros((img.shape[0]+(2*s),s,img.shape[2]),dtype="uint8")
    #拼接
    img = np.vstack((rows,img,rows))
    img = np.hstack((columns,img,columns))
    return img
def getarea(img,padding,method):
    #建立一個新的數組,該數組用於保存計算的高斯濾波值,所以要去掉padding
    result = np.zeros([img.shape[0]-2*padding,
                      img.shape[1]-2*padding,
                      img.shape[2]],dtype="uint8")
    
    for i in range(int(img.shape[0]-2)):
        for j in range(int(img.shape[1]-2)):
            area = img[i:i+3,j:j+3]
            if(method=="gaussian"):
                #將像素值賦值到中心點
                kernel = np.array([1,2,1,2,4,2,1,2,1])*(1/16)
                result[i,j,:] = gaussFilter(area,kernel)
            if(method=="median"):
                result[i,j,:] = medianFilter(area)

    return result
def gaussFilter(area,kernel):
    #拉成一條,用矩陣的乘法完成濾波計算,參考卷積覈計算方法
    area = area.reshape(-1,3)
    newk = np.dot(kernel,area)
    newk = newk.astype(np.uint8)
    return newk
imshow = plt.imshow(img_noise)
plt.show()

img9 = img_noise.copy()
img9 = padding(img9,1)
img9 = getarea(img9,1,"gaussian")
imshow = plt.imshow(img9)


10.中值濾波

使用中值濾波器(3×33\times3大小)來對imori_noise.jpg進行降噪處理吧!

中值濾波器是一種可以使圖像平滑的濾波器。這種濾波器用濾波器範圍內(在這裏是3×33\times3)像素點的中值進行濾波,請在這裏也採用Zero Padding。

在濾波器中,取範圍內的中值數填入中心點

def medianFilter(area):
    #拉成一條,並且轉置爲橫向
    area = area.reshape(-1,3).T
    #進行排序後取出中間值
    newk = np.sort(area,axis=1)[:,4]
    return newk
imshow = plt.imshow(img_noise)
plt.show()

img10 = img_noise.copy()
img10 = padding(img10,1)
img10 = getarea(img10,1,"median")
imshow = plt.imshow(img10)


原創首發,歡迎來我的博客留言討論,我的博客主頁:LeonG是什麼意思?我的知乎專欄:LeonG與機器學習

版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

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