初步认识
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
//声明一些全局变量,比如图像、模板和结果矩阵,以及匹配方法和窗口名称
Mat img; Mat templ; Mat result;
char* image_window = "Source Image";
char* result_window = "Result window";
int match_method;
int main(int argc, char* argv[])
{
//图片加载
img = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\1.png");
templ = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\template\\1.png");
namedWindow(image_window, WINDOW_AUTOSIZE);
namedWindow(result_window, WINDOW_AUTOSIZE);
//创建将存储每个模板位置匹配结果的结果矩阵。详细观察结果矩阵的大小(匹配所有可能的位置)
int result_cols = img.cols - templ.cols + 1;
int result_rows = img.rows - templ.rows + 1;
result.create(result_rows, result_cols, CV_32FC1);
//执行模板匹配
matchTemplate(img, templ, result, match_method);
// 归一化
normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
//利用cv::minMaxLoc对结果矩阵R中的最小值和最大值进行局部定位
double minVal; double maxVal; Point minLoc; Point maxLoc;
Point matchLoc;
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());
//对于前两个方法(TM_SQDIFF和MT_SQDIFF_NORMED),最好的匹配是最小值。
//对于其他来说,值越高代表匹配越好。因此,我们将相应的值保存在matchLoc变量中:
if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
{
matchLoc = minLoc;
}
else
{
matchLoc = maxLoc;
}
//显示源图像和结果矩阵。在可能匹配的最高区域周围画一个矩形
rectangle(img, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
imshow( image_window, img);
imshow( result_window, result );
waitKey(0);
destroyAllWindows();
}
-
大图是匹配的效果
字符识别算法
想法:把所有的字符模板放到一张图片,根据其各自位置建一个列表,将分割好的图片一次检测匹配,根据其在模板大图上的位置,根据索引在列表找到对应的字符
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <imgproc.hpp>
#include <iostream>
#include <map>
using namespace cv;
using namespace std;
Mat getPos(Mat& image);//定位到字串的位置
void segChar(Mat& image);//字符分割 保存至文件夹
void charTem(Mat& image);//利用图像融合,做一个字符模板
string recog();//读取文件夹中char 图片,进行模板匹配,找到字符
//建立对应的字符字典
map<int, string> maps = { {0, "N",},
{1, "J",},
{2, "S",},
{3, "G",},};
int pos[4] = {};
int main(int argc, char* argv[])
{
//图片加载
Mat img;
img = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\1.png");
//定位 并取出文字区域
Mat pos_img;
pos_img = getPos(img);
//分割每个字符图片 保存至文件夹
segChar(pos_img);
//实现字符的模板
charTem(img);
//识别
string chars = recog();
cout << chars << endl;
imshow("test1", pos_img);
waitKey(0);
destroyAllWindows();
}
Mat getPos(Mat& image)
{
Mat ret;
Mat img_copy;
image.copyTo(img_copy);
Mat img = img_copy;
//!!! 图像预处理 部分
cvtColor(img_copy, img_copy, COLOR_BGR2GRAY);
bitwise_not(img_copy, img_copy);//黑白像素取反
GaussianBlur(img_copy, img_copy, Size(3, 3), 0, 0);//高斯模糊,消除噪点
threshold(img_copy, img_copy, 50, 255, THRESH_BINARY | THRESH_OTSU);//阈值化
dilate(img_copy, img_copy, Mat(), Point(-1, -1),5);//膨胀
int morph_size = 2;//闭运算,把字符之间进一步缩短,变成一团
Mat element = getStructuringElement(MORPH_RECT, Size(morph_size * 2, morph_size * 2), Point(morph_size, morph_size));
morphologyEx(img_copy, img_copy,MORPH_CLOSE,element,Point(-1,1),3);
//!!! 找轮廓部分
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(img_copy, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
//获取轮廓的左上和右下两个坐标位置,左上是坐标最小的,右下是坐标最大的
int left[2] = { contours[0][0].x, contours[0][0].y };
int right[2] = { contours[0][0].x, contours[0][0].y };
for (size_t i = 0; i < contours.size(); i++)
{
for (size_t j = 1; j < contours[i].size(); j++)
{
if (left[0] > contours[i][j].x) {
left[0] = contours[i][j].x;
}
if (left[1] > contours[i][j].y) {
left[1] = contours[i][j].y;
}
if (right[0] < contours[i][j].x) {
right[0] = contours[i][j].x;
}
if (right[1] < contours[i][j].y) {
right[1] = contours[i][j].y;
}
}
}
//在原图中框出区域
//rectangle(ret,Point(left[0],left[1]), Point(right[0],right[1]),(255));
//取出框中的图像
ret = img(Rect(left[0], left[1], right[0]- left[0], right[1] - left[1]));//参数是 矩形区域的左上角坐标和对应的x,y的边长
return ret;
}
void segChar(Mat& image)
{
Mat img_copy;
image.copyTo(img_copy);
//!!! 图像预处理 部分
cvtColor(img_copy, img_copy, COLOR_BGR2GRAY);
bitwise_not(img_copy, img_copy);//黑白像素取反
GaussianBlur(img_copy, img_copy, Size(3, 3), 0, 0);//高斯模糊,消除噪点
threshold(img_copy, img_copy, 50, 255, THRESH_BINARY | THRESH_OTSU);//阈值化
dilate(img_copy, img_copy, Mat(), Point(-1, -1), 3);//膨胀
erode(img_copy, img_copy, Mat(), Point(-1, -1), 3);
//!!! 投影法进行字符分割,投影到水平方向,那就是统计每个横坐标上的那一列有多少个白色的像素
//! 画出像素投影图
int src_width = img_copy.cols;
int src_height = img_copy.rows;
int* projectValArry = new int[src_width]();//创建用于储存每列白色像素个数的数组
//memset(projectValArry, 0, src_width*4);//初始化数组
//取列白色像素个数
for (int i = 0; i < src_height; i++) {
for (int j = 0; j < src_width; j++) {
if (img_copy.at<uchar>(i, j)) {
projectValArry[j]++;
}
}
}
//将每列白色像素数目绘制成直方图
//定义画布 绘制垂直投影下每列白色像素的数目
Mat verticalProjectionMat(src_height, src_width, CV_8UC1, Scalar(0));
for (int i = 0; i < src_width; i++) {
for (int j = 0; j < projectValArry[i]; j++) {
verticalProjectionMat.at<uchar>(src_height - j - 1, i) = 255;
}
}
imshow("verticalProjectionMat", verticalProjectionMat);//汉字的分割确实没有英文效果好
//! 根据每列白色像素数目设置截取起始和截止列
vector<Mat> split_src;//定义Mat vector ,存储图片组
bool white_block = 0, black_block = 0;//定义标志,用来指示在白色像素区还是在全黑区域
int temp_col_forword = 0, temp_col_behind = 0;//定义列temp_col_forword temp_col_behind,记录字符截取起始列和截止列
Mat split_temp;
//遍历数组projectValArry,统计每列的白色像素,投影到水平区域
for (int i = 0; i < src_width; i++) {
if (projectValArry[i]) {//表示区域有白色像素
white_block = 1;
black_block = 0;
}
else { //若无白色像素(进入黑色区域)
if (white_block == 1) {//若前一列有白色像素
temp_col_behind = i;//取当前列为截止列
//截取下一部分
split_temp = img_copy(Rect(temp_col_forword, 0, temp_col_behind - temp_col_forword, src_height)).clone();
split_src.push_back(split_temp);//将截取玩的图片存入列表中
}
temp_col_forword = i;//记录最新黑色区域的列号,记为起始列
black_block = 1;//表示进入黑色区域
white_block = 0;
}
}
//! 将截出来的图片组放入文件夹
for (size_t i = 0; i < split_src.size(); i++)
{
string img_Name = "E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\" + to_string(i) + ".jpg";
imwrite(img_Name, split_src[i]);
}
imshow("test2", img_copy);
}
void charTem(Mat& image)
{
Mat img = Mat::zeros(image.size(), image.type());
int img_col = img.cols;
int img_row = img.rows;
//cout << img_col << " : " << img_row << endl;//778 : 270
//依次将几个字符按顺序图像融合,注意各自的坐标位置
Mat img1 = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\0.jpg");
cout << img1.size() << endl;
Mat imageROI1 = img(Rect(100, 100, img1.cols, img1.rows));// 设置图片1要放入模板的位置
pos[0] = 100;// 0位置 对应 字符 N
addWeighted(imageROI1, 0, img1, 1, 0, imageROI1);
Mat img2 = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\1.jpg");
Mat imageROI2 = img(Rect(200, 100, img2.cols, img2.rows));// 设置图片1要放入模板的位置
pos[1] = 200;// 0位置 对应 字符 J
addWeighted(imageROI2, 0, img2, 1, 0, imageROI2);
Mat img3 = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\2.jpg");
Mat imageROI3 = img(Rect(300, 100, img3.cols, img3.rows));// 设置图片1要放入模板的位置
pos[2] = 300;// 0位置 对应 字符 J
addWeighted(imageROI3, 0, img3, 1, 0, imageROI3);
Mat img4 = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\3.jpg");
Mat imageROI4 = img(Rect(400, 100, img4.cols, img4.rows));// 设置图片1要放入模板的位置
pos[2] = 400;// 0位置 对应 字符 J
addWeighted(imageROI4, 0, img4, 1, 0, imageROI4);
imshow("test3", img);
string img_Name = "E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\template\\temp.jpg";
imwrite(img_Name, img);
}
string recog()
{
string ret;
Mat temp = imread("E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\template\\temp.jpg");//模板
String pattern = "E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr";
vector<cv::String> fn;
glob(pattern, fn, false);
vector<Mat> images;
//size_t count = fn.size(); //number of png files in images folder,我这10个字符的图片
for (size_t i = 0; i < fn.size(); i++)
{
string img_Name = "E:\\研究生\\数字图像处理\\c++\\字符识别\\Project1\\ocr\\" + to_string(i) + ".jpg";
Mat img = imread(img_Name);
images.push_back(img);
}
//识别过程
Mat result;
int result_cols = temp.cols - images[0].cols + 1;
int result_rows = temp.rows - images[0].rows + 1;
int match_method = 0;
result.create(result_rows, result_cols, CV_32FC1);
vector<Point> loc;
for (size_t i = 0; i < images.size(); i++)
{
matchTemplate(temp, images[i], result, match_method);
// 归一化
normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());
//利用cv::minMaxLoc对结果矩阵R中的最小值和最大值进行局部定位
double minVal; double maxVal; Point minLoc; Point maxLoc;
Point matchLoc;
minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());
//对于前两个方法(TM_SQDIFF和MT_SQDIFF_NORMED),最好的匹配是最小值。
//对于其他来说,值越高代表匹配越好。因此,我们将相应的值保存在matchLoc变量中:
if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
{
matchLoc = minLoc;
}
else
{
matchLoc = maxLoc;
}
loc.push_back(matchLoc);
}
//根据matchloc 的数值进行字符的匹配
for (size_t i = 0; i < loc.size(); i++)
{
cout << loc[i].x << endl;
if (loc[i].x > 95 &loc[i].x < 105){
ret = ret + maps[0];
}
else if (loc[i].x > 195 & loc[i].x < 205) {
ret = ret + maps[1];
}
else if (loc[i].x > 295 & loc[i].x < 305) {
ret = ret + maps[2];
}
else if (loc[i].x > 395 & loc[i].x < 405) {
ret = ret + maps[3];
}
}
return ret;
}