OpenCV——邊緣檢測

邊緣檢測

  • 一、邊緣檢測
  • 二、邊緣檢測算子
    • 2.1、Sobel算子
    • 2.2、Scharr算子
    • 2.3、Laplacian算子
  • 三、Canny邊緣檢測
    • 3.1、Canny邊緣檢測的步驟
    • 3.2、Canny算法的實現

一、邊緣檢測

邊緣是指圖像中像素的灰度值發生劇烈變化的區域:
在這里插入圖片描述
圖像中的邊緣主要有以下幾種成因:

  1. 表面不連續:兩個面的交界處會自然形成邊緣
  2. 深度不連續:主要是視覺因素
  3. 顏色不連續:兩種不同顏色的交匯處會形成邊緣
  4. 照明不連續:受光線影響形成的陰影會產生邊緣

邊緣檢測方法主要有以下兩大類:

  • 通過灰度值曲線一階導數的最大值來尋找邊緣,如Sobel算子、Scharr算子、Prewitt算子、roberts算子等
  • 通過灰度值曲線二階導數過零點來尋找邊緣,如Laplacian算子、Canny邊緣檢測等

二、邊緣檢測算子

2.1、Sobel算子

Sobel算子是通過一階導數的最大值進行邊緣檢測的,用Sobel算子進行邊緣檢測的步驟如下:

1. 將圖像與x方向的Sobel算子進行卷積。x方向的Sobel算子(尺寸3*3)如下:

-1 -2  1
-2  0  2
-1  0  1

2. 將圖像與y方向的Sobel算子進行卷積。y方向的Sobel算子(尺寸3*3)如下:

-1 -2 -10  0  01  2  1

3. 對圖像中的像素計算近似梯度幅度:
4. 統計極大值所在位置,獲得圖像的邊緣:

Sobel算子有著不同的尺寸和階次。如果想自己生成Sobel算子,則可以用getDerivKernels()函數實現:

//生成邊緣檢測用的濾波器。ksize=CV_SCHARR時生成的是Scharr濾波器,其余情況下生成的是Sobel濾波器
void Imgproc.getDerivKernels(Mat kx, Mat ky, int dx, int dy, int ksize, boolean normalize, int ktype)
  • kx:行濾波器的輸出矩陣,類型為ktype
  • ky:列濾波器的輸出矩陣,類型為ktype
  • dx:x方向上導數的階次
  • dy:y方向上導數的階次
  • ksize:生成濾波器的尺寸,可選參數有CV_SCHARR或者1、3、5、7
  • normalize:是否對濾波器系數進行歸一化。如果要濾波器的圖像的數據類型是浮點型,則一般需要進行歸一化;如果處理的是8位圖像,結果存儲在16位圖像中并希望保留所有的小數部分,則需要將normalize設為false
  • ktype:濾波器系數的類型,可以是CV_32F或CV_64F

該函數智能生成Sobel或Scharr算子。事實上,Sobel()函數和Scharr()函數內部調用的就是這個函數。

//用Sobel算子進行邊緣檢測
void Imgproc.Sobel(Mat src, Mat dst, int ddepth, int dx, int dy, int ksize)
  • src:輸入圖像
  • dst:輸出圖像,和src具有相同的尺寸和通道數
  • ddepth:輸出圖像的深度
  • dx:x方向求導的階數,通常只能是0或1。如果dx為0,則表示x方向上沒有求導
  • dy:y方向求導的階數,通常只能是0或1。如果dy為0,則表示y方向上沒有求導
  • ksize:Sobel算子的尺寸,只能是1、3、5或7

由于圖像的邊緣可能從高灰度值變為低灰度值,也可能從低灰度值變為高灰度值,所以用Sobel()函數計算的結果可能為正也可能為負。為了正確地顯示圖像,還需要用convertScaleAbs()函數將計算結果轉為絕對值:

//計算矩陣中數值的絕對值,并轉換為8位數據類型,可在此過程中進行縮放。對矩陣中每個數據和函數依次執行三項操作:縮放、求絕對值、轉換為CV_8U類型。如為多通道矩陣,函數則需對每個通道獨立進行處理
void Core.convertScaleAbs(Mat src, Mat dst, double alpha)
  • src:輸入矩陣
  • dst:輸出矩陣
  • alpha:縮放因子,可選
public class Sobel {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像灰度圖并顯示Mat src = Imgcodecs.imread("F:/IDEAworkspace/opencv/src/main/java/demo/butterfly.png", Imgcodecs.IMREAD_GRAYSCALE);HighGui.imshow("src", src);HighGui.waitKey(0);Mat grad = new Mat();Mat gx = new Mat();Mat gy = new Mat();Mat abs_gx = new Mat();Mat abs_gy = new Mat();//提取x方向邊緣Imgproc.Sobel(src, gx, -1, 1, 0);Core.convertScaleAbs(gx, abs_gx);//提取y方向邊緣Imgproc.Sobel(src, gy, -1, 0, 1);Core.convertScaleAbs(gy, abs_gy);//顯示x和y方向邊緣HighGui.imshow("Sobel-X", gx);HighGui.waitKey(0);HighGui.imshow("Sobel-Y", gy);HighGui.waitKey(0);//計算整副圖像的邊緣并顯示Core.addWeighted(abs_gx, 0.5, abs_gy, 0.5, 0, grad);HighGui.imshow("Sobel", grad);HighGui.waitKey(0);//直接計算整幅圖像邊緣Mat all = new Mat();Imgproc.Sobel(src, all, -1, 1, 1);HighGui.imshow("all", all);HighGui.waitKey(0);System.exit(0);}
}

原圖灰度圖:
在這里插入圖片描述

X方向邊緣:
在這里插入圖片描述

Y方向邊緣:
在這里插入圖片描述

整圖邊緣:
在這里插入圖片描述

一次計算整幅圖邊緣:
在這里插入圖片描述

2.2、Scharr算子

用Sobel算計進行邊緣檢測的效率較高,但它有一個缺點:當Sobel算子尺寸較小時精度比較低。如果Sobel濾波器的尺寸為33且梯度方向接近水平或垂直方向,則問題會變得愈發明顯。為了解決這個問題,OpenCV引進了Scharr算子。Scharr算子其實是一個特殊尺寸33的濾波器,在getDerivKernels()函數中將ksize設為CV_SCHARR時就是Scharr算子。當濾波器尺寸為3*3時,使用Scharr算子的速度與Sobel算子的速度一樣,但是準確度更高。

//x方向的Scharr算子
-3  0  3
-10 0 10
-1  0  3//y方向Scharr算子
-3  -10  -30    0   03   10   3

Scharr算子的濾波器尺寸只能是3*3,因為它的產生就是為了解決Sobel算子在該尺寸的問題

//用Scharr算子進行邊緣檢測
void Imgproc.Scharr(Mat src, Mat dst, int ddepth, int dx, int dy)
  • src:輸入圖像
  • dst:輸出圖像,和src具有相同的尺寸和通道數
  • ddepth:輸出圖像的深度
  • dx:x方向求導的階數,通常只能是0或1。如果dx為0,則表示x方向上沒有求導
  • dy:y方向求導的階數,通常只能是0或1。如果dy為0,則表示y方向上沒有求導
public class Scharr {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像灰度圖并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/butterfly.png", Imgcodecs.IMREAD_GRAYSCALE);HighGui.imshow("src", src);HighGui.waitKey(0);Mat grad = new Mat();Mat gx = new Mat();Mat gy = new Mat();Mat abs_gx = new Mat();Mat abs_gy = new Mat();//提取x方向邊緣Imgproc.Scharr(src, gx, -1, 1, 0);Core.convertScaleAbs(gx, abs_gx);//提取y方向邊緣Imgproc.Scharr(src, gy, -1, 0, 1);Core.convertScaleAbs(gy, abs_gy);//在屏幕上顯示X與Y方向邊緣HighGui.imshow("Scharr-X", gx);HighGui.waitKey(0);HighGui.imshow("Scharr-Y", gy);HighGui.waitKey(0);//計算整幅圖的邊緣并顯示Core.addWeighted(abs_gx, 0.5, abs_gy, 0.5, 0, grad);HighGui.imshow("Scharr", grad);HighGui.waitKey(0);System.exit(0);}
}

原圖:
在這里插入圖片描述

X方向邊緣:
在這里插入圖片描述

Y方向邊緣;
在這里插入圖片描述

整圖邊緣:

在這里插入圖片描述

2.3、Laplacian算子

Sobel算子和Scharr算子進行邊緣檢測的效率較高,但是它們具有方向性,需要先分別在x方向和y方向求導,然后根據兩個結果經計算后才可以得到圖像的邊緣。Laplacian算子則沒有方向性,不需要分方向計算。Laplacian算子和Sobel算子、Scharr算子的另一個區別是:Laplacian算子是一個基于二階導數的邊緣檢測算子。ksize=1時的Laplacian算子如下:

0  1  0
1 -4  1
0  1  1
//用Laplacian算子進行邊緣檢測
void Imgproc.Laplacian(Mat src, Mat dst, int ddepth, int ksize)
  • src:輸入圖像
  • dst:輸出圖像,和src具有相同的尺寸和通道數
  • ddepth:輸出圖像的深度
  • ksize:濾波器尺寸,必須為正奇數
public class Laplacian {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像灰度圖并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/butterfly.png", Imgcodecs.IMREAD_GRAYSCALE);HighGui.imshow("src", src);HighGui.waitKey(0);//高斯濾波后用Laplacian算子提取邊緣Mat dst = new Mat();Imgproc.GaussianBlur(src, dst, new Size(3, 3), 5);Imgproc.Laplacian(src, dst, 0, 3);Core.convertScaleAbs(dst, dst);//顯示HighGui.imshow("Laplacian", dst);HighGui.waitKey(0);System.exit(0);}
}

原圖:

在這里插入圖片描述

Laplacian算子邊緣:
在這里插入圖片描述

三、Canny邊緣檢測

Canny邊緣檢測算法源自John F.Canny于1986年發表的論文,論文中提出了以下3個評價最優邊緣檢測的標準:

  1. 準確檢測:算法能盡可能多地標識出圖像的實際邊緣,而遺漏或錯標的邊緣點應盡可能少
  2. 精確定位:檢測出的邊緣點的位置應與實際邊緣中心盡可能接近
  3. 單次響應:每個邊緣位置只能標識一次

3.1、Canny邊緣檢測的步驟

1. 平滑降噪:

在Canny邊緣檢測中,一般使用高斯平滑濾波器進行平滑降噪。高斯濾波器考慮了像素離濾波器中心的距離因素,距離越近權重越大,距離越遠權重越小。以下是一個5*5的高斯濾波器:

2  4  5  4  2
4  9 12  9  4
5 12 15 12  5    *   1/139
4  9 12  9  4
2  4  5  4  2

2. 梯度計算:
計算圖像中每像素的梯度幅值和方向,主要分以下兩步:

  1. 用Sobel算子分別檢測x方向和y方向的邊緣
  2. 計算梯度的幅值和方向。為了簡化起見,梯度方向取0°、45°、90°和135°這四個值
    在這里插入圖片描述

3. 非極大值抑制:

上一步得到的梯度圖像存在邊緣較粗及噪聲干擾等問題,此時可以用非極大值抑制來影除非邊緣的像素。Canny 中的非極大值抑制是沿著梯度方向對幅值進行比較,如圖所示。圖中A點位于邊緣附近,箭頭方向為梯度方向。選擇梯度方向上A點附近的像素B和C來檢驗A點的梯度值是否為極大值,若為極大值,則A保留為(候選)邊緣點,否則A點被抑制。由此可見,所謂非極大值抑制就是將不是極大值的 候選點予以剔除的過程。

在這里插入圖片描述

4. 雙閾值處理:

經過以上三步之后得到的邊緣質量已經很高了,但還是存在一些偽邊緣,因此Canny算法用雙閾值法對邊緣進行篩選。雙閿值法設置 minVal 和 maxVal 兩個閾值,當候選的邊緣點的梯度幅值高于 maxVal 時被認為是真正的邊界,當低于 minVal 時則被拋棄:如果介于兩者之間,則要看這個點是否與某個被確定為真正的邊界的像素相連,如果是,則認定為邊界點,否則該點被拋棄。

如圖所示,由于 A 點高于 maxVal,所以是真正的邊界點;由C點雖然低于 maxVal 但高于minVal 并且與 A 點相連,所以也是真正的邊界點,而 B 點介于 minVal 和 maxVal 之間,但沒有與真正的邊界點相連,因而被拋棄。為了達到較好的效果,選擇合適的 maxVal 和 minVal 值非常重要。

在這里插入圖片描述

3.2、Canny算法的實現

//用Canny算法進行邊緣檢測
void Imgproc.Canny(Mat image, Mat edges, double threshold1, double threshold2, int apertureSize)
  • image:8位輸入圖像
  • edges:輸出的邊緣圖像,必須是8位單通道圖像,尺寸與輸入圖像相同
  • threshold1:閾值1
  • threshold2:閾值2。threshold1和threshold2誰大誰小沒有規定,系統會自動選擇較大值為maxVal,較小值為minVal
  • apertureSize:Sobel算子的尺寸

Canny邊緣檢測的過程雖然較為復雜,但是經過OpenCV封裝后的Canny()函數卻非常簡單:

public class Canny {static {OpenCV.loadLocally(); // 自動下載并加載本地庫}public static void main(String[] args) {//讀取圖像灰度圖并顯示Mat src = Imgcodecs.imread("/Users/acton_zhang/J2EE/MavenWorkSpace/opencv_demo/src/main/java/demo1/butterfly.png", Imgcodecs.IMREAD_GRAYSCALE);HighGui.imshow("src", src);HighGui.waitKey(0);//進行Canny邊緣檢測并顯示Mat dst = new Mat();Imgproc.GaussianBlur(src, src, new Size(3, 3), 5);Imgproc.Canny(src, dst, 60, 200);HighGui.imshow("Canny", dst);HighGui.waitKey(0);System.exit(0);}
}

原圖:

在這里插入圖片描述

Canny算法檢測:

在這里插入圖片描述

可以看出Canny邊緣檢測的效果非常好。無論是Sobel算子、Scharr算子還是Laplacian算子檢測的邊緣都比較模糊,而Canny算法得出的邊緣非常請清晰。當然,為了得到較好的邊緣,Canny算法耗費的時間也比較長。

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

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

相關文章

2506認證資訊|工信部出手整治多品牌充電寶,WMC上海稍遜往年,RED修訂Common Charger,WiFi7 FCC測試

01 — 中國 工信部擬制定移動電源強制性國家標準 該標準將從以下方面全面提升移動電源安全性: 1. 擬在GB 31241、GB 4943.1基礎上,新增或加嚴過充電、針刺等試驗要求。 2. 擬提出影響電池安全的正負極材料、隔膜等關鍵材料要求。 3. 擬規范鋰離子電池…

Linux Regulator 子系統核心邏輯與關鍵問題全解析

Linux Regulator 子系統核心邏輯與關鍵問題全解析 一、什么是 regulator 子系統?核心作用? regulator 子系統是 Linux 內核為板級/SoC 多路可控電源設計的統一電源管理框架。它的主要作用是: 為每一路可控電源(Buck、LDO、DCDC …

制造業官網3D應用,讓產品會“說話”

在當今數字化時代,裝備制造業正經歷著前所未有的變革。隨著消費升級和國內經濟的蓬勃發展,中國社會的經濟格局從傳統的“工業經濟”向多元化的“服務經濟”轉型。裝備制造業作為制造業與服務業融合的核心領域,積極探索全新的“服務化”發展模…

SCAU15--氣球狂歡節

15 氣球狂歡節 Time Limit:1000MS Memory Limit:65535K 題型: 編程題 語言: G;GCC 描述: 一個充滿魔法的國度中,存在一場年度的節日,名為“氣球狂歡節”。在這個節日中,有一個傳統的比賽,那就是“氣球挑戰賽”…

python打卡day56@浙大疏錦行

知識點回顧: 假設檢驗基礎知識 原假設與備擇假設P值、統計量、顯著水平、置信區間 白噪聲 白噪聲的定義自相關性檢驗:ACF檢驗和Ljung-Box 檢驗偏自相關性檢驗:PACF檢驗 平穩性 平穩性的定義單位根ADF檢驗: 越小越平穩 季節性檢驗 ACF檢驗序列…

采集文章+原創AI處理+發布網站詳細教程

簡數采集器是新一代的網站文章采集和發布平臺,完全在線配置和使用云采集,功能強大,操作簡單,配置快捷高效。 簡數不僅提供網頁文章采集、數據批量處理、定時采集、定時定量自動發布等基本功能,還集成強大的SEO工具與接…

Hystrix超時降級機制全解析

Hystrix的超時降級實現主要通過以下核心機制完成,結合配置、注解和Fallback邏輯實現服務容錯: 1. 超時觸發條件 默認超時時間:Hystrix默認超時閾值為1秒,超過該時間未響應則觸發降級。自定義配置:可通過HystrixComman…

6月份最新代發考試戰報:思科華為HCIP HCSE 考試通過

6月份最新代發考試戰報:思科華為HCIP HCSE 考試通過 H19-423 HCSA-Presales-IP Network 數通考試通過, H12-725 HCIP-Security安全 考試通過,H13-121 HCIP-Kunpeng Application Developer鯤鵬計算 考試通過,CCNP 350-401考試通過…

談談我的軟考經歷

我 2020 年高考進入大學,軟件工程專業,去年(24年7月)畢業開始工作。我實習是在一家云計算公司,公司內部對軟考的證書沒有什么激勵或補助之類的,我也一直認為計算機嘛,“talk is cheap&#xff0…

CVPR 2025革命性突破!可變形Mamba,刷新SOTA記錄!

CVPR 2025上,眾多創新研究展示了Mamba在圖像分類、目標檢測、語義分割等多個任務中的卓越表現。其中,可變形Mamba的最新研究成果正在不斷刷新我們對視覺任務性能的認知。大連理工大學發布的DefMamba通過可變形掃描策略動態調整掃描路徑,優先關…

蜂鳥代理IP+云手機:跨境電商多賬號運營的“隱形風控引擎”

在亞馬遜、TikTok Shop等平臺的嚴苛風控下,跨境電商多賬號運營長期面臨“設備關聯封號”“IP污染限流”“地域畫像矛盾”三大痛點。傳統方案賬號存活率不足35%,而蜂鳥代理IP與云手機技術的協同,通過IP層隔離設備層虛擬化行為層仿真三重防護&a…

Boss:組件

能幫到你的話,就給個贊吧 😘 文章目錄 組件Event Begin Play獲取 Owner:不會報錯嗎,組件初始化的時候 Owner還不存在吧 Attack General:Boss普通攻擊不可以連續觸發:只有在當前動作為NoAction時才可以攻擊 …

供應鏈數據可視化大屏

在全球化與數字化轉型的雙重浪潮下,供應鏈管理正面臨前所未有的挑戰:黑天鵝事件頻發、多環節協同效率低下、庫存與成本難以平衡……如何讓供應鏈更透明、更敏捷、更具韌性?供應鏈數據可視化大屏應運而生,成為企業破解管理痛點的關…

XML讀寫數據-XPATH用法,快速定位元素

在XPath查詢效率對比中,兩種方式的性能差異如下: ?絕對路徑方案? /configuration/system.applicationHost/sites/site[nameWebSite1] 直接通過文檔層級導航,避免全局掃描適合已知完整路徑結構的場景,解析速度最快13 ?相對路徑…

Python 多版本與開發環境治理架構設計

Python 多版本治理理念(Windows 平臺 零基礎友好)-CSDN博客 Python 多版本開發環境治理:理論架構與實踐-CSDN博客 Python 開發環境全棧隔離架構:從 Anaconda 到 PyCharm 的四級防護體系-CSDN博客 【零基礎】Python 多版本虛擬環境…

IDE如何快速切換JLINK版本

JLINK是比較常用的調試器,因為產品維護,我們的電腦上可是裝了好幾個版本的JLINK,怎么進行快速的切換呢?方法如下: 1、使用Everything工具搜索JLinkDLLUpdater.exe,找到當前需要使用的JLINK版本安裝目錄下的…

WebSocket單例模式實現與使用

提示:記錄工作中遇到的需求及解決辦法 文章目錄 前言一、代碼二、功能說明三、使用場景 前言 前端通過WebSocket的單例模式實現實時通信效果 提示:以下是本篇文章正文內容,下面案例可供參考 一、代碼 export default class SocketService …

【緩存技術】深入分析如果使用好緩存及注意事項

Java 架構師緩存深度實踐指南:策略、陷阱與高并發場景實戰 一、緩存設計核心策略 1. 緩存選型與場景適配 緩存選型需結合業務場景、數據規模、性能要求等多維度評估: 場景推薦方案工具/技術案例高頻讀、極少寫本地緩存Caffeine、Guava Cache電商平臺商…

wordpress Contact Form 7表單插件設置使用教程

在wordpress安裝插件Contact Form 7后,前端的提示信息會根據后臺的語言決定。如果你安裝的是版本的wordpress程序,出來的提示信息就是英文的。如果你安裝的是中文版的wordpress程序,出來的提示信息就是中文的。前端顯示什么樣的語言取決你安裝…

Qt實現tcp通信(QTcpServer和QTcpSocket的應用)詳細教程

Qt實現tcp通信(QTcpServer和QTcpSocket的應用)詳細教程 服務端 監聽地址和端口 ip可以是Ipv4Any,本機地址,也可以是固定的某個ip 端口號則作為服務端綁定的端口,客戶端連接服務端時需要連接到服務端綁定的端口&#x…