图像相似度算法的C#实现及测评

 

 

近日逛博客的时候偶然发现了一个有关图片相似度的Python算法实现。想着很有意思便搬到C#上来了,给大家看看。

闲言碎语

  才疏学浅,只把计算图像相似度的一个基本算法的基本实现方式给罗列了出来,以至于在最后自己测评的时候也大发感慨,这个算法有点不靠谱。不管怎么样,这个算法有时候还是有用的,所以还是列出来跟大家伙一起分享分享~~

  PS:图像处理这一块博大精深,个人偶尔发现了点东西拿来分享。说的不好的地方,写得太糟的地方,诸位准备扔砖头还望淡定,淡定~~

基本知识介绍

颜色直方图

颜色直方图是在许多图像检索系统中被广泛采用的颜色特征,它所描述的是不同色彩在整幅图像中所占的比例,而并不关心每种色彩所处的空间位置,即无法描述图像中的对象或物体。颜色直方图特别适用于描述那些难以进行自动分割的图像。

灰度直方图

  灰度直方图是灰度级的函数,它表示图像中具有每种灰度级的像素的个数,反映图像中每种灰度出现的频率。灰度直方图的横坐标是灰度级,纵坐标是该灰度级出现的频率,是图像的最基本的统计特征。

  本文中即是使用灰度直方图来计算图片相似度,关于算法那一块也不赘言了,毕竟图像学图形学,直方图我是门儿都不懂,我也不准备打肿脸充胖子,只想实现一个最基本的算法,然后从最直观的角度看看这个算法的有效性,仅此而已。

算法实现

诸位看官休怪笔者囫囵吞枣,浅尝辄止的学习态度。额毕竟是因兴趣而来,于此方面并无半点基础(当然,除了知道RGB是啥玩意儿——这还幸亏当年计算机图形学的老师是个Super美女,因此多上了几节课的缘故),更谈不上半点造诣,看官莫怪莫怪,且忍住怒气,是走是留,小生不敢有半点阻拦~~

大致步骤如下:

1, 将图像转换成相同大小,以有利于计算出相像的直方图来

2, 计算转化后的灰度直方图

3, 利用XX公式,得到直方图相似度的定量度量

4, 输出这些不知道有用没用的相似度结果数据

代码实现

步骤1, 将图像转化成相同大小,我们暂且转化成256 X 256吧。

public Bitmap Resize(string imageFile, string newImageFile)

{

img = Image.FromFile(imageFile);

Bitmap imgOutput = new Bitmap(img, 256, 256);

imgOutput.Save(newImageFile, System.Drawing.Imaging.ImageFormat.Jpeg);

imgOutput.Dispose();

return (Bitmap)Image.FromFile(newImageFile);

}

  这部分代码很好懂,imageFile为原始图片的完整路径,newImageFile为强转大小后的256 X 256图片的路径,为了“赛”后可以看到我们转化出来的图片长啥样,所以我就把它保存到了本地了,以至于有了上面略显丑陋的代码。

步骤2,计算图像的直方图

public int[] GetHisogram(Bitmap img)

{

BitmapData data = img.LockBits(new System.Drawing.Rectangle( 0 , 0 , img.Width , img.Height ),ImageLockMode.ReadWrite , PixelFormat.Format24bppRgb );

int[ ] histogram = new int[ 256 ];

unsafe

{

byte* ptr = ( byte* )data.Scan0;

int remain = data.Stride - data.Width * 3;

for( int i = 0 ; i < histogram.Length ; i ++ )

histogram[ i ] = 0;

for( int i = 0 ; i < data.Height ; i ++ )

{

for( int j = 0 ; j < data.Width ; j ++ )

{

int mean = ptr[ 0 ] + ptr[ 1 ] + ptr[ 2 ];

mean /= 3;

histogram[ mean ] ++;

ptr += 3;

}

ptr += remain;

}

}

img.UnlockBits( data );

return histogram;

}

这段就是惊天地泣鬼神的灰度直方图计算方法,里面的弯弯绕还是留给诸位自己去掺和。

步骤3,计算直方图相似度度量

这一步骤的法宝在于这个:

Sim(G,S)= HowTo其中G,S为直方图,N 为颜色空间样点数

为了大家少敲两行字儿,也给出一堆乱七八糟的代码:

//计算相减后的绝对值

private float GetAbs(int firstNum, int secondNum)

{

float abs = Math.Abs((float)firstNum - (float)secondNum);

float result = Math.Max(firstNum, secondNum);

if (result == 0)

result = 1;

return abs / result;

}

//最终计算结果

public float GetResult(int[] firstNum,int[] scondNum)

{

if (firstNum.Length != scondNum.Length)

{

return 0;

}

else

{

float result = 0;

int j = firstNum.Length;

for (int i = 0; i < j; i++)

{

result += 1 - GetAbs(firstNum[i], scondNum[i]);

Console.WriteLine(i + "----" + result);

}

return result/j;

}

}

步骤4,输出

  这个……诸位爱怎么输出就怎么输出吧。直接Console也好,七彩命令行输出也罢,亦或者保存到文本文件中留作纪念啦啦,诸位“好自为之”~~

算法测评

  真对不住大家,忘了跟大家说,我也不是一个专业的算法测评人员,但是作为一个半拉子测试人员免不了手痒痒想要看看这个算法到底有多大能耐,就拿出几张图片出来验验货吧。

以下是算法测评结果。以下部分内容话带调侃,绝无恶意,开开玩笑,娱乐大众~~

路人甲

路人乙

图像相似度

恶搞点评

图像相似度算法的C#实现及测评_第1张图片

图像相似度算法的C#实现及测评_第2张图片

100%

里面什么都没有!?

恭喜你,如果你看不出来这是两张白底图片,那么你还真是小白,因为你连自家人都认不出来啊~~

图像相似度算法的C#实现及测评_第3张图片

图像相似度算法的C#实现及测评_第4张图片

100%

天下乌鸦一般黑,这个算法在这一点上立场还算坚定,表现不错~

图像相似度算法的C#实现及测评_第5张图片

图像相似度算法的C#实现及测评_第6张图片

100%

碰到Win7也不动心,意志坚定地给出了100%的正确答案。

这算法比我意志坚定多了,我可是win7刚出来个7000就装了,还一直用到现在,不过确实好用~~

图像相似度算法的C#实现及测评_第7张图片

图像相似度算法的C#实现及测评_第8张图片

88.84%

明明很不一样的“我”跟“你”摆在那里,怎么就相似度这么高咧??

难道,“我”,“你”都认不出来??

哦,我忘了,这两张图片的大背景是一样的,难怪……

图像相似度算法的C#实现及测评_第9张图片

图像相似度算法的C#实现及测评_第10张图片

16.08%

MS跟Apple这么水火不相容?

【均使用默认桌面~~】

图像相似度算法的C#实现及测评_第11张图片

图像相似度算法的C#实现及测评_第12张图片

50.64%

终于了解了Jack跟Rose不能在一起的真正原因:

不是爱的不够深,也不是泰坦尼克号沉了,用老妈的话说“没有‘夫妻’相”

—— 还是老妈这个过来人老道~~

图像相似度算法的C#实现及测评_第13张图片

图像相似度算法的C#实现及测评_第14张图片

99.21%

哇,太不可思议了,竟然是这样。

这算法这样“黑”“白”不分??

我得向Jack跟Rose的忠实Fans道歉了,上面的话是一时失言~~祝他们俩白头偕老,下辈子千万别做船了,坐船也不出海,出海也不去北极,……

  经过我略显玩世不恭的测评活动,说实话,我对这个算法是相当的失望,尤其是最后一次对比中的结果,目前情绪低落中。这倒不是说这算法的一无是处,应该是我或者某些前辈用错了地方,个人觉得算法使用的局限性太大,也或许是我的期望值太高了吧。

后记

  开始看到这玩意儿的时候觉得这玩意儿很简单啊,可是一想不对劲,没有这么容易的事情,要不Google,MS这些大牛们做了这么久还没有像样的玩意儿出来。果不其然,为了多了解一点相关的内容,我不得不Google了一下,觉得那些术语完全不知所云,看不懂啊;看来我得祭出我一般不使用的大杀器了——百度一搜。嘿,还真找出来了一堆东西,比Google上面的看起来容易多了,可是打开链接进去瞅瞅,发现还是非我当前能力之所及。没学到东西,但是好歹还是了解了一点皮毛上的皮毛。

全文完

  诸位看官若觉得讲得没有意义,浪费了你的时间,那就权当作冷笑话听听缓解一下紧张的神经~~

参考链接:

http://blog.csdn.net/lanphaday/archive/2008/04/24/2325027.aspx

 

算法是这样的:RGB是个3维空间,分别计算两个颜色在这个空间的两个坐标点的直线距离,便是两个颜色的色差。

色差Cv=SQR(AbsR^2+AbsG^2+AbsB^2)

CS=阀值

CV<=CS为相似
CV>CS为不相似

获得两个RGB颜色的色差。
CV=ColorValueComp(RGB(255, 252, 255), RGB(255, 255, 252))

Type tpColorRGB
   coR As Byte
   coG As Byte
   coB As Byte
End Type

Public Function ColorValueComp(ByVal pColorA As Long, ByVal pColorB As Long) As Long
   '获得两个Long类型颜色的色差。
   Dim tOutValue As Long
   
   Dim tColorA As tpColorRGB
   Dim tColorB As tpColorRGB
   
   tColorA = ColorRGBGetByValue(pColorA)
   tColorB = ColorRGBGetByValue(pColorB)
   
   tOutValue = ColorRGBComp(tColorA, tColorB)
   
   ColorValueComp = tOutValue
End Function

Public Function ColorRGBGetByValue(ByVal pColorValue As Long) As tpColorRGB
   '从一个Long类型的颜色数据获得一个ColorRGB类型。
   
   Dim tOutColor As tpColorRGB
   
   With tOutColor
     
     .coR = pColorValue Mod 2 ^ 8
     .coG = (pColorValue \ 2 ^ 8) Mod 2 ^ 8
     .coB = (pColorValue \ 2 ^ 16) Mod 2 ^ 8
     
   End With
   
   ColorRGBGetByValue = tOutColor
End Function

Public Function ColorRGBComp(ByRef pColorA As tpColorRGB, ByRef pColorB As tpColorRGB) As Long
   '获得两个ColorRGB的色差。
   Dim tOutValue As Long
   
   Dim tAbsR As Long
   Dim tAbsG As Long
   Dim tAbsB As Long
     
   tAbsR = Abs(CLng(pColorA.coR) - CLng(pColorB.coR))
   tAbsG = Abs(CLng(pColorA.coG) - CLng(pColorB.coG))
   tAbsB = Abs(CLng(pColorA.coB) - CLng(pColorB.coB))
   
   tOutValue = Sqr(tAbsR ^ 2 + tAbsG ^ 2 + tAbsB ^ 2)
   
   ColorRGBComp = tOutValue
End Function

 

用法
CS=ColorValueIsBorder(RGB(255, 252, 255), RGB(255, 255, 252), 4)
返回一个布尔值。

Public Function ColorValueIsBorder(ByVal pColorA As Long, ByVal pColorB As Long, ByVal pValve As Long)
   '在容差pValve的范围内,比较两个颜色是否近似。
   Dim tOutValue As Boolean
   Dim tCompValue As Long
   tCompValue = ColorValueComp(pColorA, pColorB)
   tOutValue = tCompValue <= pValve
   ColorValueIsBorder = tOutValue
End Function

容差pValve的最大取值是442(RGB(0,0,0)与RGB(255,255,255)的色差)

如果你要用来处理图象,上述函数速度并不理想,因为VB做开方计算很浪费时间。
如果你一定要用VB写这个代码,我有一个办法是缓冲算法。也就是说,定义一个大约17MB的表,包含F(0,0,0)到F(255,255,255)所有色差的计算好的真值。然后用表T(AbsR,AbsG,AbsB)来代替F(AbsR,AbsG,AbsB),可以达到相当快的速度。当然,这个办法有点极端。

我这个算法并不仅仅用来匹配颜色,它可以匹配任何东西。只要是存在三个因素的量都可以这样匹配。
比如你用自动化设备为新摘的橘子分类,根据摄相头拍摄橘子判断成熟度(颜色中的R,G两个分量)、个头L来区分。这样就构成一个Cr、Cg、L三个分量组成的模型。
先“训练”识别器,取得许多已知类别的橘子的RGL值存储起来,并且与这个值对应一个类别代码T。你“教”它的越多,它识别越准。
然后你让机器自动拍摄橘子的RGL,与“训练”中已知的诸多RGL比较(算法与上面的RGB比较一样)。在一定阀值内找到最接近的一个RGL,取得它对应的分类代码T,这样这个橘子就被机器推落到属于T的流水线里。
用类似算法可以比较准确地判断某一张图片是否是黄色图片,只要查找3D直方图中某几个区域的像素积以及他们所占图片面积比例就可以了。加入模式识别技术甚至可以识别出它是否露点,以及漏了几点。按照这个原理,可以编写出“杀黄软件”(不说了,再说下去估计男同胞要哭了)。

HSV并不能简化你对比图象的计算,而且还把问题复杂化。因为色相模型不再是一个三维笛卡尔坐标系,而是一个圆桶状的色彩空间。

如果你觉得RGB不理想,还可以用YUV来匹配。匹配的方法大致与比较RGB一样。比较前先要转换RGB为YUV:

Y是亮度;Y和V分别是与绿色的色差。

RGB转YUV的变换矩阵是:

  0.299; -0.148; -0.615
  0.587; -0.289; -0.515
  0.144;  0.437; -0.100

YUV转RGB变换矩阵是:

  1    ;  1    ;  1
  0    ;  0.395;  2.032
  1.140; -0.581;  0

最后还要提醒你。如果你想编写魔术棒,单纯查找照片上的所有匹配色并不能实现这个效果。需要用到一个填充算法,具体写起来有点麻烦,简单地形容它就是:假设从一个点开始“燃烧”,凡是与正在“燃烧”的点接近的匹配点都在下一轮被点燃,一直烧到所有燃烧点“熄灭”。

再说一下YUV的变换矩阵用法。

RGB转YUV的变换矩阵: 

  0.299; -0.148; -0.615 
  0.587; -0.289; -0.515 
  0.144;  0.437; -0.100 

公式:

Y=0.299*R+0.587*G+0.144*B
U=-0.148*R+-0.289*G+0.437*B
V=-0.615*R+-0.515*G+-0.100*B

YUV转RGB变换矩阵是: 

  1    ;  1    ;  1 
  0    ;  0.395;  2.032 
  1.140; -0.581;  0 

公式:

R=1*Y+0*U+1.140*V
G=1*Y+0.395*U+-0.581*V
B=1*Y+2.032*U+0*V

 

 

你可能感兴趣的:(C++,windows)