首页 > 编程知识 正文

python学习笔记,小甲鱼python笔记

时间:2023-05-06 09:08:30 阅读:220241 作者:1097

Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

我写了一个程序来清洁手写笔记的扫描图,并同时减少文件大小。

示例输入输出:

左边: 300 DPI, 7.2MB PNG / 790KB JPG. 右边: 300DPI, 121KB PNG.(注解1)

免责声明:此次描述的过程基本上是Office Lens应用已经实现了的,或许还有很多其他工具也可以做同样的事。我并没有提出一个完全新的发明,我只是对现有的工具重新实现。

如果你没时间,可以直接查看github库,或者跳到结果一节,那里你可以与3D色簇程序交互(本翻译稿已将交互过程显示为gif动图,请查看原文链接参与交互)。

动机

我的有些课并没有指定教材。这些课上,我习惯每周指定一位“学生记录员”分享他们的课堂笔记,这样就可以留下些文字记录以备学生们检查自己对材料的理解。这些笔记会以PDF格式上传到课程网站上。

在学校,我们有一台可以扫描成PDF文件的“智能”影印机,但扫描输出文档有点,嗯,不美观。下面是扫描一页手写作业的输出:

影印机似乎是随机的选择是要二值化每个符号(如上图的x)还是转换符号为笨拙糟糕的JPG图像(如上图中的平方根符号)。不用说,我们可以做得更好。

概述

我们从一份可爱的学生笔记扫描开始,如下图:

这张原始的PNG图以300DPI的分辨率扫描,大小大约是7.2MB,以85的质量级转换为JPG图像后大小大概790KB(注解2)。因为扫描的PDF格式仅仅是PNG或JPG的封装容器,所以我们不会期望图片转换成PDF后文件大小会减小。况且800KB每页也很大,出于加载时间考虑,大约100KB每页(注解3)才是最合适的。

虽然这个学生做的笔记很整洁,但扫描图像看起来还是有点乱(你不能怪她)。有很多反面的字迹或隐或现,相比于背景是单一颜色的情况,反面字迹不仅让观看者分神,也不利于JPG和PNG编码器压缩。

下面是我的noteshrink.py程序输出的样子:

这是一个比较而言很小的PNG文件,大概只有121KB。输出图像不仅更小,也更干净。

流程与彩色图像基础

下面是生成上面紧致、干净图像的处理步骤:

1、确定原始扫描图像的背景色。

2、通过阈限一个不同于背景色的值来分离出图像前景。

3、在前景中选择一个小数量的“呈现色",然后把原图转换为一个索引色彩PNG图像。

在深入介绍上面每个步骤之前,复习一下彩色图像的数字存储会很有帮助。因为人类眼睛有三种类型的色彩敏感细胞,我们可以通过组合不同强度的红绿蓝三种色光(注解4)来重现每种颜色。结果就是每种颜色都对应RGB色彩空间中的一个三维点,如下所示(注解5):

尽管真正的向量空间允许无限个连续不同的像素光强,但为了数字存储我们需要离散化颜色值。典型情况是为红绿蓝三色各分配8位存储空间。无论如何,把图像中的颜色对应到三维空间中的一个点是一种有力的分析方法,在深入讨论上面的三个步骤时你会清楚的看到这一点。

确定背景色

因为一页纸的大部分面积都是没有笔迹或横线的,我们可以假定纸张颜色是扫描图像中最常出现的那一种颜色。如果扫描仪总是将空白的地方用同一个RGB三元值呈现,那问题就简单了。遗憾的是实际情况并非如此。扫描得到的颜色值会受到灰尘点,玻璃上的污迹,纸张本身各处颜色差异,光感器噪声等因素的影响。所以实际情况是,“纸张颜色”可能会是上千个不同的RGB值。

原始扫描图像是2081X2531,总共是5267011个像素。虽然我们可以考察所有的像素点,但是只考察输入图像中有代表性的样本会更快。noteshrink.py程序默认情况下只取样5%的像素(对于300DPI的扫描完全没问题)。现在,我们看看在原始扫描下随机取出的

10000个像素样本:

虽然上图和原始扫描图像一点也不像——上面看不到一个字——但两张图的色彩分布情况大体相当。两张图大体上都是灰白色的,有一些红的蓝色和灰色像素。下图是同样的10000个像素,以亮度(像素的R、G、B值相加得到)排序得到的:

离远点看上图,底下的80-90%的地方似乎都是同一颜色,然而,仔细点看还是嫩发现颜色差异的。事实上,上图中出现频率最高的的颜色,RGB值是(240,240,242),仅在10000个样本中出现226次,还不到总像素的3%。

因为众数的比重在总的样本数中只占很小比例,我们应该对其描述图像中颜色分布的可靠性提出质疑。如果在找众数前先减小色彩深度的话,我们可以更方便的找到页面背景色。下图是将8比特每通道消减至4比特每通道后的图:

现在频率最高的颜色RGB值是(224,224,224),在总的样本像素中出现3623次(36%)。本质上来说,我们通过减小色彩深度,让相近颜色变成了同一颜色,也使得找到数据中的峰值点更容易了(注解6)。

这里在可靠性和准确性之间做了一个平衡:浅的色彩深度更有利于找到颜色分布中值,较深的色彩深度更准确。最终,我用6比特每通道来确定背景色,这个值似乎很好的平衡了以上两点。

分离前景色

一旦我们找出了背景色,就可以根据每个像素颜色近似程度对图像二值化。一种自然的计算两个颜色相似程度的方法就是依据颜色的RGB空间计算它们的疯狂的茉莉距离;然而,这个简单的方法却不能很好的分开下面几种颜色:

下表是上面各种颜色和它们到背景色的疯狂的茉莉距离:

可以看到,暗灰色,也就是应该归为背景色的背面笔迹的颜色,疯狂的茉莉距离比应该归为前景的粉色还要大。任何可以将粉色归为前景的阈值都会把背面笔迹也归为前景。

我们可以通过将色彩从RGB空间映射到HSV空间来解决这个问题。HSV空间将RGB空间的立方体形状扭曲成圆柱体形状,如下剖面图所示(注解7) :

HSV圆柱以从内到外,从下到上的多彩圆环为特点,色调H指圆环上的角度,圆柱的中心线从底下的黑色到顶部的白色,中间是逐渐过度的灰色,整个线具有饱和度S为0。而整个圆柱体的圆弧外表面不论色调多少,饱和度都是1。最后,亮度V指一个颜色总的明亮程度,从圆柱底的黑到圆柱顶的白。

现在再来考虑前面RGB空间中不好区分的几种颜色,这次考察它们在HSV空间中的V值和S值:

可以看到,白色黑色和灰色在明亮度V上有很大差别,但是具有相似的低饱和度S,至少现对于红色和粉色时是这样。通过HSV提供的信息,我们可以通过下面规则成功的区分像素是否为前景:

a、明亮度V比背景色大0.3以上的,或者

b、饱和度S比背景色大0.2以上的颜色

前一个条件区分出黑色笔迹,后一个区分出红色和粉色笔迹。两个条件都有效的排除了灰色的背面笔迹,不同的图像可能需要设置不同的S/V的阈值,到结果一节可以看到详细介绍。

选择一组呈现色

一旦我们分离出了前景,我们就只剩下一些对应着纸上笔迹的颜色。让我们可视化这些颜色。但是,不是将这些颜色看成一组像素,而是把它看成是在RGB彩色空间中的一些3D点。结果的散点图看起来有点“厚重”,有几条色带:

上图是由three.js生成的可交互3D图(本译稿呈现为动图)。

现在的任务变成了将原始24位每像素的图像转换成一个具有小数量(这里是8个)代表色的索引色彩图像。这样做有两个效果:一、呈现一个颜色现在只要3比特(因为8等于2的三次方),减少了文件大小;二、使得结果图像颜色更一致,因为相似颜色很可能由结果图像中的同一颜色呈现。

为了完成这个任务,我们使用一种利用了上图“笨重”属性的数据驱动方法。选择对应着上图颜色簇中心的颜色,就可以得到一组较精确的代表色。专业点说,就是我们要使用簇分析法解决一个色彩量化问题(这个问题本身是向量量化问题的一个特例)。

我选择解决这个问题的算法是k平均算法。总的来说就是找到一组中心点,使得周围点到最近中心点的平均距离最小。下图就是你在上图中选择7个簇(也即7个中心点)时得到的(注解8):

在这个图示中,有黑色外壳的点代表前景色的取样点,有颜色的线把这些点连到最近的中心点。当整个原图转换为索引色彩图像时,每个前景点都会被离它最近的中心点代替。最后,外圆指示了到中心点的最远距离。

哨声与铃声

除了能设置亮度V和饱和度S的阈值,noteshrink.py程序还有几个其他的特性。默认情况下,程序会通过重新设置颜色的最大最小强度值为0到255,来增加最后调色板各色的艳丽度和对比度。如果没有这个调整,上面图像的8色调色板看起来就是这样:

调整后的调色板更鲜艳:

程序还有一个选项可以在隔离前景后,强制背景色变成白色。为了进一步减小PNG图像的大小,noteshrink.py程序可以自动调用像optipng,pngcrush和pngquant这样的PNG优化工具。

程序最后使用ImageMagick的转换工具将几张图像组合到一个PDF文件中去,就像这个PDF文件一样。还有,noteshrink.py自动的根据输入文件名排序(以数字为关键字,而非像shell中的globbing操作一样使用字母表排序)。专一的秀发傻傻的扫描程序(注解9)输出的文件名像scan 9.png 和 scan 10.png这样时,依据数字大小排序则更好。

结果

下面是几个程序输出的对比图,第一个(pdf文件在此)使用默认的阈值设置看起来很好:

下面是色簇的可视化结果:

下个例子(PDF文件在这)要求将饱和度阈值降低到0.045,因为下面的蓝灰线很淡。

这是色簇图:

最后,一个扫描自工程图纸的例子(pdf文件在这),这里,我把亮度V阈值设为0.05,因为背景色和线条的对比度很低:

色簇图如下:

所有4个PDF文件总共788KB,大概130KB每页。

结论和进一步工作

我很高兴能为我的课程网站写一个处理手写笔记的实用程序。另外,我也很享受本文的写作,特别是写作时我被鼓励去完善维基百科中颜色量化里的2D可视化部分。而且也终于学习了three.js(非常有趣,我还会再用这个的)。

如果我哪天重访本项目,我会试试其他的量化方案。本周我想到的一个就是使用频谱簇化处理一集色彩样本的最近相邻图。我以为这是一个全新的想法看,但是结果已经有一篇2012年的论文提出了这个想法。恩,好吧

你也可以尝试用最大期望算法来构造一个高斯混合模型来描述颜色分布——不太确定以前是否有人做过。其他有趣的想法还有试试“知觉一致”的色彩空间,比如L*a*b*来形成簇。或者尝试对给定图像自动确定最佳簇数量。

另一方面,我还有很多播客项目要做,所以我就先暂时搁置这个项目,并要求你来查看一下noteshrink.py的github库。

注解1、这里使用的手写笔录得到了我学生Ursula Monaghan和John Larkin的慷慨允许。

注解2、为了加载速度,本图片其实已经降到了150DPI。

注解3、我们的影印机在限制PDF文件大小上做的很好,这种类型的文件大概会少个50-75KB每页。

注解4、这就是加法三原色:红绿蓝。你的初级艺术老师可能告诉你说三元色是红黄蓝,这不正确。确实有减法三原色,

他们是青色,黄色和洋红色。加法三原色和光的组合有关(你显示器发的光),减法三原色和墨水及染料的组合有关。

注解5、图像由Wikimedia用户Maklaan提供,许可协议是CC BY-SA 3.0。

注解6、查看维基百科文章histogram article了解为什么增加“带宽”有作用。

注解7、图像由Wikimedia用户SharkD提供,许可协议是CC BY-SA 3.0。

注解8、为什么k=7而不是8?因为在最终图像里,我们只要8种颜色,而背景色已经占了一种。

注解9、没错,我看着你呢,Mac OS的Image Capture。

英文原文:https://mzucker.github.io/2016/09/20/noteshrink.html

译者:yuezy3

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。