summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordakkar <dakkar@thenautilus.net>2016-11-10 19:14:40 +0000
committerdakkar <dakkar@thenautilus.net>2016-11-10 19:14:51 +0000
commit207844ee0d269545c9c885f34f6cf2395449358b (patch)
treec6f305927cdc85722e6aa71dda27e9fa50e8cde9
parentallow parsed_literal blocks (diff)
downloadthenautilus-207844ee0d269545c9c885f34f6cf2395449358b.tar.gz
thenautilus-207844ee0d269545c9c885f34f6cf2395449358b.tar.bz2
thenautilus-207844ee0d269545c9c885f34f6cf2395449358b.zip
fancy QR codes articla
-rw-r--r--additions/SW/fancy-qr-codes/kawaii-beholder-qr.pngbin0 -> 270527 bytes
-rw-r--r--additions/SW/fancy-qr-codes/kawaii-beholder.jpgbin0 -> 433149 bytes
-rw-r--r--additions/SW/fancy-qr-codes/kawaii-beholder.pngbin0 -> 406802 bytes
-rw-r--r--additions/SW/fancy-qr-codes/kawaii-beholder.xcfbin0 -> 2448572 bytes
-rw-r--r--additions/SW/fancy-qr-codes/overlay-qr.pl25
-rw-r--r--additions/SW/fancy-qr-codes/squirrel-girl-qr.pngbin0 -> 91914 bytes
-rw-r--r--additions/SW/fancy-qr-codes/squirrel-girl.jpgbin0 -> 50173 bytes
-rw-r--r--additions/SW/fancy-qr-codes/squirrel-girl.pngbin0 -> 183926 bytes
-rw-r--r--additions/SW/fancy-qr-codes/squirrel-girl.xcfbin0 -> 306256 bytes
-rw-r--r--src/SW/fancy-qr-codes/.gitignore2
-rw-r--r--src/SW/fancy-qr-codes/document.en.rest.txt195
-rw-r--r--src/SW/fancy-qr-codes/document.it.rest.txt196
l---------src/SW/fancy-qr-codes/du2html.xsl1
13 files changed, 419 insertions, 0 deletions
diff --git a/additions/SW/fancy-qr-codes/kawaii-beholder-qr.png b/additions/SW/fancy-qr-codes/kawaii-beholder-qr.png
new file mode 100644
index 0000000..8e27c11
--- /dev/null
+++ b/additions/SW/fancy-qr-codes/kawaii-beholder-qr.png
Binary files differ
diff --git a/additions/SW/fancy-qr-codes/kawaii-beholder.jpg b/additions/SW/fancy-qr-codes/kawaii-beholder.jpg
new file mode 100644
index 0000000..9722164
--- /dev/null
+++ b/additions/SW/fancy-qr-codes/kawaii-beholder.jpg
Binary files differ
diff --git a/additions/SW/fancy-qr-codes/kawaii-beholder.png b/additions/SW/fancy-qr-codes/kawaii-beholder.png
new file mode 100644
index 0000000..d8e1862
--- /dev/null
+++ b/additions/SW/fancy-qr-codes/kawaii-beholder.png
Binary files differ
diff --git a/additions/SW/fancy-qr-codes/kawaii-beholder.xcf b/additions/SW/fancy-qr-codes/kawaii-beholder.xcf
new file mode 100644
index 0000000..6c7e385
--- /dev/null
+++ b/additions/SW/fancy-qr-codes/kawaii-beholder.xcf
Binary files differ
diff --git a/additions/SW/fancy-qr-codes/overlay-qr.pl b/additions/SW/fancy-qr-codes/overlay-qr.pl
new file mode 100644
index 0000000..b2cf1c2
--- /dev/null
+++ b/additions/SW/fancy-qr-codes/overlay-qr.pl
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Data::QRCode;
+use Imager::QRCode::Fancy;
+use Imager;
+
+my ($string,$input_file,$output_file) = @ARGV;
+
+my $qr_code = Data::QRCode->new(
+ input_data => $string,
+ error_correction_level => 'H',
+);
+
+print "width: ",$qr_code->width,"\n";
+
+my $src_img = Imager->new(
+ file => $input_file,
+) or die Imager->errstr;
+
+my $img = Imager::QRCode::Fancy::make({
+ qr_code => $qr_code,
+ image => $src_img,
+});
+$img->write(file=>$output_file);
diff --git a/additions/SW/fancy-qr-codes/squirrel-girl-qr.png b/additions/SW/fancy-qr-codes/squirrel-girl-qr.png
new file mode 100644
index 0000000..361e273
--- /dev/null
+++ b/additions/SW/fancy-qr-codes/squirrel-girl-qr.png
Binary files differ
diff --git a/additions/SW/fancy-qr-codes/squirrel-girl.jpg b/additions/SW/fancy-qr-codes/squirrel-girl.jpg
new file mode 100644
index 0000000..f721f6e
--- /dev/null
+++ b/additions/SW/fancy-qr-codes/squirrel-girl.jpg
Binary files differ
diff --git a/additions/SW/fancy-qr-codes/squirrel-girl.png b/additions/SW/fancy-qr-codes/squirrel-girl.png
new file mode 100644
index 0000000..277c2f3
--- /dev/null
+++ b/additions/SW/fancy-qr-codes/squirrel-girl.png
Binary files differ
diff --git a/additions/SW/fancy-qr-codes/squirrel-girl.xcf b/additions/SW/fancy-qr-codes/squirrel-girl.xcf
new file mode 100644
index 0000000..a1420f6
--- /dev/null
+++ b/additions/SW/fancy-qr-codes/squirrel-girl.xcf
Binary files differ
diff --git a/src/SW/fancy-qr-codes/.gitignore b/src/SW/fancy-qr-codes/.gitignore
new file mode 100644
index 0000000..08d33d4
--- /dev/null
+++ b/src/SW/fancy-qr-codes/.gitignore
@@ -0,0 +1,2 @@
+/document.en.du.xml
+/document.it.du.xml
diff --git a/src/SW/fancy-qr-codes/document.en.rest.txt b/src/SW/fancy-qr-codes/document.en.rest.txt
new file mode 100644
index 0000000..152b8a2
--- /dev/null
+++ b/src/SW/fancy-qr-codes/document.en.rest.txt
@@ -0,0 +1,195 @@
+==============
+Fancy QR Codes
+==============
+:CreationDate: 2016-11-10 17:06:20
+:Id: SW/fancy-qr-codes
+:tags: - software
+ - perl
+
+You may have seen `sylnsfar's "Artistic QR Code" library
+<https://github.com/sylnsfar/qrcode>`_. It produces non-standard QR
+codes that incorporate arbitrary pictures, but are nonetheless
+machine-readable [#]_.
+
+Of course, I had to understand how it worked ☺
+
+It doesn't take much to find that the magic happens in the ``combine``
+function in |myqrpy|_::
+
+ if ver > 1:
+ aloc = alig_location[ver-2]
+ for a in range(len(aloc)):
+ for b in range(len(aloc)):
+ if not ((a==b==0) or (a==len(aloc)-1 and b==0) or (a==0 and b==len(aloc)-1)):
+ for i in range(3*(aloc[a]-2), 3*(aloc[a]+3)):
+ for j in range(3*(aloc[b]-2), 3*(aloc[b]+3)):
+ aligs.append((i,j))
+
+ for i in range(qr.size[0]-24):
+ for j in range(qr.size[1]-24):
+ if not ((i in (18,19,20)) or (j in (18,19,20)) or (i<24 and j<24) or (i<24 and j>qr.size[1]-49) or (i>qr.size[0]-49 and j<24) or ((i,j) in aligs) or (i%3==1 and j%3==1) or (bg0.getpixel((i,j))[3]==0)):
+ qr.putpixel((i+12,j+12), bg.getpixel((i,j)))
+
+That code is full of magic numbers, and the multiple nested loops
+don't really help legibility much. What's going on in there?
+
+To understand where those numbers came from, I studied `a QR code
+tutorial <http://www.thonky.com/qr-code-tutorial/>`_. The most useful
+page for our purposes is `the one that explains "function patterns"
+<http://www.thonky.com/qr-code-tutorial/module-placement-matrix>`_.
+
+Everything in that function assumes a 3-pixel QR module, so:
+
+* the first block collects the placements of the alignment
+ patterns
+
+ * but only if the code is large enough to need them (``ver > 1``)
+ * and ignoring those that would collide with finder patterns
+ (``(a==b==0) or (a==len(aloc)-1 and b==0) or (a==0 and
+ b==len(aloc)-1)`` means "top-left, top-right, bottom-left corners")
+
+* the second block replaces pixels in the QR code (``qr.putpixel``)
+ with pixels from the fancy picture (``bg.getpixel``) if:
+
+ * they're not in timing patterns (``(i in (18,19,20)) or (j in
+ (18,19,20))``)
+ * they're not in finder patterns (``(i<24 and j<24) or (i<24 and
+ j>qr.size[1]-49) or (i>qr.size[0]-49 and j<24)``)
+ * they're not in alignment patterns (``(i,j) in aligs``)
+ * and two more conditions I'll explain later
+
+So all those magic numbers define the placements of finder / timing /
+alignment patterns: those *really* have to be there, otherwise the
+scanning / recognition code won't even realise it's looking at a QR
+code.
+
+The last two conditions are::
+
+ (i%3==1 and j%3==1) or (bg0.getpixel((i,j))[3]==0)
+
+The second one skips transparent pixels in the picture (``bg0`` is a
+ARGB-version of the input picture ``bg``, and the pixel value at
+position 3 is the alpha value).
+
+The first one makes sure to leave the central pixel of each 3×3 QR
+module untouched: we want the QR data to remain there!
+
+.. _`the procedure outlined before`:
+
+So, another way to get the same result would be:
+
+* paint a normal black & white QR code
+* layer the fancy picture on top of it, honouring the alpha channel
+* paint all non-data modules on top
+* paint the data modules on top, but only their central pixels
+
+Of course, it's not trivial to know whether a module is part of the
+data or of a function pattern, but we're in luck: the |libqrencode|_ C
+library `provides us with that information
+<http://fukuchi.org/works/qrencode/manual/structQRcode.html#details>`_!
+It generates the QR code as a matrix of bytes, and each byte is a
+bitfield::
+
+ MSB 76543210 LSB
+ |||||||`- 1=black/0=white
+ ||||||`-- data and ecc code area
+ |||||`--- format information
+ ||||`---- version information
+ |||`----- timing pattern
+ ||`------ alignment pattern
+ |`------- finder pattern and separator
+ `-------- non-data modules (format, timing, etc.)
+
+The Perl modules |text-qrcode|_ and |imager-qrcode|_ wrap that
+library, but do not expose the internal byte matrix. So of course I
+wrapped it again myself ☺
+
+|alien-qrencode|_ uses |alien-base|_ to install |libqrencode|_, then
+|data-qrcode|_ binds it via |inline-module|_ and provides a Perlish
+API (see `the test file </cgit/Data-QRCode/tree/t/qrcode.t>`_ for an
+example).
+
+Now, we only need to deal with the actual images: let's use |imager|_,
+which seems simple and flexible enough. The `actual code
+</cgit/Imager-QRCode-Fancy/tree/lib/Imager/QRCode/Fancy.pm>`_ is maybe
+too flexible, but the core of the logic is::
+
+ my $back_image = qr_image($qr_code);
+
+ return $back_image unless $fancy_picture;
+
+ my $front_image = qr_dots_image($qr_code);
+
+ my $target = Imager->new(xsize=>$size,ysize=>$size);
+ $target->paste(src => $back_image);
+ $target->compose(src => $fancy_picture);
+ $target->compose(src => $front_image);
+
+ return $target;
+
+which is a direct translation of `the procedure outlined
+before`_.
+
+Once we've installed |alien-qrencode|_, |data-qrcode|_, and
+|qr-fancy|_, we can say:
+
+.. parsed_literal::
+
+ perl `overlay-qr.pl`_ 'http://pokketmowse.deviantart.com/art/In-the-Kawaii-of-the-Beholder-177076230' `kawaii-beholder.png`_ `kawaii-beholder-qr.png`_
+
+and get this:
+
+.. image:: kawaii-beholder-qr.png
+
+Or maybe:
+
+.. parsed_literal::
+
+ perl `overlay-qr.pl`_ 'Squirrel Girl is the best' `squirrel-girl.png`_ `squirrel-girl-qr.png`_
+
+and get this:
+
+.. image:: squirrel-girl-qr.png
+
+Notice how the size of the "dots" adapts to the resolution of the
+picture (although it could be smarter), and how transparency is
+maintained.
+
+.. [#] more or less: I think you need a high-enough-resolution camera
+ and some pretty forgiving recognition code
+
+.. _myqrpy: https://github.com/sylnsfar/qrcode/blob/master/MyQR/myqr.py#L51
+.. |myqrpy| replace:: ``myqr.py``
+
+.. _libqrencode: http://fukuchi.org/works/qrencode/index.html.en
+.. |libqrencode| replace:: ``libqrencode``
+
+.. _`text-qrcode`: https://metacpan.org/pod/Text::QRCode
+.. |text-qrcode| replace:: ``Text::QRCode``
+
+.. _`imager-qrcode`: https://metacpan.org/pod/Imager::QRCode
+.. |imager-qrcode| replace:: ``Imager::QRCode``
+
+.. _`alien-qrencode`: /cgit/Alien-QREncode/
+.. |alien-qrencode| replace:: ``Alien::QREncode``
+
+.. _`data-qrcode`: /cgit/Data-QRCode/
+.. |data-qrcode| replace:: ``Data::QRCode``
+
+.. _`qr-fancy`: /cgit/Imager-QRCode-Fancy/
+.. |qr-fancy| replace:: ``Imager::QRCode::Fancy``
+
+.. _`inline-module`: https://metacpan.org/pod/Inline::Module
+.. |inline-module| replace:: ``Inline::Module``
+
+.. _`alien-base`: https://metacpan.org/pod/Alien::Base
+.. |alien-base| replace:: ``Alien::Base``
+
+.. _imager: https://metacpan.org/pod/Imager
+.. |imager| replace:: ``Imager``
+
+.. _`overlay-qr.pl`: overlay-qr.pl
+.. _`kawaii-beholder.png`: kawaii-beholder.png
+.. _`kawaii-beholder-qr.png`: kawaii-beholder-qr.png
+.. _`squirrel-girl.png`: squirrel-girl.png
+.. _`squirrel-girl-qr.png`: squirrel-girl-qr.png
diff --git a/src/SW/fancy-qr-codes/document.it.rest.txt b/src/SW/fancy-qr-codes/document.it.rest.txt
new file mode 100644
index 0000000..ab8b6e7
--- /dev/null
+++ b/src/SW/fancy-qr-codes/document.it.rest.txt
@@ -0,0 +1,196 @@
+==================
+Codici QR decorati
+==================
+:CreationDate: 2016-11-10 17:06:20
+:Id: SW/fancy-qr-codes
+:tags: - software
+ - perl
+
+Potreste aver visto la `libreria "Artistic QR Code" di sylnsfar
+<https://github.com/sylnsfar/qrcode>`_. Produce codici QR non-standard
+che incorporano immagini arbitrarie, ma sono comunque leggibili a
+macchina [#]_
+
+Ovviamente, dovevo capire come funziona ☺
+
+Non ci vuole molto a scoprire che la magia avviene nella funzione
+ ``combine`` in |myqrpy|_::
+
+ if ver > 1:
+ aloc = alig_location[ver-2]
+ for a in range(len(aloc)):
+ for b in range(len(aloc)):
+ if not ((a==b==0) or (a==len(aloc)-1 and b==0) or (a==0 and b==len(aloc)-1)):
+ for i in range(3*(aloc[a]-2), 3*(aloc[a]+3)):
+ for j in range(3*(aloc[b]-2), 3*(aloc[b]+3)):
+ aligs.append((i,j))
+
+ for i in range(qr.size[0]-24):
+ for j in range(qr.size[1]-24):
+ if not ((i in (18,19,20)) or (j in (18,19,20)) or (i<24 and j<24) or (i<24 and j>qr.size[1]-49) or (i>qr.size[0]-49 and j<24) or ((i,j) in aligs) or (i%3==1 and j%3==1) or (bg0.getpixel((i,j))[3]==0)):
+ qr.putpixel((i+12,j+12), bg.getpixel((i,j)))
+
+Quel codice è pieno di numeri magici, e i vari cicli annidati non
+aiutano la leggibilità. Cosa fa tutta quella roba?
+
+Per capire da dove venissero quei numeri, mi sono studiato `un
+tutorial sui codici QR
+<http://www.thonky.com/qr-code-tutorial/>`_. Per i nostri scopi, la
+pagina più utile è `quella che spiega i "function patterns"
+<http://www.thonky.com/qr-code-tutorial/module-placement-matrix>`_.
+
+In quella funzione, tutto suppone che i moduli siano 3×3 pixel, per
+cui:
+
+* il primo blocco costruisce le coordinate degli "alignment
+ patterns"
+
+ * ma solo se il codice è grande abbastanza da usarli (``ver > 1``)
+ * e saltando quelli che collidono coi "finder patterns: (``(a==b==0)
+ or (a==len(aloc)-1 and b==0) or (a==0 and b==len(aloc)-1)`` vuol
+ dire "angoli alto-sinistra, alto-destra, basso-sinistra corners")
+
+* il secondo blocco rimpiazza i pixel del codice QR (``qr.putpixel``)
+ con i pixel dall'immagine (``bg.getpixel``) se:
+
+ * non fanno parte dei "timing patterns" (``(i in (18,19,20)) or (j
+ in (18,19,20))``)
+ * non fanno parte dei "finder patterns" (``(i<24 and j<24) or (i<24
+ and j>qr.size[1]-49) or (i>qr.size[0]-49 and j<24)``)
+ * non fanno parte degli "alignment patterns" (``(i,j) in aligs``)
+ * e altre due condizioni che spiego dopo
+
+Quindi tutti quei numeri magici definiscono le coordinate dei vari
+pattern: quelli *devono* esserci, altrimenti il programma di lettura /
+riconoscimento non si accorgerebbe neppure che sta guardando un codice
+QR.
+
+Le ultime due condizioni sono::
+
+ (i%3==1 and j%3==1) or (bg0.getpixel((i,j))[3]==0)
+
+La seconda salta i pixel trasparenti dell'immagine (``bg0`` è una vesione
+ARGB dell'immagine ``bg``, e il valore in posizione 3 del pixel è il
+valore alpha).
+
+La prima condizione fa in modo che il pixel centrale di ciascun modulo
+3×3 resti inalterato: vogliamo che le informazioni del codice QR ci
+siano, da qualche parte!
+
+.. _`procedura descritta sopra`:
+
+In altre parole, per ottenere lo stesso risultato potremmo:
+
+* disegnare un normale codice QR in bianco e nero
+* sovrapporre la nostra immagine, tenendo conto del canale alpha
+* disegnarci sopra i moduli non-dati
+* disegnarci sopra i moduli dati, ma solo i pixel centrali di ciascuno
+
+Ovviamente, non è proprio banale sapere se un modulo è parte dei dati
+o di un pattern, ma siamo fortunati: la libreria C |libqrencode|_ `ci
+fornisce proprio questa informazione
+<http://fukuchi.org/works/qrencode/manual/structQRcode.html#details>`_!
+Genera il codice QR come matrice di byte, e ogni byte è in bit-field::
+
+ MSB 76543210 LSB
+ |||||||`- 1=nero/0=bianco
+ ||||||`-- dati e ECC
+ |||||`--- informazione di formato
+ ||||`---- informazione di versione
+ |||`----- timing pattern
+ ||`------ alignment pattern
+ |`------- finder pattern e separatori
+ `-------- moduli non-dati (format, timing, &c)
+
+I moduli Perl |text-qrcode|_ e |imager-qrcode|_ usano quella libreria,
+ma non espongono i dettagli interni. Per cui ho ri-scritto un po' di
+pezzi ☺
+
+|alien-qrencode|_ usa |alien-base|_ per installare |libqrencode|_; poi
+|data-qrcode|_ usa |inline-module|_ per accederla da Perl, e fornisce
+un'interfaccia Perlosa (`il file di test
+</cgit/Data-QRCode/tree/t/qrcode.t>`_ mostra un esempio d'uso).
+
+Ora, dobbiamo solo preoccuparci delle immagini: usiamo |imager|_,
+che sembra abbastanza semplice e potente. Il `codice vero e proprio
+</cgit/Imager-QRCode-Fancy/tree/lib/Imager/QRCode/Fancy.pm>`_ è forse
+troppo flessibile, ma il grosso della logica è::
+
+ my $back_image = qr_image($qr_code);
+
+ return $back_image unless $fancy_picture;
+
+ my $front_image = qr_dots_image($qr_code);
+
+ my $target = Imager->new(xsize=>$size,ysize=>$size);
+ $target->paste(src => $back_image);
+ $target->compose(src => $fancy_picture);
+ $target->compose(src => $front_image);
+
+ return $target;
+
+che è una traduzione diretta della `procedura descritta sopra`_.
+
+Una volta installati |alien-qrencode|_, |data-qrcode|_, e
+|qr-fancy|_, possiamo dire:
+
+.. parsed_literal::
+
+ perl `overlay-qr.pl`_ 'http://pokketmowse.deviantart.com/art/In-the-Kawaii-of-the-Beholder-177076230' `kawaii-beholder.png`_ `kawaii-beholder-qr.png`_
+
+e ottenere questo:
+
+.. image:: kawaii-beholder-qr.png
+
+O magari:
+
+.. parsed_literal::
+
+ perl `overlay-qr.pl`_ 'Squirrel Girl is the best' `squirrel-girl.png`_ `squirrel-girl-qr.png`_
+
+e ottenere questo:
+
+.. image:: squirrel-girl-qr.png
+
+Notare come la dimensione dei "puntini" ci adatti alla risoluzione
+dell'immagine (anche se potrebbe essere più furba), e che la
+trasparenza è rispettata.
+
+.. [#] circa: mi sa che serve una fotocamera a risoluzione altina, e
+ un programma di riconoscimento abbastanza permissivo
+
+.. _myqrpy: https://github.com/sylnsfar/qrcode/blob/master/MyQR/myqr.py#L51
+.. |myqrpy| replace:: ``myqr.py``
+
+.. _libqrencode: http://fukuchi.org/works/qrencode/index.html.en
+.. |libqrencode| replace:: ``libqrencode``
+
+.. _`text-qrcode`: https://metacpan.org/pod/Text::QRCode
+.. |text-qrcode| replace:: ``Text::QRCode``
+
+.. _`imager-qrcode`: https://metacpan.org/pod/Imager::QRCode
+.. |imager-qrcode| replace:: ``Imager::QRCode``
+
+.. _`alien-qrencode`: /cgit/Alien-QREncode/
+.. |alien-qrencode| replace:: ``Alien::QREncode``
+
+.. _`data-qrcode`: /cgit/Data-QRCode/
+.. |data-qrcode| replace:: ``Data::QRCode``
+
+.. _`qr-fancy`: /cgit/Imager-QRCode-Fancy/
+.. |qr-fancy| replace:: ``Imager::QRCode::Fancy``
+
+.. _`inline-module`: https://metacpan.org/pod/Inline::Module
+.. |inline-module| replace:: ``Inline::Module``
+
+.. _`alien-base`: https://metacpan.org/pod/Alien::Base
+.. |alien-base| replace:: ``Alien::Base``
+
+.. _imager: https://metacpan.org/pod/Imager
+.. |imager| replace:: ``Imager``
+
+.. _`overlay-qr.pl`: overlay-qr.pl
+.. _`kawaii-beholder.png`: kawaii-beholder.png
+.. _`kawaii-beholder-qr.png`: kawaii-beholder-qr.png
+.. _`squirrel-girl.png`: squirrel-girl.png
+.. _`squirrel-girl-qr.png`: squirrel-girl-qr.png
diff --git a/src/SW/fancy-qr-codes/du2html.xsl b/src/SW/fancy-qr-codes/du2html.xsl
new file mode 120000
index 0000000..e2487e0
--- /dev/null
+++ b/src/SW/fancy-qr-codes/du2html.xsl
@@ -0,0 +1 @@
+../../../templates/du2html.xsl \ No newline at end of file