要求:在图片中找到火车票,将火车票检测出来同时将火车票进行旋转矫正,单独将火车票挑选出来。
先放置效果图:
处理后的图片:
图像处理的流程:
1、HIS色域阈值分割。
2、圈出联通区域
3、将符合要求的连通域提取出来
4、形态学操作
5、Hough直线检测去除无关直线
6、求取车票的四个角的坐标
7、透视变换得到变换后的图像
相关的代码如下:
#include "stdafx.h"
#include<opencv2\opencv.hpp>
#include<vector>
#include<queue>
using namespace cv;
using namespace std;
class Point_myself
{
public:
int x;
int y;
bool visited;
Point_myself(int xx = 0, int yy = 0)
{
x = xx;
y = yy;
visited = false;
}
};
//连接矩形
vector<Rect> connect(Mat img)
{
vector<vector<Point_myself>> vec1;
vector<Point_myself> vecc1;
for (int i = 0; i < img.rows; i++)
{
vec1.push_back(vecc1);
for (int j = 0; j < img.cols; j++)
{
vec1[i].push_back(Point_myself(i, j));
}
}
queue<Point_myself> que;
Point_myself pt;
vector<vector<Point_myself>> vec2;
for (int i = 0; i < img.rows; i++)
{
for (int j = 0; j < img.cols; j++)
{
if (img.at<uchar>(i, j) == 255 && vec1[i][j].visited == false)
{
vec2.push_back(vecc1);
que.push(vec1[i][j]);
while (que.size() != 0)
{
pt = que.front();
int m = pt.x;
int n = pt.y;
que.pop();
if (m >= 1 && img.at<uchar>(m - 1, n) == 255 && vec1[m - 1][n].visited == false)
{
que.push(vec1[m - 1][n]);
vec1[m - 1][n].visited = true;
}
if (n >= 1 && img.at<uchar>(m, n - 1) == 255 && vec1[m][n - 1].visited == false)
{
que.push(vec1[m][n - 1]);
vec1[m][n - 1].visited = true;
}
if (m<img.rows - 1 && img.at<uchar>(m + 1, n) == 255 && vec1[m + 1][n].visited == false)
{
que.push(vec1[m + 1][n]);
vec1[m + 1][n].visited = true;
}
if (n<img.cols - 1 && img.at<uchar>(m, n + 1) == 255 && vec1[m][n + 1].visited == false)
{
que.push(vec1[m][n + 1]);
vec1[m][n + 1].visited = true;
}
vec2[vec2.size() - 1].push_back(pt);
}
}
}
}
vector<Rect> vec3;
Rect rectan(img.rows - 1, img.cols - 1, 0, 0);
Rect rectan2;
for (int i = 0; i < vec2.size(); i++)
{
rectan2 = rectan;
int min_r = 10000, min_c = 10000, max_r = 0, max_c = 0;
for (int j = 0; j < vec2[i].size(); j++)
{
if (vec2[i][j].x < min_r)
{
min_r = vec2[i][j].x;
}
if (vec2[i][j].x>max_r)
{
max_r = vec2[i][j].x;
}
if (vec2[i][j].y < min_c)
{
min_c = vec2[i][j].y;
}
if (vec2[i][j].y>max_c)
{
max_c = vec2[i][j].y;
}
}
rectan2.x = min_r;
rectan2.y = min_c;
rectan2.height = max_r - min_r + 1;
rectan2.width = max_c - min_c + 1;
vec3.push_back(rectan2);
}
vector<Rect> vec4;
for (int i = 0; i < vec3.size(); i++)
{
if (vec3[i].area() > 20000 && vec3[i].area() < 450000)
{
if ((vec3[i].width>0.5*vec3[i].height) && (vec3[i].width<2 * vec3[i].height))
{
vec4.push_back(vec3[i]);
}
}
}
return vec4;
}
//获取图片四周的角点
Point cross(Vec4i line1, Vec4i line2)
{
double x1, x2, x3, x4, x5, x6, x7, x8;
Point pt;
x1 = line1[0];
x2 = line1[1];
x3 = line1[2];
x4 = line1[3];
x5 = line2[0];
x6 = line2[1];
x7 = line2[2];
x8 = line2[3];
double k1, k2;
double x, y;
double dot = ((x3 - x1)*(x7 - x5) + (x4 - x2)*(x8 - x6)) / sqrt((x3 - x1)*(x3 - x1) + (x4 - x2)*(x4 - x2)) / sqrt((x7 - x5)*(x7 - x5) + (x8 - x6)*(x8 - x6));
dot = abs(dot);
if (dot > 0.7)
{
pt.x = 0;
pt.y = 0;
return pt;
}
else
{
if (((x3 - x1) != 0) && ((x7 - x5) != 0))
{
k1 = (x4 - x2) / (x3 - x1) - (x8 - x6) / (x7 - x5);
k2 = (x8 - x4) + x3*(x4 - x2) / (x3 - x1) - x7*(x8 - x6) / (x7 - x5);
x = k2 / k1;
y = x4 + (x - x3)*(x4 - x2) / (x3 - x1);
pt.x = x;
pt.y = y;
return pt;
}
else if (x7 - x5 == 0)
{
pt.x = x5;
pt.y = x4 + (x5 - x3)*(x4 - x2) / (x3 - x1);
return pt;
}
else if (x3 - x1 == 0)
{
pt.x = x3;
pt.y = x8 + (x3 - x7)*(x8 - x6) / (x7 - x5);
return pt;
}
}
}
int minn(int x1, int x2, int x3, int x4)
{
int t5, t6, t;
t5 = min(x1, x2);
t6 = min(x3, x4);
t = min(t5, t6);
if (t == x1)
{
return 0;
}
if (t == x2)
{
return 1;
}
if (t == x3)
{
return 2;
}
return 3;
}
//左上、左下、右上、右下(顺序)
void GetTransFormImage(Mat& srcImage, Mat& resultImage, vector<Point2f>& srcPoints, float width, float thea=0.6136)
{
float higth = width*thea;
Point2f src[4], dst[4] = { Point2f(0, 0),Point2f(width,0),Point2f(0,higth), Point2f(higth, width) };
for (size_t i = 0; i < srcPoints.size(); i++)
{
src[i] = srcPoints[i];
}
Mat transform_Mat = getPerspectiveTransform(src, dst);
warpPerspective(srcImage, resultImage, transform_Mat, Size(width, higth));
imshow("原图", srcImage);
imshow("变换后的图像", resultImage);
}
//求出箭头所在的大致区域
void GetAreaofArrow(Mat& srcImage, Mat& dstImage)
{
float height = srcImage.rows;
float width1 = srcImage.cols;
float bili = height * 2 / width1;
resize(srcImage, dstImage, Size(0, 0), bili, 1, cv::INTER_CUBIC);
int x = dstImage.cols *0.4;
int y = dstImage.rows / 3 - dstImage.rows / 4;
cv::rectangle(dstImage, Rect(x, dstImage.rows*0.23, dstImage.rows*0.45, y), Scalar(0, 0, 255), 1, 1, 0);
imshow("箭头图", dstImage);
}
int _tmain(int argc, _TCHAR* argv[])
{
//【1】载入原始图和Mat变量定义
Mat srcImagesmall = imread("4.jpg"); //工程目录下应该有一张名为1.jpg的素材图
Mat srcImage;
Size sizeofimage = srcImagesmall.size();
resize(srcImagesmall, srcImage, Size(sizeofimage.width*0.3, sizeofimage.height*0.3));
Mat HSIImage = Mat(Size(srcImage.cols, srcImage.rows), CV_8UC3);
//imshow("HSI原图", HSIImage);
vector <Mat> channels;
split(HSIImage, channels);
Mat Hvalue = channels.at(0);
Mat Svalue = channels.at(1);
Mat Ivalue = channels.at(2);
vector<int> vec;
for (int i = 0; i < 256; i++)
{
vec.push_back(0);
}
for (int i = 0; i < srcImage.rows; ++i)
for (int j = 0; j < srcImage.cols; ++j)
{
double H, S, I;
int Bvalue = srcImage.at<Vec3b>(i, j)(0);
int Gvalue = srcImage.at<Vec3b>(i, j)(1);
int Rvalue = srcImage.at<Vec3b>(i, j)(2);
//求Theta =acos((((Rvalue - Gvalue) + (Rvalue - Bvalue)) / 2) / sqrt(pow((Rvalue - Gvalue), 2) + (Rvalue - Bvalue)*(Gvalue - Bvalue)));
double numerator = ((Rvalue - Gvalue) + (Rvalue - Bvalue)) / 2;
double denominator = sqrt(pow((Rvalue - Gvalue), 2) + (Rvalue - Bvalue)*(Gvalue - Bvalue));
if (denominator == 0) H = 0;
else{
double Theta = acos(numerator / denominator) * 180 / 3.14;
if (Bvalue <= Gvalue)
H = Theta;
else H = 360 - Theta;
}
if (H>80 && H<220)//阈值
{
H = 360;
}
else
{
H = 0;
}
Hvalue.at<uchar>(i, j) = (int)(H * 255 / 360); //为了显示将[0~360]映射到[0~255]
vec[(int)(H * 255 / 360)] = vec[(int)(H * 255 / 360)] + 1;
//求S = 1-3*min(Bvalue,Gvalue,Rvalue)/(Rvalue+Gvalue+Bvalue);
int minvalue = Bvalue;
if (minvalue > Gvalue) minvalue = Gvalue;
if (minvalue > Rvalue) minvalue = Rvalue;
numerator = 3 * minvalue;
denominator = Rvalue + Gvalue + Bvalue;
if (denominator == 0) S = 0;
else{
S = 1 - numerator / denominator;
}
Svalue.at<uchar>(i, j) = (int)(S * 255);//为了显示将[0~1]映射到[0~255]
I = (Rvalue + Gvalue + Bvalue) / 3;
Ivalue.at<uchar>(i, j) = (int)(I);
}
Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));//腐蚀变换的结构体
vector<Rect> rectangle1;
rectangle1 = connect(Hvalue);
namedWindow("HSI");
imshow("HSI", Hvalue);//HSI图像
//
rectangle(Hvalue, Point(rectangle1[0].y, rectangle1[0].x), Point(rectangle1[0].y + rectangle1[0].width, rectangle1[0].x + rectangle1[0].height), Scalar(255, 255, 255));
imshow("矩形度度量", Hvalue);
for (int i = 0; i < Hvalue.cols; i++)
{
for (int j = 0; j < Hvalue.rows; j++)
{
if ((i>rectangle1[0].y) && (i<(rectangle1[0].y + rectangle1[0].width)) && (j>rectangle1[0].x) && (j < (rectangle1[0].x + rectangle1[0].height)))
{
continue;
}
else
{
Hvalue.at<uchar>(j, i) = 0;
}
}
}
imshow("处理后的图像", Hvalue);
erode(Hvalue, Hvalue, element);
dilate(Hvalue, Hvalue, element);
dilate(Hvalue, Hvalue, element);
dilate(Hvalue, Hvalue, element);
dilate(Hvalue, Hvalue, element);
erode(Hvalue, Hvalue, element);
erode(Hvalue, Hvalue, element);
erode(Hvalue, Hvalue, element);
imshow("HSI图像", Hvalue);
Mat midImage, dstImage;//临时变量和目标图的定义
//【2】进行边缘检测和转化为灰度图
Canny(Hvalue, midImage, 50, 200, 3);//进行一此canny边缘检测
cvtColor(midImage, dstImage, CV_GRAY2BGR);//转化边缘检测后的图为灰度图
vector<Vec4i> lines;//定义一个矢量结构lines用于存放得到的线段矢量集合
HoughLinesP(midImage, lines, 1, CV_PI / 180, 40, 100, 100);
//imshow("HSI", midImage);
//【4】依次在图中绘制出每条线段
for (size_t i = 0; i < lines.size(); i++)
{
Vec4i l = lines[i];
line(dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186, 88, 255), 1, CV_AA);
}
vector<Point> vec_pt;
for (int i = 0; i < lines.size(); i++)
{
for (int j = i + 1; j < lines.size(); j++)
{
Point pt = cross(lines[i], lines[j]);
//cout << pt.x << " " << pt.y << endl;
vec_pt.push_back(pt);
}
}
for (int i = 0; i < vec_pt.size(); i++)
{
for (int j = i + 1; j < vec_pt.size(); j++)
{
if ((abs(vec_pt[j].y - vec_pt[i].y) < 20) && (abs(vec_pt[j].x - vec_pt[i].x) < 20))
{
vec_pt[j].x = 0;
vec_pt[j].y = 0;
}
}
}
Vector<Point> vec_4pt;
for (int i = 0; i < 4; i++)
{
vec_4pt.push_back(Point(0, 0));
}
for (int i = 0; i < vec_pt.size(); i++)
{
if (vec_pt[i].x != 0)
{
//cout << vec_pt[i].x << " " << vec_pt[i].y << endl;
int t = minn(abs(vec_pt[i].x - rectangle1[0].y) + abs(vec_pt[i].y - rectangle1[0].x), abs(vec_pt[i].x - rectangle1[0].y - rectangle1[0].width) + abs(vec_pt[i].y - rectangle1[0].x), abs(vec_pt[i].x - rectangle1[0].y) + abs(vec_pt[i].y - rectangle1[0].x - rectangle1[0].height), abs(vec_pt[i].x - rectangle1[0].y - rectangle1[0].width) + abs(vec_pt[i].y - rectangle1[0].x - rectangle1[0].height));
vec_4pt[t] = Point(vec_pt[i].x, vec_pt[i].y);
}
}
vector<Point2f> srcPoints;//vector数组变量
for (int i = 0; i < 4; i++)
{
//左上 右上 左下 右下
cout << vec_4pt[i].x << " " << vec_4pt[i].y << endl;
srcPoints.push_back(Point2f(vec_4pt[i].x, vec_4pt[i].y));
}
imshow("HSI检测直线图", dstImage);
float width = 108.0 * 2, higth = 176.0 * 2;
Point2f dst[4] = { Point2f(0, 0), Point2f(higth, 0), Point2f(0, width), Point2f(higth, width) };
Point2f src[4] = { srcPoints[0],srcPoints[1],srcPoints[2],srcPoints[3] };
Mat transform_Mat = getPerspectiveTransform(src, dst);
Mat resultImage;
warpPerspective(srcImage, resultImage, transform_Mat, Size(higth, width));
imshow("结果图", srcImage);
imshow("变换", resultImage);
//找出箭头
Mat Arrow_Image;
GetAreaofArrow(resultImage, Arrow_Image);
int x = Arrow_Image.cols *0.4;
int y = Arrow_Image.rows / 3 - Arrow_Image.rows / 4;
//对大致区域进行霍夫检测
// Mat hough = Arrow_Image(x:(x+ Arrow_Image.rows*0.45), Arrow_Image.rows*0.23: (Arrow_Image.rows*0.23+y));
Mat hough = Arrow_Image(Rect(x, Arrow_Image.rows*0.23, Arrow_Image.rows*0.45, y));
Mat hough_midImage, houghdst;
Canny(hough, hough_midImage, 50, 200, 3);//进行一此canny边缘检测
cvtColor(hough_midImage, houghdst, COLOR_GRAY2BGR);//转化边缘检测后的图为灰度图
vector<Vec4i> hough_lines;//定义一个矢量结构lines用于存放得到的线段矢量集合
HoughLinesP(hough_midImage, hough_lines, 1, CV_PI / 180, 50, 50, 10);
for (size_t i = 0; i < hough_lines.size(); i++)
{
Vec4i l = hough_lines[i];
line(houghdst, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186, 88, 255), 1, CV_AA);
}
imshow("【效果图】", houghdst);
waitKey(0);
return 0;
}