一种基于Opencv文档图像增强算法的实现


因为项目需要对文档图像进行增强,也就是对于模糊、亮度偏暗或不均匀的文档进行处理方便后续的识别。传图图像增强方法主要分为两方面:空间域和频域。空间阈中增强方法,颜色的增强,如:直方图均衡化,对比度以及gama增强等;模糊,如:均值滤波等;锐化,如:局部标准差实现对比度增强。频域方法,如:小波变换,在图像的某个变换域内,对图像的变换系数进行运算,然后通过逆变换获得图像增强效果。一般来说,对于实际项目中,可能用其中一种或几种方法来进行图像增强效果一般会很差,我们更多的是对这些算法进行融合,来达到我们想要的效果。

1、基于划分模式的图像增强

假设我们有图像A、B,分别为同一场景在不一样的光照拍摄图片,其中A表示基色 ,B表示混合色。那么我们对该场景下的光照分布进行建模得到,划分模式的计算公式:结果色 = (基色 / 混合色) *255

  • 具体什么意思呢?

我们分析每个通道的数值,并基于基色进行增强,如果基色数值大于或等于混合色,那么结果色就为白色;如果基色小于混合色,结果会比基色更暗。

  • 这个算法为什么比较适合文档图像增强?

我们知道文档图像一般主要有文字和背景组成。其中文字为黑色,背景为白色。如果单纯的进行对比度之类的增强,对于较亮或较暗的图像确实有效,但对于文档,这种方法反而使得文本部分缺失,反而没有达到增强的效果。基于划分模式的图像增强,正是考虑这点,他对于暗的,如:字体,会变的更暗,对于亮的,如:背景,会变的更亮。

  • 具体怎么实现?

问题规范为图像A,B,为同一场景在不一样的光照拍摄图片,那么:

光照分布 L = A / B

如果已知 A, L ,则 B = A / L (B 为A去光照的结果)

这里,A表示我们需要增强的图片,B表示经过高斯滤波后的图片。那么,我们进行A/B就得到增强后的图像了。是不是很简单。

2、基于c++ OpenCV的实现

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
	Mat image = imread("./pic/raw.png");
	//划分算法
	//如果混合色与基色相同则结果色为白色
	//如混合色为白色则结果色为基色不变
	//如混合色为黑色则结果色为白色
	Mat src = image.clone();
	src.convertTo(src, CV_32FC3, 1.0 / 255);
	Mat gauss;
	Mat dst = src.clone();
	GaussianBlur(src, gauss, Size(101, 101), 0);
	dst = src / gauss;
	dst.convertTo(dst, CV_8UC3, 255);
	imshow("dst", dst);
	waitKey();
    
    return 0;
}

结果展示:

3、辅助增强算法

我们看到部分图像处理的并不好,如第1、3行,处理后的结果存在大量的黑色噪声,所以后续可以使用其它增强的算法来辅助进行。这里我们选择gamma对比度增强算法。具体实现如下:

//Gamma校正 fGamaa=0.45是常用值
void GammaCorrection(Mat& src, Mat& dst, float fGamma)
{
	CV_Assert(src.data);
	// accept only char type matrices
	CV_Assert(src.depth() != sizeof(uchar));
	// build look up table
	unsigned char lut[256];
	for (int i = 0; i < 256; i++)
	{
		lut[i] = saturate_cast<uchar>(pow((float)(i / 255.0), fGamma) * 255.0f);
	}

	dst = src.clone();
	const int channels = dst.channels();
	switch (channels)
	{
	case 1:
	{

		MatIterator_<uchar> it, end;
		for (it = dst.begin<uchar>(), end = dst.end<uchar>(); it != end; it++)
			*it = lut[(*it)];

		break;
	}
	case 3:
	{

		MatIterator_<Vec3b> it, end;
		for (it = dst.begin<Vec3b>(), end = dst.end<Vec3b>(); it != end; it++)
		{
			(*it)[0] = lut[((*it)[0])];
			(*it)[1] = lut[((*it)[1])];
			(*it)[2] = lut[((*it)[2])];
		}
		break;
	}
	}
}

完整代码如下:

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

//Gamma校正 fGamaa=0.45是常用值
void GammaCorrection(Mat& src, Mat& dst, float fGamma)
{
	CV_Assert(src.data);
	// accept only char type matrices
	CV_Assert(src.depth() != sizeof(uchar));
	// build look up table
	unsigned char lut[256];
	for (int i = 0; i < 256; i++)
	{
		lut[i] = saturate_cast<uchar>(pow((float)(i / 255.0), fGamma) * 255.0f);
	}

	dst = src.clone();
	const int channels = dst.channels();
	switch (channels)
	{
	case 1:
	{

		MatIterator_<uchar> it, end;
		for (it = dst.begin<uchar>(), end = dst.end<uchar>(); it != end; it++)
			*it = lut[(*it)];

		break;
	}
	case 3:
	{

		MatIterator_<Vec3b> it, end;
		for (it = dst.begin<Vec3b>(), end = dst.end<Vec3b>(); it != end; it++)
		{
			(*it)[0] = lut[((*it)[0])];
			(*it)[1] = lut[((*it)[1])];
			(*it)[2] = lut[((*it)[2])];
		}
		break;
	}
	}
}

int main(int argc, char** argv)
{
	Mat image = imread("./pic/raw.jpg");
	划分算法
	//如果混合色与基色相同则结果色为白色
	//如混合色为白色则结果色为基色不变
	//如混合色为黑色则结果色为白色
	Mat src = image.clone();
	src.convertTo(src, CV_32FC3, 1.0 / 255);
	Mat gauss;
	Mat dst = src.clone();
	GaussianBlur(src, gauss, Size(101, 101), 0);
	dst = src / gauss;
	dst.convertTo(dst, CV_8UC3, 255);
	gamma变换
	Mat matGamma;
	GammaCorrection(dst.clone(), matGamma,1.5);
	//显示最终结果
	//namedWindow("Soure", 0);
	namedWindow("dst", 0);

	imshow("Soure", image);
	imshow("dst", matGamma);
	waitKey();
    
    return 0;
}

参考连接:

camscanner(扫描全能王)功能解析与复现

扫描效果图像增强

猜你喜欢

转载自blog.csdn.net/wxplol/article/details/112645349