關於 JAVA 學習 OpenCV 的內容,函數講解,案例代碼內容我均整理在 GitHub【OpenCV3-Study-JAVA 】上java
下面代碼中所需的項目結構,圖片,請訪問 GitHub 獲取。git
package opencv; import opencv.base.OpenCVStudyBase; import org.junit.Test; import org.opencv.core.*; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; import java.util.ArrayList; import java.util.List; /** * @Author : alexliu * @Description : 主要學習<br/> * 1. 圖像腐蝕<br/> * 2. 圖像膨脹<br/> * 3. 查找條形碼案例<br/> */ public class StudyTest_8 extends OpenCVStudyBase{ /* * 腐蝕,膨脹都屬於形態學濾波。 * * 數學形態學中,基本的運算有: * 二值腐蝕和膨脹 * 二值開閉運算 * 骨架抽取 * 極限腐蝕 * 灰值腐蝕和膨脹 * 灰值開閉運算 * 灰值形態學梯度 * ..... * * * 腐蝕,膨脹的主要功能以下: * 1. 消除噪聲 * 2. 分割(isolate)出獨立的圖像元素,在圖像中鏈接(join)相鄰的元素 * 3. 尋找圖像中的明顯的極大值區域或極小值區域 * 4. 求出圖像的梯度 * * 注意: * 腐蝕和膨脹僅針對`圖像高亮`區域進行操做。 * */ private String save_dir = "study-output/study-opencv-8"; /* * 如何建立腐蝕、膨脹操做的核 * * 腐蝕和膨脹均有一個 Mat kernel 參數。這個參數就是腐蝕/膨脹操做的核,它是一個矩陣結構元素(Mat) * OpenCV 在 Imgproc 包中,提供了 getStructuringElement 的函數,來方便建立腐蝕/膨脹的核 * * getStructuringElement 原型方法: * getStructuringElement(int shape, Size ksize, Point anchor) * getStructuringElement(int shape, Size ksize) * * 參數: * shape : Integer 核的結構類型 * -- C++ 有四種(多一個用戶自定義),其餘語言3種 * -- MORPH_RECT , 一個矩形結構元素 * -- MORPH_ELLIPSE , 一個橢圓結構元素 * -- MORPH_CROSS , 一個十字形結構元素 * ksize : Size 結構元素的大小 * anchor : Point 元素中瞄點的位置。默認值 (-1,-1)表示在元素的中心位置。注意:只有十字形結構元素依賴瞄點,其餘形狀類型僅僅影響結果的偏移。 * * 原文: * shape – Element shape that could be one of the following: * MORPH_RECT - a rectangular structuring element * MORPH_ELLIPSE - an elliptic structuring element, that is, a filled ellipse inscribed into the rectangle Rect(0, 0, esize.width, 0.esize.height) * MORPH_CROSS - a cross-shaped structuring element * CV_SHAPE_CUSTOM - custom structuring element (OpenCV 1.x API) * ksize – Size of the structuring element. * anchor – Anchor position within the element. The default value (-1, -1) means that the anchor is at the center. * Note that only the shape of a cross-shaped element depends on the anchor position. * In other cases the anchor just regulates how much the result of the morphological operation is shifted. */ /* * ------------------------------------------------------------------------------------------------------------ * * 腐蝕 * * 1. 腐蝕說明: * 圖像的一部分區域與指定的核進行卷積,求核的最`小`值並賦值給指定區域。 * 腐蝕能夠理解爲圖像中`高亮區域`的'領域縮小'。 * 意思是高亮部分會被不是高亮部分的像素侵蝕掉,使高亮部分愈來愈少。 * * 2. 腐蝕函數(erode) * erode 有3個原型方法 * * erode(Mat src, Mat dst, Mat kernel, Point anchor, int iterations, int borderType, Scalar borderValue) * erode(Mat src, Mat dst, Mat kernel, Point anchor, int iterations) * erode(Mat src, Mat dst, Mat kernel) * * 參數: * src : Mat 輸入圖像 對通道數無要求,可是 depth 必須是 CV_8U、CV_16U、CV_16S、CV_32F、CV_64F 之一 * dst : Mat 輸出圖像,與原圖以上的尺寸與類型 * kernel : Mat 膨脹操做的核 , null 時表示以當前像素爲中心 3x3 爲單位的核 * 通常使用函數 Imgproc.getStructuringElement 來建立核。該函數會返回指定形狀或尺寸的矩陣結構元素。 * anchor : Point 瞄點。根據 kernel(核),處理每一個核的某個點。 (-1,-1)表明取這個核的中心位置。 * interations : Integer 迭代 dilate(膨脹)的次數,默認 1 。。 * borderType : Integer 推斷圖像外部像素的某種邊界模式,通常不須要這個參數。 * borderValue : Scalar 當 borderType 值爲常數時,區域的顏色通常不用管, * * 腐蝕,通常不須要borderType,borderValue,均有默認值。若是須要使用,可參考官網獲取更多信息 * * ------------------------------------------------------------------------------------------------------------ */ /** * 圖像腐蝕處理 * 不作任何處理的圖片 */ @Test public void testErodeNomal(){ Mat sourceImage = Imgcodecs.imread(p_test_file_path + "/shufa.png"); //Mat sourceImage = Imgcodecs.imread(test_file_path + "/5cent.jpg",Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE); // size 越小,腐蝕的單位越小,圖片越接近原圖 Mat structImage = Imgproc.getStructuringElement(Imgproc.MORPH_RECT,new Size(30,30)); Mat outImage = new Mat(); //開始腐蝕 Imgproc.erode(sourceImage,outImage,structImage); this.saveImage(this.save_dir + "/image_process_erode_nomal.png",outImage); } /** * 圖像腐蝕處理 * 灰度處理的圖片 */ @Test public void testErodeGray(){ // 因爲shufa.png 背景爲白色,字體爲黑色,在灰度的0-255顯示範圍,看不出變化 // 因此咱們換一個背景圖不是白色的。 Mat sourceImage = Imgcodecs.imread(p_test_file_path + "/shufa-1.jpg",Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE); Mat structImage = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE,new Size(30,30)); Mat outImage = new Mat(); Imgproc.erode(sourceImage,outImage,structImage); this.saveImage(this.save_dir + "/image_process_erode_gray.png",outImage); } /** * 圖像腐蝕處理 * 二值化處理的圖片 */ @Test public void testErodeThreshold() { Mat sourceImage = Imgcodecs.imread(this.p_test_file_path + "/shufa.png"); //二值化處理 cv_8uc1 8位單通道格式 Mat binaryMat = new Mat(sourceImage.height(), sourceImage.width(), CvType.CV_8UC1); Imgproc.threshold(sourceImage, binaryMat,100, 200, Imgproc.THRESH_BINARY); Mat outImage = new Mat(); //圖像腐蝕 Mat structImage = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(30,30)); Imgproc.erode(binaryMat, outImage, structImage); this.saveImage(this.save_dir + "/image_process_erode_threshold.png",outImage); } /* * ------------------------------------------------------------------------------------------------------------ * * 膨脹 * * 1. 膨脹說明: * 圖像的一部分區域與指定的核進行卷積,求核的最`大`值並賦值給指定區域。 * 膨脹能夠理解爲圖像中`高亮區域`的'領域擴大'。 * 意思是高亮部分會侵蝕不是高亮的部分,使高亮部分愈來愈多。 * * 2. 膨脹函數(dilate) * dilate 有3個原型方法 * * dilate(Mat src, Mat dst, Mat kernel, Point anchor, int iterations, int borderType, Scalar borderValue) * dilate(Mat src, Mat dst, Mat kernel, Point anchor, int iterations) * dilate(Mat src, Mat dst, Mat kernel) * * 參數: * src : Mat 輸入圖像 對通道數無要求,可是 depth 必須是 CV_8U、CV_16U、CV_16S、CV_32F、CV_64F 之一 * dst : Mat 輸出圖像,與原圖以上的尺寸與類型 * kernel : Mat 膨脹操做的核 , null 時表示以當前像素爲中心 3x3 爲單位的核 * 通常使用函數 Imgproc.getStructuringElement 來建立核。該函數會返回指定形狀或尺寸的矩陣結構元素。 * anchor : Point 瞄點。根據 kernel(核),處理每一個核的某個點。 (-1,-1)表明取這個核的中心位置。 * interations : Integer 迭代 dilate(膨脹)的次數,默認 1 。。 * borderType : Integer 推斷圖像外部像素的某種邊界模式,通常不須要這個參數。 * borderValue : Scalar 當 borderType 值爲常數時,區域的顏色通常不用管, * * 膨脹,通常不須要borderType,borderValue,均有默認值。若是須要使用,可參考官網獲取更多信息 * * ------------------------------------------------------------------------------------------------------------ */ /** * 圖像膨脹處理 * 不作任何處理的圖片 */ @Test public void testDilateNomal(){ Mat sourceImage = Imgcodecs.imread(this.p_test_file_path + "/shufa.png"); Mat outImage = new Mat(); //圖像腐蝕 Mat structImage = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(30,30)); Imgproc.dilate(sourceImage, outImage, structImage); this.saveImage(this.save_dir + "/image_process_dilate_nomal.png",outImage); } /** * 圖像膨脹處理 * 灰度處理的圖片 */ @Test public void testDilateGray(){ Mat sourceImage = Imgcodecs.imread(this.p_test_file_path + "/shufa-1.jpg",Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE); Mat outImage = new Mat(); //圖像腐蝕 Mat structImage = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(30,30)); Imgproc.dilate(sourceImage, outImage, structImage); this.saveImage(this.save_dir + "/image_process_dilate_gray.png",outImage); } /** * 圖像膨脹處理 * 二值化處理的圖片 */ @Test public void testDilateThreshold() { Mat sourceImage = Imgcodecs.imread(this.p_test_file_path + "/shufa.png"); //二值化處理 cv_8uc1 8位單通道格式 Mat binaryMat = new Mat(sourceImage.height(), sourceImage.width(), CvType.CV_8UC1); Imgproc.threshold(sourceImage, binaryMat,100, 200, Imgproc.THRESH_BINARY); Mat outImage = new Mat(); //圖像腐蝕 Mat structImage = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(30,30)); Imgproc.dilate(binaryMat, outImage, structImage); this.saveImage(this.save_dir + "/image_process_dilate_threshold.png",outImage); } /** * 查找條形碼案例 * * 步驟: * 1. 讀取灰值圖 * 2. 圖像模糊,降噪 * 3. 圖像二值化 * 4. 腐蝕圖像,經過腐蝕過濾掉不是豎線的的區域 * 5. 膨脹圖像,將腐蝕過的線條數據經過膨脹放大 * 6. 繼續用矩形核膨脹圖像,使線條連接成矩形圖像 * 7. 查找輪廓 * 8. 對比全部輪廓,過濾掉寬度小於200,偏斜角<2度的矩形圖像 * 9. 找到圖像並截取 */ @Test public void testFindLineCode() { //讀取灰值圖 Mat sourceImage = Imgcodecs.imread(this.p_test_file_path + "/tiaoma.png",Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE); //圖像高斯模糊 Mat gsMat = Mat.ones(sourceImage.size(),sourceImage.type()); Imgproc.GaussianBlur(sourceImage,gsMat,new Size(5,5),0,0); this.saveImage(this.save_dir + "/image_process_dilate_tiaoma-1.png",gsMat); //圖像二值化,adaptiveThreshold 後面再二值化的專題裏講解 Mat thresh_image = Mat.ones(sourceImage.size(),sourceImage.type()); // C 負數,取反色,超過閾值的爲黑色,其餘爲白色 Imgproc.adaptiveThreshold(gsMat, thresh_image,255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY,7,-2); this.saveImage(this.save_dir + "/image_process_dilate_tiaoma-2.png",thresh_image); //建立輸出圖像,後續的操做都是這個圖像 Mat outImage = new Mat(); /* * 圖像腐蝕操做 * * width=2 ,height=20 在腐蝕的時候,排除大多數細的垂直線。 * 這個參數不易設置過大,能夠經過屢次腐蝕來達到排除的效果。 * * ---------------------------------------------------------------------------------- * 注意 width、height 需根據圖像的大小來設置。 * * 好比我這裏的示例圖片大小是 1271(width)x648(height)。 我經過比較20是比較理想的值。 * 可是20 並比適用比示例圖小或大的圖像。因此在設置這個參數前,須要根據圖像大小來調整。 * * && 這個值沒有固定、動態的大小,不要指望自動設置,除非接入 AI 來學習。 && * * 可是咱們加入到本身工程的業務裏,咱們處理的圖片一般都是固定的幾個圖像大小 * * 好比攝像頭獲取圖像,能夠指定 500x500 * 好比掃描儀獲取圖像,能夠指定 KPI 大小,那麼獲取到的同等材質(如 A4大小)的數碼圖片大小也是同樣的。 * 好比數碼照片,在不一樣模式下照片大小是一致的,能夠根據圖片信息分類。 * * 因此,本身根據本身業務常常處理的圖片來設置一個比例參數 ,經過 sourceImage.heigth()*xParam 來獲取這個參數便可。 * ---------------------------------------------------------------------------------- */ Mat structImage = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2,20)); Imgproc.erode(thresh_image, outImage, structImage,new Point(-1,-1),3); //腐蝕了3次 this.saveImage(this.save_dir + "/image_process_dilate_tiaoma-3.png",outImage); // 在用腐蝕的圖像,進行膨脹,將線條加粗 Imgproc.dilate(outImage, outImage, structImage,new Point(-1,-1),3); // 這裏一樣進行了3次膨脹 this.saveImage(this.save_dir + "/image_process_dilate_tiaoma-4.png",outImage); /* * 這裏我劃定了一個10x5 的矩形整列。來將剩餘的線條經過膨脹連成區域。 * 範圍一樣不宜過大或太小。 * 過大:膨脹區域增大,最終結果圖像干擾太多 * 太小:膨脹區域減小,可知足的條件過多,形成不容易連成一個整理區域。 */ // 再次膨脹,使其連成區域 structImage = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(10,5)); Imgproc.dilate(outImage, outImage, structImage,new Point(-1,-1),3); this.saveImage(this.save_dir + "/image_process_dilate_tiaoma-5.png",outImage); /* * findContours 找輪廓 * * 原型方法: * * findContours(Mat image, List<MatOfPoint> contours, Mat hierarchy, int mode, int method, Point offset) * * image : Mat 是輸入圖像,圖像的格式是8位單通道的圖像,而且被解析爲二值圖像(即圖中的全部非零像素之間都是相等的)。 * coutours : List<MatOfPoint> 輸出的輪廓數組,全部找到的輪廓都會放在這個數組中。MatOfPoint表明這個對象存儲了輪廓的`點`數據 * hierarchy : Mat 這個參數能夠指定,也能夠不指定。 * 若是指定的話,輸出hierarchy,將會描述輸出輪廓樹的結構信息。 * 0號元素表示下一個輪廓(同一層級); * 1號元素表示前一個輪廓(同一層級); * 2號元素表示第一個子輪廓(下一層級); * 3號元素表示父輪廓(上一層級) * mode : Integer 輪廓的模式,將會告訴OpenCV你想用何種方式來對輪廓進行提取,有四個可選的值: * CV_RETR_EXTERNAL (0):表示只提取最外面的輪廓; * CV_RETR_LIST (1):表示提取全部輪廓並將其放入列表; * CV_RETR_CCOMP (2):表示提取全部輪廓並將組織成一個兩層結構,其中頂層輪廓是外部輪廓,第二層輪廓是「洞」的輪廓; * CV_RETR_TREE (3):表示提取全部輪廓並組織成輪廓嵌套的完整層級結構。 * method : Integer 輪廓如何呈現的方法,有三種可選的方法: * CV_CHAIN_APPROX_NONE (1):將輪廓中的全部點的編碼轉換成點; * CV_CHAIN_APPROX_SIMPLE (2):壓縮水平、垂直和對角直線段,僅保留它們的端點; * CV_CHAIN_APPROX_TC89_L1 (3)or CV_CHAIN_APPROX_TC89_KCOS(4):應用Teh-Chin鏈近似算法中的一種風格 * offset : Point 可選,若是指定了點偏移,那麼返回的輪廓中的全部點均做指定量的偏移 */ List<MatOfPoint> contours = new ArrayList<MatOfPoint>(); Mat hierarchy = new Mat(); Imgproc.findContours(outImage,contours,hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE,new Point(0,0)); // 根據輪廓能夠找到的形狀數組 // Rect[] boundRect = new Rect[contours.size()]; // 循環找到的全部輪廓 for(int i = 0; i < contours.size();i++) { //將輪廓保存爲區域 // boundRect[i] = Imgproc.boundingRect(contours.get(i)); // System.out.println(boundRect[i].tl()); // System.out.println(boundRect[i].br()); // 獲取輪廓內,最小外包矩形 RotatedRect min = Imgproc.minAreaRect(new MatOfPoint2f(contours.get(i).toArray())); //偏轉角度 if(min.angle < 2){ //獲取一個矩形 Rect minRect = min.boundingRect(); //將寬度<200的排除 if( ( minRect.br().x - minRect.tl().x ) > 200 ){ //截取 Mat code = sourceImage.submat(minRect); this.saveImage(this.save_dir + "/image_process_dilate_tiaoma-code-"+i+".png",code); } //在原圖上把該矩形表示出來 Imgproc.rectangle(sourceImage, minRect.tl(), minRect.br(), new Scalar(0, 255, 0), 1, Imgproc.LINE_AA, 0); } } //輸出原圖 this.saveImage(this.save_dir + "/image_process_dilate_tiaoma-6.png",sourceImage); } }
膨脹 dilategithub
原圖: 算法
膨脹後: 數組
腐蝕 erode 函數
獲取條形碼學習
獲取條形碼的效果可能不是最好,方法也不是最好的,只是我學到這裏的一些積累。只是拋出一種方法而已。字體
廣告欄: 歡迎關注個人 我的博客this