主要内容

在图像中本地化和读取多个条形码

方法的使用readBarcode功能从计算机视觉工具箱™检测和解码图像中的1-D和2-D条形码。条形码被广泛用于以可视的、机器可读的格式对数据进行编码。它们在许多应用程序中都很有用,例如物品识别、仓库库存跟踪和合规性跟踪。对于1-D条形码,readBarcode函数返回条形码端点的位置。对于2-D条形码,该函数返回查找器模式的位置。本例使用两种方法本地化图像中的多个条形码。一种方法是基于聚类的,它对不同的成像条件更健壮,并且需要统计和机器学习工具箱™。第二种方法使用基于分割的工作流程,可能需要根据成像条件调整参数。

使用readBarcode函数

从图片中读出二维码。

I = imread(“barcodeQR.jpg”);在图片中搜索二维码。[msg, ~, loc] = readBarcode(I);用解码后的消息注释图像。xyText = loc(2,:);Imsg = insertText(I, xyText, msg,“BoxOpacity”, 1“字形大小”25);在查找器图案位置插入填充的圆圈。Imsg = insertShape(Imsg,“FilledCircle”疯狂的,...Repmat (10, length(loc), 1)],“颜色”“红色”“不透明度”1);显示图像。imshow (Imsg)

图中包含一个轴对象。axis对象包含一个image类型的对象。

从图像中读取1-D条形码。

I = imread(“barcode1D.jpg”);读取1-D条码并确定格式。[msg, format, locs] = readBarcode(I);显示检测到的消息并格式化。disp ("检测到的格式和消息:"+格式+”、“+味精)
检测格式和消息:EAN-13, 1234567890128
插入一行显示条形码的扫描行。xyBegin = locs(1,:);imSize = size(I);I = insertShape(I,“行”,[1 xyBegin(2) imSize(2) xyBegin(2)],...“线宽”7);在条形码的末端位置插入标记。I = insertShape(I,“FilledCircle”(loc...Repmat (10, length(locs), 1)],“颜色”“红色”“不透明度”1);显示图像。imshow(我)

图中包含一个轴对象。axis对象包含一个image类型的对象。

改进条码检测

为了成功检测,条形码必须清晰可见。条形码还必须尽可能地与水平或垂直位置紧密对齐。的readBarcode函数本质上对二维或矩阵编码的旋转比对一维或线性条形码的旋转更健壮。例如,在此图像中无法检测到条形码。

I = imread(“rotated1DBarcode.jpg”);显示图像。imshow(我)

图中包含一个轴对象。axis对象包含一个image类型的对象。

将图像传递给readBarcode函数。readBarcode(我)
Ans = ""

控件旋转图像imrotate条形码大致是水平的。使用readBarcode在旋转图像上。

顺时针旋转图像30度。Irot = imrotate(I, -30);显示旋转后的图像。imshow (Irot)

图中包含一个轴对象。axis对象包含一个image类型的对象。

将旋转后的图像传递给readBarcode函数。readBarcode (Irot)
Ans = "012345678905"

检测多种条形码

readBarcode函数只检测每个图像中的单个条形码。为了检测多个条形码,必须指定感兴趣的区域(ROI)。要指定ROI,可以使用drawrectangle交互确定roi的功能。您还可以使用图像分析技术来检测图像中多个条形码的ROI。

交互式地确定roi

I = imread(“multiple1DBarcodes.jpg”);

使用drawrectangle函数绘制并获取矩形参数。

Roi1 = drawrectangle;

pos = roi1.位置;

%使用drawrectangle获得的roiROI = [180 100 330 180 180 320 330 180 180 550 330 180];imSize = size(I);i = 1:size(roi,1) [msg, format, locs] = readBarcode(i, roi(i,:));disp (“解码格式和信息:”+格式+”、“+味精)插入一行表示条形码的扫描行。xyBegin = locs(1,:);I = insertShape(I,“行”,[1 xyBegin(2) imSize(2) xyBegin(2)],...“线宽”5);用解码的信息注释图像。I = insertText(I, xyBegin, msg,“BoxOpacity”, 1“字形大小”, 20);结束
解码格式和信息:UPC-A, 012345678905解码格式和信息:EAN-13, 4567891324562解码格式和信息:CODE-39, ABC-123
imshow(我)

图中包含一个轴对象。axis对象包含一个image类型的对象。

图像分析以确定roi

使用图像分析技术自动检测多种条形码。这需要对图像中的多个条形码进行本地化,确定它们的方向,并对方向进行校正。如果不进行预处理,则无法在包含多个旋转条形码的图像中检测到条形码。

I = imread(“multiple1DBarcodesRotated.jpg”);灰色= im2gray(I);显示图像。imshow(我)

图中包含一个轴对象。axis对象包含一个image类型的对象。

将未处理的图像传递给readBarcode函数。readBarcode (Igray“一维”
Ans = ""

对未处理的图像进行检测,结果没有检测到。

步骤1:使用MSER检测条形码的候选区域

方法检测图像中感兴趣的区域detectMSERFeatures函数。然后,您可以基于特定的标准(如纵横比)消除感兴趣的区域。您可以使用过滤结果中的二值图像进行进一步处理。

检测MSER特征。[~, cc] = detectMSERFeatures(灰色);计算区域属性majoraxisength和minoraxisength。regionStatistics = regionprops(cc,“MajorAxisLength”“MinorAxisLength”);过滤掉长径比低的不合适的组件%的候选条码中的条。minAspectRatio = 10;candidateRegions = find(([regionStatistics.MajorAxisLength]./[regionStatistics.MinorAxisLength]) > minAspectRatio);用于存储过滤后的组件的二进制图像。BW = false(大小(灰色));更新二进制映像。i = 1:length(candidateRegions) BW(cc.PixelIdxList{candidateRegions(i)}) = true;结束显示过滤后的二值图像。imshow (BW)标题(“条形码的候选区域”

图中包含一个轴对象。带有标题为条形码候选区域的axes对象包含一个类型为image的对象。

步骤2:利用霍夫变换提取条形码线段

方法检测图像中的突出边缘边缘函数。然后使用霍夫变换找到感兴趣的线。这些线表示条形码中竖条的可能候选对象。

%执行霍夫变换。BW = edge(BW,“精明”);[H,T,R] = hough(BW);显示边缘检测操作结果。imshow (BW)

图中包含一个轴对象。axis对象包含一个image类型的对象。

确定抑制邻域的大小。reductionRatio = 500;nhSize =地板(size(H)/reductionRatio);idx = mod(nhSize,2) < 1;nhSize(idx) = nhSize(idx) + 1;识别霍夫变换中的峰值。P = houghpeaks(H,length(candidateRegions),“NHoodSize”, nhSize);根据检测到的峰值检测线条。lines = houghlines(BW,T,R,P);显示使用houghlines函数检测到的行。Ihoughlines = ones(尺寸(BW));%检测到行的起始点和结束点。startPts =重塑([lines(:).]Point1], 2, length(lines))';endPts =重塑([lines(:).]Point2], 2, length(lines))';Ihoughlines =插入形状(Ihoughlines,“行”, [startPts, endPts],...“线宽”2,“颜色”“绿色”);显示原始图像与检测到的线条叠加。Ibarlines = imoverlay(I, ~Ihoughlines(:,:,1));imshow (Ibarlines)

图中包含一个轴对象。axis对象包含一个image类型的对象。

步骤3:在图像中本地化条形码

在提取线段后,提出了两种方法来定位图像中的单个条形码:

  • 方法1:基于聚类的技术,使用来自统计和机器学习工具箱™的功能来识别单个条形码。这种技术对于使用上面的图像分析技术检测到的异常值更稳健。它还可以扩展到广泛的成像条件,而无需调整参数。

  • 方法2:采用基于分割的工作流程分离单个条形码。该方法利用其他图像分析技术对提取的条形码进行定位和旋转校正。虽然这工作得相当好,但它可能需要一些参数调优,以防止检测到异常值。

方法一:基于聚类的工作流

在这个工作流中有两个步骤:

1.确定条形码线段的平分线

虽然通常的做法是直接使用线(使用Hough变换获得的线)来定位条形码,但此方法使用线来进一步检测每条线的垂直平分线。平分线表示为笛卡尔空间中的点,这使得它们适合于识别单个条形码。使用等分线使单个条形码的检测更加可靠,因为它减少了对相似但属于不同条形码的行的错误分类。

2.对平分线进行聚类以识别单个条形码

由于条形码中的所有条都近似地彼此平行,因此理想情况下,每个条的平分线应该是同一条线,因此它们对应的点应该聚集在一个点周围。在实践中,这些等分线将因段而异,但仍然保持足够相似,以允许使用基于密度的聚类算法。执行此群集操作的结果是一组群集,每个群集指向一个单独的条形码。本例使用dbscan(统计和机器学习工具箱)函数,该函数不需要预先了解集群的数量。不同的集群(条形码)在这个示例中是可视化的。

该示例检查统计和机器学习工具箱™许可证。如果找到了许可证,本例使用集群方法。除此之外,本例使用分割方法。

usecluster = license(“测试”“statistics_toolbox”);如果usecluster [boundingBox, orientation, Iclusters] = clusteringLocalization(lines, size(I));显示检测到的集群。imshow (Iclusters)其他的disp (“基于集群的工作流需要统计和机器学习工具箱的许可证”结束

图中包含一个轴对象。axis对象包含一个image类型的对象。

方法二:基于工作流的分割

去除背景噪声和变化后,使用形态学操作将检测到的竖条分组为单独的条形码,如imdilate.该示例使用regionprops函数确定每个条形码的包围框和方向。结果用于从原始图像中裁剪单个条形码,并将它们大致定位为水平。

如果~ usecluster [boundingBox, orientation, Idilated] = segmentationLocalization(Ihoughlines);显示放大后的图像。imshow (Idilated)结束

步骤4:裁剪条形码,并纠正他们的旋转

使用分割得到的边界框从原始图像中裁剪条形码。定向结果用于将条形码对齐为近似水平。

定位和旋转图像中的条形码。%correctedImages = cell(1,长度(方向));存储条形码的裁剪和旋转校正图像。i = 1:长度(方向)i = insertShape(i,“矩形”(我:),边界框(,大小)“线宽”3,“颜色”“红色”);如果方位(i) > 0方位(i) = -(90 -方位(i));其他的方位(i) = 90 +方位(i);结束从原始图像中裁剪条形码,并使用%检测方向。correctedImages{i} = imrotate(imcrop(灰色,boundingBox(i,:)), orientation(i));结束显示带有本地化条形码的图像。imshow(我)

图中包含一个轴对象。axis对象包含一个image类型的对象。

步骤5:检测裁剪和旋转校正图像中的条形码

裁剪和旋转校正图像的条形码,然后使用readBarcode函数来解码它们。

将每个图像传递给readBarcode函数。i = 1:length(correctedImages) [msg, format, ~] = readBarcode(correctedImages{i},“一维”);disp (“解码格式和信息:”+格式+”、“+味精)结束
解码格式和信息:UPC-A, 012345678905解码格式和信息:EAN-13, 4567891324562解码格式和信息:CODE-39, ABC-123

这个例子展示了如何readBarcode函数可用于检测,解码和定位图像中的条形码。虽然当条形码的对齐大致是水平或垂直时,该功能工作良好,但当条形码出现旋转时,它需要额外的预处理。上面详细介绍的预处理步骤是处理图像中未对齐的多个条形码的良好起点。

金宝app支持功能

clusteringLocalization使用基于聚类的工作流来本地化单个条形码。

函数[boundingBox, orientation, icluster] = clusteringLocalization(lines, imSize)%------------------------------------------------------------------------确定条形码线段的平分线%------------------------------------------------------------------------表,用于存储检测到的线的平分线的属性。linesBisector = array2table(0(长度(行),4),“VariableNames”, {“θ”的ρ“x”“y”});使用线条的方向值来确定方向。平分线的%值idxNeg = find([lines.]Theta] < 0);idxPos = find([lines.]Theta] >= 0);negAngles = 90 + [lines(idxNeg).theta];linesbisecor .theta(idxNeg) = negAngles;posAngles = [lines(idxPos)。Theta] - 90;linesBisector.theta(idxPos) = posAngles;确定检测到的线的中点。midPts = 0(长度(行),2);确定平分线的“rho”值。i = 1:长度(行)midPts(i,:) =(行(i)。Point1 + lines(i).point2)/2;linesBisector.rho(i) = abs(midPts(i,2) - tand(lines(i).theta) * midPts(i,1))/...((tand(lines(i).theta)²+ 1)^ 0.5);结束使用平分线的极坐标更新平分线的[x,y]位置%的坐标。[linesBisector。x, linesBisector。y] = pol2cart(deg2rad(linesBisector.theta),linesBisector.rho,“罗”);%------------------------------------------------------------------------对平分线进行聚类以识别单个条形码%------------------------------------------------------------------------存储用于聚类的平分线[x,y]数据。X = [linesbisecor . X, linesbisecor .y];得到两点之间的成对距离D = pdist2(X,X);%执行基于密度的空间聚类来分离不同的%条形码在图像中。searchRadius = max(imSize/5);minPoints = 10;idx = dbscan(D,searchRadius, minPoints);识别集群的数量(条形码)。numClusters = unique(idx(idx > 0));存储检测到的行的端点。dataXY = cell(1, length(numClusters));%图像显示检测到的集群(条形码)。Iclusters = ones(imSize);i = 1:length(numClusters) classIdx = find(idx == i);rgbColor = rand(1,3);startPts =重塑([lines(classsidx).]point1], 2,长度(classsidx))';endPts =重塑([lines(classsidx)。point2], 2,长度(classsidx))';插入与当前集群(条形码)对应的行。Iclusters = insertShape(“行”, [startPts, endPts],...“线宽”2,“颜色”, rgbColor);更新每个集群中行(条形码)的端点。dataXY{i} = [startPts;endPts];结束%------------------------------------------------------------------------%条形码的本地化参数%------------------------------------------------------------------------orientation = 0 (1,length(numClusters));boundingBox = 0(长度(numClusters), 4);填充条形码的裁剪图像。Padding = 40;确定单个集群(条形码)的ROI和方向。i = 1:length(numClusters)边框坐标与填充。x1 = min(dataXY{i}(:,1)) - padding;x2 = max(dataXY{i}(:,1)) + padding;y1 = min(dataXY{i}(:,2)) - padding;y2 = max(dataXY{i}(:,2)) + padding;boundingBox(i,:) = [x1, y1, x2-x1, y2-y1];%条形码的方向。orientation(i) = mean(linesBisector.)(idx == i));结束结束

segmentationLocalization使用基于分段的工作流来本地化单个条形码。

函数[boundingBox, orientation, Idilated] = segmentationLocalization(Ihoughlines)%------------------------------------------------------------------------%使用图像放大来分离条形码%------------------------------------------------------------------------用检测到的线创建二值图像。Ibw = ~Ihoughlines(:,:,1);Ibw(Ibw > 0) = true;使用磁盘结构元素扩展映像。。diskRadius = 10;%可能需要根据输入图像进行调整。Se = strel(“磁盘”, diskRadius);ililated =不扩张(Ibw, se);%------------------------------------------------------------------------%条形码的本地化参数%------------------------------------------------------------------------计算区域属性方向和边界框。regionStatistics = regionprops(已美化,“定位”的边界框(“大小));%条形码裁剪图像的填充。Padding = 40;boundingBox = 0(长度(regionStatistics), 4);idx = 1:length(regionStatistics) boundingBox(idx,:) = regionStatistics(idx).BoundingBox;边框坐标与填充。boundingBox(idx,1) = boundingBox(idx,1) -填充;boundingBox(idx,2) = boundingBox(idx,2) -填充;boundingBox(idx,3) = boundingBox(idx,3) + 2*padding;boundingBox(idx,4) = boundingBox(idx,4) + 2*padding;结束orientation = [regionStatistics(:).Orientation];结束

参考文献

[1]克鲁索,克莱门特,等。"野外的实时条码检测"IEEE计算机视觉应用冬季会议,2015。