我们来用JAVA制作一个进行图像识别的小程序.输入两个图像路径,就可以进行比对,然后输出相似度
接下来是制作过程
首先制作界面GUI
制作完成后效果如下
接下来开始实现功能 在点击对比按钮后
先从两个输入框中获取两个图像路径 载入内存中 代码如下
protected void CompareImage() {
try {
BufferedImage bufferedImage1=ImageIO.read(new File(image1_textField.getText()));
BufferedImage bufferedImage2=ImageIO.read(new File(image2_textField.getText()));
//获得相似度
float Similarity=CompareImageTool.CompareImage(bufferedImage1,bufferedImage2);
//在标签上显示
label.setText("图片相似度:"+Similarity);
} catch (IOException e) {
// TODO Auto-generated catch block
Info_Label.setText("读取图片出错!");
e.printStackTrace();
}
}
我们这边将读取进来的图片转化为两个BufferedImage对象,发送到CompareImageTool.CompareImage方法中进行比较
接下来我们完成这个对比的方法
我们这里要求的是图片相似度,算法有很多种,我们这边采用这样一种方法。
首先扫描图片的每个像素,获取所有RGB值,相同的RGB值放在一起。最后得到的是一个保存该图片所有RGB值出现的次数的HashMap
代码如下
private static HashMap GetRGBMap(BufferedImage bufferedImage) {
HashMap map=new HashMap();
for(int x=0;x<bufferedImage.getWidth();x++)
{
for(int y=0;y<bufferedImage.getHeight();y++)
{
double RGBValue=bufferedImage.getRGB(x, y);
//如果没有保存该色值,存入
if(map.get(RGBValue)==null)
{
map.put(RGBValue, 1);
}
//将该色值出现次数增加一次
else
{
int Times=(int) map.get(RGBValue);
Times+=1;
map.put(RGBValue, Times);
}
}
}
return map;
}
那么接下来怎么办呢?怎么计算相似度呢?
这边就要用到一点数学知识,我们可以将这个得到的HashMap视为一个特殊的向量,向量维数视作所有出现过的RGB值,分量的值则是出现的次数。
这个时候就巧妙的将两张图片,转化为两个含有该图像特征的向量
这个时候就好办了,大家肯定学习过这么一条公式 a*b=|a|*|b|*cos<a,b>
也就是向量点积,等于向量的模相乘再乘上向量夹角。那么这个夹角,就可以视为两条向量之间的相似程度。
当然,为了使我们得到的cos<a,b>的值卡在0,1之间,还需要对向量进行归一化
代码如下
private static HashMap VectorNormalizing(HashMap RGBMap) {
//求图片特征向量1的模长
double ModulaLength=0;
for(Object i:RGBMap.keySet())
{ //先将所有平方相加
ModulaLength+=Math.pow((double)(int)RGBMap.get(i), 2);
}
//求平方根
ModulaLength=Math.sqrt(ModulaLength);
//将图片特征向量1标准化
for(Object i:RGBMap.keySet())
{
double a=(double)(int) RGBMap.get(i);
a=a/ModulaLength;
RGBMap.put(i, a);
}
return RGBMap;
}
接下来就是最后一步了!求出所需的cos<a,b>即是相似程度!
代码如下
public static float CompareImage(BufferedImage bufferedImage1, BufferedImage bufferedImage2) {
HashMap RGBMap1=GetRGBMap(bufferedImage1);
HashMap RGBMap2=GetRGBMap(bufferedImage2);
//相似度
float Similarity=0;
//将两个HashMap视作特征向量进行归一化
RGBMap1=VectorNormalizing(RGBMap1);
RGBMap2=VectorNormalizing(RGBMap2);
//向量点积得到相似度
for(Object i:RGBMap1.keySet())
{ double Value2;
if(RGBMap2.get(i)==null)
{Value2=0;}
else
{Value2= (double) RGBMap2.get(i);}
double Value1=(double) RGBMap1.get(i);
Similarity+=Value1*Value2;
}
return Similarity*100;
}
那么程序到这边就做好了!接下来看看效果
我们截取了两张图片,位置稍有不同
接下来运行程序进行识别
相似度88.4 可以看出大致是同一张图片
关于图像比对,其实有非常多的算法,我这个只是其中一种。大家如果认真看了应该能发现一些问题
1.我这边单纯比对的是像素的色值出现比例,那么可能出现两张图片相同色值的像素位置完全不同,却被识别成同一张的情况。不过这种情况基本也不会出现,因为每个像素的RGB值有2^24种,所以可以近似认为色值组成相同,就是同一张图片。
2.可能会有这样的情况,将一张图的全部RGB值都增加,比如将红色分量整体提升1,那么肉眼绝对看不出来区别,相似度却会被认为是0,虽然正常情况不会这样,但是为了能使该程序的容错能力更强。还可以增加颜色相似度的比较,比如
rgb(100,100,100)和rgb(110,110,110)的两个像素点可以视为颜色比较相近,而不是一棍子打死认为完全不同。
大家有兴趣可以对该程序进行改善
----------
欢迎关注我的github https://github.com/luckyCatMiao