主要内容

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

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

使用readBarcode函数

从图片中读取二维码。

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

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

从图像中读取一维条形码。

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

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

提高条形码检测

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

我= 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

我= imread (“multiple1DBarcodes.jpg”);

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

roi1 = drawrectangle;

pos = roi1.Position;

% roi使用drawrectangle获得ROI = [180 100 330 180 180 320 330 180 550 330 180];imSize =大小(I);i = 1:size(roi,1) [msg, format, locs] = readBarcode(i, roi(i,:));disp (“解码格式和信息:”+格式+", "+味精)%插入一行以指示条形码的扫描行。: xyBegin = loc (1);我= insertShape (,“行”,[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

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

我= imread (“multiple1DBarcodesRotated.jpg”);Igray = im2gray(我);%显示图像。imshow(我)

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

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

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

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

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

%检测MSER特征。[~, cc] = detectMSERFeatures(Igray);%计算区域属性majoraxlength和minoraxlength。regionStatistics = regionprops (cc、“MajorAxisLength”“MinorAxisLength”);%滤除低纵横比的组件%条形码中的栏的候选项。minAspectRatio = 10;regionstatistics . minoraxlength = find(([regionstatistics . majoraxlength]./[regionstatistics . minoraxlength]) > minAspectRatio);%二进制图像存储过滤后的组件。BW = false(大小(Igray));%更新二进制图像。i = 1:length(candidatregions) BW(cc.PixelIdxList{candidatregions (i)}) = true;结束%显示带有过滤成分的二值图像。imshow (BW)标题(“条形码的候选区域”

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

步骤2:利用hough变换提取条码线段

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

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

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

%确定抑制邻居的大小。reductionRatio = 500;nhSize =地板(大小(H) / reductionRatio);idx = mod(nhSize,2) < 1;nhSize(idx) = nhSize(idx) + 1;识别Hough变换中的峰值。P = houghpeaks (H,长度(candidateRegions),“NHoodSize”, nhSize);%根据检测到的峰值检测线路。行= houghlines (BW T R P);%显示使用粗线功能检测到的线。Ihoughlines = 1(大小(BW));%被检测行的起始和结束点。startPts =重塑(((:行)。point1), 2、长度()行)';endPts =重塑(((:行)。卷帘窗),2、长度()行)';Ihoughlines = insertShape (Ihoughlines,“行”(startPts endPts),...“线宽”2,“颜色”“绿色”);显示与检测线重叠的原始图像。Ibarlines = imoverlay(I, ~Ihoughlines(:,: 1));imshow (Ibarlines)

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

步骤3:定位图像中的条形码

提取线段后,提出两种方法对图像中的单个条码进行定位:

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

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

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

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

1.确定条码线段的等分线

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

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

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

该示例检查统计和机器学习工具箱™许可。如果找到license,本例采用聚类方法。否则,本例将使用分割方法。

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

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

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

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

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

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

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

定位和旋转图像中的条形码。correctedImages = cell(1, length(orientation));%存储裁剪和旋转校正的条码图像。i = 1:长度(方向)i = insertShape(i,“矩形”(我:),边界框(,大小)“线宽”3,“颜色”“红色”);如果> 0 Orientation (i) = -(90 - Orientation (i));其他的方向(i) = 90 +方向(i)结束从原始图像中裁剪条形码并使用%检测方向。correctedImages{i} = imrotate(imcrop(Igray,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函数可用于检测、解码和定位图像中的条形码。当条形码大致水平或垂直对齐时,该功能可以正常工作,但当条形码出现旋转时,它需要额外的预处理。上面详细介绍的预处理步骤是处理图像中没有对齐的多个条形码的良好起点。

万博1manbetx支持功能

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

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

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

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

参考文献

Creusot, Clement,等。《野外实时条码检测》计算机视觉技术,2015。