解决 QR 拼图的算法

Algorithm to solve QR puzzle

我正在构建算法以根据相同大小的块数自动解决/重建 QR 码。

我的做法:

  1. 获得 3 个边方块之一:位置方块
  2. 使用 ImageMagic 以某种方式获取外黑色边框的宽度

给定的示例是 14px x 14px(更新:它可能是 13px x 13px)

  1. 设置为常量:BLOCK_SIZE
  2. 开始循环匹配有效的图块和已编译的 QR 码块

有效是:不匹配且不是 3 个角块中的任何一个(黑色大方块)

4.1。呈现已编译的 qr 块和 1 个随机有效图块的所有可能组合,直到找到匹配

4.2 如果找到匹配(tile 可以放在已经 compiled/combined 二维码块的一侧)然后记录 match

4.3匹配条件:

没有宽度和高度符合此等式的黑色区域:

AREA_HEIGHT % BLOCK_SIZE  == 0  && AREA_WIDTH % BLOCK_SIZE  == 0

现在出现了一些实施并发症:

  1. 如何实现匹配一个瓦片到一个瓦片的算法(或者已经 compiled/combined QR 码块)?

我觉得ImageMagic可以解决。 目标是获取原始二维码

Example QR tiles in ZIP

更新

正如我所见,解决方案缩小到让 ImageMagick 扫描并确定是否可以在不违反 QR 码规则的情况下将两个图块放在一起。算法应尝试并排放置 2 个图块并扫描边界区域。诀窍是测量连接瓷砖的黑色 lines/blocks 的宽度和高度。怎么做?

更新2

在某些情况下,使用 2 列宽度为 1px 的图块匹配进行比较是不够的。例如,两种算法都会将这 2 个图块连接成:

有效

无效

如您在 INVALID 中所见,第二个图块(右边的那个)的一部分已被删除,如红色箭头所示,但由于比较是由 1px - 算法完成的不关心这种情况。 Hance 无效二维码。

更新3

http://dropcanvas.com/6bl6v

存档包含:

如果您的算法将加入 src1.pngsrc2-invalid.png,它会失败。这是关于连接的瓷砖最终是否会产生有效的 QR。我希望你明白我的意思。

更新4

我会在测试并得到正确的解决方案后接受答案。可能需要一些时间....

更新 5:测试

Mark 的解决方案似乎一切都是正确的。

我对你的解释一无所知,但对于匹配图块来说,比较沿公共边缘的像素就足够了。这是因为拆分是跨 QR 单元格进行的,而不是在它们之间进行的。

您可以逐行填充数组,每次尝试所有未使用的块,直到找到匹配项。实际上可能会巧合地出现多个匹配项,因此建议在找到匹配项后继续搜索。这将导致递归实现。

角落和它们的直接邻居更容易手动放置。仔细观察一下,您也可以找到具有时序模式的块。

如果使用单个图块 PNG 中的所有可用信息,这很容易:

QR reader 表示:http://www.oracle.com/javaone/index.html

我花了不到 3 分钟的时间想出了解决方案——主要是因为我必须同时在 phone 上用一只手键入相应的命令....

您是如何生产瓷砖的?

更新

现在 @RCola 终于回答了我的问题 ("How did you produce the tiles?"),我将揭示我是如何快速想出正确的二维码图像的。

我没有按照他的要求使用算法。我用了"side-channel"攻击,by-passing这样的算法有什么需要考虑的。

这个 side-channel 使用了一个 "information leak",它包含在@RCola 的 ZIP 文件中提供的图块图像中。当 运行 执行一个简单的 identify 命令时,此信息会被泄露。这里我 运行 它不是作为 'simple' 命令,而是——为了使泄露的信息更明显——作为强制特定输出格式的命令:

identify -format "%f :  %g\n" {1,2,3,4,5}*.png

  10297-13918-3702.png :  400x400+160+0
  11976-7751-26756.png :  400x400+80+0
  13789-10513-4721.png :  400x400+80+160
 13858-18007-13070.png :  400x400+240+0
  15816-4564-31665.png :  400x400+160+240
  16369-21469-8252.png :  400x400+320+80
  17636-24599-1877.png :  400x400+80+240
 18056-16294-30425.png :  400x400+80+320
 20021-11440-20836.png :  400x400+240+320
 20056-20936-29071.png :  400x400+0+240
  21875-14159-1067.png :  400x400+320+240
 22161-18187-20222.png :  400x400+240+80
  22806-3380-17484.png :  400x400+0+160
  24426-18830-5627.png :  400x400+320+160
 24658-20374-23042.png :  400x400+0+0
 26507-21853-11958.png :  400x400+240+240
 27206-10104-18226.png :  400x400+0+320
 28824-13023-24184.png :  400x400+160+80
 30261-16558-25650.png :  400x400+0+80
   31250-3578-9750.png :  400x400+320+0
  32078-14314-1511.png :  400x400+160+160
     4555-18116-29.png :  400x400+160+320
  5004-10810-17642.png :  400x400+320+320
  5167-27533-24066.png :  400x400+240+160
  5774-30645-16062.png :  400x400+80+80

identify {1,2,3,4,5}*.png 的输出中会包含相同的信息——只是不会跳得那么明显。)

这说明了什么? %g percent-escape 打印 geometry 有关与 PNG tile 关联的 canvas 的信息:

  1. 400x400 是此图块 canvas 的整体大小。很明显,这是代表完整二维码的原始 PNG 的大小。
  2. +X+Y 是在完整 canvas.
  3. 上与此图块关联的 偏移量

使用此信息可以非常容易地直接知道必须将哪个 PNG 图块放置在拼图的哪个位置:

  • +0+0明显要到左上角
  • +0+80 显然刚好低于
  • +80+0 显然就在左上角的右边
  • ...等等,直到
  • +320+320进入右下角

OP 可以通过将 +repage 参数添加到他用来生成图块的 IM 命令来避免 side-channel 信息泄漏(在这种情况下会泄露完整的游戏)。

为了 "repair" 他提供的漏水瓷砖并制作更具挑战性的瓷砖,我这样做了:

 mkdir challenge

 for i in {1,2,3,4,5}*.png; do
     convert $i +repage challenge/$i
 done

更新 2

(作为对在十六进制编辑器的帮助下 "see" 信息失败感到绝望的评论的回答...)

identify 揭示的相同信息也可以在 exiftool 的帮助下获得(尽管以较少的 "accessible" 形式):

exiftool 11976-7751-26756.png  | grep -E '(Virtual Image|Width|Height|Offset)'
 Image Width                     : 80
 Image Height                    : 80
 Image Offset                    : 80, 0 (pixels)
 Virtual Image Width             : 400
 Virtual Image Height            : 400

我试过如下。

  1. 遍历所有 PNG 并提取它们的北边、东边、南边和西边。如果任何 PNG 有两条白边,请告诉用户它是 corner-piece。如果任何 PNG 有一条白边,请告诉用户它是 edge-piece.

  2. 遍历所有边块。将每个转换为 PBM 格式文件并去除 headers 和新行,使其成为一个 80 位长字符串,每个数字为零或一个表示黑色或白色。计算字符串的校验和,然后反转字符串以考虑翻转或旋转的图像,然后重新计算校验和。打印出校验和并通过管道输入 sort,以便匹配的边一起出现。

代码如下:

#!/bin/bash

# Remove any edges generated in previous runs of this script
rm *_[NESW]*png 2> /dev/null
rm *_[NESW]*txt 2> /dev/null

DEBUG=1

# Process all PNGs
for f in *.png; do

   echo "Processing $f ..."

   # Get basename of image
   base=$(basename -s .png "$f")

   # Get width and height - not actually used at the moment
   read w h <<< $(identify -format "%w %h" "$f")
   [ $DEBUG -gt 0 ] && echo "   width:$w"
   [ $DEBUG -gt 0 ] && echo "   height:$h"

   # Extract North edge
   convert "$f" +repage -crop x1+0+0 +repage "${base}_N.png"
   [ $DEBUG -gt 0 ] && echo "   North edge extracted"

   # Extract East edge
   convert "$f" +repage -gravity east -crop 1x+0+0 -rotate 90 +repage "${base}_E.png"
   [ $DEBUG -gt 0 ] && echo "   East edge extracted"

   # Extract South edge
   convert "$f" +repage -gravity south -crop x1+0+0 +repage "${base}_S.png"
   [ $DEBUG -gt 0 ] && echo "   South edge extracted"

   # Extract West edge
   convert "$f" +repage -gravity west -crop 1x+0+0 -rotate 90 +repage "${base}_W.png"
   [ $DEBUG -gt 0 ] && echo "   West edge extracted"

   # Test if corner or edge piece
   n=0
   for edge in N E S W; do
      name="${base}_${edge}.png"
      min=$(identify -format "%[min]" "$name")
      if [ $min -gt 0 ]; then
         ((n++))
         e=$name
      fi
   done
   [ $n -eq 1 ] && echo "   $e is edge-piece"
   [ $n -eq 2 ] && echo "   $name is corner-piece"
done

EDITED FROM HERE ---
# Now convert all edges to text, forwards and backwards to allow rotation
for f in *_[NESW].png; do
   base=$(basename -s png "$f")
   # Convert to PBM format, remove 2 header lines and make into one line string
   str=$(convert "$f" -compress none pbm: | sed "1,2d" | tr -d "\n ")
   echo "$str:$f"
   str=$(rev <<< $str)
   echo "$str:$f (flipped)"
done | sort

部分输出(保存space)

Processing 4555-18116-29.png ...
   width:80
   height:80
   North edge extracted
   East edge extracted
   South edge extracted
   West edge extracted
   4555-18116-29_S.png is edge-piece
Processing 5004-10810-17642.png ...
   width:80
   height:80
   North edge extracted
   East edge extracted
   South edge extracted
   West edge extracted
   5004-10810-17642_W.png is corner-piece
Processing 5167-27533-24066.png ...
   width:80
   height:80
   North edge extracted
   East edge extracted
   South edge extracted
   West edge extracted
Processing 5774-30645-16062.png ...
   width:80
   height:80
   North edge extracted
   East edge extracted
   South edge extracted
   West edge extracted
0a7bb6f610c0f1a6da4794ea7ae00f00:10297-13918-3702_W.png (flipped)
0a7bb6f610c0f1a6da4794ea7ae00f00:11976-7751-26756_E.png (flipped)   <-- this image is identical to the one above as the md5 checksum on the left is the same
0ce419e072c7ea5d14e3525d4afe150e:11976-7751-26756_W.png (flipped)
0ce419e072c7ea5d14e3525d4afe150e:13858-18007-13070_E.png (flipped)  <-- this image is identical to the one above as the md5 checksum on the left is the same
0ce419e072c7ea5d14e3525d4afe150e:20056-20936-29071_S.png
0ce419e072c7ea5d14e3525d4afe150e:24658-20374-23042_E.png (flipped)
0ce419e072c7ea5d14e3525d4afe150e:24658-20374-23042_S.png
0ce419e072c7ea5d14e3525d4afe150e:27206-10104-18226_N.png
0ce419e072c7ea5d14e3525d4afe150e:30261-16558-25650_N.png

备注

请注意,这假设您没有在 @YvesDaoust 识别的 "fields" QR 码之间完全平铺图像。

此外,代码的某些部分是多余的,因为我在开发代码的同时开发算法 - 我认为 -rotates+repages 是不必要的,事情可以优化更多,但没有表明问题需要速度。也可以在单个 IM 命令中提取北、东、南和西边缘。我获得的宽度和高度未使用,因此可以从代码中删除。

此外,md5 校验和并不是真正必要的,sort 会将 0001110001010110 放在另一个相同的字符串旁边,而无需对它们进行校验和。

应要求,我正在上传完整的解决方案。我删除了 md5 校验和的东西,因为它是不必要的。您需要向右滚动才能看到以下文件名:

00000000000000000000000000000000000000000000000000000000000000000000000000000000:10297-13918-3702_N.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:10297-13918-3702_N.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:11976-7751-26756_N.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:11976-7751-26756_N.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:13858-18007-13070_N.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:13858-18007-13070_N.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:16369-21469-8252_E.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:16369-21469-8252_E.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:18056-16294-30425_S.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:18056-16294-30425_S.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:20021-11440-20836_S.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:20021-11440-20836_S.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:20056-20936-29071_W.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:20056-20936-29071_W.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:21875-14159-1067_E.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:21875-14159-1067_E.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:22806-3380-17484_W.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:22806-3380-17484_W.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:24426-18830-5627_E.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:24426-18830-5627_E.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:24658-20374-23042_N.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:24658-20374-23042_N.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:24658-20374-23042_W.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:24658-20374-23042_W.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:27206-10104-18226_S.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:27206-10104-18226_S.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:27206-10104-18226_W.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:27206-10104-18226_W.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:30261-16558-25650_W.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:30261-16558-25650_W.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:31250-3578-9750_E.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:31250-3578-9750_E.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:31250-3578-9750_N.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:31250-3578-9750_N.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:4555-18116-29_S.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:4555-18116-29_S.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:5004-10810-17642_E.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:5004-10810-17642_E.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000000000000000000:5004-10810-17642_S.png
00000000000000000000000000000000000000000000000000000000000000000000000000000000:5004-10810-17642_S.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000111111111111100:21875-14159-1067_S.png (flipped)
00000000000000000000000000000000000000000000000000000000000000000111111111111100:5004-10810-17642_N.png (flipped)
00000000000000000000000000000000000000000000000000000000000001111111111111111111:28824-13023-24184_W.png (flipped)
00000000000000000000000000000000000000000000000000000000000001111111111111111111:5774-30645-16062_E.png (flipped)
00000000000000000000000000000000000000000000000000000000000011111111111111111111:13789-10513-4721_E.png
00000000000000000000000000000000000000000000000000000000000011111111111111111111:32078-14314-1511_W.png
00000000000000000000000000000000000000000000000000000001111111111111000000000000:11976-7751-26756_W.png
00000000000000000000000000000000000000000000000000000001111111111111000000000000:13858-18007-13070_E.png
00000000000000000000000000000000000000000000000000000001111111111111000000000000:20056-20936-29071_S.png (flipped)
00000000000000000000000000000000000000000000000000000001111111111111000000000000:24658-20374-23042_E.png
00000000000000000000000000000000000000000000000000000001111111111111000000000000:24658-20374-23042_S.png (flipped)
00000000000000000000000000000000000000000000000000000001111111111111000000000000:27206-10104-18226_N.png (flipped)
00000000000000000000000000000000000000000000000000000001111111111111000000000000:30261-16558-25650_N.png (flipped)
00000000000000000000000000000000000000000000000000000001111111111111000000000000:31250-3578-9750_W.png
00000000000000000000000000000000000000000000000000000011111111111110000000000000:16369-21469-8252_N.png
00000000000000000000000000000000000000000000000000000011111111111110000000000000:18056-16294-30425_W.png (flipped)
00000000000000000000000000000000000000000000000000000011111111111110000000000000:27206-10104-18226_E.png (flipped)
00000000000000000000000000000000000000000000000000000011111111111110000000000000:31250-3578-9750_S.png
00000000000000000000000000000000000000000000001111111111111111111111111111111111:10297-13918-3702_S.png
00000000000000000000000000000000000000000000001111111111111111111111111111111111:28824-13023-24184_N.png
00000000000000000000000000000000000001111111111111111111111111000000000000000000:20021-11440-20836_N.png (flipped)
00000000000000000000000000000000000001111111111111111111111111000000000000000000:26507-21853-11958_S.png (flipped)
00000000000000000000000000000000000001111111111111111111111111111111111111100000:15816-4564-31665_W.png
00000000000000000000000000000000000001111111111111111111111111111111111111100000:17636-24599-1877_E.png
00000000000000000000000000000000000001111111111111111111111111111111111111111111:21875-14159-1067_W.png
00000000000000000000000000000000000001111111111111111111111111111111111111111111:26507-21853-11958_E.png
00000000000000000000000000000000000011111111111111111111111110000000000000000000:13789-10513-4721_S.png
00000000000000000000000000000000000011111111111111111111111110000000000000000000:17636-24599-1877_N.png
00000000000000000000000000000001111111111111000000000000000000000000001111111111:22161-18187-20222_W.png
00000000000000000000000000000001111111111111000000000000000000000000001111111111:28824-13023-24184_E.png
00000000000000000000000000000011111111111111111111111111111111111111100000000000:15816-4564-31665_E.png (flipped)
00000000000000000000000000000011111111111111111111111111111111111111100000000000:26507-21853-11958_W.png (flipped)
00000000000000000000000000000111111111111111111111111111111111111111000000000000:20056-20936-29071_N.png (flipped)
00000000000000000000000000000111111111111111111111111111111111111111000000000000:22806-3380-17484_S.png (flipped)
00000000000000000000000000001111111111111111111111111111111111111110000000000000:18056-16294-30425_E.png (flipped)
00000000000000000000000000001111111111111111111111111111111111111110000000000000:4555-18116-29_W.png (flipped)
00000000000000000000000000111111111111100000000000001111111111111000000000000011:16369-21469-8252_S.png (flipped)
00000000000000000000000000111111111111100000000000001111111111111000000000000011:24426-18830-5627_N.png (flipped)
00000000000000000000000001111111111111111111111111111111111111110000000000000000:10297-13918-3702_W.png (flipped)
00000000000000000000000001111111111111111111111111111111111111110000000000000000:11976-7751-26756_E.png (flipped)
00000000000000000000000111111111111100000000000001111111111111111111111111111111:13789-10513-4721_N.png
00000000000000000000000111111111111100000000000001111111111111111111111111111111:5774-30645-16062_S.png
00000000000000000000111111111111100000000000000000000000000111111111111100000000:15816-4564-31665_N.png
00000000000000000000111111111111100000000000000000000000000111111111111100000000:32078-14314-1511_S.png
00000000000000000000111111111111100000000000001111111111111111111111111100000000:13789-10513-4721_W.png (flipped)
00000000000000000000111111111111100000000000001111111111111111111111111100000000:22806-3380-17484_E.png (flipped)
00000000000000000001111111111111111111111111000000000000000000000000000000000000:13789-10513-4721_S.png (flipped)
00000000000000000001111111111111111111111111000000000000000000000000000000000000:17636-24599-1877_N.png (flipped)
00000000000000000011111111111100000000000000000000000000111111111111111111111111:22161-18187-20222_S.png
00000000000000000011111111111100000000000000000000000000111111111111111111111111:5167-27533-24066_N.png
00000000000000000011111111111111111111111110000000000000000000000000000000000000:20021-11440-20836_N.png
00000000000000000011111111111111111111111110000000000000000000000000000000000000:26507-21853-11958_S.png
00000000000000001111111111111111111111111111111111111110000000000000000000000000:10297-13918-3702_W.png
00000000000000001111111111111111111111111111111111111110000000000000000000000000:11976-7751-26756_E.png
00000000000001111111111111000000000000000000000000000000000000000000000000000000:16369-21469-8252_N.png (flipped)
00000000000001111111111111000000000000000000000000000000000000000000000000000000:18056-16294-30425_W.png
00000000000001111111111111000000000000000000000000000000000000000000000000000000:27206-10104-18226_E.png
00000000000001111111111111000000000000000000000000000000000000000000000000000000:31250-3578-9750_S.png (flipped)
00000000000001111111111111000000000000011111111111110000000000000111111111111111:21875-14159-1067_N.png (flipped)
00000000000001111111111111000000000000011111111111110000000000000111111111111111:24426-18830-5627_S.png (flipped)
00000000000001111111111111000000000000011111111111111111111111111111111111111100:20021-11440-20836_W.png
00000000000001111111111111000000000000011111111111111111111111111111111111111100:4555-18116-29_E.png
00000000000001111111111111111111111111111111111111110000000000000000000000000000:18056-16294-30425_E.png
00000000000001111111111111111111111111111111111111110000000000000000000000000000:4555-18116-29_W.png
00000000000001111111111111111111111111111111111111111111111111111111111111111100:20021-11440-20836_E.png
00000000000001111111111111111111111111111111111111111111111111111111111111111100:5004-10810-17642_W.png
00000000000011111111111110000000000000000000000000000000000000000000000000000000:11976-7751-26756_W.png (flipped)
00000000000011111111111110000000000000000000000000000000000000000000000000000000:13858-18007-13070_E.png (flipped)
00000000000011111111111110000000000000000000000000000000000000000000000000000000:20056-20936-29071_S.png
00000000000011111111111110000000000000000000000000000000000000000000000000000000:24658-20374-23042_E.png (flipped)
00000000000011111111111110000000000000000000000000000000000000000000000000000000:24658-20374-23042_S.png
00000000000011111111111110000000000000000000000000000000000000000000000000000000:27206-10104-18226_N.png
00000000000011111111111110000000000000000000000000000000000000000000000000000000:30261-16558-25650_N.png
00000000000011111111111110000000000000000000000000000000000000000000000000000000:31250-3578-9750_W.png (flipped)
00000000000011111111111110000000000000111111111111111111111111111111111111111000:22806-3380-17484_N.png
00000000000011111111111110000000000000111111111111111111111111111111111111111000:30261-16558-25650_S.png
00000000000011111111111111111111111111000000000000011111111111111111111111111111:10297-13918-3702_E.png (flipped)
00000000000011111111111111111111111111000000000000011111111111111111111111111111:13858-18007-13070_W.png (flipped)
00000000000011111111111111111111111111111111111111100000000000000000000000000000:20056-20936-29071_N.png
00000000000011111111111111111111111111111111111111100000000000000000000000000000:22806-3380-17484_S.png
00000000000111111111111100000000000000000000000000000000000000111111111111100000:17636-24599-1877_W.png
00000000000111111111111100000000000000000000000000000000000000111111111111100000:20056-20936-29071_E.png
00000000000111111111111100000000000001111111111111000000000000111111111111111111:13858-18007-13070_S.png (flipped)
00000000000111111111111100000000000001111111111111000000000000111111111111111111:22161-18187-20222_N.png (flipped)
00000000000111111111111111111111111111111111111111000000000000000000000000000000:15816-4564-31665_E.png
00000000000111111111111111111111111111111111111111000000000000000000000000000000:26507-21853-11958_W.png
00000000001111111111111000000000000000000000000000000000000001111111111111000000:30261-16558-25650_E.png (flipped)
00000000001111111111111000000000000000000000000000000000000001111111111111000000:5774-30645-16062_W.png (flipped)
00000000001111111111111000000000000000000000000001111111111111111111111111000000:17636-24599-1877_S.png
00000000001111111111111000000000000000000000000001111111111111111111111111000000:18056-16294-30425_N.png
00000000001111111111111000000000000011111111111110000000000001111111111111000000:11976-7751-26756_S.png
00000000001111111111111000000000000011111111111110000000000001111111111111000000:5774-30645-16062_N.png
00000000001111111111111000000000000011111111111111111111111110000000000000111111:16369-21469-8252_W.png (flipped)
00000000001111111111111000000000000011111111111111111111111110000000000000111111:22161-18187-20222_E.png (flipped)
00000000111111111111100000000000000000000000000111111111111100000000000000000000:15816-4564-31665_N.png (flipped)
00000000111111111111100000000000000000000000000111111111111100000000000000000000:32078-14314-1511_S.png (flipped)
00000000111111111111100000000000000000000000000111111111111111111111111110000000:15816-4564-31665_S.png (flipped)
00000000111111111111100000000000000000000000000111111111111111111111111110000000:4555-18116-29_N.png (flipped)
00000000111111111111100000000000001111111111111000000000000011111111111110000000:32078-14314-1511_E.png
00000000111111111111100000000000001111111111111000000000000011111111111110000000:5167-27533-24066_W.png
00000000111111111111111111111111110000000000000000000000000000000000000001111111:28824-13023-24184_S.png (flipped)
00000000111111111111111111111111110000000000000000000000000000000000000001111111:32078-14314-1511_N.png (flipped)
00000000111111111111111111111111110000000000000111111111111100000000000000000000:13789-10513-4721_W.png
00000000111111111111111111111111110000000000000111111111111100000000000000000000:22806-3380-17484_E.png
00000001111111111111000000000000011111111111110000000000000111111111111100000000:32078-14314-1511_E.png (flipped)
00000001111111111111000000000000011111111111110000000000000111111111111100000000:5167-27533-24066_W.png (flipped)
00000001111111111111111111111111100000000000000000000000000111111111111100000000:15816-4564-31665_S.png
00000001111111111111111111111111100000000000000000000000000111111111111100000000:4555-18116-29_N.png
00000011111111111110000000000000000000000000000000000000011111111111110000000000:30261-16558-25650_E.png
00000011111111111110000000000000000000000000000000000000011111111111110000000000:5774-30645-16062_W.png
00000011111111111110000000000001111111111111000000000000011111111111110000000000:11976-7751-26756_S.png (flipped)
00000011111111111110000000000001111111111111000000000000011111111111110000000000:5774-30645-16062_N.png (flipped)
00000011111111111111111111111110000000000000000000000000011111111111110000000000:17636-24599-1877_S.png (flipped)
00000011111111111111111111111110000000000000000000000000011111111111110000000000:18056-16294-30425_N.png (flipped)
00000111111111111100000000000000000000000000000000000000111111111111100000000000:17636-24599-1877_W.png (flipped)
00000111111111111100000000000000000000000000000000000000111111111111100000000000:20056-20936-29071_E.png (flipped)
00000111111111111111111111111100000000000001111111111111111111111111111111111111:26507-21853-11958_N.png
00000111111111111111111111111100000000000001111111111111111111111111111111111111:5167-27533-24066_S.png
00000111111111111111111111111111111111111110000000000000000000000000000000000000:15816-4564-31665_W.png (flipped)
00000111111111111111111111111111111111111110000000000000000000000000000000000000:17636-24599-1877_E.png (flipped)
00011111111111111111111111111111111111111100000000000001111111111111000000000000:22806-3380-17484_N.png (flipped)
00011111111111111111111111111111111111111100000000000001111111111111000000000000:30261-16558-25650_S.png (flipped)
00111111111111100000000000000000000000000000000000000000000000000000000000000000:21875-14159-1067_S.png
00111111111111100000000000000000000000000000000000000000000000000000000000000000:5004-10810-17642_N.png
00111111111111111111111111111111111111111000000000000011111111111110000000000000:20021-11440-20836_W.png (flipped)
00111111111111111111111111111111111111111000000000000011111111111110000000000000:4555-18116-29_E.png (flipped)
00111111111111111111111111111111111111111111111111111111111111111110000000000000:20021-11440-20836_E.png (flipped)
00111111111111111111111111111111111111111111111111111111111111111110000000000000:5004-10810-17642_W.png (flipped)
11000000000000011111111111110000000000000111111111111100000000000000000000000000:16369-21469-8252_S.png
11000000000000011111111111110000000000000111111111111100000000000000000000000000:24426-18830-5627_N.png
11111100000000000001111111111111111111111111000000000000011111111111110000000000:16369-21469-8252_W.png
11111100000000000001111111111111111111111111000000000000011111111111110000000000:22161-18187-20222_E.png
11111110000000000000000000000000000000000000001111111111111111111111111100000000:28824-13023-24184_S.png
11111110000000000000000000000000000000000000001111111111111111111111111100000000:32078-14314-1511_N.png
11111110000000000000000000000000011111111111111111111111111111111111111111111111:24426-18830-5627_W.png (flipped)
11111110000000000000000000000000011111111111111111111111111111111111111111111111:5167-27533-24066_E.png (flipped)
11111111110000000000000000000000000011111111111110000000000000000000000000000000:22161-18187-20222_W.png (flipped)
11111111110000000000000000000000000011111111111110000000000000000000000000000000:28824-13023-24184_E.png (flipped)
11111111111111100000000000001111111111111000000000000011111111111110000000000000:21875-14159-1067_N.png
11111111111111100000000000001111111111111000000000000011111111111110000000000000:24426-18830-5627_S.png
11111111111111111100000000000011111111111110000000000000111111111111100000000000:13858-18007-13070_S.png
11111111111111111100000000000011111111111110000000000000111111111111100000000000:22161-18187-20222_N.png
11111111111111111110000000000000000000000000000000000000000000000000000000000000:28824-13023-24184_W.png
11111111111111111110000000000000000000000000000000000000000000000000000000000000:5774-30645-16062_E.png
11111111111111111111000000000000000000000000000000000000000000000000000000000000:13789-10513-4721_E.png (flipped)
11111111111111111111000000000000000000000000000000000000000000000000000000000000:32078-14314-1511_W.png (flipped)
11111111111111111111111100000000000000000000000000111111111111000000000000000000:22161-18187-20222_S.png (flipped)
11111111111111111111111100000000000000000000000000111111111111000000000000000000:5167-27533-24066_N.png (flipped)
11111111111111111111111111111000000000000011111111111111111111111111000000000000:10297-13918-3702_E.png
11111111111111111111111111111000000000000011111111111111111111111111000000000000:13858-18007-13070_W.png
11111111111111111111111111111110000000000000111111111111100000000000000000000000:13789-10513-4721_N.png (flipped)
11111111111111111111111111111110000000000000111111111111100000000000000000000000:5774-30645-16062_S.png (flipped)
11111111111111111111111111111111110000000000000000000000000000000000000000000000:10297-13918-3702_S.png (flipped)
11111111111111111111111111111111110000000000000000000000000000000000000000000000:28824-13023-24184_N.png (flipped)
11111111111111111111111111111111111110000000000000111111111111111111111111100000:26507-21853-11958_N.png (flipped)
11111111111111111111111111111111111110000000000000111111111111111111111111100000:5167-27533-24066_S.png (flipped)
11111111111111111111111111111111111111111110000000000000000000000000000000000000:21875-14159-1067_W.png (flipped)
11111111111111111111111111111111111111111110000000000000000000000000000000000000:26507-21853-11958_E.png (flipped)
11111111111111111111111111111111111111111111111000000000000000000000000001111111:24426-18830-5627_W.png
11111111111111111111111111111111111111111111111000000000000000000000000001111111:5167-27533-24066_E.png

这是一个更好的算法,我已经编写了脚本:

  1. 从每个图块中,从每个边缘切掉 1 像素的行或列,并将其输出为 ImageMagick 的特殊 *.text 格式,使用表明它们各自 left/right 的名称或 top/bottom 边原点。

  2. 转换每个这样的 column/row,创建其 *.txtmd5sum 并按字母顺序排序。

  3. 判断哪些MD5和相同

  4. 从相同的 MD5 和中确定适合相应边界的候选。

代码片段

for i in {1,2,3,4,5}*.png; do
  convert ${i}[1x80+0+0]  +repage left---edge-${i/.png/.text}
  convert ${i}[1x80+79+0] +repage right--edge-${i/.png/.text}
  convert ${i}[80x1+0+0]  +repage top----edge-${i/.png/.text}
  convert ${i}[80x1+0+79] +repage bottom-edge-${i/.png/.text}
done

一个这样的文本文件的例子是这样的:

cat right--edge-5167-27533-24066.text

# ImageMagick pixel enumeration: 1,80,255,gray
0,0: (0,0,0)  #000000  gray(0)
0,1: (0,0,0)  #000000  gray(0)
0,2: (0,0,0)  #000000  gray(0)
0,3: (0,0,0)  #000000  gray(0)
0,4: (0,0,0)  #000000  gray(0)
0,5: (0,0,0)  #000000  gray(0)
0,6: (0,0,0)  #000000  gray(0)
0,7: (255,255,255)  #FFFFFF  gray(255)
0,8: (255,255,255)  #FFFFFF  gray(255)
0,9: (255,255,255)  #FFFFFF  gray(255)
0,10: (255,255,255)  #FFFFFF  gray(255)
0,11: (255,255,255)  #FFFFFF  gray(255)
0,12: (255,255,255)  #FFFFFF  gray(255)
0,13: (255,255,255)  #FFFFFF  gray(255)
0,14: (255,255,255)  #FFFFFF  gray(255)
0,15: (255,255,255)  #FFFFFF  gray(255)
0,16: (255,255,255)  #FFFFFF  gray(255)
0,17: (255,255,255)  #FFFFFF  gray(255)
0,18: (255,255,255)  #FFFFFF  gray(255)
0,19: (255,255,255)  #FFFFFF  gray(255)
0,20: (255,255,255)  #FFFFFF  gray(255)
0,21: (255,255,255)  #FFFFFF  gray(255)
0,22: (255,255,255)  #FFFFFF  gray(255)
0,23: (255,255,255)  #FFFFFF  gray(255)
0,24: (255,255,255)  #FFFFFF  gray(255)
0,25: (255,255,255)  #FFFFFF  gray(255)
0,26: (255,255,255)  #FFFFFF  gray(255)
0,27: (255,255,255)  #FFFFFF  gray(255)
0,28: (255,255,255)  #FFFFFF  gray(255)
0,29: (255,255,255)  #FFFFFF  gray(255)
0,30: (255,255,255)  #FFFFFF  gray(255)
0,31: (255,255,255)  #FFFFFF  gray(255)
0,32: (255,255,255)  #FFFFFF  gray(255)
0,33: (0,0,0)  #000000  gray(0)
0,34: (0,0,0)  #000000  gray(0)
0,35: (0,0,0)  #000000  gray(0)
0,36: (0,0,0)  #000000  gray(0)
0,37: (0,0,0)  #000000  gray(0)
0,38: (0,0,0)  #000000  gray(0)
0,39: (0,0,0)  #000000  gray(0)
0,40: (0,0,0)  #000000  gray(0)
0,41: (0,0,0)  #000000  gray(0)
0,42: (0,0,0)  #000000  gray(0)
0,43: (0,0,0)  #000000  gray(0)
0,44: (0,0,0)  #000000  gray(0)
0,45: (0,0,0)  #000000  gray(0)
0,46: (0,0,0)  #000000  gray(0)
0,47: (0,0,0)  #000000  gray(0)
0,48: (0,0,0)  #000000  gray(0)
0,49: (0,0,0)  #000000  gray(0)
0,50: (0,0,0)  #000000  gray(0)
0,51: (0,0,0)  #000000  gray(0)
0,52: (0,0,0)  #000000  gray(0)
0,53: (0,0,0)  #000000  gray(0)
0,54: (0,0,0)  #000000  gray(0)
0,55: (0,0,0)  #000000  gray(0)
0,56: (0,0,0)  #000000  gray(0)
0,57: (0,0,0)  #000000  gray(0)
0,58: (0,0,0)  #000000  gray(0)
0,59: (0,0,0)  #000000  gray(0)
0,60: (0,0,0)  #000000  gray(0)
0,61: (0,0,0)  #000000  gray(0)
0,62: (0,0,0)  #000000  gray(0)
0,63: (0,0,0)  #000000  gray(0)
0,64: (0,0,0)  #000000  gray(0)
0,65: (0,0,0)  #000000  gray(0)
0,66: (0,0,0)  #000000  gray(0)
0,67: (0,0,0)  #000000  gray(0)
0,68: (0,0,0)  #000000  gray(0)
0,69: (0,0,0)  #000000  gray(0)
0,70: (0,0,0)  #000000  gray(0)
0,71: (0,0,0)  #000000  gray(0)
0,72: (0,0,0)  #000000  gray(0)
0,73: (0,0,0)  #000000  gray(0)
0,74: (0,0,0)  #000000  gray(0)
0,75: (0,0,0)  #000000  gray(0)
0,76: (0,0,0)  #000000  gray(0)
0,77: (0,0,0)  #000000  gray(0)
0,78: (0,0,0)  #000000  gray(0)
0,79: (0,0,0)  #000000  gray(0)

如您所见,它是对提取列的每个像素颜色的文本描述(其中像素坐标在每行文本的第一个字段中给出)。第一行表示使用哪种颜色space。

*按字母顺序对 MD5 和进行排序

按字母顺序对这些文本文件的 MD5 总和进行排序时,在像素颜色行和列相同的地方应该会立即显而易见:

md5sum *.text | sort

结果:

(我在某些行的末尾手动添加了一些注释。)

09c0670b59c03fb3d6f8116ee0fe35a2  bottom-edge-10297-13918-3702.text
09c0670b59c03fb3d6f8116ee0fe35a2  top----edge-28824-13023-24184.text
10be914aa8b6aaa8c0e35a4a93421f3a  left---edge-20056-20936-29071.text  #left edge of QR
10be914aa8b6aaa8c0e35a4a93421f3a  left---edge-22806-3380-17484.text   #left edge of QR
10be914aa8b6aaa8c0e35a4a93421f3a  left---edge-24658-20374-23042.text  #left edge of QR
10be914aa8b6aaa8c0e35a4a93421f3a  left---edge-27206-10104-18226.text  #left edge of QR
10be914aa8b6aaa8c0e35a4a93421f3a  left---edge-30261-16558-25650.text  #left edge of QR
10be914aa8b6aaa8c0e35a4a93421f3a  right--edge-16369-21469-8252.text   #right edge of QR
10be914aa8b6aaa8c0e35a4a93421f3a  right--edge-21875-14159-1067.text   #right edge of QR
10be914aa8b6aaa8c0e35a4a93421f3a  right--edge-24426-18830-5627.text   #right edge of QR
10be914aa8b6aaa8c0e35a4a93421f3a  right--edge-31250-3578-9750.text    #right edge of QR
10be914aa8b6aaa8c0e35a4a93421f3a  right--edge-5004-10810-17642.text   #right edge of QR
217c799ce772d99e11b7ba2a04f42bb7  bottom-edge-11976-7751-26756.text
217c799ce772d99e11b7ba2a04f42bb7  top----edge-5774-30645-16062.text
2c96cebc6cc175b54cae54f1a27771fa  bottom-edge-24426-18830-5627.text
2c96cebc6cc175b54cae54f1a27771fa  top----edge-21875-14159-1067.text
3088414b0fe1190bbd4ed9315d86a0ac  left---edge-13789-10513-4721.text
3088414b0fe1190bbd4ed9315d86a0ac  right--edge-22806-3380-17484.text
3755d45bb6ad21b70f545f39c5550eda  left---edge-17636-24599-1877.text
3755d45bb6ad21b70f545f39c5550eda  right--edge-20056-20936-29071.text
41fc32b4b70622b4aff7d9e81f81daad  left---edge-13858-18007-13070.text
41fc32b4b70622b4aff7d9e81f81daad  right--edge-10297-13918-3702.text
480bed740a716fa10593ba061c5b6df5  left---edge-5004-10810-17642.text
480bed740a716fa10593ba061c5b6df5  right--edge-20021-11440-20836.text
55cc4713ee13ebbeb5c2e212e430da94  bottom-edge-28824-13023-24184.text
55cc4713ee13ebbeb5c2e212e430da94  top----edge-32078-14314-1511.text
62fecd6971445d4176a842727767d3f7  left---edge-4555-18116-29.text
62fecd6971445d4176a842727767d3f7  right--edge-18056-16294-30425.text
69565761226f122f98267862108119a6  left---edge-21875-14159-1067.text
69565761226f122f98267862108119a6  right--edge-26507-21853-11958.text
72cec2b0a518c2cce7204188923d9f79  left---edge-26507-21853-11958.text
72cec2b0a518c2cce7204188923d9f79  right--edge-15816-4564-31665.text
7343348f1b10e47b33e0bde47b455c2b  left---edge-16369-21469-8252.text
7343348f1b10e47b33e0bde47b455c2b  right--edge-22161-18187-20222.text
7661c257d28e1916208ed2a70989e42d  bottom-edge-13789-10513-4721.text
7661c257d28e1916208ed2a70989e42d  top----edge-17636-24599-1877.text
7d2a0d7aae6dc017ffa7af7180b1c017  left---edge-5167-27533-24066.text
7d2a0d7aae6dc017ffa7af7180b1c017  right--edge-32078-14314-1511.text
85b744d1774cd4ebe50f0cc40d10b491  left---edge-18056-16294-30425.text
85b744d1774cd4ebe50f0cc40d10b491  right--edge-27206-10104-18226.text
88942ff37ce0e884d1995a6dd11708d6  left---edge-10297-13918-3702.text
88942ff37ce0e884d1995a6dd11708d6  right--edge-11976-7751-26756.text
9185daee4fdcea482c9fa805bba40346  bottom-edge-31250-3578-9750.text
9185daee4fdcea482c9fa805bba40346  top----edge-16369-21469-8252.text
9b8a7f18e4ebbc7249e5f26ac18190a8  left---edge-28824-13023-24184.text
9b8a7f18e4ebbc7249e5f26ac18190a8  right--edge-5774-30645-16062.text
9bd8f7a474229aefd988c4231f3b26f4  bottom-edge-21875-14159-1067.text
9bd8f7a474229aefd988c4231f3b26f4  top----edge-5004-10810-17642.text
a1778c1a468c122bf408a9c2bec9135c  bottom-edge-13858-18007-13070.text
a1778c1a468c122bf408a9c2bec9135c  top----edge-22161-18187-20222.text
a4f3eee0e523f1b343d292987da297b3  bottom-edge-20056-20936-29071.text
a4f3eee0e523f1b343d292987da297b3  bottom-edge-24658-20374-23042.text
a4f3eee0e523f1b343d292987da297b3  top----edge-27206-10104-18226.text
a4f3eee0e523f1b343d292987da297b3  top----edge-30261-16558-25650.text
af851da184ea6d6586c64aaefdc6875b  bottom-edge-22806-3380-17484.text
af851da184ea6d6586c64aaefdc6875b  top----edge-20056-20936-29071.text
b4047de9fb6458ebfde5b3a11b40241a  left---edge-5774-30645-16062.text
b4047de9fb6458ebfde5b3a11b40241a  right--edge-30261-16558-25650.text
b58fe9214967f6d2311e11cb0bf1acc4  left---edge-15816-4564-31665.text
b58fe9214967f6d2311e11cb0bf1acc4  right--edge-17636-24599-1877.text
bef4d7004da4f5de4ca5beeabc1222e8  bottom-edge-5774-30645-16062.text
bef4d7004da4f5de4ca5beeabc1222e8  top----edge-13789-10513-4721.text
c042d1cd3dfb339177e8da3c2907d58e  bottom-edge-15816-4564-31665.text
c042d1cd3dfb339177e8da3c2907d58e  top----edge-4555-18116-29.text
c5f38ebda77190495d77df1fb7002d1e  bottom-edge-32078-14314-1511.text
c5f38ebda77190495d77df1fb7002d1e  top----edge-15816-4564-31665.text
c61f738453fa3a86b52b2de5c0d43530  left---edge-20021-11440-20836.text
c61f738453fa3a86b52b2de5c0d43530  right--edge-4555-18116-29.text
c772762652d22cbf228e57f0dcf1a3b4  bottom-edge-22161-18187-20222.text
c772762652d22cbf228e57f0dcf1a3b4  top----edge-5167-27533-24066.text
cfaa399406e0be3d07297f23c684e7c3  left---edge-11976-7751-26756.text
cfaa399406e0be3d07297f23c684e7c3  left---edge-31250-3578-9750.text
cfaa399406e0be3d07297f23c684e7c3  right--edge-13858-18007-13070.text
cfaa399406e0be3d07297f23c684e7c3  right--edge-24658-20374-23042.text
d39c2a0c14c20b66b441d368c164171d  bottom-edge-18056-16294-30425.text  #bottom edge of QR
d39c2a0c14c20b66b441d368c164171d  bottom-edge-20021-11440-20836.text  #bottom edge of QR
d39c2a0c14c20b66b441d368c164171d  bottom-edge-27206-10104-18226.text  #bottom edge of QR
d39c2a0c14c20b66b441d368c164171d  bottom-edge-4555-18116-29.text      #bottom edge of QR
d39c2a0c14c20b66b441d368c164171d  bottom-edge-5004-10810-17642.text   #bottom edge of QR
d39c2a0c14c20b66b441d368c164171d  top----edge-10297-13918-3702.text   #top edge of QR
d39c2a0c14c20b66b441d368c164171d  top----edge-11976-7751-26756.text   #top edge of QR
d39c2a0c14c20b66b441d368c164171d  top----edge-13858-18007-13070.text  #top edge of QR
d39c2a0c14c20b66b441d368c164171d  top----edge-24658-20374-23042.text  #top edge of QR
d39c2a0c14c20b66b441d368c164171d  top----edge-31250-3578-9750.text    #top edge of QR
d71e7b561d2cc251c962a843daff53bb  bottom-edge-5167-27533-24066.text
d71e7b561d2cc251c962a843daff53bb  top----edge-26507-21853-11958.text
d7ffe4aaf744387e7fa9db32271247e0  bottom-edge-26507-21853-11958.text
d7ffe4aaf744387e7fa9db32271247e0  top----edge-20021-11440-20836.text
da2aa01201b3da20a970928bc2bdef36  bottom-edge-30261-16558-25650.text
da2aa01201b3da20a970928bc2bdef36  top----edge-22806-3380-17484.text
dd86f02cfc0e260463c75ae5b8709f33  bottom-edge-16369-21469-8252.text
dd86f02cfc0e260463c75ae5b8709f33  top----edge-24426-18830-5627.text
dfdaa88ebf33284ac0367f92060cd060  bottom-edge-17636-24599-1877.text
dfdaa88ebf33284ac0367f92060cd060  top----edge-18056-16294-30425.text
f8e2f384f2269e086dc00fb6ff0b3f41  left---edge-32078-14314-1511.text
f8e2f384f2269e086dc00fb6ff0b3f41  right--edge-13789-10513-4721.text
fa59372704f3b48912a33f3780e9dce7  left---edge-22161-18187-20222.text
fa59372704f3b48912a33f3780e9dce7  right--edge-28824-13023-24184.text
fff7e26c6f9852fcfa51e79d154dc58a  left---edge-24426-18830-5627.text
fff7e26c6f9852fcfa51e79d154dc58a  right--edge-5167-27533-24066.text

可以立即识别出以下模式:

  1. 10 个相同的 MD5 和 d39c2a0c14c20b66b441d368c164171d
  2. 10 个相同的 MD5 和 10be914aa8b6aaa8c0e35a4a93421f3a
  3. left/right 边
  4. 的 20 对相同的 MD5 和
  5. top/bottom 边
  6. 的 20 对相同的 MD5 和

在“1”的情况下。和“2”。很容易看出这些都是全白边,代表 left/right/top/bottom 条完整的 QR。

剩下的比赛,'3'。和“4”。可以很容易地以确定的方式将每个单独的图块调整到左侧或右侧 "partner"。


更新

直到现在我才看到马克的回答。但是我看他的方法和我的差不多。

虽然我不愿意添加我的完整脚本来生成最终重新组合的二维码。

所以如果 Mark 的解决方案包括这个,他应该得到正确答案的批准。