前景背景分割——ostu算法的原理及实现 OpenCV (八)
OpenCV 【八】——前景背景分割——ostu算法的原理及實現
- 實驗結果
- 代碼實現
- 實現原理
- 參考資料
實驗結果
代碼實現
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
//計算圖像灰度直方圖
Mat calcgrayhist(const Mat& image)
{Mat histogram = Mat::zeros(Size(256, 1), CV_32SC1);//圖像寬高int rows = image.rows;int cols = image.cols;for (int i = 0; i < rows; i++){for (int j = 0; j < rows; j++){int index = int(image.at<uchar>(i, j));histogram.at<int>(0, index) += 1;}}return histogram;
};//OTSU
int OTSU(const Mat& image, Mat& OTSU_image)
{int rows = image.rows;int cols = image.cols;Mat histogram = calcgrayhist(image);//歸一化直方圖Mat normhist;histogram.convertTo(normhist, CV_32FC1, 1.0 / (rows * cols), 0.0);//計算累加直方圖和一階累積矩Mat zeroaccumulate = Mat::zeros(Size(256, 1), CV_32FC1);Mat oneaccumulate = Mat::zeros(Size(256, 1), CV_32FC1);for (int i = 0; i < 256; i++){if (i == 0){zeroaccumulate.at<float>(0, i) = normhist.at<float>(0, i);oneaccumulate.at<float>(0, i) = i * normhist.at<float>(0, i);}else{zeroaccumulate.at<float>(0, i) = zeroaccumulate.at<float>(0, i - 1)+ normhist.at<float>(0, i);oneaccumulate.at<float>(0, i) = oneaccumulate.at<float>(0, i - 1)+ i * normhist.at<float>(0, i);}}//計算間類方差Mat variance = Mat::zeros(Size(256, 1), CV_32FC1);float mean = oneaccumulate.at<float>(0, 255);for (int i = 0; i < 255; i++){if (zeroaccumulate.at<float>(0, i) == 0 || zeroaccumulate.at<float>(0, i) == 1)variance.at<float>(0, i) = 0;else{float cofficient = zeroaccumulate.at<float>(0, i) * (1.0 -zeroaccumulate.at<float>(0, i));variance.at<float>(0, i) = pow(mean * zeroaccumulate.at<float>(0, i)- oneaccumulate.at<float>(0, i), 2.0) / cofficient;}}//找到閾值;Point maxloc;//計算矩陣中最大值minMaxLoc(variance, NULL, NULL, NULL, &maxloc);int otsuthreshold = maxloc.x;threshold(image, OTSU_image, otsuthreshold, 255, THRESH_BINARY);return otsuthreshold;
};int main()
{Mat img, gray_img, dst;img = imread("E:\\workspace\\opencv_study\\example\\1.jpg");imshow("img", img);cvtColor(img, gray_img, COLOR_BGR2GRAY);imwrite("1_gray.bmp", gray_img);imshow("gray_img", gray_img);OTSU(gray_img, dst);imshow("otsu image", dst);imwrite("ostu.bmp", dst);waitKey(0);return 0;
};
實現原理
Otsu算法(大津法或最大類間方差法)使用的是聚類的思想,把圖像的灰度數按灰度級分成2個部分,使得兩個部分之間的灰度值差異最大,每個部分之間的灰度差異最小,通過方差的計算來尋找一個合適的灰度級別來劃分。 所以可以在二值化的時候采用otsu算法來自動選取閾值進行二值化。otsu算法被認為是圖像分割中閾值選取的最佳算法,計算簡單,不受圖像亮度和對比度的影響。因此,使類間方差最大的分割意味著錯分概率最小。
從L個灰度級遍歷t,使得t為某個值的時候,前景和背景的方差最大, 則 這個 t 值便是我們要求得的閾值。
其中,方差的計算公式如下:
g=wo * (uo - u) * (uo - u) + w1 * (u1 - u) * (u1 - u)
[ 此公式計算量較大,可以采用:
g = wo * w1 * (uo - u1) * (uo - u1) ]
//實現方法1:
Source Code: https://blog.csdn.net/silent_gods/article/details/81046919
//將OTSU算法實現了一遍#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"#include <string>
#include <stdio.h>using namespace std;
using namespace cv;//#define Gamma 3//OTSU 函數實現
int OTSU(Mat srcImage)
{int nCols = srcImage.cols;int nRows = srcImage.rows;int threshold = 0;//init the parametersint nSumPix[256];float nProDis[256];for (int i = 0; i < 256; i++){nSumPix[i] = 0;nProDis[i] = 0;}//統計灰度集中每個像素在整幅圖像中的個數for (int i = 0; i < nRows; i++){for (int j = 0; j < nCols; j++){nSumPix[(int)srcImage.at<uchar>(i, j)]++;}}//計算每個灰度級占圖像中的概率分布for (int i = 0; i < 256; i++){nProDis[i] = (float)nSumPix[i] / (nCols*nRows);}//遍歷灰度級【0,255】,計算出最大類間方差下的閾值float w0, w1, u0_temp, u1_temp, u0, u1, delta_temp;double delta_max = 0.0;for (int i = 0; i < 256; i++){//初始化相關參數w0 = w1 = u0 = u1 = u0_temp = u1_temp = delta_temp = 0;for (int j = 0; j < 256; j++){//背景部分if (j <= i){w0 += nProDis[j];u0_temp += j*nProDis[j];}//前景部分else{w1 += nProDis[j];u1_temp += j*nProDis[j];}}//計算兩個分類的平均灰度u0 = u0_temp / w0;u1 = u1_temp / w1;//依次找到最大類間方差下的閾值delta_temp = (float)(w0*w1*pow((u0 - u1), 2)); //前景與背景之間的方差(類間方差)if (delta_temp > delta_max){delta_max = delta_temp;threshold = i;}}return threshold;
}int main()
{namedWindow("srcGray", 0);cvResizeWindow("srcGray", 640, 480);namedWindow("otsuResultImage", 0);cvResizeWindow("otsuResultImage", 640, 480);namedWindow("dst", 0);cvResizeWindow("dst", 640, 480);//圖像讀取及判斷Mat srcImage;srcImage = imread("D:\\0604.png");if (!srcImage.data){return -1;}imshow("srcImage", srcImage);Mat srcGray;cvtColor(srcImage, srcGray, CV_RGB2GRAY);imshow("srcGray", srcGray);//調用otsu算法得到圖像int otsuThreshold = OTSU(srcGray);cout << otsuThreshold << endl;//定義輸出結果圖像Mat otsuResultImage = Mat::zeros(srcGray.rows, srcGray.cols, CV_8UC1);//利用得到的閾值進行二值化操作for (int i = 0; i < srcGray.rows; i++){for (int j = 0; j < srcGray.cols; j++){//cout << (int)srcGray.at<uchar>(i, j) << endl;//高像素閾值判斷if (srcGray.at<uchar>(i, j) > otsuThreshold){otsuResultImage.at<uchar>(i, j) = 255;}else{otsuResultImage.at<uchar>(i, j) = 0;}//cout <<(int)otsuResultImage.at<uchar>(i, j) << endl;}}imshow("otsuResultImage", otsuResultImage);waitKey(0);return 0;
}
參考資料
https://www.cnblogs.com/moon1992/p/5092726.html
https://blog.csdn.net/silent_gods/article/details/81046919
https://blog.csdn.net/u012005313/article/details/51945075
https://blog.csdn.net/liuzhuomei0911/article/details/51440305
https://blog.csdn.net/xiachong27/article/details/80572469
https://blog.csdn.net/u011574296/article/details/72829925
https://baike.baidu.com/item/otsu/16252828?fr=aladdin
https://www.cnblogs.com/uestc-mm/p/5366908.html
https://blog.csdn.net/baimafujinji/article/details/50629103
總結
以上是生活随笔為你收集整理的前景背景分割——ostu算法的原理及实现 OpenCV (八)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 有谁知道正宗的卤猪蹄子怎么做吗?
- 下一篇: MATLAB【二】————图像做减法,批