[ANN] QRCode.jl: Create QR Code within Julia

Simply use add QRCode to play with it. Here is the source and the documentation.

This is my first project in Julia, so I’m looking for feedback on my code. I’m interested in making it more idiomatic, and possibly more efficient (as far as I know, there is a lot of room to improve).

In the mean time, I hope this tool can be useful for the community. The package was created during the Efficient Scientific Computing with Julia workshop, taught by Valentin Churavy at the Okinawa Institute of Science and Technology in July 2019. It seems I cannot post more than 2 links at the moment, links to all these things are available on the README on GitHub.

21 Likes

Neat! Exporting in vector formats (PDF or SVG) would be nice, too.

2 Likes

Hey, this is great! I needed this a few weeks ago, trying the #betterposter thing (which recommends using a QR code to link to further information).

Making SVG or PDF is easy enough:

using Luxor, QRCode

qrc = qrcode("https:/www.julialang.org")

@svg begin
    tiles = Tiler(250, 250, size(qrc)..., margin=0)
    squares = first.(tiles)
    for x in eachindex(qrc)
        if qrc[x]
            box(squares[x], tiles.tilewidth, tiles.tileheight, :fill)
        end
    end
end 250 250 "/tmp/qrcode"

I like the idea of pushing the graphics until the QR code is only just readable. This just about works:

qrc = qrcode("https:/www.julialang.org")

@svg begin
    tiles = Tiler(250, 250, size(qrc)..., margin=0)
    setline(0.5)
    squares = first.(tiles)
    @layer begin
        for (n, pt) in enumerate(box(O, 140, 140, vertices=true))
            sethue([Luxor.julia_purple, Luxor.julia_blue,  Luxor.julia_red,  Luxor.julia_green][n])
            circle(pt, 50, :fill)
        end
    end
    for x in eachindex(qrc)
        if qrc[x]
            sethue("black")
            box(squares[x], tiles.tilewidth, tiles.tileheight, :fill)
        end
    end
end 250 250 "/tmp/qrcode"

but I’m testing it on a screen - it might not be so reliable when printed out.

Nice package!

8 Likes

It would also be great to have QR code objects (maybe we should introduce a type) be displayed nicely in the REPL. Something along the lines of

julia> using QRCode, UnicodePlots

julia> function plotqr(qr)
           idxs = findall(qr)
           x = getindex.(idxs, 1)
           y = getindex.(idxs, 2)
           scatterplot(x,y, grid=false, border=:none, labels=false, color=:white)
       end
plotqr (generic function with 1 method)

julia> plotqr(qrcode("Display this QR code in the REPL!"))
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⠄⠄⠄⠄⠄⠄⠄⠀⠄⠄⠀⠀⠀⠄⠄⠀⠄⠀⠀⠀⠄⠀⠀⠄⠀⠄⠄⠀⠀⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⡃⠀⡂⡂⡂⠀⡃⠀⡁⠀⠃⠁⠃⡁⡁⡀⠀⡀⡀⡂⠃⠃⡃⡁⠃⠂⡀⡁⡁⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⡅⡀⡁⡁⡁⡀⡅⠀⡀⡁⠁⡀⠄⠀⡄⠀⠅⡄⠁⡅⡅⠁⡁⠁⡅⠅⠄⠁⠀⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⠄⠀⠀⠀⠀⠀⠄⠄⠀⠂⠀⠄⠀⠂⠀⠄⠆⠆⠆⠀⠆⠄⠄⠄⠆⠀⠀⠆⠄⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⡃⠂⠁⡃⡁⠁⠂⡀⡃⠁⡂⠁⡀⠁⠁⠂⠂⠀⠀⡃⡀⠂⡁⠀⠀⡃⡁⠂⡃⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⠅⡄⡄⠁⠀⡀⡁⡀⡁⠅⡄⡄⠀⠀⡀⠄⠀⠅⠄⠁⠅⡅⠀⠁⡅⡁⠀⡅⡀⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⠀⠄⠂⠄⠆⠀⠄⠂⠂⠄⠀⠂⠀⠂⠄⠆⠄⠆⠀⠀⠂⠆⠂⠀⠄⠂⠀⠀⠄⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⡃⡃⠂⡁⡂⠁⠂⠂⠁⡀⡃⡂⠀⠀⠁⠃⠃⠀⠂⡃⠂⡂⠃⠁⠀⠂⠁⠀⠂⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⡁⡁⡀⡁⡀⡀⡁⠁⡄⠅⡄⠁⡅⠁⡄⠁⡅⠁⡄⠅⡄⠁⡁⡁⡀⡁⡁⡀⡁⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⠆⠀⠄⠄⠄⠀⠆⠀⠂⠀⠀⠂⠂⠂⠂⠀⠂⠀⠄⠀⠄⠀⠆⠀⠄⠄⠄⠀⠆⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⡃⠀⠃⠃⠃⠀⡃⠀⠃⡂⠀⡁⡂⠃⡁⠃⡁⠂⠀⠀⡃⠀⡃⠀⠃⠃⠃⠀⡃⠀⠀⠀⠀⠀⠀
     ⠀⠀⠀⠀⠀⠁⠁⠁⠁⠁⠁⠁⠀⠁⠁⠁⠀⠀⠀⠀⠁⠀⠀⠁⠀⠀⠀⠁⠁⠁⠁⠁⠁⠁⠀⠀⠀⠀⠀⠀

Obviously this needs more fine tuning, like larger box-like points, but you get the idea.

2 Likes

Let’s kindly ping @Evizero, who might know how to tweak UnicodePlots to get some nice visualization here.

Fun idea. The main thing to aim for in UnicodePlots would be to get some solid contrasting shapes without big gaps between the characters. The only one of these four I can never capture is the tidiest one, with 1 pixel gaps between the squares. The other ones can all get captured, after a few tries… (The quality of the screen and camera would affect recognition too…?)

how about

julia> using QRCode, Colors, ImageInTerminal

julia> qrc = qrcode("https:/www.julialang.org");

julia> Gray.(1 .- qrc)

7 Likes

I think this looks great! Why are there stripes in the background?

Also, this doesn’t work for me on windows, but perhaps this is due to my console (I’m using ConEmu through cmder)?

The terminal must support at least 256 colors in order for ImageInTerminal to work.

The stripes are an artifact of how the package works. Each character effectively draws two pixel by setting background and foreground color and writing the block character (so top block is foreground, bottom block is background). Long story short, turns out foreground white is not displayed the same as background white by my terminal at least

1 Like

I had line-spacing issues with ImageInTerminal, but this works for me:

using Crayons, Crayons.Box

function blockplot(io::IO, x::AbstractArray{Bool})
    blocks = ("  ", NEGATIVE("  "))
    for i in 1:size(x,1)
        for j in 1:size(x,2)
            print(io, blocks[x[i,j]+1])
        end
        println(io)
    end
end

blockplot(x::AbstractArray{Bool}) = blockplot(stdout, x)

using QRCode

blockplot(qrcode("Hello, World!"))

Edit: The dependency on Crayons can be eliminated by changing the definition of blocks to

    blocks = ("  ", "\e[7m  \e[27m")

where the weird-looking string was created using string(NEGATIVE(" ")).

2 Likes

Glad you like it!
Funnily enough, a few weeks ago I designed a #betterposter LaTeX template for my University.

I really like the idea to have macros @svg or @png to export images, I’ve never written macros before but that sounds like a perfect application. Do you mind if I use some of your code, maybe?

1 Like

I’m preparing a poster for a conference next week, and you got me sold on this #betterposter thing @cormullion. I’ll be linking to one of my Julia packages on github, but I’d love to shove Julia in there if I can. But unlike you I’d rather play it a bit more safe and have the julia colors a bit washed out. So I added

setopacity(0.75)

which resulted in:
screenshot_2019-07-29_15%3A46%3A13

Hope that’s not “illigal” or something :grimacing:

2 Likes

:slight_smile: better watch Professor Johnson’s Keynote first in case I’m doing it wrong… You’re welcome to pan for gold in my code… :smile:

1 Like

I love UnicodePlots! I don’t think any QR code could read them though, so that’s probably not gonna work out. ImageInTerminal looks better, but it seems it’s very terminal-dependent!

This is amazing!!

Definitely watching that soon, thanks :slight_smile:

Looks great to me! Nothing illegal going on!

This is exactly what I hoped would happen when I decided to expose the BitArray instead of an image, I wanted people to use it in creative ways. Thanks guys! It was totally worth it ^^

2 Likes

If you wanted to, you could return an Array{Gray{Bool},2} (from Colors.jl) which would behave as an array with respect to functions like getindex but like an image for operations like show and save. Best of both worlds, somehow.

Interesting! However I might lose the ability to draw QR code in different colors, or do something more unexpected maybe. It’s very simple to transform to Array of Gray (Gray.(.! qrcode("hello"))), but the opposite is more complicated…

The inverse of that would be .! Bool.(ans), so not really complicated, but I see what you mean.