影象處理之基於NCC模板匹配識別
阿新 • • 發佈:2019-02-11
一:基本原理
NCC是一種基於統計學計算兩組樣本資料相關性的演算法,其取值範圍為[-1, 1]之間,而對影象來說,每個畫素點都可以看出是RGB數值,這樣整幅影象就可以看成是一個樣本資料的集合,如果它有一個子集與另外一個樣本資料相互匹配則它的ncc值為1,表示相關性很高,如果是-1則表示完全不相關,基於這個原理,實現影象基於模板匹配識別演算法,其中第一步就是要歸一化資料,數學公式如下:
二:實現步驟
(1) 獲取模板畫素並計算均值與標準方差、畫素與均值diff資料樣本
(2) 根據模板大小,在目標影象上從左到右,從上到下移動視窗,計
算每移動一個畫素之後視窗內畫素與模板畫素的ncc值,與閾值比較,大於
閾值則記錄位置
(3) 根據得到位置資訊,使用紅色矩形標記出模板匹配識別結果。
(4) UI顯示結果
三:程式設計實現
基於JAVA語言完成了整個演算法程式設計實現與演示,其中第一步的程式碼如下:
第二步的實現程式碼如下:int tw = template.getWidth(); int th = template.getHeight(); int[] tpixels = new int[tw * th]; getRGB(template, 0, 0, tw, th, tpixels); for(int i=0; i<tpixels.length; i++) { tpixels[i] = (tpixels[i] >> 16) & 0xff; } double[] meansdev = getPixelsMeansAndDev(tpixels); double[] tDiff = calculateDiff(tpixels, meansdev[0]); int raidus_width = tw / 2; int raidus_height = th / 2;
第三步的實現程式碼如下:int[] windowPixels = new int[tw * th]; Arrays.fill(windowPixels, 0); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { // calculate the means and dev for each window if(row < raidus_height || (row + raidus_height) >= height) continue; if(col < raidus_width || (col + raidus_width) >= width) continue; int wrow = 0; Arrays.fill(windowPixels, 0); for(int subrow = -raidus_height; subrow <= raidus_height; subrow++ ) { int wcol = 0; for(int subcol = -raidus_width; subcol <= raidus_width; subcol++ ) { if(wrow >= th || wcol >= tw) { continue; } windowPixels[wrow * tw + wcol] = getPixelValue(width, col + subcol, row + subrow, inPixels); wcol++; } wrow++; } // calculate the ncc double[] _meansDev = getPixelsMeansAndDev(windowPixels); double[] diff = calculateDiff(windowPixels, _meansDev[0]); double ncc = calculateNcc(tDiff, diff, _meansDev[1], meansdev[1]); if(ncc > threhold) { Point mpoint = new Point(); mpoint.x = col; mpoint.y = row; points.add(mpoint); } } }
// draw matched template on target image according position
setRGB( dest, 0, 0, width, height, inPixels );
Graphics2D g2d = dest.createGraphics();
g2d.setPaint(Color.RED);
g2d.setStroke(new BasicStroke(4));
for(Point p : points)
{
g2d.drawRect(p.x - raidus_width, p.y - raidus_height, tw, th);
}
其中第二步用到的計算NCC的方法實現如下: private double calculateNcc(double[] tDiff, double[] diff, double dev1, double dev2) {
// TODO Auto-generated method stub
double sum = 0.0d;
double count = diff.length;
for(int i=0; i<diff.length; i++)
{
sum += ((tDiff[i] * diff[i])/(dev1 * dev2));
}
return (sum / count);
}
UI部分完整原始碼如下:package com.gloomyfish.image.templae.match;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class DemoUI extends JComponent {
/**
*
*/
private static final long serialVersionUID = 1L;
private BufferedImage targetImage;
private BufferedImage template;
public DemoUI()
{
super();
java.net.URL imageURL = this.getClass().getResource("words.png");
java.net.URL templateURL = this.getClass().getResource("template.png");
try {
template = ImageIO.read(templateURL);
targetImage = ImageIO.read(imageURL);
} catch (IOException e) {
e.printStackTrace();
}
}
public void setTarget(BufferedImage target) {
this.targetImage = target;
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
if(targetImage != null) {
g2.drawImage(targetImage, 10, 10, targetImage.getWidth(), targetImage.getHeight(), null);
}
if(template != null) {
g2.drawImage(template, 20+targetImage.getWidth(), 10, template.getWidth(), template.getHeight(), null);
}
}
public static void main(String[] args) {
JFrame f = new JFrame("模板匹配與識別");
JButton okBtn = new JButton("匹配");
final DemoUI ui = new DemoUI();
okBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ui.process();
}
});
JPanel btnPanel = new JPanel();
btnPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
btnPanel.add(okBtn);
f.getContentPane().add(btnPanel, BorderLayout.SOUTH);
f.getContentPane().add(ui, BorderLayout.CENTER);
f.setSize(500, 500);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
protected void process() {
NccTemplateMatchAlg algo = new NccTemplateMatchAlg(template);
targetImage = algo.filter(targetImage, null);
this.repaint();
}
}
四:程式執行效果如下
其中左邊是目標影象、右邊為模板影象
PS:部落格從10月份開始每月都有多篇相關影象處理文章更新
歡迎大家繼續關注