對普通人而言,識別任意兩張圖片是否相似是件很容易的事兒。但是從計算機的角度來識別的話,需要先識別出影像的特徵,然後才能進行比對。在影像識別中,顏色特徵是最為常見的。每張影像都可以轉化成顏色分佈直方圖,如果兩張圖片的直方圖很接近,就可以認為它們很相似。這有點類似於判斷文字的相似程度。
影像比較
先來比對兩張圖片,一張是原圖另一張是經過直方圖均衡化之後的圖片。
二者的相關性因子是-0.056,這說明兩張圖的相似度很低。在上一篇文章 影像直方圖與直方圖均衡化 中,已經解釋過什麼是直方圖均衡化。通過直方圖均衡化後,兩張圖片確實是不同的,可以從下圖看出。
我們來看看如何使用直方圖比較。
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("
")
.append("協方差:").append(compareHist.covariance(source[0],target[0])).append("
")
.append("相關性因子:").append(compareHist.ncc(source[0],target[0]));
result.setText(sb.toString());複製程式碼
其中,CompareHist 這個類是用於直方圖比較的類。
然後,再來比較兩張完全一致的圖片,可以看到他們的相關性因子是1.0,表示兩者完全一致。
最後,來比對兩張完全不同的圖片,可以看到它們的相關性因子是0.037,表面二者幾乎沒有什麼相似之處。
直方圖比較是識別影像相似度的演算法之一,也是最簡單的演算法。當然,還有很多其他的演算法啦。
直方圖反向投影
所謂反向投影就是首先計算某一特徵的直方圖模型,然後使用模型去尋找影像中存在的該特徵。
其中,b(xi)表示在位置xi上畫素對應的直方圖第b(xi)個bin,直方圖共m個bin,qu表示第u個bin的值。
下圖是皇馬的拉莫斯在2017年歐冠決賽時的圖片。直方圖反向投影可以根據球員球衣中的某一塊區域,來查詢圖片中拉莫斯所穿的球衣。
上圖是不是很酷炫?來看看是怎樣使用反向投影的,需要先計算出樣本的直方圖,然後使用模型去尋找原圖中存在的該特徵。反向投影的結果包含了:以每個輸入影像畫素點為起點的直方圖對比結果。在這裡是一個單通道的浮點型影像。
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`複製程式碼
目前已經實現的功能:
下週我們開始做模板匹配的演算法。
如果您想看該系列先前的文章可以訪問下面的文集:
www.jianshu.com/nb/10401400