对普通人而言,识别任意两张图片是否相似是件很容易的事儿。但是从计算机的角度来识别的话,需要先识别出图像的特征,然后才能进行比对。在图像识别中,颜色特征是最为常见的。每张图像都可以转化成颜色分布直方图,如果两张图片的直方图很接近,就可以认为它们很相似。这有点类似于判断文本的相似程度。
图像比较
先来比对两张图片,一张是原图另一张是经过直方图均衡化之后的图片。
原图和直方图均衡化比较.png
二者的相关性因子是-0.056,这说明两张图的相似度很低。在上一篇文章 图像直方图与直方图均衡化 中,已经解释过什么是直方图均衡化。通过直方图均衡化后,两张图片确实是不同的,可以从下图看出。
直方图均值化.png我们来看看如何使用直方图比较。
final Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.test_hist);
image0.setImageBitmap(bitmap);
CV4JImage cv4jImage = new CV4JImage(bitmap);
ImageProcessor imageProcessor = cv4jImage.convert2Gray().getProcessor();
int[][] source = null;
int[][] target = null;
CalcHistogram calcHistogram = new CalcHistogram();
int bins = 180;
source = new int[imageProcessor.getChannels()][bins];
calcHistogram.calcHSVHist(imageProcessor,bins,source,true);
if (imageProcessor instanceof ByteProcessor) {
EqualHist equalHist = new EqualHist();
equalHist.equalize((ByteProcessor) imageProcessor);
image1.setImageBitmap(cv4jImage.getProcessor().getImage().toBitmap());
target = new int[imageProcessor.getChannels()][bins];
calcHistogram.calcHSVHist(imageProcessor,bins,target,true);
}
CompareHist compareHist = new CompareHist();
StringBuilder sb = new StringBuilder();
sb.append("巴氏距离:").append(compareHist.bhattacharyya(source[0],target[0])).append("\r\n")
.append("协方差:").append(compareHist.covariance(source[0],target[0])).append("\r\n")
.append("相关性因子:").append(compareHist.ncc(source[0],target[0]));
result.setText(sb.toString());
其中,CompareHist 这个类是用于直方图比较的类。
然后,再来比较两张完全一致的图片,可以看到他们的相关性因子是1.0,表示两者完全一致。
两张相同的图比较.png
最后,来比对两张完全不同的图片,可以看到它们的相关性因子是0.037,表面二者几乎没有什么相似之处。
两张完全不同的图比较.png
直方图比较是识别图像相似度的算法之一,也是最简单的算法。当然,还有很多其他的算法啦。
直方图反向投影
所谓反向投影就是首先计算某一特征的直方图模型,然后使用模型去寻找图像中存在的该特征。
反向投影的算法.png
其中,b(xi)表示在位置xi上像素对应的直方图第b(xi)个bin,直方图共m个bin,qu表示第u个bin的值。
下图是皇马的拉莫斯在2017年欧冠决赛时的图片。直方图反向投影可以根据球员球衣中的某一块区域,来查找图片中拉莫斯所穿的球衣。
直方图反向投影.png上图是不是很酷炫?来看看是怎样使用反向投影的,需要先计算出样本的直方图,然后使用模型去寻找原图中存在的该特征。反向投影的结果包含了:以每个输入图像像素点为起点的直方图对比结果。在这里是一个单通道的浮点型图像。
Resources res = getResources();
Bitmap bitmap1 = BitmapFactory.decodeResource(res, R.drawable.test_project_target);
targetImage.setImageBitmap(bitmap1);
Bitmap bitmap2 = BitmapFactory.decodeResource(res, R.drawable.test_project_sample);
sampleImage.setImageBitmap(bitmap2);
CV4JImage cv4jImage = new CV4JImage(bitmap1);
ColorProcessor colorProcessor = (ColorProcessor)cv4jImage.getProcessor();
BackProjectHist backProjectHist = new BackProjectHist();
int w = colorProcessor.getWidth();
int h = colorProcessor.getHeight();
CV4JImage resultCV4JImage = new CV4JImage(w,h);
ByteProcessor byteProcessor = (ByteProcessor)resultCV4JImage.getProcessor();
// sample
CV4JImage sample = new CV4JImage(bitmap2);
ColorProcessor sampleProcessor = (ColorProcessor)sample.getProcessor();
CalcHistogram calcHistogram = new CalcHistogram();
int bins = 32;
int[][] hist = new int[sampleProcessor.getChannels()][bins];
calcHistogram.calcHSVHist(sampleProcessor,bins,hist,true);
byte[][] source = new byte[][]{colorProcessor.getRed(),colorProcessor.getGreen(),colorProcessor.getBlue()};
byte[][] target = new byte[3][w*h];
Tools.rgb2hsv(source,target);
ByteProcessor hsvByteProcessor = new ByteProcessor(target[0],w,h);
backProjectHist.backProjection(hsvByteProcessor,byteProcessor,hist[0],new int[]{0,180});
result.setImageBitmap(byteProcessor.getImage().toBitmap());
其中,BackProjectHist 这个类是用于直方图反向投影的类。
总结
直方图比较和直方图反向投影的算法都已经包含在cv4j中。
cv4j 是gloomyfish和我一起开发的图像处理库,纯java实现,目前还处于早期的版本。这次我们填完直方图的坑以后,终于把它发布到jcenter上了。
单独下载cv4j
compile 'com.cv4j:cv4j:0.1.0'
也可以下载rxcv4j,它是使用 RxJava2.x 进行的封装,如果下载该模块的话无需再下载cv4j。
compile 'com.cv4j:rxcv4j:0.1.0'
目前已经实现的功能:
cv4j.png
下周我们开始做模板匹配的算法。
如果您想看该系列先前的文章可以访问下面的文集:
http://www.jianshu.com/nb/10401400
网友评论
欢迎订阅《Tony沈哲的独家号》https://toutiao.io/subjects/5688