k-鄰近手寫數字識別的java實現

最近空下來學習了簡單的圖像識別基礎,做了這個算是一個小入門吧,就當爲之後的畢設鋪鋪路了。這個原理實現起來很簡單,我用的是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

 


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章