牙叔教程 简单易懂
上面这张图片网上说是opencv自带的, 我下载的4.5.2的opencv的安卓版本, 就没找到
knn简介
百科简介
邻近算法,或者说K最近邻(KNN,K-NearestNeighbor)分类算法是数据挖掘分类技术中最简单的方法之一。所谓K最近邻,就是K个最近的邻居的意思,说的是每个样本都可以用它最接近的K个邻近值来代表。近邻算法就是将数据集合中每一个记录进行分类的方法 。
网友大奸猫对knn的算法描述
S1 算距离
给定未知样本点A,计算它与训练集中的每个样本点的距离
S2 找邻居
将S1计算好的距离升序排列,取前k个最近的样本点作为A的邻居
S3 确定分类
这k个邻居中,包含邻居数最多的类别作为A的类别
knn示意图
目标是黄色多边形, 距离黄色多边形最近的3个邻居, 有两个是红色, 一个是蓝色,
按照近朱者赤近墨者黑, 少数服从多数来推断, 就认为黄色多边形是红色
也可以说是, 你身边那种人多, 你就会成为那种人
算法常用的距离
曼哈顿距离(城市街区距离)
欧式距离
马氏距离(闵可夫斯基距离)
余弦距离
切比雪夫距离
海明距离
autojs版本
8.8.16-0
本版本自带3.4.3的opencv
knn简要流程
- 图片是一大张, 所以第一步是切割图片, 每个图片只包含一个数字
- 训练
- 预测
训练
训练就是把特征和标签一一对应起来, 再对数据做一些处理,
这里主要说一下特征提取,
特征: 取图片每个像素点的数值, 一张图片是20X20, 就是400个像素, 并且图片是一个通道
关键代码:
let tempData = Imgcodecs.imread(filePath, 0); // 一个通道
tempData = tempData.reshape(0, 1); // 矩阵变为一行
tempData.convertTo(tempData, CvType.CV_32F); // 数据变为浮点数
tempData.copyTo(tmp); // 保存特征
trainlabel.put(0, 0, trainClasses); // 打标签
knn.train(trainData, Ml.ROW_SAMPLE, trainlabel); // 训练
预测
预测就是拿一小部分数据测试,
训练的时候留一小部分数据, 不参加训练, 而是用于预测
预测, 和训练一样,
提取图片特征, 拿去和训练好的数据计算距离,
然后返回匹配度最高的值
关键代码
let tempData = Imgcodecs.imread(filePath, 0); // 一个通道
tempData = tempData.reshape(0, 1); // 矩阵变为一行
tempData.convertTo(tempData, CvType.CV_32F); // 数据变为浮点数
let response = knn.findNearest(tempData, k, nearests); // 计算最佳匹配
统计
log("测试总数: " + testNum);
log("正确分类数: --> " + trueNum);
log("准确率:" + (trueNum / testNum) * 100 + "%");
Mat
mat是opencv常用的数据类型, 理解Mat的格式后, 对理解opencv代码很有帮助
修改Mat
autojs的Mat没有at方法, 那么修改数据就用get和put
runtime.images.initOpenCvIfNeeded();
log(new org.opencv.core.Mat().getClass());
delete org.opencv.core.Mat;
log(new org.opencv.core.Mat().getClass());
importClass(org.opencv.core.Mat);
importClass(org.opencv.core.CvType);
//32位浮点数 1个channel
let trainlabel = Mat.ones(100, 1, CvType.CV_32FC1);
for (var i = 0; i < 100; i++) {
log("修改前" + i + ": ", trainlabel.get(i, 0));
let item = util.java.array("float", 1);
item[0] = i;
log(trainlabel.put(i, 0, item));
log("修改后" + i + ": ", trainlabel.get(i, 0));
}
打印Mat宽高行列
let trainlabel = Mat.ones(100, 1, CvType.CV_32FC1);
let infoList = [
"\n",
"row: " + trainlabel.rows(),
"col: " + trainlabel.cols(),
"height: " + trainlabel.height(),
"width: " + trainlabel.width(),
];
log(infoList.join("\n"));
// row: 100
// col: 1
// height: 100
// width: 1
把Mat想象成一堵墙就可以了,
横着的是row, 竖着的是col,
有多少row, 就有多height,
有多少col, 就有多width
Mat参数一般是row前, col后
先看数据在第几行, 再看数据在第几列
也符合人的思维
我们读书也是从左往右, 从上往下
古人的话估计是先看第几列, 再看第几行,
因为古人的书是竖着写的
打印Mat具体的数据
runtime.images.initOpenCvIfNeeded();
importClass(org.opencv.core.Mat);
importClass(org.opencv.core.CvType);
// 3行2列
let trainlabel = Mat.ones(3, 2, CvType.CV_32FC1);
let width = trainlabel.width();
let height = trainlabel.height();
let arr = [];
// height和行数 数值一样
// 可以认为 hegith ⇔ 行数
for (var i = 0; i < height; i++) {
let childArr = [];
for (var j = 0; j < width; j++) {
let item = trainlabel.get(i, j);
childArr.push(item);
}
arr.push(childArr);
}
log(JSON.stringify(arr, null, " "));
// [
// [[1.0], [1.0]],
// [[1.0], [1.0]],
// [[1.0], [1.0]]
// ]
声明
部分内容来自网络
本教程仅用于学习, 禁止用于其他用途
bilibili
微信公众号 牙叔教程
QQ群
747748653
完整源码
代码是autojs8.8.16写的