DSB18: Barcode erzeugen und lesen

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen

Autor: Tobias Brandt
Betreuer: Prof. Schneider

Ein Beispiel für einen Barcode. Hier: 'HSHL'

Motivation

Barcodes begleiten unseren Alltag bereits seid Jahrzehnten. Sie codieren Preise im Einzelhandel, ermöglichen das Verfolgen von Briefen und Paketen oder sind als Teil der Stempelkarte Teil der Zeiterfassung in der Industrie. Doch wie werden Barcodes erzeugt und gelesen? Dieser Artikel soll einen kurzen Einblick in die Notation, die Erstellung und das letzlich das Lesen des speziellen CODE39 geben. Integration des Programms erfolgt in Matlab.

Aufgabenstellung

Erzeugen Sie ein Matlab Executable, welches ein Barcode erzeugt. Mit einer Webcam soll ein beliebiger Barcode auf einem Dokument unabhängig von Position und Lage eingescannt werden können.

Anforderungen

  1. Arbeiten Sie sich in die Erstellung von Barcodes ein.
  2. Erzeugen Sie ein Matlab Executable, welches aus einen Barcode erzeugt.
  3. Lesen Sie mit Matlab über eine Webcam diesen Barcode ein, so dass die dahinterliegenden Daten angezeigt werden.
  4. Wissenschaftliche Dokumentation als HSHL-Wiki Artikel
  5. Softwareentwicklung nach SDE Standard in SVN
  6. Funktionsnachweis als YouTube-Video (vgl. Veranstaltungsregeln)

Prinzip Code39

CODE39 Zeichentabelle

Der Code 39 ist ein diskreter Code mit variabler Codelänge (Wenn mehr Zeichen dargestllt werden sollen, verlängert sich auch der Code). Der Code wurde 1973 erfunden und war somit der erste alphanumerische Barcode. Im Grundsatz besteht der barcode aus weißen und schwarzen Strichen.

Der Name ("Code 3 of 9") geht auf die Struktur der Kodierung zurück. Dabei werden die Zeichen der zu kodierenden Nachricht durch 9 schwarze und weiße Balken repräsentiert. Drei dieser Balken sind breit und der rest schmal. Jeder Block, dieser ein Zeichen darstellt, besteht aus 5 Strichen und 4 Lücken. Getrennt sind die Blöcke durch eine schmale weiße Lücke. Eine nachricht im Code39 beginnt und endet jeweils mit einem Sternchen '*'. Dies wird auch als Start-oder Stoppzeichen bezeichnet.

Der Standartcode verfügt über keine Prüfziffern und kann folgende Zeichen darstellen:

0123456789[Space]ABCDEFGHIJKLMNOPQRSTUVWXYZ-.$/+%

Die Kodierung der einzelnen Zeichen ist in der rechts abgebildeten Zeichentabelle festgelegt.

In der Folgenden Auflistung werden die Vor-und Nachteile Stichpunktartig aufgeführt:

Vorteile:

  • große Lesesicherheit
  • sehr weit verbreitet
  • einfach in der Produktion


Nachteile:

  • geringe Code Dichte
  • keine Prüfsumme

Implementierung Barcode erstellen

Um einen Barcode zu erstellen wird zunächst die erforderliche Höhe und Breite des Bilds was benötigt wird berechnet. Danach wird als Grundlage ein weißes Bild als JPG erstellt.

function barcode_erstellen (eingabe,s_element,b_element)
%%Funktionsbeschreibung
%--------------------------------------------------------------------------
%  Programmname:    barcode_erstellen(eingabe,s_element,b_breite)
%       Version:    1.0.0
%         Datum:    12.05.2018
%  Beschreibung:    Das Programm erstellt einen 1D Barcode in CODE39 Format
%   
%        Autor:    Tobias Brandt
%
%         INPUT:    eingabe: Zeichen die in einen Code39 umgewandelt werden
%                            sollen. (Nur Großbuchstaben, Zahlen und
%                            Sonderzeichen '-';'.';' ';'$';'+';'%')
%                    s_element: Dicke eines schmalen Elments in Pixel
%                    b_element: Dicke eines breiten Elements in Pixel
%
%                   Die Relation von Höhe und Breite muss 1:2 sein.
%        
%        OUTPUT:    Das Programm erstellt einen Bracode mit den
%                   eingegebenen Daten als .jpg.
%                   Der Code beginnt und endet jeweils mit einem *
%
%       Beispiel:   barcode_erstellen('HSHL',10,20)
%
%    
%--------------------------------------------------------------------------

%% Leeres Bild erzeuegen
% Länge des Barcode in Pixel bestimmen
%(length(eingabe)+2)=Anzahl der Zeichen, wegen 2x Sterne
%*(6*schmal+3*breit) wegen 3 schmalen Strichen, 3schmalen Lücken, 2 breite Striche, 1 breiten Lücke
%((length(eingabe)+1))= Anzahl der Lücken
Laenge = ((length(eingabe)+2)*(6*s_element+3*b_element))+((length(eingabe)+1)*s_element);
% Höhe des Barcodes damit evtl. noch Platz ist für einen Text
Hoehe = 10*b_element; 
Breite= b_element*6 + Hoehe;
% Schwarzes Bild erzeugen
Barcode = zeros(Breite,Laenge,3);



% schwarzes Bild zu weiß konvertieren
for k=1:3
for i=1:Breite
    for j=1:Laenge+(4*b_element+s_element)
        Barcode(i,j,k)=255;
    end
end
end

Hier nach wird als Anfangszeichen ein Sternchen gezeichnet. Das bilden der Striche übernehmen hierfür extra angelegte Funktionen. Darauf folgen abfragen welches Zeichen codiert werden muss. dementsprechen werden die Funktionen um Striche und Lücken zu bilden aufgerufen. Aus Gründen der Übersichtlichkeit wird nur die Abfrage für den Buchstaben A dargestellt.


% "*" als erstes Zeichen einfügen 
%Startzeichen "*" einfügen
[xkoordinate,Barcode] = breit_luecke(xkoordinate,Barcode,b_element,Hoehe);
[xkoordinate,Barcode] = breit_luecke(xkoordinate,Barcode,b_element,Hoehe);
[xkoordinate,Barcode] = schmal_strich(xkoordinate,Barcode,s_element,Hoehe);
[xkoordinate,Barcode] = breit_luecke(xkoordinate,Barcode,b_element,Hoehe);
[xkoordinate,Barcode] = schmal_strich(xkoordinate,Barcode,s_element,Hoehe);
[xkoordinate,Barcode] = schmal_luecke(xkoordinate,Barcode,s_element,Hoehe);
[xkoordinate,Barcode] = breit_strich(xkoordinate,Barcode,b_element,Hoehe);
[xkoordinate,Barcode] = schmal_luecke(xkoordinate,Barcode,s_element,Hoehe);
[xkoordinate,Barcode,] = breit_strich(xkoordinate,Barcode,b_element,Hoehe);
[xkoordinate,Barcode] = schmal_luecke(xkoordinate,Barcode,s_element,Hoehe);
[xkoordinate,Barcode] = schmal_strich(xkoordinate,Barcode,s_element,Hoehe);
[xkoordinate,Barcode] = schmal_luecke(xkoordinate,Barcode,s_element,Hoehe);

for i=1:length(eingabe)
    %Bei Eingabe von A wird der Code generiert
    if eingabe(i) == 'A'
       [xkoordinate,Barcode] = breit_strich(xkoordinate,Barcode,b_element,Hoehe);
       [xkoordinate,Barcode] = schmal_luecke(xkoordinate,Barcode,s_element,Hoehe);
       [xkoordinate,Barcode] = schmal_strich(xkoordinate,Barcode,s_element,Hoehe);
       [xkoordinate,Barcode] = schmal_luecke(xkoordinate,Barcode,s_element,Hoehe);
       [xkoordinate,Barcode] = schmal_strich(xkoordinate,Barcode,s_element,Hoehe);
       [xkoordinate,Barcode] = breit_luecke(xkoordinate,Barcode,b_element,Hoehe);
       [xkoordinate,Barcode] = schmal_strich(xkoordinate,Barcode,s_element,Hoehe);
       [xkoordinate,Barcode] = schmal_luecke(xkoordinate,Barcode,s_element,Hoehe);
       [xkoordinate,Barcode] = breit_strich(xkoordinate,Barcode,b_element,Hoehe);
       [xkoordinate,Barcode] = schmal_luecke(xkoordinate,Barcode,s_element,Hoehe);    
    end

Anschließend wird der Barcode abgespeichert und angezeigt

%% Dateiname erstellen
a{1} = eingabe;
a{2} = '.JPG';
Dateiname = [a{1} a{2}];
%% Barcode speichern
imwrite(Barcode,Dateiname)
Ausgabetext=['Erstellen und Speichern von ',Dateiname,' fertig gestellt'];
disp('-----------------------------------------------------------')
disp(Ausgabetext)
disp('-----------------------------------------------------------')
%%Barcode anzeigen
imshow(Barcode);

end

Implementierung Barcode lesen

Der zuvor erstellte Barcode kann über diese Executable über eine Webcam eingelesen werden. Wenn ein Barcode erkannt wurde, wird dieser decodiert, angezeigt und das Programm beendet. Für einen weiteren Scan kann die executable erneut gestartet werden.

Im folgenden soll die Funktionsweise kurz Zusammengefasst werden, Detailangaben finden sich als Kommentare im Quelltext. Zunächst wird das zu analysierende Bild eingelesen und in ein Schwarz/Weiss Bild umgewandelt. Danach sorgen verschiedene Filter zunächst dazu kleine Partikel zu enfernen. Größere Objekte die den Bildrand berühren werden ebenfalls entfernt.

%--------------------------------------------------------------------------
%  Programmname:    Barcode_aus_Webcam_lesen
%       Version:    1.0.0
%         Datum:    01.07.2018
%  Beschreibung:    Das Programm liest einen 1D Barcode in CODE39 Format
%                   mit einer Webcam ein und stellt die dahinter liegenden
%                   Informationen dar.
%   
%        Autor:    Tobias Brandt
%
%         INPUT:    Webcambild (image)
%        
%        OUTPUT:    Der entschlüsselte Barcode
%
%    
%--------------------------------------------------------------------------

%% Initialisierung
clear all; 
close all;
clear cam;            % alte Videoverbindungen schließen
clc;

%% Kamera initialisieren

camList = webcamlist % Verfügbare Webcams auflisten
cam = webcam(1);      % zur Webcam verbinden, Einstellungen anzeigen

%% Endlosschleife für das Kamerabild

while 1
    
image = snapshot(cam);          %Bild von Kamera erstellen  
B = rgb2gray(image);            %umwandeln des Bilds in ein Graustufenbild

subplot(3,3,1);                 %Bild zur Kontrolle ausgeben
imshow(B);
title('Grau');

thresh = graythresh(B);         % automatische Bestimmung eines Schwellenwertes, um Vorder- und Hintergrund zu unterscheiden
C = im2bw(B,thresh);            % Umwandeln des Graustufenbildes in ein b/w-Bild 
C = ~C;                         % Bild invertieren

subplot(3,3,2);                 % Bild zur Kontrolle ausgeben
imshow(C);
title('BW');

C = imclearborder(C,4);         % alle zusammenhängende Objekte mit Kontakt zum rand werden '0' geschrieben
ThresholdBarcode = imfill(C,'holes'); % alle schwarzen Bereiche, die komlett von weißen Bereichen umgeben sind, auf weiß gesetzt und damit zu dem Objekt hinzugefügt

Nach dem Aufbereiten des Bilds wird ein Blob erzeugt um den Code einfach in dem Bild zu lokalisiern. Durch die darauf folgenden Berechnungen kann eine Zeile des Barcodes seperiert werden. Die Bloberkennung und die Barcodelokalisierung werden hier nicht erwähnt.

%% Hoehe des Barcodes ermitteln um die mittlere Zeile zu berechnen

% Y-Barcodekoordinate oben links erkennen

yoben=0;                     %Variable um die Y-Barcodekoordinate oben links zu speichern
for y=1:1:RowNum
    for x=1:1:ColumNum
    if image(y,x,1)==255
        if yoben==0
            yoben=y;         
        end
    end
    end
end
%Y-Barcodekoordinate unten links erkennen

yunten=0;                   %Variable um die Y-Barcodekoordinate unten links zu speichern
for y=RowNum:-1:1
    for x=1:1:ColumNum
    if image(y,x,1)==255
        if yunten==0
            yunten=y;         
        end
    end
    end
end
ylang=yunten-yoben;         % Die Höhe des Barcodes berechnen

%% mittlere zeile aus dem schwarz-weiß Barcode in Zeile[] kopieren
mitte=round(yoben+ylang/2);
zeile=[];

if mitte>0
    for i=1:1:ColumNum
          zeile(i) = ThresholdBarcode(mitte,i);
    end
end

Für die spätere Dekodierung wird aus der erkannten Zeile mit Binärzahlen ein Array erstellt in dem die Striche und Luecken erkannt wurden.

schmaler Strich = b breiter Strich = B schmale Lücke =w breite Lücke = W

%% gleiche bereiche Zählen

bereiche=[];   % Arraay für gleiche Bereiche
zaehler=0;     % Zähler für die gleichen Bereiche

%gleiche aufeinanderfolgende pixel in einem Bereich zusammen fassen
for i=1:1:length(zeile)
    if i<length(zeile)
        if zeile(i)==zeile(i+1)
            zaehler=zaehler+1;
        else 
            zaehler=zaehler+1;
            bereiche= [bereiche zaehler]; % aneinanderfügen der Bereiche in dem Array
            zaehler=0;
        end
    end
end
% Der letzte weiße Bereich nach dem Barcode wird nicht abgespeichert, da
% der else Fall nicht eintritt. Deswegen braucht das letzte Element nicht
% gelöscht werden

if(length(bereiche))>0
bereiche(1)=[];  %erstes Element des Arrays löschen, da dies der weiße Bereich vor dem Barcode ist
end
%% breite der Striche festlegen

breit = max(bereiche);     %größtes Element des Arrays ist der breiteste Strich
schmal = min(bereiche);    % kleinstes Element des arrays ist der schmalste Strich
luecke = floor((breit-schmal)/2); % Als Hysterese wird der Zwischenbereich gleichermaßen für schmal und groß aufgeteilt


%%Striche in Array erkennen
%b= schmaler Strich; B= breiter Strich 
%w= schmale Lücke; W= breite Lücke

striche=[]; %Array in dem die Kennzeichen für Lücke und Striche gespeichert werden

% Striche werden erkannt und durch b oder B an jeder geraden Stelle abgespeichert
% da der Code immer mit einem Strich beginnt und abwechselnd eine Lücke
% folgt
for i=1:2:(length(bereiche))
     if (bereiche(i)>=schmal && bereiche(i)<=schmal+luecke)
         striche(i)='b';
     end
     if (bereiche(i)>=breit-luecke && bereiche(i)<=breit)
         striche(i)='B';
     end
end

%Luecken in Array erkennen
for i=2:2:(length(bereiche))
     if (bereiche(i)>=schmal && bereiche(i)<=schmal+luecke)
         striche(i)='w';
     end
     if (bereiche(i)>=breit-luecke && bereiche(i)<=breit)
         striche(i)='W';
     end
end

Für die Erkennung der einzelnen zeichen muss eine Startbedingung definiert werden, da ansonsten ein Fehler der Matzrixdimension entstehen würde. Also erkennung wurden hier die beiden Sternchen am Anfang und am Ende, sowie die Strichanzahl verwendet.


%% Bedingung für Entziffern definieren
% Ein Code39 Barcode beginnt und endet immer mit einem '*' 
% Deswegen wird eine Anfangsbedingung definiert, dass die beiden Sterne
% erkannt werden müssen, bevor die Dekodierung beginnt. So werden Fehler
% vermieden.


l=1;                  % Anfangswert wo das nächste Zeichen entschlüsselt wird
s='';                 % variable für das einzelne erkannte Zeichen
Output=[];            % gesamter Output
kontrolle=0;          % Startvariable für die Dekodierung
compare=[];           % Wert aus dem Array der verglichen werden soll
stern='bWbwBwBwb';    % Zeichenfolge für einen Stern als Strich und Lücke

if mod((length(striche)-19),10)==0 % Nur starten wenn die Strich und Lückenanzahl zwei Sterne lang und der Rest durch 10 teilbar ist, ansonsten fehlt etwas oder ist zu viel
    for i=1:1:9
        compare(i)=striche(i);     % gucken ob die ersten 9 Zeichen einem Stern entsprechen
    end
    if compare == stern;  
      
     for i=length(striche)-8:1:length(striche) % gucken ob die letzten 9 Zeichen einem Stern entsprechen
        compare(i-(length(striche)-9))=striche(i); 
    end
    if compare == stern;  
       kontrolle=1;                 % wenn die Anfangsbedingung erfüllt ist, Startvariable auf '1' setzen
    end
    end
end

%% Einzelne Zeichen erkennnen
% für jedes Zeichen ist eine Lücken und Strichfolge vorgegeben. Diese wird
% mit der abfolge in dem Array verglichen und der dahinter liegende Wert
% als Output abgespeichert z.B. entspricht 'bWbwBwBwb' einen Stern

if kontrolle==1
for i=9:10:length(striche)          % Das Strice Array im 10er Schritt durchlaufen, da ein Zeichen aus 5 Strichen und 5 Lücken besteht
    if striche(l:i) == 'bWbwBwBwb'  
        s ='*';
    elseif striche(l:i) == 'bwbWBwBwb'
        s ='0';

Das entschlüsselte Zeichen wird in einem letzten Schritt in das Ausgabearray "OUTPUT" hinzugefügt und ausgegeben.

Verwendete Geräte

Für das Einlesen des Barcodes wurde die Webcam Logitec HD Webcam C310 verwendet. Die Videoauflösung beträgt 1.280 x 720 Pixel.

Beispielbilder

Folgende Bilder wurden mit dem Programm Barcode_erstellen erstellt.

Für das Einlesen der Barcodes wurden diese ausgedruckt und teilweise mit Störungen versehen. Im folgenden sind einige davon dargestellt. Es wird immer der HSHL Barcode dargestellt. Die Störungen sind unter den Bildern beschrieben.


Youtube Video

Weblinks

Literatur

  • Bernhard Lenk: Handbuch der automatischen Identifikation. Band 1-3: 2D-Codes, Matrixcodes, Stapelcodes, Composite Codes, Dotcodes. Lenk Monika Fachbuchverlag, Kirchheim unter Teck 2002, ISBN 978-3-935551-01-4.
  • ten Hompel, M., u.A.: Identifikationssysteme und Automatisierung. Heidelberg: Springer, 2008. ISBN 978-3-540-75880-8

BSD-Lizenzbedingung BSD-Lizenz

Copyright (c) 2018, Hochschule Hamm-Lippstadt, Dep. Lip. 1, Prof. Schneider
Hochschule Hamm-Lippstadt. Alle Rechte vorbehalten.




→ zurück zum Hauptartikel: Digitale Signal- und Bildverarbeitung SoSe2018