Opencv图像处理学习(二十九)——旋转文本图像矫正

图像文本选择通常是由于扫描仪在进行图像扫描时,未能正常按照其行列水平垂直扫描引起的现象。在现实场景中,我们需要对旋转文本进行几何矫正。这次利用傅立叶变换中时域与频域的变换关系,实现选择文本图像矫正。

旋转文本图像的明显特征就是存在分行间隔,当文本图像旋转时,其频域中的频谱也会随之旋转。根据这一特征来计算文本图像的DFT变换,DFT变换的结果是低频位于边界四角,高频集中在中心区域,将低频与高频互换,实现中心的移动,进而可看到文本图像的频谱有明显的倾斜直线,再通过计算图像直线的倾斜角度,利用仿射变换就可以完成选择文本的图像矫正。旋转文本图像矫正的具体步骤如下:

<1>图像DFT尺寸转换。快速傅立叶变换是基于图像尺寸2、3或5倍数完成的,因此对于输入源图像,首先应将其变成DFTSize,Opencv中提供了函数getOptimalDFTSize()来实现尺寸转换。

PS:copyMakeborder函数用来复制图像,超过边界区域填充为0。

<2>DFT变换。该步骤中首先将处理的输入图像转换为实部与虚部,接着通道合并,通过DFT变换得到实、虚部两通道,然后计算实部与虚部的幅度值,并完成数据归一化映射。

<3>频域中心移动,傅立叶变换得到的低频部分在边缘角中,高频部分位于图像中心,对于倾斜文本图像,需要将低频部分与高频部分互换中心。通常采用的方法是将图像等分成4分,然后将区域进行互调,完成中心移动。

<4>倾斜角检测:经过频域中心移动后,只需要检测出图像中直线的倾斜角就可以对旋转文本进行校正。计算直线倾斜角有很多方法,这里介绍利用霍夫变换线检测方法进行直线倾斜角的计算,首先将傅立叶变换后的频谱图进行固定二值化处理,这里阈值的选择和场景有很大关系,读者可根据实际应用场景进行合理调整;然后根据霍夫变换检测直线的步骤来完成中的直线检测,计算得到图像直线的角度;最后判断图像中检测到的线角度是否符合要求,对符合要求的线角度进行图像的角度转换。

<5>仿射变换矫正

对得到的线角度计算旋转矩阵,利用仿射变换完成旋转文本矫正。

给个源代码:

cv::Mat check(cv::Mat check_Mat)
{
	cv::Mat srcImage(check_Mat);
	cv::Mat dstImage;

	cv::cvtColor(srcImage, srcImage,cv::COLOR_RGB2GRAY);

	/******************图像DFT尺寸转换****************************/	
	const int nRows = srcImage.rows;
	const int nCols = srcImage.cols;

	int cRows = cv::getOptimalDFTSize(nRows);//图片尺寸转换,获取DFT尺寸
	int cCols = cv::getOptimalDFTSize(nCols);

	cv::Mat sizeConvMat;

	cv::copyMakeBorder(srcImage, sizeConvMat, 0, cRows - nRows, 0, cCols - nCols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
	/***********************图像DFT变换*************************/
	cv::Mat groupMats[] = { cv::Mat_(sizeConvMat),cv::Mat::zeros(sizeConvMat.size(),CV_32F)};
	cv::Mat mergeMat;

	cv::merge(groupMats, 2, mergeMat);//通道合并
	cv::dft(mergeMat, mergeMat);//DFT变换
	cv::split(mergeMat, groupMats);//分离通道
	cv::magnitude(groupMats[0], groupMats[1], groupMats[0]);//计算幅度
	cv::Mat magnitudeMat = groupMats[0].clone();
	magnitudeMat += cv::Scalar::all(1);//归一化操作,赋值加1
	cv::log(magnitudeMat, magnitudeMat);//对数变换
	cv::normalize(magnitudeMat, magnitudeMat, 0, 1, CV_MINMAX);//归一化
	magnitudeMat.convertTo(magnitudeMat, CV_8UC1, 255, 0);//图像类型转换

	/*********************频域中心移动*******************/
	int cx = magnitudeMat.cols / 2;
	int cy = magnitudeMat.rows / 2;

	cv::Mat tmp;
	cv::Mat q0(magnitudeMat, cv::Rect(0, 0, cx, cy));
	cv::Mat q1(magnitudeMat, cv::Rect(cx, 0, cx, cy));
	cv::Mat q2(magnitudeMat, cv::Rect(0, cy, cx, cy));
	cv::Mat q3(magnitudeMat, cv::Rect(cx, cy, cx, cy));

	q0.copyTo(tmp);//交换象限
	q3.copyTo(q0);
	tmp.copyTo(q3);
	q1.copyTo(tmp);
	q2.copyTo(q1);
	tmp.copyTo(q2);

	/*******************倾斜角检测*********************/
	cv::Mat binaryMagnMat;
	
	cv::threshold(magnitudeMat, binaryMagnMat, 133, 255, CV_THRESH_BINARY);
	
	std::vector lines;
	
	binaryMagnMat.convertTo(binaryMagnMat, CV_8UC1, 255, 0);
	cv::HoughLines(binaryMagnMat, lines, 1, CV_PI / 180, 100, 0, 0);

	cv::Mat houghMat(binaryMagnMat.size(), CV_8UC3);

	float theta = 0;

	for (size_t i = 0; i < lines.size(); i++)//检测线角度判断
	{
		float thetaTemp = lines[i][1] * 180 / CV_PI;

		if (thetaTemp > 0 && thetaTemp < 90)
		{
			theta = thetaTemp;
			break;
		}
	}

	float angelT = nRows * tan(theta / 180 * CV_PI);//角度转换
	theta = atan(angelT) * 180 / CV_PI;

	/*******************仿射变换校正*******************/
	cv::Point2f centerPoint = cv::Point2f(nCols / 2 , nRows / 2);//取图像中心
	
	double Scale = 1;
	
	cv::Mat warpMat = cv::getRotationMatrix2D(centerPoint,theta,Scale);//计算旋转矩阵
	
	cv::Mat resultImage(srcImage.size(), srcImage.type());
	
	cv::warpAffine(srcImage, resultImage, warpMat, resultImage.size());//仿射变换
	dstImage = resultImage;

	return dstImage;
}

 

 

 

 

 

你可能感兴趣的:(学习opencv)