最近空下來學習了簡單的圖像識別基礎,做了這個算是一個小入門吧,就當爲之後的畢設鋪鋪路了。這個原理實現起來很簡單,我用的是20*20大小的圖片,主要就是將待識別圖片的400個像素的灰度值提取出來與訓練樣本進行比對,找到灰度值相減絕對值最小的樣本,即爲待識別圖片的數字值,效果如下:
接下來就一步一步的來實現這個小程序。
一、外觀設計
外觀主要是採用java的WindowBuilder,如果沒有的話可以去下一個http://www.eclipse.org/windowbuilder/download.php,具體如何安裝這裏就不做介紹了。
頁面採用一個大的Jpanel,一個頂部菜單JMenuBar操作,菜單中包含三個小菜單JMenuItem,分別爲識別,加載圖像,退出
在大的Jpanel裏面有一個小的Jpanel,用來刷新加載圖片,右側有一個JButton,實現識別功能,下方JLabel爲識別結果
下面來具體實現代碼部分
(1)頂部菜單
setTitle("數字識別");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//關閉按鈕
setResizable(false);//不能修改窗口大小
setBounds(0,0,600,600);
JMenuBar menuBar = new JMenuBar();//綁定菜單欄
setJMenuBar(menuBar);
JMenu menu = new JMenu("操作");
menuBar.add(menu);//加入操作菜單
JMenuItem importItem = new JMenuItem("加載圖片");
JMenuItem serach = new JMenuItem("識別");
JMenuItem exit = new JMenuItem("退出");
menu.add(importItem);
menu.add(serach);
menu.add(exit);
(2)主面板
contentPane = new JPanel();//加入面板
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JLabel label = new JLabel("識別結果:");
label.setBounds(10,450,97,15);
contentPane.add(label);
JLabel result = new JLabel("");
result.setBounds(90,450,97,15);
contentPane.add(result);
JButton button = new JButton("識別");
button.setBounds(450, 300, 100, 25);
contentPane.add(button);
(3)圖片面板
imagePanel viewPanel = new imagePanel();
viewPanel.setBounds(10, 10, 300, 300);//280
contentPane.add(viewPanel);//添加到主面板
(4)加載圖片
/*
* 面板,預覽加載圖片
*/
public class imagePanel extends JPanel{
private Image image;
public imagePanel() {
setLayout(null);
}
public void loadImage(BufferedImage src){
image = src.getScaledInstance(300, 300, Image.SCALE_SMOOTH);//創建縮略圖400*400 280
repaint();
}
@Override
public void paint(Graphics g) {
//如何重繪面板
if (image != null) {
//清除上次加載的圖片
g.clearRect(0, 0, getWidth(), getHeight());
g.drawImage(image, 0, 0, this);
}
}
}
(5)監聽事件
/*
* 加入點擊事件監聽器
*/
importItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("加載");
JFileChooser chooser =new JFileChooser();
int result = chooser.showDialog(null, "導入圖片");
if(result == 0){
file = chooser.getSelectedFile();
try {
BufferedImage src = ImageIO.read(file);
viewPanel.loadImage(src);//將圖片重新繪製
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
System.out.println(result);
}
});
/*
* 加載訓練數據
*/
DistinguishUtil distinguishUtil = new DistinguishUtil();
distinguishUtil.loadData(new File("traindata2.csv"));
setTitle("數字圖像識別---加載數據完成");
serach.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("識別");
String resultString = distinguishUtil.distinguish(file);
result.setText(resultString);
}
});
exit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("退出");
}
});
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按鈕");
String resultString = distinguishUtil.distinguish(file);
result.setText(resultString);
}
});
二、數據處理
(1)獲取圖片的灰度特徵值
public int[] getDigest(File targetImage) throws IOException{
BufferedImage image = ImageIO.read(targetImage);
int[] digest = new int[image.getHeight()*image.getWidth()];
image.getRGB(0, 0, image.getWidth(), image.getHeight(), digest, 0, image.getWidth());//將灰度特徵值保存進數組
//255 -(r*30 + g*59 + b*11)/100
for (int i = 0; i < digest.length; i++) {
int color = digest[i];
int r = color << 8 >>> 24;
int g = color << 16 >>> 24;
int b = color << 24 >>> 24;
int gray = 255 -(r*30 + g*59 + b*11)/100;
digest[i] = gray;
}
return digest;
}
(2)數據對象
/*
* 該類的一個對象封裝一條數據
*/
public class trainData {
private String targetString;//識別結果
private int[] digestString;//長度爲28*28的灰度特徵值
public String getTargetString() {
return targetString;
}
public void setTargetString(String targetString) {
this.targetString = targetString;
}
public int[] getDigestString() {
return digestString;
}
public void setDigestString(int[] digestString) {
this.digestString = digestString;
}
}
(3)加載訓練數據
ExcelUtil util = new ExcelUtil();
try {
for(int i = 0; i <= 1200 ; i++){
int[] dataInt = util.readRdx(i+1);
String target = util.readTarget(i+1);
trainData traindata = new trainData();
traindata.setDigestString(dataInt);
traindata.setTargetString(target);
datas.add(traindata);
}
} catch (EncryptedDocumentException e) {
e.printStackTrace();
} catch (InvalidFormatException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(int i = 0; i <= 1200 ; i++){
int[] dataInt = util.readRdx(i+1);
String target = util.readTarget(i+1);
trainData traindata = new trainData();
traindata.setDigestString(dataInt);
traindata.setTargetString(target);
datas.add(traindata);
}
} catch (EncryptedDocumentException e) {
e.printStackTrace();
} catch (InvalidFormatException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
(4)比對數據
public String distinguish(File targetImage) {
int[] digest;
int index = 0;
try {
digest = getDigest(targetImage);
if (digest != null) {
int minDif = getDif(digest, datas.get(0).getDigestString());//與第一個訓練數據集進行比對
for (int i = 1; i < datas.size(); i++) {
int dif = getDif(digest, datas.get(i).getDigestString());
if(dif < minDif){
minDif = dif;
index = i;
}
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return datas.get(index).getTargetString();
}
//獲取特徵值差值
public int getDif(int[] imageDigest,int[] trainingDigest){
int dif = 0;
for (int i = 0; i < trainingDigest.length; i++) {
dif += Math.abs((imageDigest[i]-trainingDigest[i]));
}
return dif;
}
這裏要說明一下,訓練數據分爲兩部分,一部分就是5000張手寫的數字圖片,另一部分是5000張圖片的灰度值,由第一部分提取出來的csv格式的數據,每條數據包含401個內容,分別爲本條數據代表的數字以及400個像素的灰度值,可以用Excel打開如下圖
歡迎大家隨時批評指正,一起交流學習,可以加我q 504334357