随着技术的不断发展,图像识别已成为重要的应用领域之一。在众多的图像识别问题中,手写数字识别是一项极具挑战性的任务,因为不同的手写风格可能导致数字形状不同。本文将介绍如何使用OpenCV和MNIST数据库来实现手写数字识别。
1. MNIST数据库简介
MNIST(Mixed National Institute of Standards and Technology)是一个广泛使用的手写数字数据库,包括60,000个训练图像和10,000个测试图像,每个数字的大小为28×28像素。MNIST在图像识别领域被广泛使用,因为它已经成为一个基准数据集,可以用来验证各种图像处理算法的性能。
2. OpenCV简介
OpenCV是一个广泛使用的开源图像处理库,它提供了许多计算机视觉和图像处理算法和函数,在数字识别领域也有广泛的应用。在本文中,我们将使用OpenCV来预处理图像、提取数字特征并执行分类任务。
3. 实现步骤
本文的实现步骤分为三个部分:
3.1 预处理
我们需要对图像进行处理,以提高识别准确性。为实现预处理步骤,我们将使用以下三种技术:
3.1.1 二值化
我们将手写数字图像转换为黑白二值图像,以使数字更加清晰。对于每个像素,如果它的灰度值小于一个阈值,则将其设置为0(黑色),否则将其设置为1(白色)。通过二值化技术,我们可以大大减少图像的信息量,减少计算时间和内存占用。
3.1.2 去除噪声
在二值化图像中,可能出现一些孤立的噪声像素点。这些噪声点会影响数字特征的提取,因此需要将它们从图像中去除。常用的方法是执行形态学处理,如腐蚀、膨胀、开运算、闭运算等。
3.1.3 归一化
由于手写数字的大小和位置可能不同,因此需要将图像归一化为相同的大小。在本文中,我们将使用28×28像素的图像作为标准大小。可以使用缩放或裁剪来完成此任务。
3.2 特征提取
在预处理步骤之后,我们需要从图像中提取数字的特征。在本文中,我们将使用轮廓特征提取技术。通过找到数字的轮廓,我们可以获得数字的形状信息,而数字的形状有助于区分不同的数字。轮廓特征提取方法包括图像边缘检测、二值化、寻找轮廓等。
3.3 分类
我们需要将数字特征与数字标签联系起来,并执行分类任务。在本文中,我们将使用KNN(K-近邻)算法。KNN算法是一种简单而有效的分类算法,对于不同的数字,KNN可以根据与训练集的距离来确定其所属类别。KNN算法具有很好的扩展性和优良的性能,它可以处理大量的数据,因此在数字识别中经常使用。
4. 结论
本文介绍了使用OpenCV和MNIST数据库来实现手写数字识别的方法。该方法包括预处理、特征提取和分类三个步骤。通过将图像转换为二值图像并进行轮廓提取,可以提高数字特征的准确性和可靠性。而KNN分类算法可以根据特征距离分辨不同的数字。这项工作具有实际应用的价值,例如在自动化文书处理、银行卡识别、车牌识别等领域。
相关问题拓展阅读:
如何在程序中调用Caffe做图像分类 / 蓝讯
Caffe是目前深度学习比较优秀好用的一个开源库,采样c++和CUDA实现,具有速度快,模型定义方便等雀茄优点。学习了几天过后,发现也有一个不方便的地方,就是在我的程序中调用Caffe做图像分类没有直接的接口。Caffe的数据层可以从数据库(支持leveldb、lmdb、hdf5)、图片、和内存中读入。我们要在程序中使用,当然得从内存中读入,我们首先在模型定义文件中定义数据层:
layers {
name: “mydata”
type: MEMORY_DATA
top: “data”
top: “label”
transform_param {
scale: 0.
}
memory_data_param {
batch_size: 10
channels: 1
height: 24
width: 24
}
}
这里必须设置memory_data_param中的四个参数,对应这些参数可以参见源码中caffe.proto文件。现在,我们可以设计一个Classifier类来封装一下:
#ifndef CAFFE_CLASSIFIER_H
#define CAFFE_CLASSIFIER_H
#include
#include
#include “caffe/net.hpp”
#include “caffe/data_layers.hpp”
#include
using cv::Mat;
namespace caffe {
template
class Classifier {
public:
explicit Classifier(const string& param_file, const string& weights_file);
Dtype test(vector &images, vector &labels, int iter_num);
virtual ~Classifier() {}
inline shared_ptr > net() { return net_; }
void predict(vector &images, vector *labels);
void predict(vector &data, vector *labels, int num);
void extract_feature(vector &images, vector> *out);
protected:
shared_ptr > net_;
MemoryDataLayer *m_layer_;
int batch_size_;
int channels_;
int height_;
int width_;
DISABLE_COPY_AND_ASSIGN(Classifier);
};
}//namespace
#endif //CAFFE_CLASSIFIER_H
构造函数中我们通过模型定义文件(.prototxt)和训练好的模型(.caffemodel)文件构造一慧州个Net对象,并用m_layer_指向Net中的memory data层,以便待会调用MemoryDataLayer中AddMatVector和Reset函数加入数据。
#include
#include
#include
#include
#include “caffe/net.hpp”顷碧察
#include “caffe/proto/caffe.pb.h”
#include “caffe/util/io.hpp”
#include “caffe/util/math_functions.hpp”
#include “caffe/util/upgrade_proto.hpp”
#include “caffe_classifier.h”
namespace caffe {
template
Classifier::Classifier(const string& param_file, const string& weights_file) : net_()
{
net_.reset(new Net(param_file, TEST));
net_->CopyTrainedLayersFrom(weights_file);
//m_layer_ = (MemoryDataLayer*)net_->layer_by_name(“mnist”).get();
m_layer_ = (MemoryDataLayer*)net_->layers().get();
batch_size_ = m_layer_->batch_size();
channels_ = m_layer_->channels();
height_ = m_layer_->height();
width_ = m_layer_->width();
}
template
Dtype Classifier::test(vector &images, vector &labels, int iter_num)
{
m_layer_->AddMatVector(images, labels);
//
int iterations = iter_num;
vector* > bottom_vec;
vector test_score_output_id;
vector test_score;
Dtype loss = 0;
for (int i = 0; i *>& result =
net_->Forward(bottom_vec, &iter_loss);
loss += iter_loss;
int idx = 0;
for (int j = 0; j cpu_data();
for (int k = 0; k count(); ++k, ++idx) {
const Dtype score = result_vec;
if (i == 0) {
test_score.push_back(score);
test_score_output_id.push_back(j);
} else {
test_score += score;
}
const std::string& output_name = net_->blob_names()
net_->output_blob_indices()>;
LOG(INFO)
void Classifier::predict(vector &images, vector *labels)
{
int original_length = images.size();
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i valid_labels, predicted_labels;
valid_labels.resize(valid_length, 0);
m_layer_->AddMatVector(images, valid_labels);
vector* > bottom_vec;
for(int i = 0; i *>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result->cpu_data();
for(int j = 0; j count(); j++)
{
predicted_labels.push_back(result_vec);
}
}
if(original_length != valid_length)
{
images.erase(images.begin()+original_length, images.end());
}
labels->resize(original_length, 0);
std::copy(predicted_labels.begin(), predicted_labels.begin() + original_length, labels->begin());
}
template
void Classifier::predict(vector &data, vector *labels, int num)
{
int size = channels_*height_*width_;
CHECK_EQ(data.size(), num*size);
int original_length = num;
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i predicted_labels;
Dtype * label_ = new Dtype;
memset(label_, 0, valid_length);
m_layer_->Reset(data.data(), label_, valid_length);
vector* > bottom_vec;
for(int i = 0; i *>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result->cpu_data();
for(int j = 0; j count(); j++)
{
predicted_labels.push_back(result_vec);
}
}
if(original_length != valid_length)
{
data.erase(data.begin()+original_length*size, data.end());
}
delete label_;
labels->resize(original_length, 0);
std::copy(predicted_labels.begin(), predicted_labels.begin() + original_length, labels->begin());
}
template
void Classifier::extract_feature(vector &images, vector> *out)
{
int original_length = images.size();
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i valid_labels;
valid_labels.resize(valid_length, 0);
m_layer_->AddMatVector(images, valid_labels);
vector* > bottom_vec;
out->clear();
for(int i = 0; i *>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result->cpu_data();
const int dim = result->count(1);
for(int j = 0; j num(); j++)
{
const Dtype * ptr = result_vec + j * dim;
vector one_;
for(int k = 0; k push_back(one_);
}
}
if(original_length != valid_length)
{
images.erase(images.begin()+original_length, images.end());
out->erase(out->begin()+original_length, out->end());
}
}
INSTANTIATE_CLASS(Classifier);
} // namespace caffe
由于加入的数据个数必须是batch_size的整数倍,所以我们在加入数据时采用填充的方式。
CHECK_EQ(num % batch_size_, 0)
“The added data must be a multiple of the batch size.”; //AddMatVector
在模型文件的最后,我们把训练时的loss层改为argmax层:
layers {
name: “predicted”
type: ARGMAX
bottom: “prob”
top: “predicted”
如何在程序中调用Caffe做图像分类,调用caffe图像分类
Caffe是目前深度学习比较优秀好用的一个开源库,采样c++和CUDA实现,具有速度快,模型定义方便等优点。学习了几天过后,发现也有一个不方便的地方,就是在我的程序中调用Caffe做图像分类没有直接的接口。Caffe的数据层可以从数据库(支持leveldb、lmdb、hdf5)、图片、和内存中读入。我们要在程序中使用,当然得从内存中读入,我们首先在模型定义文件中定义数据局穗层:
layers {
name: “mydata”
type: MEMORY_DATA
top: “data”
top: “label”
transform_param {
scale: 0.
}
memory_data_param {
batch_size: 10
channels: 1
height: 24
width: 24
}
}
这里必须设置memory_data_param中的四个参数,对应这些参数可以参见源码中caffe.proto文件。现在,我们可以设计一个Classifier类来封装一下:
#ifndef CAFFE_CLASSIFIER_H
#define CAFFE_CLASSIFIER_H
#include
#include
#include “caffe/net.hpp”
#include “caffe/data_layers.hpp”
#include
using cv::Mat;
namespace caffe {
template
class Classifier {
public:
explicit Classifier(const string& param_file, const string& weights_file);
Dtype test(vector &images, vector &labels, int iter_num);
virtual ~Classifier() {}
inline shared_ptr > net() { return net_; }
void predict(vector &images, vector *labels);
void predict(vector &data, vector *labels, int num);
void extract_feature(vector &images, vector> *out);
protected:
shared_ptr > net_;
MemoryDataLayer *m_layer_;
int batch_size_;
int channels_;
int height_;
int width_;
知察
DISABLE_COPY_AND_ASSIGN(Classifier);
};
}//namespace
#endif //CAFFE_CLASSIFIER_H
构造函数中我们通过模型定义文件(.prototxt)和训练好的模型(.caffemodel)文件构造一个Net对象,并用m_layer_指向Net中的memory data层,以便待会调用MemoryDataLayer中AddMatVector和Reset函数加入数据。
#include
#include
#include
#include
搭腊茄 #include “caffe/net.hpp”
#include “caffe/proto/caffe.pb.h”
#include “caffe/util/io.hpp”
#include “caffe/util/math_functions.hpp”
#include “caffe/util/upgrade_proto.hpp”
#include “caffe_classifier.h”
namespace caffe {
template
Classifier::Classifier(const string& param_file, const string& weights_file) : net_()
{
net_.reset(new Net(param_file, TEST));
net_->CopyTrainedLayersFrom(weights_file);
//m_layer_ = (MemoryDataLayer*)net_->layer_by_name(“mnist”).get();
m_layer_ = (MemoryDataLayer*)net_->layers().get();
batch_size_ = m_layer_->batch_size();
channels_ = m_layer_->channels();
height_ = m_layer_->height();
width_ = m_layer_->width();
}
template
Dtype Classifier::test(vector &images, vector &labels, int iter_num)
{
m_layer_->AddMatVector(images, labels);
//
int iterations = iter_num;
vector* > bottom_vec;
vector test_score_output_id;
vector test_score;
Dtype loss = 0;
for (int i = 0; i *>& result =
net_->Forward(bottom_vec, &iter_loss);
loss += iter_loss;
int idx = 0;
for (int j = 0; j cpu_data();
for (int k = 0; k count(); ++k, ++idx) {
const Dtype score = result_vec;
if (i == 0) {
test_score.push_back(score);
test_score_output_id.push_back(j);
} else {
test_score += score;
}
const std::string& output_name = net_->blob_names()
net_->output_blob_indices()>;
LOG(INFO)
void Classifier::predict(vector &images, vector *labels)
{
int original_length = images.size();
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i valid_labels, predicted_labels;
valid_labels.resize(valid_length, 0);
m_layer_->AddMatVector(images, valid_labels);
vector* > bottom_vec;
for(int i = 0; i *>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result->cpu_data();
for(int j = 0; j count(); j++)
{
predicted_labels.push_back(result_vec);
}
}
if(original_length != valid_length)
{
images.erase(images.begin()+original_length, images.end());
}
labels->resize(original_length, 0);
std::copy(predicted_labels.begin(), predicted_labels.begin() + original_length, labels->begin());
}
template
void Classifier::predict(vector &data, vector *labels, int num)
{
int size = channels_*height_*width_;
CHECK_EQ(data.size(), num*size);
int original_length = num;
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i predicted_labels;
Dtype * label_ = new Dtype;
memset(label_, 0, valid_length);
m_layer_->Reset(data.data(), label_, valid_length);
vector* > bottom_vec;
for(int i = 0; i *>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result->cpu_data();
for(int j = 0; j count(); j++)
{
predicted_labels.push_back(result_vec);
}
}
if(original_length != valid_length)
{
data.erase(data.begin()+original_length*size, data.end());
}
delete label_;
labels->resize(original_length, 0);
std::copy(predicted_labels.begin(), predicted_labels.begin() + original_length, labels->begin());
}
template
void Classifier::extract_feature(vector &images, vector> *out)
{
int original_length = images.size();
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i valid_labels;
valid_labels.resize(valid_length, 0);
m_layer_->AddMatVector(images, valid_labels);
vector* > bottom_vec;
out->clear();
for(int i = 0; i *>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result->cpu_data();
const int dim = result->count(1);
for(int j = 0; j num(); j++)
{
const Dtype * ptr = result_vec + j * dim;
vector one_;
for(int k = 0; k push_back(one_);
}
}
if(original_length != valid_length)
{
images.erase(images.begin()+original_length, images.end());
out->erase(out->begin()+original_length, out->end());
}
}
INSTANTIATE_CLASS(Classifier);
} // namespace caffe
由于加入的数据个数必须是batch_size的整数倍,所以我们在加入数据时采用填充的方式。
CHECK_EQ(num % batch_size_, 0)
”The added data must be a multiple of the batch size.”; //AddMatVector
在模型文件的最后,我们把训练时的loss层改为argmax层:
layers {
name: “predicted”
type: ARGMAX
bottom: “prob”
top: “predicted”
}
如何在程序中调用Caffe做图像分类
Caffe是目前深度学习比较优秀好用的一个开源库,采样c++和CUDA实现,具有速度快,模型定义方便等优点团租。学习了几天过后,发现也有一个不方便的地方,就是在我的程序中调用Caffe做图像分类没有直接的接口。Caffe的数据层可以从数据库(支持leveldb、lmdb、hdf5)、图片、和内存中读入。我们要在程序中使用,当然得从内存中读入,我们首先在模型定义文件中定义数据层:
layers {
name: “mydata”
type: MEMORY_DATA
top: “data”
top: “label”
transform_param {
scale: 0.
}
memory_data_param {
batch_size: 10
channels: 1
height: 24
width: 24
}
}
这里必须设置memory_data_param中的四个参数,对应这些参数可以参见源码中caffe.proto文件。现在,我们可以设计一桐或森个Classifier类来封装一下:
#ifndef CAFFE_CLASSIFIER_H
#define CAFFE_CLASSIFIER_H
#include
#include
#include “caffe/net.hpp”
#include “caffe/data_layers.hpp”
#include
using cv::Mat;
namespace caffe {
template
class Classifier {
public:
explicit Classifier(const string& param_file, const string& weights_file);
Dtype test(vector &images, vector &labels, int iter_num);
virtual ~Classifier() {}
inline shared_ptr > net() { return net_; }
void predict(vector &images, vector *labels);
void predict(vector 局亩&data, vector *labels, int num);
void extract_feature(vector &images, vector> *out);
protected:
shared_ptr > net_;
MemoryDataLayer *m_layer_;
int batch_size_;
int channels_;
int height_;
int width_;
DISABLE_COPY_AND_ASSIGN(Classifier);
};
}//namespace
#endif //CAFFE_CLASSIFIER_H
构造函数中我们通过模型定义文件(.prototxt)和训练好的模型(.caffemodel)文件构造一个Net对象,并用m_layer_指向Net中的memory data层,以便待会调用MemoryDataLayer中AddMatVector和Reset函数加入数据。
#include
#include
#include
#include
#include “caffe/net.hpp”
#include “caffe/proto/caffe.pb.h”
#include “caffe/util/io.hpp”
#include “caffe/util/math_functions.hpp”
#include “caffe/util/upgrade_proto.hpp”
#include “caffe_classifier.h”
namespace caffe {
template
Classifier::Classifier(const string& param_file, const string& weights_file) : net_()
{
net_.reset(new Net(param_file, TEST));
net_->CopyTrainedLayersFrom(weights_file);
//m_layer_ = (MemoryDataLayer*)net_->layer_by_name(“mnist”).get();
m_layer_ = (MemoryDataLayer*)net_->layers().get();
batch_size_ = m_layer_->batch_size();
channels_ = m_layer_->channels();
height_ = m_layer_->height();
width_ = m_layer_->width();
}
template
Dtype Classifier::test(vector &images, vector &labels, int iter_num)
{
m_layer_->AddMatVector(images, labels);
//
int iterations = iter_num;
vector* > bottom_vec;
vector test_score_output_id;
vector test_score;
Dtype loss = 0;
for (int i = 0; i *>& result =
net_->Forward(bottom_vec, &iter_loss);
loss += iter_loss;
int idx = 0;
for (int j = 0; j cpu_data();
for (int k = 0; k count(); ++k, ++idx) {
const Dtype score = result_vec;
if (i == 0) {
test_score.push_back(score);
test_score_output_id.push_back(j);
} else {
test_score += score;
}
const std::string& output_name = net_->blob_names()
net_->output_blob_indices()>;
LOG(INFO)
void Classifier::predict(vector &images, vector *labels)
{
int original_length = images.size();
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i valid_labels, predicted_labels;
valid_labels.resize(valid_length, 0);
m_layer_->AddMatVector(images, valid_labels);
vector* > bottom_vec;
for(int i = 0; i *>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result->cpu_data();
for(int j = 0; j count(); j++)
{
predicted_labels.push_back(result_vec);
}
}
if(original_length != valid_length)
{
images.erase(images.begin()+original_length, images.end());
}
labels->resize(original_length, 0);
std::copy(predicted_labels.begin(), predicted_labels.begin() + original_length, labels->begin());
}
template
void Classifier::predict(vector &data, vector *labels, int num)
{
int size = channels_*height_*width_;
CHECK_EQ(data.size(), num*size);
int original_length = num;
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i predicted_labels;
Dtype * label_ = new Dtype;
memset(label_, 0, valid_length);
m_layer_->Reset(data.data(), label_, valid_length);
vector* > bottom_vec;
for(int i = 0; i *>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result->cpu_data();
for(int j = 0; j count(); j++)
{
predicted_labels.push_back(result_vec);
}
}
if(original_length != valid_length)
{
data.erase(data.begin()+original_length*size, data.end());
}
delete label_;
labels->resize(original_length, 0);
std::copy(predicted_labels.begin(), predicted_labels.begin() + original_length, labels->begin());
}
template
void Classifier::extract_feature(vector &images, vector> *out)
{
int original_length = images.size();
if(original_length == 0)
return;
int valid_length = original_length / batch_size_ * batch_size_;
if(original_length != valid_length)
{
valid_length += batch_size_;
for(int i = original_length; i valid_labels;
valid_labels.resize(valid_length, 0);
m_layer_->AddMatVector(images, valid_labels);
vector* > bottom_vec;
out->clear();
for(int i = 0; i *>& result = net_->Forward(bottom_vec);
const Dtype * result_vec = result->cpu_data();
const int dim = result->count(1);
for(int j = 0; j num(); j++)
{
const Dtype * ptr = result_vec + j * dim;
vector one_;
for(int k = 0; k push_back(one_);
}
}
if(original_length != valid_length)
{
images.erase(images.begin()+original_length, images.end());
out->erase(out->begin()+original_length, out->end());
}
}
INSTANTIATE_CLASS(Classifier);
} // namespace caffe
由于加入的数据个数必须是batch_size的整数倍,所以我们在加入数据时采用填充的方式。
CHECK_EQ(num % batch_size_, 0)
“The added data must be a multiple of the batch size.”; //AddMatVector
在模型文件的最后,我们把训练时的loss层改为argmax层:
layers {
name: “predicted”
type: ARGMAX
bottom: “prob”
top: “predicted”
opencv mnist数据库的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于opencv mnist数据库,OpenCV实现手写数字识别:使用MNIST数据库,如何在程序中调用Caffe做图像分类 / 蓝讯,如何在程序中调用Caffe做图像分类,调用caffe图像分类,如何在程序中调用Caffe做图像分类的信息别忘了在本站进行查找喔。
来源地址:OpenCV实现手写数字识别:使用MNIST数据库 (opencv mnist数据库)
转载声明:本站文章若无特别说明,皆为原创,转载请注明来源:www.88531.cn资享网,谢谢!^^