超市售货员扫一下条形码就能结账?为什么别人扫一下你的二维码就能加上你的微信?小小的条形码、二维码中究竟如何蕴藏如此多不同的信息?
现在我们购买到的大多数商品,包装上面都有条形码。条形码初期存在多个发明,现在也存在非常多的变种。条形码可以算是和摩尔斯电码沾点边,因为早期的一位发明家曾经受到过摩尔斯电码的启示。确实,从某种意义上说,我们可以想象把摩尔斯电码的点和划,垂直画成线,变成窄和宽的条纹。当然现在主流应用上的条形码,并不是简单地从摩尔斯电码转换来的。
条形码世界
今天,计算机越来越多地参与着我们生活的方方面面。为了把信息迅速方便地送入计算机,人们设计了各种各样的编码,条形码就是其中的一种。其他的编码方法,比如RFID,尽管成本越来越低,但相比之下,条形码仍然是最便宜的,其成本几乎为零,因为条形码只需要用油墨印在包装盒上即可。
条形码是怎样将信息编码的呢?对于不同的应用存在许多标准。我们这里介绍一下其中一个标准,即GTIN-13以及它所对应的条形码编码标准EAN-13。GTIN是Global TradeItem Number的意思,其中的GTIN-13标准是用13位数字代表全球贸易中的万物。其中最高的三位代表生产物品的国家,比如中国生产的产品前三位是690-695。其余一些位数表示地域,行业等信息,靠后的一些位数是物品的代码。如果13位数全部用满,可以表述10万亿种不同的物品,足够全球所有人每人研发1000种不同的商品。
我们现在在市场上买到的商品,大部分都有这样一个13位数的代码。GTIN兼容了国际标准书号(ISBN),国际标准期刊序号(ISSN)等等。当GTIN-13需要印刷成条形码的时候,使用的是EAN-13条形码标准。
比如作者参与写作的一本科学普及书的条形码,如下图所示。对于图书,不管是哪个国家出版的,其前三位都被指定为978或979。而杂志期刊等,前三位总是977。
大家可以仔细观察上面的条形码,我们可以看到数字5出现了几次,不过它们似乎长的不完全一样,在左边的两个是一个样,而在右边的两个显然不同。如果认真看看,左边与右边条纹是“黑白颠倒”的关系。可是,再看看左边出现的两个7,它们似乎也不一样。此外,在条形码左边与右边各有6位数字,这并不难理解,但整个码符号的最左边那个9是怎么来的?难道可以无中生有吗?
为此,我们需要了解一下EAN-13(包括ISBN,ISSN,UPC等)条形码的生成规则。
条形码的编码规则
EAN-13条形码是用两个条纹来表示一个数字的,整个码包括13个数字,其中直接编码12个,左边6个右边6个,第13个用后面介绍的方法隐含编码。条形码两边与中间安排了起始符(S)中间符(M)以及结束符(E),各为两个窄条。因此,所有码,不管具体数字是什么,都包含有30个条纹。
那么,每个数字又是怎样用两个条纹表示的呢?这就需要我们在更细尺度上讨论。每个数字的两个条纹,包括了两黑两白四个区域,它们的总宽度为7个单位宽度。这个单位宽度可以根据印刷精度自由选择,比如当我们把单位宽度选定为0.5毫米时,每个数字所占宽度为3.5毫米。
对于左边6个数字,每个数字的左边一定是白的,而右边一定是黑的。每个数字从左到右都是“白黑白黑”地构造。这样,当几个数字一个个紧挨在一起的时候,它们之间就可以存在一个清晰的边界。
了解了这些,我们就可以想象自己是当年制定这种条形码的设计师,很容易地数一数可用的编码有哪些。
我们可以把7个单位宽度组合在一起,看成7个比特的二进制数。如果这7个比特可以任意安排黑白,则可以表达128个字符。不过我们已经限定最左边一定是白色,最右边一定是黑色,这样就只有中间5个比特可以改变,或者说最多可以表达32个字符。把这32个可能的字符全部画出来,就得到下面这个图。
现在,我们把不符合“白黑白黑”要求的字符去掉,还剩下20个可用的字符。这20个字符又可以分成两类,一类包含有奇数个(3个或5个)单位宽度为黑,共10个,另一类包含有偶数个(2个或4个)单位宽度为黑,也是共有10个。我们把这两类字符分别用来作为0-9数字的代码。其中奇偶性为奇的10个称为EAN-L码,在上面图中用浅棕色标注。奇偶性为偶地10个称为EAN-G码,在上面图中用浅绿色标注。这两种左边的条形码如下图所示。
那么,右边的条形码又是什么样的呢?我们希望最终的条形码具有一定的对称性,比如希望右边的码左黑右白,这样可以与结束符(E)有一个清晰的边界。因此最简单的一个做法,是把EAN-L码黑白颠倒,这样我们就有了EAN-R码,如下图所示。显然,由于EAN-L的奇偶性为奇,因此很容易看出EAN-R的奇偶性为偶,里面黑条的总宽度为偶数个单位宽度,与EAN-L正好反过来。
有了左边与右边数字的条形编码图形,我们就很容易拼接出一个完整的条形码。当我们只需要编码12个数字时,也就是说当13位数中最高位为0时,左边6个数字都用EAN-L码,右边6个数字则用EAN-R码。把数字与S,M,E符拼接在一起后,我们就可以得到如下图所示的条形码。
我们前面问过一个问题,在13位编码的整个条形码码符号的最左边那个数字(我们前面图书的条形码中的9)是怎么来的。此外,大家还会问,我们前面谈到的EAN-G图形能不能用在条形码的左边,代替EAN-L码?实际上,这两个问题是联系在一起的,EAN-13中,最左边那个数字,就是利用EAN-G图形,代替左边6个数字中一部分EAN-L图形来表述的。
在条码左边6个数字中,每个数字可以选用L或者G两种码。因此通过选择每一位的L或G,一共可以得到64(2的6次方)种组合。人们从这64种组合中,挑出了10个组合,用来表述13位编码中的最左边那个数。这10个组合以及它们代表的数字为:
0=LLLLLL;1=LLGLGG;2=LLGGLG;3=LLGGGL;4=LGLLGG;5=LGGLLG;6=LGGGLL;7=LGLGLG;8=LGLGGL;9=LGGLGL。
这样一来,最左边这个数不需要直接用一个单独的图形表述,而只需要通过选配左边6个数字编码图形的L或G组来“隐喻”。
现在我们再回过头看我们前面那本书的条形码,原来的一些疑问也就豁然开朗了。
首先,右边的两个5与左边的两个5自然不会是相同的,我们知道 EAN-L 与 EAN-R 对应的10个编码,是黑白反转的关系。那么,同在左边的相同数字编码一样吗?不一定。比如上面条码中左边的两个7,它们一个是 L 码另一个是 G 码。因为这个条码编码了第13位数字9,因此左边6个数字的L或G的选择为9=LGGLGL,因此它的第一个7是L而第二个7是G,所以这两个7长得不一样。此外,我们还可以看出左边的两个5恰好都是L,否则它们也未必相同。
条形码带给我们的启示
在很多编码工作的实践中,我们的着眼点是提高编码的效率。也就是说,利用尽量少的资源来存储或者传输比较多的信息。但是在设计条形码的时候,更需要考虑的,是可靠性和准确性。为此,我们可能会“浪费”一些编码资源,来提高编码的冗余度。
在前面谈到的条形码中,每个数字的编码空间有7个单位宽度,如果充分利用可以编制128个字符。但是我们对编码空间作了限制:(1)左白右黑,(2)包含两个条纹。这样一来,这个编码空间中就只剩下20个可以用的组合了。但是这样做带来的好处非常多。首先是两个数字挨在一起,它们之间存在一个黑白清晰的边界。同时在每个数字的编码空间中,也不会出现一大片黑,或者一大片白的状况,而是存在足够的黑白变化,便于扫描器辨别。更重要的是,这样的编码方法提供了很多简便的查错方法。比如一个完整的条形码,不论是什么内容,总是包含30个条纹。这样,当扫码器扫过之后如果发现多于30或少于30个条纹,立即就能知道是出错了。
条形码应用中,还会出现一个常见的复杂性,就是扫码器既可能从左向右正着扫,也可能反过来扫。这就要求条形码自身携带左右标识。当我们在条形码的左边使用EAN-L码,右边使用EAN-R码的时候,条形码的左右就非常分明。左边所有数字的奇偶性为奇,右边所有数字的奇偶性为偶。当我们需要编制13位数编码,因此在左边6个数中有些会使用EAN-G码的时候,左边有些数字的奇偶性也可能呈现偶。不过,我们前面看到,左边6个数中最左边那一位总是使用L码,这就足以作为条形码的左标识了。
从条形码到二维码
条形码毫无疑问是非常成功的,但由于条形码是编码空间是一维的,因此可以携带的信息非常有限。很自然,人们想到要向平面上两个维度发展。多年来,二维的条形码出现过许多标准及变化。我们今天经常看到的一种是QR码。下图所示QR码是作者创作的一个科学普及音乐视频文件在微云上的链接。
这里特意提醒一下读者,一个二维码当中,直接编码的内容是几十乃至上百个字母或数字,它们通常构成一个链接,但它们不是视频文件本身。视频文件往往会需要几十 MB 乃至几十 GB 存储空间,二维码存储不了那么多的数据。
当然,二维码要容纳几十乃至上百个字母和数字也并不容易。对比条形码,人们对二维码在可靠性和准确性上的要求是一样的。但是,二维码要比条形码容纳更多的信息,因此还必须兼顾编码的效率。这两项要求在有的情况下是矛盾的,但是在很多时候二者是相辅相成的。
二维码扫码使用时通常是用手机来拍照,在手机内得到一个由像素构成的二维点阵。手机中的软件只有可靠准确地获得了二维码的高度宽度等外形参数,才能正确地读取编制在二维码中的数据。那么,怎样才能方便地获取二维码的外形参数呢?这就需要我们在设计时作出仔细的考虑。
从QR码上,可以看到在左上角,右上角和左下角各有一个口字形或回字形的方块。我们应该可以猜测出,这三个方块就是为了提供二维码外形参数的。
问题是,为什么需要三个方块呢?只在左上角上留一个方块行不行?我们知道手机的照相机照出来的照片是会发生形变的,如果只有一个方块,就很不容易获取二维码的高度和宽度这两个参数。
如果只留两个方块,比如,保留右上角和左下角这两个方块。这好像也不行,因为在手机的照片中,软件无法去辨认哪边是上哪边是下。如果只保留左上角和右上角两个方块,则上下倒是可以区分出来了。但是由于手机照片的形变,我们只能得到二维码宽度这个参数,而很难得到高度这个参数。因此,我们现在看到的二维码里面有三个大方块。
我们这里谈到的仅仅是一些最基本的考虑,实际上二维码里的学问还是不少的,这里有一个问题提供给大家思考。下面这个二维码中,只包含了一个字母“a”。既然只有一个字母,那么这个字母占据的面积应该是很小的吧?因此在整个码所占的面积中,应该大部分是空白。
但是,我们并没有在上面这个二维码中看到大块的白色或黑色区域,而是到处都是错落有致,黑白相间。大家不妨想想为什么要这样设计?用什么办法可以做到这点?