写在前面
之前写过一给手写数字识别系统,那时候是大一时候,刚刚对数字图像处理有了一点点接触。实际上真正开始学这门课实在上学期,也就是大三上学期,这学期才开始正式做这个实验。上学期用opencv自己实现了均值滤波,直方图均衡化,腐蚀膨胀等相关的图像处理操作,学习了相关理论知识,再回看之前写的代码感觉真的有很多当初根本不理解的地方,现在一看就忽然理解了。而且对于之前的实现还有改进的地方。这次这个实验也是使用mnist的数据集,但是这次的要求和上次不一样了,上次是依葫芦画瓢。感觉懂了,实际上不懂,但这次是真正要实现一个模板匹配的算法。下面的代码注释把所有涉及到的点都说明白了,对着代码看应该会比较好。
在这之前先给出算法流程
下面给出算法流程:
下面给出代码。
我用的环境是vs2019+opencv3.4.7
系统一共是两个程序,一个是训练程序,一个测试程序。训练程序用于把数据集的图片01序列存在txt文档。测试程序通过读取文档对待识别数字进行识别。测试集采用训练集每个数字前20张图片。训练集每个数字有100张图。
下面是训练代码:
/*
因为测试集是训练集的子集,所以本程序在cvtest2(这个程序把训练集1000张图片全部训练)的基础上,把训练集的范围降到100的数据,即每个数字10个样本,用来测试测试集的识别率。
*/
#include "cv.h"
#include "highgui.h"
#include<opencv2/opencv.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <opencv\cv.hpp>
#include<sstream>
#include <iostream>
#include <fstream>
#include <Windows.h>
using namespace std;
using namespace cv;
ofstream codeo("E:\\code\\vs\\txtfile\\2lb.txt", ios::binary);//不追加,每次训练都重新覆盖原文件;
//计算欧氏距离
int getDist(int test[100], int sample[100])
{
int sum = 0;
for (int i = 0; i < 100; i++)
{
sum += pow(test[i] - sample[i], 2);//这里省略开根号
}
return sum;
}
//对图像进行区域分割并字符串化图片区域,得到test数组,里面存放着一张图片的01序列
void getPXSum(Mat& src, int(&test)[100])
{
int mark = 0, count = 0;//mark是划分的格子的序号,count是格子内计数像素个数
//threshold(src, src, 100, 1, CV_THRESH_BINARY);
while (mark < 100)//100.个格子10*10
{
for (int i = 0; i < 30; i++)
{
for (int j = 0; j < 30; j++)//一个格子里面有30*30=90个像素
{
if (src.at <uchar>(i + 30 * (mark / 10), j + 30 * (mark % 10)) > 200) /*Mat类中的at方法作用:用于获取图像矩阵某点的值或改变某点的值。对于单通道图像的使用方法:image.at<uchar>(i,j) = 255;对于RGB三通道图像的使用方法:image.at<Vec3b>(i,j)[0] = 255;image.at<Vec3b>(i,j)[1] = 255;image.at<Vec3b>(i,j)[2] = 255;*/
count++;//mark/7是得出当前是第几行的格子
//printf_s("%d",src.at <uchar>(i + 3 * (mark % 7), j + 3 * (mark / 7)));
}
//printf_s("\n");
}
if (count > 50) test[mark] = 1;//若格子里超过半数像素是亮的,则记这个格子为1
else
{
test[mark] = 0;
}
//printf_s("黑色像素占该3*3区域的%d/9\n", count);
count = 0;
mark++;
}
}
//输出一张图片的字符串序列
void readNum(int(&test)[100])
{
//ofstream codeo("E:\\code\\vs\\txtfile\\1.txt", ios::binary | ios::app);
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
//printf_s("%d", test[i * 10 + j]);//输出一张图片的字符串序列
int buffer = test[i * 10 + j];
//while (!codei.eof()) {
codeo << buffer;
}
//printf_s("\n");
}
codeo << endl;
printf_s("\n");
}
//统计所有列像素的总和
int getColSum(Mat src, int col)
{
int sum = 0;
int height = src.rows;
int width = src.cols;
for (int i = 0; i < height; i++)
{
sum = sum + src.at <uchar>(i, col);
}
return sum;
}
//统计所有行像素的总和
int getRowSum(Mat src, int row)
{
int sum = 0;
int height = src.rows;
int width = src.cols;
for (int i = 0; i < width; i++)
{
sum += src.at <uchar>(row, i);
}
return sum;
}
//上下切割
void cutTop(Mat& src, Mat& dstImg)
{
int top, bottom;
top = 0;
bottom = src.rows;
int i;
for (i = 0; i < src.rows; i++)
{
int colValue = getRowSum(src, i);//统计所有行像素的总和
//cout <<i<<" th "<< colValue << endl;
if (colValue > 0)//扫描直到行像素的总和大于0时,记下当前位置top
{
top = i;
break;
}
}
for (; i < src.rows; i++)
{
int colValue = getRowSum(src, i);//统计所有行像素的总和
//cout << i << " th " << colValue << endl;
if (colValue == 0)//继续扫描直到行像素的总和等于0时,记下当前位置bottom
{
bottom = i;
break;
}
}
int height = bottom - top;
Rect rect(0, top, src.cols, height);
dstImg = src(rect).clone();
}
//左右切割
int cutLeft(Mat& src, Mat& leftImg, Mat& rightImg)
{
int left, right;
left = 0;
right = src.cols;
int i;
for (i = 0; i < src.cols; i++)
{
int colValue = getColSum(src, i);//统计所有列像素的总和
//cout <<i<<" th "<< colValue << endl;
if (colValue > 0)//扫描直到列像素的总和大于0时,记下当前位置left
{
left = i;
break;
}
}
if (left == 0)
{
return 1;
}
//继续扫描
for (; i < src.cols; i++)
{
int colValue = getColSum(src, i);//统计所有列像素的总和
//cout << i << " th " << colValue << endl;
if (colValue == 0)//继续扫描直到列像素的总和等于0时,记下当前位置right
{
right = i;
break;
}
}
int width = right - left;//分割图片的宽度则为right - left
Rect rect(left, 0, width, src.rows);//构造一个矩形,参数分别为矩形左边顶部的X坐标、Y坐标,右边底部的X坐标、Y坐标(左上角坐标为0,0)
leftImg = src(rect).clone();//实际上这句话进行了一个裁剪的功能
//clone的作用是对src进行了深拷贝,创建了一个完整的副本
//利用Mat方法直接赋值获取的区域图像仍然会改变原图.
//若想直接复制出ROI区域, 需要把原始图像进行clone即可
/*1. 使用拷贝构造函数Mat(constMat& m, const Rect& roi ),矩形roi指定了兴趣区
Mat src = imread(“xx.jpg”);
Mat srcROI( src, Rect(0,0,src.cols/2,src.rows/2));
2. 使用操作符”()”,即Mat operator () ( const Rect&roi ) const,矩形roi指定了兴趣区
Mat src = imread(“xx.jpg”);
Mat srcROI = src(Rect(0,0,src.cols/2,src.rows/2));
注意:以上两种操作,srcROI的数据与源图像src共享存储区,所以此后在srcROI上的操作也会作用在源图像src上。
所以,当需要提取出独立的ROI区域时,应该使用
Mat srcROI = src(Rect(0,0,src.cols/2,src.rows/2)).clone();
*/
Rect rectRight(right, 0, src.cols - right, src.rows);//分割后剩下的原图
rightImg = src(rectRight).clone();
cutTop(leftImg, leftImg);//上下切割
return 0;
}
//test形参传引用,会保存数据,处理图片,并获得字符串
void readImg(Mat& src, int(&test)[100], int i)
{
imshow("origin0", src);//显示原图片
medianBlur(src, src, 3);//中值滤波去椒盐噪声
// imshow("origin2", src);//显示去噪后图片
Mat element = getStructuringElement(MORPH_RECT, Size(2, 2));//返回指定形状和尺寸的结构元素,也就是生成一个卷积核
//dilate(src, src, element);//膨胀
//imshow("origin3", src);//显示膨胀后图片
threshold(src, src, 100, 255, THRESH_BINARY);//二值化,100是阈值
Mat rightImg, leftImg;
cutLeft(src, leftImg, rightImg);
imshow("origin4", leftImg);//显示切割后图片
resize(leftImg, leftImg, Size(300, 300), 0, 0, CV_INTER_LINEAR);
imshow("origin5", leftImg);//显示调整尺寸后图片
stringstream ss;
ss << i;//此举将int类型转换为字符串类型
/*
stringstream还是相当强大的。简单易懂,虽然写的行数比较多!
基本数据类型转换例子 int和string, 也支持string和char* ,int和char* 之间的转换。
注意同一个stringstream对象,再进行多次转换的时候,必须调用stringstream的成员函数clear().
头文件<sstream>
如int转string
int n = 0;
std::stringstream ss;
std::string str;
ss << n;
ss >> str;
*/
imwrite("E:\\code\\vs\\cvtest1\\minist\\moban\\" + ss.str() + ".bmp", leftImg);//streamstring在调用str()时,会返回临时的string对象。
getPXSum(leftImg, test);//计算切割好的图片的序列,存到test里
/**/
}
int main()
{
int sample[10][100][100];//保存样本图片字符串,每个数字抽取10副图,每幅图拥有100个字符的序列
//double num[10][3] = { 0 };//统计结果,即10个数字的三率
int test[100];//保存测试图片字符串
int test_num = 7;//测试的数字
int true_num = 0;//数字的识别结果
printf_s("测试样本\n");
char name[100];//测试图片路径
//sprintf_s(name, "E:\\code\\vs\\cvtest1\\minist\\train-images\\%d_%d.bmp", test_num, test_num);
//Mat src = imread(name, CV_LOAD_IMAGE_GRAYSCALE);//读取的测试图片图片
sprintf_s(name, "E:\\code\\vs\\cvtest1\\minist\\moban\\shou.bmp");
Mat src = imread(name, CV_LOAD_IMAGE_GRAYSCALE);//读取图片
// imshow("origin0", src);//显示原图片
readImg(src, test, test_num);//获得了src的序列
//codeo << "test";
//readNum(test);
int minidist = 100, dist;
int chou = 20;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 80; j++)//每个数字取后80个样本作为训练样本
{
//chou++;
char name[100];//模板图片的存储位置
sprintf_s(name, "E:\\code\\vs\\cvtest1\\minist\\train-images\\%d_%d.bmp", i, j + 20);//取每个数字第20个样本以后的图像
Mat Template = imread(name, CV_LOAD_IMAGE_GRAYSCALE);//读取模板
readImg(Template, sample[i][j], i);//模板第i个数字的第j副图
codeo << i;
readNum(sample[i][j]);
printf_s("与数字%d第%d个模板", i, j + 1);
printf_s("欧式距离:");
dist = getDist(test, sample[i][j]);
if (minidist >= dist) {
minidist = dist;
true_num = i;
}
printf_s("%d\n\n\n 当前minidist:%d", dist, minidist);
}
}
printf_s("识别结果为:%d", true_num);
codeo.close();
waitKey(0);
//getchar();
return 0;
}
下面是测试程序:
/*
本程序用于测试测试集test的数字识别率。用的数据集是从1000个全部训练集中抽取的100个数据,
*/
#include "cv.h"
#include "highgui.h"
#include<opencv2/opencv.hpp>
#include <stdio.h>
#include <stdlib.h>
#include <opencv\cv.hpp>
#include<sstream>
#include <iostream>
#include <fstream>
#include <Windows.h>
using namespace std;
using namespace cv;
//计算欧氏距离
int getDist(int test[100], int sample[100])
{
int sum = 0;
for (int i = 0; i < 100; i++)
{
sum += pow(test[i] - sample[i], 2);//这里省略开根号
}
return sum;
}
//对图像进行区域分割并字符串化图片区域,得到test数组,里面存放着一张图片的01序列
void getPXSum(Mat& src, int(&test)[100])
{
int mark = 0, count = 0;//mark是划分的格子的序号,count是格子内计数像素个数
//threshold(src, src, 100, 1, CV_THRESH_BINARY);
while (mark < 100)//49.个格子7*7
{
for (int i = 0; i < 30; i++)
{
for (int j = 0; j < 30; j++)//一个格子里面有3*3九个像素
{
if (src.at <uchar>(i + 30 * (mark / 10), j + 30 * (mark % 10)) > 200) /*Mat类中的at方法作用:用于获取图像矩阵某点的值或改变某点的值。对于单通道图像的使用方法:image.at<uchar>(i,j) = 255;对于RGB三通道图像的使用方法:image.at<Vec3b>(i,j)[0] = 255;image.at<Vec3b>(i,j)[1] = 255;image.at<Vec3b>(i,j)[2] = 255;*/
count++;//mark/7是得出当前是第几行的格子
//printf_s("%d",src.at <uchar>(i + 3 * (mark % 7), j + 3 * (mark / 7)));
}
//printf_s("\n");
}
if (count > 50) test[mark] = 1;//若格子里超过半数像素是亮的,则记这个格子为1
else
{
test[mark] = 0;
}
// printf_s("黑色像素占该3*3区域的%d/9\n", count);
count = 0;
mark++;
}
}
//输出一张图片的字符串序列
void readNum(int(&test)[100])
{
//ofstream codeo("E:\\code\\vs\\txtfile\\1.txt", ios::binary | ios::app);
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
//printf_s("%d", test[i * 7 + j]);//输出一张图片的字符串序列
int buffer = test[i * 10 + j];
//while (!codei.eof()) {
}
//printf_s("\n");
}
//printf_s("\n");
}
//统计所有列像素的总和
int getColSum(Mat src, int col)
{
int sum = 0;
int height = src.rows;
int width = src.cols;
for (int i = 0; i < height; i++)
{
sum = sum + src.at <uchar>(i, col);
}
return sum;
}
//统计所有行像素的总和
int getRowSum(Mat src, int row)
{
int sum = 0;
int height = src.rows;
int width = src.cols;
for (int i = 0; i < width; i++)
{
sum += src.at <uchar>(row, i);
}
return sum;
}
//上下切割
void cutTop(Mat& src, Mat& dstImg)
{
int top, bottom;
top = 0;
bottom = src.rows;
int i;
for (i = 0; i < src.rows; i++)
{
int colValue = getRowSum(src, i);//统计所有行像素的总和
//cout <<i<<" th "<< colValue << endl;
if (colValue > 0)//扫描直到行像素的总和大于0时,记下当前位置top
{
top = i;
break;
}
}
for (; i < src.rows; i++)
{
int colValue = getRowSum(src, i);//统计所有行像素的总和
//cout << i << " th " << colValue << endl;
if (colValue == 0)//继续扫描直到行像素的总和等于0时,记下当前位置bottom
{
bottom = i;
break;
}
}
int height = bottom - top;
Rect rect(0, top, src.cols, height);
dstImg = src(rect).clone();
}
//左右切割
int cutLeft(Mat& src, Mat& leftImg, Mat& rightImg)
{
int left, right;
left = 0;
right = src.cols;
int i;
for (i = 0; i < src.cols; i++)
{
int colValue = getColSum(src, i);//统计所有列像素的总和
//cout <<i<<" th "<< colValue << endl;
if (colValue > 0)//扫描直到列像素的总和大于0时,记下当前位置left
{
left = i;
break;
}
}
if (left == 0)
{
return 1;
}
//继续扫描
for (; i < src.cols; i++)
{
int colValue = getColSum(src, i);//统计所有列像素的总和
//cout << i << " th " << colValue << endl;
if (colValue == 0)//继续扫描直到列像素的总和等于0时,记下当前位置right
{
right = i;
break;
}
}
int width = right - left;//分割图片的宽度则为right - left
Rect rect(left, 0, width, src.rows);//构造一个矩形,参数分别为矩形左边顶部的X坐标、Y坐标,右边底部的X坐标、Y坐标(左上角坐标为0,0)
leftImg = src(rect).clone();//实际上这句话进行了一个裁剪的功能
Rect rectRight(right, 0, src.cols - right, src.rows);//分割后剩下的原图
rightImg = src(rectRight).clone();
cutTop(leftImg, leftImg);//上下切割
return 0;
}
//test形参传引用,会保存数据,处理图片,并获得字符串
void readImg(Mat& src, int(&test)[100])
{
imshow("原图", src);//显示原图片
medianBlur(src, src, 3);//中值滤波去椒盐噪声
//imshow("origin2", src);//显示去噪后图片
Mat element = getStructuringElement(MORPH_RECT, Size(2, 2));//返回指定形状和尺寸的结构元素,也就是生成一个卷积核
//dilate(src, src, element);//膨胀
//imshow("origin3", src);//显示膨胀后图片
threshold(src, src, 100, 255, THRESH_BINARY);//二值化,100是阈值
Mat rightImg, leftImg;
cutLeft(src, leftImg, rightImg);
imshow("切割后的图片", leftImg);//显示切割后图片
resize(leftImg, leftImg, Size(300, 300), 0, 0, CV_INTER_LINEAR);
imshow("显示调整尺寸后图片", leftImg);//显示调整尺寸后图片
stringstream ss;
//ss << i;//此举将int类型转换为字符串类型
//imwrite("E:\\code\\vs\\cvtest1\\minist\\moban\\" + ss.str() + ".bmp", leftImg);//streamstring在调用str()时,会返回临时的string对象。
getPXSum(leftImg, test);//计算切割好的图片的序列,存到test里
}
int main()
{
//ofstream code("E:\\code\\vs\\txtfile\\1.txt", ios::binary|ios::app);
//code << "123122222222222"<<endl;
//code.close();
ifstream codei("E:\\code\\vs\\txtfile\\2lb.txt", ios::binary);
int sample1[10][80][101];//保存样本图片字符串,每个数字100副图,每幅图拥有49个字符的序列;
int sample[10][80][100];
char ch;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 80; j++)
{
for (int k = 0; k < 101; k++)
{
codei.get(ch);
if ((int)ch == 10)
{
k--;
continue;
}
sample1[i][j][k] = (int)ch - 48;
//cout << sample1[i][j][k];
}
//cout << endl;
}
}
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 80; j++)
{
for (int k = 0; k + 1 < 101; k++)
{
sample[i][j][k] = sample1[i][j][k + 1];
cout << sample[i][j][k];
}
cout << endl;
}
}
double num[10][3] = { 0 };//统计结果,即10个数字的三率
int test[100];//保存测试图片字符串
int true_num = 0;//数字的识别结果
char name[100];//测试图片路径
double trues = 0;//数字i的正确识别数量
double faluses = 0;//数字i错误识别的数量
double jv = 0;//拒绝识别的数量
for (int t1 = 0; t1 < 10; t1++)
{
for (int t2 = 0; t2 < 20; t2++)
{
sprintf_s(name, "E:\\code\\vs\\cvtest1\\minist\\test-images\\%d_%d.bmp", t1, t2);
Mat src = imread(name, CV_LOAD_IMAGE_GRAYSCALE);//读取的测试图片图片
imshow("origin0", src);//显示原图片
readImg(src, test);//获得了src的序列
//codeo << "test";
readNum(test);
/*-------------------以上处理测试识别的图像,以下是匹配模板-----------------------------*/
int minidist = 100, dist;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 80; j++)
{
// printf("当前测试数字:%d_%d\n", t1,t2);
//printf("当前图片序列:");
readNum(sample[i][j]);
// printf_s("与数字%d第%d个模板", i, j + 1);
//printf_s("欧式距离:");
dist = getDist(test, sample[i][j]);
if (minidist > dist) {
minidist = dist;
true_num = i;
}
// printf_s("%d \n", dist);
//printf_s("%d \n当前minidist:%d\n\n\n", dist, minidist);
}
}
if (minidist > 26)
{
jv++;
cout <<"mimidist:"<<minidist<< "拒绝识别\n";
continue;
}
if (true_num == t1)
{
trues++;
}
else
{
faluses++;
}
printf_s("识别结果为:%d,minidist:%d\n", true_num, minidist);
}
printf("数字%d成功率为%.2f\n", t1, trues / 20.0);
printf("数字%d错误率为%.2f\n", t1, faluses / 20.0);
printf("数字%d拒绝识别率为%.2f\n", t1, jv / 20.0);
cout << endl;
trues = 0;
faluses = 0;
jv = 0;
}
waitKey(0);
//getchar();
return 0;
}
当然,为了填补上次手写数字识别系统的缺陷,我重新改了一下之前的过的东西,在这篇文章一并分享
我在这所用的东西,其实也是算欧氏距离。仔细想一想,上面的方法是要求我们把一张大的图片分成小块,每一块包含了很多像素,然后再对这些块里超过半数为1的块记为1,实际上就是把像素规模进行变小了嘛。我一个300*300的像素要分成10*10的格子,不就相当于我直接把它变成10*10像素的图片吗。而且再opencv里面有专门计算两个Mat之间的对应元素绝对值差绝对值相加的函数,这不就是是欧氏距离吗。我甚至可以不同把它二值化(相当于半数像素为白色,格子置为1,反之置零),而是灰度化,这样计算欧氏距离是不是更准呢?当然这里计算绝对值之和应该是很多个255相加,但是也可以改成只加一。但是这样的效果是不一样的,大家可以去试一下,改一句代码即可,我再代码中有写出注释。这里用的还是1000张图片的数据集。和之前不一样的是,这里是直接读取数据集图片,而没有训练和读取训练数据。
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int getColSum(Mat src, int col)//统计所有列像素的总和
{
int sum = 0;
int height = src.rows; int width = src.cols;
for (int i = 0; i < height; i++)
{
sum = sum + src.at <uchar>(i, col);
}
return sum;
}
int getRowSum(Mat src, int row)//统计所有行像素的总和
{
int sum = 0;
int height = src.rows;
int width = src.cols;
for (int i = 0; i < width; i++)
{
sum += src.at <uchar>(row, i);
}
return sum;
}
void cutTop(Mat& src, Mat& dstImg)//上下切割
{
int top, bottom;
top = 0;
bottom = src.rows;
int i;
for (i = 0; i < src.rows; i++)
{
int colValue = getRowSum(src, i);//统计所有行像素的总和
//cout <<i<<" th "<< colValue << endl;
if (colValue > 0)//扫描直到行像素的总和大于0时,记下当前位置top
{
top = i;
break;
}
}
for (; i < src.rows; i++)
{
int colValue = getRowSum(src, i);//统计所有行像素的总和
//cout << i << " th " << colValue << endl;
if (colValue == 0)//继续扫描直到行像素的总和等于0时,记下当前位置bottom
{
bottom = i;
break;
}
}
int height = bottom - top;
Rect rect(0, top, src.cols, height);
dstImg = src(rect).clone();
}
int cutLeft(Mat& src, Mat& leftImg, Mat& rightImg)//左右切割
{
int left, right;
left = 0;
right = src.cols;
int i;
for (i = 0; i < src.cols; i++)
{
int colValue = getColSum(src, i);//统计所有列像素的总和
//cout <<i<<" th "<< colValue << endl;
if (colValue > 0)//扫描直到列像素的总和大于0时,记下当前位置left
{
left = i;
break;
}
}
if (left == 0)
{
return 1;
}//扫描完成
for (; i < src.cols; i++)
{
int colValue = getColSum(src, i);//统计所有列像素的总和
if (colValue == 0)//继续扫描直到列像素的总和等于0时,记下当前位置right
{
right = i;
break;
}
}
int width = right - left;//分割图片的宽度则为right - left
Rect rect(left, 0, width, src.rows);//构造一个矩形,参数分别为矩形左边顶部的X坐标、Y坐标,右边底部的X坐标、Y坐标(左上角坐标为0,0)
leftImg = src(rect).clone();
Rect rectRight(right, 0, src.cols - right, src.rows);//分割后剩下的原图
rightImg = src(rectRight).clone();
cutTop(leftImg, leftImg);//上下切割
return 0;
}
void getPXSum(Mat& src, int& a)//获取所有像素点和(白色)
{
threshold(src, src, 100, 255, 0);
a = 0;
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
//if (src.at <uchar>(i, j) > 200) a++;
a += src.at <uchar>(i, j);//src.at <uchar>(i, j)表示(i,j)点的像素值;
}
}
}
int getSubtract(Mat& src, int TemplateNum) //数字识别
{
Mat leftim, rightim;
Mat img_result;
int min = 10000000;
int serieNum = 0;
for (int i = 0; i < TemplateNum; i++)
{
for (int j = 0; j <= 99; j++)
{
char name[80];
sprintf_s(name, "E:\\code\\vs\\cvtest1\\minist\\train-images\\%d_%d.bmp", i, j);
Mat Template = imread(name, CV_LOAD_IMAGE_GRAYSCALE);//读取模板
threshold(Template, Template, 100, 255, 3);
//threshold(src, src, 100, 255, CV_THRESH_BINARY);
//resize(src, src, Size(32, 48), 0, 0, CV_INTER_LINEAR);
cutLeft(Template, leftim, rightim);//裁剪;
//Mat element = getStructuringElement(MORPH_RECT, Size(2, 2));//返回指定形状和尺寸的结构元素,也就是生成一个卷积核
//dilate(leftim, leftim, element);//膨胀
resize(leftim, leftim, Size(8, 8), (0, 0), (0, 0), 3);//调整尺寸
//imwrite("E:\\code\\vs\\cvtest1\\minist\\moban\\mo.bmp", leftim);//streamstring在调用str()时,会返回临时的string对象。
imshow("模板", leftim);
//imshow(name, Template);
/*让需要匹配的图分别和10个模板对应像素点值相减,
然后求返回图片的整个图片的像素点值得平方和,
和哪个模板匹配时候返回图片的平方和最小则就可以得到结果*/
absdiff(leftim, src, img_result);//AbsDiff,OpenCV中计算两个数组差的绝对值的函数。
int diff = 0;
getPXSum(img_result, diff);//获取所有像素点和
//printf("%d,%d\n", i, diff);
if (diff < min)//像素点对比
{
min = diff;
serieNum = i;
//printf("%d,%d\n", i, min);
}
}
}
printf("匹配的数字是%d\n", serieNum);
//waitKey(0);
return serieNum;
}
int main()
{
Mat src = imread("E:\\code\\vs\\cvtest1\\minist\\moban\\2.jpg", CV_LOAD_IMAGE_GRAYSCALE);//读取图片1.jpg,2.jpg
Mat leftim, rightim, std, t;
//std=imread("1.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//t = imread("0_0.bmp",1);
imshow("原图", src);
threshold(src, src, 100, 255, 1);//二值化
//threshold(src, src, 100, 255, 3);//二值化
medianBlur(src, src, 3);//中值滤波去椒盐噪声
resize(src, src, Size(src.cols / 4, src.rows / 4), (0, 0), (0, 0), 3);
//resize(src, src, Size(32, 48), 0, 0, CV_INTER_LINEAR);//调整尺寸
imshow("二值化图片", src);//显示二值化后图片
int i = 1;
while (cutLeft(src, leftim, rightim) != 1)
{
char nameLeft[40];//存放可变窗口名字
cutLeft(src, leftim, rightim);//裁剪;
sprintf_s(nameLeft, "%dth number", i);//可变窗口名字,赋值给nemeleft
Mat element = getStructuringElement(MORPH_RECT, Size(2, 2));//返回指定形状和尺寸的结构元素,也就是生成一个卷积核
dilate(leftim, leftim, element);//膨胀
//dilate(leftim, leftim, element);//膨胀
resize(leftim, leftim, Size(8, 8), (0, 0), (0, 0), 3);
imshow(nameLeft, leftim);
getSubtract(leftim, 10);//识别函数;
// imwrite("E:\\code\\vs\\cvtest1\\minist\\moban\\shou.bmp", leftim);//streamstring在调用str()时,会返回临时的string对象。
src = rightim;
i++;
}//printf("%d ; %d\n", std.cols * 2, std.rows * 2);
waitKey(0);
return 0;
}
可以看到准确率比上一篇要高
0 条评论