火车票检测与提取矫正

来源:互联网 发布:spss数据 excel 编辑:程序博客网 时间:2024/05/07 09:51

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

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

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



原创粉丝点击