JuliaImages / QRCoders.jl

Creating QR Codes within Julia
https://juliaimages.org/QRCoders.jl/
MIT License
67 stars 11 forks source link

Release new version of QRCoders #21

Closed RexWzh closed 1 year ago

RexWzh commented 1 year ago

I think this repo is ready to be registered. After that, it might be more convenient for QRDecoders.jl to include this package.

Compare with QRCode.jl

using QRCoders, BenchmarkTools, Test
import QRCode.qrcode as orgqrcode
mat2 = qrcode("Hello world!"; compact=true);
mat1 = orgqrcode("Hello world!"; compact=true);
# note that the resulting matrices are not always the same
# since the original mask-rule is rectified
@test mat1 == mat2 

# Byte mode
@btime qrcode("Hello world!"; compact=true);
@btime orgqrcode("Hello world!"; compact=true);

# Numeric mode
@btime qrcode("0123456789" ^ 30; compact=true);
@btime orgqrcode("0123456789" ^ 30; compact=true);

# Alphanumeric mode
@btime qrcode("HELLO WORLD" ^ 30; compact=true); 
@btime orgqrcode("HELLO WORLD" ^ 30; compact=true); 

# max version
@btime qrcode("HELLO WORLD" ^ 190; compact=true); 
@btime orgqrcode("HELLO WORLD" ^ 190; compact=true); 
QRCoders.jl mode input string cost
Byte "Hello world!" 406.058 μs (14194 allocations: 746.17 KiB)
Numeric "0123456789" ^ 30 3.098 ms (100030 allocations: 5.09 MiB)
Alphanumeric "HELLO WORLD" ^ 30 5.094 ms (160384 allocations: 8.23 MiB)
Alphanumeric "HELLO WORLD" ^ 190(max input) 33.779 ms (922529 allocations: 46.93 MiB)
QRCode.jl mode input string cost
Byte "Hello world!" 4.011 ms (175888 allocations: 8.43 MiB)
Numeric "0123456789" ^ 30 25.891 ms (1043655 allocations: 50.04 MiB)
Alphanumeric "HELLO WORLD" ^ 30 38.452 ms (1567245 allocations: 75.29 MiB)
Alphanumeric "HELLO WORLD" ^ 190(max input) 304.884 ms (12002957 allocations: 577.33 MiB)

I'll try to register the package here. Thanks for the advice during the period and for this opportunity to get involved in the project.! @johnnychen94 :)

codecov[bot] commented 1 year ago

Codecov Report

Base: 99.70% // Head: 100.00% // Increases project coverage by +0.29% :tada:

Coverage data is based on head (98261cb) compared to base (f02ba74). Patch coverage: 100.00% of modified lines in pull request are covered.

Additional details and impacted files ```diff @@ Coverage Diff @@ ## master #21 +/- ## =========================================== + Coverage 99.70% 100.00% +0.29% =========================================== Files 4 5 +1 Lines 337 358 +21 =========================================== + Hits 336 358 +22 + Misses 1 0 -1 ``` | [Impacted Files](https://codecov.io/gh/JuliaImages/QRCoders.jl/pull/21?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaImages) | Coverage Δ | | |---|---|---| | [src/QRCoders.jl](https://codecov.io/gh/JuliaImages/QRCoders.jl/pull/21/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaImages#diff-c3JjL1FSQ29kZXJzLmps) | `100.00% <100.00%> (ø)` | | | [src/encode.jl](https://codecov.io/gh/JuliaImages/QRCoders.jl/pull/21/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaImages#diff-c3JjL2VuY29kZS5qbA==) | `100.00% <100.00%> (ø)` | | | [src/errorcorrection.jl](https://codecov.io/gh/JuliaImages/QRCoders.jl/pull/21/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaImages#diff-c3JjL2Vycm9yY29ycmVjdGlvbi5qbA==) | `100.00% <100.00%> (+1.38%)` | :arrow_up: | | [src/matrix.jl](https://codecov.io/gh/JuliaImages/QRCoders.jl/pull/21/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaImages#diff-c3JjL21hdHJpeC5qbA==) | `100.00% <100.00%> (ø)` | | | [src/tables.jl](https://codecov.io/gh/JuliaImages/QRCoders.jl/pull/21/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaImages#diff-c3JjL3RhYmxlcy5qbA==) | `100.00% <100.00%> (ø)` | | Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaImages). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=JuliaImages)

:umbrella: View full report at Codecov.
:loudspeaker: Do you have feedback about the report comment? Let us know in this issue.

johnnychen94 commented 1 year ago

The result looks great! I'm pretty busy here so can't do a review right now.

But feel free to merge if you're confident. I can do an after-merge review when I find some time and it's always possible to fix potential issues (if there were) afterward.

Just want to point out (without digging into the details) that 14194 allocations: 746.17 KiB often indicates some performance tweak room by reducing memory allocations. Maybe there are some loops that you can pre-allocate a buffer array and reuse it? Type instability can also cause these many allocations.

RexWzh commented 1 year ago

Just want to point out (without digging into the details) that 14194 allocations: 746.17 KiB often indicates some performance tweak room by reducing memory allocations. Maybe there are some loops that you can pre-allocate a buffer array and reuse it?

Good advice!! Here is the benchmark result after rewriting the function penalty. (use @view to index sub-matrices and edit other details)

revised version

mode input string cost
Byte "Hello world!" 142.909 μs (370 allocations: 32.23 KiB)
Numeric "0123456789" ^ 30 1.454 ms (1982 allocations: 291.95 KiB)
Alphanumeric "HELLO WORLD" ^ 30 2.495 ms (3200 allocations: 562.52 KiB)
max version "HELLO WORLD" ^ 190 14.310 ms (17825 allocations: 3.01 MiB)

steps in qrcode

message = "Hello world!"
eclevel, mode = Medium(), getmode(message)
version = getversion(message, mode, eclevel)
data = @btime encodemessage(message, mode, eclevel, version) # test 1
matrix = emptymatrix(version)
masks = @btime makemasks(matrix) # test 2
matrix = placedata!(matrix, data)
addversion!(matrix, version) # fill in version bits
maskedmats = @btime [addformat!(xor.(matrix, mat), i-1, eclevel) for (i, mat) in enumerate(masks)]; # test 3
mask = @btime argmin(penalty.(maskedmats)) - 1 # test 4
matrix = maskedmats[mask + 1]
Benchmark results of main steps: function cost
encodemessage 13.925 μs (238 allocations: 22.75 KiB)
8 * makemask 5.445 μs (18 allocations: 1.38 KiB)
8 * addformat! 5.631 μs (51 allocations: 2.64 KiB)
8 * penalty 106.084 μs (52 allocations: 3.94 KiB)

Some notes:

# Condition 3: specific patterns in rows or columns
patt1 = BitArray([1, 0, 1, 1, 1, 0, 1, 0, 0, 0 ,0])
patt2 = BitArray([0, 0, 0 ,0, 1, 0, 1, 1, 1, 0, 1])
function check(i, j)
    hline = @view(matrix[i, j:j + 10])
    vline = @view(matrix[j:j + 10, i])
    return (hline == patt1 || hline == patt2) + (vline == patt1 || vline == patt2)
end
p3 = 40 * sum(check(i, j) for i in 1:n for j in 1:(n-10))