OpenCV 3 floodFill(漫水填充)、圖片的放大縮小 pyrUp、pyrDown、Resize JAVA OpenCV專題學習10

關於 JAVA 學習 OpenCV 的內容,函數講解。內容我均整理在 GitHubd的【OpenCV3-Study-JAVAhtml

下面代碼中所需的項目結構,圖片,請訪問 GitHub 獲取。java

內容在註釋裏了。git

package opencv.study;

import com.liuqi.opencv.base.OpenCVProcessBase;
import org.junit.Test;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

/**
 * @Author : alexliu
 * @Description : 主要學習<br/>
 * 1. floodFill 漫水|水漫填充<br/>
 * 2. pyrUp 向上採樣<br/>
 * 3. pyrDown 向下採樣<br/>
 * 4. resize 放大縮小<br/>
 */
public class StudySection_10 extends OpenCVProcessBase {


    private String save_dir = "study-output/study-opencv-10";

    /**--------------------*
     *  第一節 floodFill
     **--------------------*/

    /**
     * 漫水填充
     *
     * 又叫水漫填充,用特定的顏色填充連通區域,經常使用來標記或分離圖像的一部分。
     * 漫水填充,將種子點顏色相近的區域進行連通,相似 photoshop 的魔術棒工具。
     */
    @Test
    public void testFloodFill(){

        /*
         * 官方說明 : https://docs.opencv.org/3.0-beta/modules/imgproc/doc/miscellaneous_transformations.html#floodfill
         *
         * 原型方法 :
         *      floodFill(Mat image, Mat mask, Point seedPoint, Scalar newVal)  這個原型方法,用參數測試了下,沒效果。不知道緣由。
         *      floodFill(Mat image, Mat mask, Point seedPoint, Scalar newVal, Rect rect, Scalar loDiff, Scalar upDiff, int flags)
         *
         *      參數 :
         *          image : Mat 輸入圖像 輸入/輸出圖像,1通道或3通道,8位圖像
         *          mask : Mat 操做掩膜,單通道,8位,比image 長寬都大2個像素的圖像。
         *          seedPoint : Point ,起始點,漫水填充的種子點
         *          newVal : Scalar ,像素點被染色的值,即經過起始點漫水填充後的區域填充的顏色
         *          rect : Rect ,不傳的話,默認爲0。設置漫水填充的最小區域。若是設置了,而漫水填充找出的區域 < rect 那麼不會填充。
         *          loDiff : Scalar ,在當前觀察到的像素和它的一個相鄰的像素點之間的最大亮度/顏色的負差 最大值。
         *          upDiff : Scalar ,在當前觀察到的像素和它的一個相鄰的像素點之間的最大亮度/顏色的正差 最大值。
         *          flags : int ,操做標識符。參看下面示例
         *
         *          通常不須要borderType,borderValue,均有默認值。若是須要使用,可參考官網獲取更多信息
         */

        Mat src = Imgcodecs.imread(this.p_test_file_path + "/caoyuan.jpg");
        // mask 單通道,8位圖,比原始圖片寬高多2像素
        Mat mask = new Mat(src.height()+2,src.width()+2 , CvType.CV_8UC1);
        Point seedPonit = new Point(30,35);
        Scalar loDiff = new Scalar(20,20,20); //沒怎麼明白,建議要了解的看下官方說明
        Scalar upDiff = new Scalar(20,20,20); //沒怎麼明白,建議要了解的看下官方說明
        /*
         * flags 此參數參看示例設置
         *
         * 分別分爲3個部分,低、高、中,參數之間用爲運算符號 "|" (按位或)連接
         *
         * 1. 低八位
         *      -- 可選值: 4 、8 ,2個選項。
         *      -- 4 只計算水平、垂直方向,8 多了個對角線方向
         * 2. 高八位
         *      -- 可選值 ,0 或 FLOODFILL_MASK_ONLY 、 FLOODFILL_FIXED_RANGE,除0外,2個常量能夠組合或單獨使用
         *      -- FLOODFILL_MASK_ONLY  不會填充原始圖,只填充 mask 。 即 newVal 參數會失效
         *      -- FLOODFILL_FIXED_RANGE 設置後:會考慮當前像素與種子像素之間的色差,不設置:會考慮當前像素與鄰近像素之間的差。
         * 3. 中八位
         *      -- 當高八位設置成 FLOODFILL_MASK_ONLY 時,因爲不填充 src,填充 mask,而 mask 爲單通道,newVal 爲 RGB,因此不適用,致使 newVal 失效。
         *          這裏就給一個值,用於填充 mask 。
         *      -- 若是爲 0 ,則 mask 會用1來填充
         *      -- 注意,非0 的狀況,中八位的值要填充高八位,須要向左位移8位。如填充 值爲38 ,38二進制位 100110 , 向左位移8位 10011000000000
         */
        int flags = 8 | Imgproc.FLOODFILL_MASK_ONLY | Imgproc.FLOODFILL_FIXED_RANGE | (38<<8);
        // flags 計算規則(位運算)
        // 原始數字                                      二進制
        // 8                                     =                    1000
        // Imgproc.FLOODFILL_MASK_ONLY = 131072  =      100000000000000000
        // Imgproc.FLOODFILL_FIXED_RANGE = 65536 =       10000000000000000
        // 38<<8 = 100110 << 8                   =          10011000000000
        //
        // 按位或("|")計算:                            110010011000001000

        Rect rect = new Rect(2,2,1,1);

        Scalar newVal = new Scalar(0,0,255);

        //這個原型方法,用參數測試了下,沒效果。不知道緣由。
        //Imgproc.floodFill(src,mask,seedPonit,newVal);

        Imgproc.floodFill(src,mask,seedPonit,newVal,rect,loDiff,upDiff,flags);
        this.saveImage(this.save_dir + "/image_process_floodfill_src.png",src);
        this.saveImage(this.save_dir + "/image_process_floodfill_mask.png",mask);

    }

    /**--------------------*
     *  第二節 pyrUp、pyrDown
     **--------------------*/

    /*
     * 在學習 pyrUp、pyrDown 以前先了解下圖像金字塔
     *
     * 圖像金字塔:
     *      圖像金字塔是圖像中多尺度表達圖像的一種,最主要的用途是圖像的分割。用分辨率來解釋圖片的的簡單結構。
     *      好比,同一張圖片:原始爲 1024x768 ,改變下: (1024x0.9)x(768x0.9)=922x691 依次類推.... 分辨率 越低,則圖像越小。
     *      用金字塔來比喻圖像分辨率,會以梯度往下采集圖像,直至達到結束條件:
     *
     * 圖像金字塔示例:
     *      金字塔的底層爲原始圖像分辨率,金字塔層級越高分辨率越低。
     *
     *          3 縮小 ---------      ...
     *          2 縮小 ---------    830x622
     *          1 縮小 ---------    922x691
     *          0 原始 ---------  1024 x 768
     *
     *      在金字塔中層級方向是: 【上】 0,1,2,3.....x 【下】
     *          -- pyrDown 向下採樣,能夠理解爲縮小
     *          -- pyrUp 向上採樣,能夠理解爲放大
     *
     *      注意:pyrDown、pyrUp 不是互逆的。 既 原始圖經過pyrDown 縮小後,再經過 pyrUp放大後的圖像像素與原始圖像像素是不同的。
     *
     * -- pyrUp 放大,對原始圖像擴大,在新增的行、列像素中,根據周圍的像素去估計可能"丟失"的像素

     */

    /**
     * 圖像向上採樣
     */
    @Test
    public void testPyrUp(){

        /*
         * 原文地址: https://docs.opencv.org/3.0-beta/modules/imgproc/doc/filtering.html?highlight=pyrup
         *
         * 原型方法
         *      pyrUp(Mat src, Mat dst, Size dstsize, int borderType)
         *      pyrUp(Mat src, Mat dst, Size dstsize)
         *      pyrUp(Mat src, Mat dst)
         *
         *      參數說明:
         *          src : Mat 輸入圖像
         *          dst : Mat 輸出圖像
         *          dstsize : Size 輸出圖像的大小,默認值 Size(src.clos*2 , src.rows*2)
         *          borderType : int 邊界模式,不明白意思,一些資料說無論便可 //Pixel extrapolation method (only BORDER_DEFAULT supported). See borderInterpolate for details.
         */

        Mat sourceImage = Imgcodecs.imread(this.p_test_file_path + "/5cent.jpg",Imgcodecs.CV_LOAD_IMAGE_COLOR);

        Mat dstImage = new Mat();
        Imgproc.pyrUp(sourceImage , dstImage);
        this.saveImage(this.save_dir + "/image_process_pyrUp_1.png",dstImage);

    }

    /**
     * 圖像向下採樣
     */
    @Test
    public void testPyrDown(){

        /*
         * 原文地址: https://docs.opencv.org/3.0-beta/modules/imgproc/doc/filtering.html?highlight=pyrdown
         *
         * 原型方法
         *      pyrDown(Mat src, Mat dst, Size dstsize, int borderType)
         *      pyrDown(Mat src, Mat dst, Size dstsize)
         *      pyrDown(Mat src, Mat dst)
         *
         *      參數說明:
         *          src : Mat 輸入圖像
         *          dst : Mat 輸出圖像
         *          dstsize : Size 輸出圖像的大小,默認值 Size((src.clos+1)/2 , (src.rows+1)/2)
         *          borderType : int 邊界模式,不明白意思,一些資料說無論便可 //Pixel extrapolation method (only BORDER_DEFAULT supported). See borderInterpolate for details.
         */

        Mat sourceImage = Imgcodecs.imread(this.p_test_file_path + "/5cent.jpg",Imgcodecs.CV_LOAD_IMAGE_COLOR);

        Mat dstImage = new Mat();
        Imgproc.pyrDown(sourceImage , dstImage);
        this.saveImage(this.save_dir + "/image_process_pyrDown_1.png",dstImage);

        /*
         *  pyrDown 採用高斯金字塔算法來向下採樣
         *  1. 對圖像進行高斯卷積
         *  2. 將全部偶數行、列去掉
         *
         *  簡單以下:
         *
         *              1 2 3 4 5 6     1 3 5
         *              2 2 3 4 5 6     3 3 5
         *              3 2 3 4 5 6
         *              4 2 3 4 5 6
         *              原始矩陣(6x4)    pyrDown 後(3x2)【去掉偶數行、列】每一個矩陣縮小了一倍
         */
    }


    /**--------------------*
     *  第三節 resize
     **--------------------*/

    /*
     * resize 是 OpenCV 裏專門用來調整圖像大小的函數
     *
     * 原型方法 :
     *      resize(Mat src, Mat dst, Size dsize)
     *      resize(Mat src, Mat dst, Size dsize, double fx, double fy, int interpolation)
     *
     * 參數說明
     *      src: 輸入,原圖像,即待改變大小的圖像
     *      dst: 輸出,改變大小以後的圖像
     *      dsize: 輸出圖像的大小 Size(width,height) ,經過 new Size(0,0) 能夠設置其值爲0,當 dsize 爲0時,按照 fx、fy 縮放圖片
     *          -- 當 dsize 爲 0 時,經過計算 fx、fy 來對圖片進行縮放,縮放的公式爲:new Size(round(fx*src.cols), round(fy*src.rows))
     *      fx: 寬度的縮放比例,當 dsize 爲 0 時才生效。
     *      fy: 高度的縮放比例,當 dsize 爲 0 時才生效。
     *      interpolation: int 插值方式,默認 INTER_LINEAR 線性插值
     *          -- INTER_LINEAR 線性插值
     *          -- INTER_NEAREST 最近鄰插值
     *          -- INTER_AREA 區域插值(利用像素區域關係的重採樣插值)
     *          -- INTER_CUBIC 三次樣條插值(超過4x4像素鄰域內的雙三次插值)
     *          -- INTER_LANCZOS4 Lanczos 插值(超過8x8像素鄰域內的Lanczos插值)
     *
     *          *** 縮小通常用 INTER_AREA,放大2種 INTER_CUBIC(效率不高,速度慢)、INTER_LANCZOS4(效率高、速度快)
     *
     * 注意:
     * dsize 與 fx,fy 分別爲2種模式,必須啓用一種。要麼自定義大小(dszie),要麼按照比例(fx,fy); 也就是說,dsize、fx、fy 3者不能同時都爲 0。
     *
     */

    /**
     * 圖像縮小
     */
    @Test
    public void testResize_small(){

        // 讀取彩色圖
        Mat sourceImage = Imgcodecs.imread(this.p_test_file_path + "/5cent.jpg",Imgcodecs.CV_LOAD_IMAGE_COLOR);

        // 定義一個比原圖小的 mat
        Mat dstImage = Mat.ones(400,400,CvType.CV_8UC3);
        // 會根據輸出圖像 dstImage的大小,自動計算出 fx,fy,進行縮放
        Imgproc.resize(sourceImage,dstImage,dstImage.size());
        this.saveImage(this.save_dir + "/image_process_resize_small_1.png",dstImage);

        // 等比縮放
        dstImage = Mat.ones(sourceImage.rows()/3,sourceImage.cols()/3,CvType.CV_8UC3);
        Imgproc.resize(sourceImage,dstImage,dstImage.size());
        this.saveImage(this.save_dir + "/image_process_resize_small_2.png",dstImage);

        // 指定比例的縮放
        dstImage = new Mat();
        Imgproc.resize(sourceImage,dstImage,new Size(0,0) , 0.2, 0.4 ,Imgproc.INTER_AREA);
        this.saveImage(this.save_dir + "/image_process_resize_small_3.png",dstImage);


        //比較插值不一樣,縮小後的效果
        Mat src1 = new Mat();
        sourceImage.copyTo(src1);

        Mat src2 = new Mat();
        sourceImage.copyTo(src2);

        Mat dstImage1 = null,dstImage2 = null;
        for(int i=0; i<3; i++){

            dstImage1 = Mat.ones(src1.rows()/2,src1.cols()/2,CvType.CV_8UC3);
            dstImage2 = Mat.ones(src2.rows()/2,src2.cols()/2,CvType.CV_8UC3);

            // 線性插值
            Imgproc.resize(src1,dstImage1,dstImage1.size(),0,0,Imgproc.INTER_LINEAR);
            // 區域插值
            Imgproc.resize(src2,dstImage2,dstImage2.size(),0,0,Imgproc.INTER_AREA);


            dstImage1.copyTo(src1);
            dstImage2.copyTo(src2);
        }

        //輸出內容能夠放大看看效果
        this.saveImage(this.save_dir + "/image_process_resize_small_Linear.png",dstImage1);
        this.saveImage(this.save_dir + "/image_process_resize_small_area.png",dstImage2);


    }

    /**
     * 圖像放大
     */
    @Test
    public void testResize_big(){

        // 讀取彩色圖
        Mat sourceImage = Imgcodecs.imread(this.p_test_file_path + "/5cent.jpg",Imgcodecs.CV_LOAD_IMAGE_COLOR);

        // 定義一個比原圖大的 mat
        Mat dstImage = Mat.ones(1000,1000,CvType.CV_8UC3);
        // 會根據輸出圖像 dstImage的大小,自動計算出 fx,fy,進行縮放
        Imgproc.resize(sourceImage,dstImage,dstImage.size());
        this.saveImage(this.save_dir + "/image_process_resize_big_1.png",dstImage);

        // 等比放大
        dstImage = Mat.ones((int)(sourceImage.rows()*1.3),(int)(sourceImage.cols()*1.3),CvType.CV_8UC3);
        Imgproc.resize(sourceImage,dstImage,dstImage.size());
        this.saveImage(this.save_dir + "/image_process_resize_big_2.png",dstImage);

        // 指定比例的放大
        dstImage = new Mat();
        Imgproc.resize(sourceImage,dstImage,new Size(0,0) , 1.3, 1.8 ,Imgproc.INTER_CUBIC);
        this.saveImage(this.save_dir + "/image_process_resize_big_3.png",dstImage);


        //比較插值不一樣,放大後的效果
        Mat src1 = new Mat();
        sourceImage.copyTo(src1);

        Mat src2 = new Mat();
        sourceImage.copyTo(src2);

        Mat src3 = new Mat();
        sourceImage.copyTo(src3);

        Mat dstImage1 = null,dstImage2 = null,dstImage3 = null;
        for(int i=0; i<6; i++){

            dstImage1 = Mat.ones((int)(src1.rows()*1.3),(int)(src1.cols()*1.3),CvType.CV_8UC3);
            dstImage2 = Mat.ones((int)(src2.rows()*1.3),(int)(src2.cols()*1.3),CvType.CV_8UC3);
            dstImage3 = Mat.ones((int)(src3.rows()*1.3),(int)(src3.cols()*1.3),CvType.CV_8UC3);

            // 線性插值
            Imgproc.resize(src1,dstImage1,dstImage1.size(),0,0,Imgproc.INTER_LINEAR);
            // 三次條插值
            Imgproc.resize(src2,dstImage2,dstImage2.size(),0,0,Imgproc.INTER_CUBIC);
            // Lanczos插值
            Imgproc.resize(src3,dstImage3,dstImage3.size(),0,0,Imgproc.INTER_LANCZOS4);

            dstImage1.copyTo(src1);
            dstImage2.copyTo(src2);
            dstImage3.copyTo(src3);
        }

        //輸出內容能夠放大看看效果,看看那種放大要平滑點
        this.saveImage(this.save_dir + "/image_process_resize_big_linear.png",dstImage1);
        this.saveImage(this.save_dir + "/image_process_resize_big_cubic.png",dstImage2);
        this.saveImage(this.save_dir + "/image_process_resize_big_lanczos.png",dstImage3);

    }

}

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

相關文章
相關標籤/搜索