火车票检测与提取矫正

要求:在图片中找到火车票,将火车票检测出来同时将火车票进行旋转矫正,单独将火车票挑选出来。

先放置效果图:
处理后的图片:

图像处理的流程:
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;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值