上一篇的代码如果然后处理下面图片就会失效,因为没有明显的轮廓信息。
但观察图片就会发现图中每行文字都在一条直线上,所以这时候考虑用霍夫检查,通过画出的每行直线计算角度然后矫正图片。
1.图片灰度化
2.canny运算,找出文字轮廓
3霍夫检测,得到每行直线
4计算角度矫正图片
#include
#include
using namespace std;
using namespace cv;
double CalcDegree(Mat &binarysrc);
//度数转换
double DegreeTrans(double theta)
{
double res = theta / CV_PI * 180;
return res;
}
//逆时针旋转图像degree角度(原尺寸)
void rotateImage(Mat src, Mat& img_rotate, double degree)
{
//旋转中心为图像中心
Point2f center;
center.x = float(src.cols / 2.0);
center.y = float(src.rows / 2.0);
int length = 0;
length = sqrt(src.cols*src.cols + src.rows*src.rows);
//计算二维旋转的仿射变换矩阵
Mat M = getRotationMatrix2D(center, degree, 1);
warpAffine(src, img_rotate, M, src.size(), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色
}
double CalcDegree(Mat &binarysrc)
{
Mat binary,dstImage,lineimage;
binarysrc.copyTo(binary);
binarysrc.copyTo(dstImage);
binarysrc.copyTo(lineimage);
cvtColor(binary, binary, COLOR_RGB2GRAY);
Canny(binary, binary, 50, 200, 3);
// vectorlines;
// HoughLines(binary, lines, 1, CV_PI / 180, 200, 0, 0);
vector lines;
HoughLines(binary, lines, 1, CV_PI / 180, 300, 0, 0);//第5个参数就是阈值,阈值越大,检测精度越高
//cout << lines.size() << endl;
//由于图像不同,阈值不好设定,因为阈值设定过高导致无法检测直线,阈值过低直线太多,速度很慢
//所以根据阈值由大到小设置了三个阈值,如果经过大量试验后,可以固定一个适合的阈值。
if (!lines.size())
{
HoughLines(binary, lines, 1, CV_PI / 180, 200, 0, 0);
}
//cout << lines.size() << endl;
if (!lines.size())
{
HoughLines(binary, lines, 1, CV_PI / 180, 150, 0, 0);
}
//cout << lines.size() << endl;
if (!lines.size())
{
cout << "没有检测到直线!" << endl;
return 0;
}
float sum = 0;
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0], thrta = lines[i][1];
Point pt1, pt2;
double a = cos(thrta), b = sin(thrta);
double x0 = a*rho, y0 = b*rho; //x=ρcosθ y = ρsinθ
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(lineimage, pt1, pt2, Scalar(0, 255, 0), 1, 0, 0);
sum += thrta;
}
imshow("画线图", lineimage);
float average = sum / lines.size();
cout << "average theta:" << average << endl;
double angle = DegreeTrans(average) - 90;
Mat dst;
rotateImage(dstImage, dst, angle);
imshow("旋转后", dst);
return angle;
}
int main()
{
Mat src;
src = imread("wenzi.jpg", 1);
if (src.data == 0)
{
printf("没有读入图片");
return 0;
}
imshow("原图",src);
double angle=CalcDegree(src);
cout << "angle:" << angle;
waitKey(0);
return 0;
}
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
参数:
image:边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图)
lines:储存着检测到的直线的参数对 的容器 ,其中lines存放两个参数 第一个极坐标中是原点到目标的的距离,第二个是弧度 θ。θ/Π*180为度数。
rho:参数极径 以像素值为单位的分辨率. 我们使用 1 像素.
theta:参数极角 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
theta:要”检测” 一条直线所需最少的的曲线交点
srn and stn: 参数默认为0.
https://blog.csdn.net/nchfgfb/article/details/70026811 原作者
pt1.x = cvRound(x0 + 1000*(-b))这段代码看得我有点蒙蔽,我就百度一下把人家写好的拿下来放在这里
这里是取了点(x0,y0)在直线上上下1000的距离,那么用cvLine画出来的线段就是从pt1 -> pt2的了。那么pt1->pt2的直线距离就是2000。可以取其他的距离,不一定去1000,如600也可以,具体的数字可以自己定义。
1.这个地方也许会出现检测出来的线段长度比pt1->pt2还大,即包含了我们画的线段 , 这是肯定的。
2. 还会出现本来线段没有pt1->pt2这么长,那么我们画的就会过长了。 也是肯定会出现的情况。
因为:CV_HOUGH_STANDARD方法 只能得出rh0 和 θ的值。 这两个值只能确定直线,而不能确定线段是从哪开始到哪结束。
此方法标准型的霍夫变换检测只能确定线段对应的直线。如果你想得到一条直线的两个端点的具体坐标,可以使用CV_HOUGH_PROBABILISTIC(概率型霍夫变换).