本文全面講解OpenCV圖像預處理的七大核心技術(插值方法、邊緣填充、圖像矯正(透視變換)、圖像掩膜、ROI切割、圖像添加水印、圖像噪點消除),每個知識點都配有詳細解釋和實用代碼示例,幫助初學者建立系統的圖像處理知識體系。
一、插值方法:圖像縮放的核心技術
插值是在圖像縮放或旋轉時估算新像素值的方法,不同方法在速度和質量上有顯著差異。
1. 最近鄰插值
- 原理:直接取最鄰近像素的值
- 特點:速度最快,但會產生鋸齒
- 適用場景:實時系統或低功耗設備
resized = cv2.resize(img, (800, 600), interpolation=cv2.INTER_NEAREST)
2. 雙線性插值
- 原理:取周圍4個像素的加權平均值
- 特點:速度與質量平衡
- 適用場景:大多數常規縮放需求
resized = cv2.resize(img, (800, 600), interpolation=cv2.INTER_LINEAR)
3. 雙三次插值
- 原理:取周圍16個像素的加權平均值
- 特點:質量高但速度慢
- 適用場景:高質量圖像處理
resized = cv2.resize(img, (800, 600), interpolation=cv2.INTER_CUBIC)
4. Lanczos插值
- 原理:使用sinc函數進行插值
- 特點:最高質量,最慢速度
- 適用場景:專業圖像處理
resized = cv2.resize(img, (800, 600), interpolation=cv2.INTER_LANCZOS4)
5.完整示例
import cv2# 讀圖
cat = cv2.imread('../images/1.jpg')
w = cat.shape[1]
h = cat.shape[0]# 旋轉
# 獲取旋轉矩陣
M = cv2.getRotationMatrix2D((w//2, h//2), 45, 0.5)# 應用仿射變換函數
dst = cv2.warpAffine(cat, M, (w, h))# 最近鄰插值 flags=cv2.INTER_NEAREST
dst1 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_NEAREST)# 雙線性插值 單線性插值 插兩次:水平和垂直
dst2 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_LINEAR)# 像素區域插值 縮小:均值濾波 放大:整數 最近鄰 不是整數 雙線性 4點 2 x 2
dst3 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_AREA)# 雙三次插值 16個 4 x 4 w_i w_j
dst4 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_CUBIC)# Lanczos插值 64個 8 x 8
dst5 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_LANCZOS4)# 顯示
cv2.imshow('dst', dst)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.imshow('dst3', dst3)
cv2.imshow('dst4', dst4)
cv2.imshow('dst5', dst5)cv2.waitKey(0)
cv2.destroyAllWindows()
二、邊緣填充:處理邊界問題的關鍵
當進行卷積操作時,圖像邊界需要特殊處理,OpenCV提供多種填充方式。
1. 填充方法對比
方法 | 原理 | 效果示例 |
---|---|---|
BORDER_REPLICATE | 復制邊界像素值 | `aaaa |
BORDER_REFLECT | 鏡像反射邊界 | `fedc |
BORDER_REFLECT_101 | 反射不含邊界 | `gfed |
BORDER_CONSTANT | 用常數填充 | `0000 |
BORDER_WRAP | 循環填充 | `cdef |
2. 完整示例
import cv2# 讀圖
cat = cv2.imread('../images/1.jpg')
w = cat.shape[1]
h = cat.shape[0]# 旋轉
# 獲取旋轉矩陣
M = cv2.getRotationMatrix2D((w//2, h//2), 45, 0.5)# 應用仿射變換函數
dst = cv2.warpAffine(cat, M, (w, h))# 最近鄰插值 邊界復制
dst1 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_NEAREST,borderMode=cv2.BORDER_REPLICATE)# 雙線性插值 邊界反射
dst2 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_LINEAR,borderMode=cv2.BORDER_REFLECT)# 像素區域插值 邊界反射101
dst3 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_AREA,borderMode=cv2.BORDER_REFLECT_101)# 雙三次插值 邊界常數
dst4 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_CUBIC,borderMode=cv2.BORDER_CONSTANT,borderValue=(0,0,255))# Lanczos插值 邊界包裹
dst5 = cv2.warpAffine(cat, M, (w, h), flags=cv2.INTER_LANCZOS4,borderMode=cv2.BORDER_WRAP)# 顯示
cv2.imshow('dst', dst)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.imshow('dst3', dst3)
cv2.imshow('dst4', dst4)
cv2.imshow('dst5', dst5)cv2.waitKey(0)
cv2.destroyAllWindows()
三、圖像矯正:透視變換的應用
透視變換可以將傾斜視角的圖像矯正為正視角,常用于文檔掃描和車牌識別。
1. 透視變換四步法
- 識別源點:選擇圖像中的四個特征點
- 定義目標點:確定矯正后的位置
- 計算變換矩陣:使用
getPerspectiveTransform
- 應用變換:使用
warpPerspective
2. 完整示例
import cv2
import numpy as np# 讀圖
card = cv2.imread('../images/3.png')
shape = card.shape# 獲取透視變換矩陣
# 原圖中卡片的四個角點:左上、右上、左下、右下
# [[178, 100], [487, 134], [124, 300], [490, 350]]
pt1 =np.float32([[178, 100], [487, 134], [124, 300], [490, 350]])
pt2 =np.float32([[0,0],[shape[1],0],[0,shape[0]],[shape[1],shape[1]]])M = cv2.getPerspectiveTransform(pt1,pt2)# 透視變換
dst = cv2.warpPerspective(card,M,(shape[1],shape[0]),cv2.INTER_LINEAR,borderMode=cv2.BORDER_REFLECT)cv2.imshow('card',card)
cv2.imshow('dst',dst)cv2.waitKey(0)
cv2.destroyAllWindows()
四、圖像掩膜:精確控制處理區域
掩膜是二值化圖像,用于指定哪些區域需要處理或保留,以實現特定任務的目標。
掩膜操作三部曲
- 創建掩膜:手動繪制或通過閾值處理生成
- 應用掩膜:使用按位與操作保留目標區域
- 顏色替換:修改特定區域顏色
第一種:
# 創建圓形掩膜
mask = np.zeros(img.shape[:2], dtype=np.uint8)
cv2.circle(mask, (200, 200), 100, 255, -1)# 應用掩膜
masked_img = cv2.bitwise_and(img, img, mask=mask)# 顏色替換
img[mask == 255] = (0, 255, 0) # 將掩膜區域變為綠色
第兩種(完整):
cv2.inRange用于進行多通道圖像(尤其是彩色圖像)的閾值操作。
import cv2
import numpy as np# 讀圖
demo = cv2.imread('../images/demo.png')
demo = cv2.resize(demo,(500,500))# 顏色空間轉換 轉 HSV
demo_hsv = cv2.cvtColor(demo,cv2.COLOR_BGR2HSV)# 定義顏色范圍
low = np.array([0,43,46])
high = np.array([10,255,255])# 創建掩膜 cv2.inRange(img,low,high) 傳 hsv 顏色空間下的圖像 二維
mask = cv2.inRange(demo_hsv,low,high)# 顏色提取 cv2.bitwise_and(src1,src2[,mask]) 傳的是原圖
dst = cv2.bitwise_and(demo,demo,mask=mask)cv2.imshow('demo',demo)
cv2.imshow('mask',mask)
cv2.imshow('dst',dst)cv2.waitKey(0)
cv2.destroyAllWindows()
含顏色替換操作示例:
import cv2
import numpy as np# 讀圖
demo = cv2.imread('../images/demo.png')
demo = cv2.resize(demo,(500,500))# 顏色空間轉換 轉 HSV
demo_hsv = cv2.cvtColor(demo,cv2.COLOR_BGR2HSV)# 定義顏色范圍
low = np.array([0,43,46])
high = np.array([10,255,255])# 創建掩膜 cv2.inRange(img,low,high) 傳 hsv 顏色空間下的圖像 二維
mask = cv2.inRange(demo_hsv,low,high)# 顏色替換 布爾索引
arr = (mask == 255)
# print(arr)demo[arr] = (0, 255, 0)
# demo[mask == 255] = (0, 255, 0)cv2.imshow('demo',demo)cv2.waitKey(0)
cv2.destroyAllWindows()
五、ROI切割:聚焦關鍵區域
ROI(Region of Interest)是圖像中需要特別關注的區域。
ROI操作技巧
# 直接選擇矩形區域
roi = img[100:300, 200:400] # y1:y2, x1:x2# 在原始圖像上繪制ROI框
cv2.rectangle(img, (200, 100), (400, 300), (0, 255, 0), 2)# 處理ROI區域(例如轉換為灰度)
roi_gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
六、圖像添加水印:版權保護技術
水印技術可分為可見水印和不可見水印。
1. 三步驟:
- 模板輸入:使用模板輸入所輸入的圖片其實是作為要添加的水印,有了水印的彩色圖之后,我們需要用它來制作一個掩模,這就用到了灰度化和二值化,即先灰度化后二值化,這就得到了帶有水印圖案的掩模。
- 與運算:有了模板的掩膜后(也就是二值化圖),在要添加水印的圖像中,根據掩膜的大小切割出一個ROI區域,也就是要添加水印的區域,讓其與模板的掩膜進行與運算。與運算的過程中,只要有黑色像素,那么得到的結果圖中的對應位置也會是黑色像素。由于模板的掩膜中目標物體的像素值為黑色,所以經過與運算后,就會在ROI區域中得到模板圖的形狀。
- 圖像融合:將模板的形狀添加到水印區域之后,就可以將該圖像與原始的模板圖進行圖像融合。該組件的目的就是將圖像對應的數組中的對應元素進行相加(一定要注意這里的兩個數組是規格相同的,也就是說要么都是灰度圖,要么都是彩圖),其過程如下圖所示。
2. 可見水印實現
# 讀取水印圖片
watermark = cv2.imread("watermark.png", cv2.IMREAD_UNCHANGED)# 調整水印大小
watermark = cv2.resize(watermark, (200, 100))# 提取alpha通道
watermark_img = watermark[:, :, :3]
alpha = watermark[:, :, 3] / 255.0# 創建疊加區域
roi = img[10:110, 10:210]# 混合圖像
for c in range(3):roi[:, :, c] = (1 - alpha) * roi[:, :, c] + alpha * watermark_img[:, :, c]
3. 完整示例:
import cv2# 讀圖
bg = cv2.imread('../images/bg.png')
logo = cv2.imread('../images/logohq.png')
h, w = logo.shape[0:2] # h, w
roi = bg[0:h, 0:w]# 轉為灰度圖
gray = cv2.cvtColor(logo, cv2.COLOR_BGR2GRAY)# 二值化
# 黑 logo 白底(閾值法 小于閾值的變黑,大于閾值的設maxval/變白) 與上背景
_, mask1 = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
bg1 = cv2.bitwise_and(roi, roi, mask=mask1)# 白 logo黑底(反閾值法 大于閾值變黑,小于閾值的設maxval/變白) 與上 logo
_, mask2 = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
bg2 = cv2.bitwise_and(logo, logo, mask=mask2)# 融合 兩個結果相加
roi[:] = cv2.add(bg1,bg2) # 直接對 roi 修改,就能實現原背景改變,因為切片出來的 roi和原背景變量指向內存同一塊地址
# dst = cv2.add(bg1,bg2)
# roi[:] = dstcv2.imshow('mask1', mask1)
cv2.imshow('bg1', bg1)
cv2.imshow('mask2', mask2)
cv2.imshow('bg2', bg2)
cv2.imshow('roi', roi)
cv2.imshow('bg', bg)cv2.waitKey(0)
cv2.destroyAllWindows()
七、圖像噪點消除:提升圖像質量
噪聲是圖像中隨機出現的像素值變化,常見濾波方法如下:
1. 均值濾波
- 原理:取鄰域內像素的平均值
- 效果:消除高斯噪聲,但模糊邊緣
blur = cv2.blur(img, (5, 5))
2. 高斯濾波
- 原理:加權平均,中心點權重最高
- 效果:有效消除高斯噪聲
gaussian = cv2.GaussianBlur(img, (5, 5), 0)
3. 中值濾波
- 原理:取鄰域內像素的中值
- 效果:消除椒鹽噪聲,保留邊緣
median = cv2.medianBlur(img, 5)
4. 雙邊濾波
- 原理:同時考慮空間距離和像素值差異
- 效果:保留邊緣同時減少噪聲
bilateral = cv2.bilateralFilter(img, 9, 75, 75)
5. 濾波器選擇指南
噪聲類型 | 推薦濾波器 | 參數示例 |
---|---|---|
高斯噪聲 | 高斯濾波 | (5,5), σ=0 |
椒鹽噪聲 | 中值濾波 | 內核大小=5 |
邊緣保護 | 雙邊濾波 | d=9, σColor=75, σSpace=75 |
6. 完整示例:
import cv2# 讀圖
lvbo2 = cv2.imread('../images/lvbo2.png')
lvbo3 = cv2.imread('../images/lvbo3.png')# 均值濾波
dst1 = cv2.blur(lvbo2,(3,3))# 方框濾波 底層使用飽和運算 最大255 及白的很多
dst2 = cv2.boxFilter(lvbo2,-1,(3,3),normalize=False)# 高斯濾波
dst3 = cv2.GaussianBlur(lvbo2,(3,3),1)# 中值濾波 非線性(邏輯操作)
dst4 = cv2.medianBlur(lvbo3,3)# 雙邊濾波
dst5 = cv2.bilateralFilter(lvbo2,7,75,75)cv2.imshow('lvbo2',lvbo2)
# cv2.imshow('lvbo3',lvbo3)
cv2.imshow('dst1',dst1)
# cv2.imshow('dst2',dst2)
# cv2.imshow('dst3',dst3)
# cv2.imshow('dst4',dst4)
cv2.imshow('dst5',dst5)cv2.waitKey(0)
cv2.destroyAllWindows()
圖像預處理綜合應用
def advanced_preprocessing(image_path):# 1. 讀取圖像img = cv2.imread(image_path)# 2. 透視變換矯正src_pts = np.float32([[58, 72], [375, 65], [35, 392], [400, 398]])dst_pts = np.float32([[0, 0], [500, 0], [0, 600], [500, 600]])M = cv2.getPerspectiveTransform(src_pts, dst_pts)corrected = cv2.warpPerspective(img, M, (500, 600))# 3. 降噪處理denoised = cv2.bilateralFilter(corrected, 9, 75, 75)# 4. 創建ROI區域roi = denoised[100:400, 150:350]# 5. 添加水印watermark = cv2.imread("watermark.png", cv2.IMREAD_UNCHANGED)watermark = cv2.resize(watermark, (100, 50))roi_watermark = denoised[10:60, 10:110]alpha = watermark[:, :, 3] / 255.0for c in range(3):roi_watermark[:, :, c] = alpha * watermark[:, :, c] + (1 - alpha) * roi_watermark[:, :, c]return denoised
總結
- 插值選擇:實時系統用最近鄰,質量優先用雙三次
- 邊緣填充:反射101(BORDER_REFLECT_101)效果最自然
- 透視變換:確保四個點形成凸四邊形
- 掩膜應用:結合閾值法創建精確掩膜
- 噪聲消除:
- 椒鹽噪聲 → 中值濾波
- 高斯噪聲 → 高斯濾波
- 邊緣保護 → 雙邊濾波