溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么利用openCV分割圖像

發(fā)布時間:2021-09-06 16:52:58 來源:億速云 閱讀:172 作者:chen 欄目:開發(fā)技術

這篇文章主要介紹“怎么利用openCV分割圖像”,在日常操作中,相信很多人在怎么利用openCV分割圖像問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么利用openCV分割圖像”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

本次實驗為大家分享了openCV實現(xiàn)圖像分割的具體實現(xiàn)代碼,供大家參考,具體內容如下

一.實驗目的

進一步理解圖像的閾值分割方法和邊緣檢測方法的原理。
掌握圖像基本全局閾值方法和最大類間方差法(otsu法)的原理并編程實現(xiàn)。
編程實現(xiàn)圖像的邊緣檢測。

二.實驗內容和要求

編程實現(xiàn)圖像閾值分割(基本全局閾值方法和otsu法)和邊緣檢測。

三.實驗主要儀器設備和材料

計算機,VS2017+OpenCV

四.實驗原理與方法

圖像的閾值分割的基本原理

圖像的二值化處理圖像分割中的一個主要內容,就是將圖像上的點的灰度置為0或255,也就是講整個圖像呈現(xiàn)出明顯的黑白效果。用I表示原圖,R表示二值化后的圖,則二值化的過程可以用以下公式表示:

怎么利用openCV分割圖像

thr表示選取的閾值。二值化的過程就是當原圖的像素灰度值大于閾值就將其變白,否則就將其變黑。即將256個亮度等級的灰度圖像通過適當?shù)拈y值選取而將圖像變?yōu)槎€級別灰度級,這樣只有二個灰度級的圖像在圖像處理分析過程中占有非常重要的地位,特別是在實用的圖像處理中。
根據(jù)對全圖使用統(tǒng)一閾值還是對不同區(qū)域使用不同閾值,可以分為全局閾值方法(global thresholding)和局部閾值方法(local thresholding,也叫做自適應閾值方法adaptive thresholding);這種與坐標相關的閾值也叫動態(tài)閾值,具體的方法,可以參考相關的圖像處理書籍。

1、基本全局閾值方法,即在整個圖像中所有的象素點,其閾值thr相同,具體步驟為:

(1)選取一個初始估計值T;
(2)用T分割圖像。這樣便會生成兩組像素集合:G1由所有灰度值大于T的像素組成,而G2由所有灰度值小于或等于T的像素組成。
(3)對G1和G2中所有像素計算平均灰度值u1和u2。
(4)計算新的閾值:T=(u1 + u2)/2。
(5)重復步驟(2)到(4),直到得到的T值之差小于一個事先定義的參數(shù)T0。

2、Otsu方法的算法步驟為:

(1)先計算圖像的歸一化直方圖;
(2)i表示分類的閾值,也即一個灰度級,從0開始迭代;
(3)通過歸一化的直方圖,統(tǒng)計0~i 灰度級的像素(背景像素) 所占整幅圖像的比例w0,并統(tǒng)計背景像素的平均灰度u0;統(tǒng)計i~255灰度級的像素(前景像素) 所占整幅圖像的比例w1,并統(tǒng)計前景像素的平均灰度u1;
(4)計算前景像素和背景像素的方差 g = w0w1(u0-u1) (u0-u1)
(5)i++,直到i為256時結束迭代;
(6)將最大g相應的i值作為圖像的全局閾值。

邊緣檢測

圖像中邊緣的檢測可以借助一階和二階微分實現(xiàn),常見的一階邊緣檢測算子包括Roberts算子、Prewitt算子和Sobel算子,二階算子主要是Laplacian算子,由于受噪聲影響比較大,往往在使用之前先對圖像進行平滑處理,LOG算子就是先對圖像進行高斯平滑,然后進行拉普拉斯變換并求零交叉點。Canny算子是最優(yōu)的邊緣檢測算子。

五.實驗內容

1、圖像的閾值分割:

圖像為車牌圖像,編寫代碼實現(xiàn)基本全局閾值法和Otsu法,比較分割結果。

怎么利用openCV分割圖像

怎么利用openCV分割圖像

2、邊緣檢測

用邊緣檢測算子對車牌圖像進行處理,可以用梯度算子、Laplacian算子或Canny算子(Canny算子可以直接用OpenCV函數(shù))。比較先閾值分割后邊緣檢測和直接對圖像進行邊緣檢測這兩種情況的結果是否有差別。
注意:這里提取灰度邊緣即可。

怎么利用openCV分割圖像

代碼:

#include "pch.h"
#include <iostream> 

#include <opencv2/opencv.hpp>  
using namespace std;
using namespace cv;

// 拉普拉斯銳化函數(shù)
void LaplacianSharpDeal(const Mat &src, Mat &dst) {
 if (!src.data)return;
 for (int i = 0; i < src.rows; ++i)
  for (int j = 0; j < src.cols; ++j) {
   float a;
   if (i > 1 && i < src.rows - 1 && j > 1 && j < src.cols - 1) {
    a = 5 * (float)src.at<uchar>(i, j) - (float)src.at<uchar>(i - 1, j) - (float)src.at<uchar>(i, j - 1) -
     (float)src.at<uchar>(i, j + 1) - (float)src.at<uchar>(i + 1, j);
   }
   else {//邊緣賦值
    a = src.at<uchar>(i, j);
   }
   if (a > 255 || a < 0) {
    dst.at<uchar>(i, j) = src.at<uchar>(i, j);
   }
   else {
    dst.at<uchar>(i, j) = a;
   }
  }
}

// 基本全局閾值方法函數(shù)
int BasicGlobalThreshold(Mat src, float oldValue)
{ 
 int cols = src.cols;
 int rows = src.rows;
 float G1 = 0;
 float G2 = 0;
 float g1 = 0;
 float g2 = 0;
 float u1 = 0;
 float u2 = 0;
 float T0 = 0;
 // 計算灰度直方圖分布,統(tǒng)計像素數(shù)和頻率
 for (int i = 0; i < rows; i++)
 {
  for (int j = 0; j < cols; j++)
  {
   if (src.at<uchar>(i, j) > oldValue)
   {
    G1 += src.at<uchar>(i, j);
    g1 += 1;
   }
   else
   {
    G2 += src.at<uchar>(i, j);
    g2 += 1;
   }
  }
 }
 u1 = G1 / g1;
 u2 = G2 / g2;
 T0 = (u1 + u2) / 2;
 std::cout << T0 << std::endl;
 if (abs(oldValue - T0) < 0.1) {
  return T0;
 }
 else
 {
  BasicGlobalThreshold(src, T0);
 }
}

// Otsu方法函數(shù)
int Otsu(Mat src)
{
 int cols = src.cols;
 int rows = src.rows;
 int nPixelNum = cols * rows;
 // 初始化
 int pixelNum[256];
 double probability[256];
 for (int i = 0; i < 256; i++)
 {
  pixelNum[i] = 0;
  probability[i] = 0.0;
 }
 // 統(tǒng)計像素數(shù)和頻率
 for (int i = 0; i < rows; i++)
 {
  for (int j = 0; j < cols; j++)
  {
   pixelNum[src.at<uchar>(i, j)]++;
  }
 }
 for (int i = 0; i < 256; i++)
 {
  probability[i] = (double)0.1*pixelNum[i] / nPixelNum;
 }
 // 計算
 int Threshold = 0;          // 最佳閾值
 double MaxDelta = 0.0;      // 最大類間方差
 double Mean_0 = 0.0;        // 左邊平均值
 double Mean_1 = 0.0;        // 右邊平均值
 double Delta = 0.0;         // 類間方差
 double Mean_0_temp = 0.0;   // 左邊平均值中間值
 double Mean_1_temp = 0.0;   // 右邊平均值中間值
 double Probability_0 = 0.0;       // 左邊頻率值
 double Probability_1 = 0.0;       // 右邊頻率值
 for (int j = 0; j < 256; j++)
 {
  for (int i = 0; i < 256; i++)
  {
   if (i < j)// 前半部分
   {
    Probability_0 += probability[i];
    Mean_0_temp += i * probability[i];
   }
   else      // 后半部分
   {
    Probability_1 += probability[i];
    Mean_1_temp += i * probability[i];
   }
  }
  // 計算平均值
  // Mean_0_teamp計算的是前半部分的灰度值的總和除以總像素數(shù),
  // 所以要除以前半部分的頻率才是前半部分的平均值,后半部分同樣
  Mean_0 = Mean_0_temp / Probability_0;
  Mean_1 = Mean_1_temp / Probability_1;
  Delta = (double)(Probability_0 * Probability_1 * pow((Mean_0 - Mean_1), 2));
  if (Delta > MaxDelta)
  {
   MaxDelta = Delta;
   Threshold = j;
  }
  // 相關參數(shù)歸零
  Probability_0 = 0.0;
  Probability_1 = 0.0;
  Mean_0_temp = 0.0;
  Mean_1_temp = 0.0;
  Mean_0 = 0.0;
  Mean_1 = 0.0;
  Delta = 0.0;
 }
 return Threshold;
}

void main() {
 Mat image = imread("A1.bmp", 0);
 Mat image1,image2;
 Mat image3(image.size(), image.type());
 Mat image4(image.size(), image.type());

 std::cout << "基本全局閾值方法" << std::endl;

 int OstuThreshold1 = BasicGlobalThreshold(image, 0.01);
 int OstuThreshold2 = Otsu(image);

 std::cout << "Otsu方法" << std::endl;
 std::cout << OstuThreshold2 << std::endl;
 threshold(image, image1, OstuThreshold1, 255, CV_THRESH_OTSU);
 threshold(image, image2, OstuThreshold2, 255, CV_THRESH_OTSU);

 LaplacianSharpDeal(image2, image3);
 LaplacianSharpDeal(image, image4);
 
 imshow("基本全局閾值方法", image1);
 imshow("Otsu方法", image2);
 imshow("先閾值分割后邊緣檢測", image3);
 imshow("直接對圖像進行邊緣檢測", image4);
 waitKey();
}

到此,關于“怎么利用openCV分割圖像”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注億速云網站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

AI