QR-Code erzeugen und lesen

Aus HSHL Mechatronik
Zur Navigation springen Zur Suche springen

Autor: Hauke Ludwig
Betreuer: Prof. Schneider

Wohin man auch blickt ... überall prangert ein QR-Code an Wänden.

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

  1. Arbeiten Sie sich in die Erstellung von QR-Codes ein.
  2. Erzeugen Sie ein Matlab Executable, welches aus einer eingegebenen URL einen QR-Code mit beliebigem Logo in der Mitte erzeugt.
  3. 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.

Vorbearbeitung des Bildes

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

Beispiele für QR Codes

Weblinks

Einzelnachweis

  1. [1] MatLab, Dokumentation "regionprops" -> Bounding Box

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