#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
#define ERROR 1234
//度数转换
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, Size(length, length), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色
}
//通过霍夫变换计算角度
double CalcDegree(const Mat &srcImage, Mat &dst)
{
Mat midImage, dstImage;
dstImage = srcImage.clone();
cvtColor(srcImage, midImage, CV_BGR2GRAY);
Mat bw;
adaptiveThreshold(~midImage, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
Mat horizontal = bw.clone();
int scale = 30; //这个值越大,检测到的直线越多
int horizontalsize = horizontal.cols / scale;
Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontalsize, 1));
erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));
//Canny(srcImage, midImage, 50, 200, 3);
//cvtColor(midImage, dstImage, CV_GRAY2BGR);
//通过霍夫变换检测直线
vector<Vec2f> lines;
HoughLines(horizontal, lines, 1, CV_PI / 180, 300, 0, 0);//第5个参数就是阈值,阈值越大,检测精度越高
//cout << lines.size() << endl;
//由于图像不同,阈值不好设定,因为阈值设定过高导致无法检测直线,阈值过低直线太多,速度很慢
//所以根据阈值由大到小设置了三个阈值,如果经过大量试验后,可以固定一个适合的阈值。
if (!lines.size())
{
HoughLines(horizontal, lines, 1, CV_PI / 180, 200, 0, 0);
}
//cout << lines.size() << endl;
if (!lines.size())
{
HoughLines(horizontal, lines, 1, CV_PI / 180, 150, 0, 0);
}
//cout << lines.size() << endl;
if (!lines.size())
{
cout << "没有检测到直线!" << endl;
return ERROR;
}
float sum = 0;
//依次画出每条线段
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0];
float theta = lines[i][1];
Point pt1, pt2;
//cout << theta << endl;
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));
//只选角度最小的作为旋转角度
sum += theta;
line(dstImage, pt1, pt2, Scalar(55, 100, 195), 1); //Scalar函数用于调节线段颜色
imshow("直线探测效果图", dstImage);
}
float average = sum / lines.size(); //对所有角度求平均,这样做旋转效果会更好
cout << "average theta:" << average << endl;
double angle = DegreeTrans(average) - 90;
//rotateImage(dstImage, dst, angle);
//imshow("直线探测效果图2", dstImage);
return angle;
}
void ImageRecify(const char* pInFileName, const char* pOutFileName)
{
double degree;
Mat src = imread(pInFileName);
imshow("原始图", src);
Mat dst;
//倾斜角度矫正
degree = CalcDegree(src,dst);
if (degree == ERROR)
{
cout << "矫正失败!" << endl;
return;
}
rotateImage(src, dst, degree);
cout << "angle:" << degree << endl;
imshow("旋转调整后", dst);
Mat resulyImage = dst(Rect(0, 0, src.cols, src.rows)); //根据先验知识,估计好文本的长宽,再裁剪下来
imshow("裁剪之后", resulyImage);
imwrite(pOutFileName, resulyImage);
}
int main()
{
ImageRecify("E:\\fengce\\0813\\06R187260B9000.tif", "FinalImage.tif");
waitKey(0);
return 0;
}
代码更新 目前采用的方法鲁棒性会比较好一点
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#include <io.h>
using namespace cv;
using namespace std;
#define MAX_PATH 1024 //最长路径长度
#define ERROR 1234
//计算旋转的角度
double computeIntersect(cv::Vec4i a)
{
double angle = 0;
double flage_temp;
int x1 = a[0], y1 = a[1], x2 = a[2], y2 = a[3];
Point tempPoint;
double temp;
tempPoint.x = x2 - x1;
tempPoint.y = y2 - y1;
if ((0 == tempPoint.x) && (0 == tempPoint.y))
{
return 0;
}
if (0 == tempPoint.x)
{
angle = 90;
return angle;
}
if (0 == tempPoint.y)
{
angle = 0;
return angle;
}
temp = fabsf(float(tempPoint.y) / float(tempPoint.x));
temp = atan(temp);
temp = temp * 180 / CV_PI;
if (tempPoint.y < 0)
{
angle = -temp;
return angle;
}
else
{
angle =temp;
return angle;
}
return ERROR;
}
//度数转换
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, Size(length, length), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色
}
//通过霍夫变换计算角度
double CalcDegree(const Mat &srcImage, Mat &dst)
{
Mat midImage, dstImage;
dstImage = srcImage.clone();
double threa;
cvtColor(srcImage, midImage, CV_BGR2GRAY);
Mat bw;
adaptiveThreshold(~midImage, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
Mat horizontal = bw.clone();
int scale = 30; //这个值越大,检测到的直线越多
int horizontalsize = horizontal.cols / scale;
Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontalsize, 1));
erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));
vector<Vec4i> lines;
HoughLinesP(horizontal,lines,1,CV_PI/180,100,200,50);
if (!lines.size())
{
return ERROR;
}
float sum = 0;
//依次画出每条线段
for (size_t i = 0; i < lines.size(); i++)
{
Vec4i l = lines[i];
threa = computeIntersect(lines[i]);
sum += threa;
//cout << threa << endl;
line(dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186, 88, 255), 1, CV_AA);
}
float average = sum / lines.size(); //对所有角度求平均,这样做旋转效果会更好
return average;
}
void ImageRecify(string pInFileName, string pOutFileName)
{
double degree;
Mat src = imread(pInFileName);
//imshow("原始图", src);
Mat dst;
//倾斜角度矫正
degree = CalcDegree(src,dst);
if (degree == ERROR)
{
cout << "矫正失败!" << endl;
return;
}
rotateImage(src, dst, degree);
//cout << "angle:" << degree << endl;
//imshow("旋转调整后", dst);
Mat resulyImage = dst(Rect(0, 0, src.cols, src.rows)); //根据先验知识,估计好文本的长宽,再裁剪下来
//imshow("裁剪之后", resulyImage);
imwrite(pOutFileName, resulyImage);
}
//得到文件名
void getAllFiles(string path, vector<string>& files)
{
//文件句柄
long hFile = 0;
//文件信息
struct _finddata_t fileinfo; //很少用的文件信息读取结构
string p; //string类很有意思的一个赋值函数:assign(),有很多重载版本
if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
if ((fileinfo.attrib & _A_SUBDIR)) //判断是否为文件夹
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
{
files.push_back(p.assign(path).append("/").append(fileinfo.name));//保存文件夹名字
getAllFiles(p.assign(path).append("/").append(fileinfo.name), files);//递归当前文件夹
}
}
else //文件处理
{
files.push_back(p.assign(path).append("/").append(fileinfo.name));//文件名
}
} while (_findnext(hFile, &fileinfo) == 0); //寻找下一个,成功返回0,否则-1
_findclose(hFile);
}
}
//读取txt
void readTxt(string file)
{
ifstream infile;
infile.open(file.data()); //将文件流对象与文件连接起来
assert(infile.is_open()); //若失败,则输出错误消息,并终止程序运行
int i = 0;
string s;
while (getline(infile, s))
{
string path= "E:\\fengce\\0816\\"+static_cast<ostringstream*>(&(ostringstream() <<i))->str()+".tif";
ImageRecify(s, path);
cout << s << endl;
i++;
}
infile.close(); //关闭文件输入流
}
int main()
{
double timeStart = (double)getTickCount();
ImageRecify("E:\\fengce\\0815\\08R18726080013.tif","FinalImage.tif");
double nTime = ((double)getTickCount() - timeStart) / getTickFrequency();
cout << "运行上面程序共耗时:" << nTime << "秒\n" << endl;
//waitKey(0);
//readTxt("AllFiles.txt");
return 0;
}
效果图