使用C++和Qt的涂鸦程序(三)

本文介绍了使用C++和Qt开发涂鸦程序时,针对图像相似度计算的算法选择与优化。作者分析了SSIM(结构相似性)和PSNR(峰值信噪比)在不同场景下的应用,并提出针对涂鸦程序的特别修改版PSNR算法,结合CIE Lab颜色空间计算颜色距离,以更好地考虑颜色和空间信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用C++和Qt的涂鸦程序(一)
使用C++和Qt的涂鸦程序(二)
源代码的github
https://github.com/drink-crow/graffiti
这一篇用来记录程序中用到的算法,实现以及修改的细节。
按照思路,图像相似度的计算是这个小程序里的核心之一。图像相似度的算法有很多,比如以颜色信息区分的颜色直方图,峰值信号和背景噪音比的PSNR,结构详细性的SSIM等都是非常常用的图像详细度的算法。最近还有许多机器学习识别的方法。

项目需求

虽然图像相似度的算法有很多,但其中大多数都不大适合这个项目,因为这些算法往往是为原图与计算完成的效果图考虑的。而程序开始涂鸦之时,涂鸦图像与原图的差距非常大,一些算法在这种情况下不是特别的可靠,比如用余弦相似度计算的方法。
计算的方法要同时考虑颜色信息和空间信息。目前主流的算法中为了计算简便或者其它原因大多都没有同时考虑到其中两种信息。比如颜色直方图只考虑了颜色信息而完全没有空间信息,显然不适合。SSIM的计算中也只考虑到对比度,而且计算时彩色图像会转成灰度图像,颜色失真会非常的大。

SSIM(结构相似性)

SSIM的计算方法网上有很多,这里不在赘述。简略的介绍可以查看下维基百科上
https://en.wikipedia.org/wiki/Structural_similarity
程序中关于SSIM的实现可以查看pictureCompare.h 中的函数

double getSSIM_CPU(vector<vector<int>> s1, vector<vector<int>> s2, int h, int w, int bit, double pixelRadius);

SSIM的实现在代码中虽然是最开始编写的部分,但是算法本身对颜色信息的支持不大好,就没有进行太多的优化。
在SSIM的实现中值得注意的是

 //划分窗口的数量
 int M = ceil((double)height/convolutionWindowSize);
 int N = ceil((double)width / convolutionWindowSize);

根据给定的像素半径来对整个图像进行了划分。一个图像分为了MxN个格子。这种做法在许多介绍SSIM的地方也有提到。因为在一定距离下人眼观察画面,一次只能集中在一块地方。按照生物习惯去调节算法,得出来的效果更接近于直觉
而每个窗口大小是多少比较合适,目前没找到理论上的依据,经过多次的实验。这里建议是像素半径在15以上比较好,如果输入图像的分辨率比较大的话,可以设置得更大。

在程序中可以看到、
错误颜色的涂鸦

抛去颜色可以大致的看到轮廓是相似的,但是颜色确实完全不对的。
因为在SSIM的计算过程中输入图xy 的平均值 μ x \mu_{x} μx, μ y \mu_{y} μy和标准差 σ x \sigma_{x} σx, σ y \sigma_{y} σy以及它们之间的协方差 σ x y \sigma_{xy} σxy是根据输入图像灰度化后计算的,损失了非常多的颜色信息。
如果把涂鸦图转化成灰度图来看
仅灰度的涂鸦以灰度信息来看的,还是比较符合。
上文以及提到,造成这种情况的主要原因还是SSIM的算法开发初衷是用来衡量压缩后的图片的质量的。所以对比的两个图片本身应该是非常相似的,至少色彩色调上是一致的,就以明暗度、对比度来代替本来的颜色信息,以制不适用于这个图像程序

特别修改的PSNR

上面提到SSIM经过实验可以看到仅在只有灰度信息下才能比较好的工作。而要考虑到更多的颜色信息的话,这里采用了一种特别修改的过的PSNR的算法
先来看看效果吧
正确颜色的涂鸦
这里可以看到,在随机产生的颜色点的情况下,最终涂鸦出来的图像与原图是在颜色上比较接近的。证明在本程序里是适用的。

峰值信噪比PSNR的计算非常简单。简化下就是

P S N R = 20 × l o g 10 ( M A X I M S E ) PSNR=20 \times log_{10}(\frac{MAX_{I}}{\sqrt{MSE}}) PSNR=20×log10(MSE MAXI)

其中 M A X I MAX_{I} MAXI在8位灰度模式下就是

M A X I = 2 8 − 1 = 255 MAX_{I}=2^{8}-1=255 MAXI=281=255 M S E = 1 m n ∑ i = 0 m − 1 ∑ j = 0 n − 1 [ I ( i , j ) − K ( i , j ) ] 2 MSE=\frac{1}{mn}\sum_{i=0}^{m-1}\sum_{j=0}^{n-1}[I(i,j)-K(i,j)]^{2} MSE=mn1i=0m1j=0n1[I(i,j)K(i,j)]2

MSE的计算

忽略色彩的话计算特别简单,但是这里需要多种颜色信息的话怎么做呢?
关键在与MSE计算公式中的
I ( i , j ) − K ( i , j ) I(i,j)-K(i,j) I(i,j)K(i,j)这一部分在图像处理中可以理解为像素点间颜色的距离,在灰度模式下就是明暗差别。
在彩色信息下如何衡量颜色距离是一个关键的问题。其中的关键点在于颜色空间
衡量颜色距离的比较好的色彩空间是CIE Lab颜色空间,关于Lab颜色空间的描述网上有许多,这里不在大费笔墨。CIE Lab颜色空间的一大特点是数值上变化带来颜色变化和感官上颜色变化是一致的,用来衡量色彩距离是再好不过。
RGB颜色转到Lab颜色,再计算颜色距离是个复杂的过程,具体可以参考
https://www.compuphase.com/cmetric.htm
在程序代码是

//默认是小端模式,0xAARRGGBB,则读出来第一个则是BB
double getLABColorDistance(const unsigned char * c1,const unsigned char * c2)
{
long rmean = ((c1[2] + c2[2]) / 2);
long r = (c1[2] - c2[2]);
long g = (c1[1] - c2[1]);
long b = (c1[0] - c2[0]);

return sqrt((((512 + rmean)*r*r) >> 8) + 4 * g*g + (((767 - rmean)*b*b) >> 8));
}

Qt里图像的数据流格式0xAARRGGBB,读出来的第一位是BB,程序里也延续这个做法比较方便。这个颜色计算是每个像素点都要用到的,以这样的函数调用方式开销比较大。因此改为内联的方式以提高速度

inline double getLABColorDistance(const unsigned char *c1, const unsigned char *c2)
{
 return sqrt((((512 + ((c1[2] + c2[2]) / 2))*(c1[2] - c2[2])*(c1[2] - c2[2])) >> 8) + 4 * (c1[1] - c2[1])*(c1[1] - c2[1]) + (((767 - ((c1[2] + c2[2]) / 2))*(c1[0] - c2[0])*(c1[0] - c2[0])) >> 8));
}

压缩后不方便阅读,但是计算的方法是一样的。
这样计算时就考虑到颜色信息,也考虑到空间信息了。

M A X I MAX_{I} MAXI的取值

还有另外一个问题是 M A X I MAX_{I} MAXI,在单通道的灰度模式下是颜色数量,在采用了Lab颜色距离下忍采用颜色数量并不合理,RGB的颜色数量已经是 2 24 − 1 = 16777215 2^{24}-1=16777215 2241=16777215种,一个这个数字计算下,即使两幅图像每一个像素都达到了最大差距,PSNR也能轻松破百。而PSNR的值高于40就是说明图像非常接近原始图像了,显然不合适。Lab颜色空间的种类比RGB、CMYK都要大得多,更加不合适
(笔者高等数学学得不太好,接下来这段纯属胡扯)
在灰度模式下,颜色是一维线性的, M A X I MAX_{I} MAXI取颜色数量,仿照其中的物理意义将其扩展到Lab颜色空间, M A X I MAX_{I} MAXI取RGB颜色转化成Lab后所能囊括的最大的颜色空间的的体积是个球体,其投影-圆的面积的特征长度直径D就是 M A X I MAX_{I} MAXI的取值,约等于
M A X I ≈ 2113 MAX_{I}\approx2113 MAXI2113
虽然是纯属臆测的取值方法,但是实际运用上还是能得出大致正确的结果
这部分如果您有更好的方法,请在评论区留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值