2021SC@SDUSC
4.对非标准的QR二维码图片,进行定位,然后用Zbar解码显示。
这里主要参考opencv二维码识别解码_nickchao的IT生涯-CSDN博客_opencv二维码识别的博客。不过该博客的处理没有考虑多个识别点时的情况:
例图:
本文主要处理去除干扰的识别点的方向进行研究解决。根据二维码特性:
我们只要找到90°±Δx的角,且夹角两边为最小的边即可。
找到三个点后,我们需要对齐做旋转处理,旋转的角度如下:
其中处理的步骤分为:
灰度处理-》边缘检测-》特征轮廓检测-》提取特征点-》排除干扰点-》绘制直角三角形-》纠正旋转-》提取ROI-》识别
这里先给效果,后展示代码
边缘检测:
?特征轮廓检测:
?提取特征点-》排除干扰点-》绘制直角三角形
纠正旋转
提取ROI
识别
源码如下:
void MyClass::QrRun(){
RNG rng(12345);
//imshow("原图", srcimage);
Mat src_all = srcimage.clone();
Mat src_gray;
//灰度处理
src_gray = getBlur(getGray(srcimage));
Scalar color = Scalar(1, 1, 255);
Mat threshold_output;
vector<vector<Point> > contours, contours2;
vector<Vec4i> hierarchy;
Mat drawing = Mat::zeros(srcimage.size(), CV_8UC3);
Mat drawing2 = Mat::zeros(srcimage.size(), CV_8UC3);
Mat drawingAllContours = Mat::zeros(srcimage.size(), CV_8UC3);
threshold_output = getThold(src_gray);
findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0));
int c = 0, ic = 0, k = 0, area = 0;
// 边缘检测
//通过黑色定位角作为父轮廓,有两个子轮廓的特点,筛选出三个定位角
int parentIdx = -1;
for (int i = 0; i< contours.size(); i++)
{
//画出所以轮廓图
drawContours(drawingAllContours, contours, parentIdx, CV_RGB(255, 255, 255), 1, 8);
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)
{
//保存找到的三个黑色定位角
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);
ic = 0;
parentIdx = -1;
}
}
//提取特征点
//填充的方式画出黑色定位角的轮廓
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());
//获取定位角的中心坐标
vector<Point> pointfind;
for (int i = 0; i<contours2.size(); i++)
{
pointfind.push_back(Center_cal(contours2, i));
}
//排除干扰点
Mat dst;
Point point[3]; double angle; Mat rot_mat;
///选择合适的点-核心筛选
if (pointfind.size()>3){
double lengthA = 10000000000000000, lengthB = 10000000000000000000;
for (int i = 0; i < pointfind.size(); i++){
for (int j = 0; j < pointfind.size(); j++){
for (int k = 0; k < pointfind.size(); k++){
if (i != j&&j != k&&i != k){
double dxa, dxb,dya,dyb;
double k1, k2, wa, wb;
dxa = pointfind[i].x - pointfind[j].x;
dxb = pointfind[i].x - pointfind[k].x;
dya = pointfind[i].y - pointfind[j].y;
dyb = pointfind[i].y - pointfind[k].y;
if (dxa == 0 || dxb == 0)continue;
k1 = dya/dxa;
k2 = dyb/dxb ;
wa = sqrt(pow(dya, 2) + pow(dya, 2));
wb = sqrt(pow(dyb, 2) + pow(dxb, 2));
double anglea = abs(atan(k1) * 180 / CV_PI) + abs(atan(k2) * 180 / CV_PI);
if (int(anglea)>=85&&int(anglea)<=95&&wa<=lengthA&&wb<=lengthB){
lengthA = wa;
lengthB = wb;
point[0] = pointfind[i];
point[1] = pointfind[j];
point[2] = pointfind[k];
}
}
}
}
}
}
else{
for (int i = 0; i < 3; i++){
point[i] = pointfind[i];
}
}
//绘制直角三角形
//计算轮廓的面积,计算定位角的面积,从而计算出边长
area = contourArea(contours2[0]);
int area_side = cvRound(sqrt(double(area)));
for (int i = 0; i < 3; i++){
line(drawing2, point[i], point[(i + 1)%3], color, area_side / 2, 8);
}
//纠正旋转
//判断是否正对
if (!IsCorrect(point)){
//进入修正环节
double angle; Mat rot_mat;
int start = 0;
for (int i = 0; i < 3; i++){
double k1, k2,kk;
k1 = (point[i].y - point[(i + 1) % 3].y) / (point[i].x - point[(i + 1) % 3].x);
k2 = (point[i].y - point[(i + 2) % 3].y) / (point[i].x - point[(i + 2) % 3].x);
kk = k1*k2;
if (k1*k2 <0)
start = i;
}
double ax, ay, bx, by;
ax = point[(start + 1) % 3].x;
ay = point[(start + 1) % 3].y;
bx = point[(start + 2) % 3].x;
by = point[(start + 2) % 3].y;
Point2f center(abs(ax - bx) / 2, abs(ay -by)/ 2);
double dy = ay - by;
double dx = ax - bx;
double k3 = dy / dx;
angle =atan(k3) * 180 / CV_PI;//转化角度
rot_mat = getRotationMatrix2D(center, angle, 1.0);
warpAffine(src_all, dst, rot_mat, src_all.size(), 1, 0, 0);//旋转原图查看
warpAffine(drawing2, drawing2, rot_mat, src_all.size(), 1, 0, 0);//旋转连线图
warpAffine(src_all, src_all, rot_mat, src_all.size(), 1, 0, 0);//旋转原图
namedWindow("Dst");
imshow("Dst", dst);
}
namedWindow("DrawingAllContours");
imshow("DrawingAllContours", drawingAllContours);
namedWindow("Drawing2");
imshow("Drawing2", drawing2);
namedWindow("Drawing");
imshow("Drawing", drawing);
//提取ROI
//接下来要框出这整个二维码
Mat gray_all, threshold_output_all;
vector<vector<Point> > contours_all;
vector<Vec4i> hierarchy_all;
cvtColor(drawing2, gray_all, CV_BGR2GRAY);
threshold(gray_all, threshold_output_all, 45, 255, THRESH_BINARY);
findContours(threshold_output_all, contours_all, hierarchy_all, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0));//RETR_EXTERNAL表示只寻找最外层轮廓
Point2f fourPoint2f[4];
//求最小包围矩形
RotatedRect rectPoint = minAreaRect(contours_all[1]);//pointfind.size()-3
//将rectPoint变量中存储的坐标值放到 fourPoint的数组中
rectPoint.points(fourPoint2f);
int maxx = 0, maxy = 0, minx = 100000, miny = 100000;
for (int i = 0; i < 4; i++)
{
if (maxx < fourPoint2f[i].x)maxx = fourPoint2f[i].x;
if (maxy < fourPoint2f[i].y)maxy = fourPoint2f[i].y;
if (minx > fourPoint2f[i].x)minx = fourPoint2f[i].x;
if (miny > fourPoint2f[i].y)miny = fourPoint2f[i].y;
line(src_all, fourPoint2f[i % 4], fourPoint2f[(i + 1) % 4]
, Scalar(0), 3);
}
namedWindow("Src_all");
///边际处理
int set_inter = 5;
while (true)
{
minx -= set_inter;
miny -= set_inter;
maxx += set_inter;
maxy += set_inter;
if (maxx > srcimage.size().width || maxy > srcimage.size().height || minx < 0 || miny < 0){
minx += set_inter;
miny += set_inter;
maxx -= set_inter;
maxy -= set_inter;
set_inter--;
}
else
{
break;
}
}
imshow("Src_all", src_all(Rect(minx, miny, maxx - minx, maxy - miny)));//ROI
Mat fout = src_all(Rect(minx, miny, maxx - minx, maxy - miny));//ROI
//识别
Dis_code(fout);
waitKey(0);
destroyAllWindows();
}
|