exponenta event banner

Локализация и чтение нескольких штрихкодов в изображении

В этом примере показано, как использовать readBarcode функция из Toolbox™ Computer Vision для обнаружения и декодирования 1-D и 2-D штрихкодов в изображении. Штрих-коды широко используются для кодирования данных в визуальном машиночитаемом формате. Они полезны во многих приложениях, таких как идентификация номенклатуры, отслеживание складских запасов и отслеживание соответствия нормативным требованиям. Для 1-D штрихкодов readBarcode функция возвращает местоположение конечных точек штрихкода. Для 2-D штрихкодов функция возвращает местоположения шаблонов поиска. В этом примере используются два подхода для локализации нескольких штрихкодов в изображении. Одним из подходов является кластеризация, которая является более надежной к различным условиям визуализации и требует Toolbox™ статистики и машинного обучения. Второй подход использует рабочий процесс на основе сегментации и может потребовать настройки параметров на основе условий визуализации.

Обнаружение штрих-кода с помощью readBarcode Функция

Считывание QR-кода из изображения.

I = imread("barcodeQR.jpg");

% Search the image for a QR Code.
[msg, ~, loc] = readBarcode(I);

% Annotate the image with the decoded message.
xyText =  loc(2,:);
Imsg = insertText(I, xyText, msg, "BoxOpacity", 1, "FontSize", 25);

% Insert filled circles at the finder pattern locations.
Imsg = insertShape(Imsg, "FilledCircle", [loc, ...
    repmat(10, length(loc), 1)], "Color", "red", "Opacity", 1);

% Display image.
imshow(Imsg)

Figure contains an axes. The axes contains an object of type image.

Считывание штрихкода 1-D из изображения.

I = imread("barcode1D.jpg");

% Read the 1-D barcode and determine the format..
[msg, format, locs] = readBarcode(I);

% Display the detected message and format.
disp("Detected format and message: " + format + ", " + msg)
Detected format and message: EAN-13, 1234567890128
% Insert a line to show the scan row of the barcode.
xyBegin = locs(1,:); imSize = size(I);
I = insertShape(I,"Line",[1 xyBegin(2) imSize(2) xyBegin(2)], ...
    "LineWidth", 7);

% Insert markers at the end locations of the barcode.
I = insertShape(I, "FilledCircle", [locs, ...
    repmat(10, length(locs), 1)], "Color", "red", "Opacity", 1);

% Display image.
imshow(I)

Figure contains an axes. The axes contains an object of type image.

Улучшение обнаружения штрих-кодов

Для успешного обнаружения штрих-код должен быть хорошо виден. Штрих-код также должен быть максимально приближен к горизонтальному или вертикальному положению. readBarcode функция по своей сути более устойчива к вращениям для 2-D или матричных кодов, чем к 1-D или линейным штрихкодам. Например, штрих-код не может быть обнаружен в этом изображении.

I = imread("rotated1DBarcode.jpg");

% Display the image.
imshow(I)

Figure contains an axes. The axes contains an object of type image.

% Pass the image to the readBarcode function.
readBarcode(I)
ans = 
""

Поверните изображение с помощью imrotate чтобы штрих-код был примерно горизонтальным. Использовать readBarcode на повернутом изображении.

% Rotate the image by 30 degrees clockwise.
Irot = imrotate(I, -30);

% Display the rotated image.
imshow(Irot)

Figure contains an axes. The axes contains an object of type image.

% Pass the rotated image to the readBarcode function.
readBarcode(Irot)
ans = 
"012345678905"

Обнаружение нескольких штрихкодов

readBarcode функция обнаруживает только один штрих-код в каждом изображении. Для обнаружения нескольких штрихкодов необходимо указать область, представляющую интерес (ROI). Для определения ROI можно использовать drawrectangle функция для интерактивного определения значений ROI. Для определения окупаемости инвестиций в несколько штрихкодов изображения можно также использовать методы анализа изображений.

Интерактивное определение ROI

I = imread("multiple1DBarcodes.jpg");

Используйте drawrectangle функция для рисования и получения параметров прямоугольника.

roi1 = drawrectangle;

pos = roi1.Position;

% ROIs obtained using drawrectangle
roi = [180 100 330 180
    180 320 330 180
    180 550 330 180];

imSize = size(I);
for i = 1:size(roi,1)
    [msg, format, locs] = readBarcode(I, roi(i,:));
    disp("Decoded format and message: " + format + ", " + msg)
    
    % Insert a line to indicate the scan row of the barcode.
    xyBegin = locs(1,:);
    I = insertShape(I,"Line",[1 xyBegin(2) imSize(2) xyBegin(2)], ...
        "LineWidth", 5);
    
    % Annotate image with decoded message.
    I = insertText(I, xyBegin, msg, "BoxOpacity", 1, "FontSize", 20);
end
Decoded format and message: UPC-A, 012345678905
Decoded format and message: EAN-13, 4567891324562
Decoded format and message: CODE-39, ABC-123
imshow(I)

Figure contains an axes. The axes contains an object of type image.

Анализ изображений для определения ROI

Используйте методы анализа изображений для автоматизации обнаружения нескольких штрихкодов. Это требует локализации нескольких штрихкодов в изображении, определения их ориентации и корректировки для ориентации. Без предварительной обработки штрихкоды не могут быть обнаружены в изображении, содержащем несколько повернутых штрихкодов.

I = imread("multiple1DBarcodesRotated.jpg");
Igray = im2gray(I);

% Display the image.
imshow(I)

Figure contains an axes. The axes contains an object of type image.

% Pass the unprocessed image to the readBarcode function.
readBarcode(Igray, '1D')
ans = 
""

Обнаружение необработанного изображения не привело к обнаружению.

Шаг 1: Определение областей-кандидатов для штрихкодов с помощью MSER

Обнаружение областей, представляющих интерес для изображения, с помощью detectMSERFeatures функция. Затем можно исключить интересующие области на основе определенных критериев, таких как соотношение сторон. Для дальнейшей обработки можно использовать двоичное изображение из отфильтрованных результатов.

% Detect MSER features.
[~, cc] = detectMSERFeatures(Igray);

% Compute region properties MajorAxisLength and MinorAxisLength.
regionStatistics = regionprops(cc, 'MajorAxisLength', 'MinorAxisLength');

% Filter out components that have a low aspect ratio as unsuitable
% candidates for the bars in the barcode.
minAspectRatio = 10;
candidateRegions = find(([regionStatistics.MajorAxisLength]./[regionStatistics.MinorAxisLength]) > minAspectRatio);

% Binary image to store the filtered components.
BW = false(size(Igray));

% Update the binary image.
for i = 1:length(candidateRegions)
    BW(cc.PixelIdxList{candidateRegions(i)}) = true;
end

% Display the binary image with the filtered components.
imshow(BW)
title("Candidate regions for the barcodes")

Figure contains an axes. The axes with title Candidate regions for the barcodes contains an object of type image.

Шаг 2: Извлечение сегментов линии штрихкода с помощью преобразования

Обнаружение выдающихся кромок в изображении с помощью edge функция. Затем используйте преобразование Каф, чтобы найти интересующие строки. Линии представляют возможные кандидаты для вертикальных полос в штрихкоде.

% Perform hough transform.
BW = edge(BW,'canny');
[H,T,R] = hough(BW);

% Display the result of the edge detection operation.
imshow(BW)

Figure contains an axes. The axes contains an object of type image.

% Determine the size of the suppression neighborhood.
reductionRatio = 500;
nhSize = floor(size(H)/reductionRatio);
idx = mod(nhSize,2) < 1;
nhSize(idx) = nhSize(idx) + 1;

% Identify the peaks in the Hough transform.
P  = houghpeaks(H,length(candidateRegions),'NHoodSize',nhSize);

% Detect the lines based on the detected peaks.
lines = houghlines(BW,T,R,P);

% Display the lines detected using the houghlines function.
Ihoughlines = ones(size(BW));

% Start and end points of the detected lines.
startPts = reshape([lines(:).point1], 2, length(lines))';
endPts = reshape([lines(:).point2], 2, length(lines))';

Ihoughlines = insertShape(Ihoughlines, 'Line', [startPts, endPts], ...
    'LineWidth', 2, 'Color', 'green');

% Display the original image overlayed with the detected lines.
Ibarlines = imoverlay(I, ~Ihoughlines(:,:,1));
imshow(Ibarlines)

Figure contains an axes. The axes contains an object of type image.

Шаг 3: Локализация штрихкодов в изображении

После извлечения отрезков линии представлены два способа локализации отдельных штрихкодов на изображении:

  • Метод 1: Метод кластеризации, который использует функциональные возможности Toolbox™ статистики и машинного обучения для идентификации отдельных штрихкодов. Этот метод является более устойчивым к отклонениям, которые были обнаружены с использованием описанных выше методов анализа изображения. Он также может быть расширен до широкого диапазона условий формирования изображения без необходимости настройки параметров.

  • Метод 2: Основанный на сегментации рабочий процесс для разделения отдельных штрихкодов. Этот метод использует другие методы анализа изображений для локализации и коррекции поворота извлеченных штрихкодов. Хотя это работает достаточно хорошо, может потребоваться некоторая настройка параметров для предотвращения обнаружения отклонений.

Метод 1: Поток операций на основе кластеризации

В этом рабочем процессе есть два шага:

1. Определение биссекторов отрезков штрихкода

Хотя обычная практика заключается в непосредственном использовании линий (которые были получены с помощью преобразования Хафа) для локализации штрихкода, этот метод использует линии для дальнейшего обнаружения перпендикулярных биссектрисов для каждой из линий. Линии биссектрисы представлены в виде точек в декартовом пространстве, что делает их пригодными для идентификации отдельных штрихкодов. Использование биссектрисов делает обнаружение отдельных штрихкодов более надежным, поскольку это приводит к меньшим ошибкам в классификации линий, которые похожи, но принадлежат к различным штрихкодам.

2. Выполнить кластеризацию биссекторов для идентификации отдельных штрихкодов

Поскольку все стержни в штрихкоде приблизительно параллельны друг другу, биссектрисы каждого из этих стержней в идеале должны быть одной и той же линией, и поэтому их соответствующие точки должны объединяться вокруг одной точки. На практике эти биссектрисы будут варьироваться от сегмента к сегменту, но все еще остаются достаточно похожими, чтобы позволить использовать алгоритм кластеризации на основе плотности. Результатом выполнения этой операции кластеризации является набор кластеров, каждый из которых указывает на отдельный штрихкод. В этом примере используется dbscan (Statistics and Machine Learning Toolbox), которая не требует предварительного знания количества кластеров. В этом примере визуализируются различные кластеры (штрихкоды).

В примере проверяется наличие лицензии на Toolbox™ статистики и машинного обучения. Если лицензия найдена, в примере используется метод кластеризации. В противном случае в примере используется метод сегментации.

useClustering = license('test','statistics_toolbox');

if useClustering
    [boundingBox, orientation, Iclusters] = clusteringLocalization(lines, size(I));
    
    % Display the detected clusters.
    imshow(Iclusters)
else
    disp("The clustering based workflow requires a license for the Statistics and Machine Learning Toolbox")
end

Figure contains an axes. The axes contains an object of type image.

Метод 2: Поток операций на основе сегментации

После удаления фонового шума и изменения обнаруженные вертикальные полосы группируются в отдельные штрихкоды с использованием морфологических операций, например, imdilate. В примере используется regionprops для определения ограничивающей рамки и ориентации для каждого из штрихкодов. Результаты используются для обрезки отдельных штрихкодов из исходного изображения и для их ориентирования примерно по горизонтали.

if ~useClustering
    [boundingBox, orientation, Idilated] = segmentationLocalization(Ihoughlines);
    
    % Display the dilated image.
    imshow(Idilated)
end

Шаг 4. Обрезать штрихкоды и скорректировать их вращение

Штрих-коды обрезаются из исходного изображения с помощью ограничивающих рамок, полученных в результате сегментации. Результаты ориентации используются для выравнивания штрихкодов приблизительно по горизонтали.

% Localize and rotate the barcodes in the image.
correctedImages = cell(1, length(orientation));

% Store the cropped and rotation corrected images of the barcodes.
for i = 1:length(orientation)
    
    I = insertShape(I, 'Rectangle', boundingBox(i,:), 'LineWidth',3, 'Color', 'red');
    
    if orientation(i) > 0
        orientation(i) = -(90 - orientation(i));
    else
        orientation(i) = 90 + orientation(i);
    end
    
    % Crop the barcode from the original image and rotate it using the
    % detected orientation.
    correctedImages{i} = imrotate(imcrop(Igray,boundingBox(i,:)), orientation(i));
end

% Display the image with the localized barcodes.
imshow(I)

Figure contains an axes. The axes contains an object of type image.

Шаг 5: Определение штрихкодов на кадрированных и скорректированных по повороту изображениях

Обрезанные и скорректированные на поворот изображения штрихкодов затем используются вместе с readBarcode функция их декодирования.

% Pass each of the images to the readBarcode function.
for i = 1:length(correctedImages)
    [msg, format, ~] = readBarcode(correctedImages{i}, '1D');
    disp("Decoded format and message: " + format + ", " + msg)
end
Decoded format and message: UPC-A, 012345678905
Decoded format and message: EAN-13, 4567891324562
Decoded format and message: CODE-39, ABC-123

В этом примере показано, как readBarcode функция может использоваться для обнаружения, декодирования и локализации штрихкодов в изображении. Хотя функция хорошо работает, когда выравнивание штрихкодов примерно горизонтально или вертикально, она нуждается в дополнительной предварительной обработке, когда штрихкоды появляются повернутыми. Этапы предварительной обработки, описанные выше, являются хорошей отправной точкой для работы с несколькими штрихкодами, которые не выровнены в изображении.

Вспомогательные функции

clusteringLocalization использует рабочий процесс на основе кластеризации для локализации отдельных штрихкодов.

function [boundingBox, orientation, Iclusters] = clusteringLocalization(lines, imSize)

%------------------------------------------------------------------------
% Determine Bisectors of Barcode Line Segments
%------------------------------------------------------------------------

% Table to store the properties of the bisectors of the detected lines.
linesBisector = array2table(zeros(length(lines), 4), 'VariableNames', {'theta', 'rho', 'x', 'y'});

% Use the orientation values of the lines to determine the orientation.
% values of the bisectors
idxNeg = find([lines.theta] < 0);
idxPos = find([lines.theta] >= 0);

negAngles = 90 + [lines(idxNeg).theta];
linesBisector.theta(idxNeg) = negAngles;

posAngles = [lines(idxPos).theta] - 90;
linesBisector.theta(idxPos) = posAngles;

% Determine the midpoints of the detected lines.
midPts = zeros(length(lines),2);

% Determine the 'rho' values of the bisectors.
for i = 1:length(lines)
    midPts(i,:) = (lines(i).point1 + lines(i).point2)/2;
    linesBisector.rho(i) = abs(midPts(i,2) - tand(lines(i).theta) * midPts(i,1))/...
        ((tand(lines(i).theta)^2 + 1) ^ 0.5);
end

% Update the [x,y] locations of the bisectors using their polar
% coordinates.
[linesBisector.x, linesBisector.y] = pol2cart(deg2rad(linesBisector.theta),linesBisector.rho,'ro');

%------------------------------------------------------------------------
% Perform Clustering on the Bisectors to Identity the Individual Barcodes
%------------------------------------------------------------------------

% Store the [x,y] data of the bisectors to be used for clustering.
X = [linesBisector.x,linesBisector.y];

% Get pairwise distance between the points
D = pdist2(X,X);

% Perform density-based spatial clustering to separate the different
% barcodes in the image.
searchRadius = max(imSize/5);
minPoints = 10;
idx = dbscan(D,searchRadius, minPoints);

% Identify the number of clusters (barcodes).
numClusters = unique(idx(idx > 0));

% Store the endpoints of the detected lines.
dataXY = cell(1, length(numClusters));

% Image to show the detected clusters (barcodes).
Iclusters = ones(imSize);

for i = 1:length(numClusters)
    classIdx = find(idx == i);
    
    rgbColor = rand(1,3);
    startPts = reshape([lines(classIdx).point1], 2, length(classIdx))';
    endPts = reshape([lines(classIdx).point2], 2, length(classIdx))';
    
    % Insert lines corresponding to the current cluster (barcode).
    Iclusters = insertShape(Iclusters, 'Line', [startPts, endPts], ...
        'LineWidth', 2, 'Color', rgbColor);
    
    % Update the endpoints of the lines in each cluster (barcode).
    dataXY{i} = [startPts; endPts];
end

%------------------------------------------------------------------------
% Localization parameters for the barcode
%------------------------------------------------------------------------

orientation = zeros(1,length(numClusters));
boundingBox = zeros(length(numClusters), 4);

% Padding the cropped images of barcodes.
padding = 40;

% Determine the ROI and orientation of the individual clusters (barcodes).
for i = 1:length(numClusters)
    
    % Bounding box coordinates with padding.
    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 of the barcode.
    orientation(i) = mean(linesBisector.theta(idx == i));
    
end

end

segingedLocalization использует рабочий процесс на основе сегментации для локализации отдельных штрихкодов.

function [boundingBox, orientation, Idilated] = segmentationLocalization(Ihoughlines)

%------------------------------------------------------------------------
% Use image dilation to separate the barcodes
%------------------------------------------------------------------------

% Create binary image with the detected lines.
Ibw = ~Ihoughlines(:,:,1);
Ibw(Ibw > 0) = true;

% Dilate the image using a disk structuring element.
diskRadius = 10; % Might need tuning depending on the input image.
se = strel('disk', diskRadius);
Idilated = imdilate(Ibw, se);

%------------------------------------------------------------------------
% Localization parameters for the barcode
%------------------------------------------------------------------------

% Compute region properties Orientation and BoundingBox.
regionStatistics = regionprops(Idilated, 'Orientation', 'BoundingBox');

% Padding for the cropped images of barcodes.
padding = 40;

boundingBox = zeros(length(regionStatistics), 4);

for idx = 1:length(regionStatistics)
    
    boundingBox(idx,:) = regionStatistics(idx).BoundingBox;
    
    % Bounding box coordinates with padding.
    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;
    
end

orientation = [regionStatistics(:).Orientation];

end

Ссылки

[1] Крезот, Мягкий, и др. «Обнаружение штрих-кода в Wild в реальном времени». Зимняя конференция IEEE по применению компьютерного зрения, 2015.