前景背景分割——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 parameters
    int 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大江东去浪淘尽千古风流人物

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值