SigSys15 Barcode erzeugen und lesen: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
(Änderung 15826 von Steffen SchulzeMiddendorf (Diskussion) rückgängig gemacht.) |
||
(81 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
'''Autor: Steffen Schulze Middendorf | '''Autor:''' [[Benutzer: Steffen_SchulzeMiddendorf| Steffen Schulze Middendorf]]<br/> | ||
'''Betreuer:''' [[Benutzer:Ulrich_Schneider| Prof. Schneider]] | |||
[[Datei: | [[Datei:HSHL.JPG|mini|Ein Beispiel für einen Barcode. Hier: 'HSHL']] | ||
== Motivation == | == 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. | |||
== | == CODE39 == | ||
[[Datei:CODE39 Codierung.png|mini|CODE39 Zeichentabelle]] | |||
= | Der CODE39 ist in der ISO/IEC 16388<ref>[http://www.iso.org/iso/catalogue_detail.htm?csnumber=43897 ISO/IEC 16388:2007 Information technology -- Automatic identification and data capture techniques -- Code 39 bar code symbology specification] ''[[Internationale Organisation für Normung|International Organization for Standardization]]''</ref> spezifiziert, grundsätzlich besteht dieser aus Strichen und Lücken, jeweils in schmal und in breit.Es handelt sich um einen diskreten Code, das heisst jeder Buchstabe beginnt und endet mit einem Strich. Zwischen jedem Buchstaben findet sich zur Abgrenzung eine schmale Lücke. Jeder erzeugte Strichcode beginnt und endet mit einem Sternchen(*). Man spricht hier von einem Start/Stopp-Zeichen. | ||
In der Tabelle rechts ist der gesamte verfügbare Zeichensatz dargestellt. | |||
In der Standart Variante verfügt der CODE39 über keine Prüfziffern. Im folgenden sollen die Vor-und Nachteile stichpunktartig dargestellt werden. | |||
'''Vorteile:''' | |||
*robust | |||
*hohe Drucktoleranzen akzeptabel | |||
'''Nachteile:''' | |||
*geringe Code Dichte | |||
*keine Prüfsumme | |||
== Projektplan == | |||
{| class="mw-datatable" | |||
! style="font-weight: bold;" | Vorgang | |||
! style="font-weight: bold;" | Abgeschlossen bis | |||
|- | |||
| Semsterstart | |||
| KW10 / 2015 | |||
|- | |||
| Themenauswahl | |||
| KW12 / 2015 | |||
|- | |||
| Informationen sammeln | |||
| KW13 / 2015 | |||
|- | |||
| Erzeugen von Barcode implementieren | |||
| KW15 / 2015 | |||
|- | |||
| Funktionscheck - Erstellen | |||
| KW17 / 2015 | |||
|- | |||
| Lesen von Barcode implementieren | |||
| KW16 / 2015 | |||
|- | |||
| Funktionscheck - Lesen | |||
| KW17 / 2015 | |||
|- | |||
| Code Optimierung und Fehlerbehebung | |||
| KW18 / 2015 | |||
|- | |||
| Kommentieren und dokumentieren abschließen | |||
| KW18 / 2015 | |||
|- | |||
| Wiki-Artikel finalsieren | |||
| KW20 / 2015 | |||
|- | |||
| Abgabe | |||
| KW25 / 2015 | |||
|} | |||
== Implementierung Barcode erstellen == | |||
[[Datei:Barcode_erstellen.jpg|mini|Übersicht Funktionsablauf Barcode_erstellen]] | |||
Die Funktion zum Erzeugen von Barcodes benötigt drei Eingaben. Den zu codierenden Text, sowie die Breiten der schmalen und breiten Elemente. Als Element wird z.B. ein schmaler Strich oder breite Lücke bezeichnet. | |||
Grundsätzliches Vorgehen: Zunächst wird ein leeres Bild in der benötigten Länge erzeugt. Diese Länge resultiert direkt aus den Funktionsparametern. Danach wird der Eigabetext sukzessiv eingelesen und in die CODE39 Notation überführt. Jeder CODE39 Barcode startet und ended wie oben beschrieben mit einem Sternchen. Dieses wird automatisch hinzugefügt. | |||
Die erstellten Codes lassen sich daher unter Zuhilfename eines Smartphones inkl. gängigen Barcode-Tool aus dem Appstore/Playstore lesen. Zum Funktionstest wurde die Software "Neoreader" erfolgreich verwendet. | |||
<source lang="matlab"> | |||
function barcode_erstellen(Eingabe,schmal,breit) | |||
%%------------------------------------------------------------------------- | |||
% PROGRAM: barcode_erstellen(Eingabe,schmal,breit) | |||
% VERSION: 1.01 | |||
% DATE: 2015-05-02 | |||
% DESCRIPTION: Program creates user defiend barcodes in CODE39 | |||
% notation. | |||
% | |||
% AUTHOR: Steffen Schulze Middendorf | |||
% | |||
% INPUT: Eingabe: Type in what you want to transform into CODE39 | |||
% notation. Function works exclusively with | |||
% capital letters and numbers. | |||
% In addition: '-';'.';' ';'$';'/';'+';'%' | |||
% schmal: Declare thickness of slender element in pixel | |||
% breit: Declare thickness of a wide element in pixel | |||
% pixel | |||
% | |||
% Relation between slender and wide have to be 1:2. | |||
% | |||
% OUTPUT: This progamm creates a new .JPG file in the program | |||
% folder. Content of this image is the created barcode. | |||
% Name of this file and data content of the barcode | |||
% are the same. Each barcode starts and ends with a '*' | |||
% automatically. | |||
% | |||
% EXAMPLE: barcode_erstellen('HSHL',50,100) | |||
% | |||
% DISCLAIMER: This program is freeware. Use at your own risk. | |||
% Provided without any warranty | |||
%-------------------------------------------------------------------------- | |||
%% Informationen einlesen und leeres Bild erstellen | |||
%Größe des Barcodes in Pixel bestimmmen aus Anzahl Zeichen + Zusatzlücken | |||
%+2 für START STOPP Zeichen -1 FÜR Lücke zum Schluss | |||
Laenge=(6*schmal+3*breit)*(length(Eingabe)+2)+((length(Eingabe)+1)*schmal); | |||
%Leeres schwarzes Bild(CODE39) in benötigter Breite und Höhe erstellen, | |||
%Standarthöhe ist zehnfache breite eines breiten Elements. | |||
CODE39 = zeros(10*breit,Laenge,3); | |||
%Startpunkt in x festlegen(Pixelspalte) | |||
Startpunkt=0; | |||
%% Startzeichen, Eingabe und Endzeichen in CODE39 notation in leeres Bild einfügen | |||
% Insgesamt besteht jeder CODE39 barcode aus 4 verschiedenen Elementen. | |||
% Zur Erstellung jedes dieser Elemente existiert eine Erstellungsfunktion. | |||
% schmal_strich(Startpunkt,Bild,schmal); | |||
% schmal_luecke(Startpunkt,Bild,schmal); | |||
% breit_luecke(Startpunkt,Bild,breit); | |||
% breit_strich(Startpunkt,Bild,breit); | |||
%Startzeichen "*" einfügen | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = breit_luecke(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
%Jedes Zeichen beginnt mit einem Strich und endet mit einer schmalen Lücke | |||
%Schleife durchläuft jedes einzelne Teichen der Eingabe und codiert es in | |||
%die entsprechende Strich/Lücke Kombination. Nach CODE39 Standart. | |||
for i=1:length(Eingabe) | |||
if Eingabe(i) == '0' | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
%Jede Element-Erstellfunktion bekommt den bisherigen erstellten CODE | |||
%die jeweiligen Paramter für breit und schmal sowie den Startpunkt | |||
%für die Erstellung Ihres nächsen Elemements übergeben. | |||
%Rückgabewerte sind der neue CODE und der neue Startpunkt. | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = breit_luecke(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
end | |||
if Eingabe(i) == '1' | |||
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = breit_luecke(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
end | |||
....... | |||
end | |||
</source> | |||
Zur besseren Übersicht wurde ein Teil des Codes entfernt. Letztlich wiederholen sich die IF-Abfragen und stellen jeweils einen Buchstaben da. Die Kombination von Strichen und Lücken wird direkt in das anfangs noch leere Bild übertragen. | |||
<source lang="matlab"> | |||
%Endzeichen "*" einfügen | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = breit_luecke(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit); | |||
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal); | |||
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal); | |||
%% Dateiname zusammensetzen aus Eingabetext und Endung '.JPG' und Bild auf Festplatte schreiben | |||
%Dateiname zusammensetzen | |||
a{1} = Eingabe; | |||
a{2} = '.JPG'; | |||
Dateiname = [a{1} a{2}]; | |||
%Datei auf Festplatte schreiben | |||
imwrite(CODE39,Dateiname) | |||
Ausgabetext=['Generation and saving of ',Dateiname,' completed succesfully.']; | |||
disp('-----------------------------------------------------------') | |||
disp(Ausgabetext) | |||
disp('-----------------------------------------------------------') | |||
end | |||
</source> | |||
Letzlich wird ein neues Bild im Format JPEG im Funktionsordner auf die Festplatte geschrieben, der Dateiname einspricht der Eingabe beim Funktionsaufruf und enthält den erzeugten Barcode. | |||
Exemplarisch sei der Inhalt einer der verwendeten Funktionen hier breit_strich erwähnt, der Aufbau der anderen drei Funktionen zum erstellen der einzelnen Elemente ist prinzipiell identisch: | |||
<source lang="matlab"> | |||
function [Startpunkt,Bild] = breit_strich(Startpunkt,Bild,breit) | |||
%Einfärben des aktiveren Bereichs ab Startpunkt bis Startpunkt plus | |||
%Elementbreite | |||
for k=1:breit | |||
Bild(:,Startpunkt+k,1)=255; | |||
Bild(:,Startpunkt+k,2)=255; | |||
Bild(:,Startpunkt+k,3)=255; | |||
end | |||
%Berechnung des neuen Startpunkts | |||
Startpunkt=Startpunkt+breit; | |||
</source> | |||
Jede Element-Erstellfunktion bekommt den bisherigen erstellten Code die jeweiligen Paramter für breit und schmal sowie den Startpunkt für die Erstellung des nächsten Elemements übergeben. Rückgabewerte sind der aktualisierte Strichcode(inkl. der durchgefürhrten Änderungen, d.h. des hinzufügen Elements) und der neue Startpunkt für den nächsten Funktionsdurchlauf. | |||
== Implementierung Barcode lesen == | |||
[[Datei:Barcode_lesen.jpg|mini|Übersicht Funktionsablauf Barcode_lesen]] | |||
Die Funktion zum Lesen von Barcodes benötigt eine Eingabe als Eingabe ausschließlich den Pfad zu einem Dokument im JPEG Format welches einen in CODE39 Notation erstellten Barcode enthält. In vielen Fällen kann das erstellte Programm trotz Fehlstellen oder Abweichungen den Inhalt des Barcodes auslesen. Siehe dazu auch die Beispiele im Kapitel "Beispielbilder". | |||
Typische Fehlstellen die "restauriert" bzw. akzeptiert werden werden können sind: | |||
*Fingerabdrücke | |||
*Linien die den Barcode kreuzen | |||
*Fehlstellen an den Rändern des Barcodes | |||
*Kleine Winkel bis max. 10° schieflage des Barcodes | |||
Die Funktion gibt den Inhalt des Barcodes anschließend als Array zurück. | |||
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, danach zerstörte schwarze Striche aufzufüllen und zuletzt, Striche die nicht als Barcode angenommen werden können zu entfernen. Die Filterparamter wurden dazu an die zu erwartenden Bild(ca.2000x2000) und Codebreiten(z.b. erstellt durch Barcode_erstellen Funktion mit den Paramentern 50,100) angepasst. | |||
<source lang="matlab"> | |||
function [Inhalt] = barcode_lesen(Barcode) | |||
%%------------------------------------------------------------------------- | |||
% PROGRAM: barcode_lesen(Barcode) | |||
% VERSION: 1.01 | |||
% DATE: 2015-05-02 | |||
% DESCRIPTION: Program reads user defiend barcodes in CODE39 | |||
% notation and is moderately fault tolerant. | |||
% | |||
% AUTHOR: Steffen Schulze Middendorf | |||
% | |||
% INPUT: Barcode: This progamm reads a '.JPG' file. | |||
% Content of this image have to be a barcode | |||
% in CODE39 notation. | |||
% | |||
% OUTPUT: Inhalt: Content of the barcode. | |||
% | |||
% EXAMPLE: barcode_lesen('C:\Users\HSHL.JPG') | |||
% | |||
% DISCLAIMER: This program is freeware. Use at your own risk. | |||
% Provided without any warranty | |||
%-------------------------------------------------------------------------- | |||
%% Bild einlesen und Filtern | |||
%Bild einlesen | |||
Dateiname=Barcode; | |||
Barcode=imread(Barcode); | |||
%Umwandlung in Schwarz/Weiss Bild | |||
SWBarcode=~im2bw(Barcode); | |||
%Filter anwenden | |||
%Elemente kleiner 100 pixel löschen | |||
SWBarcode = bwareaopen(SWBarcode,100,8); | |||
%Zerstörte Schwarze Striche wieder auffüllen | |||
Filterparameter = strel('square',9); | |||
SWBarcode = imclose(SWBarcode,Filterparameter); | |||
%Faben wieder umkehren und kleine Striche entfernen | |||
SWBarcode=~SWBarcode; | |||
Filterparameter = strel('square',13); | |||
SWBarcode = imclose(SWBarcode,Filterparameter); | |||
</source> | |||
Im Anschluss wird der Barcode auf dem Bild gesucht. Dies geschieht durch durchlaufen des gesamten Bildes in einer FOR-Schleife und Zählen von schwarzen Pixel, sobald der Schwellert(Pixelcounter) überschritten ist, ist auch der Anfang des Barcodes gefunden. Das Ende findet sich durch inverses Durchlaufen. | |||
<source lang="matlab"> | |||
%% Anfang und Ende des Barcodes finden | |||
%Größe des eingelesenen Bildes feststellen | |||
Abmasse=size(SWBarcode); | |||
xmass=Abmasse(2); | |||
ymass=Abmasse(1); | |||
%Gesuchte Maße initialisieren | |||
yEnde=0; | |||
xEnde=0; | |||
yStart=0; | |||
xStart=0; | |||
CounterSchwarzePixel=0; | |||
Anfanggefunden=0; | |||
%Anfang des Barcodes finden in Y(Höhe), durch zählen der schwarzen Pixel | |||
%von oben beginnend | |||
for i=1:ymass | |||
if Anfanggefunden==1 | |||
break | |||
end | |||
for k=1:xmass | |||
if SWBarcode(i,k)== 0 | |||
%Counter zählt schwarze Pixel um sicherzustellen das | |||
%nicht aufgrund eines nicht gefilterten schwarzen Pixels | |||
%der falsche Bereich ausgeschnitten wird | |||
CounterSchwarzePixel=CounterSchwarzePixel+1; | |||
end | |||
%Wenn Pixelcounter > 500 wird die Schleife abgebrochen und | |||
%und der yStart Wert gesichert | |||
if SWBarcode(i,k)== 0 && CounterSchwarzePixel > 10000 | |||
yStart=i; | |||
Anfanggefunden=1; | |||
break | |||
end | |||
end | |||
end | |||
%Zurücksetzen der Zähler | |||
CounterSchwarzePixel=0; | |||
Anfanggefunden=0; | |||
%Ende des Barcodes finden in Y(Höhe), durch zählen der schwarzen Pixel von | |||
%unten beginnend | |||
for i=ymass:-1:1 | |||
if Anfanggefunden==1 | |||
break | |||
end | |||
for k=xmass:-1:1 | |||
if SWBarcode(i,k)== 0 | |||
CounterSchwarzePixel=CounterSchwarzePixel+1; | |||
%Counter zählt schwarze Pixel um sicherzustellen das | |||
%nicht aufgrund eines nicht gefilterten schwarzen Pixels | |||
%der falsche Bereich ausgeschnitten wird | |||
end | |||
%Wenn Pixelcounter > 500 wird die Schleife abgebrochen und | |||
%und der yStart Wert gesichert | |||
if SWBarcode(i,k)== 0 && CounterSchwarzePixel > 10000 | |||
yEnde=i; | |||
Anfanggefunden=1; | |||
break | |||
end | |||
end | |||
end | |||
%Zurücksetzen der Zähler | |||
%Anfang des Barcodes finden in X(Länge) | |||
Anfanggefunden=0; | |||
for i=1:xmass | |||
if Anfanggefunden==1 | |||
break | |||
end | |||
for k=yStart:yEnde | |||
if SWBarcode(k,i)== 0 | |||
xStart=i; | |||
Anfanggefunden=1; | |||
break | |||
end | |||
end | |||
end | |||
%Ende des Barcodes finden in X(Länge) | |||
for i=1:xmass | |||
for k=yStart:yEnde | |||
if SWBarcode(k,i)== 0 && i>xEnde | |||
xEnde=i; | |||
end | |||
end | |||
end | |||
</source> | |||
Nun wird der signifikante Bereich, der den Barcode enthalten soll ausgeschnitten. Zudem wird im unteren Teil des Funktionsabschnitts der breiteste breite Strich gesucht. Dieser Wert ist für die spätere Analyse und Tolerierung von großer Wichtigkeit. | |||
<source lang="matlab"> | |||
%% Barcode ausschneiden auf Bereich mit Code-Inhalt unter Berücksichtung der analysierten Anfang-Ende Maße | |||
CodeZentriert=SWBarcode(yStart:yEnde,xStart:xEnde); | |||
AbmasseZentriert = size(CodeZentriert); | |||
%Größe des neuen zentrierten Bildes feststellen | |||
xmassZ=AbmasseZentriert(2); | |||
ymassZ=AbmasseZentriert(1); | |||
%% Breite von breiten und schmalen Elementen herausfinden | |||
%Herausfinden wie breit (Pixel) ein breiter Strich ist | |||
%Initialisieren der gesuchten Variablen | |||
Breit=0; | |||
MaximalBreit=0; | |||
for i=1:xmassZ | |||
if CodeZentriert(round(ymassZ/2),i) == 1 | |||
%Es wird so lange addiert wie durchgängig schwarze Pixel vorhanden | |||
%sind. | |||
Breit=Breit+1; | |||
end | |||
if CodeZentriert(round(ymassZ/2),i) == 0 | |||
%Sobald ein weißer Pixel unterbricht wird anaysiert ob es sich um | |||
%die längste Folge von schwarzen Pixeln gehandelt hat, wenn ja | |||
%wird die Länge in Maximalbreit gesichert. | |||
if Breit > MaximalBreit | |||
MaximalBreit=Breit; | |||
end | |||
Breit=0; | |||
end | |||
end | |||
%Maximale Breite wird genutzt um auch die Breite des schmalen Elements | |||
%zu bestimmen Verhältnis 1:2 ist bekannt. | |||
Breit=MaximalBreit; | |||
Schmal=round(Breit/2); | |||
%Zugeschnittenes Bild anzeigen | |||
imshow(CodeZentriert) | |||
</source> | |||
Um nun den Code wieder entschlüsseln zu können, müssen Toleranzen für die Erkennung von Elementen festgelgt werden. Grundsätzlich bedeutet das dünne von dicken Strichen bzw. Lücken unterscheiden zu können. Grundlage ist hierzu der gefundene breiteste Strich. Von diesem lassen sich nun die Grenzen für alle anderen Elemente ableiten. | |||
<source lang="matlab"> | |||
%% Toleranzen für Elemente festlegen | |||
%Toleranzen festlegen oT = obere Toleranz uT = unterere Toleranz | |||
%Breites Element | |||
oTBreit = round(Breit+Breit/3); | |||
uTBreit = round(Breit-Breit/3); | |||
%Schmales Element | |||
oTSchmal = round(Schmal+Schmal/3); | |||
uTSchmal = round(Schmal-Schmal/3); | |||
</source> | |||
Letztendlich wird der verbliebene zentrierte Barcode nochmals von links nach rechts in einer FOR-Schleife durchlaufen um den Inhalt nach und nach auszulesen. Die Identifikation erfolgt anhand der oben angegeben Toleranzen und dem Farbwechsel, der den Anfang und das Ende eines neuen Elements beschreibt. | |||
Die Elemente werden jeweils zu Bündeln zu je 10 Elementen zusammengefasst. Wobei: | |||
*1 = schmaler Strich | |||
*2 = schmale Lücke | |||
*3 = breiter Strich | |||
*4 = breite Lücke | |||
Ein Array mit zehn dieser Zahlen wird dann der Funktion Entschlüsseln zur Verfügung gestellt. Diese erkennt anhand der Kombination den zugehörigen Buchstaben. | |||
<source lang="matlab"> | |||
%% Code nacheinander einlesen und auswerten | |||
%Initialisieren der Variablen | |||
Breite=0; | |||
Ausgabe=[]; | |||
FarbeALT=0; | |||
Anfanggefunden=0; | |||
%Bild "CodeZentriert" durchlaufen und nach Elementen Strich/Lücke suchen | |||
%Anfang finden(wichtig für schräge codes) | |||
for i=1:xmassZ | |||
Farbe=CodeZentriert((round(ymassZ/2)),i); | |||
if Anfanggefunden ~= 1 && Farbe==0 | |||
Anfanggefunden=1; | |||
end | |||
%Solange kein Farbwechsel stattfindet, Breiten aufaddieren | |||
if Anfanggefunden == 1 | |||
if Farbe==FarbeALT | |||
Breite=Breite+1; | |||
end | |||
%Wenn Farbwechsel stattfindet, vergleich um welches Element es | |||
%sich handelt unter Berücksichtung der Toleranzen | |||
%Schmaler Strich | |||
if Farbe ~= FarbeALT || i==xmassZ | |||
if (Breite>uTSchmal) && (Breite<oTSchmal) && (FarbeALT==1) | |||
%Ausgabe speichert ein Array mit den Zahlen 1,2,3,4 diese | |||
%stehen jeweils für ein Element | |||
Ausgabe=[Ausgabe 1]; | |||
end | |||
%Schmaler Strich | |||
if (Breite>uTSchmal) && (Breite<oTSchmal) && (FarbeALT==0) | |||
Ausgabe=[Ausgabe 2]; | |||
end | |||
%Breite Lücke | |||
if (Breite>uTBreit) && (Breite<oTBreit) && (FarbeALT==1) | |||
Ausgabe=[Ausgabe 3]; | |||
end | |||
%Breiter Strich | |||
if (Breite>uTBreit) && (Breite<oTBreit) && (FarbeALT==0) | |||
Ausgabe=[Ausgabe 4]; | |||
end | |||
Breite=1; | |||
FarbeALT=Farbe; | |||
end | |||
end | |||
end | |||
k=0; | |||
q=0; | |||
Loesung=[]; | |||
Inhalt=[]; | |||
while numel(Ausgabe) > 0 | |||
if numel(Ausgabe) > 10 | |||
for k=1:10 | |||
%Immer 10 Zahlen(1,2,3,4) ergeben einen Buchstaben, daher | |||
%werden jetzt immer 10 aufeinander folgenden Zahlen zu einem | |||
%Lösungspaket zusammengechnürt | |||
Loesung=[Loesung Ausgabe(k)]; | |||
end | |||
for k=1:10 | |||
%Verwertete 10 Element löschen | |||
Ausgabe(1)=[]; | |||
end | |||
%Ausgabearray wird in den wahren Inhalt transformiert. Kombination | |||
%von 1,2,3,4 ergbit wieder ein Zeichen, dafür wird die Funktion | |||
%entschlüsseln genutzt | |||
[Zeichen] = entschluesseln(Loesung); | |||
%Zeichen wird in Inhalt(Ausgabearray) geschrieben | |||
Inhalt=[Inhalt Zeichen]; | |||
%Lösungsarray wieder leer setzen | |||
Loesung=[]; | |||
else | |||
%Wenn sich nur noch 10 Elemente im Ausgabe befindet wird noch einmal | |||
%der Inhalt von Ausgabe um 1(schmale Lücke) erweitert. | |||
Loesung=[Ausgabe 1]; | |||
[Zeichen] = entschluesseln(Loesung); | |||
Inhalt=[Inhalt Zeichen]; | |||
Ausgabe=[]; | |||
Ausgabetext=['Reading and analysing of ',Dateiname,' completed succesfully. Content:'] | |||
disp('-----------------------------------------------------------') | |||
disp(Inhalt) | |||
disp('-----------------------------------------------------------') | |||
end | |||
end | |||
end | |||
</source> | |||
Der entschlüsselte Buchstabe wird in einem letzten Schritt in das Ausgabearray "Inhalt" hinzugefügt. | |||
Der Barcode ist nun wieder in ACII Zeichen umgewandelt und wird ausgegeben. | |||
== Beispielbilder und Funktionsbeweis == | |||
Folgende Bilder konnten unter Zuhilfename der oben gezeigten Funktion ausgelesen werden. Die hinzugefügten Abweichungen sind unter den Bildern zusammengefasst.Die Bilder weisen einen steigenden Schwierigkeitsgrad auf. Alle Barcodes haben den Inhalt "HSHL". | |||
<gallery class="center"> | |||
Datei:K1600_HSHL1.JPG|(1) Bild enhält ausschließlich Barcode | |||
Datei:K1600_HSHL2.JPG|(2) Barcode ist nicht mehr zentral und zusätzlich schief | |||
Datei:K1600_HSHL3.JPG|(3) Barcode ist dezentral, schief und weißt Fehlstellen auf | |||
Datei:K1600_HSHL4.JPG|(4) Wie 3 aber zusätzlich teilweise durchgestrichen | |||
Datei:K1600_HSHL5.JPG|(5) Wie 4 aber zusätzlich vom Bildschirm fotografiert | |||
Datei:K1600_HSHL6.JPG|(6) Wie 5 aber zusätzlichen Interferenzen | |||
</gallery> | |||
== Lessons learned und Fazit== | |||
Die Aufgabe Barcodes zu generieren und zu lesen war Teil der Prüfungsleistung im Bereich Signalverarbeitung im Studiengang Business and Systems Engineering. Das Projekt hat mir die Grundlagen der Bildverabreitung in Matlab näher gebracht. Mir ist klar geworden wie mächtig die in Matlab(Image processing toolbox) integrierten Filter bei richtigem Einsatz sein können. Das eine auf die Situation zugeschnitte Parametrisierung mindestens genauso von Bedeutung ist. | |||
Auch die Content-Erstellung im Wiki war für mich neu und ich war überrascht wie schnell man auch hier Erfolge erzielen konnte. | |||
== Download == | |||
*[[Medium:2015-05-17_Barcode.zip|Barcode Programme inkl. Testbild]] | |||
== Youtube Video == | |||
*[https://www.youtube.com/watch?v=imEk-2UWk1s&feature=youtu.be: Youtube Video] | |||
== Weblinks == | == Weblinks == | ||
*[http:// | *[http://barcode.tec-it.com/barcode-generator.aspx?LANG=de: Barcode Generator] | ||
*[http:// | *[http://de.wikipedia.org/wiki/Strichcode Strichcodes auf Wikipedia] | ||
*[http:// | *[http://www.mzi-systemtechnik.de/BarcodeWozu.html: Anwendungsbeispiele] | ||
---- | ---- | ||
→ zurück zum Hauptartikel: [[SigSys_SoSe2015| Signalverarbeitende Systeme SoSe2015]] | → zurück zum Hauptartikel: [[SigSys_SoSe2015| Signalverarbeitende Systeme SoSe2015]] | ||
== Einzelnachweise == | |||
<references /> |
Aktuelle Version vom 25. November 2016, 10:41 Uhr
Autor: Steffen Schulze Middendorf
Betreuer: Prof. Schneider
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.
CODE39
Der CODE39 ist in der ISO/IEC 16388[1] spezifiziert, grundsätzlich besteht dieser aus Strichen und Lücken, jeweils in schmal und in breit.Es handelt sich um einen diskreten Code, das heisst jeder Buchstabe beginnt und endet mit einem Strich. Zwischen jedem Buchstaben findet sich zur Abgrenzung eine schmale Lücke. Jeder erzeugte Strichcode beginnt und endet mit einem Sternchen(*). Man spricht hier von einem Start/Stopp-Zeichen.
In der Tabelle rechts ist der gesamte verfügbare Zeichensatz dargestellt.
In der Standart Variante verfügt der CODE39 über keine Prüfziffern. Im folgenden sollen die Vor-und Nachteile stichpunktartig dargestellt werden.
Vorteile:
- robust
- hohe Drucktoleranzen akzeptabel
Nachteile:
- geringe Code Dichte
- keine Prüfsumme
Projektplan
Vorgang | Abgeschlossen bis |
---|---|
Semsterstart | KW10 / 2015 |
Themenauswahl | KW12 / 2015 |
Informationen sammeln | KW13 / 2015 |
Erzeugen von Barcode implementieren | KW15 / 2015 |
Funktionscheck - Erstellen | KW17 / 2015 |
Lesen von Barcode implementieren | KW16 / 2015 |
Funktionscheck - Lesen | KW17 / 2015 |
Code Optimierung und Fehlerbehebung | KW18 / 2015 |
Kommentieren und dokumentieren abschließen | KW18 / 2015 |
Wiki-Artikel finalsieren | KW20 / 2015 |
Abgabe | KW25 / 2015 |
Implementierung Barcode erstellen
Die Funktion zum Erzeugen von Barcodes benötigt drei Eingaben. Den zu codierenden Text, sowie die Breiten der schmalen und breiten Elemente. Als Element wird z.B. ein schmaler Strich oder breite Lücke bezeichnet.
Grundsätzliches Vorgehen: Zunächst wird ein leeres Bild in der benötigten Länge erzeugt. Diese Länge resultiert direkt aus den Funktionsparametern. Danach wird der Eigabetext sukzessiv eingelesen und in die CODE39 Notation überführt. Jeder CODE39 Barcode startet und ended wie oben beschrieben mit einem Sternchen. Dieses wird automatisch hinzugefügt.
Die erstellten Codes lassen sich daher unter Zuhilfename eines Smartphones inkl. gängigen Barcode-Tool aus dem Appstore/Playstore lesen. Zum Funktionstest wurde die Software "Neoreader" erfolgreich verwendet.
function barcode_erstellen(Eingabe,schmal,breit)
%%-------------------------------------------------------------------------
% PROGRAM: barcode_erstellen(Eingabe,schmal,breit)
% VERSION: 1.01
% DATE: 2015-05-02
% DESCRIPTION: Program creates user defiend barcodes in CODE39
% notation.
%
% AUTHOR: Steffen Schulze Middendorf
%
% INPUT: Eingabe: Type in what you want to transform into CODE39
% notation. Function works exclusively with
% capital letters and numbers.
% In addition: '-';'.';' ';'$';'/';'+';'%'
% schmal: Declare thickness of slender element in pixel
% breit: Declare thickness of a wide element in pixel
% pixel
%
% Relation between slender and wide have to be 1:2.
%
% OUTPUT: This progamm creates a new .JPG file in the program
% folder. Content of this image is the created barcode.
% Name of this file and data content of the barcode
% are the same. Each barcode starts and ends with a '*'
% automatically.
%
% EXAMPLE: barcode_erstellen('HSHL',50,100)
%
% DISCLAIMER: This program is freeware. Use at your own risk.
% Provided without any warranty
%--------------------------------------------------------------------------
%% Informationen einlesen und leeres Bild erstellen
%Größe des Barcodes in Pixel bestimmmen aus Anzahl Zeichen + Zusatzlücken
%+2 für START STOPP Zeichen -1 FÜR Lücke zum Schluss
Laenge=(6*schmal+3*breit)*(length(Eingabe)+2)+((length(Eingabe)+1)*schmal);
%Leeres schwarzes Bild(CODE39) in benötigter Breite und Höhe erstellen,
%Standarthöhe ist zehnfache breite eines breiten Elements.
CODE39 = zeros(10*breit,Laenge,3);
%Startpunkt in x festlegen(Pixelspalte)
Startpunkt=0;
%% Startzeichen, Eingabe und Endzeichen in CODE39 notation in leeres Bild einfügen
% Insgesamt besteht jeder CODE39 barcode aus 4 verschiedenen Elementen.
% Zur Erstellung jedes dieser Elemente existiert eine Erstellungsfunktion.
% schmal_strich(Startpunkt,Bild,schmal);
% schmal_luecke(Startpunkt,Bild,schmal);
% breit_luecke(Startpunkt,Bild,breit);
% breit_strich(Startpunkt,Bild,breit);
%Startzeichen "*" einfügen
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = breit_luecke(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
%Jedes Zeichen beginnt mit einem Strich und endet mit einer schmalen Lücke
%Schleife durchläuft jedes einzelne Teichen der Eingabe und codiert es in
%die entsprechende Strich/Lücke Kombination. Nach CODE39 Standart.
for i=1:length(Eingabe)
if Eingabe(i) == '0'
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
%Jede Element-Erstellfunktion bekommt den bisherigen erstellten CODE
%die jeweiligen Paramter für breit und schmal sowie den Startpunkt
%für die Erstellung Ihres nächsen Elemements übergeben.
%Rückgabewerte sind der neue CODE und der neue Startpunkt.
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = breit_luecke(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
end
if Eingabe(i) == '1'
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = breit_luecke(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
end
.......
end
Zur besseren Übersicht wurde ein Teil des Codes entfernt. Letztlich wiederholen sich die IF-Abfragen und stellen jeweils einen Buchstaben da. Die Kombination von Strichen und Lücken wird direkt in das anfangs noch leere Bild übertragen.
%Endzeichen "*" einfügen
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = breit_luecke(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = breit_strich(Startpunkt,CODE39,breit);
[Startpunkt,CODE39] = schmal_luecke(Startpunkt,CODE39,schmal);
[Startpunkt,CODE39] = schmal_strich(Startpunkt,CODE39,schmal);
%% Dateiname zusammensetzen aus Eingabetext und Endung '.JPG' und Bild auf Festplatte schreiben
%Dateiname zusammensetzen
a{1} = Eingabe;
a{2} = '.JPG';
Dateiname = [a{1} a{2}];
%Datei auf Festplatte schreiben
imwrite(CODE39,Dateiname)
Ausgabetext=['Generation and saving of ',Dateiname,' completed succesfully.'];
disp('-----------------------------------------------------------')
disp(Ausgabetext)
disp('-----------------------------------------------------------')
end
Letzlich wird ein neues Bild im Format JPEG im Funktionsordner auf die Festplatte geschrieben, der Dateiname einspricht der Eingabe beim Funktionsaufruf und enthält den erzeugten Barcode.
Exemplarisch sei der Inhalt einer der verwendeten Funktionen hier breit_strich erwähnt, der Aufbau der anderen drei Funktionen zum erstellen der einzelnen Elemente ist prinzipiell identisch:
function [Startpunkt,Bild] = breit_strich(Startpunkt,Bild,breit)
%Einfärben des aktiveren Bereichs ab Startpunkt bis Startpunkt plus
%Elementbreite
for k=1:breit
Bild(:,Startpunkt+k,1)=255;
Bild(:,Startpunkt+k,2)=255;
Bild(:,Startpunkt+k,3)=255;
end
%Berechnung des neuen Startpunkts
Startpunkt=Startpunkt+breit;
Jede Element-Erstellfunktion bekommt den bisherigen erstellten Code die jeweiligen Paramter für breit und schmal sowie den Startpunkt für die Erstellung des nächsten Elemements übergeben. Rückgabewerte sind der aktualisierte Strichcode(inkl. der durchgefürhrten Änderungen, d.h. des hinzufügen Elements) und der neue Startpunkt für den nächsten Funktionsdurchlauf.
Implementierung Barcode lesen
Die Funktion zum Lesen von Barcodes benötigt eine Eingabe als Eingabe ausschließlich den Pfad zu einem Dokument im JPEG Format welches einen in CODE39 Notation erstellten Barcode enthält. In vielen Fällen kann das erstellte Programm trotz Fehlstellen oder Abweichungen den Inhalt des Barcodes auslesen. Siehe dazu auch die Beispiele im Kapitel "Beispielbilder". Typische Fehlstellen die "restauriert" bzw. akzeptiert werden werden können sind:
- Fingerabdrücke
- Linien die den Barcode kreuzen
- Fehlstellen an den Rändern des Barcodes
- Kleine Winkel bis max. 10° schieflage des Barcodes
Die Funktion gibt den Inhalt des Barcodes anschließend als Array zurück.
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, danach zerstörte schwarze Striche aufzufüllen und zuletzt, Striche die nicht als Barcode angenommen werden können zu entfernen. Die Filterparamter wurden dazu an die zu erwartenden Bild(ca.2000x2000) und Codebreiten(z.b. erstellt durch Barcode_erstellen Funktion mit den Paramentern 50,100) angepasst.
function [Inhalt] = barcode_lesen(Barcode)
%%-------------------------------------------------------------------------
% PROGRAM: barcode_lesen(Barcode)
% VERSION: 1.01
% DATE: 2015-05-02
% DESCRIPTION: Program reads user defiend barcodes in CODE39
% notation and is moderately fault tolerant.
%
% AUTHOR: Steffen Schulze Middendorf
%
% INPUT: Barcode: This progamm reads a '.JPG' file.
% Content of this image have to be a barcode
% in CODE39 notation.
%
% OUTPUT: Inhalt: Content of the barcode.
%
% EXAMPLE: barcode_lesen('C:\Users\HSHL.JPG')
%
% DISCLAIMER: This program is freeware. Use at your own risk.
% Provided without any warranty
%--------------------------------------------------------------------------
%% Bild einlesen und Filtern
%Bild einlesen
Dateiname=Barcode;
Barcode=imread(Barcode);
%Umwandlung in Schwarz/Weiss Bild
SWBarcode=~im2bw(Barcode);
%Filter anwenden
%Elemente kleiner 100 pixel löschen
SWBarcode = bwareaopen(SWBarcode,100,8);
%Zerstörte Schwarze Striche wieder auffüllen
Filterparameter = strel('square',9);
SWBarcode = imclose(SWBarcode,Filterparameter);
%Faben wieder umkehren und kleine Striche entfernen
SWBarcode=~SWBarcode;
Filterparameter = strel('square',13);
SWBarcode = imclose(SWBarcode,Filterparameter);
Im Anschluss wird der Barcode auf dem Bild gesucht. Dies geschieht durch durchlaufen des gesamten Bildes in einer FOR-Schleife und Zählen von schwarzen Pixel, sobald der Schwellert(Pixelcounter) überschritten ist, ist auch der Anfang des Barcodes gefunden. Das Ende findet sich durch inverses Durchlaufen.
%% Anfang und Ende des Barcodes finden
%Größe des eingelesenen Bildes feststellen
Abmasse=size(SWBarcode);
xmass=Abmasse(2);
ymass=Abmasse(1);
%Gesuchte Maße initialisieren
yEnde=0;
xEnde=0;
yStart=0;
xStart=0;
CounterSchwarzePixel=0;
Anfanggefunden=0;
%Anfang des Barcodes finden in Y(Höhe), durch zählen der schwarzen Pixel
%von oben beginnend
for i=1:ymass
if Anfanggefunden==1
break
end
for k=1:xmass
if SWBarcode(i,k)== 0
%Counter zählt schwarze Pixel um sicherzustellen das
%nicht aufgrund eines nicht gefilterten schwarzen Pixels
%der falsche Bereich ausgeschnitten wird
CounterSchwarzePixel=CounterSchwarzePixel+1;
end
%Wenn Pixelcounter > 500 wird die Schleife abgebrochen und
%und der yStart Wert gesichert
if SWBarcode(i,k)== 0 && CounterSchwarzePixel > 10000
yStart=i;
Anfanggefunden=1;
break
end
end
end
%Zurücksetzen der Zähler
CounterSchwarzePixel=0;
Anfanggefunden=0;
%Ende des Barcodes finden in Y(Höhe), durch zählen der schwarzen Pixel von
%unten beginnend
for i=ymass:-1:1
if Anfanggefunden==1
break
end
for k=xmass:-1:1
if SWBarcode(i,k)== 0
CounterSchwarzePixel=CounterSchwarzePixel+1;
%Counter zählt schwarze Pixel um sicherzustellen das
%nicht aufgrund eines nicht gefilterten schwarzen Pixels
%der falsche Bereich ausgeschnitten wird
end
%Wenn Pixelcounter > 500 wird die Schleife abgebrochen und
%und der yStart Wert gesichert
if SWBarcode(i,k)== 0 && CounterSchwarzePixel > 10000
yEnde=i;
Anfanggefunden=1;
break
end
end
end
%Zurücksetzen der Zähler
%Anfang des Barcodes finden in X(Länge)
Anfanggefunden=0;
for i=1:xmass
if Anfanggefunden==1
break
end
for k=yStart:yEnde
if SWBarcode(k,i)== 0
xStart=i;
Anfanggefunden=1;
break
end
end
end
%Ende des Barcodes finden in X(Länge)
for i=1:xmass
for k=yStart:yEnde
if SWBarcode(k,i)== 0 && i>xEnde
xEnde=i;
end
end
end
Nun wird der signifikante Bereich, der den Barcode enthalten soll ausgeschnitten. Zudem wird im unteren Teil des Funktionsabschnitts der breiteste breite Strich gesucht. Dieser Wert ist für die spätere Analyse und Tolerierung von großer Wichtigkeit.
%% Barcode ausschneiden auf Bereich mit Code-Inhalt unter Berücksichtung der analysierten Anfang-Ende Maße
CodeZentriert=SWBarcode(yStart:yEnde,xStart:xEnde);
AbmasseZentriert = size(CodeZentriert);
%Größe des neuen zentrierten Bildes feststellen
xmassZ=AbmasseZentriert(2);
ymassZ=AbmasseZentriert(1);
%% Breite von breiten und schmalen Elementen herausfinden
%Herausfinden wie breit (Pixel) ein breiter Strich ist
%Initialisieren der gesuchten Variablen
Breit=0;
MaximalBreit=0;
for i=1:xmassZ
if CodeZentriert(round(ymassZ/2),i) == 1
%Es wird so lange addiert wie durchgängig schwarze Pixel vorhanden
%sind.
Breit=Breit+1;
end
if CodeZentriert(round(ymassZ/2),i) == 0
%Sobald ein weißer Pixel unterbricht wird anaysiert ob es sich um
%die längste Folge von schwarzen Pixeln gehandelt hat, wenn ja
%wird die Länge in Maximalbreit gesichert.
if Breit > MaximalBreit
MaximalBreit=Breit;
end
Breit=0;
end
end
%Maximale Breite wird genutzt um auch die Breite des schmalen Elements
%zu bestimmen Verhältnis 1:2 ist bekannt.
Breit=MaximalBreit;
Schmal=round(Breit/2);
%Zugeschnittenes Bild anzeigen
imshow(CodeZentriert)
Um nun den Code wieder entschlüsseln zu können, müssen Toleranzen für die Erkennung von Elementen festgelgt werden. Grundsätzlich bedeutet das dünne von dicken Strichen bzw. Lücken unterscheiden zu können. Grundlage ist hierzu der gefundene breiteste Strich. Von diesem lassen sich nun die Grenzen für alle anderen Elemente ableiten.
%% Toleranzen für Elemente festlegen
%Toleranzen festlegen oT = obere Toleranz uT = unterere Toleranz
%Breites Element
oTBreit = round(Breit+Breit/3);
uTBreit = round(Breit-Breit/3);
%Schmales Element
oTSchmal = round(Schmal+Schmal/3);
uTSchmal = round(Schmal-Schmal/3);
Letztendlich wird der verbliebene zentrierte Barcode nochmals von links nach rechts in einer FOR-Schleife durchlaufen um den Inhalt nach und nach auszulesen. Die Identifikation erfolgt anhand der oben angegeben Toleranzen und dem Farbwechsel, der den Anfang und das Ende eines neuen Elements beschreibt. Die Elemente werden jeweils zu Bündeln zu je 10 Elementen zusammengefasst. Wobei:
- 1 = schmaler Strich
- 2 = schmale Lücke
- 3 = breiter Strich
- 4 = breite Lücke
Ein Array mit zehn dieser Zahlen wird dann der Funktion Entschlüsseln zur Verfügung gestellt. Diese erkennt anhand der Kombination den zugehörigen Buchstaben.
%% Code nacheinander einlesen und auswerten
%Initialisieren der Variablen
Breite=0;
Ausgabe=[];
FarbeALT=0;
Anfanggefunden=0;
%Bild "CodeZentriert" durchlaufen und nach Elementen Strich/Lücke suchen
%Anfang finden(wichtig für schräge codes)
for i=1:xmassZ
Farbe=CodeZentriert((round(ymassZ/2)),i);
if Anfanggefunden ~= 1 && Farbe==0
Anfanggefunden=1;
end
%Solange kein Farbwechsel stattfindet, Breiten aufaddieren
if Anfanggefunden == 1
if Farbe==FarbeALT
Breite=Breite+1;
end
%Wenn Farbwechsel stattfindet, vergleich um welches Element es
%sich handelt unter Berücksichtung der Toleranzen
%Schmaler Strich
if Farbe ~= FarbeALT || i==xmassZ
if (Breite>uTSchmal) && (Breite<oTSchmal) && (FarbeALT==1)
%Ausgabe speichert ein Array mit den Zahlen 1,2,3,4 diese
%stehen jeweils für ein Element
Ausgabe=[Ausgabe 1];
end
%Schmaler Strich
if (Breite>uTSchmal) && (Breite<oTSchmal) && (FarbeALT==0)
Ausgabe=[Ausgabe 2];
end
%Breite Lücke
if (Breite>uTBreit) && (Breite<oTBreit) && (FarbeALT==1)
Ausgabe=[Ausgabe 3];
end
%Breiter Strich
if (Breite>uTBreit) && (Breite<oTBreit) && (FarbeALT==0)
Ausgabe=[Ausgabe 4];
end
Breite=1;
FarbeALT=Farbe;
end
end
end
k=0;
q=0;
Loesung=[];
Inhalt=[];
while numel(Ausgabe) > 0
if numel(Ausgabe) > 10
for k=1:10
%Immer 10 Zahlen(1,2,3,4) ergeben einen Buchstaben, daher
%werden jetzt immer 10 aufeinander folgenden Zahlen zu einem
%Lösungspaket zusammengechnürt
Loesung=[Loesung Ausgabe(k)];
end
for k=1:10
%Verwertete 10 Element löschen
Ausgabe(1)=[];
end
%Ausgabearray wird in den wahren Inhalt transformiert. Kombination
%von 1,2,3,4 ergbit wieder ein Zeichen, dafür wird die Funktion
%entschlüsseln genutzt
[Zeichen] = entschluesseln(Loesung);
%Zeichen wird in Inhalt(Ausgabearray) geschrieben
Inhalt=[Inhalt Zeichen];
%Lösungsarray wieder leer setzen
Loesung=[];
else
%Wenn sich nur noch 10 Elemente im Ausgabe befindet wird noch einmal
%der Inhalt von Ausgabe um 1(schmale Lücke) erweitert.
Loesung=[Ausgabe 1];
[Zeichen] = entschluesseln(Loesung);
Inhalt=[Inhalt Zeichen];
Ausgabe=[];
Ausgabetext=['Reading and analysing of ',Dateiname,' completed succesfully. Content:']
disp('-----------------------------------------------------------')
disp(Inhalt)
disp('-----------------------------------------------------------')
end
end
end
Der entschlüsselte Buchstabe wird in einem letzten Schritt in das Ausgabearray "Inhalt" hinzugefügt. Der Barcode ist nun wieder in ACII Zeichen umgewandelt und wird ausgegeben.
Beispielbilder und Funktionsbeweis
Folgende Bilder konnten unter Zuhilfename der oben gezeigten Funktion ausgelesen werden. Die hinzugefügten Abweichungen sind unter den Bildern zusammengefasst.Die Bilder weisen einen steigenden Schwierigkeitsgrad auf. Alle Barcodes haben den Inhalt "HSHL".
-
(1) Bild enhält ausschließlich Barcode
-
(2) Barcode ist nicht mehr zentral und zusätzlich schief
-
(3) Barcode ist dezentral, schief und weißt Fehlstellen auf
-
(4) Wie 3 aber zusätzlich teilweise durchgestrichen
-
(5) Wie 4 aber zusätzlich vom Bildschirm fotografiert
-
(6) Wie 5 aber zusätzlichen Interferenzen
Lessons learned und Fazit
Die Aufgabe Barcodes zu generieren und zu lesen war Teil der Prüfungsleistung im Bereich Signalverarbeitung im Studiengang Business and Systems Engineering. Das Projekt hat mir die Grundlagen der Bildverabreitung in Matlab näher gebracht. Mir ist klar geworden wie mächtig die in Matlab(Image processing toolbox) integrierten Filter bei richtigem Einsatz sein können. Das eine auf die Situation zugeschnitte Parametrisierung mindestens genauso von Bedeutung ist. Auch die Content-Erstellung im Wiki war für mich neu und ich war überrascht wie schnell man auch hier Erfolge erzielen konnte.
Download
Youtube Video
Weblinks
→ zurück zum Hauptartikel: Signalverarbeitende Systeme SoSe2015