用误差扩散抖动算法生成艺术QR码
抖动算法生成QR码的艺术
做图像生成或二维码相关应用的同学值得一看,这套误差扩散掩盖数据模块的思路可以直接落地,帮你做出既好看又能扫的二维码。
Making your own dithered QR codes
制作你自己的抖动二维码
QR codes: the basics
二维码:基础知识
A QR code is really just a way of encoding a few bytes of data in a way that can be easily read froma photo by a smartphone or similar device. They're designed to be very robust to bad focus, bad printing, funny angles, and missing areas. Exactly how they work isn't terribly important, but for the purposes of this page, a QR code is a grid of squares that divide into two parts — the function patterns (which are the bold shapes in the above example) and the data modules (everything else). The function modules are mostly used to let the scanner find the QR code easily, so they have to be very clear and distinct. The data modules are where the actual data is stored, along with some extra header data etc, and since those are only read after the scanner has used the function modules to work out exactly where the QR code is, they can be modified a bit more. This is often used by brands to create QR codes that look a bit distinctive, and while this kind of modification makes them a bit less robust when scanning, but you can generally get away with a fair bit before the codes become unscannable.
二维码实际上只是一种编码几字节数据的方式,使得智能手机或类似设备可以轻松地从照片中读取。它们被设计得非常稳健,能够应对对焦不准、打印质量差、角度奇怪和部分缺失的情况。它们的具体工作原理并不十分重要,但就本页而言,二维码是一个方格网格,分为两部分——功能图案(即上述示例中的粗体形状)和数据模块(其他所有部分)。功能模块主要用于让扫描器轻松找到二维码,因此它们必须非常清晰和醒目。数据模块是存储实际数据的地方,还有一些额外的头部数据等,由于这些数据只有在扫描器利用功能模块确定二维码的确切位置后才会被读取,所以它们可以稍微修改。品牌经常利用这一点来创建看起来有点独特的二维码,虽然这种修改会使扫描时的稳健性稍差,但在代码变得无法扫描之前,你通常可以容忍相当多的修改。
Putting pictures in QR codes
在二维码中嵌入图片
This is a QR code I saw on Mastodon. One of the modifications you can do with the data modules is to shrink them — the scanner will look where the finder patterns say the centres of the pixels should be, and so as long as the code isn't distorted when you try to scan it. That means the rest of the space is yours to play with. Dave divides each pixel into a three-by-three grid, and uses the middle one to store the data, and the others for a photo. The result is a low-res, one-bit photo with some salt-and-pepper noise on it.
这是我在 Mastodon 上看到的一个二维码。你可以对数据模块进行的修改之一是缩小它们——扫描器会查看定位图案指示的像素中心位置,因此只要在扫描时代码没有变形,其余空间就归你自由使用。Dave 将每个像素分成一个三乘三的网格,用中间的一个来存储数据,其他的用于图片。结果是一张低分辨率、单比特的图片,带有一些椒盐噪声。
Dithering
抖动
Obviously when we crush an image into a low colour depth, we don't normally just threshold it — that is, make the dark pixels black and the light pixels white. We normally use a kind of chequerboard effect to create midtones as well. A Bayer filter can be used to apply this idea across a whole image without having to sacrifice too much fine detail.
显然,当我们把图像压缩到低色彩深度时,我们通常不会只是简单地阈值化——也就是说,把暗像素变成黑色,亮像素变成白色。我们通常使用一种棋盘格效果来创建中间色调。拜耳滤波器可以用于将这种想法应用到整个图像,而不必牺牲太多细节。
Error diffusion
误差扩散
An attempt to improve on this method was Floyd-Steinberg dithering.
试图改进这种方法的是 Floyd-Steinberg 抖动。
In this method, you start in the top left and threshold the pixel normally — if it's more than 50% brightness, you make it white, and otherwise you make it black. Say it was 70% brightness — we're going to make it white, which is 100% brightness, which means we've added 30% of a pixel too much brightness. To counteract that, we're going to "diffuse" that error to other pixels — all the nearby pixels that we haven't thresholded yet are made a few percend darker, to a total of 30% of a pixel. When we come to threshold those pixels, we'll take that into account. The idea is that once we've thresholded the entire picture, every part of the image will be, on average, closer to the correct brightness than more regular dithering. The irregular patterns are also a bit less distracting.
在这种方法中,你从左上角开始,正常地对像素进行阈值处理——如果亮度超过50%,就设为白色,否则设为黑色。假设亮度为70%——我们将设为白色,即100%亮度,这意味着我们增加了30%的像素亮度。为了抵消这一点,我们将这个误差“扩散”到其他像素——所有附近尚未进行阈值处理的像素都会稍微变暗一些,总共减少30%的像素亮度。当我们对这些像素进行阈值处理时,会考虑到这一点。其思想是,一旦我们对整幅图像进行了阈值处理,图像的每个部分平均而言会比常规抖动更接近正确的亮度。不规则的图案也不太容易分散注意力。
QR codes with error-diffusion dithering
带有误差扩散抖动的QR码
The other advantage of this irregular dithering is that the salt-and-pepper noise caused by the QR code data modules is much harder to spot. But it's still there, and it's why the image looks so noisy. I mean, it would look fairly noisy anyway because it's a 147×147 pixel one-bit image, but some of that noise is because one in nine of the pixels are effectively random colours.
这种不规则抖动的另一个优点是,由QR码数据模块引起的椒盐噪声更难被察觉。但它仍然存在,这也是图像看起来如此嘈杂的原因。我的意思是,它看起来本来就会相当嘈杂,因为这是一幅147×147像素的1位图像,但其中一些噪声是因为每九个像素中就有一个实际上是随机颜色。
But we can solve that using more error diffusion.
但我们可以通过更多的误差扩散来解决这个问题。
Error-diffusing the data modules
对数据模块进行误差扩散
In this case, we're doing two passes on the error diffusion. The second is the same as normal, but the first is specifically to mask the data modules. We already know what colour all of those pixels have to be, so we can make them those colours and diffuse that error to the surrounding eight pixels. The error will potentially be quite big — normally we get to choose the colour so the error can never be more than 50%, but here we might have to make a pixel in a dark area pure white and end up creating 95% error. That sounds bad, but that's exactly why it's so important to diffuse this error instead of just accepting it.
在这种情况下,我们进行了两次误差扩散。第二次与正常情况相同,但第一次专门用于掩盖数据模块。我们已经知道所有这些像素必须是什么颜色,所以我们可以将它们设为这些颜色,并将误差扩散到周围的八个像素。误差可能会相当大——通常我们可以选择颜色,因此误差永远不会超过50%,但在这里,我们可能不得不将暗区域的像素设为纯白色,最终产生95%的误差。这听起来很糟糕,但正是如此,扩散这个误差而不是仅仅接受它才如此重要。
You can see that this makes the image much cleaner.
你可以看到,这使得图像更加清晰。
Getting fancy with it
玩出花样
The generator tool allows you to do some extra tricks. You can rotate the QR code before adding the image, and tinker with the QR code settings to see if any of the alternative encodings happen to look nicer.
生成器工具允许你进行一些额外的技巧。你可以在添加图像之前旋转QR码,并调整QR码设置,看看是否有任何替代编码碰巧看起来更好。
You can also allow the generator to change a few of those 95%-error data modules — QR codes have enough error correction in them that you can afford to change a few pixels and they'll still scan. (That's how QR codes with logos in the middle work.) But in practice that doesn't really affect image quality much if you diffuse the error from the data modules and it seriously affects how well the code scans.
你还可以允许生成器更改那些95%错误数据模块中的几个——QR码具有足够的纠错能力,你可以更改几个像素,它们仍然可以扫描。(这就是中间带有标志的QR码的工作原理。)但在实践中,如果你从数据模块中扩散错误,这实际上不会对图像质量产生太大影响,而且它会严重影响代码的扫描效果。
Using these in real life
在现实生活中使用这些
This all works pretty well if you don't take it too far and the code is going to be on a big screen or a poster or something. If it's on, say, a paper flyer that could get crumpled then you're going to need more of that redundancy, robustness and error correction that we gave up to make the QR code pretty. Ultimately it's a trade-off between aesthetics and scannability — and remember that just because a code scans on your phone, from a laptop screen, doesn't mean it will scan on a random stranger's potato phone from a printout in bad lighting.
如果你不过分追求美观,并且代码将显示在大屏幕或海报等上,这一切都效果不错。如果它是在比如可能被弄皱的纸质传单上,那么你将需要更多的冗余、稳健性和纠错能力,而我们为了美化QR码而放弃了这些。最终,这需要在美观和可扫描性之间进行权衡——请记住,仅仅因为代码在你的手机或笔记本电脑屏幕上可以扫描,并不意味着它能在光线不佳的情况下从打印件上用陌生人的低端手机扫描成功。
Also bear in mind that the generator tool makes tiny images with no margin — some margin is needed to make a QR code scan reliably, and browsers will normally blur images when upscaling them if you don't disable that with CSS. The margin has to be the opposite colour to the middle of the big three "finder" pattern squares — normally that's white but you can generate inveted QR codes that need a black background instead.
还要记住,生成器工具会生成没有边距的小图像——需要一些边距才能使QR码可靠扫描,而且如果你不通过CSS禁用,浏览器在放大图像时通常会模糊图像。边距必须与三个大"定位"图案方块中间的颜色相反——通常是白色,但你可以生成反转的QR码,这些码需要黑色背景。
更进一步:量化金融体系
看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力