QRCodeDetect/main.cpp

193 lines
6.7 KiB
C++

#include <opencv2/opencv.hpp>
#include <iostream>
#include <cmath>
#include "Eigen/Dense"
#include <algorithm>
using namespace cv;
Mat cameraIntrinsics = (Mat_<float>(3, 3)
<< 1.118687327324564e+03, 0.0, 0.0, 0, 1.135263811351046e+03, 0.0, 703.25397, 482.50630, 1.0);
std::vector<Point2f> center(std::vector<std::vector<cv::Point>> contours) {
std::vector<Moments> mu(contours.size());
for (int i = 0; i < contours.size(); ++i) {
mu[i] = moments(contours[i], false);
}
std::vector<Point2f> mc(contours.size());
for (int j = 0; j < contours.size(); ++j) {
mc[j] = Point2f(mu[j].m10 / mu[j].m00, mu[j].m01 / mu[j].m00);
}
return mc;
}
/*
* p0---p1
* /
* /
* /
* p2---p3
*
* */
void sortPointsForPerspective(std::vector<Point2f> &corners) {
for (int i = 0; i < 4; ++i) {
for (int j = i; j > 0; j--) {
if (corners[j].x < corners[j - 1].x)
swap(corners[j], corners[j - 1]);
else break;
}
}
if (corners[0].y > corners[1].y) { swap(corners[0], corners[1]); }
if (corners[2].y > corners[3].y) { swap(corners[2], corners[3]); }
swap(corners[1], corners[2]);
}
Mat imgPreProcessing(const Mat &img) {
// Mat img1;
// medianBlur(img,img1,3);
Mat imgGray;
cvtColor(img, imgGray, COLOR_BGR2GRAY); // 转灰度图
Mat imgBinary;
double thresh = 50;
threshold(imgGray, imgBinary, thresh, 255, THRESH_OTSU | THRESH_BINARY); // 转二值图
return imgBinary;
}
Mat imgFindQr(const Mat &imgBinary, Mat img) {
std::vector<cv::Vec4i> hierarchy;
std::vector<std::vector<cv::Point>> contours;
findContours(imgBinary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE); // 轮廓
std::vector<Point2f> mc = center(contours);
std::vector<Vec4i> blockConfid; // 信任特征块序号
for (int i = 0; i < contours.size(); ++i) {
const double minMidArea = 100; //中间块最小面积
const double maxMidArea = pow(min(img.rows, img.cols), 2) * 25 / 441;//中间块最大面积
int firstChild = hierarchy[i][2]; //子块
int parent = hierarchy[i][3]; //父块
if (firstChild < 0 || parent < 0) continue; //筛选掉无父子的轮廓
int uncle0 = hierarchy[parent][0];
int uncle1 = hierarchy[parent][1];
int grand = hierarchy[parent][3];
if ((uncle0 < 0 && uncle1 < 0) || grand < 0)continue; //筛选掉独立轮廓
double area = contourArea(contours[i]);
if (area < minMidArea || maxMidArea < area) continue; //面积筛选掉面积过大过小轮廓
double ratio1 = contourArea(contours[parent]) / area,
ratio2 = area / contourArea(contours[firstChild]);
std::vector<float> centroidVec{mc[i].x, mc[i].y, 1.0, mc[parent].x, mc[parent].y, 1.0, mc[firstChild].x,
mc[firstChild].y, 1.0};
Eigen::Map<Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>> centroid(centroidVec.data(),
3, 3);
if (34.0 / 25.0 <= ratio1 && ratio1 <= 64.0 / 25.0 //面积比例 外/中 = 49/25
&& 16.0 / 9.0 <= ratio2 && ratio2 <= 34.0 / 9.0 // 面积比例 中/内 = 25/9
&& abs(centroid.determinant()) <= 1) {//三中心三角形面积 < 1
blockConfid.emplace_back(firstChild, i, parent, grand);
}
}
int outLabelTemp = -1;
std::vector<int> outLabel;
for (auto &i : blockConfid) { // 挑选共同外框
int outLabelNow = i[3];
if (outLabelNow != outLabelTemp) {
outLabel.push_back(outLabelNow);
outLabelTemp = outLabelNow;
}
}
int outAll[outLabel.size()][4];
for (int i = 0; i < outLabel.size(); ++i) { outAll[i][0] = outLabel[i]; }
int num[outLabel.size()];
memset(num, 0, sizeof(num));
for (auto &i : blockConfid) {
for (int j = 0; j < outLabel.size(); ++j) {
if (i[3] == outLabel[j]) {
outAll[j][num[j] + 1] = i[2];
num[j]++;
break;
}
}
}
int num1 = 0;
std::vector<Point2f> approx;
std::vector<std::vector<Point2f>> quaCorners;
std::vector<Mat> qrCodes;
std::vector<Mat> straight_qrcode;
std::vector<std::string> decoded_info;
std::vector<Point2f> qrFrame(4);
qrFrame[0] = Point2f(-20, -20);
qrFrame[1] = Point2f(230, -20);
qrFrame[2] = Point2f(-20, 230);
qrFrame[3] = Point2f(230, 230);
for (int i = 0; i < sizeof(outAll) / sizeof(outAll[0]); ++i) {
Mat qrCode;
approxPolyDP(contours[outAll[i][0]], approx, 3, true);
putText(img, std::to_string(outAll[i][0]), contours[outAll[i][0]][0], FONT_HERSHEY_PLAIN, 2.0,
Scalar(0, 0, 255), 2);
drawContours(img, contours, outAll[i][0], Scalar(0, 0, 255), 1, 8);
sortPointsForPerspective(approx);
Mat perTran = getPerspectiveTransform(approx, qrFrame);
std::cout << perTran << std::endl;
warpPerspective(imgBinary, qrCode, perTran, Size(210, 210));
// std::string qrframe = "qrcode";
// qrframe.append(std::to_string(i + 1));
// imshow(qrframe, qrCode);
quaCorners.push_back(approx);
qrCodes.push_back(qrCode);
}
return img;
}
int main(int argc, char **argv) {
// Mat img, res, imgBinary;
//
// img = imread("/home/ryoo/CLionProjects/QRcode/cameraOpen/qrcode.JPG");
// resize(img, img, Size(img.cols / 4, img.rows / 4), 0, 0, INTER_LINEAR);
//// std::vector<std::string> decoded_info;
//// std::vector<Point> points;
//// std::vector<Mat> straight_qrcode;
//// QRCodeDetector qrdetector =QRCodeDetector();
//// qrdetector.detectAndDecodeMulti(img,decoded_info,points,straight_qrcode);
//// std::string name ="demo";
////
//// for (int i = 0; i < straight_qrcode.size(); ++i) {
//// name.append(std::to_string(i+1));
//// imshow(name, straight_qrcode[i]);
////
//// }
//// waitKey();
// imgBinary = imgPreProcessing(img);
// res = imgFindQr(imgBinary, img);
// imshow("demo", img);
// waitKey();
Mat image, res, imgBinary;
VideoCapture cap;
cap.open(1);
if (!cap.isOpened())
return -1;
cap.set(CAP_PROP_FRAME_WIDTH, 1280);
cap.set(CAP_PROP_FRAME_HEIGHT, 720);
std::cout<<"The Intrinsics of camera is "<<std::endl<<cameraIntrinsics<<std::endl;
while (1) {
cap >> image;//等价于cap.read(frame)
imgBinary = imgPreProcessing(image);
res = imgFindQr(imgBinary, image);
imshow("test", res);
char c = waitKey(66);
if (image.empty())
break;
if (c == 27)
break;
}
cap.release();
destroyAllWindows();
return 0;
}