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

 


 

 

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