各位老铁们,大家好,今天由我来为大家分享深入探索:OpenCV C++ 编程实践教程(第六部分),以及的相关问题知识,希望对大家有所帮助。如果可以帮助到大家,还望关注收藏下本站,您的支持是我们最大的动力,谢谢大家了哈,下面我们开始吧!
image.pngdouble Threshold( InputArray src, OutputArray dst, double thresh, double maxval, int type );image.png 需要注意的是,当类型为THRESH_OTSU 或THRESH_TRIANGLE 时,输入参数src 仅支持uchar 类型。此时,thresh也被用作输出参数,通过Otsu和TRIANGLE算法自动计算。
2、局部阈值分割
局部阈值分割的核心也是计算阈值矩阵。后面提到的比较常用的自适应阈值算法(也称为移动平均算法)是一种简单但高效的局部阈值算法。其核心思想是将每个像素邻域的“平均”值作为该位置的阈值。
6.2、直方图技术法
包含出现明显对比且背景为双峰的对象的图像具有包含波谷位置的直方图,其中两个峰对应于对象内部和外部的大量点,两个峰之间对应于对象的波谷边缘附近的点数量相对较少。
直方图技术是首先找到这两个峰值,然后取两个峰值之间方差对应的灰度值,这就是需要的阈值。
常见的方法是首先对直方图进行高斯平滑,并逐渐增大高斯滤波器的标准差,直到平滑后的直方图能够得到两个唯一的峰值以及它们之间唯一的最小值。但该方法需要手动调整。这是一种根据规则自动选择波峰和波谷的方法。
假设输入图像为I,高度为H,宽度为W。HistogramI表示其对应的灰度直方图,histogramI(k)表示灰度值等于k的像素点个数,其中0k255 。
步骤1:找到灰度直方图的第一个峰值,并找到其对应的灰度值。显然,灰度直方图的最大值为第一个峰值,对应的灰度值用firstPeak表示。
步骤2:找到直方图的第二个峰值,并找到其对应的灰度值。第二个峰值不一定是直方图的第二大值,因为它很可能出现在第一个峰值附近。
image.png 第3 步:找到两个峰之间的波谷。如果有两个或多个波谷,则取左边的波谷,其对应的灰度值就是阈值。 int threshTwoPeaks(const Mat 图像, Mat thresh_out)
{
//计算灰度直方图
Mat 直方图=calGrayHist(image);
//求灰度直方图最大峰值对应的灰度值
点firstPeakLoc;
minMaxLoc(直方图,NULL,NULL,NULL,firstPeakLoc);
intfirstPeak=firstPeakLoc.x;
//找到灰度直方图第二个峰值对应的灰度值
垫测量距离=Mat:zeros(Size(256, 1), CV_32FC1);
for (int k=0; k 256; k++)
{
int hist_k=histogram.at(0, k);
measureDists.at(0, k)=pow(float(k -firstPeak), 2)*hist_k;
}
点第二个PeakLoc;
minMaxLoc(measureDists, NULL, NULL, NULL, secondaryPeakLoc);
int secondaryPeak=secondaryPeakLoc.x;
//找到两者中最小值对应的灰度值作为阈值
点threshLoc;
阈值=0;
if (firstPeak secondaryPeak)//第一个峰值在第二个峰值的左侧
{
minMaxLoc(histogram.colRange(firstPeak, secondaryPeak), NULL, NULL, NULL, threshLoc);
thresh=firstPeak + threshLoc.x + 1;
}
else//第一个峰值在第二个峰值的右侧
{
minMaxLoc(histogram.colRange(第二峰值, 第一峰值), NULL, NULL, NULL, threshLoc);
thresh=secondaryPeak + threshLoc.x + 1;
}
//阈值分割
阈值(图像,thresh_out,阈值,255,THRESH_BINARY);
返回阈值;
}
6.3、熵方法
image.png 使用熵计算阈值的步骤如下:
第一步:计算I的累积概率直方图,也称为零阶累积矩,记为image.png 第二步:计算每个灰度级的熵,记为image.png 第三步:计算f (t )=f1(t) +f2(t) 最大化t值,即得到的阈值,即thresh=argtmax(f (t) ),其中image.png
6.4、Otsu阈值处理
对图像中,所选的分割阈值应最大化前景区域平均灰度级、背景区域平均灰度级和整幅图像平均灰度级之间的差异。此差异由邻域区域表示。 Otsu[2]提出了最大方差法,它是基于判别分析和最小二乘原理推导出来的。计算过程简单,是常用的稳定的阈值分割算法。
第一步:计算灰度直方图的零阶累积矩(或累积直方图)。 image.png 第二步:计算灰度直方图的一阶累积矩。 image.png 步骤3:计算图像I的整体灰度平均均值,实际上是k=255时的一阶累积距离,即image.png 步骤4:计算每个灰度级作为阈值,前景区域平均灰度级、背景区域的平均灰度级以及整幅图像的平均灰度级的方差。下面的测量用于测量方差: image.png 步骤5:找到上面提到的最大值2(k),然后对应的k就是Otsu自动选择的阈值,即image.pngint otsu(const Mat image , Mat OtsuThresh 图片)
{
//计算灰度直方图
Mat 直方图=calGrayHist(image);
//归一化灰度直方图
Mat 范数;
histogram.convertTo(normHist, CV_32FC1, 1.0/(image.rows*image.cols), 0.0);
//计算累积直方图(零阶累积距离)和一阶累积距离
Mat ZeroCumuMoment=Mat:zeros(Size(256, 1), CV_32FC1);
Mat oneCumuMoment=Mat:zeros(Size(256, 1), CV_32FC1);
for (int i=0; i 256; i++)
{
如果(我==0)
{
ZeroCumuMoment.at(0, i)=normHist.at(0, i);
oneCumuMoment.at(0, i)=normHist.at(0, i);
}
别的
{
ZeroCumuMoment.at(0, i)=normHist.at(0, i) + ZeroCumuMoment.at(0, i - 1);
oneCumuMoment.at(0,i)=normHist.at(0,i) + oneCumuMoment.at(0,i - 1);
}
}
//计算类之间的方差
矩阵方差=Mat:zeros(Size(256, 1), CV_32FC1);
//总体平均值
浮动平均值=oneCumuMoment.at(0, 255);
for (int j=0; j 256; j++)
{
if (zeroCumuMoment.at(0, j)==0 || ZeroCumuMoment.at(0, j)==1)
{//分母不能为零
方差.at(0, j)=0;
}
别的
{
方差.at float(0, j)=pow(mean*zeroCumuMoment.at(0, j) - oneCumuMoment.at(0, j), 2)/ZeroCumuMoment.at(0, j)*(1.0 - ZeroCumuMoment.at (0, j));
}
}
//找到最大值作为阈值
点最大定位点;
minMaxLoc(方差, NULL, NULL, NULL, maxLoc);
int thresh=maxLoc.x;
//阈值处理
阈值(图像,OtsuThreshImage,阈值,255,THRESH_BINARY);
返回阈值;
}
6.5、自适应阈值
在光照不均匀或者灰度值分布不均匀的情况下,如果采用全局阈值分割,得到的分割效果往往会很不理想。那么想到的策略就是为每个位置的灰度值设置一个相应的阈值,而位置阈值的设置也不可避免地与010-59000相关。
在对图像进行平滑处理时,均值平滑、高斯平滑和中值平滑使用不同的规则来计算以当前像素为中心的邻域内的灰度“平均值”,因此平滑后的输出结果可以作为各个像素设置的参考值阈值,例如将均值滤波的结果乘以一定的比例系数作为最终的阈值矩阵。
平滑算子的宽度必须大于识别对象的宽度。平滑算子的尺寸越大,平滑后的结果就越能作为每个像素阈值的参考。当然,它不可能是无限的。
第一步:对图像进行平滑处理,平滑结果记为fsmooth(I),其中fsmooth可以表示均值平滑、高斯平滑、中值平滑。步骤2:自适应阈值矩阵Thresh=(1-ratio)*fsmooth(I),一般令ratio=0.15。步骤3:利用局部阈值分割规则进行阈值分割。 Mat AdaptiveThresh(Mat I, int radius, float radio, METHOD 方法)
{
//step1: 平滑图像矩阵
垫子光滑;
开关(方法)
{
案例MEAN:
boxFilter(I, 平滑, CV_32FC1, 大小(2 * 半径+ 1, 2 * 半径+ 1));
休息;
案例GAUSS:
GaussianBlur(I, 平滑, 大小(2 * 半径+ 1, 2 * 半径+ 1), 0, 0);
休息;
案例MEDIAN:
MedianBlur(I, 平滑, 2 * 半径+ 1);
休息;
默认:
休息;
}
//step2: 将平滑结果乘以尺度系数,然后对其求导图像矩阵
I.convertTo(I, CV_32FC1);
smooth.convertTo(平滑, CV_32FC1);
Mat diff=I - (1.0 - radio)*smooth;
//step3:阈值处理,当0时,输出值为255,否则,输出值为0
Mat out=Mat:zeros(diff.size(), CV_8UC1);
for (int r=0; r I.rows; r++)
{
for (int c=0; c I.cols; c++)
{
if (diff.at(r, c) 0)
{
输出(r,c)=255;
}
}
}
返回;
}可以理解OpenCV提供的自适应阈值函数:
无效adaptiveThreshold(InputArray src,OutputArray dst,
【深入探索:OpenCV C++ 编程实践教程(第六部分)】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
期待这篇文章能解释OpenCV中一些比较高级的功能。
有19位网友表示赞同!
我一直在尝试学习OpenCV, C++的这个系列文章对我很有用!
有5位网友表示赞同!
上次看的openCV教程有点水,希望能深入讲解
有11位网友表示赞同!
OpenCV真是一套强大且常用的工具!
有8位网友表示赞同!
希望这篇文章能涵盖一些实际应用场景。
有6位网友表示赞同!
我最近想用OpenCV做图像处理项目,不知道应该从何入手。
有19位网友表示赞同!
感觉C++和OpenCV的组合确实很强大!
有17位网友表示赞同!
学习opencv一直是我今年的目标!
有15位网友表示赞同!
期待看到一些代码案例,这样更容易理解。
有5位网友表示赞同!
文章讲得详细点我才能更好地跟上进度啊。
有5位网友表示赞同!
OpenCV真是一个好用的库!
有18位网友表示赞同!
最近在做机器视觉项目,需要用到opencv!
有19位网友表示赞同!
这篇文章对我想尝试使用OpenCV的人来说太棒了!
有17位网友表示赞同!
希望这篇文章能解决我一些以前遇到的opencv的难题。
有7位网友表示赞同!
学习计算机视觉最重要的就是掌握OpenCV!
有18位网友表示赞同!
C++确实是一个适合做图像处理的语言!
有6位网友表示赞同!
我已经了解了一些OpenCV的基础知识,期待深入学习!
有16位网友表示赞同!
文章能否介绍一些比较少见的OpenCV功能?
有10位网友表示赞同!
终于找到可以帮我学习OpenCV的资源了!
有7位网友表示赞同!
希望这篇文章能讲得像我平常写代码一样简单易懂!
有16位网友表示赞同!