OpenCV3 圖像膨脹 dilate、腐蝕 erode、提取圖像中的條形碼 JAVA 實現

關於 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

原圖: OpenCV3 圖像膨脹 dilate、腐蝕 erode、提取圖像中的條形碼 JAVA 實現算法

膨脹後: OpenCV3 圖像膨脹 dilate、腐蝕 erode、提取圖像中的條形碼 JAVA 實現數組

腐蝕 erode OpenCV3 圖像膨脹 dilate、腐蝕 erode、提取圖像中的條形碼 JAVA 實現函數

獲取條形碼學習

獲取條形碼的效果可能不是最好,方法也不是最好的,只是我學到這裏的一些積累。只是拋出一種方法而已。字體

OpenCV3 圖像膨脹 dilate、腐蝕 erode、提取圖像中的條形碼 JAVA 實現

OpenCV3 圖像膨脹 dilate、腐蝕 erode、提取圖像中的條形碼 JAVA 實現

OpenCV3 圖像膨脹 dilate、腐蝕 erode、提取圖像中的條形碼 JAVA 實現

廣告欄: 歡迎關注個人 我的博客this

相關文章
相關標籤/搜索