一、KNN手写识别原理

在下图中,要判断绿色圆归属为哪个类(红三角形还是蓝四边形)
如果K=3,因为红三角形占比例为2/3,所以绿色圆归属为红色三角形;
如果K=5,因为蓝四边形比例为3/5,所以绿色圆被赋予蓝色四方形类。

那么如何计算上图中各个图形距离绿圆的距离呢?KNN使用的是欧氏距离原理:(可以把图形当作一个点)计算两个点间的距离


这个是二维的,如果是三维或者多维的欧氏距离:
两个n维向量a(x1,x2,…,xn)与b(x1,x2,…,xn)间的欧式距离

二、基本思路

  1. 数字保存的画布大小用32×32的二维数组表示。
  • 如下图中一个大方格相当于10×10的像素,一个大方格就是32×32的二维数组中的一个元素
  • 当画笔画到这个方格的区域时,就将这个方格以及周围的8个方格(上下左右,斜线)都标记为1(一开始我只标记一个,效果很差,要达到下图2效果),其他没有划线部分初始化为0

  1. 计算当前手写的和样本的欧式距离,按距离升序排序,取前K个。
  • 从前K个排序好的距离中,取出出现频率最高的那个数字
  • 为了弥补KNN的误差,可以把与样本匹配到的数字(可能结果)都输出来,以供用户选择
  1. 除以上的思路外,还可以自己添加训练集,提高识别准确率,增加一个保存的功能
  2. 思维导图

    借鉴博客:https://blog.csdn.net/weixin_42621338/article/details/81989035
    看效果(默认识别功能,可选择,写完数字后点击鼠标即可实现识别)

三、附录代码

  • 目前代码还不是很完善,没有加入点击判断(是否写有数字)
  • 没有橡皮擦~ 简陋版就先这样,慢慢优化
    用到两个文件操作:写入、读取。
    public void out() throws IOException{//创建文件名为:保存为数字+当前时间File ins=new File("D:/learning/mydemo/Javaworkspace/rect/src/com/手写识别/"+number+'-'+System.currentTimeMillis());//写入文件FileWriter write=new FileWriter(ins);for(int i=0;i<Area.arraySize;i++){for(int j=0;j<Area.arraySize;j++){write.write(draw[i][j]+"");}}write.flush();write.close();}

public void in() throws IOException{File file =new File ("D:/learning/mydemo/Javaworkspace/rect/src/com/手写识别/");//得到该目录下的所有文件名String[] filelist=file.list();for(int i=0;i<filelist.length;i++){FileReader readfile=new FileReader("D:/learning/mydemo/Javaworkspace/rect/src/com/手写识别/"+filelist[i]);//读取文件,输入流读取返回intint b=readfile.read();for(int k=0;k<Area.arraySize;k++){for(int j=0;j<Area.arraySize;j++){//文章末为-1if(b!=-1){//从文件名中取出的数存入数组getfile[k][j]=Integer.valueOf((char)b+"");//读取下一个b=readfile.read();}}}//关闭文件readfile.close();//提取文件名首数字int a =Integer.valueOf(filelist[i].substring(0, 1));//  System.out.println("本次检查数字:"+a);//调用计算距离公式,得到的距离传给距离数组setdistance(getfile,draw,a);}}

显示窗体:

package com.Digital;import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.TextArea;import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;public class ShowUI {/*** 主函数* @param argc*/public static void main(String [] argc){ShowUI s=new ShowUI();s.show();}/*** 显示窗体方法*/public void show(){/** 创建窗体步骤:* 1.设置窗体名字* 2.设置窗体大小* 3.设置窗体关闭方法* 4.设置窗体居中显示* 5.设置窗体布局* 6.设置窗体可见*/JFrame jf=new JFrame("手写识别");jf.setSize(400, 480);jf.setDefaultCloseOperation(3);jf.setLocationRelativeTo(null);jf.setLayout(new FlowLayout());//流式布局//添加功能按钮JButton iden=new JButton("识别");JButton train=new JButton("训练");jf.add(iden);jf.add(train);//下拉框String []number={"0","1","2","3","4","5","6","7","8","9"};JComboBox<String> num=new JComboBox<String>(number);jf.add(num);//写字识别处JPanel jpw=new JPanel();jpw.setPreferredSize(new Dimension(Area.Size, Area.Size));jpw.setBackground(Color.white);jf.add(jpw);//显示识别结果,用TextArea(自带滚动条),如果是JTextArea需要自行绑定滚动条TextArea jps=new TextArea(2,30);jps.setBackground(Color.white);jf.add(jps);jf.setVisible(true);//获取JPanel上的画笔Graphics g=jpw.getGraphics();//新建监听器,下面在各个组件上添加Listener l=new Listener(g, jpw,jps);iden.addActionListener(l);train.addActionListener(l);num.addItemListener(l);jpw.addMouseListener(l);jpw.addMouseMotionListener(l);}/*** 基本设计,画布大小* @author mo**/public interface Area{int Size=320;int arraySize=32;int K=3;}
}

监听器:

package com.Digital;import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JTextArea;import com.Digital.ShowUI.Area;
import com.MyArrayList.MyArrayList;public class Listener implements ActionListener,MouseMotionListener,MouseListener,ItemListener {private int [][]draw=new int [Area.arraySize][Area.arraySize];//保存每一个位置的二维数组private int [][]getfile=new int[Area.arraySize][Area.arraySize];private int []count=new int[10];private MyArrayList<Distance> distance=new MyArrayList<Distance>();private Graphics2D g;private int x1,y1;private JPanel jp;private TextArea jt;private String name="识别";private int number;/*** 二维数组的初始化*/public void init(){for(int i=0;i<Area.arraySize;i++){for(int j=0;j<Area.arraySize;j++){draw[i][j]=0;}}int []c={0,0,0,0,0,0,0,0,0,0};count=c;distance.clear();}/*** 构造传参* 画笔+画板* @param g* @param jp*/public Listener(Graphics g,JPanel jp,TextArea jt){this.g=(Graphics2D) g;this.g.setStroke(new BasicStroke(10.0f));this.jp=jp;this.jt=jt;}/*** 选择要识别的数字*/public void itemStateChanged(ItemEvent e){number=Integer.parseInt((String)e.getItem());System.out.println(number);}/*** 选择模式*/public void actionPerformed(ActionEvent e){if(e.getActionCommand().equals("识别"))name="识别";else if(e.getActionCommand().equals("训练")){name="训练";}}/*** 画曲线*/public void mouseDragged(MouseEvent e){g.drawLine(x1, y1, e.getX(), e.getY());//求二维数组下标int index_x=x1/10;int index_y=y1/10;//用四舍五入的方法判断是落在哪个格子(点)中if(x1%10>4)index_x++;if(y1%10>4)index_y++;for(int i=-1;i<2;i++){if(index_x+i>=0&&index_x+i<Area.arraySize)draw[index_y][index_x+i]=1;if(index_y+i>=0&&index_y+i<Area.arraySize)draw[index_y+i][index_x]=1;}x1=e.getX();y1=e.getY();}/*** Invoked when the mouse cursor has been moved onto a component* but no buttons have been pushed.*/public void mouseMoved(MouseEvent e){}public void mouseClicked(MouseEvent e){//输出记录的矩阵,检查是否符合预期
//      for(int i=0;i<Area.arraySize;i++){//          for(int j=0;j<Area.arraySize;j++){//              System.out.print(draw[i][j]);
//          }
//          System.out.println(" ");
//      }
//      System.out.println("_____________________________________________");//if(e.getClickCount()==2){//训练模式直接写入保存就可以if(name.equals("训练")){try{out();}catch(IOException e0){e0.printStackTrace();}}//识别模式if(name.equals("识别")){try{//读取数据in();}catch(IOException e0){e0.printStackTrace();}//(以上得到距离数组)根据距离排序//  System.out.println(distance.getsize()+"大小");for(int i=0;i<distance.getsize();i++){for(int j=i;j<distance.getsize();j++){if(distance.getObject(i).dis>distance.getObject(j).dis)distance.swap(i, j);}}//得出K个临近的数字出现的频率for(int i=0;i<Area.K;i++){for(int j=0;j<10;j++){//   System.out.println("K计数的数字:"+distance.getObject(i).number);if(distance.getObject(i).number==j)count[j]++;}}int result=0,max=0;//得出最大频率的数字就是结果for(int i=0;i<10;i++){// System.out.println("最大频率:"+max);if(max<count[i]){max=count[i];result=i;}}//不确定是否K个中只识别到一个数字,所以是可能System.out.println("识别的数字是:"+result);jt.append("识别的数字可能为:"+result);//如果前K个数字中也识别到其他的数字,也把他们输出for(int i=0;i<10;i++){if(count[i]>0&&result!=i)jt.append(" "+i);}jt.append("\n");}//本次操作结束,重绘且更新数组jp.paint(g);init();//}}/*** Invoked when a mouse button has been pressed on a component.*/public void mousePressed(MouseEvent e){x1=e.getX();y1=e.getY();}/*** Invoked when a mouse button has been released on a component.*/public void mouseReleased(MouseEvent e){}/*** Invoked when the mouse enters a component.*/public void mouseEntered(MouseEvent e){}/*** Invoked when the mouse exits a component.*/public void mouseExited(MouseEvent e){}/** 保存训练的数据*/public void out() throws IOException{//创建文件名为:保存为数字+当前时间File ins=new File("D:/learning/mydemo/Javaworkspace/rect/src/com/手写识别/"+number+'-'+System.currentTimeMillis());//写入文件FileWriter write=new FileWriter(ins);for(int i=0;i<Area.arraySize;i++){for(int j=0;j<Area.arraySize;j++){write.write(draw[i][j]+"");}}write.flush();write.close();}/*** 读取数据*/public void in() throws IOException{File file =new File ("D:/learning/mydemo/Javaworkspace/rect/src/com/手写识别/");//得到该目录下的所有文件名String[] filelist=file.list();for(int i=0;i<filelist.length;i++){FileReader readfile=new FileReader("D:/learning/mydemo/Javaworkspace/rect/src/com/手写识别/"+filelist[i]);//读取文件,输入流读取返回intint b=readfile.read();for(int k=0;k<Area.arraySize;k++){for(int j=0;j<Area.arraySize;j++){//文章末为-1if(b!=-1){//从文件名中取出的数存入数组getfile[k][j]=Integer.valueOf((char)b+"");//读取下一个b=readfile.read();}}}//关闭文件readfile.close();//提取文件名首数字int a =Integer.valueOf(filelist[i].substring(0, 1));// System.out.println("本次检查数字:"+a);//调用计算距离公式,得到的距离传给距离数组setdistance(getfile,draw,a);}}/*** 计算当前写的数字与样本的欧式距离* @param a 32*32数组* @param b 32*32数组* @param n 数字*/public void setdistance(int [][]a,int [][]b,int n){int sum=0;for(int i=0;i<Area.arraySize;i++){for(int j=0;j<Area.arraySize;j++){sum+=(int)Math.pow((double)(a[i][j]-b[i][j]), 2);}}sum=(int)Math.sqrt((double)sum);Distance d=new Distance(sum,n);distance.add(d);//  System.out.println(n+"数字距离为:"+sum);}/*** 内部保存距离的类* 有距离,数字,构造函数* @author mo**/class Distance{public int dis;public int number;public Distance(int d,int n){this.dis=d;this.number=n;}public Distance(){}}
}

Java学习笔记(八):简单的窗体实现KNN手写体识别(借鉴)相关推荐

  1. 【Java学习笔记八】包装类和vector

    包装类 在Java语言中,每一种基本的数据类型都有相应的对象类型,称为他们基本类型的包装类(包裹类). 字节byte:Byte.短整数型short:Short 标准整数型int:Integer.长整数 ...

  2. java学习笔记(八)----包,jar文件

    包  //建立包后同一个文件中的类都属于这个包,所有的类都必须按包名所对应的目录,在硬盘中存放.同一个包中的类在相互调用时,是不用指定包名的.    ---在编译时对于下面这个类,用这样的方法  ja ...

  3. java学习笔记(二十八)——开发一个小项目(VMeeting3.0)

    上篇文章按照较规范的产品需求文档梳理了项目的逻辑,感觉开发起来明晰了很多:挂上一篇文章java学习笔记(二十七)--开发一个小项目(VMeeting2.0)_Biangbangbing的博客-CSDN ...

  4. 黑马程序员_java自学学习笔记(八)----网络编程

    黑马程序员_java自学学习笔记(八)----网络编程 android培训. java培训.期待与您交流! 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无 ...

  5. Java学习笔记:2022年1月10日

    Java学习笔记:2022年1月10日 ​ 摘要:这篇笔记主要记录了学习<Java核心技术 卷一>的第四章时的一些心得,主要阐述了对象与类这一部分的内容.需要注意的是,这一章的内容需要精心 ...

  6. Java学习笔记(原创)

    Java学习笔记(原创) 2011-12-01 16:37:00|  分类: Java|举报|字号 订阅 下载LOFTER客户端 基本知识 一. Java基础 1. java语言的特点: ①简单:没有 ...

  7. java学习笔记13--反射机制与动态代理

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note13.html,转载请注明源地址. Java的反射机制 在Java运行时环境中,对于任意 ...

  8. java学习笔记12--异常处理

    java学习笔记系列: java学习笔记11--集合总结 java学习笔记10--泛型总结 java学习笔记9--内部类总结 java学习笔记8--接口总结 java学习笔记7--抽象类与抽象方法 j ...

  9. c# 学习笔记 (2) 窗体之间互相调用的方法

    c# 学习笔记 (2) 窗体之间互相调用的方法 创建一个winform工程 创建两个窗体 一个子窗体,一个父窗体,这里为了演示,子窗体和父窗体上都有一个文本框和按键,点击任意一个窗体上的按键,窗体上文 ...

最新文章

  1. mysql ssl_cipher_mysql添加用户:出现Field 'ssl_cipher' doesn't have a defa
  2. wk一sm5时间温度控制器_新能源汽车电机控制器温度计算及其模型—DC电容篇
  3. java替换特殊字符串
  4. 键盘敲入 A 字母时,操作系统期间发生了什么...
  5. 提高生产力,最全 MyBatisPlus 讲解!
  6. 特斯拉已撤回德国电池工厂建厂补贴申请 原有望获得近13亿美元
  7. Atitti usrQBf1801 翻页控件规范  v2
  8. textarea 的中文输入判断与搜狗输入法的特殊行为
  9. PLC1200通过CB1241RS485通讯走modbus rtu连接昆仑通态
  10. 哈工大计算机系统大作业 程序人生-Hello’s P2P From Program to Process
  11. 深入理解Amazon Alexa Skill(四)
  12. umail for linux,U-Mail邮件系统 for CentOS(6.X) x64
  13. 附加支付和统筹支付_上海市医疗保险,请问账户支付和统筹支付是什...
  14. 数据控制—完整性约束
  15. echarts自定义地图总结(VUE)
  16. java文件边读边写_[Java教程]node.js 利用流实现读写同步,边读边写
  17. mysql 时间格式转换年月日时分秒
  18. 网页游戏制作html5,利用HTML5 Canvas制作一个简单的打飞机游戏
  19. 爷爷:啥是佩奇?佩奇:Python 10 秒做出来,你看像不像?
  20. 华为云FusionInsight智能数据湖版本新能力解读

热门文章

  1. word审阅功能、查看word文档的好姿势
  2. 编译器与Debug的传奇:女牛人Grace Hopper小传
  3. python怎样遍历列表中数字_Python 遍历列表里面序号和值的方法(三种)
  4. Java里那些高大上的名称
  5. 前端开发必备技能知识笔记-二级目录的配置
  6. 新思路二级c语言程序,新思路 二级c语言
  7. Modern Family S01E05 part5
  8. 问题追踪定位常用工具
  9. 硬盘分区并且重新格式化之后的数据恢复
  10. IsMouseOver 和MouseEnter\MouseLeave 事件