相信刚学习opencv的朋友们,都接触到了threshold函数函数参数介绍:
InputArray src,源图像
OutputArray dst,输出图像
double thresh,门限值
double maxval,最大值
int type,函数类型选择,THRESH_BINARY,THRESH_BINARY_INV,THRESH_TRUNC,THRESH_TOZERO,THRESH_TOZERO_INV
这里,我们要自己设定阈值,如果我们图片是多样性,那么很可能有的图片取到的是全黑或者全白,不是我们预期的结果。那么此时adaptiveThreshold就帮我们解决了这个问题。依旧看看原型
opencv中adaptiveThreshold函数分析: 参数: _src 要二值化的灰度图 _dst 二值化后的图 maxValue 二值化后要设置的那个值 method 块计算的方法(ADAPTIVE_THRESH_MEAN_C 平均值,ADAPTIVE_THRESH_GAUSSIAN_C 高斯分布加权和) type 二值化类型(CV_THRESH_BINARY 大于为最大值,CV_THRESH_BINARY_INV 小于为最大值) blockSize 块大小(奇数,大于1) delta 差值(负值也可以) 源码和注释如下:
/** @brief 自适应二值化
*@param _src 要二值化的灰度图
*@param _dst 二值化后的图
*@param maxValue 二值化后要设置的那个值
*@param method 块计算的方法(ADAPTIVE_THRESH_MEAN_C 平均值,ADAPTIVE_THRESH_GAUSSIAN_C 高斯分布加权和)
*@param type 二值化类型(CV_THRESH_BINARY 大于为最大值,CV_THRESH_BINARY_INV 小于为最大值)
*@param blockSize 块大小(奇数,大于1)
*@param delta 差值(负值也可以)
*/
void cv::adaptiveThreshold(InputArray _src, OutputArray _dst, double maxValue,
int method, int type, int blockSize, double delta)
{
Mat src = _src.getMat();
// 原图必须是单通道无符号8位
CV_Assert(src.type() == CV_8UC1);
// 块大小必须大于1,并且是奇数
CV_Assert(blockSize % 2 == 1 && blockSize > 1);
Size size = src.size();
// 构建与原图像相同的图像
_dst.create(size, src.type());
Mat dst = _dst.getMat();
if (maxValue < 0)
{
// 二值化后值小于0,图像都为0
dst = Scalar(0);
return;
}
// 用于比较的值
Mat mean;
if (src.data != dst.data)
mean = dst;
if (method == ADAPTIVE_THRESH_MEAN_C)
// 计算平均值作为比较值
boxFilter(src, mean, src.type(), Size(blockSize, blockSize),
Point(-1, -1), true, BORDER_REPLICATE);
else if (method == ADAPTIVE_THRESH_GAUSSIAN_C)
// 计算高斯分布和作为比较值
GaussianBlur(src, mean, Size(blockSize, blockSize), 0, 0, BORDER_REPLICATE);
else
CV_Error(CV_StsBadFlag, "Unknown/unsupported adaptive threshold method");
int i, j;
// 将maxValue夹到[0,255]的uchar范围区间,用作二值化后的值
uchar imaxval = saturate_cast<uchar>(maxValue);
// 根据二值化类型计算delta值
int idelta = type == THRESH_BINARY ? cvCeil(delta) : cvFloor(delta);
// 计算生成每个像素差对应的值表格,以后查表就可以。但像素差范围为什么是768,我确实认为512已经够了
uchar tab[768];
if (type == CV_THRESH_BINARY)
for (i = 0; i < 768; i++)
// i = src[j] - mean[j] + 255
// i - 255 > -idelta ? imaxval : 0
// = src[j] - mean[j] + 255 -255 > -idelta ? imaxval : 0
// = src[j] > mean[j] - idelta ? imaxval : 0
tab[i] = (uchar)(i - 255 > -idelta ? imaxval : 0);
else if (type == CV_THRESH_BINARY_INV)
for (i = 0; i < 768; i++)
// i = src[j] - mean[j] + 255
// i - 255 <= -idelta ? imaxval : 0
// = src[j] - mean[j] + 255 - 255 <= -idelta ? imaxval : 0
// = src[j] <= mean[j] - idelta ? imaxval : 0
tab[i] = (uchar)(i - 255 <= -idelta ? imaxval : 0);
else
CV_Error(CV_StsBadFlag, "Unknown/unsupported threshold type");
// 如果连续,加速运算
if (src.isContinuous() && mean.isContinuous() && dst.isContinuous())
{
size.width *= size.height;
size.height = 1;
}
// 逐像素计算src[j] - mean[j] + 255,并查表得到结果
for (i = 0; i < size.height; i++)
{
const uchar* sdata = src.data + src.step*i;
const uchar* mdata = mean.data + mean.step*i;
uchar* ddata = dst.data + dst.step*i;
for (j = 0; j < size.width; j++)
// 将[-255, 255] 映射到[0, 510]然后查表
ddata[j] = tab[sdata[j] - mdata[j] + 255];
}
}这个函数有个特点,就是它的结果是白底黑轮廓,如果不是你想要的可以进行黑白反转,用下面函数。
void CNumOcr::matReverse(Mat& src) {
uchar* data = (uchar *) src.data;
int step = src.step / sizeof(uchar);
for (int i = 0; i < src.rows; i++)
for (int j = 0; j < src.cols; j++)
data[i * step + j] = 255 - data[i * step + j];
}最终,我们看看对应的效果图
AdaptiveThreshold效果图:

threshold效果图:

还是可以看出来,自动阈值的内容偏细。当然了,threshold虽然粗,但是阈值是在80及255的范围,如果是其它值,那结果又不一样了。
收藏的用户(0) X
正在加载信息~
推荐阅读
最新回复 (0)
站点信息
- 文章2313
- 用户1336
- 访客11759589
每日一句
Happiness depends on your mindset.
幸福取决于你的心态。
幸福取决于你的心态。
新会员