OpenCV旋转文本矫正

转自:https://blog.csdn.net/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.

最终结果:



源程序:


 
 
  1. #include <opencv2/core/core.hpp>
  2. #include <opencv2/highgui/highgui.hpp>
  3. #include <opencv2/imgproc/imgproc.hpp>
  4. #include <iostream>
  5. using namespace cv;
  6. using namespace std;
  7. Mat XUANZHUAN(Mat srcImage)
  8. {
  9. Mat srcGray;
  10. cvtColor(srcImage, srcGray, CV_RGB2GRAY);
  11. const int nRows = srcGray.rows;
  12. const int nCols = srcGray.cols;
  13. //计算傅里叶变换尺寸
  14. int cRows = getOptimalDFTSize(nRows);
  15. int cCols = getOptimalDFTSize(nCols);
  16. Mat sizeConvMat;
  17. copyMakeBorder(srcGray, sizeConvMat, 0, cRows - nRows, 0, cCols - nCols, BORDER_CONSTANT, Scalar::all( 0));
  18. //图像DFT变换
  19. //通道组建立
  20. Mat groupMats[] = { Mat_< float>(sizeConvMat), Mat::zeros(sizeConvMat.size(), CV_32F) };
  21. Mat mergeMat;
  22. //把两页合成一个2通道的mat
  23. merge(groupMats, 2, mergeMat);
  24. //对上面合成的mat进行离散傅里叶变换,支持原地操作,傅里叶变换结果为复数,通道1存的是实部,通道2存的是虚部。
  25. dft(mergeMat, mergeMat);
  26. //把变换的结果分割到各个数组的两页中,方便后续操作
  27. split(mergeMat, groupMats);
  28. //求傅里叶变化各频率的幅值,幅值放在第一页中
  29. magnitude(groupMats[ 0], groupMats[ 1], groupMats[ 0]);
  30. Mat magnitudeMat = groupMats[ 0].clone();
  31. //归一化操作,幅值加1
  32. magnitudeMat += Scalar::all( 1);
  33. //傅里叶变换的幅度值范围大到不适合在屏幕上显示,高值在屏幕上显示为白点,而低值为黑点,
  34. //高低值的变化无法有效分辨,为了在屏幕上凸显出高低的变化得连续性,我们可以用对数尺度来替换线性尺度
  35. log(magnitudeMat, magnitudeMat);
  36. //归一化
  37. normalize(magnitudeMat, magnitudeMat, 0, 1,CV_MINMAX);
  38. magnitudeMat.convertTo(magnitudeMat, CV_8UC1, 255, 0);
  39. //imshow("magnitudeMat2", magnitudeMat);
  40. //重新分配象限,使(0,0)移动到图像中心,
  41. //傅里叶变换之前要对源图像乘以(-1)^(x+y),进行中心化
  42. //这是对傅里叶变换结果进行中心化
  43. int cx = magnitudeMat.cols / 2;
  44. int cy = magnitudeMat.rows / 2;
  45. Mat tmp;
  46. //Top-Left--为每一个象限创建ROI
  47. Mat q0(magnitudeMat, Rect(0, 0, cx, cy));
  48. //Top-Right
  49. Mat q1(magnitudeMat, Rect(cx, 0, cx, cy));
  50. //Bottom-Left
  51. Mat q2(magnitudeMat, Rect(0, cy, cx, cy));
  52. //Bottom-Right
  53. Mat q3(magnitudeMat, Rect(cx, cy, cx, cy));
  54. //交换象限,(Top-Left with Bottom-Right)
  55. q0.copyTo(tmp);
  56. q3.copyTo(q0);
  57. tmp.copyTo(q3);
  58. //交换象限,(Top-Right with Bottom-Letf)
  59. q1.copyTo(tmp);
  60. q2.copyTo(q1);
  61. tmp.copyTo(q2);
  62. Mat binaryMagnMat;
  63. threshold(magnitudeMat, binaryMagnMat, 155, 255, CV_THRESH_BINARY);
  64. vector<Vec2f> lines;
  65. binaryMagnMat.convertTo(binaryMagnMat, CV_8UC1, 255, 0);
  66. HoughLines(binaryMagnMat, lines, 1, CV_PI / 180, 100, 0, 0);
  67. cout << "lines.size: " << lines.size() << endl;
  68. Mat houghMat(binaryMagnMat.size(), CV_8UC3);
  69. //绘制检测线
  70. for ( size_t i = 0; i < lines.size(); i++)
  71. {
  72. float rho = lines[i][ 0], theta = lines[i][ 1];
  73. Point pt1, pt2;
  74. //坐标变换生成线表达式
  75. double a = cos(theta), b = sin(theta);
  76. double x0 = a*rho, y0 = b*rho;
  77. pt1.x = cvRound(x0 + 1000 * (-b));
  78. pt1.y = cvRound(y0 + 1000 * (a));
  79. pt2.x = cvRound(x0 - 1000 * (-b));
  80. pt2.y = cvRound(y0 - 1000 * (a));
  81. line(houghMat, pt1, pt2, Scalar( 0, 0, 255), 1, 8, 0);
  82. }
  83. imshow( "houghMat", houghMat);
  84. float theta = 0;
  85. //检测线角度判断
  86. for ( size_t i = 0; i < lines.size(); i++)
  87. {
  88. float thetaTemp = lines[i][ 1] * 180 / CV_PI;
  89. if (thetaTemp > 0 && thetaTemp < 90)
  90. {
  91. theta = thetaTemp;
  92. break;
  93. }
  94. }
  95. //角度转换
  96. float angelT = nRows* tan(theta / 180 * CV_PI) / nCols;
  97. theta = atan(angelT) * 180 / CV_PI;
  98. cout << "theta: " << theta << endl;
  99. //取图像中心
  100. Point2f centerPoint = Point2f(nCols / 2, nRows / 2);
  101. double scale = 1;
  102. //计算旋转中心
  103. Mat warpMat = getRotationMatrix2D(centerPoint, theta, scale);
  104. //仿射变换
  105. Mat resultImage(srcGray.size(), srcGray.type());
  106. warpAffine(srcGray, resultImage, warpMat, resultImage.size());
  107. return resultImage;
  108. }
  109. int main()
  110. {
  111. Mat srcImage = imread( "D:\\4.jpg");
  112. if (srcImage.empty())
  113. return -1;
  114. imshow( "srcImage", srcImage);
  115. Mat resultImage = XUANZHUAN(srcImage);
  116. imshow( "resultImage", resultImage);
  117. waitKey( 0);
  118. return 0;
  119. }




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值