//
// Created by leoxae on 19-9-21.
//
#include "QRcodeLocation.h"
#include "../cv/ImgProcession.h"
RNG rng(12345);
Mat QRcodeLocation::imgtrans(Mat img){
int width = img.cols;
int height = img.rows;
Mat dst = img.clone();
//透视变换
vector<Point2f> srcPoints = {
Point2f(0, 0),
Point2f(width, 0),
Point2f(0, height),
Point2f(width, height)
};
vector<Point2f> dstPoints = {
Point2f(120, height),
Point2f(width- 120, height),
Point2f(0, 0),
Point2f(width , 0)
};
Mat transform = getPerspectiveTransform(srcPoints, dstPoints);
Mat transmat;
warpPerspective(dst, transmat, transform, Size(width, height));
return transmat;
}
Mat QRcodeLocation::imgtrans_flip(Mat img){
int width = img.cols;
int height = img.rows;
Mat dst = img.clone();
//透视变换
vector<Point2f> srcPoints = {
Point2f(0, 0),
Point2f(width, 0),
Point2f(0, height),
Point2f(width, height)
};
vector<Point2f> dstPoints = {
Point2f(0, 0),
Point2f(width , 0),
Point2f(120, height),
Point2f(width- 120, height),
};
Mat transform = getPerspectiveTransform(srcPoints, dstPoints);
Mat transmat;
warpPerspective(dst, transmat, transform, Size(width, height));
return transmat;
}
Point QRcodeLocation::Center_cal(vector<vector<Point>> contours, int i)//找到所提取轮廓的中心点
{
int centerx = 0, centery = 0, n = contours[i].size();
//在提取的小正方形的边界上每隔周长个像素提取一个点的坐标,求所提取四个点的平均坐标(即为小正方形的大致中心)
centerx = (contours[i][n / 4].x + contours[i][n * 2 / 4].x + contours[i][3 * n / 4].x + contours[i][n - 1].x) / 4;
centery = (contours[i][n / 4].y + contours[i][n * 2 / 4].y + contours[i][3 * n / 4].y + contours[i][n - 1].y) / 4;
Point point1 = Point(centerx, centery);
return point1;
}
Mat QRcodeLocation::FindQRcode(Mat img){
//透视变换
Mat transmat;
transmat = imgtrans_flip(img);
// transmat = imgtrans(img);
// imshow("transmat",transmat);
// waitKey();
Mat hsv;
cvtColor(transmat,hsv,COLOR_BGR2HSV);
Scalar lower_white (0,0,0);
Scalar upper_white (180,255,180);
Mat mask_white;
inRange(hsv,lower_white,upper_white,mask_white);
Mat gray = mask_white;
threshold(gray,gray,100,255,THRESH_OTSU + THRESH_BINARY);
Scalar color = Scalar(1, 1, 255);
Mat threshold_output;
vector<vector<Point>> contours, contours2;
vector<Vec4i> hierarchy;
Mat drawing = Mat::zeros(img.size(), CV_8UC3);
Mat drawing2 = Mat::zeros(img.size(), CV_8UC3);
threshold(gray, threshold_output, 112, 255, THRESH_BINARY);
// imshow("预处理后",threshold_output);
// waitKey();
// Canny(threshold_output,threshold_output,136,196,3);
// imshow("预处理后canny:",threshold_output);
//寻找轮廓
//第一个参数是输入图像二值化的
//第二个参数是内存存储器,FindContours找到的轮廓放到内存里面。
//第三个参数是层级,**[Next, Previous, First_Child, Parent]** 的vector
//第四个参数是类型,采用树结构
//第五个参数是节点拟合模式,这里是全部寻找
findContours(threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0));
//CHAIN_APPROX_NONE全体,CV_CHAIN_APPROX_SIMPLE,RETR_TREE RETR_EXTERNAL RETR_LIST RETR_CCOMP
cout << "初始轮廓数量==>" << contours.size() << endl;
int c = 0, ic = 0, k = 0, area = 0;
//程序的核心筛选
int parentIdx = -1;
if (!contours.empty()) {
for (int i = 0; i < contours.size(); i++) {
if (hierarchy[i][2] != -1 && ic == 0) {
parentIdx = i;
ic++;
} else if (hierarchy[i][2] != -1) {
ic++;
} else if (hierarchy[i][2] == -1) {
ic = 0;
parentIdx = -1;
}
if (ic >= 2 && contourArea(contours[parentIdx]) < 150000 && contourArea(contours[parentIdx]) > 100) {
contours2.push_back(contours[parentIdx]);
drawContours(drawing, contours, parentIdx,
CV_RGB(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), 1, 8);
imshow("drawing", drawing);
waitKey();
ic = 0;
parentIdx = -1;
//得出一个二维码定位角的面积,以便计算其边长(area_side)
area = contourArea(contours[i]);
cout << "area==>" << area << endl;
cout << "i= " << i << " hierarchy[i][2]= " << hierarchy[i][2]
<< " parentIdx= " << parentIdx << " ic= " << ic << endl;
}
}
} else {
//if contours为空
cout << "[error]没有找到相应轮廓" << endl;
return img;
}
for (int i = 0; i<contours2.size(); i++){
drawContours(drawing2, contours2, i, CV_RGB(rng.uniform(100, 255), rng.uniform(100, 255), rng.uniform(100, 255)), -1, 4, hierarchy[k][2], 0, Point());
// imshow("drawing3",drawing2);
// waitKey();
}
Point point[3];
for (int i = 0; i<contours2.size(); i++)
{
point[i] = Center_cal(contours2, i);
}
int length = sizeof(point)/sizeof(point[0]);
cout << "length=" << length << endl;
for (int i = 0; i < 3; i++){
cout << "points[i]=" << point[i] << endl;
}
if ((point[2].x == 0 || point[2].y == 0)){
cout << "二维码定位轮廓检测不全" << endl;
return img;
} else {
//合并三个定位矩形轮廓点集
vector<Point> pt_array = pointmerge(contours2);
// area = contourArea(contours2[0]);
// int area_side = cvRound(sqrt(double(area)));
// for (int i = 0; i<contours2.size(); i++)
// {
// line(drawing2, point[i%contours2.size()], point[(i + 1) % contours2.size()], color, area_side / 4, 8);
// }
// imshow("提取后", drawing2);
cout << "轮廓数量" << contours2.size() << endl;
//imshow( "Contours", drawing );
RotatedRect rct = minAreaRect(pt_array);
Point2f pts[4];
rct.points(pts);
circle(transmat,pts[0],2,Scalar(0,0,255),-1);
circle(transmat,pts[1],2,Scalar(0,255,0),-1);
circle(transmat,pts[2],2,Scalar(255,0,0),-1);
circle(transmat,pts[3],2,Scalar(0,255,255),-1);
// imshow("drawcircle",transmat);
// waitKey();
vector<Point2f> pointlist{pts[0],pts[1],pts[2],pts[3]};
Rect ROI = boundingRect(pointlist);
cout << "x=" << ROI.x << "," << "y=" << ROI.y << endl;
cout << "width=" << ROI.width << ",height=" << ROI.height << endl;
if (ROI.width <= ROI.height){
ROI.x = ROI.x /* 0.8*/;
ROI.y = ROI.y /* 0.8*/;
ROI.height = ROI.height /*2.5*/;
ROI.width = ROI.height;
} else {
ROI.x = ROI.x /* 0.8*/;
ROI.y = ROI.y /* 0.8*/;
ROI.width = ROI.width/*2.5*/;
ROI.height = ROI.width;
}
Mat rmat = transmat(ROI);
Rect rm (ROI.x - 20, ROI.y - 20, ROI.width + 40, ROI.height + 40);
Mat roimat = transmat(rm);
resize(roimat,roimat,Size(roimat.cols*1.2,roimat.rows*1.2));
imshow("roimat",roimat);
waitKey();
return roimat;
}
}
/**
* vector容器合并
* @param contours2
* @return
*/
vector<Point> QRcodeLocation::pointmerge(vector<vector<Point>> contours2) {
vector<Point> ptarray;
vector<Point> pt1 = contours2[0];
vector<Point> pt2 = contours2[1];
vector<Point> pt3 = contours2[2];
ptarray.insert(ptarray.end(), pt1.begin(),pt1.end());
ptarray.insert(ptarray.end(), pt2.begin(),pt2.end());
ptarray.insert(ptarray.end(), pt3.begin(),pt3.end());
return ptarray;
}
知识兔//
// Created by leoxae on 19-9-21.
//
#ifndef KEEKOAIROBOT_QRCODELOCATION_H
#define KEEKOAIROBOT_QRCODELOCATION_H
#include "../../globals.h"
using namespace std;
class QRcodeLocation{
public:
/**
* 查找QRcode二维码位置
* @param img 原图
* @return 二维码截取后的图
*/
static Mat FindQRcode(Mat img);
/**
* 二维码透视变换
* @param img
* @return
*/
static Mat imgtrans(Mat img);
/**
* 计算轮廓中心点
* @param contours 轮廓点集
* @param i 索引值
* @return 中心点坐标
*/
static Point Center_cal(vector<vector<Point>> contours, int i);
static vector<Point> pointmerge(vector<vector<Point>> vector);
static Mat imgAffinetrans(Mat src, vector<Point2f> srcpts);
static Mat img_Rotate(Mat src);
static Mat imgtrans_flip(Mat img);
};
#endif //KEEKOAIROBOT_QRCODELOCATION_H
知识兔