转自:https://blog.youkuaiyun.com/spw_1201/article/details/53608805
旋转文本矫正:
图像文本旋转通常在仿射变换时获取图像的倾斜角度,利用傅里叶变换中的时域与频域的变换关系,实现旋转文本的校正。
旋转文本的特征明显就是存在分行间隔,当文本图像旋转时,其频域中的频谱也会随之旋转。根据这一特征来计算文本图像的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)
- img – Image.
- pt1 – First point of the line segment.
- pt2 – Second point of the line segment.
- color – Line color.
- thickness – Line thickness.
- lineType –
Type of the line:
- 8 (or omitted) - 8-connected line.
- 4 - 4-connected line.
- CV_AA - antialiased line.
- shift – Number of fractional bits in the point coordinates.
(4)仿射变换矫正
Mat getRotationMatrix2D(Point2f center, double angle, double scale)
- center – Center of the rotation in the source image.
- angle – Rotation angle in degrees. Positive values mean counter-clockwise rotation (the coordinate origin is assumed to be the top-left corner).
- scale – Isotropic scale factor.
最终结果:
源程序:
-
#include <opencv2/core/core.hpp>
-
#include <opencv2/highgui/highgui.hpp>
-
#include <opencv2/imgproc/imgproc.hpp>
-
#include <iostream>
-
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_<
float>(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<Vec2f> 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;
-
-
}
-
-