旋转文本矫正:
图像文本旋转通常在仿射变换时获取图像的倾斜角度,利用傅里叶变换中的时域与频域的变换关系,实现旋转文本的校正。
旋转文本的特征明显就是存在分行间隔,当文本图像旋转时,其频域中的频谱也会随之旋转。根据这一特征来计算文本图像的DFT变换,DFT变换的结果是低频位于边界四角,高频集中在中心区域,将低频和高频互换,实现中心的移动,进而可以看到文本图像的频谱有明显的倾斜直线,再通过计算倾斜直线的倾斜角度,利用仿射变换就可以完成旋转文本的图像矫正。
(1)录入一张图像:
前几步的处理和傅里叶变化一致,就是生成傅里叶频谱图。
(2)频域中心移动,傅里叶变化得到的低频部分在边缘角中,高频部分在图像中心,对于倾斜文本图像,我们关心的是图像中的低频部分,因此需要将其与高频部分互换中心。通常的做法是四等分,绕后进行互调。
(3)倾斜度检测。
只要检测出图像直线的倾斜角,就可以进行旋转文本,方法很多,采用Hough变化线检测方法进行直线倾斜角计算。首先进行二值化,然后根据huogh变换检测直线的步骤来完成图像中的直线检测,计算得到图像直线的角度;最后判断角度是否符合要求,对符合要求的线角度进行图像的角度转换。
Hough变换检测线:
HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold,double srn=0,doublestn=0 )
lines:输出检测到的线的数量。theta=CV_PI/180;theshold:是阈值,只有大于这个阈值的线,才会被检测到。
rho:像素中的距离分辨率。
根据检测的线,绘制出线。
所用函数:
void line(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)
Type of the line:
(4)仿射变换矫正
Mat getRotationMatrix2D(Point2f center, double angle, double scale)
最终结果:
源程序:
#include
#include
#include
#include
using namespace cv;
using namespace std;
Mat XUANZHUAN(Mat srcImage)
{
Mat srcGray;
cvtColor(srcImage, srcGray, CV_RGB2GRAY);
const int nRows = srcGray.rows;
const int nCols = srcGray.cols;
//计算傅里叶变换尺寸
int cRows = getOptimalDFTSize(nRows);
int cCols = getOptimalDFTSize(nCols);
Mat sizeConvMat;
copyMakeBorder(srcGray, sizeConvMat, 0, cRows - nRows, 0, cCols - nCols, BORDER_CONSTANT, Scalar::all(0));
//图像DFT变换
//通道组建立
Mat groupMats[] = { Mat_(sizeConvMat), Mat::zeros(sizeConvMat.size(), CV_32F) };
Mat mergeMat;
//把两页合成一个2通道的mat
merge(groupMats, 2, mergeMat);
//对上面合成的mat进行离散傅里叶变换,支持原地操作,傅里叶变换结果为复数,通道1存的是实部,通道2存的是虚部。
dft(mergeMat, mergeMat);
//把变换的结果分割到各个数组的两页中,方便后续操作
split(mergeMat, groupMats);
//求傅里叶变化各频率的幅值,幅值放在第一页中
magnitude(groupMats[0], groupMats[1], groupMats[0]);
Mat magnitudeMat = groupMats[0].clone();
//归一化操作,幅值加1
magnitudeMat += Scalar::all(1);
//傅里叶变换的幅度值范围大到不适合在屏幕上显示,高值在屏幕上显示为白点,而低值为黑点,
//高低值的变化无法有效分辨,为了在屏幕上凸显出高低的变化得连续性,我们可以用对数尺度来替换线性尺度
log(magnitudeMat, magnitudeMat);
//归一化
normalize(magnitudeMat, magnitudeMat, 0,1,CV_MINMAX);
magnitudeMat.convertTo(magnitudeMat, CV_8UC1, 255, 0);
//imshow("magnitudeMat2", magnitudeMat);
//重新分配象限,使(0,0)移动到图像中心,
//傅里叶变换之前要对源图像乘以(-1)^(x+y),进行中心化
//这是对傅里叶变换结果进行中心化
int cx = magnitudeMat.cols / 2;
int cy = magnitudeMat.rows / 2;
Mat tmp;
//Top-Left--为每一个象限创建ROI
Mat q0(magnitudeMat, Rect(0, 0, cx, cy));
//Top-Right
Mat q1(magnitudeMat, Rect(cx, 0, cx, cy));
//Bottom-Left
Mat q2(magnitudeMat, Rect(0, cy, cx, cy));
//Bottom-Right
Mat q3(magnitudeMat, Rect(cx, cy, cx, cy));
//交换象限,(Top-Left with Bottom-Right)
q0.copyTo(tmp);
q3.copyTo(q0);
tmp.copyTo(q3);
//交换象限,(Top-Right with Bottom-Letf)
q1.copyTo(tmp);
q2.copyTo(q1);
tmp.copyTo(q2);
Mat binaryMagnMat;
threshold(magnitudeMat, binaryMagnMat, 155, 255, CV_THRESH_BINARY);
vector lines;
binaryMagnMat.convertTo(binaryMagnMat, CV_8UC1, 255, 0);
HoughLines(binaryMagnMat, lines, 1, CV_PI / 180, 100, 0, 0);
cout << "lines.size: " << lines.size() << endl;
Mat houghMat(binaryMagnMat.size(), CV_8UC3);
//绘制检测线
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0], theta = lines[i][1];
Point pt1, pt2;
//坐标变换生成线表达式
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000 * (-b));
pt1.y = cvRound(y0 + 1000 * (a));
pt2.x = cvRound(x0 - 1000 * (-b));
pt2.y = cvRound(y0 - 1000 * (a));
line(houghMat, pt1, pt2, Scalar(0, 0, 255), 1,8,0);
}
imshow("houghMat", houghMat);
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) / nCols;
theta = atan(angelT) * 180 / CV_PI;
cout << "theta: " << theta << endl;
//取图像中心
Point2f centerPoint = Point2f(nCols / 2, nRows / 2);
double scale = 1;
//计算旋转中心
Mat warpMat = getRotationMatrix2D(centerPoint, theta, scale);
//仿射变换
Mat resultImage(srcGray.size(), srcGray.type());
warpAffine(srcGray, resultImage, warpMat, resultImage.size());
return resultImage;
}
int main()
{
Mat srcImage = imread("D:\\4.jpg");
if (srcImage.empty())
return -1;
imshow("srcImage", srcImage);
Mat resultImage = XUANZHUAN(srcImage);
imshow("resultImage", resultImage);
waitKey(0);
return 0;
}