193 lines
6.7 KiB
C++
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;
|
|
}
|