赞
踩
3、说一下大津阈值/OTSU阈值(最大类间方差法)。
最大类间方差法是由日本学者大津于1979年提出的,是一种自适应的阈值确定的方法,又叫大津法,简称OTSU。它是按图像的灰度特性,将图像分成背景和目标2部分。背景和目标之间的类间方差越大,说明构成图像的2部分的差别越大,当部分目标错分为背景或部分背景错分为目标都会导致2部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。
对于大小为 M×N 的图像I(x,y),前景(即目标)和背景的分割阈值记作T,属于前景的像素点数占整幅图像的比例记为ω0,其平均灰度μ0;背景像素点数占整幅图像的比例为ω1,其平均灰度为μ1。图像的总平均灰度记为μ,类间方差记为g。
图像中像素的灰度值小于阈值T的像素个数记作N0,像素灰度大于阈值T的像素个数记作N1,则有:
ω0=N0/ M×N (1)
ω1=N1/ M×N (2)
N0+N1=M×N (3)
ω0+ω1=1 (4)
μ=ω0*μ0+ω1*μ1 (5)
g=ω0(μ0-μ)^2+ω1(μ1-μ)^2 (6)
OTSU的公式如下,如果当前阈值为t,
w0:前景点所占比例
w1:背景点所占比例,w1 = 1- w0
u0:前景灰度均值
u1:背景灰度均值
u:全局灰度均值,u = w0*u0 + w1*u1
g:类间最大方差,g = w0(u0-u)*(u0-u) + w1(u1-u)*(u1-u) = w0*(1 – w0)*(u0 - u1)* (u0 - u1)
目标函数为 g,g 越大,t 就是越好的阈值。为什么采用这个函数作为判别依据,直观是这个函数反映了前景和背景的差值,
差值越大,前景与背景差别越大,阈值越好。
- /// <summary>
- /// 返回OTSU阈值
- /// </summary>
- /// <param name="src"></单通道灰度图像>
- /// <param name="width"></图像的宽>
- /// <param name="height"></图像的高>
- /// <returns></returns>
- int Otsu_byte(const uchar* src, const int width, const int height)
- {
- int i = 0;
- float histogram[256];
- //unsigned char* temp=new uchar[width*height];
-
- for (i = 0; i < 256; i++)
- {
- histogram[i] = 0;
- }
-
- //histogram
- for (int i = 0; i < height; i++)
- {
- for (int j = 0; j < width; j++)
- {
- histogram[src[i * width + j]]++;//统计灰度直方图.
- }
- }
- //normalize histogram
- int size = height * width;
- for (int i = 0; i < 256; i++)
- {
- histogram[i] = histogram[i] / size;//直方图归一化.
- }
-
- //average pixel value
- float avgValue = 0;
- for (int i = 0; i < 256; i++)
- {
- avgValue += i * histogram[i];//整幅图像的平均灰度.
- }
-
- int threshold;
- float maxVariance = 0;
- float w = 0, u = 0;
- for (int i = 0; i < 256; i++)
- {
- w += histogram[i];//假设当前灰度i为阈值, 0~i 灰度的像素(假设像素值在此范围的像素叫做前景像素) 所占整幅图像的比例.
- u += i * histogram[i];// 灰度i 之前的像素(0~i)的平均灰度值:前景像素的平均灰度值.
-
- float t = avgValue * w - u;
- float variance = t * t / (w * (1 - w));
- if (variance > maxVariance)
- {
- maxVariance = variance;
- threshold = i;
- }
- }
-
- //delete temp;
- return threshold;
- }
基于OpenCV 的IplImage:
- //OTSU阈值
- int Otsu(IplImage* src)
- {
- int height=src->height;
- int width=src->width;
-
- //histogram
- float histogram[256] = {0};
- for(int i=0; i < height; i++)
- {
- unsigned char* p=(unsigned char*)src->imageData + src->widthStep * i;
- for(int j = 0; j < width; j++)
- {
- histogram[*p++]++;//统计灰度直方图.
- }
- }
- //normalize histogram
- int size = height * width;
- for(int i = 0; i < 256; i++)
- {
- histogram[i] = histogram[i] / size;//直方图归一化.
- }
-
- //average pixel value
- float avgValue=0;
- for(int i=0; i < 256; i++)
- {
- avgValue += i * histogram[i];//整幅图像的平均灰度.
- }
-
- int threshold;
- float maxVariance=0;
- float w = 0, u = 0;
- for(int i = 0; i < 256; i++)
- {
- w += histogram[i];//假设当前灰度i为阈值, 0~i 灰度的像素(假设像素值在此范围的像素叫做前景像素) 所占整幅图像的比例.
- u += i * histogram[i];// 灰度i 之前的像素(0~i)的平均灰度值:前景像素的平均灰度值.
-
- float t = avgValue * w - u;
- float variance = t * t / (w * (1 - w) );
- if(variance > maxVariance)
- {
- maxVariance = variance;
- threshold = i;
- }
- }
-
- return threshold;
- }
基于OpenCV的Mat:
- #include <opencv2/opencv.hpp>
- #include <cv.h>
- #include <highgui.h>
- #include <cxcore.h>
-
- using namespace std;
- using namespace cv;
-
- int otsuGray(const Mat src)
- {
- Mat img = src;
- int c = img.cols; //图像列数
- int r = img.rows; //图像行数
- int T = 0; //阈值
- uchar* data = img.data; //数据指针
- int ftNum = 0; //前景像素个数
- int bgNum = 0; //背景像素个数
- int N = c*r; //总像素个数
- int ftSum = 0; //前景总灰度值
- int bgSum = 0; //背景总灰度值
- int graySum = 0;
- double w0 = 0; //前景像素个数占比
- double w1 = 0; //背景像素个数占比
- double u0 = 0; //前景平均灰度
- double u1 = 0; //背景平均灰度
- double Histogram[256] = {0}; //灰度直方图
- double temp = 0; //临时类间方差
- double g = 0; //类间方差
-
- //灰度直方图
- for(int i = 0; i < r ; i ++)
- {
- for(int j = 0; j <c; j ++)
- {
- Histogram[img.at<uchar>(i,j)]++;
- }
- }
- //求总灰度值
- for(int i = 0; i < 256; i ++)
- {
- graySum += Histogram[i]*i;
- }
-
- for(int i = 0; i < 256; i ++)
- {
- ftNum += Histogram[i]; //阈值为i时前景个数
- bgNum = N - ftNum; //阈值为i时背景个数
- w0 = (double)ftNum/N; //前景像素占总数比
- w1 = (double)bgNum/N; //背景像素占总数比
- if(ftNum == 0) continue;
- if(bgNum == 0) break;
- //前景平均灰度
- ftSum += i*Histogram[i];
- u0 = ftSum/ftNum;
-
- //背景平均灰度
- bgSum = graySum - ftSum;
- u1 = bgSum/bgNum;
-
- g = w0*w1*(u0-u1)*(u0-u1);
- if(g > temp)
- {
- temp = g;
- T = i;
- }
- }
-
- return T
-
- /* for(int i=0; i<img.rows; i++)
- {
- for(int j=0; j<img.cols; j++)
- {
- if((int)img.at<uchar>(i,j)>T)
- img.at<uchar>(i,j) = 255;
- else
- img.at<uchar>(i,j) = 0;
- }
- }
- return img;*/
- }
- //【此处代码来自:https://blog.csdn.net/a153375250/article/details/50970104?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-1&spm=1001.2101.3001.4242】
OpenCV中已有实现该算法,亦可直接调用。
- int value1 = 0;
- cv::Mat dst;
- cv::Mat src = cv::imread("..\\testPicture\\001.jpg", cv::IMREAD_GRAYSCALE);
-
- value1 = cv::threshold(src, dst, 0,255, CV_THRESH_OTSU);//返回OTSU阈值
类间方差法对噪音和目标大小十分敏感,它仅对类间方差为单峰的图像产生较好的分割效果。
当目标与背景的大小比例悬殊时,类间方差准则函数可能呈现双峰或多峰,此时效果不好,但是类间方差法是用时最少的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。