这篇文章发布于 2021年06月27日,星期日,17:00,归类于 Canvas相关。 阅读 16631 次, 今日 5 次 13 条评论
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=9982
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。
最近接到一个需求,图片上显示文字,如果图片颜色比较单纯,则直接显示文字,如果图片花里花哨的,则文字显示区域高斯模糊,这样方便文字显示。
这里讲下我是怎么实现的,注意,本文介绍的方法一定不是最好的方法,原因见结语。
好,开始。
一、颜色相似度判断
无论是整体颜色算法,还是基于主色判断,都离不开颜色相似度的判断,这个判断有时候也被称为颜色差异判断(Color Difference),常见算法有下面这几个。
1. 纯粹的颜色距离判断
就是取R、G、B三个色值的平方差。
2. 加上权重
因为人类对R、G、B不同色值差异的感受程度是不一样的,因此,也有的算法会通过增加或减少权重的方式计算颜色的相似度,一种做法是使用2、4、3权重法,具体算法如下图所示:
3. 更复杂的权重计算
我则是使用的第三种权重计算方法。
该方法相关代码如下所示(如果要兼容IE,请自行调整语法,或者babel转下):
const colorDistance = function (arrRGB1, arrRGB2) { let [r1, g1, b1] = arrRGB1; let [r2, g2, b2] = arrRGB2; let rmean = (r1 + r2) / 2; let r = r1 - r2; let g = g1 - g2; let b = b1 - b2; return Math.sqrt((2 + rmean / 256) * r * r + 4 * g * g + (2 + (255 - rmean) / 256) * b * b); }
好,现在有了颜色相似度算法了,那怎么计算整片区域的色值都是相似的呢。
算法其实很多,由于我之前使用过color-thief,就是取图像主色的JS项目,于是决定基于主色判断整片区域的色值是否是相似的。
二、主色判断法
color-thief的项目地址是:https://github.com/lokesh/color-thief
目前Star数量接近1万了,基本上就是图像取主色最佳实现了。
例如,有下图所示的一张图:
我们取这张图像的前4种颜色,就会有如下图所示的结果:
此时,我们只需要对这4种颜色进行相似度判断,如果都非常相似,则图像就可以认为颜色单一。
这4个色值分别是:
- rgb(73,42,50)
- rgb(104,46,51)
- rgb(129,49,52)
- rgb(52,37,48)
相似度计算方法如下,两两计算,然后取平均值(依赖于上面出现的colorDistance()
方法)。
// 计算平均颜色距离 let arrLocalDominantColor = [[73,42,50], [104,46,51], [129,49,52], [52,37,48]]; let arrDistance = []; arrLocalDominantColor.forEach(function (arrRGB) { arrLocalDominantColor.forEach(function (arrRGB2) { if (arrRGB2 != arrRGB) { arrDistance.push(colorDistance(arrRGB, arrRGB2)); } }); }); // 求和 let sum = arrDistance.reduce(function (prev, curv) { return prev + curv; }); console.log('平均相似度' + Math.round(100 * sum / arrDistance.length) / 100);
计算结果为:68.45
基本上,按照我自己的实践,平均相似度小于100就可以认为是近似纯色了。
至此,似乎问题已经解决了,然而,并没有这么简单。
比方说下面这张素材图:
从视觉上来看,应该认为是纯色,文字可以直接显示。
但是,最终的取色结果却不是这样的,很多高亮的线条会被认为是一种重要的配色。
4种颜色(rgb(54,56,52)、rgb(166,172,124)、rgb(132,136,98)、rgb(122,126,112))的相似度计算值的平均值高达162.66。
是一个较大的数值。
很显然,此时单纯通过主色判断图像是否接近纯色是不安全的做法,那有没有什么进一步突破的手段呢?
//zxx: 如果你看到这段文字,说明你现在访问是体验糟糕的垃圾盗版网站,你可以访问原文获得很好的体验:https://www.zhangxinxu.com/wordpress/?p=9982(作者张鑫旭)
三、巧用浏览器本身算法
有一种方法是4个颜色互相对比相似度,剔除那个和其他3个色值差异最大的色值,使用剩余的3个值进行平均相似度计算。
这种算法可以一定程度上提高整图相似度的判断度,但是还不够准确。
还是这张图:
其四个主色是:
可以看到,如果使用上面的算法,移除的颜色居然是占据比重最大的深色,这就有些不对了。
所以,我没有使用上面的方法,而是采用了一种更加巧妙的方法,可以增强颜色相似度的判断。
尺寸缩小绘制判别法
这个方法就是想办法缩小原图的尺寸,这样,浏览器会自动进行一定范围内的色彩融合,这样,图像中占比面积比较小的元素的颜色就可能会被冲淡,甚至不可见。
例如,拍了张星空图片,上面有很多星星,如果把这种图片缩小,则星星就会因为像素点不足,直接就看不到了。
例如,上图原图尺寸是 518px * 347px,那我就可以把Canvas画布的尺寸设置为1/13,把图像绘制上去,作为取色图。
var canvasWidth = 518 / 13; var canvasHeight = 347 / 13;
此时的取色结果是这样子的:
此时4种颜色的相似度计算值仅仅是25.26,就非常符合我们的视觉感受效果了。
说明
尺寸缩小的比例系数建议使用13, 17, 23这样的质数,可以尽可能地触发颜色的补偿计算,结果会更加准确。
四、整理了个JS给大家使用
做人做到底,送佛送到西。
很多人对原理什么的并不特别关心,关心的更多的是直接一个方法一把梭,效果就出来。
OK,为了满足这方面的需求,我特意整了个JS,可以判断图像,或者图像部分区域是不是在视觉上接近纯色。
项目地址是:https://gitee.com/zhangxinxu/image-similarity
欢迎大家关注我的gitee账户。
其中的image-similarity.js就是引用就能有结果的JS了。
使用说明
image-similarity.js的执行依赖于 color-thief.js,本项目中的 color-thief.js 做过一点自定义,使其支持部分区域取色的选择,使用原项目JS可能会没有效果。
使用示意:
<script src="./color-thief.js"></script>
<script src="./image-similarity.js"></script>
全局暴露了2个方法,一个是 imageSimilarityValue()
方法,语法如下:
imageSimilarityValue(src, bounding)
其中:
src
是任意格式的图像地址。
bounding
是图像上局部区域的尺寸设置,格式是数组,需要4个数组项,都是数值,分别表示坐标和宽高,例如 [10, 10, 300, 100]
表示判断原始图像左上角坐标是 10,10,宽高是 300×100 的矩形区域的视觉色彩是否丰富。
返回值是个Promise,通常使用示意。
imageSimilarityValue(src, bounding).then(result => {
// result
});
其中 result
是个对象,格式如下所示:
{
colors: ['rgb(0,0,0)', ...],
similarity: 0-255
}
colors
是图像限制在特定尺寸后选取的4个主要颜色,similarity
是这些颜色的平均相似度值。
第二个方法是imageSimilarity()
方法,语法如下:
imageSimilarity(src, bounding).then(similarity => {
// similarity是数值
});
这里的 similarity
是整数值,范围从0-5,分别表示相似的程度,值越小则越相似。
这是当前JS项目内置的规则:
// 0 极度相似
// 1 相似
// 2 不太相似
// 3 不相似
// 4 差异较大
imageSimilarity()
方法底层依赖的就是 imageSimilarityValue()
方法,这里的 similarity
其实就是把 imageSimilarityValue()
方法中返回的 similarity
值和 50 相处取了个整。
为什么取50?全是我自己的感觉,所以,这个相似度的阈值选择可能是不准确的,大家可以根据自己实际需求进行调整。
demo示意
做了2个demo演示页面,给大家看看效果。
首先是整体图像判断是不是视觉上接近纯色,您可以狠狠地点击这里:选择图片判断是否接近纯色demo
点击文件选择框,选择任意的图像,就可以看到判断结果了,例如下面一些结果截图:
image-similarity.js还支持判断图片局部区域的颜色是否视觉相似度比较高。
您可以狠狠地点击这里:图片部分区域颜色是否近似demo
大家可以拖拽图片上的黄色框框,可以看看对应面积内容和最后的对比结果是否接近。
下面几张图是效果示意(点击图片可以切换素材):
看起来还是有点味道的。
五、结语
本文一开头有提到“一定不是最好的方法”,原因在于,在一个图像所有像素点信息都已知的情况下,显然,基于算法,对整体的相似度进行计算是最为精准的。
然而,这是理论上的分析结果,执行层面就没这么简单了,我对算法之类的东西关注并不多,这就超出了我的能力范畴了,我觉得应该有人已经做过类似的事情了,如果有谁知道的,可以反馈下,不甚感谢。
一个东西好不好,还要看好不好上手,容不容易懂,如果从这个角度看,本文这种基于主色判断颜色相似度的方法其实也是很不错的方法。
说不定,从实践结果来看,要比纯算法计算的还要精准。
好了,就说这些,感谢大家的阅读。
欢迎转发,欢迎分享。
本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。
本文地址:https://www.zhangxinxu.com/wordpress/?p=9982
(本篇完)
- JS HEX十六进制与RGB, HSL颜色的相互转换 (0.476)
- JS实现图片相似度的判断 (0.411)
- JS实现照片图片变成黑白线条线稿 (0.128)
- 纯JS实现图像的人脸识别功能 (0.128)
- 时鲜技术:图像的像素化处理 (0.113)
- JS与条形码的生成 (0.113)
- 做了个纯前端JPG/PNG尺寸缩放+压缩的在线工具 (0.113)
- 如何用简单的Web方法实现图片的马赛克效果 (0.113)
- 图片主色获取脚本rgbaster.js小介绍小使用 (0.094)
- 3D LUT 滤镜颜色映射原理剖析与JS实现 (0.094)
- 照片位图转SVG矢量图片JS工具primitive.js等简介 (RANDOM - 0.015)
有的图片还是判断不准,不知道什么原因
以前遇到过类似的问题,就是文字加一个1像素的外发光。
一般接到这样的需求,不都是想着怎么解决提出这个需求的人么?大佬你为何还要想下去?
毕竟要靠这个手艺吃饭
可曾试过在HSV颜色空间下做事情?
想给张老师提包的第1501天
赞,文字显示区域高斯模糊 在花里胡哨的图片中效果明显吗?哈哈哈我第一想法是类似打码的虚背景 + 文字 应该会更清晰
学到了
明目张胆的发布vpn广告,不会被请喝茶吗?
谷歌会根据当前用户的特质显示对应的广告,所以,是不是你自己……
有点恐怖
关于取主色调,我发现用中位切分法切一刀后,取像素量最大的那块计算平均值就能满足了
太厉害了,我要是遇到这种需求,直接说实现不了。。。