近邻点集融合算法

QR二维码结构简介

QR二维码识别中在对图像预处理后最重要的一步就是要进行定位,QR二维码中有三个位置探测图形,通过扫描其特征便可以进行精细定位。


其中位置探测图形的比例特征如下:


本文介绍的方法用于在二维码经过图像预处理并已经粗定位二维码大致位置后,检测位置探测图案坐标以精确定位。
首先要做的是,横竖扫描图像像素,找到满足黑白黑白黑且比例关系近似1:1:3:1:1的,将其两端的中点坐标记录下来,最终得到整幅图像所有的候选点,代码附在文末。
效果如图:


下一步使用原创的近邻点融合算法筛选出真正的位置探测图形所在位置的点。

近邻点融合

1、搜索每个点附近一定距离内近邻点的数量,用每个点近邻点集的平均坐标代替原本的坐标:这样可以将比较集中的点的集群向内收缩融合,孤立点由于没有紧邻点则会直接删除,在不停的迭代中其他零散的点的紧邻点减少的速度会远远大于三个位置探测图形所在位置相对更集中的点。
融合效果如左图,右图是每个近邻点集求出平均坐标后的效果。

2、当得到的近邻点集数量小于某个值时(默认定为20)或者迭代次数到达预设的最大迭代次数(10),结束迭代。
3、筛选出拥有紧邻点最多的三个点,求出其平均坐标就是三个位置探测图形所在的坐标了。
效果如图:

后续只需要通过判断这三个点的位置关系,对图像进行校正即可。
更新:
使用400张左右的二维码图片进行测试,其中包含了不同方向不同角度,以上方法得到的定位准确率仅有70%不到,因此算法仍需优化。
思考后加入了一个反馈机制,对于得到的三个坐标进行计算,判断其各种位置关系是否是真正的三个位置探测图形,若计算结果发现位置关系差别较大,则对近邻点融合算法的参数进行调整重新融合,相关代码已更新。

经过实验对比,该算法准确率可以提高到89%左右,单次耗时在Matlab下为2秒左右,而 https://blog.csdn.net/c602273091/article/details/42033069 中提到的方法在matlab下的准确略能达到95%,且运行速度普遍低于1秒,集中在0.8-0.9秒,但是实验发现几乎所有错误都集中在前200张图片,因此该方法在普适情况下更加适用,但是在某些情况下近邻点融合算法能略占优势。

本文所述算法以及 https://blog.csdn.net/c602273091/article/details/42033069 中算法的matlab实现(命名为group_points.m)已打包上传,链接如下:
https://download.csdn.net/download/whitett/11139476
不知道为啥要5个积分,需要可以评论联系我。

QR二维码精定位算法:

%横竖扫描图像像素,找到满足黑白黑白黑且比例关系近似1:1:3:1:1的,
%将其两端的中点坐标记录下来,最终得到整幅图像所有的候选点,
%然后下一步筛选出真正的位置探测图形所在位置的点。
%紧邻点融合算法:
%1、搜索每个点附近一定距离内近邻点的数量,用
%每个点近邻点集的平均坐标代替原本的坐标:这样可以将比较集中的点
%的集群向内收缩融合,孤立点由于没有紧邻点则会直接删除,在不停的
%迭代中其他零散的点的紧邻点减少的速度会远远大于三个位置探测图形
%所在位置相对更集中的点。
%2、当拥有近邻点集的点数量小于某个值时(默认定为20)或者
%迭代次数到达预设的最大迭代次数时结束迭代。
%3、筛选出拥有紧邻点最多的三个点,求出其平均坐标就是三个
%位置探测图形所在的坐标了。
%4、判断得到的三个点坐标是否是正确的三个位置探测图形位置,
%若不是,则调整参数进行二次计算,直到判断通过。function points = detect(img)
clc;close all;
[m, n,~] = size(img);
tol = 5; %初始距离阈值points = findInRowAndCol(img); %横纵像素扫描,找到拥有1:1:3:1:1比例的位置% tol = 4;
iterate =10; %最大迭代次数
num = 0;
itnum = 0;
numMax = 30; %结束迭代条件,小于numMax个点则结束for i = 1:iterate[num, points,numRepeat] = Points_merge(tol,points); %近邻点融合函数itnum = itnum + 1;if(num < numMax)  %小于numMax个点集结束迭代break;end
end% 排序,前三个即为紧邻点数量最多的三个点
for i = 1:num-1for j = i:numif(numRepeat(i) < numRepeat(j))temp = numRepeat(i);numRepeat(i) = numRepeat(j);numRepeat(j) = temp;temp1 =  points(i,1);points(i,1) =  points(j,1);points(j,1) = temp1;temp2 =  points(i,2);points(i,2) =  points(j,2);points(j,2) = temp2;endend
end%反馈,若三点不满足一定条件则进行二次计算重新定位
count = 0;  %计算重复定位次数
Feedback = detectFeedback(points);
if(Feedback == 0)points = dectectAgain1(img, tol, numMax, count)
end%画出拥有近邻点集做多的前三个点的平均坐标
for index = 1:3x(index) = points(index,1);y(index) = points(index,2);
end
figure(1);
subplot(1,1,1);
imshow(img);
hold on
for index = 1:3plot(y(index),x(index),'*r');
end
grid on

扫描1:1:3:1:1位置探测图案的代码:

%横、纵两个方向扫描图像像素,找到满足黑白黑白黑且
%比例关系近似1:1:3:1:1的,将其两端的中点坐标记录下来,
%最终得到整幅图像所有的候选点。function pointAll = findInRowAndCol()
clear;close all;clc;
f = imread('test.jpg');
k=graythresh(f);
img=im2bw(f,k);
img = 255*img;[rows, cols] = size(img);skipRows = 1;
currentState = 0;
pointRow = [];
pointCol = [];
% C = [];%横向扫描
for row = 1:skipRows:rowsstateCount(1) = 0; % 占比为1的黑色像素stateCount(2) = 0; % 占比为1的白色像素stateCount(3) = 0; % 占比为3的黑色像素stateCount(4) = 0; % 占比为1的白色像素stateCount(5) = 0; % 占比为1的黑色像素currentState = 1;  % 模块属性计数器(黑色像素模块/白色像素模块)ptr_row = img(row,:);for col = 1:colsif(ptr_row(col) < 128)%该像素为黑色if(mod(currentState, 2) == 0)%若当前计数器为白色模块计数器,将其转入下一级计数器即黑色模块计数器currentState = currentState + 1;endstateCount(currentState) = stateCount(currentState) + 1;else%该像素为白色if(mod(currentState, 2) == 0)%若当前计数器为白色模块计数器,将其转入下一级计数器即黑色模块计数器stateCount(currentState) = stateCount(currentState) + 1;elseif(currentState == 5)%在最后一个黑色模块计数器后出现了一个白色像素,即已经获取了黑 白 黑 白 黑的连续像素片段%需要开始通过1:1:3:1:1判断前5个模块是否是位置探测图形的特征[check,totalFinderSize] = QRcheckRatio(stateCount);%判断是否符合1:1:3:1:1
%                      C = [C,check];if(check == 1)%满足比例,记入x = row;y = col - totalFinderSize/2;pointRow = [pointRow;[x,y]];currentState = 1;stateCount(1) = 0;stateCount(2) = 0;stateCount(3) = 0;stateCount(4) = 0;stateCount(5) = 0; else% 不满足比例,继续扫描currentState = 4;stateCount(1) = stateCount(3);stateCount(2) = stateCount(4);stateCount(3) = stateCount(5);stateCount(4) = 1;stateCount(5) = 0;endelse% 不是最后一个黑色模块计数器5% 转换为白色模块计数器currentState = currentState + 1;stateCount(currentState) = stateCount(currentState) + 1;endendendend
end%纵向扫描
for col = 1:skipRows:colsstateCount(1) = 0; % 占比为1的黑色像素stateCount(2) = 0; % 占比为1的白色像素stateCount(3) = 0; % 占比为3的黑色像素stateCount(4) = 0; % 占比为1的白色像素stateCount(5) = 0; % 占比为1的黑色像素currentState = 1;  % 模块属性计数器(黑色像素模块/白色像素模块)ptr_col = img(:,col);for row = 1:rowsif(ptr_col(row) < 128)%该像素为黑色if(mod(currentState, 2) == 0)%若当前计数器为白色模块计数器,将其转入下一级计数器即黑色模块计数器currentState = currentState + 1;endstateCount(currentState) = stateCount(currentState) + 1;else%该像素为白色if(mod(currentState, 2) == 0)%若当前计数器为白色模块计数器,将其转入下一级计数器即黑色模块计数器stateCount(currentState) = stateCount(currentState) + 1;elseif(currentState == 5)%在最后一个黑色模块计数器后出现了一个白色像素,即已经获取了黑 白 黑 白 黑的连续像素片段%需要开始通过1:1:3:1:1判断前5个模块是否是位置探测图形的特征[check,totalFinderSize] = QRcheckRatio(stateCount);%判断是否符合1:1:3:1:1if(check)x = row - totalFinderSize/2;y = col;pointCol = [pointCol;[x,y]];currentState = 1;stateCount(1) = 0;stateCount(2) = 0;stateCount(3) = 0;stateCount(4) = 0;stateCount(5) = 0; else% 不满足比例,继续扫描currentState = 4;stateCount(1) = stateCount(3);stateCount(2) = stateCount(4);stateCount(3) = stateCount(5);stateCount(4) = 1;stateCount(5) = 0;endelse% 不是最后一个黑色模块计数器5% 转换为白色模块计数器currentState = currentState + 1;stateCount(currentState) = stateCount(currentState) + 1;endendendend
end%画点
pointAll = [pointRow; pointCol];
x = pointAll(:,1);
y = pointAll(:,2);
subplot(1,1,1)
imshow(img);
hold on
plot(y,x,'*r');
grid on

其中判断是否满足比例关系的函数代码如下:

function [bool,totalFinderSize] = QRcheckRatio(stateCount)totalFinderSize = 0;
for i = 1:5count = stateCount(i);totalFinderSize = totalFinderSize +  count;if(count == 0)bool = false;end
end
if(totalFinderSize<7)bool = false;
end
moduleSize = ceil(totalFinderSize / 7); % 单个模块平均宽度%计算比例 1:1:3:1:1
tol_factor = 0.5; %用于判断比例的误差系数
maxVariance = moduleSize*tol_factor;
retVal = ((abs(moduleSize - (stateCount(1)))< maxVariance) &&...(abs(moduleSize - (stateCount(2)))< maxVariance) &&...(abs(3*moduleSize - (stateCount(3)))< 3*maxVariance) &&...(abs(moduleSize - (stateCount(4)))< maxVariance) &&...(abs(moduleSize - (stateCount(5)))< maxVariance));bool = retVal;

扫描结果如图:

近邻点集融合算法代码如下:

%紧邻点融合
%搜索每个点附近一定距离内近邻点的数量,用每个点近邻点集
%的平均坐标代替原本的坐标,比较集中的点的集群将会向内
%收缩融合,孤立点直接删除。function [num1, points,numRepeat] = Points_merge(tol, points)
clc;close all;tol_distance = tol;
[num, ~] = size(points);
xgroup = [];
ygroup = [];
score(1:num) = 0;
xgroup(1:num) = 0;
ygroup(1:num) = 0;%删除重叠点
for i = 1:num-1for j = i+1:numif(points(i,1) == points(j,1) && points(i,2) == points(j,2))points(i,1) = 0;points(i,2) = 0;endend
end%距离较近的点记入近邻点集,用以求平均
for i = 1:numfor j = 1:numdistance = sqrt((points(i,1)-points(j,1))*(points(i,1)-points(j,1))+...(points(i,2)-points(j,2))*(points(i,2)-points(j,2)));if (distance < tol_distance)score(i) = score(i) + 1;%将相近点的坐标相加,为后续求平均距离做准备xgroup(i) = xgroup(i) + points(j,1);ygroup(i) = ygroup(i) + points(j,2);endend
end%计算每个点的平均坐标,并用其替换掉原本的点坐标
for i = 1:numpoints(i,1)= xgroup(i)/ score(i);points(i,2)= ygroup(i)/ score(i);if(score(i) == 1)points(i,1)= 0;points(i,2)= 0;end
end%通过上一步计算的平均坐标 合并拥有相同坐标的点,即拥有相同近邻点集的点
numRepeat(1:num) = 1; %记录近邻点集数量的计数器
k = 0;
for i = 1:num-1  %将重复项化为零if(points(i,1)~= 0 && points(i,2)~=0)%遇到非零点计数器才会加一k = k+1;endfor j = i+1 : numif(points(i,1) == points(j,1) && points(i,2) == points(j,2) ...&& points(i,1)~= 0 && points(i,2)~=0)numRepeat(k) = numRepeat(k)+1;points(j,1)= 0;%坐标相同的点清为零,等下一步将其剔除points(j,2)= 0;endend
end
%非零项往前移,将零项往后移
i = 1;
while i ~= num  if (points(i,1) == 0 && points(i,2) == 0)for index = i:num-1points(index,1) = points(index+1,1);points(index,2) = points(index+1,2);points(num,1) = 0;points(num,2) = 0;endelsei = i + 1;end%判断是否已将所有非零项和零项分开is = issort(points);if(is)i = num;end
end%非零项的数量
num1 = 0;
for i = 1:numif (points(i,1) ~= 0 || points(i,2) ~= 0)num1 = num1 + 1;end
end% %画点
% img = imread('rotate.jpg');
% for index = 1:num1
%     x(index) = points(index,1);
%     y(index) = points(index,2);
% end
% figure
% subplot(1,1,1)
% imshow(img);
% hold on
% for index = 1:num1
%     plot(y(index),x(index),'*r');
% end
% grid onend

其中判断是否已将所有非零项和零项分开的函数代码如下:

%判断所有非零点和零点是否已经被分开function is = issort(points)[num,~] = size(points);for i = 1:numif (points(i,1) == 0 && points(i,2) == 0)for k = i+1:numif (points(k,1) ~= 0 || points(k,2) ~= 0)is = 0;return;endif(k == num)is = 1;return;endendelseif (i == num)is = 1;return;end
end

判断得到的三个点是否是正确的三个位置探测图形坐标:

function Feedback = detectFeedback(points)Feedback = 1;
result = 1;
point1 = points(1,:); %取三点
point2 = points(2,:);
point3 = points(3,:);
vector12 = point2 - point1; %求向量,区分正负
vector21 = point1 - point2;
vector23 = point3 - point2;
vector32 = point2 - point3;
vector13 = point3 - point1;
vector31 = point1 - point3;
length1 = norm(vector12); %三条边长
length2 = norm(vector23);
length3 = norm(vector31);
%三边夹角
angle1 = 180/pi * acos(dot(vector12, vector13)/(norm(vector12)* norm(vector13)));
angle2 = 180/pi * acos(dot(vector23, vector21)/(norm(vector23)* norm(vector21)));
angle3 = 180/pi * acos(dot(vector31, vector32)/(norm(vector31)* norm(vector32)));%条件
%三边长度差距不能过大,设定为不得超过2.5倍
if(length1 < length2/2.5 || length1 < length3/2.5 || ...length2 < length1/2.5 || length2 < length3/2.5 || ...length3 < length1/2.5 || length3 < length2/2.5)result = 0;
%三边夹角不得过大,经实验设定为120度
elseif(angle1 > 120 || angle2 > 120 || angle3 > 120)result = 0;
%点在x方向上距离较近时的情况进行排除,
%该情况下三点几乎呈现等边三角形并且x坐标值距离较近
elseif(angle1 < 70 && angle2 < 70 && angle3 < 70 &&...(abs(point1(2)-point2(2)) > length1/3.5 &&...abs(point2(2)-point3(2)) > length2/3.5 &&...abs(point1(2)-point3(2)) > length3/3.5))result = 0;
end%最终判断
if(result == 0)Feedback = 0;
end

二次计算循环块1

function points = dectectAgain1(img, tol, numMax, count)
clc;close all;
count = count + 1; %重复定位次数增加一次points = findInRowAndCol(img); %横纵像素扫描,找到拥有1:1:3:1:1比例的位置tol = tol + count;     %初始距离阈值
iterate =10; %最大迭代次数
num = 0;
itnum = 0;
numMax = numMax + 2; %结束迭代条件,小于numMax个点则结束for i = 1:iterate[num, points,numRepeat] = Points_merge(tol,points); %近邻点融合函数itnum = itnum + 1;if(num < numMax)  %小于numMax个点集结束迭代break;end
end% 排序,前三个即为紧邻点数量最多的三个点
for i = 1:num-1for j = i:numif(numRepeat(i) < numRepeat(j))temp = numRepeat(i);numRepeat(i) = numRepeat(j);numRepeat(j) = temp;temp1 =  points(i,1);points(i,1) =  points(j,1);points(j,1) = temp1;temp2 =  points(i,2);points(i,2) =  points(j,2);points(j,2) = temp2;endend
end%反馈
Feedback = detectFeedback(points);
if(Feedback == 0 && tol > 1)points = dectectAgain2(img, tol, numMax, count);
end

二次计算循环块2

function points = dectectAgain2(img, tol, numMax, count)
clc;close all;
count = count + 1; %重复定位次数增加一次points = findInRowAndCol(img); %横纵像素扫描,找到拥有1:1:3:1:1比例的位置tol = tol - count;     %初始距离阈值
iterate =10; %最大迭代次数
num = 0;
itnum = 0;
numMax = numMax + 2; %结束迭代条件,小于numMax个点则结束for i = 1:iterate[num, points,numRepeat] = Points_merge(tol,points); %近邻点融合函数itnum = itnum + 1;if(num < numMax)  %小于numMax个点集结束迭代break;end
end% 排序,前三个即为紧邻点数量最多的三个点
for i = 1:num-1for j = i:numif(numRepeat(i) < numRepeat(j))temp = numRepeat(i);numRepeat(i) = numRepeat(j);numRepeat(j) = temp;temp1 =  points(i,1);points(i,1) =  points(j,1);points(j,1) = temp1;temp2 =  points(i,2);points(i,2) =  points(j,2);points(j,2) = temp2;endend
end%反馈
Feedback = detectFeedback(points);
if(Feedback == 0 && tol > 1)points = dectectAgain1(img, tol, numMax, count);
end

以上均为个人学习所得,欢迎分享交流。
本文参考: https://blog.csdn.net/c602273091/article/details/43901137

Matlab实现 通过检测QR二维码位置探测图案进行精确定位相关推荐

  1. 从头开始训练一个检测QR二维码区域的YOLOv3模型

    条形码和二维码在识别的时候主要包含定位和解码两个步骤.寻找码的位置,除了用传统的图像算法之外,也可以借助深度学习.那么深度学习的效率如何,我做了一个实验. 为QR二维码训练YOLOv3模型 编译Dar ...

  2. 【OpenCV 4开发详解】QR二维码检测

    本文首发于"小白学视觉"微信公众号,欢迎关注公众号 本文作者为小白,版权归人民邮电出版社发行所有,禁止转载,侵权必究! 经过几个月的努力,小白终于完成了市面上第一本OpenCV 4 ...

  3. QR二维码的攻击方法与防御

    Blackeagle · 2013/07/03 18:59 QR二维码(Quick Response Code)是由日本丰田子公司Denso Wave于1994年发明并开始使用的一种矩阵二维码符号.与 ...

  4. OpenCV4.0 快速QR二维码检测测试示例

    点击我爱计算机视觉标星,更快获取CVML新技术 近几年由于微信大力推广移动支付,二维码已经成为手机App的标配,在众多种类的二维码中,QR码是最为流行的. 刚刚发布的OpenCV4.0-Alpha新增 ...

  5. 【Matlab编程实现常见小问题之二】Matlab如何实现QR二维码的生成与识别

    本篇文章中,旨在解决如何用Matlab编程实现QR二维码的生成与识别.编程环境是Matlab2012a,所用的开源库是ZXing,ZXing是一个开源Java类库用于解析多种格式的1D/2D条形码.目 ...

  6. Android OpenCV(四十):QR二维码检测与识别

    QR二维码 QR码(英语:Quick Response Code:全称为快速响应矩阵图码)是二维码的一种,于1994年由日本DENSO WAVE公司发明.QR来自英文Quick Response的缩写 ...

  7. 【图像处理】QR二维码识别与生成matlab代码

    1 简介 QR二维码的识别技术是数字图像处理领域研究的一个热门课题.随着物联网的不断发展,QR二维码凭借其强大的信息存储能力,方便快捷的识读优点,安全可靠的编码技术,已经逐渐地应用于各个行业领域.同时 ...

  8. 目标检测:二维码检测方案

    Python微信订餐小程序课程视频 https://edu.csdn.net/course/detail/36074 Python实战量化交易理财系统 https://edu.csdn.net/cou ...

  9. Opencv的使用小教程3——利用轮廓检测实现二维码定位

    Opencv的使用小教程3--利用轮廓检测实现二维码定位 二维码具有什么特征 实现效果 识别二维码的流程 1.预处理图像 2.寻找轮廓 3.通过寻找到的轮廓确定"回"的位置 4.创 ...

最新文章

  1. python使用matplotlib可视化雷达图(polar函数可视化雷达图、极坐标图、通过径向方向来显示数据之间的关系)
  2. 基于管道模式的容器设计
  3. 如何设置电脑自动锁屏_工信部重要提醒:赶紧设置这个密码!手机丢时也不怕损失!...
  4. java 保护类型_Java 类的受保护访问(学习 Java 编程语言 046)
  5. Xen虚拟机迁移技术
  6. python图书管理系统增删改查_python基础-字典的增删改查
  7. 代码实现两个数交换方法
  8. python 头条 上传_Python+selenium自动化之文件上传
  9. 《软件需求分析(第二版)》第 13 章——需求开发面临的特殊难题 重点部分总结
  10. matlab从哪里学,从零开始学MATLAB(附光盘)/从零开始学系列
  11. 添加库文件_S7200的库文件导至200SMART正确操作
  12. 使用基础node.js的express框架在连接数据库的过程中,出现Error: Cannot enqueue Handshake after invoking quit.的解决方案
  13. springboot_poi思路
  14. Git fatal: empty string is not a valid pathspec, please use . instead if you meant to match
  15. ssl证书不可信 群晖_上海云盾 CDN 网站 SSL 证书过期更新不生效问题排查和解决...
  16. mysql省市区三级联动数据库的源码(一)
  17. 未来的人工智能和 AR/VR 会从哪些方面影响教育?有什么机会?
  18. 吴恩达机器学习打卡day6
  19. 军团指挥官(权限题)
  20. VUE笔记——B站狂神说

热门文章

  1. 1024程序员节,17城公益骑行,传智播客邀你益骑燃!
  2. 第7章第39节:多图排版:图片和色块的组合排版 [PowerPoint精美幻灯片实战教程]
  3. 【Word】如何设置多级列表
  4. 巨磁阻磁头和水平磁记录技术介绍
  5. Linux 下中文字体安装
  6. 南非世界杯的一些感想
  7. Python全网爬资源,好用,盘它!
  8. Java架构师成长之道之Java程序流程控制
  9. 5.3-badge组件
  10. 电解电容与固态电容全面对比分析