原为本人机器人视觉课程作业,要求为实现文档扫描功能,下给出基本思路及代码过程
原要求作业如下所示
步骤采用类似于ppt给出的第一种思路
读入图片如下,
该图片为4608*3456尺寸大小的图片,为避免处理过于复杂,这边使用resize进行图像缩放,缩小后尺寸为576*432,缩小相关代码如下
Mat QuickDemo::resize_demo(Mat &image) {
Mat zoomin, zoomout;
int h = image.rows;
int w = image.cols;
resize(image, zoomin, Size(w / 8, h / 8), 0, 0, INTER_LINEAR);
//imshow("zoomin", zoomin);
//resize(image, zoomout, Size(w*1.5, h*1.5), 0, 0, INTER_LINEAR);
//imshow("zoomout", zoomout);
return zoomin;
}
再对图形进行基本预处理,预处理代码如下
Mat version_lesson::pre_operate_image(Mat &image) {
Mat gray,blur,canny, imgDil;
cvtColor(image, gray, COLOR_BGR2GRAY); //灰度
//imshow("gray", gray);
GaussianBlur(gray, blur, Size(3, 3), 3, 0); //高斯模糊(参数四和五:sigmaX 和 sigmaY
imshow("blur", blur);
Canny(blur, canny, 25, 75); //边缘检测(参数四和五:两个阈值
//imshow("canny", canny);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(canny, imgDil, kernel); //膨胀(参数三:膨胀内核
//imshow("imgDil", imgDil);
return canny;
}
示例处理效果如下:从左至右分别为灰度图、高斯滤波、有高斯滤波 、无高斯滤波后的边缘检测、膨胀后图像
基尔霍夫直线检测(接下来代码统一在同一函数中,文后有源码)
这一步的代码如下,HoughLinesP的输入图像为image,输出为lines,距离步长为1,角度步长为默认值CV_PI/180,累加值阈值为80,最短直线长度为100像素点(该值为不断调试出的最合适值),同一直线最大像素间隔为10
vector lines;
HoughLinesP(image, lines, 1, CV_PI / 180.0, 80, 100, 10);
for (int i = 0; i < lines.size(); ++i) {
line(rgb, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(0, 255, 0), 2, 8);
}
imshow("lines", rgb);
找外边缘轮廓,为了更方便的寻找外边缘轮廓,csdn博主“一个热爱学习的深度渣渣”提供了一个比较好的方法(原文链接http://t.csdn.cn/uO7p6),即先判断直线是横线还是竖线,将其分类。
在第五步的代码基础上,这边又检验了一下lines中的数据,代码及检测结果如下。
发现检测到了13条直线,(恶补了一下返回值Vec4i的知识)每一组数据中前两个一组(x1,y1),后两个一组(x2,y2)分别表示检测到直线的起点和终点
接下来开始判断竖线or横线,并将其分类,代码如下:
vector heng_lines,shu_lines;//定义横线、竖线类别
for (int i = 0; i < lines.size(); ++i) {
int x = lines[i][0] - lines[i][2];//x改变量
int y = lines[i][1] - lines[i][3];//y改变量
x = x * x;//防止出现负数,用平方比较
y = y * y;
//cout << x << endl << y << endl;
if (x
对分类的竖线、横线进行排序,并找到边框直线。竖线判断中心点x坐标即可,横线判断中心点y坐标即可,代码如下所示,思想是存到数组内后利用max_element和min_element返回其最大最小值,所在下标即对应边框直线
if (shu_lines.size() < 2 || heng_lines.size() < 2) {// 确保水平线和垂直线至少有两条
cout << "Not enough edge lines... " << endl;
}
int heng_center_y[10], shu_center_x[10];
for (int i = 0; i < heng_lines.size(); ++i) //横线判断y坐标,这边中心点不用除以2也无所谓
heng_center_y[i] = heng_lines[i][1] + heng_lines[i][3];
int maxp_heng = max_element(heng_center_y, heng_center_y + heng_lines.size())- heng_center_y;
int minp_heng = min_element(heng_center_y, heng_center_y + heng_lines.size()) - heng_center_y;
//cout << "最大下标"<
找到边框直线后将其画出来,代码及效果图如下,这边竖线用蓝色,横线用绿色
绘制最外端边框直线
line(rgb, Point(heng_lines[minp_heng][0], heng_lines[minp_heng][1]), Point(heng_lines[minp_heng][2], heng_lines[minp_heng][3]), Scalar(0, 255, 0), 2, 8);
line(rgb, Point(heng_lines[maxp_heng][0], heng_lines[maxp_heng][1]), Point(heng_lines[maxp_heng][2], heng_lines[maxp_heng][3]), Scalar(0, 255, 0), 2, 8);
line(rgb, Point(shu_lines[minp_shu][0], shu_lines[minp_shu][1]), Point(shu_lines[minp_shu][2], shu_lines[minp_shu][3]), Scalar(255, 0, 0), 2, 8);
line(rgb, Point(shu_lines[maxp_shu][0], shu_lines[maxp_shu][1]), Point(shu_lines[maxp_shu][2], shu_lines[maxp_shu][3]), Scalar(255, 0, 0), 2, 8);
imshow("rgb", rgb);
求单应性矩阵
求两点关系用到的代码如下,这边要注意图像y坐标与笛卡尔坐标系相反
Point2f linesIntersect(double b1, double b2, double k1, double k2) {//算两两相交交点
double x, y;//交点
x = (b2 - b1) / (k1 - k2);
y = (k1*b2 - k2 * b1) / (k2 - k1);
if (x < 0)
x = -x;
if (y < 0)
y = -y;
return Point2f(x, y);
}
//算四条直线斜率
double k_heng_min, k_heng_max, k_shu_min, k_shu_max;
k_heng_min = (double)(heng_lines[minp_heng][3] - heng_lines[minp_heng][1]) / (double)(heng_lines[minp_heng][2] - heng_lines[minp_heng][0]);
k_heng_max = (double)(heng_lines[maxp_heng][3] - heng_lines[maxp_heng][1]) / (double)(heng_lines[maxp_heng][2] - heng_lines[maxp_heng][0]);
k_shu_min = (double)(shu_lines[minp_shu][3] - shu_lines[minp_shu][1]) / (double)(shu_lines[minp_shu][2] - shu_lines[minp_shu][0]);
k_shu_max = (double)(shu_lines[maxp_shu][3] - shu_lines[maxp_shu][1]) / (double)(shu_lines[maxp_shu][2] - shu_lines[maxp_shu][0]);
//算四条直线y=x+b的b
double b_heng_min, b_heng_max, b_shu_min, b_shu_max;
b_heng_min = -k_heng_min * (double)heng_lines[minp_heng][2] + (double)heng_lines[minp_heng][3];
b_heng_max = -k_heng_max * (double)heng_lines[maxp_heng][2] +(double)heng_lines[maxp_heng][3];
b_shu_min = -k_shu_min * (double)shu_lines[minp_shu][2] + (double)shu_lines[minp_shu][3];
b_shu_max = -k_shu_max * (double)shu_lines[maxp_shu][2] + (double)shu_lines[maxp_shu][3];
//求交点
Point2f p_zuoshang, p_zuoxia, p_youshang, p_youxia;
p_zuoshang = linesIntersect(b_heng_min, b_shu_min, k_heng_min, k_shu_min);//左上交点为上方横线交左侧竖线
p_zuoxia = linesIntersect(b_heng_max, b_shu_min, k_heng_max, k_shu_min);//同上类似
p_youshang = linesIntersect(b_heng_min,b_shu_max, k_heng_min, k_shu_max );
p_youxia = linesIntersect(b_heng_max, b_shu_max, k_heng_max, k_shu_max);
//cout << p_zuoshang << endl << p_zuoxia << endl << p_youshang << endl << p_youxia<
由原图像长宽为576*432,故原图像四个顶点的坐标为,由此可以得到透视前后的变化坐标,进而求取单应性变换矩阵并完成透视变换,代码如下:
vector ori_pts;//输入点坐标
ori_pts.push_back(p_zuoshang);
ori_pts.push_back(p_youshang);
ori_pts.push_back(p_zuoxia);
ori_pts.push_back(p_youxia);
cout << ori_pts << endl;
int dst_width = 432, dst_height = 576;
vector dst_pts;//输出点坐标
dst_pts.push_back(Point(0, 0));
dst_pts.push_back(Point(dst_width - 1, 0));
dst_pts.push_back(Point(0, dst_height - 1));
dst_pts.push_back(Point(dst_width - 1, dst_height - 1));
Mat H;//单应性变换矩阵
Mat toushi;//输出图像
H = findHomography(ori_pts, dst_pts);
warpPerspective(yuan, toushi, H, yuan.size());
imshow("终", toushi);
总运行结果如下
下附部分图像操作函数完整源码
本代码仅供参考!!!请理解全文后自行敲打完成!!!
本代码仅供参考!!!请理解全文后自行敲打完成!!!
本代码仅供参考!!!请理解全文后自行敲打完成!!!
Mat version_lesson::pre_operate_image(Mat &image) {
Mat gray,blur,canny, imgDil;
cvtColor(image, gray, COLOR_BGR2GRAY); //灰度
//imshow("gray", gray);
GaussianBlur(gray, blur, Size(3, 3), 3, 0); //高斯模糊(参数四和五:sigmaX 和 sigmaY
imshow("blur", blur);
Canny(blur, canny, 25, 75); //边缘检测(参数四和五:两个阈值
//imshow("canny", canny);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(canny, imgDil, kernel); //膨胀(参数三:膨胀内核
//imshow("imgDil", imgDil);
return canny;
}
Mat version_lesson::hough_P_line_detect(Mat &image,Mat &rgb,Mat &yuan) {
vector lines;
HoughLinesP(image, lines, 1, CV_PI / 180.0, 80, 100, 10);
for (int i = 0; i < lines.size(); ++i) {
line(rgb, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(0, 255, 0), 2, 8);
}
imshow("lines", rgb);
vector heng_lines,shu_lines;//定义横线、竖线类别
for (int i = 0; i < lines.size(); ++i) {
int x = lines[i][0] - lines[i][2];//x改变量
int y = lines[i][1] - lines[i][3];//y改变量
x = x * x;//防止出现负数,用平方比较
y = y * y;
//cout << x << endl << y << endl;
if (x ori_pts;//输入点坐标
ori_pts.push_back(p_zuoshang);
ori_pts.push_back(p_youshang);
ori_pts.push_back(p_zuoxia);
ori_pts.push_back(p_youxia);
cout << ori_pts << endl;
int dst_width = 432, dst_height = 576;
vector dst_pts;//输出点坐标
dst_pts.push_back(Point(0, 0));
dst_pts.push_back(Point(dst_width - 1, 0));
dst_pts.push_back(Point(0, dst_height - 1));
dst_pts.push_back(Point(dst_width - 1, dst_height - 1));
Mat H;//单应性变换矩阵
Mat toushi;//输出图像
H = findHomography(ori_pts, dst_pts);
warpPerspective(yuan, toushi, H, yuan.size());
imshow("终", toushi);
return rgb;
}
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main(int argc, char **argv)
{
version_lesson vl;
QuickDemo qd;
Mat src1;
Mat src = imread("D:/Open CV/picture/version_lesson3.jpg");//version_lesson3.jpg
src = qd.resize_demo(src);//图像过大缩小8倍
imshow("原图", src);
src1 = vl.pre_operate_image(src);
src1 = vl.hough_P_line_detect(src1,src,src);
//src1 = vl.find_contours_MAX(src1, src);
waitKey(0);
destroyAllWindows();
return 0;
}