OpenCV3 4.8 用积分图像创建二值图

创建二值图

  • 对于一张黑白纸张图片,想要提取图片中的文字部分并创建二值图有几种常见的方法:使用固定阈值、使用自适应阈值等等。下面将依次介绍各类方法的优劣。
    在这里插入图片描述

1.使用固定阈值cv::threshold

cv::Mat image = cv::imread("book.jpg", 0);

//图像逆时针旋转90°(先转置,在水平翻转)
cv::transpose(image, image);
cv::flip(image, image, 0);

//显示最初图像
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);

//固定阈值70
cv::Mat binaryFixed;
cv::threshold(image, binaryFixed, 70, 255, cv::THRESH_BINARY);
cv::namedWindow("Fixed Threshold");
cv::imshow("Fixed Threshold", binaryFixed);

在这里插入图片描述

效果一般,无论选用什么阈值,图像都会丢失一部分文本,因此我们引入自适应阈值

2.(推荐)使用自适应阈值cv::adaptiveThreshold

cv::Mat binaryAdaptive;
int blockSize = 21;  //邻域的尺寸
int threshold = 10;  //像素与邻域的平均值进行比较

//自适应阈值化函数
cv::adaptiveThreshold(image,  //源图像
	binaryAdaptive,  //输出二值图像
	255,             //输出的最大值
	cv::ADAPTIVE_THRESH_MEAN_C, //方法(局部平均值)
	cv::THRESH_BINARY, //阈值类型
	blockSize,       //邻域大小
	threshold);      //使用的阈值
	
cv::namedWindow("Adaptive Threshold");
cv::imshow("Adaptive Threshold", binaryAdaptive);

在这里插入图片描述

效果很好。本例阈值化方法选用局部平均值,在下面的补充中会介绍高斯加权累计值

实测消耗时间0.0063683秒。

3.用积分图像编写自适应阈值

cv::Mat binary = image.clone();

int nl = binary.rows; //行数
int nc = binary.cols; //每行的元素数

//计算积分图像
cv::Mat iimage;
cv::integral(image, iimage, CV_32S);

//逐行
int halfSize = blockSize / 2;
for (int j = halfSize; j < nl - halfSize - 1; j++) {

	//得到第j行的首地址
	uchar* data = binary.ptr<uchar>(j);
	//得到积分图像上方j - halfSize行的首地址
	int* idata1 = iimage.ptr<int>(j - halfSize);
	//得到积分图像下方j + halfSize + 1行的首地址
	int* idata2 = iimage.ptr<int>(j + halfSize + 1);

	//每行的所有像素
	for (int i = halfSize; i < nc - halfSize - 1; i++) {

		//计算累加值
		int sum = (idata2[i + halfSize + 1] - idata2[i - halfSize] -
			idata1[i + halfSize + 1] + idata1[i - halfSize]) / (blockSize * blockSize);

		//应用自适应阈值
		if (data[i] < (sum - threshold))
			data[i] = 0;
		else
			data[i] = 255;
	}
}

//添加白边
	for (int j = 0; j < halfSize; j++) {
		uchar* data = binary.ptr<uchar>(j);

		for (int i = 0; i < binary.cols; i++) {
			data[i] = 255;
		}
	}
	for (int j = binary.rows - halfSize - 1; j < binary.rows; j++) {
		uchar* data = binary.ptr<uchar>(j);

		for (int i = 0; i < binary.cols; i++) {
			data[i] = 255;
		}
	}
	for (int j = halfSize; j < nl - halfSize - 1; j++) {
		uchar* data = binary.ptr<uchar>(j);

		for (int i = 0; i < halfSize; i++) {
			data[i] = 255;
		}
		for (int i = binary.cols - halfSize - 1; i < binary.cols; i++) {
			data[i] = 255;
		}
	}

cv::namedWindow("Adaptive Threshold (integral)");
cv::imshow("Adaptive Threshold (integral)", binary);

在这里插入图片描述

由于算法没有包括白边修理,最后手动添加了白边。效果与方法2几乎一模一样,就是通过计算邻域平均值和阈值来比较。

实测消耗时间0.001875秒。

4.利用图像运算符编写自适应阈值化

cv::Mat filtered;
cv::Mat binaryFiltered;

//cv::boxFilter计算矩形区域内像素的平均值
cv::boxFilter(image, filtered, CV_8U, cv::Size(blockSize, blockSize));
//检查像素是否大于(mean + threshold)
binaryFiltered = image >= (filtered - threshold);

cv::namedWindow("Adaptive Threshold (filtered)");
cv::imshow("Adaptive Threshold (filtered)", binaryFiltered);

在这里插入图片描述

本算法使用cv::boxFilter计算矩形区域内像素的平均值,再通过>=号生成二值图,也非常巧妙,但运算较慢。

实测消耗时间0.0140532秒。

补充:cv::transpose函数的使用

函数签名

CV_EXPORTS_W void transpose(InputArray src, OutputArray dst);

参数很简单,分别为:输入矩阵,输出矩阵。

函数用于图像的转置(即图像矩阵转置),配合cv::flip可实现图像的旋转。如:

//图像逆时针旋转90°(先转置,再水平翻转)
cv::transpose(image, image);
cv::flip(image, image, 0);

//图像顺时针旋转90°(先转置,再垂直翻转)
cv::transpose(image, image);
cv::flip(image, image, 1);

//图像旋转180°(先水平翻转,再垂直翻转)
cv::flip(image, image, -1);

cv::adaptiveThreshold函数的使用

函数签名

CV_EXPORTS_W void adaptiveThreshold( InputArray src, OutputArray dst,
                                     double maxValue, int adaptiveMethod,
                                     int thresholdType, int blockSize, double C );

参数分别为:输入矩阵,输出二值图像,输出的最大值,方法,阈值类型,邻域大小,使用的阈值。
函数用于使用自适应阈值生成二值图。本例中的函数还可以使用高斯(Gaussian)加权累计值,方法的标志位cv::ADAPTIVE_THRESH_GAUSSIAN_C
在这里插入图片描述

cv::integral函数的使用

函数签名

CV_EXPORTS_W void integral( InputArray src, OutputArray sum, int sdepth = -1 );

参数分别为:输入矩阵,输出积分图像(与源图像通道数相同),depth类型(常用CV_32S)。

积分图像可用于非常便利的统计矩形框内的像素和,只需要先计算出积分图像,再经过简单的加减法即可得出结果。
在这里插入图片描述

cv::boxFilter函数的使用

函数签名

CV_EXPORTS_W void boxFilter( InputArray src, OutputArray dst, int ddepth,
                             Size ksize, Point anchor = Point(-1,-1),
                             bool normalize = true,
                             int borderType = BORDER_DEFAULT );

参数分别为:输入矩阵,输出的邻域平均值矩阵,depth类型,邻域大小cv::Size,后面默认即可。
函数用于生成邻域平均值矩阵,后期可通过运算符生成二值图

cv::sum函数的使用

函数签名

CV_EXPORTS_AS(sumElems) Scalar sum(InputArray src);

参数即为输入图像。

函数用于得到图像像素和。函数返回cv::Scalar类型向量,如果输入图为灰度图,返回的向量只有第一个像素不为0,其余均为0;如果输入图为彩色图,返回向量各元素表示各通道的像素和。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值