最近空下来学习了简单的图像识别基础,做了这个算是一个小入门吧,就当为之后的毕设铺铺路了。这个原理实现起来很简单,我用的是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