QR-Code erzeugen und lesen: Unterschied zwischen den Versionen
(→Code) |
(→Video) |
||
(9 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
Zeile 21: | Zeile 21: | ||
=== Extrahierung === | === Extrahierung === | ||
Die Extrahierung des QR-Codes aus dem Bild erfolgt anhand eines Box-Find-Algorithmus, welcher bereits in MatLab implementiert ist <ref>[http://www.mathworks.de/de/help/images/ref/regionprops.html#bqkf8hf] MatLab, Dokumentation "regionprops" -> Bounding Box </ref>. | |||
Anhand der ermittelten Boxen können jene gefiltert werden, welche nicht eine annähernde rechteckige Form besitzen. Dieser Filter wird anhand eines Seitenverhältnisvergleiches durchgeführt, wie er nachfolgend dargestellt ist. | |||
<source lang="matlab"> | |||
for cnt = 1 : numel(stat) | |||
if stat(cnt).BoundingBox(3)/stat(cnt).BoundingBox(4)>0.5 && stat(cnt).BoundingBox(3)/stat(cnt).BoundingBox(4)<1.2 %like rectangle will be filtered | |||
bb(cnt,:) = stat(cnt).BoundingBox; | |||
else | |||
bb(cnt,:)=[-1 -1 -1 -1]; | |||
end | |||
end | |||
</source> | |||
Übrig gebliebene Boxen werden anhand ihrer Größe und ihres Abstandes zu einander verglichen. Dies geschieht, indem jede Box mit der anderen verglichen wird. Anschließend werden gleichartige Boxen gesucht und in Pärchen sortiert. | |||
<source lang="matlab"> | |||
%% Calculate distance between Size of squares | |||
distSize = sqrt(bsxfun(@minus,bb(:,3),bb(:,3)').^2 + bsxfun(@minus,bb(:,4),bb(:,4)').^2); | |||
distSize(distSize(:,:)>distSizeThresh)=0; | |||
%% Calculate distance between position of squares | |||
distPos = sqrt(bsxfun(@minus,bb(:,1),bb(:,1)').^2 + bsxfun(@minus,bb(:,2),bb(:,2)').^2); | |||
distPos(distPos(:,:)>distPosThresh)=0; | |||
%% Filter boxes for euqal squares | |||
for k=1:size(distSize,1) | |||
for l=1:k | |||
if distSize(k,l)~= 0 && size(find(distSize==distSize(k,l)),1)>=6 | |||
Pair(end+1,:)=[k l]; | |||
end | |||
end | |||
end | |||
</source> | |||
Um zu ermitteln, ob die Boxen auch den gleichen Inhalt besitzen, wird die "Dichte" der schwarzen Pixel in der Box ermittelt. Wenn die Boxen die gleichen Dichten aufweisen, dann wird angenommen, dass die Boxen eine sehr starke Ähnlichkeiot aufweisen. | |||
<source lang="matlab"> | |||
for k=1:size(Pair,1) | |||
CropImage1=imcrop(cAll,bb(Pair(k,1),:)); | |||
Density(k,1)=sum(sum(size(find(CropImage1~=0),1)))/(bb(Pair(k,1),3)*bb(Pair(k,1),4)); | |||
CropImage2=imcrop(cAll,bb(Pair(k,2),:)); | |||
Density(k,2)=sum(sum(size(find(CropImage2~=0),1)))/(bb(Pair(k,2),3)*bb(Pair(k,2),4)); | |||
if abs(Density(k,1)-Density(k,2)) < diff_Density | |||
if Density(k,1)<0.3 && Density(k,1)>0.1 | |||
rectangle('position',bb(Pair(k,1),:),'edgecolor','g','linewidth',2); | |||
Padding_Crop = max([Padding_Crop bb(Pair(k,1),3:4)./1.1]); | |||
if size(find(DensityPassed(:,1)==Pair(k,1)),1) == 0 | |||
DensityPassed(end+1,:)=[Pair(k,1) bb(Pair(k,1),1)+0.5*bb(Pair(k,1),3) bb(Pair(k,1),2)+0.5*bb(Pair(k,1),4)]; | |||
end | |||
end | |||
if Density(k,2)<0.3 && Density(k,2)>0.1 | |||
rectangle('position',bb(Pair(k,2),:),'edgecolor','g','linewidth',2); | |||
Padding_Crop = max([Padding_Crop bb(Pair(k,1),3:4)]./1.1); | |||
if size(find(DensityPassed(:,1)==Pair(k,2)),1) == 0 | |||
DensityPassed(end+1,:)=[Pair(k,2) bb(Pair(k,2),1)+0.5*bb(Pair(k,2),3) bb(Pair(k,2),2)+0.5*bb(Pair(k,2),4)]; | |||
end | |||
end | |||
end | |||
end | |||
</source> | |||
Nachdem alle gleichen Pärchen herausgefiltert wurden, werden nun mindestens Dreier-Gruppen gesucht, da die Ecken des QR-Codes gesucht werden. Alle Pärchen, welche alleine sind, werden gelöscht und sind für die restliche Bearbeitung nicht von Belangen. Sollte mindestens eine Dreier-Gruppe gefunden werden, wird das Bild auf diesen Bereich zugeschnitten. Der Zuschnitt wird dynamisch ermittelt, indem der Abstand der Boxen zueinander genommen wird und zur Umrandung der Boxen prozentual addiert wird. | |||
<source lang="matlab"> | |||
while size(Passed,1)>3 | |||
kick=mean(sqrt(bsxfun(@minus,Passed(:,1),Passed(:,1)').^2 + bsxfun(@minus,Passed(:,2),Passed(:,2)').^2)); | |||
Passed(find(kick==max(kick)),:)=[]; | |||
end | |||
% get dynamic crop area | |||
Padding_Crop=mean(mean(sqrt(bsxfun(@minus,Passed(:,1),Passed(:,1)').^2 + bsxfun(@minus,Passed(:,2),Passed(:,2)').^2)))/3.5; | |||
minCrop = [min(Passed(:,1))-Padding_Crop min(Passed(:,2))-Padding_Crop]; | |||
maxCrop = [max(Passed(:,1))+Padding_Crop max(Passed(:,2))+Padding_Crop]; | |||
% grop image | |||
cAllcropped = imcrop(Image, [minCrop maxCrop-minCrop]); | |||
</source> | |||
=== Code === | === Code === | ||
Zeile 249: | Zeile 324: | ||
*[http://youtu.be/KA8hDldvfv0 YouTube: How to Decode a QR Code by Hand] | *[http://youtu.be/KA8hDldvfv0 YouTube: How to Decode a QR Code by Hand] | ||
*[http://de.wikipedia.org/wiki/QR_Code Wikipedia: QR Code] | *[http://de.wikipedia.org/wiki/QR_Code Wikipedia: QR Code] | ||
== Einzelnachweis == | |||
<references/> | |||
---- | ---- | ||
→ zurück zum Hauptartikel: [[DSB_SoSe2014| Digitale Signal- und Bildverarbeitung SoSe2014]] | → zurück zum Hauptartikel: [[DSB_SoSe2014| Digitale Signal- und Bildverarbeitung SoSe2014]] |
Aktuelle Version vom 16. Dezember 2014, 09:53 Uhr
Autor: Hauke Ludwig
Betreuer: Prof. Schneider
Motivation
QR-Codes sind omnipräsent, aber wie codiert man Informationen als QR-Code?
Ziel
Erzeugen Sie ein Matlab Executable, welches aus einer eingegebenen URL einen QR-Code mit beliebigem Logo in der Mitte erzeugt.
Aufgabe
- Arbeiten Sie sich in die Erstellung von QR-Codes ein.
- Erzeugen Sie ein Matlab Executable, welches aus einer eingegebenen URL einen QR-Code mit beliebigem Logo in der Mitte erzeugt.
- Lesen Sie mit Matlab diesen QR-Code ein, so dass dieser Sie auf eine Webpage verlinkt.
Lösung
Vorbearbeitung
Als erser Schritt wird das zu verarbeitende Bild in ein Schwarz/Weiß-Bild konvertiert. Bei dieser Konvertierung werden gleichzeitig alle Bildelemente welche nicht einem Grauwert entsprechen eleminiert. Grauwertentsprechende Werte sind Werte, welche gleichmäßige Anteile an Rot-, Grün- und Blautanteile haben. Das Ergebnis der Eleminierung einzelner Pixel ist deutlich an den schwarzen Flecken im Grayscale-Bild zu erkennen.
Extrahierung
Die Extrahierung des QR-Codes aus dem Bild erfolgt anhand eines Box-Find-Algorithmus, welcher bereits in MatLab implementiert ist [1]. Anhand der ermittelten Boxen können jene gefiltert werden, welche nicht eine annähernde rechteckige Form besitzen. Dieser Filter wird anhand eines Seitenverhältnisvergleiches durchgeführt, wie er nachfolgend dargestellt ist.
for cnt = 1 : numel(stat)
if stat(cnt).BoundingBox(3)/stat(cnt).BoundingBox(4)>0.5 && stat(cnt).BoundingBox(3)/stat(cnt).BoundingBox(4)<1.2 %like rectangle will be filtered
bb(cnt,:) = stat(cnt).BoundingBox;
else
bb(cnt,:)=[-1 -1 -1 -1];
end
end
Übrig gebliebene Boxen werden anhand ihrer Größe und ihres Abstandes zu einander verglichen. Dies geschieht, indem jede Box mit der anderen verglichen wird. Anschließend werden gleichartige Boxen gesucht und in Pärchen sortiert.
%% Calculate distance between Size of squares
distSize = sqrt(bsxfun(@minus,bb(:,3),bb(:,3)').^2 + bsxfun(@minus,bb(:,4),bb(:,4)').^2);
distSize(distSize(:,:)>distSizeThresh)=0;
%% Calculate distance between position of squares
distPos = sqrt(bsxfun(@minus,bb(:,1),bb(:,1)').^2 + bsxfun(@minus,bb(:,2),bb(:,2)').^2);
distPos(distPos(:,:)>distPosThresh)=0;
%% Filter boxes for euqal squares
for k=1:size(distSize,1)
for l=1:k
if distSize(k,l)~= 0 && size(find(distSize==distSize(k,l)),1)>=6
Pair(end+1,:)=[k l];
end
end
end
Um zu ermitteln, ob die Boxen auch den gleichen Inhalt besitzen, wird die "Dichte" der schwarzen Pixel in der Box ermittelt. Wenn die Boxen die gleichen Dichten aufweisen, dann wird angenommen, dass die Boxen eine sehr starke Ähnlichkeiot aufweisen.
for k=1:size(Pair,1)
CropImage1=imcrop(cAll,bb(Pair(k,1),:));
Density(k,1)=sum(sum(size(find(CropImage1~=0),1)))/(bb(Pair(k,1),3)*bb(Pair(k,1),4));
CropImage2=imcrop(cAll,bb(Pair(k,2),:));
Density(k,2)=sum(sum(size(find(CropImage2~=0),1)))/(bb(Pair(k,2),3)*bb(Pair(k,2),4));
if abs(Density(k,1)-Density(k,2)) < diff_Density
if Density(k,1)<0.3 && Density(k,1)>0.1
rectangle('position',bb(Pair(k,1),:),'edgecolor','g','linewidth',2);
Padding_Crop = max([Padding_Crop bb(Pair(k,1),3:4)./1.1]);
if size(find(DensityPassed(:,1)==Pair(k,1)),1) == 0
DensityPassed(end+1,:)=[Pair(k,1) bb(Pair(k,1),1)+0.5*bb(Pair(k,1),3) bb(Pair(k,1),2)+0.5*bb(Pair(k,1),4)];
end
end
if Density(k,2)<0.3 && Density(k,2)>0.1
rectangle('position',bb(Pair(k,2),:),'edgecolor','g','linewidth',2);
Padding_Crop = max([Padding_Crop bb(Pair(k,1),3:4)]./1.1);
if size(find(DensityPassed(:,1)==Pair(k,2)),1) == 0
DensityPassed(end+1,:)=[Pair(k,2) bb(Pair(k,2),1)+0.5*bb(Pair(k,2),3) bb(Pair(k,2),2)+0.5*bb(Pair(k,2),4)];
end
end
end
end
Nachdem alle gleichen Pärchen herausgefiltert wurden, werden nun mindestens Dreier-Gruppen gesucht, da die Ecken des QR-Codes gesucht werden. Alle Pärchen, welche alleine sind, werden gelöscht und sind für die restliche Bearbeitung nicht von Belangen. Sollte mindestens eine Dreier-Gruppe gefunden werden, wird das Bild auf diesen Bereich zugeschnitten. Der Zuschnitt wird dynamisch ermittelt, indem der Abstand der Boxen zueinander genommen wird und zur Umrandung der Boxen prozentual addiert wird.
while size(Passed,1)>3
kick=mean(sqrt(bsxfun(@minus,Passed(:,1),Passed(:,1)').^2 + bsxfun(@minus,Passed(:,2),Passed(:,2)').^2));
Passed(find(kick==max(kick)),:)=[];
end
% get dynamic crop area
Padding_Crop=mean(mean(sqrt(bsxfun(@minus,Passed(:,1),Passed(:,1)').^2 + bsxfun(@minus,Passed(:,2),Passed(:,2)').^2)))/3.5;
minCrop = [min(Passed(:,1))-Padding_Crop min(Passed(:,2))-Padding_Crop];
maxCrop = [max(Passed(:,1))+Padding_Crop max(Passed(:,2))+Padding_Crop];
% grop image
cAllcropped = imcrop(Image, [minCrop maxCrop-minCrop]);
Code
%****************************************************************
% Hochschule Hamm-Lippstadt *
%****************************************************************
% Modul : QR Code Extractor *
% *
% Datum : 15-Jun-2014 *
% *
% Funktion : Extrahierung eines QR_Codes aus Bildern *
% *
% Implementation : MATLAB R2013a *
% *
% Author : Hauke Ludwig *
% *
% Letzte Änderung : 15-Jun-2014 *
% *
% Copyright (c) 2014, Hauke Ludwig *
% All rights reserved. *
%***************************************************************/
function [Pair] = extract (Image)
%% extract
% returns extracted QR-Code as bitfield from Image
% (as RGB-Matrix (uint8) width x heigh x RGB )
%% Defines
distSizeThresh = 10;
distPosThresh = 100;
distDensityThresh = 15;
diff_Density = 0.04;
Padding_Crop = 50;
%% Matrix
Pair = [0 0];
DensityPassed = [0 0 0];
%% Code
% Image Convertion
gray= rgb2gray(Image);
gray(~(abs(Image(:,:,1)-Image(:,:,2))+abs(Image(:,:,2)-Image(:,:,3))+abs(Image(:,:,1)-Image(:,:,3))<40))=0;
BW=~im2bw(Image,graythresh(Image));
BW(~(abs(Image(:,:,1)-Image(:,:,2))+abs(Image(:,:,2)-Image(:,:,3))+abs(Image(:,:,1)-Image(:,:,3))<40))=0;
cAll = edge(BW,'sobel','vertical')+edge(BW,'sobel','horizontal')+edge(gray,'sobel','horizontal')+edge(gray,'sobel','vertical');
%% DISPLAY RESULT
figure
subplot(2,4,1)
imshow(Image)
title('Source')
subplot(2,4,4)
imshow(cAll)
title('Canny')
subplot(2,4,2)
imshow(gray)
title('Grayscale')
subplot(2,4,3)
imshow(~BW)
title('Black/White')
%% Find boxes in image
stat = regionprops(logical(bwareaopen(cAll,100)),'boundingbox');
for cnt = 1 : numel(stat)
if stat(cnt).BoundingBox(3)/stat(cnt).BoundingBox(4)>0.5 && stat(cnt).BoundingBox(3)/stat(cnt).BoundingBox(4)<1.2 %like rectangle will be filtered
bb(cnt,:) = stat(cnt).BoundingBox;
else
bb(cnt,:)=[-1 -1 -1 -1];
end
end
bb(bb(:,:)==-1)=[];
bb=reshape(bb,size(bb,2)/4,4);
%% Calculate distance between Size of squares
distSize = sqrt(bsxfun(@minus,bb(:,3),bb(:,3)').^2 + bsxfun(@minus,bb(:,4),bb(:,4)').^2);
distSize(distSize(:,:)>distSizeThresh)=0;
%% Calculate distance between position of squares
distPos = sqrt(bsxfun(@minus,bb(:,1),bb(:,1)').^2 + bsxfun(@minus,bb(:,2),bb(:,2)').^2);
distPos(distPos(:,:)>distPosThresh)=0;
%% DISPLAY RESULT
subplot(2,4,6)
imshow(cAll)
title('Canny operated / equal square detection')
hold on
%% Filter boxes for euqal squares
for k=1:size(distSize,1)
for l=1:k
if distSize(k,l)~= 0 && size(find(distSize==distSize(k,l)),1)>=6
Pair(end+1,:)=[k l];
end
end
end
Pair(1,:)=[];
for k=1:size(Pair,1)
CropImage1=imcrop(cAll,bb(Pair(k,1),:));
Density(k,1)=sum(sum(size(find(CropImage1~=0),1)))/(bb(Pair(k,1),3)*bb(Pair(k,1),4));
CropImage2=imcrop(cAll,bb(Pair(k,2),:));
Density(k,2)=sum(sum(size(find(CropImage2~=0),1)))/(bb(Pair(k,2),3)*bb(Pair(k,2),4));
if abs(Density(k,1)-Density(k,2)) < diff_Density
if Density(k,1)<0.3 && Density(k,1)>0.1
rectangle('position',bb(Pair(k,1),:),'edgecolor','g','linewidth',2);
Padding_Crop = max([Padding_Crop bb(Pair(k,1),3:4)./1.1]);
if size(find(DensityPassed(:,1)==Pair(k,1)),1) == 0
DensityPassed(end+1,:)=[Pair(k,1) bb(Pair(k,1),1)+0.5*bb(Pair(k,1),3) bb(Pair(k,1),2)+0.5*bb(Pair(k,1),4)];
end
end
if Density(k,2)<0.3 && Density(k,2)>0.1
rectangle('position',bb(Pair(k,2),:),'edgecolor','g','linewidth',2);
Padding_Crop = max([Padding_Crop bb(Pair(k,1),3:4)]./1.1);
if size(find(DensityPassed(:,1)==Pair(k,2)),1) == 0
DensityPassed(end+1,:)=[Pair(k,2) bb(Pair(k,2),1)+0.5*bb(Pair(k,2),3) bb(Pair(k,2),2)+0.5*bb(Pair(k,2),4)];
end
end
end
end
if size(DensityPassed,1)>1
DensityPassed(1,:)=[];
end
if size(DensityPassed,1)>1
distDensity = sqrt(bsxfun(@minus,DensityPassed(:,2),DensityPassed(:,2)').^2 + bsxfun(@minus,DensityPassed(:,3),DensityPassed(:,3)').^2);
for k=1:size(distDensity,1)
for l=k+1:size(distDensity,2)
PairDensity(k,1)=k;
if distDensity(k,l) < distDensityThresh
if sum(sum(ismember(PairDensity(1:k-1,:),k))) ~= 0
PairDensity(k,:)=[];
for x=1:size(PairDensity,1)-1
if sum(ismember(PairDensity(x,:),k)) ~= 0 && sum(ismember(PairDensity(x,:),l)) == 0
PairDensity(x,end+1)= l;
end
end
else
PairDensity(k,end+1)= l;
end
end
end
if size(PairDensity,1) >= k
if sum(PairDensity(k,2:end)) == 0
PairDensity(k,:)=[];
end
end
end
Passed = [-1 -1];
for k=1:size(PairDensity,1)
if sum(PairDensity(k,2:end))>0 % Pair found
summean = [0 0];
count = 0;
for x=1:size(PairDensity,2)
if PairDensity(k,x) ~= 0
summean = [DensityPassed(PairDensity(k,x),2)+summean(1) DensityPassed(PairDensity(k,x),3)+summean(2)];
count = count +1;
end
end
Passed(end+1,:)=[summean(1)/count summean(2)/count];
else if PairDensity(k,1) ~= 0 % no Pair
Passed(end+1,:)= DensityPassed(PairDensity(k,1),2:3);
end
end
end
subplot(2,4,6)
plot(Passed(:,1),Passed(:,2),'*r');
Passed(1,:)=[];
%% Crop Image on Area of Interest
while size(Passed,1)>3
kick=mean(sqrt(bsxfun(@minus,Passed(:,1),Passed(:,1)').^2 + bsxfun(@minus,Passed(:,2),Passed(:,2)').^2));
Passed(find(kick==max(kick)),:)=[];
end
% get dynamic crop area
Padding_Crop=mean(mean(sqrt(bsxfun(@minus,Passed(:,1),Passed(:,1)').^2 + bsxfun(@minus,Passed(:,2),Passed(:,2)').^2)))/3.5;
minCrop = [min(Passed(:,1))-Padding_Crop min(Passed(:,2))-Padding_Crop];
maxCrop = [max(Passed(:,1))+Padding_Crop max(Passed(:,2))+Padding_Crop];
% grop image
cAllcropped = imcrop(Image, [minCrop maxCrop-minCrop]);
%% DISPLAY RESULT
subplot(2,4,5)
imshow(cAllcropped);
title('croped image from source')
normCropped = im2bw(cAllcropped);
subplot(2,4,7)
imshow(cAllcropped);
hold on
plot(Passed(:,1)-minCrop(1),Passed(:,2)-minCrop(2),'*r');
title('croped image from source with equal squares')
subplot(2,4,8)
imshow(normCropped);
hold on
plot(Passed(:,1)-minCrop(1),Passed(:,2)-minCrop(2),'*r');
title('croped image from black/white')
DensityPassed = [ DensityPassed(:,1) DensityPassed(:,2)-minCrop(1) DensityPassed(:,3)-minCrop(2)];
%% Find cornerpairs
cPair=sqrt(bsxfun(@minus,Passed(:,1),Passed(:,1)').^2 + bsxfun(@minus,Passed(:,2),Passed(:,2)').^2);
cPair(cPair(:,:)==0)=NaN;
minPair=min(cPair);
else
fprintf('Less equal Square to find a QR Code!\n');
end
end
Siehe auch
Weblinks
- QR Generator
- Open Source QR Code Generator
- Mobile Tagging
- YouTube: How to Decode a QR Code by Hand
- Wikipedia: QR Code
Einzelnachweis
→ zurück zum Hauptartikel: Digitale Signal- und Bildverarbeitung SoSe2014