OpenCVSharp4 識別物體系列之預(yù)處理

之前幾篇是OpenCV操作圖片,這不是OpenCV的賣點,賣點是計算機視覺,所謂視覺,即識別。問題場景簡單描述為
輸入:圖片
輸出:對應(yīng)的物體的位置,數(shù)量等信息

計算機識別的整體步驟大概為:
預(yù)處理
分割
特征提取
機器學(xué)習(xí)分類
后期處理

去噪

  • 鹽噪點(椒鹽類似)
            Mat lena = Cv2.ImRead("lena.jpg");

            // 添加椒鹽噪點
            Mat salt = new Mat();
            lena.CopyTo(salt);
            Random rd = new Random();
            int n = 5000;// 噪點個數(shù)
            for (int k = 0; k < n; k++)
            {
                int i = rd.Next() % salt.Cols;
                int j = rd.Next() % salt.Rows;
                salt.At<Vec3b>(j, i) = new Vec3b(255, 255, 255);
            }
lena.jpg

噪點
  • 均值濾波
    在圖像上對目標像素給一個模板,該模板包括了其周圍的臨近像素(如以目標像素為中心的周圍8個像素,構(gòu)成一個濾波模板,即包括目標像素本身),再用模板中的全體像素的平均值來代替原來像素值。
            // 均值濾波
            Mat blur = new Mat();
            Cv2.Blur(salt, blur, new Size(3, 3));
            Cv2.ImShow("Blur", blur);
均值結(jié)果
  • 中值濾波
    中值濾波法是一種非線性平滑技術(shù),它將每一像素點的灰度值設(shè)置為該點某鄰域窗口內(nèi)的所有像素點灰度值的中值.


    中值濾波
            // 中值濾波
            Mat median = new Mat();
            Cv2.MedianBlur(salt, median, 3);
            Cv2.ImShow("median", median);
中值結(jié)果
  • 高斯濾波
    高斯濾波就是對整幅圖像進行加權(quán)平均的過程,每一個像素點的值,都由其本身和鄰域內(nèi)的其他像素值經(jīng)過加權(quán)平均后得到。高斯濾波的具體操作是:用一個模板(或稱卷積、掩模)掃描圖像中的每一個像素,用模板確定的鄰域內(nèi)像素的加權(quán)平均灰度值去替代模板中心像素點的值。


    高斯
            // 高斯濾波
            Mat gaussian = new Mat();
            Cv2.GaussianBlur(salt, gaussian, new Size(3, 3), 0);// 第三個參數(shù) 必須為正奇數(shù),x,y可以不同
            Cv2.ImShow("gaussian", gaussian);
高斯結(jié)果
  • 盒式濾波


    盒式濾波1

    盒式濾波2

    如果歸一化,則和均值濾波是一樣的

            // 盒式濾波
            Mat box = new Mat();
            Cv2.BoxFilter(salt, box, MatType.CV_8UC3, new Size(5,5));
            Cv2.ImShow("box", box);
盒式結(jié)果
  • 形態(tài)學(xué) 濾波
    1. 膨脹
      膨脹(dilate)就是求局部的最大值的操作。從數(shù)學(xué)的角度就是圖像與核進行卷積
      核可以是任何形狀核大小,它擁有一個單獨定義出來的參考點,稱為錨點,可以把核視為模板或者掩碼
      數(shù)學(xué)公式


      膨脹
    2. 腐蝕
      腐蝕就是求局部最小值的操作。


      腐蝕
    3. 開閉運算
      • 開運算:先腐蝕后膨脹

      • 閉運算:先膨脹后腐蝕

      • 總結(jié):

        • 對二值化圖像:去掉像素用腐蝕,開運算;增加像素用膨脹,閉運算
        • 對灰度圖像:灰度值降低,用腐蝕,開運算;灰度值增加用膨脹,閉運算
        • 使用先開后比閉的操作,可以有效的去除噪聲
            // 形態(tài)學(xué) Open
            Mat morphotoOpen = new Mat();
            Cv2.MorphologyEx(salt, morphotoOpen, MorphTypes.Open, new Mat());
            Cv2.ImShow("morphotoOpen", morphotoOpen);
形態(tài)學(xué) 開閉 結(jié)果
  • 雙邊濾波
    雙邊濾波(Bilateral filter)是一種非線性的濾波方法,是結(jié)合圖像的空間鄰近度和像素值相似度的一種折中處理,同時考慮空域信息和灰度相似性,達到保邊去噪的目的。具有簡單、非迭代、局部的特點 [1] 。雙邊濾波器的好處是可以做邊緣保存(edge preserving),一般過去用的維納濾波或者高斯濾波去降噪,都會較明顯地模糊邊緣,對于高頻細節(jié)的保護效果并不明顯。
    雙邊濾波器顧名思義比高斯濾波多了一個高斯方差sigma-d,它是基于空間分布的高斯濾波函數(shù),所以在邊緣附近,離的較遠的像素不會太多影響到邊緣上的像素值,這樣就保證了邊緣附近像素值的保存。但是由于保存了過多的高頻信息,對于彩色圖像里的高頻噪聲,雙邊濾波器不能夠干凈的濾掉,只能夠?qū)τ诘皖l信息進行較好的濾波
            Mat bilateral = new Mat();
            Cv2.BilateralFilter(salt, bilateral, 9, 80, 80);
            Cv2.ImShow("bilateral", bilateral);
雙邊濾波 結(jié)果
  • 高斯噪點
            Mat gaussianNoise = new Mat(lena.Size(), MatType.CV_64FC1);
            RNG rng = new RNG((ulong)DateTime.Now.Ticks);
            rng.Fill(gaussianNoise, DistributionType.Normal, 0, 15);
            Mat yccImg = lena.CvtColor(ColorConversionCodes.BGR2YCrCb);
            Mat[] sigMats =Cv2.Split(yccImg);
            sigMats[0].ConvertTo(sigMats[0], MatType.CV_64FC1); 
            sigMats[0] = sigMats[0] + gaussianNoise;
            sigMats[0].ConvertTo(sigMats[0], MatType.CV_8UC1);  //Y通道加高斯噪聲后圖像,自動截斷小于零和大于255的值
            Mat gaussianImg = new  Mat(lena.Size(), MatType.CV_8UC3);   //添加高斯噪聲的圖像;
            Cv2.Merge(sigMats, gaussianImg);
            gaussianImg = gaussianImg.CvtColor(ColorConversionCodes.YCrCb2BGR);
            Cv2.ImShow("gaussianImg", gaussianImg);
高斯噪點

此處采用的YCrCb格式,然后噪點疊加在Y通道。具體機制,百度YCrCb的介紹即可。

  • 均值濾波
    均值
  • 中值濾波
    中值
  • 高斯濾波
    高斯
  • 雙邊濾波
    雙邊

對于不同的噪點,需要調(diào)整對應(yīng)的去噪方式。

去除背景

采用算法去除背景,只留下關(guān)注的信息。一張背景圖,一張當前圖,兩張圖采用減法或者除法可以去掉背景信息。

  • 減法 R=L-I
  • 除法 R=255*(1-I/L)
    此種方法并不是一個非常好的方法,后續(xù),在動態(tài)識別中會介紹更多的去除背景的方法。

二值化

二值圖像:只有兩種顏色,黑和白,1白色,0黑色

        //
        // 摘要:
        //     Applies a fixed-level threshold to each array element.
        //
        // 參數(shù):
        //   src:
        //     input array (single-channel, 8-bit or 32-bit floating point).
        //
        //   dst:
        //     output array of the same size and type as src.
        //
        //   thresh:
        //     threshold value.
        //
        //   maxval:
        //     maximum value to use with the THRESH_BINARY and THRESH_BINARY_INV thresholding
        //     types.
        //
        //   type:
        //     thresholding type (see the details below).
        //
        // 返回結(jié)果:
        //     the computed threshold value when type == OTSU
        public static double Threshold(InputArray src, OutputArray dst, double thresh, double maxval, ThresholdTypes type);
    //
    // 摘要:
    //     Thresholding type
    [Flags]
    public enum ThresholdTypes
    {
        //
        // 摘要:
        //     \f[\texttt{dst} (x,y) = \fork{\texttt{maxval}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f]
        Binary = 0,
        //
        // 摘要:
        //     \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{maxval}}{otherwise}\f]
        BinaryInv = 1,
        //
        // 摘要:
        //     \f[\texttt{dst} (x,y) = \fork{\texttt{threshold}}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f]
        Trunc = 2,
        //
        // 摘要:
        //     \f[\texttt{dst} (x,y) = \fork{\texttt{src}(x,y)}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{0}{otherwise}\f]
        Tozero = 3,
        //
        // 摘要:
        //     \f[\texttt{dst} (x,y) = \fork{0}{if \(\texttt{src}(x,y) > \texttt{thresh}\)}{\texttt{src}(x,y)}{otherwise}\f]
        TozeroInv = 4,
        Mask = 7,
        //
        // 摘要:
        //     flag, use Otsu algorithm to choose the optimal threshold value
        Otsu = 8,
        //
        // 摘要:
        //     flag, use Triangle algorithm to choose the optimal threshold value
        Triangle = 16
    }
  • 二值化方式
    • Origin


      Origin
    • Binary


      binary_math.png

      binary
    • BinaryInv


      binarayinv_math

      binarayinv.png
    • Trunc


      trunc_math

      trunc
    • Tozero


      tozero_math

      tozero

      *TozeroInv


      tozeroinv_math

      threshtozeroinv
  • 閾值取值方式
    • 手動
      Threshold中指定第三個值,即閾值
    • Otsu
      不用自己指定thresh值,系統(tǒng)會進行計算并且作為返回值返回。
      最大類間方差法是由日本學(xué)者大津于1979年提出的,是一種自適應(yīng)的閾值確定的方法,又叫大津法,簡稱OTSU。它是按圖像的灰度特性,將圖像分成背景和目標2部分。背景和目標之間的類間方差越大,說明構(gòu)成圖像的2部分的差別越大,當部分目標錯分為背景或部分背景錯分為目標都會導(dǎo)致2部分差別變小。因此,使類間方差最大的分割意味著錯分概率最小。
      • 原理
        1. 先計算圖像的直方圖,即將圖像所有的像素點按照0~255共256個bin,統(tǒng)計落在每個bin的像素點數(shù)量
        2. 歸一化直方圖,也即將每個bin中像素點數(shù)量除以總的像素點,使其限制在0~1之間
        3. 在這里設(shè)置一個分類的閾值i,也即一個灰度級,開始從0迭代
        4. 通過歸一化的直方圖,統(tǒng)計0~i 灰度級的像素(假設(shè)像素值在此范圍的像素叫做前景像素) 所占整幅圖像的比例w0,并統(tǒng)計前景像素的平均灰度u0;統(tǒng)計i~255灰度級的像素(假設(shè)像素值在此范圍的像素叫做背景像素) 所占整幅圖像的比例w1,并統(tǒng)計背景像素的平均灰度u1;在這里,設(shè)圖像的總平均灰度為u2,類間方差記為g。
          其中:
          u2=ω0?u0+ω1?u1,g=ω0(u0?μ2)2+ω1(u1?u2)2
          將u2帶入g中,可得:
          g=ω0ω1(u0?u1)2
        5. ++i,閾值的灰度值加1,并轉(zhuǎn)到第4個步驟,直到i為256時結(jié)束迭代
        6. 將最大g相應(yīng)的<script type="math/tex" id="MathJax-Element-948">i</script>值作為圖像的全局閾值

參照:https://blog.csdn.net/qq_29462849/article/details/81022607

  • Triangle
    不用自己指定thresh值,系統(tǒng)會進行計算并且作為返回值返回。
    最適用于單個波峰,最開始用于醫(yī)學(xué)分割細胞等。
    • 原理:
      1. 圖像轉(zhuǎn)灰度
      2. 計算圖像灰度直方圖
      3. 尋找直方圖中兩側(cè)邊界
      4. 尋找直方圖最大值
      5. 檢測是否最大波峰在亮的一側(cè),否則翻轉(zhuǎn)
      6. 計算閾值得到閾值T,如果翻轉(zhuǎn)則255-T

參照https://blog.csdn.net/qq_41498261/article/details/102770535

  • 測試:
    • 手動
      • 代碼
           Mat lena = Cv2.ImRead("lena.jpg", ImreadModes.Grayscale);
           Cv2.ImShow("lena", lena);

           Mat Binary = new Mat();
           Cv2.Threshold(lena, Binary, 140, 255, ThresholdTypes.Binary);
           Cv2.ImShow("Binary", Binary);

           Mat BinaryInv = new Mat();
           Cv2.Threshold(lena, BinaryInv, 140, 255, ThresholdTypes.BinaryInv);
           Cv2.ImShow("BinaryInv", BinaryInv);

           Mat Tozero = new Mat();
           Cv2.Threshold(lena, Tozero, 140, 255, ThresholdTypes.Tozero);
           Cv2.ImShow("Tozero", Tozero);

           Mat TozeroInv = new Mat();
           Cv2.Threshold(lena, TozeroInv, 140, 255, ThresholdTypes.TozeroInv);
           Cv2.ImShow("TozeroInv", TozeroInv);

           Mat Trunc = new Mat();
           Cv2.Threshold(lena, Trunc, 140, 255, ThresholdTypes.Trunc);
           Cv2.ImShow("Trunc", Trunc);

           Cv2.WaitKey();
  • 結(jié)果


    lena.jpg

    binray.jpg

    binaryinv.jpg

    trunc.jpg

    tozero.jpg

    tozeroinv.jpg
  • OSTU
    • 代碼
            Mat lena = Cv2.ImRead("lena.jpg", ImreadModes.Grayscale);
            Cv2.ImShow("lena", lena);

            Mat Binary = new Mat();
            double thresh = Cv2.Threshold(lena, Binary, 140, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
            Console.WriteLine(thresh);
            Cv2.ImShow("Binary", Binary);

            Mat BinaryInv = new Mat();
            thresh = Cv2.Threshold(lena, BinaryInv, 140, 255, ThresholdTypes.BinaryInv | ThresholdTypes.Otsu);
            Console.WriteLine(thresh);
            Cv2.ImShow("BinaryInv", BinaryInv);

            Mat Tozero = new Mat();
            thresh = Cv2.Threshold(lena, Tozero, 140, 255, ThresholdTypes.Tozero | ThresholdTypes.Otsu);
            Console.WriteLine(thresh);
            Cv2.ImShow("Tozero", Tozero);

            Mat TozeroInv = new Mat();
            thresh = Cv2.Threshold(lena, TozeroInv, 140, 255, ThresholdTypes.TozeroInv | ThresholdTypes.Otsu);
            Console.WriteLine(thresh);
            Cv2.ImShow("TozeroInv", TozeroInv);

            Mat Trunc = new Mat();
            thresh = Cv2.Threshold(lena, Trunc, 140, 255, ThresholdTypes.Trunc | ThresholdTypes.Otsu);
            Console.WriteLine(thresh);
            Cv2.ImShow("Trunc", Trunc);

            Cv2.WaitKey();
  • 效果


    lena

    binray

    binaryinv

    trunc

    tozero

    tozeroinv

    console
    • Triangle
      代碼與OSTU基本一致,只是第5個參數(shù)后面是| ThresholdTypes.Otsu
      其輸出值為114
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容