OpenCV实现手写数字识别:使用MNIST数据库 (opencv mnist数据库)

随着技术的不断发展,图像识别已成为重要的应用领域之一。在众多的图像识别问题中,手写数字识别是一项极具挑战性的任务,因为不同的手写风格可能导致数字形状不同。本文将介绍如何使用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数据库)