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

В этом примере показано, как использовать readBarcode функция от Computer Vision Toolbox™, чтобы обнаружить и декодировать 1D и 2D штрихкоды в изображении. Штрихкоды широко используются, чтобы закодировать данные в визуальном, машиночитаемом формате. Они полезны во многих приложениях, таких как идентификация элемента, складское отслеживание ресурсов и отслеживание податливости. Для 1D штрихкодов, readBarcode функция возвращает местоположение конечных точек штрихкода. Для 2D штрихкодов функция возвращает местоположения шаблонов средства поиска. Этот пример использует два подхода для локализации нескольких штрихкодов в изображении. Один подход основан на кластеризации, который более устойчив к различным условиям обработки изображений и требует Statistics and Machine Learning 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 object. The axes object contains an object of type image.

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

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 object. The axes object contains an object of type image.

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

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

I = imread("rotated1DBarcode.jpg");

% Display the image.
imshow(I)

Figure contains an axes object. The axes object 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 object. The axes object contains an object of type image.

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

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

readBarcode функция обнаруживает только один штрихкод в каждом изображении. Для того, чтобы обнаружить несколько штрихкодов, необходимо задать видимую область (ROI). Чтобы задать ROI, можно использовать drawrectangle функционируйте, чтобы в интерактивном режиме определить ROI. Можно также использовать методы анализа изображения, чтобы обнаружить 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 object. The axes object contains an object of type image.

Анализ изображения, чтобы определить ROI

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

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

% Display the image.
imshow(I)

Figure contains an axes object. The axes object 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 object. The axes object 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 object. The axes object 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 object. The axes object contains an object of type image.

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

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

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

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

Метод 1: Кластеризация основанного рабочего процесса

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

1. Определите биссектрисы линейных сегментов штрихкода

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

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

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

Пример проверяет на лицензию Statistics and Machine Learning 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 object. The axes object 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 object. The axes object 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

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

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] Крезот, Мягкий, и др. "Обнаружение Штрихкода В реальном времени в Дикой природе". Зимняя Конференция по IEEE по Приложениям Компьютерного зрения, 2015.