是这样的,我是大一学生,我们期末有一项要求需要每个人想一个实用的程序项目并去实现,我想到的一个主题是身份证数字识别,具体是读取一张身份证图片,程序自动给出身份证数字。我的程序已经基本上写好了,但是不知道为什么识别的很不准确(识别的方法是使用matchtemplate函数),比如5会识别成3,我看了别人使用相同的方法但是识别很准确(悲)。有大佬可以帮忙看一下吗,真的非常感谢
#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>
#define DEFAULT_CARD_WIDTH 640
#define DEFAULT_CARD_HEIGHT 400
#define FIX_IDCARD_SIZE Size(DEFAULT_CARD_WIDTH, DEFAULT_CARD_HEIGHT)
#define FIX_TEMPLATE_SIZE Size(153, 28)
using namespace std;
using namespace cv;
int main() {
Mat src = imread("C:\\Users\\86188\\Desktop\\1.png");//先载入一张身份证图像
//处理身份证
Mat src_img = src;
//1、无损压缩 640*400 (通用卡片类的处理方式)
resize(src_img, src_img, FIX_IDCARD_SIZE);
Mat dst_img;
//2、灰度化
Mat dst;
cvtColor(src_img, dst, COLOR_BGR2GRAY);
imshow("gray", dst);
Mat temp;
//3、二值化(降噪)
threshold(dst, temp, 100, 255, THRESH_BINARY);
Mat r;
// 4.1 腐蚀、膨胀
Mat erodeElement = getStructuringElement(MORPH_RECT, Size(20, 10));
erode(temp, r, erodeElement);
imshow("erode", r);
//4、轮廓检测,把所有的连续的闭包用矩形包起来
/*
* 一个矩形用两个点表示,contours就包含了很多矩形
*/
vector<vector<Point>> contours;
vector<Rect> rects;
findContours(r, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
for (size_t i = 0; i < contours.size(); i++)
{
// 基于两点构建矩形
Rect rect = boundingRect(contours.at(i));
// 绘制矩形
rectangle(r, rect, Scalar(0, 0, 255));
imshow("contours", dst);
// 对符合条件的图片进行筛选,宽高比大于1:9的
if (rect.width > rect.height * 9)
{
cout << "找到了" << endl;
rects.push_back(rect);
rectangle(r, rect, Scalar(0, 0, 255));
}
}
// 如果只找到了一个矩形,说明这个就是,如果多个就找出纵坐标最低的矩形
if (rects.size() == 1)
{
Rect rect = rects.at(0);
dst_img = temp(rect);
}
else
{
int lowPoint = 0;
Rect finalRect;
for (size_t i = 0; i < rects.size(); ++i)
{
Rect rect = rects.at(i);
Point p = rect.tl();
if (rect.tl().y > lowPoint)
{
lowPoint = rect.tl().y;
finalRect = rect;
}
}
rectangle(r, finalRect, Scalar(255, 255, 0));
dst_img = temp(finalRect);//在'temp'上绘制所需要的身份证数字区域矩形
}
bitwise_not(dst_img, dst_img);//我这里将白底黑字反色
//GaussianBlur(dst_img, dst_img, Size(1, 1), 0);//高斯模糊
//Mat erodeElement1 = getStructuringElement(MORPH_RECT, Size(2, 2));
//erode(dst_img, dst_img, erodeElement1);//以上两行为腐蚀操作
//threshold(dst_img, dst_img, 100, 255, THRESH_BINARY);//二值化,图像一下子由模糊变清晰
//float dilationSize = 0.9; // 膨胀的尺寸
//Mat element = getStructuringElement(MORPH_RECT, Size(2 * dilationSize + 1, 2 * dilationSize + 1), Point(dilationSize, dilationSize));
//dilate(dst_img, dst_img, element);//上三行都是用来膨胀的,本来的字符太细了
//以上都是可以对dst_img再进行改变的一系列操作
imshow("身份证数字模块", dst_img);
double width = dst_img.cols; // 获取图像宽度
double height = dst_img.rows; // 获取图像高度
double digitWidth = width / 19; // 这里的digitWidth是用来重新改变模板大小所用的参数
vector<string> matchedNumbers; // 存储匹配的数字
vector<vector<Point>> edges;//用来存储边缘,用于后面的按边缘分割数字
vector<Rect> reps;//存储轮廓
findContours(dst_img, edges, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
for (size_t i = 0; i < edges.size(); i++)
{
// 获取当前数字的轮廓
Rect reps = boundingRect(edges.at(i));
// 计算当前数字的起始横坐标
double x = reps.x;
// 提取当前数字的图像
Mat digitImg = dst_img(reps);
//resize(digitImg, digitImg, Size(240, 360));
//GaussianBlur(digitImg, digitImg, Size(3, 3), 0);
//threshold(digitImg, digitImg, 100, 255, THRESH_BINARY);//二值化,图像一下子由模糊变清晰
//Mat erodeElement1 = getStructuringElement(MORPH_RECT, Size(3,6));
//erode(digitImg, digitImg, erodeElement1);
imshow("依据边缘轮廓切割后的数字" + to_string(i), digitImg);
// 进行模板匹配
double maxMatchVal = 0;
string maxMatchNum;
for (int j = 0; j < 10; j++) {
string templatePath = "C:\\Users\\86188\\Desktop\\numbers\\" + to_string(j) + ".png";
Mat templateImg = imread(templatePath, IMREAD_GRAYSCALE);
resize(templateImg, templateImg, Size(digitWidth, height));
//imshow("处理后的模板" + to_string(j), templateImg);//在循环里展示要用to_string()语句,实际上是给每个窗口赋不同名称
Mat result;
matchTemplate(digitImg, templateImg, result, TM_CCORR_NORMED);
double matchVal;
minMaxLoc(result, nullptr, &matchVal, nullptr, nullptr);
if (matchVal > maxMatchVal) {
maxMatchVal = matchVal;
maxMatchNum = to_string(j);
}//这里都是索引算法,用来循环寻找最合适的匹配
}
matchedNumbers.push_back(maxMatchNum); // 将匹配的数字添加到容器中
}
// 输出匹配的数字
cout << "匹配的数字:";
string idNumber;
for (const string& num : matchedNumbers) {
cout << num << " ";
idNumber = idNumber+num;
}
Mat bg(src.rows, src.cols * 2, CV_8UC3, Scalar(255, 255, 255));
Mat sho(bg, Rect(0, 0, src.cols, src.rows));
src.copyTo(sho);
putText(bg, idNumber, Point(src.cols, 0.9*src.rows), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 0), 2);
namedWindow("身份证信息", WINDOW_NORMAL);
imshow("身份证信息", bg);
waitKey();
return 0;
}