JavaCV 圖像邊緣檢測(cè)之Canny 算法詳解
JavaCV 圖像邊緣檢測(cè) 之 Canny 算法
引言
在圖像處理領(lǐng)域,邊緣檢測(cè)是一項(xiàng)至關(guān)重要的任務(wù)。圖像中的邊緣包含了豐富的信息,例如物體的輪廓、區(qū)域的邊界等。通過(guò)準(zhǔn)確地檢測(cè)邊緣,我們可以進(jìn)一步對(duì)圖像進(jìn)行分析、識(shí)別和理解。
邊緣檢測(cè)算法有多種,其中 Canny邊緣檢測(cè)
算法 以其精確性和可靠性而備受關(guān)注。Canny邊緣檢測(cè)算法的目標(biāo)是在盡可能減少噪聲影響的同時(shí),準(zhǔn)確地檢測(cè)出圖像中的邊緣。它不像一些簡(jiǎn)單的邊緣檢測(cè)算法那樣容易受到噪聲干擾而產(chǎn)生虛假邊緣,也不會(huì)遺漏重要的邊緣信息。
在實(shí)際應(yīng)用中,Canny邊緣檢測(cè)
廣泛應(yīng)用于計(jì)算機(jī)視覺(jué)、醫(yī)學(xué)圖像處理、工業(yè)檢測(cè)等眾多領(lǐng)域。例如,在計(jì)算機(jī)視覺(jué)的目標(biāo)識(shí)別任務(wù)中,準(zhǔn)確的邊緣檢測(cè)可以幫助我們更好地定位和識(shí)別目標(biāo)物體的形狀;在醫(yī)學(xué)圖像處理中,對(duì)X光
、CT等圖像
進(jìn)行邊緣檢測(cè)有助于醫(yī)生發(fā)現(xiàn)病變區(qū)域的輪廓;在工業(yè)檢測(cè)方面,可以用于檢測(cè)產(chǎn)品的外形缺陷等。
JavaCV
是一個(gè)在Java平臺(tái)上用于計(jì)算機(jī)視覺(jué)的庫(kù),它為我們?cè)?code>Java環(huán)境下實(shí)現(xiàn)Canny邊緣檢測(cè)
提供了便利。通過(guò)JavaCV
,我們可以輕松地將Canny邊緣檢測(cè)算法
應(yīng)用到各種圖像處理項(xiàng)目中,充分發(fā)揮其優(yōu)勢(shì)。接下來(lái),我們將詳細(xì)介紹如何使用JavaCV
來(lái)實(shí)現(xiàn)Canny邊緣檢測(cè)
。
Canny 邊緣檢測(cè)的原理
Canny 邊緣檢測(cè)是一種基于多階段算法的邊緣檢測(cè)方法,其目標(biāo)是找到圖像中強(qiáng)度變化最為顯著的位置,即邊緣。以下是 Canny 邊緣檢測(cè)的主要步驟和原理:
1. 高斯濾波
在進(jìn)行邊緣檢測(cè)之前,首先對(duì)圖像進(jìn)行高斯濾波。這一步的目的是減少噪聲對(duì)邊緣檢測(cè)的影響。高斯濾波是一種線性平滑濾波器,它通過(guò)對(duì)圖像中的每個(gè)像素點(diǎn)與其周圍的像素點(diǎn)進(jìn)行加權(quán)平均來(lái)實(shí)現(xiàn)平滑效果。權(quán)重是根據(jù)高斯分布函數(shù)確定的,離中心像素點(diǎn)越近的像素點(diǎn)權(quán)重越大,離中心像素點(diǎn)越遠(yuǎn)的像素點(diǎn)權(quán)重越小。
2. 計(jì)算梯度幅值和方向
接下來(lái),通過(guò)使用一階偏導(dǎo)數(shù)的有限差分來(lái)近似計(jì)算圖像的梯度幅值和方向。梯度幅值表示圖像中像素點(diǎn)的強(qiáng)度變化程度,梯度方向表示強(qiáng)度變化的方向。具體來(lái)說(shuō),對(duì)于圖像中的每個(gè)像素點(diǎn),計(jì)算其在水平方向和垂直方向上的偏導(dǎo)數(shù),然后根據(jù)這兩個(gè)偏導(dǎo)數(shù)計(jì)算出梯度幅值和方向。
3. 非極大值抑制
非極大值抑制是為了細(xì)化邊緣,只保留梯度方向上的局部最大值作為邊緣點(diǎn)。在這一步中,對(duì)于每個(gè)像素點(diǎn),將其梯度幅值與沿梯度方向上的兩個(gè)相鄰像素點(diǎn)的梯度幅值進(jìn)行比較。如果該像素點(diǎn)的梯度幅值不是局部最大值,則將其置為 0,否則保持不變。這樣可以去除一些非邊緣點(diǎn),使邊緣更加清晰。
4. 雙閾值檢測(cè)
最后,通過(guò)雙閾值檢測(cè)來(lái)確定最終的邊緣。設(shè)置一個(gè)高閾值和一個(gè)低閾值,高于高閾值的像素點(diǎn)確定為邊緣點(diǎn),低于低閾值的像素點(diǎn)被排除,介于兩者之間的像素點(diǎn)如果與確定的邊緣點(diǎn)相連則也被視為邊緣點(diǎn)。這樣可以去除一些弱邊緣,同時(shí)保留一些強(qiáng)邊緣和與強(qiáng)邊緣相連的弱邊緣,使邊緣更加連續(xù)和準(zhǔn)確。
JavaCV 實(shí)現(xiàn) Canny 邊緣檢測(cè)的 Maven 依賴
為了在 Java 項(xiàng)目中使用 JavaCV 實(shí)現(xiàn) Canny 邊緣檢測(cè),需要在項(xiàng)目的 pom.xml 文件中添加以下 Maven 依賴:
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.5.7</version> </dependency>
JavaCV 實(shí)現(xiàn) Canny 邊緣檢測(cè)的步驟
步驟一:圖像讀取
首先,我們需要讀取一張圖像。可以使用 JavaCV 中的 Imgcodecs
類來(lái)實(shí)現(xiàn)圖像的讀取。以下是讀取圖像的代碼示例:
import org.opencv.core.Mat; import org.opencv.imgcodecs.Imgcodecs; public class CannyEdgeDetection { public static void main(String[] args) { // 讀取圖像 Mat image = Imgcodecs.imread("input.jpg"); if (image.empty()) { System.out.println("無(wú)法讀取圖像"); return; } } }
在上述代碼中,我們使用 Imgcodecs.imread()
方法讀取了一張名為 input.jpg
的圖像,并將其存儲(chǔ)在一個(gè) Mat
對(duì)象中。如果圖像讀取失敗,則輸出錯(cuò)誤信息并返回。
步驟二:高斯濾波
接下來(lái),對(duì)圖像進(jìn)行高斯濾波??梢允褂?JavaCV 中的 Imgproc
類來(lái)實(shí)現(xiàn)高斯濾波。以下是對(duì)圖像進(jìn)行高斯濾波的代碼示例:
import org.opencv.core.Mat; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; public class CannyEdgeDetection { public static void main(String[] args) { // 讀取圖像 Mat image = Imgcodecs.imread("input.jpg"); if (image.empty()) { System.out.println("無(wú)法讀取圖像"); return; } // 高斯濾波 Mat blurredImage = new Mat(); Imgproc.GaussianBlur(image, blurredImage, new org.opencv.core.Size(5, 5), 0); } }
在上述代碼中,我們首先創(chuàng)建了一個(gè)新的 Mat
對(duì)象 blurredImage
,用于存儲(chǔ)濾波后的圖像。然后,使用 Imgproc.GaussianBlur()
方法對(duì)圖像進(jìn)行高斯濾波。該方法的參數(shù)分別為輸入圖像、輸出圖像、高斯核的大小和標(biāo)準(zhǔn)差。在這個(gè)例子中,我們使用了一個(gè)大小為 5x5
的高斯核,標(biāo)準(zhǔn)差為 0。
步驟三:計(jì)算梯度幅值和方向
計(jì)算圖像的梯度幅值和方向可以使用 JavaCV 中的 Imgproc
類的 Sobel()
方法。以下是計(jì)算梯度幅值和方向的代碼示例:
import org.opencv.core.Mat; import org.opencv.core.Size; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; public class CannyEdgeDetection { public static void main(String[] args) { // 讀取圖像 Mat image = Imgcodecs.imread("input.jpg"); if (image.empty()) { System.out.println("無(wú)法讀取圖像"); return; } // 高斯濾波 Mat blurredImage = new Mat(); Imgproc.GaussianBlur(image, blurredImage, new Size(5, 5), 0); // 計(jì)算梯度幅值和方向 Mat gradientX = new Mat(); Mat gradientY = new Mat(); Imgproc.Sobel(blurredImage, gradientX, -1, 1, 0); Imgproc.Sobel(blurredImage, gradientY, -1, 0, 1); // 計(jì)算梯度幅值和方向 Mat magnitude = new Mat(); Mat direction = new Mat(); Imgproc.cartToPolar(gradientX, gradientY, magnitude, direction); } }
在上述代碼中,我們首先創(chuàng)建了兩個(gè)新的 Mat
對(duì)象 gradientX
和 gradientY
,分別用于存儲(chǔ)水平方向和垂直方向上的梯度。然后,使用 Imgproc.Sobel()
方法分別計(jì)算水平方向和垂直方向上的梯度。該方法的參數(shù)分別為輸入圖像、輸出圖像、輸出圖像的深度、水平方向上的導(dǎo)數(shù)階數(shù)和垂直方向上的導(dǎo)數(shù)階數(shù)。在這個(gè)例子中,我們將輸出圖像的深度設(shè)置為 -1
,表示與輸入圖像的深度相同。水平方向上的導(dǎo)數(shù)階數(shù)為 1
,垂直方向上的導(dǎo)數(shù)階數(shù)為 0
,表示計(jì)算水平方向上的梯度。同樣地,我們可以計(jì)算垂直方向上的梯度。
接下來(lái),我們使用 Imgproc.cartToPolar()
方法計(jì)算梯度幅值和方向。該方法的參數(shù)分別為水平方向上的梯度、垂直方向上的梯度、輸出的梯度幅值和輸出的梯度方向。
步驟四:非極大值抑制
非極大值抑制可以使用 JavaCV 中的 Imgproc
類的 Canny()
方法來(lái)實(shí)現(xiàn)。以下是進(jìn)行非極大值抑制的代碼示例:
import org.opencv.core.Mat; import org.opencv.core.Size; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; public class CannyEdgeDetection { public static void main(String[] args) { // 讀取圖像 Mat image = Imgcodecs.imread("input.jpg"); if (image.empty()) { System.out.println("無(wú)法讀取圖像"); return; } // 高斯濾波 Mat blurredImage = new Mat(); Imgproc.GaussianBlur(image, blurredImage, new Size(5, 5), 0); // 計(jì)算梯度幅值和方向 Mat gradientX = new Mat(); Mat gradientY = new Mat(); Imgproc.Sobel(blurredImage, gradientX, -1, 1, 0); Imgproc.Sobel(blurredImage, gradientY, -1, 0, 1); // 計(jì)算梯度幅值和方向 Mat magnitude = new Mat(); Mat direction = new Mat(); Imgproc.cartToPolar(gradientX, gradientY, magnitude, direction); // 非極大值抑制 Mat edges = new Mat(); Imgproc.Canny(magnitude, edges, 50, 150); } }
在上述代碼中,我們首先創(chuàng)建了一個(gè)新的 Mat
對(duì)象 edges
,用于存儲(chǔ)非極大值抑制后的邊緣圖像。然后,使用 Imgproc.Canny()
方法進(jìn)行非極大值抑制。該方法的參數(shù)分別為輸入的梯度幅值圖像、輸出的邊緣圖像、低閾值和高閾值。在這個(gè)例子中,我們將低閾值設(shè)置為 50
,高閾值設(shè)置為 150
。
步驟五:雙閾值檢測(cè)
雙閾值檢測(cè)已經(jīng)在非極大值抑制的步驟中完成了,因?yàn)?Imgproc.Canny()
方法會(huì)自動(dòng)進(jìn)行雙閾值檢測(cè)。
案例分析與對(duì)比展示
為了更好地理解 Canny 邊緣檢測(cè)的效果,我們可以使用一張圖片進(jìn)行案例分析,并對(duì)比展示處理前后的圖像。
以下是一個(gè)完整的示例代碼,用于讀取一張圖片,進(jìn)行 Canny 邊緣檢測(cè),并顯示處理前后的圖像:
import org.opencv.core.Mat; import org.opencv.core.Size; import org.opencv.core.Core; import org.opencv.highgui.HighGui; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; public class CannyEdgeDetection { public static void main(String[] args) { // 讀取圖像 Mat image = Imgcodecs.imread("input.jpg"); if (image.empty()) { System.out.println("無(wú)法讀取圖像"); return; } // 高斯濾波 Mat blurredImage = new Mat(); Imgproc.GaussianBlur(image, blurredImage, new Size(5, 5), 0); // 計(jì)算梯度幅值和方向 Mat gradientX = new Mat(); Mat gradientY = new Mat(); Imgproc.Sobel(blurredImage, gradientX, -1, 1, 0); Imgproc.Sobel(blurredImage, gradientY, -1, 0, 1); // 計(jì)算梯度幅值和方向 Mat magnitude = new Mat(); Mat direction = new Mat(); Core.cartToPolar(gradientX, gradientY, magnitude, direction); // 非極大值抑制 Mat edges = new Mat(); Imgproc.Canny(magnitude, edges, 50, 150); // 保存邊緣檢測(cè)后的圖像 Imgcodecs.imwrite("path/to/your/edge_detection_result.jpg", edges); } }
在上述代碼中,我們首先讀取了一張名為 input.jpg
的圖片。然后,對(duì)圖像進(jìn)行了高斯濾波、計(jì)算梯度幅值和方向、非極大值抑制等操作,得到了邊緣檢測(cè)后的圖像。最后,使用 HighGui.imshow()
方法顯示原始圖像和邊緣檢測(cè)后的圖像,并使用 HighGui.waitKey()
方法等待用戶按下任意鍵退出程序。
以下是一張?jiān)紙D像和經(jīng)過(guò) Canny 邊緣檢測(cè)后的圖像對(duì)比:
原始圖像邊緣檢測(cè)后的圖像
從對(duì)比圖中可以看出,經(jīng)過(guò) Canny 邊緣檢測(cè)
后,圖像中的邊緣更加清晰,物體的輪廓更加明顯。
總結(jié)
本文詳細(xì)介紹了 JavaCV
圖像邊緣檢測(cè)之 Canny 邊緣檢測(cè)
算法。首先闡述了 Canny 邊緣檢測(cè)
的原理,包括高斯濾波、計(jì)算梯度幅值和方向、非極大值抑制以及雙閾值檢測(cè)等關(guān)鍵步驟。然后介紹了在 Java 項(xiàng)目中使用 JavaCV 實(shí)現(xiàn) Canny 邊緣檢測(cè)
的 Maven 依賴。接著,通過(guò)詳細(xì)的代碼示例展示了 Canny 邊緣檢測(cè)
的每個(gè)關(guān)鍵步驟,并對(duì)代碼進(jìn)行了注釋說(shuō)明。最后,通過(guò)案例分析和圖像對(duì)比展示了 Canny 邊緣檢測(cè)
的實(shí)際效果。通過(guò)本文的學(xué)習(xí),讀者可以掌握 Canny 邊緣檢測(cè)算法
的原理和實(shí)現(xiàn)方法,并能夠在實(shí)際項(xiàng)目中應(yīng)用這一技術(shù)來(lái)提取圖像中的邊緣信息。
參考資料文獻(xiàn)
到此這篇關(guān)于JavaCV 圖像邊緣檢測(cè) 之 Canny 算法的文章就介紹到這了,更多相關(guān)JavaCV 圖像邊緣檢測(cè)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?Mybatis使用resultMap時(shí),屬性賦值順序錯(cuò)誤的巨坑
這篇文章主要介紹了Java?Mybatis使用resultMap時(shí),屬性賦值順序錯(cuò)誤的巨坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01Java連接MYSQL數(shù)據(jù)庫(kù)的詳細(xì)步驟
這篇文章主要為大家介紹了Java連接MYSQL數(shù)據(jù)庫(kù)的詳細(xì)步驟,感興趣的小伙伴們可以參考一下2016-05-05spring boot使用@Async異步注解的實(shí)現(xiàn)原理+源碼
通常我們都是采用多線程的方式來(lái)實(shí)現(xiàn)上述業(yè)務(wù)功能,但spring 提供更優(yōu)雅的方式來(lái)實(shí)現(xiàn)上述功能,就是@Async 異步注解,在方法上添加@Async,spring就會(huì)借助AOP,異步執(zhí)行方法,接下來(lái)通過(guò)本文給大家介紹spring boot異步注解的相關(guān)知識(shí),一起看看吧2021-06-06Java利用位運(yùn)算實(shí)現(xiàn)比較兩個(gè)數(shù)的大小
這篇文章主要為大家介紹了,在Java中如何不用任何比較判斷符(>,==,<),返回兩個(gè)數(shù)( 32 位整數(shù))中較大的數(shù),感興趣的可以了解一下2022-08-08普通對(duì)象使用spring容器中的對(duì)象的實(shí)現(xiàn)方法
這篇文章主要介紹了普通對(duì)象使用spring容器中的對(duì)象的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06spring cloud config分布式配置中心的高可用問(wèn)題
本文給大家介紹spring cloud config分布式配置中心的高可用問(wèn)題,通過(guò)整合Eureka來(lái)實(shí)現(xiàn)配置中心的高可用,需要的朋友參考下本文2018-01-01使用Spring boot 的profile功能實(shí)現(xiàn)多環(huán)境配置自動(dòng)切換
這篇文章主要介紹了使用Spring boot 的profile功能實(shí)現(xiàn)多環(huán)境配置自動(dòng)切換的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2018-11-11Java網(wǎng)絡(luò)編程實(shí)例——簡(jiǎn)單模擬在線聊天
學(xué)了java網(wǎng)絡(luò),也是該做個(gè)小案例來(lái)鞏固一下了。本次案例將使用UDP和多線程模擬即時(shí)聊天,簡(jiǎn)單練練手。2021-05-05Spring boot route Controller接收參數(shù)常用方法解析
這篇文章主要介紹了Spring boot route Controller接收參數(shù)常用方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10