燕山大学课程实践项目报告:ISBN号识别系统的设计与开发(下)

简介: 燕山大学课程实践项目报告:ISBN号识别系统的设计与开发

源代码


这个源代码仅供参考,给大家一个流程,不能运行就不能运行,理解思路就好,自己改进,我要是传一份正确率90多的,大家还比什么呢?


不要因为不能运行再找我了,也不要跟我要源代码了,问题可以告诉你,要源码没有


#include<opencv.hpp>
#include<opencv2/opencv.hpp>
#include<iostream>
#include<vector>
#include<string>
using namespace cv;
using namespace std;
void splitSample(char ch,int k,Mat inputImg)
{
  string st =  to_string(k) + ".jpg";
    imwrite("D:\\二级项目图片\\"+to_string(ch) +"."+ st, inputImg);
} 
//计算修正角度
double GetTurnTheta(Mat inputImg) {
  //计算垂直方向导数
  Mat yImg;
  Sobel(inputImg, yImg, -1, 0, 1, 5);
  //直线检测
  vector<Vec2f>lines;
  HoughLines(yImg, lines, 1, CV_PI / 180, 180);
  //计算旋转角度
  float thetas = 0;
  for (int i = 0; i < lines.size(); i++) {
    float theta = lines[i][1];
    thetas += theta;
  }
  if (lines.size() == 0) {//未检测到直线
    thetas = CV_PI / 2;
  }
  else {//检测到直线,取平均值
    thetas /= lines.size();
  }
  return thetas;
}
/*--------------------------------------------------------------------------------------------------------------------------
 Function: originalImgToGrayImg
 Description:将原图转化成灰度图
 ---------------------------------------------------------------------------------------------------------------------------
 Calls:NONE
 Called By: main
 Table Accessed: NONE
 Table Updated:  NONE
 ---------------------------------------------------------------------------------------------------------------------------
  Input:
      第一个参数:原图,Mat
      第二个参数:转出的灰度图,Mat
  Output : 灰度图
  Return :  None
  Others:    NONE
--------------------------------------------------------------------------------------------------------------------------*/
void originalImgToGrayImg(Mat inputImg, Mat& outputImg)
{
  int row = inputImg.rows;
  int col = inputImg.cols;
  outputImg.create(row, col, CV_8UC1);
  for (int i = 0; i < row; i++)
  {
    for (int j = 0; j < col; j++)
    {
      double sum = 0;
            //得到三个通道的像素值
      int b = inputImg.at<Vec3b>(i, j)[0];
        int g = inputImg.at<Vec3b>(i, j)[1];
      int r = inputImg.at<Vec3b>(i, j)[2];
      sum = b * 0.114 + g * 0.587 + r * 0.299;
      outputImg.at<uchar>(i, j) = static_cast<uchar>(sum);
    }
  }
}
//冒泡排序对非边界值与其八邻域的进行排序,找到中值
int BubbletoMedian(vector<int>& a)
{
  int median;
  for (int i = 0; i < 9; i++)
  {
    for (int j = 0; j < 9 - i - 1; j++)
    {
      if (a[j] > a[j + 1])
      {
        int temp;
        temp = a[j + 1];
        a[j + 1] = a[j];
        a[j] = temp;
      }
    }
  }
  median = a[4];
  return median;
}
//去噪处理
void denoising(Mat gray1, Mat& grayImg)
{
  grayImg = Mat(gray1.rows, gray1.cols, CV_8UC1);
  vector<int>temp(9); //定义九个方向
  int dx[9] = { 1,-1,1,-1,-1,0,1,0,0 };
  int dy[9] = { 1,-1,-1,1,0,1,0,-1,0 };
  for (int i = 0;i < gray1.rows;i++)
  {
    for (int j = 0;j < gray1.cols;j++)
    {
      //边缘部分不做处理
      if (i == 0 || i == gray1.rows - 1 || j == 0 || j == gray1.cols - 1)
      {
        grayImg.at<uchar>(i, j) = gray1.at<uchar>(i, j);
      }
      else
      {
        for (int k = 0;k < 9;k++)
        {
          temp[k] = gray1.at<uchar>(i + dx[k], j + dy[k]);
        }
        grayImg.at<uchar>(i, j) = BubbletoMedian(temp);
      }
    }
  }
}
//迭代法求阈值
void getThreshold(Mat grayImg, int &theThreshold)
{
  //1.求灰度直方图
    vector<int>histo(256);
    for (int i = 0;i < grayImg.rows;i++)
    {
        for (int j = 0;j < grayImg.cols;j++)
        {
            histo[grayImg.at<uchar>(i, j)]++;
        }
    }
  //2.根据上面的直方图 用迭代法求阈值
    int count0, count1;//count0,count1分别是大于t0和小于t0的像素点的个数
  int t0 = 127,t=0; //t0是初始的阈值,t是每一次经过迭代运算后的阈值 当t=t0时认为找到
  int z0, z1;      //z0,在z1分别是大于t0和小于t0的像素值的总和
     while (1)
     {
         count0=count1=z0 = z1= 0;
         for (int i=0;i<histo.size();i++)
         {
             if (i<=t0)
             {
                 count0+= histo[i];
                 z0 += i * histo[i];
             }
             else
             {
         count1 += histo[i];
         z1 += i * histo[i];
             }
         }
         t = (z0/count0 + z1/count1)/2;
         if (t0==t) break;
         else t0 = t;
     }
     theThreshold = t0-10;
}
// 根据灰度直方图得到二值图
Mat toBinaryGraph( Mat grayImg)
{
    int theThreshold;
    //求阈值
  getThreshold(grayImg, theThreshold);
    Mat binG=Mat(grayImg.rows,grayImg.cols, CV_8UC1);
  for (int i = 0;i < grayImg.rows;i++)
  {
    for (int j = 0;j < grayImg.cols;j++)
    {
              if (grayImg.at<uchar>(i, j) < theThreshold)binG.at<uchar>(i, j) = 255;
               else binG.at<uchar>(i, j) = 0;
    }
  }
    return binG;
}
//水平投影找到行
pair<int, int> SelectRow2(Mat inputImg)
{
    pair<int, int>p;             //记录ISBN所在的上界(p.first)和下届(p.second)
    vector<int>arr(inputImg.rows); //存储每行的水平投影的结果
  for (int i = 0;i < inputImg.rows;i++)
  {
        //水平投影
    for (int j = 0;j < inputImg.cols;j++)
            if (inputImg.at<uchar>(i, j) != 0)arr[i]++;
        //当此行的水平投影值大于指定阈值表示找到上届 
        if (arr[i] > 10) {
            p.first = i;break;
        }
  }
  for (int i = p.first;i < inputImg.rows;i++)
  {
        //水平投影
    for (int j = 0;j < inputImg.cols;j++)
      if (inputImg.at<uchar>(i, j) != 0)arr[i]++;
        //当此行的水平投影值小于指定阈值表示找到下届 
        if (arr[i] < 10) {
            p.second = i;break;
        }
  }
    //有些图片不规则,图中没有全零行单独处理
    if (p.second-p.first<=10) p.second = p.first+32;
    return p;
}
pair<int, int> SelectRow(Mat inputImg)
{
  pair<int, int>a;vector<int>arr(inputImg.rows);
  bool find = false;
  for (int i = 0; i < inputImg.rows; i++)
  {
    for (int j = 0; j < inputImg.cols; j++)
    {    //0表示黑 255表示白
      if (inputImg.at<uchar>(i, j) == 255)
      {
        a.first = i;
        find = true;
        break;
      }
      if (find)break;
    }
  }
  for (int i = a.first + 1; i < inputImg.rows; i++)
  {
    for (int j = 0; j < inputImg.cols; j++)
    {
      //0表示黑 255表示白
      if (inputImg.at<uchar>(i, j) == 255)
        arr[i]++;//这里是在水平投影
    }
    if (arr[i] == 0)
    {
      a.second = i;break;
    }
  }
  //有些图片不规则,图中没有全零行模糊处理
  if (a.second - a.first <= 10) a.second = a.first + 32;
  return a;
}
//竖直投影分割列
void sliptCol(Mat inputImg, vector<pair<int, int> >& a)
{
    vector<int>theCol(inputImg.cols);
    //做竖直投影
    for (int i=0;i<inputImg.rows;i++)
    {
        for (int j=0;j<inputImg.cols;j++)
        {
            if (inputImg.at<uchar>(i, j) != 0) 
                theCol[j]++;
        }
    }
    //用nums区分走右边界,num为偶数则代表左边界 num 为奇数是右边界
    int num = 0;pair<int, int>p;
  for (int j = 0;j < inputImg.cols;j++)
  {
        //用theCol[j] >= 3判断 适当把截取范围取大一点
        if (theCol[j] >= 3 && num % 2 == 0) 
        {
            num++;
            p.first = j;j += 2;
    }
        else if (theCol[j] == 0 && num % 2 != 0) 
        { 
            num++;
            p.second = j;
            a.push_back(p);j += 2;
        }
  }
}
//找最小矩形框
void findMinRectangle(Mat inputImg, int& st, int& ed, int& le, int& ri)
{
    vector<int>hProjectoin(inputImg.rows);
    vector<int>vProjectoin(inputImg.cols);
    for (int i=0;i<inputImg.rows;i++)
    {
        for (int j=0;j<inputImg.cols;j++)
        {
            if (inputImg.at<uchar>(i, j) != 0) { hProjectoin[i]++;vProjectoin[j]++; }
        }
    }
   st = le = 0;ed = inputImg.rows;ri = inputImg.cols;
  for (int i = 0;i < inputImg.rows;i++)
    if (hProjectoin[i] != 0) ed = i;
  for (int i = 0;i < inputImg.cols;i++)
    if (vProjectoin[i] != 0)ri = i;
  for (int i = inputImg.rows - 1;i >= 0;i--)
    if (hProjectoin[i] != 0) st = i;
  for (int i = inputImg.cols - 1;i >= 0;i--)
    if (vProjectoin[i] != 0)le = i;
}
//两张图做差(统计两张图片不同点像素点的个数)
double absDi(Mat inputImg, Mat sampleImg)
{
    //记录两张图不同的像素点的个数
  double diffNums = 0;double sameNums = 0;
    for (int i=0;i<inputImg.rows;i++)
    {
        for (int j=0;j<inputImg.cols;j++)
        {
            //对应位置的像素值不同 则diffnums++
      if (inputImg.at<uchar>(i, j) != sampleImg.at<uchar>(i, j))
        diffNums++;
      else sameNums++;
        }
    }
    return diffNums;
}
//字符识别
char recognition(Mat inputImg,int k)
{
    string sampleImgPath = "样例3";
    vector<String> sampleImgFN;
    glob(sampleImgPath, sampleImgFN, false);
    int sampleImgNums = sampleImgFN.size();
    vector<pair< double,int> >nums(sampleImgNums+1);
    for (int i = 0; i < sampleImgNums; i++) {
        nums[i].second= i;
        Mat numImg = imread(sampleImgFN[i], 0);
        //大小同一
        resize(numImg, numImg, Size(40, 60));
        resize(inputImg,inputImg,Size(40,60));
        nums[i].first = absDi(inputImg, numImg);
    }
  imshow("图片", inputImg);
  waitKey();
  //排序 越小说明匹配度越高
  sort(&nums[0], &nums[sampleImgNums]);
    int index= nums[0].second;
  //截取模板的函数
  //splitSample(sampleImgFN[index][sampleImgPath.size() + 1],k,inputImg);
  return sampleImgFN[index][sampleImgPath.size() + 1];
   /* if (index >= 0 && index <= 9)
        return index+'0';
    else if (index == 10) return 'B';
    else if (index == 11) return 'I';
    else if (index == 12) return 'N';
    else if (index == 13) return 'S';
    else if (index == 14) return 'X';
    else return ' ';*/
}
int main()
{
    int rtNums = 0, accNums = 0, sunNums = 0;//分别代表:正确的数量,被准确识别的字符的数量,要识别的字符的总和
   //读取 ISBN 图片
    string testImgPath = "数据集B/*";
    vector<String> testImgFN;//必须cv的String
    glob(testImgPath, testImgFN, false);
    int testImgNums = testImgFN.size();
    for (int index =12; index < testImgNums; index++) {
        Mat src = imread(testImgFN[index]);
        //图片大小统一
        double width = 700;
        double height = width * src.rows / src.cols;
        resize(src, src, Size(width, height));
        imshow("原图",src);
        Mat gray1;
        //原图转灰度图
        originalImgToGrayImg(src, gray1);
        imshow("灰度图", gray1);
        //去噪处理
        denoising(gray1,gray1);
        imshow("去噪后的图片", gray1);
        //二值化
        Mat binImg = toBinaryGraph(gray1);
        imshow("二值图", binImg);
        //计算调整角度
        double thetas = GetTurnTheta(binImg);
        thetas = 180 * thetas / CV_PI - 90;
        //旋转二值图像
        Mat bin;
        Mat M = getRotationMatrix2D(Point(width / 2, height / 2), thetas, 1);
        warpAffine(binImg, bin, M, binImg.size());
        imshow("旋转后的二值图", bin); 
    //waitKey();
        //旋转原图
    Mat bin2;
    warpAffine(src, bin2, M, src.size());
        imshow("旋转的原图", bin2);
        //从原图上截取ISBN所在行
        Mat temp = Mat(bin2, Range(SelectRow2(bin).first, SelectRow2(bin).second+1), Range(0, bin.cols));
        imshow("原图上的所在行",temp);
        //大小统一一
        resize(temp,temp, Size(60 * (temp.cols / temp.rows), 60));
        //转灰度图
        Mat gray2;
        originalImgToGrayImg(temp, gray2);
        //去噪处理
        denoising(gray2,gray2);
        //二值化
    Mat binImg2 = toBinaryGraph(gray2);
        imshow("原图所在行二值化 ", binImg2); 
       //waitKey();
        //分割ISBN所在行
        vector<pair<int, int> >a;//{(110,125),(135,150)。。。。}
        sliptCol(binImg2,a);
        int st, ed, le, ri;
        string result = "";
        for (int i = 0;i < a.size();i++)
        {
              //做列分割
              Mat subImg = Mat(binImg2, Range(0, binImg2.rows), Range(a[i].first, a[i].second+1));
              findMinRectangle(subImg, st, ed, le, ri);
              //ed - st > subImg.rows / 2如果最小矩形的高小于原来的一半 判断为‘-’宽过小判断为杂纹
              if (ed - st > subImg.rows / 2&&ri-le>3)
              {
                  //切出最小矩形的
                  Mat minImg = Mat(subImg, Range(st, ed+1), Range(le, ri+1));
                  imshow("最小矩形", minImg);
                  //模板匹配得到字符
                  char ch = recognition(minImg,index);
          if (ch >= '0' && ch <= '9' || ch == 'X') {
            result += ch;
            cout << ch << " ";
          }
              }
          }  cout << endl;
      //确定正确的 ISBN 号,来跟识别出来的 ISBN 做对比
      string cmpData = "";
      for (int i = 0; i < testImgFN[index].length(); i++) {
        if (testImgFN[index][i] >= '0' && testImgFN[index][i] <= '9' || testImgFN[index][i] == 'X' ){//|| testImgFN[index][i] == 'I' || testImgFN[index][i] == 'S' || testImgFN[index][i] == 'B' || testImgFN[index][i] == 'N') {
          cmpData += testImgFN[index][i];
        }
      }
          cout << cmpData << endl;
          sunNums += cmpData.length();
          cout << result <<endl<<index << endl;
          int times = min(cmpData.length(), result.length());
          bool flag = true;
          for (int i=0;i<times;i++)
          {
              if (cmpData[i] != result[i])
                  flag = false;
              else accNums++;
          }
          if (flag&&result.length()==cmpData.length()) { rtNums++; cout << "Yes" << endl; }
          else cout << "No" << endl;
    }
  printf("正确个数:%4.d 正确率:%f\n", rtNums, rtNums * 1.0 / testImgNums);
  printf("准确个数:%4.d 准确率:%f\n", accNums, accNums * 1.0 / sunNums);
  waitKey(0);
} 
相关文章
|
7月前
|
JavaScript 小程序 Java
基于Java的高校宣讲会管理系统设计与实现(亮点:选题新颖、功能实用、老师看见直接过!)
基于Java的高校宣讲会管理系统设计与实现(亮点:选题新颖、功能实用、老师看见直接过!)
53 0
|
2月前
|
Shell
红队渗投测试术语-课程笔记
红队渗投测试术语-课程笔记
|
7月前
|
Java 关系型数据库 MySQL
体育赛事管理系统的设计与实现(源码+论文)_kaic
体育赛事管理系统的设计与实现(源码+论文)_kaic
|
4月前
|
机器学习/深度学习 人工智能 自然语言处理
【专家系统】专家系统概述,应用场景,项目实践及案例分析,附带代码示例
专家系统是一种智能计算机程序系统,它包含了某个领域专家水平的知识与经验,能够应用人工智能技术和计算机技术,根据系统中的知识与经验进行推理和判断,模拟人类专家的决策过程,以解决那些需要人类专家处理的复杂问题。
516 1
|
7月前
|
存储 安全 Java
小说阅读平台设计与实现
小说阅读平台设计与实现
|
7月前
|
前端开发 关系型数据库 Java
学院综合绩效评价系统的设计与实现(论文+源码)_kaic
学院综合绩效评价系统的设计与实现(论文+源码)_kaic
|
7月前
|
小程序 JavaScript Java
基于Java的大学生心理健康答题小程序设计与实现(亮点:选题新颖、可以发布试卷设置题目、自动判卷、上传答案、答案解析)
基于Java的大学生心理健康答题小程序设计与实现(亮点:选题新颖、可以发布试卷设置题目、自动判卷、上传答案、答案解析)
98 0
|
7月前
|
监控 JavaScript Java
开题报告|基于Springboot的课程知识库的设计与实现
开题报告|基于Springboot的课程知识库的设计与实现
131 0
|
测试技术 开发者
软件工程高效学 | 实战案例:在线选修课程管理系统设计
软件工程是计算机领域的一门专业基础课,它对于培养开发者的软件素质、提高开发者的软件开发能力与软件项目管理能力具有重要意义。本篇介绍了利用Rational Rose进行“在线选修课程管理系统”面向对象的设计案例。
207 0
软件工程高效学 | 实战案例:在线选修课程管理系统设计
|
SQL 存储 搜索推荐
基于线上考研资讯数据抓取的推荐系统的设计与实现(论文+源码)_kaic
随着互联网的飞速发展,互联网在各行各业的应用迅速成为众多学校关注的焦点。他们利用互联网提供电子商务服务,然后有了“考研信息平台”,这将使学生考研的信息平台更加方便和简单。 对于考研信息平台的设计,大多采用java技术。在设计了一个搭载mysal数据库的全人系统,是根据目前网上考研信息平台的情况,专门开发的,专门根据学生的需要,实现网上考研信息平台的在线管理,并定期进行各种信息存储,进入考研信息平台页面后,即可开始操作主控界面。系统功能包括学生前台:首页、考研信息、申请指南、资料信息、论坛信息、我的、跳转到后台、购物车、客服、管理员:首页、人人中心、研究生信息管理、学生管理、申请指南管理、资料信