OpenCV 圖像處理核心技術 (第二部分)

歡迎來到 OpenCV 圖像處理的第二部分!在第一部分,我們學習了如何加載、顯示、保存圖像以及訪問像素等基礎知識。現在,我們將深入探索如何利用 OpenCV 提供的強大工具來修改和分析圖像。

圖像處理是計算機視覺領域的基石。通過對圖像進行各種操作,我們可以改善圖像質量、提取重要特征、準備圖像用于更復雜的任務(如目標識別或圖像分割)。

本部分將涵蓋以下關鍵主題:

  1. 圖像增強與濾波
    • 亮度與對比度調整
    • 圖像平滑(均值濾波、高斯濾波)
    • 圖像銳化(拉普拉斯算子)
  2. 圖像形態學操作
    • 腐蝕與膨脹
    • 開運算與閉運算
    • 形態學梯度
  3. 邊緣檢測
    • Sobel 算子
    • Canny 邊緣檢測
  4. 實戰:檢測圖像中的邊緣

讓我們開始吧!

1. 圖像增強與濾波

圖像增強旨在改善圖像的視覺效果或為后續處理提供更好的輸入。濾波則是通過對圖像像素及其鄰域像素進行計算,來達到平滑、銳化或提取特征的目的。

1.1 亮度與對比度調整

調整圖像的亮度和對比度是最常見的圖像增強操作之一。

  • 亮度 (Brightness): 控制圖像的整體明暗程度。增加亮度就像給圖像"加光"。
  • 對比度 (Contrast): 控制圖像中明暗區域之間的差異程度。增加對比度會使亮區更亮,暗區更暗,圖像看起來更"鮮明"。

數學上,簡單的亮度和對比度調整可以通過線性變換實現:

OpenCV 提供了 cv2.convertScaleAbs() 函數來實現這個線性變換,并且會自動處理像素值超出 [0, 255] 范圍的情況(截斷到 0 或 255)。

Python

import cv2
import numpy as np# --- 練習 1.1: 調整圖像亮度與對比度 ---# 1. 加載圖像
# 請替換成你自己的圖片路徑
image_path = 'your_image.jpg'
try:image = cv2.imread(image_path)if image is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")
except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")# 使用一個虛擬圖片代替,以便代碼可以運行(實際操作中請替換)image = np.zeros((300, 500, 3), dtype=np.uint8)cv2.putText(image, "Image not found", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)cv2.putText(image, "Please replace 'your_image.jpg'", (50, 200), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)print(f"原始圖像尺寸: {image.shape}")# 2. 定義調整參數
alpha = 1.5  # 對比度控制 (增益),大于1增強對比度
beta = 30    # 亮度控制 (偏置),正數增加亮度# 3. 應用線性變換進行亮度與對比度調整
# 注意: cv2.convertScaleAbs 會自動將結果轉換為 uint8 并取絕對值 (雖然對于亮度/對比度調整,結果通常是非負的)
adjusted_image = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)# 4. 顯示原始圖像和調整后的圖像
cv2.imshow('Original Image', image)
cv2.imshow('Adjusted Image (alpha=1.5, beta=30)', adjusted_image)# 5. 嘗試不同的參數組合
alpha_low_contrast = 0.7
beta_dark = -50
adjusted_low_contrast_dark = cv2.convertScaleAbs(image, alpha=alpha_low_contrast, beta=beta_dark)
cv2.imshow('Adjusted Image (alpha=0.7, beta=-50)', adjusted_low_contrast_dark)# 等待按鍵,然后關閉窗口
cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 1.1 完成 ---")

練習提示:

  • 嘗試不同的 alphabeta 值,觀察圖像的變化。
  • alpha 可以是小數(例如 0.5 會降低對比度)。
  • beta 可以是負數(例如 -50 會降低亮度)。
1.2 圖像平滑 (濾波)

圖像平滑(也稱為模糊)是一種常用的濾波技術,主要用于減少圖像中的噪聲或在進行邊緣檢測等操作前平滑圖像。平滑的原理是使用一個核(Kernel)掩膜(Mask)與圖像進行卷積。核是一個小矩陣,它在圖像上滑動,在每個位置,將核的元素與對應的圖像像素值相乘,然后將結果求和,得到中心像素的新值。

均值濾波 (Mean Filter)

Python

import cv2
import numpy as np# --- 練習 1.2.1: 均值濾波 ---# 1. 加載圖像
image_path = 'your_image.jpg'
try:image = cv2.imread(image_path)if image is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")
except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")image = np.zeros((300, 500, 3), dtype=np.uint8)cv2.putText(image, "Image not found", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)cv2.putText(image, "Please replace 'your_image.jpg'", (50, 200), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)# 2. 定義核的大小 (必須是正奇數,如 (3, 3), (5, 5) 等)
ksize_mean = (5, 5)# 3. 應用均值濾波
# cv2.blur() 函數用于進行均值濾波
# 參數: src (輸入圖像), ksize (核大小)
mean_blurred_image = cv2.blur(image, ksize_mean)# 4. 顯示原始圖像和均值濾波后的圖像
cv2.imshow('Original Image', image)
cv2.imshow(f'Mean Blurred Image (ksize={ksize_mean})', mean_blurred_image)# 5. 嘗試不同的核大小
ksize_mean_large = (15, 15)
mean_blurred_image_large = cv2.blur(image, ksize_mean_large)
cv2.imshow(f'Mean Blurred Image (ksize={ksize_mean_large})', mean_blurred_image_large)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 1.2.1 完成 ---")

練習提示:

  • 嘗試使用不同大小的核。核越大,平滑效果越明顯,但圖像細節丟失越多。
  • 均值濾波對去除“椒鹽噪聲”等隨機噪聲有一定效果。
高斯濾波 (Gaussian Filter)

高斯濾波是一種更常用的平滑濾波器。它使用一個符合高斯分布(正態分布)的核。與均值濾波不同,高斯核中的元素不是相等的,而是距離核中心越近的元素權重越大,距離越遠權重越小。這使得高斯濾波在平滑圖像的同時,能夠更好地保留邊緣信息。

高斯核的形狀由標準差 σ 控制。σ 越大,核越“胖”,平滑范圍越大。

Python

import cv2
import numpy as np# --- 練習 1.2.2: 高斯濾波 ---# 1. 加載圖像
image_path = 'your_image.jpg'
try:image = cv2.imread(image_path)if image is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")
except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")image = np.zeros((300, 500, 3), dtype=np.uint8)cv2.putText(image, "Image not found", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)cv2.putText(image, "Please replace 'your_image.jpg'", (50, 200), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)# 2. 定義核的大小 (必須是正奇數) 和 高斯標準差 (sigmaX, sigmaY)
# sigmaX 和 sigmaY 分別表示高斯核在X和Y方向上的標準差
# 如果 sigmaY 為 0,則 sigmaY = sigmaX
# 如果 sigmaX 和 sigmaY 都為 0,則它們由核大小計算得出
ksize_gaussian = (5, 5)
sigma_x = 0 # 通常設置為 0,讓 OpenCV 根據核大小自動計算# 3. 應用高斯濾波
# cv2.GaussianBlur() 函數用于進行高斯濾波
# 參數: src (輸入圖像), ksize (核大小), sigmaX, sigmaY (標準差), borderType (邊界處理方式,通常不需要指定)
gaussian_blurred_image = cv2.GaussianBlur(image, ksize_gaussian, sigma_x)# 4. 顯示原始圖像和高斯濾波后的圖像
cv2.imshow('Original Image', image)
cv2.imshow(f'Gaussian Blurred Image (ksize={ksize_gaussian}, sigmaX={sigma_x})', gaussian_blurred_image)# 5. 嘗試不同的核大小和 sigmaX 值
ksize_gaussian_large = (15, 15)
sigma_x_large = 5 # 明確指定較大的標準差
gaussian_blurred_image_large = cv2.GaussianBlur(image, ksize_gaussian_large, sigma_x_large)
cv2.imshow(f'Gaussian Blurred Image (ksize={ksize_gaussian_large}, sigmaX={sigma_x_large})', gaussian_blurred_image_large)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 1.2.2 完成 ---")

練習提示:

  • 比較均值濾波和高斯濾波的效果,注意它們在邊緣處的差異。
  • 嘗試不同的 ksizesigmaX 值。sigmaX 對平滑效果影響很大。當 sigmaX 較大時,即使核較小,平滑效果也很明顯。
1.3 圖像銳化

與平滑相反,圖像銳化旨在增強圖像的邊緣和細節,使圖像看起來更清晰。銳化通常通過突出圖像中的變化區域(如邊緣)來實現。

一種常見的銳化方法是使用拉普拉斯算子 (Laplacian Operator)。拉普拉斯算子是二階微分算子,它檢測圖像中灰度變化率最大的區域,這些區域通常對應于邊緣。對圖像應用拉普拉斯算子后得到的是邊緣信息。

要實現圖像銳化,可以將原始圖像與拉普拉斯算子檢測到的邊緣信息進行結合。一個簡單的銳化核是:

[ 0,  1,  0 ]
[ 1, -4,  1 ]
[ 0,  1,  0 ]

[-1, -1, -1]
[-1,  8, -1]
[-1, -1, -1]

使用這些核進行卷積可以直接得到銳化效果。

OpenCV 提供了 cv2.Laplacian() 函數來計算拉普拉斯算子,或者你可以使用 cv2.filter2D() 函數和自定義的銳化核進行卷積。對于初學者,使用 filter2D 配合銳化核可能更直觀。

Python

import cv2
import numpy as np# --- 練習 1.3: 圖像銳化 ---# 1. 加載圖像 (灰度圖通常更適合濾波操作)
# 如果你的圖片是彩色圖,可以先轉換為灰度圖
image_path = 'your_image.jpg'
try:image_color = cv2.imread(image_path)if image_color is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")image = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY) # 轉換為灰度圖
except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")# 使用一個虛擬灰度圖片代替image = np.zeros((300, 500), dtype=np.uint8)cv2.putText(image, "Image not found", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)cv2.putText(image, "Please replace 'your_image.jpg'", (50, 200), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)print(f"灰度圖像尺寸: {image.shape}")# 2. 定義銳化核 (一個常用的拉普拉斯銳化核)
sharpening_kernel = np.array([[0, -1, 0],[-1, 5, -1],[0, -1, 0]
], dtype=np.float32) # 使用 float32 類型# 3. 應用自定義核進行濾波 (銳化)
# cv2.filter2D() 函數使用自定義核對圖像進行卷積
# 參數: src (輸入圖像), ddepth (輸出圖像深度,-1 表示與輸入圖像相同), kernel (核)
sharpened_image = cv2.filter2D(image, -1, sharpening_kernel)# 4. 顯示原始灰度圖像和銳化后的圖像
cv2.imshow('Original Grayscale Image', image)
cv2.imshow('Sharpened Image (using filter2D)', sharpened_image)# 5. 使用 cv2.Laplacian 函數(僅獲取邊緣信息,不是直接銳化)
# 需要指定輸出深度,通常使用 cv2.CV_64F 以避免溢出
laplacian = cv2.Laplacian(image, cv2.CV_64F)
# 將結果轉換回 uint8
# 注意: 拉普拉斯結果可能有負值,需要取絕對值并縮放到 0-255 范圍
laplacian_abs = cv2.convertScaleAbs(laplacian)
cv2.imshow('Laplacian Edge Information', laplacian_abs)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 1.3 完成 ---")

練習提示:

  • cv2.filter2D() 非常靈活,你可以嘗試定義其他核(例如,一個簡單的邊緣檢測核 np.array([[-1, -1, -1], [-1, 8, -1], [-1, -1, -1]]))來觀察效果。
  • cv2.Laplacian() 直接計算的是圖像的二階導數,它更常用于邊緣檢測,而不是直接產生銳化后的圖像(雖然可以通過與原圖疊加實現銳化)。理解 filter2DLaplacian 的區別。
  • 注意 filter2Dddepth 參數,-1 表示輸出與輸入深度相同,如果核導致結果超出 0-255,可能會發生截斷。對于一些濾波操作,可能需要將輸入轉換為更高的數據類型(如 float32)進行計算,然后再轉換回 uint8。但在上面的銳化例子中,[-1, 5, -1] 核通常不會導致嚴重溢出,所以直接用 -1 輸出深度是可行的。

2. 圖像形態學操作

形態學操作是基于圖像中特定形狀(稱為結構元素)的幾何操作。它們主要應用于二值圖像(只有黑白兩種像素值,0 或 255),但也適用于灰度圖像。形態學操作在處理圖像中的對象形狀、大小、連接等方面非常有用,例如去除噪聲、分割獨立元素、連接相鄰元素、查找圖像中的孔洞等。

形態學操作的關鍵在于結構元素。它是一個小矩陣,定義了在操作過程中如何探測或影響鄰域像素。OpenCV 提供了 cv2.getStructuringElement() 函數來創建不同形狀和大小的結構元素。

Python

import cv2
import numpy as np# --- 練習 2.0: 創建結構元素 ---# 1. 定義結構元素的形狀和大小
# 形狀: cv2.MORPH_RECT (矩形), cv2.MORPH_ELLIPSE (橢圓形), cv2.MORPH_CROSS (交叉形)
shape = cv2.MORPH_RECT
ksize = (5, 5) # 大小 (寬度, 高度)# 2. 創建結構元素
kernel_rect = cv2.getStructuringElement(shape, ksize)
print(f"矩形結構元素 ({ksize}):\n{kernel_rect}")shape_ellipse = cv2.MORPH_ELLIPSE
ksize_ellipse = (7, 7)
kernel_ellipse = cv2.getStructuringElement(shape_ellipse, ksize_ellipse)
print(f"\n橢圓形結構元素 ({ksize_ellipse}):\n{kernel_ellipse}")shape_cross = cv2.MORPH_CROSS
ksize_cross = (5, 5)
kernel_cross = cv2.getStructuringElement(shape_cross, ksize_cross)
print(f"\n交叉形結構元素 ({ksize_cross}):\n{kernel_cross}")# 注意:形態學操作通常用于二值圖像,所以我們創建一個簡單的二值圖像作為例子
# 創建一個包含白色方塊的黑色圖像
binary_image = np.zeros((100, 100), dtype=np.uint8)
binary_image[20:80, 20:80] = 255# 添加一些小的噪聲點 (白色點)
binary_image[10, 10] = 255
binary_image[90, 90] = 255
binary_image[10, 90] = 255
binary_image[90, 10] = 255# 添加一些小的孔洞 (黑色點)
binary_image[45:55, 45:55] = 0cv2.imshow('Original Binary Image', binary_image)
cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 2.0 完成 ---")

練習提示:

  • 理解不同形狀和大小的結構元素。它們定義了形態學操作的“感受野”。
  • 注意形態學操作通常在二值圖像上效果最明顯。你需要先將圖像轉換為二值圖像(例如使用 cv2.threshold())。
2.1 腐蝕 (Erosion)

腐蝕操作會“縮小”二值圖像中的前景物體(白色區域)。它的工作原理是:對于輸出圖像的每個像素,如果其對應的輸入圖像像素的鄰域中,完全被結構元素覆蓋的區域都是前景像素,則輸出像素為前景(白色);否則為背景(黑色)。

簡單來說,如果結構元素在某個位置包含任何背景像素,并且其中心對應輸入圖像的一個前景像素,那么這個前景像素在輸出中就會變成背景。這會導致前景物體的邊緣被“腐蝕”掉。腐蝕可以用來去除圖像中的小噪聲點。

Python

import cv2
import numpy as np# --- 練習 2.1: 腐蝕操作 ---# 1. 加載或創建二值圖像 (使用上面創建的帶噪聲和孔洞的二值圖像)
binary_image = np.zeros((150, 150), dtype=np.uint8)
binary_image[30:120, 30:120] = 255 # 大方塊
binary_image[10, 10] = 255 # 噪聲點
binary_image[10, 130] = 255
binary_image[130, 10] = 255
binary_image[130, 130] = 255
binary_image[60:90, 60:90] = 0 # 孔洞# 2. 創建結構元素
kernel_erode = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))# 3. 應用腐蝕操作
# cv2.erode() 函數用于進行腐蝕
# 參數: src (輸入圖像), kernel (結構元素), iterations (迭代次數,可選,默認為1)
eroded_image = cv2.erode(binary_image, kernel_erode, iterations=1)# 4. 顯示原始二值圖像和腐蝕后的圖像
cv2.imshow('Original Binary Image', binary_image)
cv2.imshow('Eroded Image (iterations=1)', eroded_image)# 5. 嘗試多次迭代腐蝕
eroded_image_3_iter = cv2.erode(binary_image, kernel_erode, iterations=3)
cv2.imshow('Eroded Image (iterations=3)', eroded_image_3_iter)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 2.1 完成 ---")

練習提示:

  • 觀察小的噪聲點是如何被腐蝕掉的。
  • 觀察大的白色方塊的邊緣是如何向內收縮的。
  • 嘗試增加 iterations 參數的值,觀察腐蝕效果如何增強。
2.2 膨脹 (Dilation)

膨脹操作會“放大”二值圖像中的前景物體(白色區域)。它的工作原理是:對于輸出圖像的每個像素,如果其對應的輸入圖像像素的鄰域中,至少有一個像素被結構元素覆蓋的區域是前景像素,則輸出像素為前景(白色);否則為背景(黑色)。

簡單來說,如果結構元素的中心對應輸入圖像的一個背景像素,但該結構元素覆蓋的區域內包含任何前景像素,那么這個背景像素在輸出中就會變成前景。這會導致前景物體的邊緣向外擴展。膨脹可以用來填補物體內部的小孔洞或連接相鄰的物體。

Python

import cv2
import numpy as np# --- 練習 2.2: 膨脹操作 ---# 1. 加載或創建二值圖像 (使用上面創建的帶噪聲和孔洞的二值圖像)
binary_image = np.zeros((150, 150), dtype=np.uint8)
binary_image[30:120, 30:120] = 255 # 大方塊
binary_image[10, 10] = 255 # 噪聲點
binary_image[10, 130] = 255
binary_image[130, 10] = 255
binary_image[130, 130] = 255
binary_image[60:90, 60:90] = 0 # 孔洞# 2. 創建結構元素
kernel_dilate = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))# 3. 應用膨脹操作
# cv2.dilate() 函數用于進行膨脹
# 參數: src (輸入圖像), kernel (結構元素), iterations (迭代次數,可選,默認為1)
dilated_image = cv2.dilate(binary_image, kernel_dilate, iterations=1)# 4. 顯示原始二值圖像和膨脹后的圖像
cv2.imshow('Original Binary Image', binary_image)
cv2.imshow('Dilated Image (iterations=1)', dilated_image)# 5. 嘗試多次迭代膨脹
dilated_image_3_iter = cv2.dilate(binary_image, kernel_dilate, iterations=3)
cv2.imshow('Dilated Image (iterations=3)', dilated_image_3_iter)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 2.2 完成 ---")

練習提示:

  • 觀察小的孔洞是如何被膨脹操作填補的。
  • 觀察大的白色方塊的邊緣是如何向外擴展的。
  • 嘗試增加 iterations 參數的值,觀察膨脹效果如何增強。
2.3 開運算 (Opening)

開運算是先腐蝕后膨脹的操作。它常用于去除圖像中的小噪聲點(孤立的前景像素),同時基本保持較大物體的大小和形狀不變。先腐蝕可以去除小的噪聲點,再膨脹則可以恢復被腐蝕掉的較大物體。

Opening(Image)=Dilate(Erode(Image))

2.4 閉運算 (Closing)

閉運算是先膨脹后腐蝕的操作。它常用于填充物體內部的小孔洞或連接物體之間的狹窄間隙,同時基本保持物體的大小和形狀不變。先膨脹可以填補孔洞和連接間隙,再腐蝕則可以恢復因膨脹而稍有增大的物體大小。

Closing(Image)=Erode(Dilate(Image))

OpenCV 提供了 cv2.morphologyEx() 函數,它可以執行包括開運算、閉運算在內的多種形態學操作。

Python

import cv2
import numpy as np# --- 練習 2.3 & 2.4: 開運算與閉運算 ---# 1. 加載或創建二值圖像 (使用上面創建的帶噪聲和孔洞的二值圖像)
binary_image = np.zeros((150, 150), dtype=np.uint8)
binary_image[30:120, 30:120] = 255 # 大方塊
binary_image[10, 10] = 255 # 噪聲點
binary_image[10, 130] = 255
binary_image[130, 10] = 255
binary_image[130, 130] = 255
binary_image[60:90, 60:90] = 0 # 孔洞# 2. 創建結構元素
kernel_morph = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))# 3. 應用開運算
# cv2.morphologyEx() 函數用于執行多種形態學操作
# 參數: src (輸入圖像), op (操作類型), kernel (結構元素), iterations (迭代次數,可選)
# cv2.MORPH_OPEN 表示開運算
opening_image = cv2.morphologyEx(binary_image, cv2.MORPH_OPEN, kernel_morph)# 4. 應用閉運算
# cv2.MORPH_CLOSE 表示閉運算
closing_image = cv2.morphologyEx(binary_image, cv2.MORPH_CLOSE, kernel_morph)# 5. 顯示原始、開運算和閉運算后的圖像
cv2.imshow('Original Binary Image', binary_image)
cv2.imshow('Opening Operation', opening_image)
cv2.imshow('Closing Operation', closing_image)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 2.3 & 2.4 完成 ---")

練習提示:

  • 觀察開運算如何有效地去除了小的噪聲點,同時保留了較大的白色方塊。
  • 觀察閉運算如何填補了方塊內部的孔洞。
  • 嘗試使用不同大小和形狀的結構元素,以及不同的迭代次數,看看它們如何影響開閉運算的效果。
2.5 形態學梯度 (Morphological Gradient)

形態學梯度是膨脹圖像與腐蝕圖像之間的差值。它會突顯出物體的邊緣輪廓。

MorphologicalGradient=Dilate(Image)?Erode(Image)

這個操作的結果通常可以看作是物體邊界的“厚度”。

Python

import cv2
import numpy as np# --- 練習 2.5: 形態學梯度 ---# 1. 加載或創建二值圖像 (使用上面創建的二值圖像)
binary_image = np.zeros((150, 150), dtype=np.uint8)
binary_image[30:120, 30:120] = 255 # 大方塊# 2. 創建結構元素
kernel_grad = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))# 3. 應用形態學梯度
# cv2.morphologyEx() 函數, op=cv2.MORPH_GRADIENT 表示形態學梯度
gradient_image = cv2.morphologyEx(binary_image, cv2.MORPH_GRADIENT, kernel_grad)# 4. 顯示原始二值圖像和形態學梯度圖像
cv2.imshow('Original Binary Image', binary_image)
cv2.imshow('Morphological Gradient', gradient_image)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 2.5 完成 ---")

練習提示:

  • 形態學梯度結果是物體的邊界區域,其寬度取決于結構元素的大小。
  • 將其與后面的邊緣檢測結果進行比較,理解它們的不同用途。形態學梯度側重于根據物體形狀來提取邊界,而邊緣檢測側重于像素灰度變化。

3. 邊緣檢測

邊緣是圖像中像素強度發生顯著變化的區域。邊緣攜帶著圖像中物體形狀、邊界等重要信息,因此邊緣檢測是許多高級計算機視覺任務(如目標識別、圖像分割)的基礎步驟。

邊緣檢測通常涉及計算圖像的梯度。梯度表示圖像在某個方向上的變化率。在邊緣位置,梯度的大小通常較大。

3.1 Sobel 算子

OpenCV 提供了 cv2.Sobel() 函數來計算 Sobel 梯度。

Python

import cv2
import numpy as np# --- 練習 3.1: Sobel 算子邊緣檢測 ---# 1. 加載圖像并轉換為灰度圖 (邊緣檢測通常在灰度圖上進行)
image_path = 'your_image.jpg'
try:image_color = cv2.imread(image_path)if image_color is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")image = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)
except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")image = np.zeros((300, 500), dtype=np.uint8)cv2.putText(image, "Image not found", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)cv2.putText(image, "Please replace 'your_image.jpg'", (50, 200), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)# 2. 計算水平方向的 Sobel 梯度
# 參數: src (輸入圖像), ddepth (輸出圖像深度), dx (x方向導數階數), dy (y方向導數階數), ksize (Sobel核大小)
# 為了避免計算過程中出現負值截斷,通常將輸出深度設為 cv2.CV_64F
sobelx = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)# 3. 計算垂直方向的 Sobel 梯度
sobely = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)# 4. 計算梯度幅值 (這里使用近似值: |Gx| + |Gy|)
# 將結果轉換為 uint8,cv2.convertScaleAbs 會取絕對值并截斷到 0-255
sobelx_abs = cv2.convertScaleAbs(sobelx)
sobely_abs = cv2.convertScaleAbs(sobely)
sobel_combined = cv2.addWeighted(sobelx_abs, 0.5, sobely_abs, 0.5, 0) # 簡單相加作為合并# 也可以使用更精確的梯度幅值計算: sqrt(Gx^2 + Gy^2)
# combined_magnitude = np.sqrt(sobelx**2 + sobely**2)
# combined_magnitude = cv2.normalize(combined_magnitude, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U) # 歸一化到 0-255# 5. 顯示結果
cv2.imshow('Original Grayscale Image', image)
cv2.imshow('Sobel X', sobelx_abs)
cv2.imshow('Sobel Y', sobely_abs)
cv2.imshow('Sobel Combined (|Gx| + |Gy|)', sobel_combined)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 3.1 完成 ---")

練習提示:

  • 觀察 Sobel X 結果主要突出垂直方向的邊緣,Sobel Y 結果主要突出水平方向的邊緣。
  • 合并后的結果顯示了各個方向的邊緣。
  • 理解為什么需要使用 cv2.CV_64F 作為輸出深度,以及 cv2.convertScaleAbs() 的作用。
  • 嘗試不同的 ksize 值。

3.2 Canny 邊緣檢測

Canny 邊緣檢測是一種多階段的邊緣檢測算法,被廣泛認為是目前效果最好的邊緣檢測算法之一。它比 Sobel 算子更復雜,但能產生更細、更準確的邊緣。Canny 算法主要包括以下步驟:

  1. 噪聲抑制: 使用高斯濾波器平滑圖像,去除噪聲。
  2. 計算梯度: 計算圖像在水平和垂直方向的梯度(通常使用 Sobel 算子)。
  3. 非極大值抑制 (Non-maximum Suppression): 細化邊緣。在梯度方向上,只保留梯度局部最大值,抑制非最大值,使得邊緣變細。
  4. 雙閾值處理 (Double Thresholding): 定義兩個閾值:高閾值 (high_threshold) 和低閾值 (low_threshold)。
    • 梯度值高于 high_threshold 的點被確定為強邊緣
    • 梯度值低于 low_threshold 的點被排除。
    • 梯度值介于 low_thresholdhigh_threshold 之間的點被稱為弱邊緣,它們可能成為邊緣,也可能不是。
  5. 邊緣跟蹤 (Edge Tracking by Hysteresis): 連接邊緣。通過分析弱邊緣的鄰域,將與強邊緣相連的弱邊緣也視為邊緣。

OpenCV 提供了 cv2.Canny() 函數來執行 Canny 邊緣檢測。

Python

import cv2
import numpy as np# --- 練習 3.2: Canny 邊緣檢測 ---# 1. 加載圖像并轉換為灰度圖
image_path = 'your_image.jpg'
try:image_color = cv2.imread(image_path)if image_color is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")image = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)
except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")image = np.zeros((300, 500), dtype=np.uint8)cv2.putText(image, "Image not found", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)cv2.putText(image, "Please replace 'your_image.jpg'", (50, 200), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)# 2. 定義雙閾值
low_threshold = 50
high_threshold = 150 # 通常 high_threshold 是 low_threshold 的 2 到 3 倍# 3. 應用 Canny 邊緣檢測
# cv2.Canny() 函數執行 Canny 邊緣檢測
# 參數: src (8位輸入圖像), threshold1 (低閾值), threshold2 (高閾值),
#       apertureSize (Sobel算子大小,默認為3), L2gradient (是否使用更精確的L2范數計算梯度幅值,默認為False)
canny_edges = cv2.Canny(image, low_threshold, high_threshold)# 4. 顯示原始灰度圖像和 Canny 邊緣圖像
cv2.imshow('Original Grayscale Image', image)
cv2.imshow(f'Canny Edges (low={low_threshold}, high={high_threshold})', canny_edges)# 5. 嘗試不同的閾值組合
canny_edges_loose = cv2.Canny(image, 30, 100) # 較低閾值,可能檢測到更多弱邊緣
canny_edges_strict = cv2.Canny(image, 100, 200) # 較高閾值,只檢測到強邊緣
cv2.imshow(f'Canny Edges (low=30, high=100)', canny_edges_loose)
cv2.imshow(f'Canny Edges (low=100, high=200)', canny_edges_strict)cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 練習 3.2 完成 ---")

練習提示:

  • Canny 邊緣通常比 Sobel 邊緣更細。
  • 雙閾值的選擇對結果影響很大。較低的閾值組合會檢測到更多細節和噪聲,較高的閾值組合則只保留強邊緣。
  • 理解雙閾值處理和邊緣跟蹤的工作原理。

4. 實戰:檢測圖像中的邊緣

現在,我們將結合學到的知識,在一個實際圖像上進行邊緣檢測的實戰練習。我們將加載一張圖片,將其轉換為灰度圖,然后應用 Canny 邊緣檢測來找到物體的輪廓。

Python

import cv2
import numpy as np# --- 實戰練習: 使用 Canny 邊緣檢測 ---# 1. 加載你的圖像
# 請替換 'your_real_image.jpg' 為你的圖片路徑
image_path = 'your_real_image.jpg'
try:image_color = cv2.imread(image_path)if image_color is None:raise FileNotFoundError(f"圖片文件未找到: {image_path}")print(f"成功加載彩色圖像: {image_path}")
except FileNotFoundError as e:print(e)print("請確保你的圖片文件存在并位于正確路徑。")print("將使用一個內置的 OpenCV 示例圖像進行演示。")# 如果圖片加載失敗,使用 OpenCV 的內置圖像# 需要確保安裝了 opencv-contrib-python 或手動下載圖片# 例如,可以使用 './data/lena.jpg' 如果你有opencv_extra庫的data文件夾# 或者簡單創建一個模擬圖像image_color = np.zeros((400, 600, 3), dtype=np.uint8)cv2.putText(image_color, "Placeholder Image", (100, 200), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)# 2. 將圖像轉換為灰度圖
gray_image = cv2.cvtColor(image_color, cv2.COLOR_BGR2GRAY)# 3. 應用 Canny 邊緣檢測
# 可以嘗試不同的閾值來獲得最佳效果
low_threshold = 100
high_threshold = 200
edges = cv2.Canny(gray_image, low_threshold, high_threshold)# 4. 顯示原始圖像和邊緣檢測結果
cv2.imshow('Original Image', image_color)
cv2.imshow('Grayscale Image', gray_image)
cv2.imshow(f'Canny Edges (low={low_threshold}, high={high_threshold})', edges)# 5. 等待按鍵,然后關閉窗口
cv2.waitKey(0)
cv2.destroyAllWindows()print("\n--- 實戰練習 完成 ---")

實戰提示:

  • 更換不同的圖片文件進行測試。
  • 仔細調整 Canny 函數的 low_thresholdhigh_threshold 參數,觀察邊緣檢測結果的變化。這是獲得滿意邊緣檢測結果的關鍵步驟。
  • 對于一些有噪聲的圖片,你可能需要在 Canny 之前先進行高斯平滑,雖然 cv2.Canny() 函數內部已經包含了高斯平滑的步驟,但有時外部的預處理平滑可以改善結果。

總結

在這一部分,我們深入學習了 OpenCV 中的核心圖像處理技術,包括:

  • 圖像增強和濾波: 通過調整亮度對比度改善視覺效果,使用均值濾波和高斯濾波進行平滑,使用拉普拉斯算子或自定義核進行銳化。
  • 形態學操作: 利用結構元素進行腐蝕、膨脹、開運算、閉運算和形態學梯度等操作,常用于二值圖像處理和形狀分析。
  • 邊緣檢測: 學習了 Sobel 算子和更強大的 Canny 算法,用于檢測圖像中像素強度變化顯著的邊緣區域。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/903569.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/903569.shtml
英文地址,請注明出處:http://en.pswp.cn/news/903569.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【鴻蒙HarmonyOS】一文詳解華為的服務卡片

7.服務卡片 1.什么是卡片 Form Kit(卡片開發服務)提供一種界面展示形式,可以將應用的重要信息或操作前置到服務卡片(以下簡稱“卡片”),以達到服務直達、減少跳轉層級的體驗效果。卡片常用于嵌入到其他應…

探索目標檢測:邊界框與錨框的奧秘

筆者在2022年開始學習目標檢測的時候,對各種框的概念那是相當混淆,比如: 中文名詞:邊界框、錨框、真實框、預測框等英文名詞:BoundingBox、AnchorBox、Ground Truth等 同一個英文名詞比如BoundingBox翻譯成中文也有多個…

[原創](現代Delphi 12指南):[macOS 64bit App開發]: [1]如何使用原生NSAlert消息框 (runModal模式)

[作者] 常用網名: 豬頭三 出生日期: 1981.XX.XX 企鵝交流: 643439947 個人網站: 80x86匯編小站 編程生涯: 2001年~至今[共24年] 職業生涯: 22年 開發語言: C/C++、80x86ASM、Object Pascal、Objective-C、C#、R、Python、PHP、Perl、 開發工具: Visual Studio、Delphi、XCode、…

LangChain的向量RAG與MCP在意圖識別的主要區別

LangChain的向量RAG與MCP在意圖識別實現上的區別主要體現在技術路徑、流程設計以及應用場景三個方面: 1. 技術路徑差異 LangChain向量RAG 語義相似度驅動:通過用戶輸入的原始查詢與向量化知識庫的語義匹配實現意圖識別。例如,用戶提問"…

[特殊字符] Spring Cloud 微服務配置統一管理:基于 Nacos 的最佳實踐詳解

在微服務架構中,配置文件眾多、管理復雜是常見問題。本文將手把手演示如何將配置集中托管到 Nacos,并在 Spring Cloud Alibaba 項目中實現統一配置管理 自動刷新機制。 一、為什么要使用 Nacos 統一配置? 傳統方式下,每個服務都…

2025平航杯—團隊賽

2025平航杯團隊賽 計算機取證 分析起早王的計算機檢材,起早王的計算機插入過USB序列號是什么(格式:1)分析起早王的計算機檢材,起早王的便簽里有幾條待干(格式:1)分析起早王的計算機檢材,起早王的計算機默認瀏覽器是什…

JSON-RPC 2.0 規范中文版——無狀態輕量級遠程過程調用協議

前言 JSON-RPC是一種簡單、輕量且無狀態的遠程過程調用(RPC)協議,它允許不同系統通過標準化的數據格式進行通信。自2010年由JSON-RPC工作組發布以來,已成為眾多應用中實現遠程交互的基礎協議之一。本規范主要表達了JSON-RPC 2.0版…

微控制器編程 | ISP、IAP 與 ICP 的原理與比較

注:英文引文,機翻未校。 圖片清晰度限于引文原狀。 Introduction to Programming of Microcontroller: ISP, IAP and ICP 微控制器編程介紹:ISP、IAP 和 ICP Date: 30-11-2022 1. What is Microcontroller Programming 什么是微控制器編…

Allegro23.1新功能之新型via structure創建方法操作指導

Allegro23.1新功能之新型via structure創建方法操作指導 Allegro升級到了23.1后,支持創建新型via structure 通過直接定義參數來生成 具體操作如下 打開軟件,選擇 Allegro PCB Designer

IBM WebSphere Application Server 7.0/8.5.5證書過期問題處理

證書過期錯誤日志: [3/14/16 7:22:20:332 PDT] 0000007d WSX509TrustMa E CWPKI0312E: The certificate with subject DN CNMXSYSTEMS, OUctgNodeCell01, OUctgNode01, OIBM, CUS has an end date Mon Jan 11 11:17:18 PST 2016 which is no longer valid. [3/14/…

select,poll,epoll區別聯系

selsect,poll,epoll區別聯系 目錄 一、區別 二、聯系 select、poll 和 epoll 都是在 Linux 系統中用于實現 I/O 多路復用的機制,它們的主要目的是讓程序能夠同時監控多個文件描述符,以判斷是否有事件發生,從而提高 I/O 操作的效率。 一、區…

curl和wget的使用介紹

目錄 一、curl 和 wget 區別 二、wget的使用 2.1 參數說明 2.2 wget 使用示例 三、curl的使用 3.1 參數說明 3.2 curl使用示例 一、curl 和 wget 區別 wget 和 curl 都可以下載內容。它們都可以向互聯網發送請求并返回請求項,可以是文件、圖片或者是其他諸如…

日語學習-日語知識點小記-構建基礎-JLPT-N4階段(12): ておき ます

日語學習-日語知識點小記-構建基礎-JLPT-N4階段(12): ておき ます。 1、前言(1)情況說明(2)工程師的信仰 2、知識點(1)~ておき ます。(2&#x…

高質量水火焰無損音效包

今天設計寶藏給大家分享的是Cinematic Elements: Fire & Water音頻資源庫包含大量高質量的火焰和水的聲音效果。它具有非常強烈的個性特征和次世代的音效。火焰和水是兩個令人印象深刻而 interessing 的元素。它們的表現形式從微小無害到巨大毀滅性都有。因此,它們的聲音特…

畢業論文 | 傳統特征點提取算法與匹配算法對比分析

傳統特征點提取算法與匹配算法對比分析 一、特征點提取算法對比二、特征匹配算法對比三、核心算法原理與公式1. **Harris角點檢測**2. **SIFT描述子生成**3. **ORB描述子**四、完整Matlab代碼示例1. **Harris角點檢測與匹配**2. **SIFT特征匹配(需VLFeat庫)**3. **ORB特征匹配…

【網絡原理】從零開始深入理解HTTP的報文格式(二)

本篇博客給大家帶來的是網絡HTTP協議的知識點, 續上篇文章,接著介紹HTTP的報文格式. 🐎文章專欄: JavaEE初階 🚀若有問題 評論區見 ? 歡迎大家點贊 評論 收藏 分享 如果你不知道分享給誰,那就分享給薯條. 你們的支持是我不斷創作的動力 . 王子,公主請閱…

Microsoft .NET Framework 3.5 離線安裝包 下載

Microsoft. NET Framework 3.5 是支持生成和運行下一代應用程序和XML Web Services 的內部Windows 組件, 對 .NET Framework 2.0 和 3.0 中的許多新功能進行了更新和增補, 且附帶了 .NET Framework 2.0 Service Pack 1 和 .NET Framework 3.0 Service…

Flask + ajax上傳文件(三)--圖片上傳與OCR識別

本教程將詳細介紹如何使用Flask框架構建一個圖片上傳與文字識別(OCR)的Web應用。我們將使用EasyOCR作為OCR引擎,實現一個支持中文和英文識別的完整應用。 環境準備 首先,確保你已經安裝了Python 3.7+環境,然后安裝必要的依賴庫: pip install flask easyocr pillow werkz…

模型部署技巧(一)

模型部署技巧(一) 以下內容是參考CUDA與TensorRT模型部署內容第六章,主要針對圖像的前/后處理中的trick。 參考: 1.部署分類器-int8-calibration 2. cudnn安裝地址 3. 如何查找Tensor版本,與cuda 和 cudnn匹配 4. ti…

MySQL--數據引擎詳解

存儲引擎 MySQL體系結構 連接層: 主要接收客戶端的連接,然后完成一些鏈接的處理,以及認證授權的相關操作和安全方案,還要去檢查是否超過最大連接數等等,比如在連接MySQL服務器時需要輸入用戶名,密碼&#…