目錄
前言
一、圖像色彩空間轉換
1 RGB顏色空間
2 顏色加法
3 顏色加權加法
4 HSV顏色空間
5 RGB轉Gray(灰度)
二、灰度實驗
1 灰度圖
2 最大值法
3 平均值法
4 加權均值法
5 兩個極端的灰度值
三、圖像二值化處理
二值圖像
1 閾值法(THRESH_BINARY)
2 反閾值法(THRESH_BINARY_INV)
3 截斷閾值法(THRESH_TRUNC)
4 低閾值零處理(THRESH_TOZERO)
5 超閾值零處理(THRESH_TOZERO_INV)
6 OTSU閾值法
7 自適應二值化
7.1.取均值
7.2.加權求和
二值化代碼示例:
四、圖像變換操作
1.圖像翻轉(圖像鏡像旋轉)
2. 圖像仿射變換
3. 圖像旋轉
4. 圖像平移
5. 圖像縮放
總結
前言
????????這一章講一下圖像在色彩空間里面的轉換,如何把色彩圖轉化為灰度圖,以及如何將圖像二值化處理和圖像的翻轉操作
一、圖像色彩空間轉換
????????在OpenCV中,圖像色彩空間轉換是一項基礎且重要的操作,它涉及將圖像從一種顏色表示形式轉換為另一種表示形式的過程。通過這種轉換,可以更有效地執行特定類型的圖像處理和分析任務。常見的顏色空間包括RGB、HSV、YUV等。
- 色彩空間轉換的作用:
- 提高圖像處理效果:轉換到更適合的色彩空間(如HSV用于顏色分割)可以提升算法的準確性和魯棒性。
- 節省計算資源:在優化后的色彩空間中處理圖像,能降低計算復雜度,從而減少資源消耗。
1 RGB顏色空間
????????在圖像處理中,最常見的就是RGB顏色空間。RGB顏色空間是我們接觸最多的顏色空間,是一種用于表示和顯示彩色圖像的一種顏色模型。RGB代表紅色(Red)、綠色(Green)和藍色(Blue),這三種顏色通過不同強度的光的組合來創建其他顏色,廣泛應用于我們的生活中,比如電視、電腦顯示屏以及上面實驗中所介紹的RGB彩色圖。
????????RGB顏色模型基于笛卡爾坐標系,如下圖所示,RGB原色值位于3個角上,二次色青色、品紅色和黃色位于另外三個角上,黑色位于原點處,白色位于離原點最遠的角上。因為黑色在RGB三通道中表現為(0,0,0),所以映射到這里就是原點;而白色是(255,255,255),所以映射到這里就是三個坐標為最大值的點。
????????RGB顏色空間可以產生大約1600萬種顏色,幾乎包括了世界上的所有顏色,也就是說可以使用RGB顏色空間來生成任意一種顏色。
注意:在OpenCV中,顏色是以BGR的方式進行存儲的,而不是RGB,這也是上面紅色的像素值是(0,0,255)而不是(255,0,0)的原因。
2 顏色加法
圖像相加的方法有兩種:
- 使用OpenCV的cv.add()函數進行圖像相加
- 通過我們之前學過的NumPy直接相加
OpenCV加法與NumPy加法的區別:
OpenCV加法(飽和運算)
????????當計算結果超出有效像素值范圍(例如0-255)時,OpenCV會將結果截斷到最接近的邊界值。例如,加法結果為300時,輸出會被限制為255;結果為-50時,輸出會被限制為0。這種處理方式更適合圖像處理,能避免因數值溢出導致的異常亮暗區域。
NumPy加法(模運算)
????????NumPy的加法基于模運算,計算結果超出有效范圍時,會對255取模。例如,255+1=256,在8位圖像中會輸出0(對256來取模256 mod 256 = 0);300會被映射為44(對300來取模300 mod 256 = 44)。這種運算在圖像處理中可能導致不自然的像素值循環,如亮區突然變暗或暗區突然變亮。
ps:講一下模運算與取余的區別
取余運算?:
整數商(c)向 ?0 方向舍入?(即截斷小數部分,Math.trunc())。
公式:r = a - b * trunc(a / b)
?結果符號與被除數(a)相同。
?示例?:-7 ÷ 4?→ 商?c = trunc(-1.75) = -1,余數?r = -7 - (-1)*4 = -3
模運算?:
整數商(c)向 ?負無窮方向舍入?(Math.floor())。
公式:r = a - b * floor(a / b)
?結果符號與除數(b)相同。
?示例?:-7 ÷ 4?→ 商?c = floor(-1.75) = -2,余數?r = -7 - (-2)*4 = 1
只有當被除數與除數同號時,兩者結果相同(如?7 % 4 = 3,-7 % -4 = -3)
示例代碼:
import cv2 as cv
import numpy as np
# 讀取圖像
pig = cv.imread("../images/pig.png")
grass = cv.imread("../images/cao.png")# 飽和操作cv.add(img1, img2) np.uint8 0~255 250+10=255(加到最大值也只會是255,最小為0)
saturation1 = cv.add(pig, grass)
# cv.imshow("saturation1", saturation1)# 使用numpy直接相加 取模運算 對256取模 250+10=260%256=4
saturation2 = pig + grass
# cv.imshow("saturation2", saturation2)x = np.uint8([[250]])
y = np.uint8([[10]])
xy1 = cv.add(x, y)
xy2 = x + y
print(xy1)
print(xy2)cv.waitKey(0)
cv.destroyAllWindows()
3 顏色加權加法
cv2.addWeighted(src1,alpha,src2,deta,gamma)
src1、src2:輸入圖像。
alpha、beta:兩張圖象權重。
gamma:亮度調整值。
????????gamma > 0,圖像會變亮。
????????gamma < 0,圖像會變暗。
????????gamma = 0,則沒有額外的亮度調整。
????????這也是加法,但是不同的是兩幅圖像的權重不同,這就會給人一種混合或者透明的感覺。圖像混合的計算公式如下:
g(x) = (1?α)f0(x) + αf1(x)
通過修改 α 的值(0 → 1),可以實現非常炫酷的混合。
????????現在我們把兩幅圖混合在一起。第一幅圖的權重是0.7,第二幅圖的權重是0.3。函數cv2.addWeighted()可以按下面的公式對圖片進行混合操作。
dst = α?img1 + β?img2 + γ
這里γ取為零。
# 顏色加權加法 cv.addWeighted(img1, alpha(圖片1的權重), img2, beta(圖片2的權重), gamma(亮度調整))
dst = cv.addWeighted(pig, 0.4, grass, 0.6, 30)
cv.imshow("addWeighted", dst)
4 HSV顏色空間
cv.cvtColor(img, cv.COLOR_BGR2HSV)
????????HSV顏色空間指的是HSV顏色模型,這是一種與RGB顏色模型并列的顏色空間表示法。RGB顏色模型使用紅、綠、藍三原色的強度來表示顏色,是一種加色法模型,即顏色的混合是添加三原色的強度。而HSV顏色空間使用色調(Hue)、飽和度(Saturation)和亮度(Value)三個參數來表示顏色,色調H表示顏色的種類,如紅色、綠色、藍色等;飽和度表示顏色的純度或強度,如紅色越純,飽和度就越高;亮度表示顏色的明暗程度,如黑色比白色亮度低。
HSV顏色模型是一種六角錐體模型,如下圖所示:
色調H:
????????使用角度度量,取值范圍為0°~360°,從紅色開始按逆時針方向計算,紅色為0°,綠色為120°,藍色為240°。它們的補色是:黃色為60°,青色為180°,紫色為300°。通過改變H的值,可以選擇不同的顏色
飽和度S:
????????飽和度S表示顏色接近光譜色的程度。一種顏色可以看成是某種光譜色與白色混合的結果。其中光譜色所占的比例越大,顏色接近光譜色的程度就越高,顏色的飽和度就越高。飽和度越高,顏色就越深而艷,光譜色的白光成分為0,飽和度達到最高。通常取值范圍為0%~100%,其中0%表示灰色或無色,100%表示純色,通過調整飽和度的值,可以使顏色變得更加鮮艷或者更加灰暗。
明度V:
????????明度表示顏色明亮的程度,對于光源色,明度值與發光體的光亮度有關;對于物體色,此值和物體的透射比或反射比有關。通常取值范圍為0%(黑)到100%(白),通過調整明度的值,可以使顏色變得更亮或者更暗。
????????一般對顏色空間的圖像進行有效處理都是在HSV空間進行的,然后對于基本色中對應的HSV分量需要給定一個嚴格的范圍,下面是通過實驗計算的模糊范圍(準確的范圍在網上都沒有給出)。
H: 0— 180
S: 0— 255
V: 0— 255
此處把部分紅色歸為紫色范圍:
為什么有了RGB顏色空間我們還是需要轉換成HSV顏色空間來進行圖像處理呢?
????????符合人類對顏色的感知方式:人類對顏色的感知是基于色調、飽和度和亮度三個維度的,而HSV顏色空間恰好就是通過這三個維度來描述顏色的。因此,使用HSV空間處理圖像可以更直觀地調整顏色和進行色彩平衡等操作,更符合人類的感知習慣。
????????顏色調整更加直觀:在HSV顏色空間中,色調、飽和度和亮度的調整都是直觀的,而在RGB顏色空間中調整顏色不那么直觀。例如,在RGB空間中要調整紅色系的顏色,需要同時調整R、G、B三個通道的數值,而在HSV空間中只需要調整色調和飽和度即可。
????????降維處理有利于計算:在圖像處理中,降維處理可以減少計算的復雜性和計算量。HSV顏色空間相對于RGB顏色空間,減少了兩個維度(紅、綠、藍),這有利于進行一些計算和處理任務,比如色彩分割、匹配等。
因此,在進行圖片顏色識別時,我們會將RGB圖像轉換到HSV顏色空間,然后根據顏色區間來識別目標顏色。
5 RGB轉Gray(灰度)
?cv2.cvtColor 是OpenCV中的一個函數,用于圖像顏色空間的轉換。可以將一個圖像從一個顏色空間轉換為另一個顏色空間,比如從RGB到灰度圖,或者從RGB到HSV的轉換等。
cv2.cvtColor(img,code)
img 參數
????????可以傳入 NumPy 數組或 OpenCV 的 Mat 對象。Python 中 OpenCV 函數通常直接處理 NumPy 數組,OpenCV 會自動在 Mat 和數組之間轉換。例如?cv2.imread()返回的就是 NumPy 數組。
code 參數
????????用于指定顏色空間轉換類型。例如:
????????????????cv2.COLOR_RGB2GRAY?表示將 RGB 圖像轉為灰度圖
????????????????其他常見代碼包括?cv2.COLOR_BGR2RGB?等
示例代碼:
import cv2 as cv# 讀取圖像
img = cv.imread("../images/1.jpg")# 顏色轉換 cv.cvtColor(img, code, dst, dstCn)
# 轉灰度圖
dst = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow("src", dst)# 轉HSV
dst1 = cv.cvtColor(img, cv.COLOR_BGR2HSV)
cv.imshow("HSV", dst1)# 轉RGB
dst2 = cv.cvtColor(img, cv.COLOR_BGR2RGB)
cv.imshow("RGB", dst2)cv.waitKey(0)
二、灰度實驗
????????彩色圖像轉換為灰度圖像(灰度化)是圖像處理和計算機視覺中的基礎操作,其核心是將RGB三通道信息融合為單通道數據。彩色圖像由紅(R)、綠(G)、藍(B)三通道組成,而灰度圖僅保留單一亮度通道。灰度化本質是通過數學映射將三維顏色空間壓縮為一維亮度值。
1 灰度圖
????????灰度圖像指每個像素僅包含單一采樣顏色的圖像,通常表現為從純黑到純白之間的連續過渡色調。盡管采樣顏色理論上可為任意色調的不同明度變化,甚至可呈現不同亮度下的異色效果,但實際應用中多采用可見光譜范圍內的亮度測量值。
????????黑白圖像(二值圖像)僅包含絕對黑與絕對白兩種顏色狀態。相比之下,灰度圖像具有豐富的過渡層次,通過在黑白之間設置多級顏色深度實現平滑的亮度漸變。典型計算機系統中,顯示用的灰度圖像采用8位非線性存儲格式,可精確記錄256級灰度值。
????????灰度圖像的生成通常基于單電磁波頻譜的像素亮度測量。每個像素點的數值對應特定亮度級別,這種線性或非線性量化過程保留了原始場景的光照信息。8位存儲方案通過二進制編碼實現高效的灰度等級存儲,其中0代表全黑,255代表全白。
2 最大值法
????????對于彩色圖像的每個像素,它會從R、G、B三個通道的值中選出最大的一個,并將其作為灰度圖像中對應位置的像素值。
例如某圖像中某像素點的像素值如上圖所示,那么在使用最大值法進行灰度化時,就會從該像素點對應的RGB通道中選取最大的像素值作為灰度值,所以在灰度圖中的對應位置上,該像素點的像素值就是121。
示例代碼:
import cv2 as cv
import numpy as np# 讀取圖像
pig = cv.imread("../images/pig.png")
shape = pig.shape
img = np.zeros((shape[0], shape[1]), dtype=np.uint8)
# 循環遍歷每一行
for i in range(shape[0]):for j in range(shape[1]):img[i, j] = max(pig[i, j, 0], pig[i, j, 1], pig[i, j, 2])cv.imshow("pig", img)
cv.waitKey(0)
cv.destroyAllWindows()
3 平均值法
對于彩色圖像的每個像素,它會將R、G、B三個通道的像素值全部加起來,然后再除以三,得到的平均值就是灰度圖像中對應位置的像素值。
例如某圖像中某像素點的像素值如上圖所示,那么在使用平均值進行灰度化時,其計算結果就是(91+121+46)/3=86(對結果進行取整),所以在灰度圖中的對應位置上,該像素點的像素值就是86。 ?
示例代碼:
import cv2 as cv
import numpy as np# 讀取圖像
pig = cv.imread("../images/pig.png")
shape = pig.shape
img = np.zeros((shape[0], shape[1]), dtype=np.uint8)# 循環遍歷每一行
for i in range(shape[0]):for j in range(shape[1]):# int():轉為更大的數據類型,防止溢出img[i, j] = np.uint8((int(pig[i, j, 0]) + int(pig[i, j, 1]) + int(pig[i, j, 2])) // 3)cv.imshow("pig", img)
cv.waitKey(0)
cv.destroyAllWindows()
4 加權均值法
????????對于彩色圖像的每個像素,它會按照一定的權重去乘以每個通道的像素值,并將其相加,得到最后的值就是灰度圖像中對應位置的像素值。本實驗中,權重的比例為: R乘以0.299,G乘以0.587,B乘以0.114,這是經過大量實驗得到的一個權重比例,也是一個比較常用的權重比例。
????????所使用的權重之和應該等于1。這是為了確保生成的灰度圖像素值保持在合理的亮度范圍內,并且不會因為權重的比例不當導致整體過亮或過暗。
例如某圖像中某像素點的像素值如上圖所示,那么在使用加權平均值進行灰度化時,其計算結果就是10*0.299+121*0.587+46*0.114=79。所以在灰度圖中的對應位置上,該像素點的像素值就是79。
代碼示例:?
import cv2 as cv
import numpy as np# 讀取圖像
pig = cv.imread("../images/pig.png")
shape = pig.shape
img = np.zeros((shape[0], shape[1]), dtype=np.uint8)# 定義權重
wb, wg, wr = 0.114, 0.587, 0.299# 循環遍歷每一行
for i in range(shape[0]):for j in range(shape[1]):img[i, j] = round(wb * pig[i, j, 0] + wg * pig[i, j, 1] + wr * pig[i, j, 2])cv.imshow("pig", img)
cv.waitKey(0)
cv.destroyAllWindows()
5 兩個極端的灰度值
在灰度圖像中,“極端”的灰度值指的是亮度的兩個極端:最暗和最亮的值。
-
最暗的灰度值:0。這代表完全黑色,在灰度圖像中沒有任何亮度。
-
最亮的灰度值:255。這代表完全白色,在灰度圖像中具有最大亮度。
三、圖像二值化處理
二值圖像
????????將某張圖像的所有像素改成只有兩種值之一。 一幅二值圖像的二維矩陣僅由0、1兩個值構成,“0”代表黑色,“1”代白色。由于每一像素(矩陣中每一元素)取值僅有0、1兩種可能,所以計算機中二值圖像的數據類型通常為1個二進制位。二值圖像通常用于文字、線條圖的掃描識別(OCR)和掩膜圖像的存儲。
?
????????其操作的圖像也必須是灰度圖,也就是說,二值化的過程,就是將一張灰度圖上的像素根據某種規則修改為0和maxval(maxval表示最大值,一般為255,顯示白色)兩種像素值,使圖像呈現黑白的效果,能夠幫助我們更好地分析圖像中的形狀、邊緣和輪廓等特征。
????????簡便:降低計算量和計算需求,加快處理速度。
????????節約資源:二值圖像占用空間遠小于彩色圖。
????????邊緣檢測:二值化常作為邊緣檢測的預處理步驟,因為簡化后的圖易于識別出輪廓和邊界。
1~6是所有閾值法的解釋
_,binary = cv2.threshold(img,thresh,maxval,type)
????????img:輸入圖像,要進行二值化處理的灰度圖。
????????thresh:設定的閾值。當像素值大于(或小于,取決于閾值類型)thresh時,該像素被賦予的值。
????????type:閾值處理的類型。
返回值:
????????第一個值(通常用下劃線表示):計算出的閾值,若使用自適應閾值法,會根據算法自動計算出這個值。
????????第二個值(binary):二值化后的圖像矩陣。與輸入圖像尺寸相同。
在本實驗中,使用了六種不同的方式來對灰度圖進行二值化。
1 閾值法(THRESH_BINARY)
????????閾值法就是通過設置一個閾值,將灰度圖中的每一個像素值與該閾值進行比較,小于等于閾值的像素就被設置為0(通常代表背景),大于閾值的像素就被設置為maxval(通常代表前景)。對于我們的8位圖像(0~255)來說,通常是設置為255。
???????如上圖所示,在灰度圖中像素值較高的地方,如花瓣、花莖等地方的像素值比閾值高,那么在生成的二值化圖中的對應位置的像素值就會被設置為255,也就是純白色。
cv2.IMREAD_GRAYSCALE:OpenCV 中用于指定圖像加載模式的一個標志,它告訴 cv.imread() 函數以灰度格式讀取圖像。
示例代碼統一放在7下面
2 反閾值法(THRESH_BINARY_INV)
顧名思義,就是與閾值法相反。反閾值法是當灰度圖的像素值大于閾值時,該像素值將會變成0(黑),當灰度圖的像素值小于等于閾值時,該像素值將會變成maxval。
????????如上圖所示,使用反閾值法對灰度圖進行二值化時,會將灰度圖中像素值大于閾值的地方置為0(也就是黑),將灰度圖中像素值小于閾值的地方置為255(也就是白)。
示例代碼統一放在7下面
3 截斷閾值法(THRESH_TRUNC)
????????截斷閾值法,指將灰度圖中的所有像素與閾值進行比較,像素值大于閾值的部分將會被修改為閾值,小于等于閾值的部分不變。換句話說,經過截斷閾值法處理過的二值化圖中的最大像素值就是閾值。
?
當截斷閾值為255時,如上圖所示,可以看到灰度圖與二值化圖沒有任何的區別。
使用截斷閾值法進行圖像二值化處理時,設置的maxval
參數實際上是不起作用的。
示例代碼統一放在7下面
4 低閾值零處理(THRESH_TOZERO)
????????低閾值零處理,字面意思,就是像素值小于等于閾值的部分被置為0(也就是黑色),大于閾值的部分不變。 ?
如上圖所示,在灰度圖中較亮的部分,其像素值比閾值大,所以在二值化后其像素值并沒有發生變化。而灰度圖中較暗的部分,也就是像素值較低的地方,由于像素值比閾值小,就會被置為0,對應二值化圖中的黑色部分。
示例代碼統一放在7下面
5 超閾值零處理(THRESH_TOZERO_INV)
????????超閾值零處理就是將灰度圖中的每個像素與閾值進行比較,像素值大于閾值的部分置為0(也就是黑色),像素值小于等于閾值的部分不變。
????????如上圖所示,在灰度圖中較亮的部分,其像素值比閾值大,所以在二值化后其像素值會被置為0(也就是黑色),對應二值化圖中的黑色部分。而灰度圖中較暗的部分,也就是像素值較低的地方,由于像素值比閾值小,將不會發生改變。
????????以上介紹的二值化方法都需要手動設置閾值,但是在不同的環境下,攝像頭拍攝的圖像可能存在差異,導致手動設置的閾值并不適用于所有圖像,這可能會導致二值化效果不理想。
????????因此,我們需要一種能自動計算每張圖片閾值的二值化方法,能夠根據每張圖像的特點自動計算出適合該圖像的二值化閾值,從而達到更好的二值化效果。這種二值化方法可以在不同環境下適用,提高圖像處理的準確性和魯棒性。
示例代碼統一放在7下面
6 OTSU閾值法
????????cv2.THRESH_OTS 并不是一個有效的閾值類型或標。THRESH_OTSU 本身并不是一個獨立的閾值化方法,而是與 OpenCV 中的二值化方法結合使用的一個標志。具體來說,THRESH_OTSU 通常與 THRESH_BINARY 或 THRESH_BINARY_INV 結合使用。在實際應用中,如果你使用 THRESH_OTSU 標志但沒有指定其他二值化類型,默認情況下它會與 THRESH_BINARY 結合使用。也就是說,當你僅指定了 cv2.THRESH_OTSU,實際上等同于同時指定了 cv2.THRESH_BINARY + cv2.THRESH_OTSU。
????????簡單來說就是cv2.THRESH_OTSU 是 OpenCV 中用于自動計算最優閾值的標志,但它需要與其他二值化方法(如 cv2.THRESH_BINARY 或 cv2.THRESH_BINARY_INV)結合使用。單獨指定該標志時,OpenCV 默認會將其與 cv2.THRESH_BINARY 組合。
????????在介紹OTSU閾值法之前,我們首先要了解一下雙峰圖片的概念。
????????雙峰圖片就是指灰度圖的直方圖上有兩個峰值,直方圖就是對灰度圖中每個像素值的點的個數的統計圖,如下圖所示。
灰度圖直方圖的基礎概念
-
灰度級:
-
在灰度圖像中,每個像素的值代表其亮度,通常范圍是 0 到 255(對于 8 位灰度圖像)。
-
0 表示黑色,255 表示白色,中間的值表示不同程度的灰色。
-
-
直方圖定義:
-
直方圖是一個柱狀圖,其中 x 軸表示灰度級(從 0 到 255),y 軸表示對應灰度級在圖像中出現的次數(頻率)。
-
每個柱子的高度代表該灰度級在圖像中出現的像素數量。
-
????????OTSU算法是通過一個值將這張圖分前景色和背景色(也就是灰度圖中小于這個值的是一類,大于這個值的是一類。例如,如果你設置閾值為128,則所有大于128的像素點可以被視作前景,而小于等于128的像素點則被視為背景。),通過統計學方法(最大類間方差)來驗證該值的合理性,當根據該值進行分割時,使用最大類間方差計算得到的值最大時,該值就是二值化算法中所需要的閾值。通常該值是從灰度圖中的最小值加1開始進行迭代計算,直到灰度圖中的最大像素值減1,然后把得到的最大類間方差值進行比較,來得到二值化的閾值。以下是一些符號規定:
T:閾值
:前景像素點數
:背景像素點數
:前景的像素點數占整幅圖像的比例
:背景的像素點數占整幅圖像的比例
0:前景的平均像素值
1:背景的平均像素值
:整幅圖的平均像素值
rows×cols:圖像的行數和列數
下面舉個例子,有一張大小為4×4的圖片,假設閾值T為1,那么:
?
????????也就是這張圖片根據閾值1分為了前景(像素為2的部分)和背景(像素為0)的部分,并且計算出了OTSU算法所需要的各個數據,根據上面的數據,我們給出計算類間方差的公式:
????????g就是前景與背景兩類之間的方差,這個值越大,說明前景和背景的差別就越大,效果就越好。OTSU算法就是在灰度圖的像素值范圍內遍歷閾值T,使得g最大,基本上雙峰圖片的閾值T在兩峰之間的谷底。
通過OTSU算法得到閾值之后,就可以結合上面的方法根據該閾值進行二值化,在本實驗中有THRESH_OTSU和THRESH_INV_OTSU兩種方法,就是在計算出閾值后結合了閾值法和反閾值法。
注意:使用OTSU算法計算閾值時,組件中的thresh參數將不再有任何作用。
示例代碼統一放在7下面
?
7 自適應二值化
cv2.adaptiveThreshold(image_np_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 7, 10)
????????與二值化算法相比,自適應二值化更加適合用在明暗分布不均的圖片,因為圖片的明暗不均,導致圖片上的每一小部分都要使用不同的閾值進行二值化處理,這時候傳統的二值化算法就無法滿足我們的需求了,于是就出現了自適應二值化。
????????自適應二值化方法會對圖像中的所有像素點計算其各自的閾值,這樣能夠更好的保留圖片里的一些信息。自適應二值化組件內容如下圖所示:
?
其中各個參數的含義如下:
?maxval :最大閾值,一般為255
?adaptiveMethod :小區域閾值的計算方式:
?ADAPTIVE_THRESH_MEAN_C :小區域內取均值
?ADAPTIVE_THRESH_GAUSSIAN_C :小區域內加權求和,權重是個高斯核
?thresholdType :二值化方法,只能使用THRESH_BINARY、THRESH_BINARY_INV,也就是閾值法和反閾值法
?blockSize :選取的小區域的面積,如7就是7\*7的小塊。
?c :最終閾值等于小區域計算出的閾值再減去此值
下面介紹一下這兩種方法。
7.1.取均值
比如一張圖片的左上角像素值如下圖所示:
????????假如我們使用的小區域是3*3的,那么就會從圖片的左上角開始(也就是像素值為162的地方)計算其鄰域內的平均值,如果處于邊緣地區就會對邊界進行填充,填充值就是邊界的像素點,如下圖所示:
?
????????那么對于左上角像素值為162的這個點,161(也就是上圖中括號內的計算結果,結果會進行取整)就是根據平均值計算出來的閾值,接著減去一個固定值C,得到的結果就是左上角這個點的二值化閾值了,接著根據選取的是閾值法還是反閾值法進行二值化操作。緊接著,向右滑動計算每個點的鄰域內的平均值,直到計算出右下角的點的閾值為止。我們所用到的不斷滑動的小區域被稱之為核,比如3*3的小區域叫做3*3的核,并且核的大小都是奇數個,也就是3*3、5*5、7*7等。
????????自適應二值化(Adaptive Thresholding)的核心思想就是為圖像中的每個像素點計算一個局部閾值。這種方法與全局閾值化不同,后者對整個圖像使用同一個固定的閾值。而在自適應二值化中,每個像素的閾值是基于其周圍鄰域內的像素值動態確定的。
7.2.加權求和
對小區域內的像素進行加權求和得到新的閾值,其權重值來自于高斯分布。
高斯分布,通過概率密度函數來定義高斯分布,一維高斯概率分布函數為:
通過改變函數中和的值,我們可以得到如下圖像,其中均值為,標準差為
。
此時我們拓展到二維圖像,一般情況下我們使x軸和y軸的相等并且,此時我們可以得到二維高斯函數的表達式為:
高斯概率函數是相對于二維坐標產生的,其中(x,y)為點坐標,要得到一個高斯濾波器模板,應先對高斯函數進行離散化,將得到的值作為模板的系數。例如:要產生一個3*3的高斯權重核,以核的中心位置為坐標原點進行取樣,其周圍的坐標如下圖所示(x軸水平向右,y軸豎直向上)
將坐標帶入上面的公式中,即可得到一個高斯權重核。
而在opencv里,當kernel(小區域)的尺寸為1、3、5、7并且用戶沒有設置sigma的時候(sigma <= 0),核值就會取固定的系數,這是一種默認的值是高斯函數的近似。
kernel尺寸 | 核值 |
---|---|
1 | [1] |
3 | [0.25, 0.5, 0.25] |
5 | [0.0625, 0.25, 0.375, 0.25, 0.0625] |
7 | [0.03125, 0.109375, 0.21875, 0.28125, 0.21875, 0.109375, 0.03125] |
比如kernel的尺寸為3*3時,使用
進行矩陣的乘法,就會得到如下的權重值,其他的類似。
通過這個高斯核,即可對圖片中的每個像素去計算其閾值,并將該閾值減去固定值得到最終閾值,然后根據二值化規則進行二值化。
而當kernels尺寸超過7的時候,如果sigma設置合法(用戶設置了sigma),則按照高斯公式計算.當sigma不合法(用戶沒有設置sigma),則按照如下公式計算sigma的值:
某像素點的閾值計算過程如下圖所示:
?
首先還是對邊界進行填充,然后計算原圖中的左上角(也就是162像素值的位置)的二值化閾值,其計算過程如上圖所示,再然后根據選擇的二值化方法對左上角的像素點進行二值化,之后核向右繼續計算第二個像素點的閾值,第三個像素點的閾值…直到右下角(也就是155像素值的位置)為止。
當核的大小不同時,僅僅是核的參數會發生變化,計算過程與此是一樣的。
cv2.adaptiveThreshold參數解釋:
1. image_np_gray: 輸入圖像,這里必須是灰度圖像(單通道)。
2. 255: 輸出圖像的最大值。在二值化后,超過自適應閾值的像素會被設置為該最大值,通常為255表示白色;未超過閾值的像素將被設置為0,表示黑色。
3. cv2.ADAPTIVE_THRESH_GAUSSIAN_C: 自適應閾值類型。在這個例子中,使用的是高斯加權的累計分布函數(CDF),并添加一個常數 C 來計算閾值。另一種可選類型是 cv2.ADAPTIVE_THRESH_MEAN_C,它使用鄰域內的平均值加上常數 C 計算閾值。
4. cv2.THRESH_BINARY: 輸出圖像的類型。這意味著輸出圖像將會是一個二值圖像(binary image),其中每個像素要么是0要么是最大值(在這里是255)。另外還有其他選項如 cv2.THRESH_BINARY_INV 會得到相反的二值圖像。
5. 7: blockSize 參數,表示計算每個像素閾值時所考慮的7x7鄰域大小(正方形區域的寬度和高度),其值必須是奇數。
6. 10: C 參數,即上面提到的常數值,在計算自適應閾值時與平均值或高斯加權值相加。正值增加閾值,負值降低閾值,具體效果取決于應用場景。
二值化代碼示例:
import cv2 as cv
# 讀圖
flower = cv.imread('../images/flower.png')flower = cv.resize(flower, (0, 0), fx=0.5, fy=0.5)
# 灰度化
gray = cv.cvtColor(flower, cv.COLOR_BGR2GRAY)
cv.imshow("1", gray)
# 二值化 閾值法
ret, binary1 = cv.threshold(gray, 127, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
# cv.imshow('binary1', binary1)# 二值化 反閾值法
_, binary_inv = cv.threshold(gray, 127, 255, cv.THRESH_BINARY_INV)
# cv.imshow('binary2', binary_inv)# 二值化 截斷閾值法
_, binary_trunc = cv.threshold(gray, 127, 255, cv.THRESH_TRUNC)
# cv.imshow('binary3', binary_trunc)# 二值化 低閾值零處理
_, binary_tozero = cv.threshold(gray, 127, 255, cv.THRESH_TOZERO)
# cv.imshow('binary4', binary_tozero)# 二值化 超閾值零處理
_, binary_tozero_inv = cv.threshold(gray, 127, 255, cv.THRESH_TOZERO_INV)
# cv.imshow('binary5', binary_tozero_inv)# OTSU閾值法 默認結合cv.THRESH_BINARY cv.THRESH_OTSU = cv.THRESH_BINARY + cv.THRESH_OTSU
_, binary_otsu = cv.threshold(gray, 200, 255, cv.THRESH_OTSU)
# cv.imshow('binary_otsu', binary_otsu)_, binary_otsu1 = cv.threshold(gray, 200, 255, cv.THRESH_OTSU + cv.THRESH_BINARY_INV)
# cv.imshow('binary_otsu1', binary_otsu1)# 自適應二值化 小區域計算 必須結合閾值法或者反閾值法,否則會報錯,但只能結合一個
# 取均值
binary_adaptive = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 9, 6)
cv.imshow('binary_adaptive', binary_adaptive)# 加權均值法 高斯核
binary_adaptive_gaussian = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 9, 6)
cv.imshow('binary_adaptive_gaussian', binary_adaptive_gaussian)cv.waitKey(0)
cv.destroyAllWindows()
四、圖像變換操作
1.圖像翻轉(圖像鏡像旋轉)
在OpenCV中,圖片的鏡像旋轉是以圖像的中心為原點進行鏡像翻轉的。
-
cv2.flip(img,flipcode)
-
參數
-
img: 要翻轉的圖像
-
flipcode: 指定翻轉類型的標志
-
flipcode=0: 垂直翻轉,圖片像素點沿x軸翻轉
-
flipcode>0: 水平翻轉,圖片像素點沿y軸翻轉
-
flipcode<0: 水平垂直翻轉,水平翻轉和垂直翻轉的結合
-
-
import cv2 as cvface = cv.imread("../images/gezi.png")
face = cv.resize(face, (0, 0), fx=0.5, fy=0.5)
cv.imshow("face", face)# flipcode = 0 表示垂直翻轉 沿著x軸翻轉 上下翻轉 1 表示水平翻轉 沿著y軸翻轉 左右翻轉
filp_0 = cv.flip(face, 0)
cv.imshow("filp_0", filp_0)
filp_1 = cv.flip(face, 1)
cv.imshow("filp_1", filp_1)
filp_2 = cv.flip(face, -1)
cv.imshow("filp_2", filp_2)cv.waitKey(0)
cv.destroyAllWindows()
2. 圖像仿射變換
仿射變換(Affine Transformation)是一種線性變換,保持了點之間的相對距離不變。
仿射變換的基本性質
-
保持直線
-
保持平行
-
比例不變性
-
不保持角度和長度
常見的仿射變換類型
-
旋轉:繞著某個點或軸旋轉一定角度。
-
平移:僅改變物體的位置,不改變其形狀和大小。
-
縮放:改變物體的大小。
-
剪切:使物體發生傾斜變形。
仿射變換的基本原理
-
線性變換
-
二維空間中,圖像點坐標為(x,y),仿射變換的目標是將這些點映射到新的位置 (x', y')。
-
為了實現這種映射,通常會使用一個矩陣乘法的形式:
(類似于y=kx+b)
?
-
a,b,c,d 是線性變換部分的系數,控制旋轉、縮放和剪切。
-
,
是平移部分的系數,控制圖像在平面上的移動。
-
輸入點的坐標被擴展為齊次坐標形式[x,y,1],以便能夠同時處理線性變換和平移
cv2.warpAffine()函數
-
仿射變換函數
cv2.warpAffine(img,M,dsize)
- g:輸入圖像。
- M:2x3的變換矩陣,類型為np.float32。
- dsize:輸出圖像的尺寸,形式為(width,height)。
?
3. 圖像旋轉
旋轉圖像可以將圖像繞著某個點旋轉一定的角度。
cv2.getRotationMatrix2D()函數
-
獲取旋轉矩陣
cv2.getRotationMatrix2D(center,angle,scale)
-
center:旋轉中心點的坐標,格式為
(x,y)
。 -
angle:旋轉角度,單位為度,正值表示逆時針旋轉負值表示順時針旋轉。
-
scale:縮放比例,若設為1,則不縮放。
-
返回值:M,2x3的旋轉矩陣。
-
import cv2 as cvcat = cv.imread("../images/cat1.png")
cat = cv.resize(cat, (520, 520))# 獲取旋轉矩陣 cv2.getRotationMatrix2D(旋轉中心center,旋轉角度angle,縮放因子scale)
matrix = cv.getRotationMatrix2D((260, 260), -45, 1)# 仿射變換 cv2.warpAffine(src, M, dsize) (w, h)
result = cv.warpAffine(cat, matrix, (520, 520))
cv.imshow("result", result)cv.waitKey(0)
cv.destroyAllWindows()
4. 圖像平移
移操作可以將圖像中的每個點沿著某個方向移動一定的距離。
-
假設我們有一個點 P(x,y),希望將其沿x軸方向平移t_x*個單位,沿y軸方向平移t_y個單位到新的位置P′(x′,y′),那么平移公式如下:
x′=x+tx
y′=y+ty
在矩陣形式下,該變換可以表示為:
?這里的和
分別代表在x軸和y軸上的平移量。
import cv2 as cv
import numpy as npcat = cv.imread("../images/cat1.png")
cat = cv.resize(cat, (520, 520))# 定義平移量
tx = 80
ty = 120# 定義平移矩陣
M = np.float32([[1, 0, tx], [0, 1, ty]])
# 仿射變換
dst = cv.warpAffine(cat, M, (400, 400))
cv.imshow("result", dst)
cv.waitKey(0)
cv.destroyAllWindows()
5. 圖像縮放
縮放操作可以改變圖片的大小。
-
假設要把圖像的寬高分別縮放為0.5和0.8,那么對應的縮放因子sx=0.5,sy=0.8。
-
點P(x,y)對應到新的位置
,縮放公式為:
在矩陣形式下,該變換可以表示為
?相較于圖像旋轉中只能等比例的縮放,圖像縮放更加靈活,可以在指定方向上進行縮放。?
sx和sy分別表示在x軸和y軸方向上的縮放因子。
import cv2 as cv
import numpy as npcat = cv.imread("../images/cat1.png")
cat = cv.resize(cat, (520, 520))# 縮小量
sx = 0.5
sy = 0.5# 定義平移矩陣
M = np.float32([[sx, 0, 0], [0, sy, 0]])# 仿射變換
dst = cv.warpAffine(cat, M, (400, 400))
cv.imshow("result", dst)
cv.waitKey(0)
cv.destroyAllWindows()
總結
? ? ? ? 今天內容有點多,我覺得重要的地方是理解公式的含義,只要知道方法,大多內容都通了,圖像變換操作部分,主要理解矩陣里面每個數字代表的含義和矩陣是如何運算的,多數的問題就都能理解了,今天依舊努力學習/.