基于投影法的多行数字图像识别

模板图像

图像载入

/*输入图像*/
Mat img;
img = imread("E:/C语言/opencv/基于opencv垂直投影法的图像数字识别/test.png");


灰度图像转换

/*灰度图像*/
Mat grayimg;
cvtColor(img, grayimg, COLOR_BGR2GRAY);


二值化图像转换

/*二值图像*/
Mat binimg;
//第4个参数为CV_THRESH_BINARY_INV是因为输入原图为白底黑字
//若为黑底白字则选择CV_THRESH_BINARY即可
threshold(grayimg, binimg, 100, 255, CV_THRESH_BINARY_INV);


 注:因测试图像为白底黑字简单图像未增加图像预处理部分,若为背景复杂的图像需增加图像预处理部分
图像分割

方向投影法主要思想就是记录每一行或者每一列对应值的像素的个数,然后根据这些个数判断它是不是边界或者是目标物体。其中像素的个数就像是一个阈值,最后可以把每一行点的个数画出来便于直观的观察。垂直(或水平)投影法的就是,利用二值化图像素分布直方图进行分析,从而找出相邻字符的分界点进行分割。

根据测试图像特点,先基于水平投影法进行水平图像分割,后基于垂直投影法进行垂直图像分割。

/*垂直投影*/
int vertical_projection(const Mat& src, vector& roiList)
{//step1. 计算竖直投影白色点数量int w = src.cols;int h = src.rows;vector project_val_arry;for (int j = 0; j < w; j++)//列{Mat j_im = src.col(j);int num = countNonZero(j_im);project_val_arry.push_back(num);}//垂直投影显示Mat hist_im(h, w, CV_8UC1, Scalar(255));for (int i = 0; i < w; i++){for (int j = 0; j < project_val_arry[i]; j++){hist_im.ptr(h - 1 - j)[i] = 0; //第h-1-j行第i个数据}}//imshow("project_v", hist_im);//step2. 字符分割//vector roiList;int startIndex = 0;int endIndex = 0;bool inBlock = false;//是否遍历到了字符区内for (int i = 0; i < w; ++i){if (!inBlock && project_val_arry[i] != 0)//进入字符区了{inBlock = true;startIndex = i;//cout << "startIndex is " << startIndex << endl;}else if (project_val_arry[i] == 0 && inBlock)//进入空白区了{endIndex = i;inBlock = false;Mat roiImg = src(Rect(startIndex, 0, endIndex + 1 - startIndex, h));roiList.push_back(roiImg);}}return 0;
}/*水平投影*/
int horizontal_projection(const Mat& src, vector& coiList)
{//step1. 计算水平投影白色点数量int w = src.cols;int h = src.rows;vector project_val_arry;for (int i = 0; i < h; i++)//行{Mat i_im = src.row(i);int num = countNonZero(i_im);project_val_arry.push_back(num);}//显示Mat hist_im(h, w, CV_8UC1, Scalar(255));for (int i = 0; i < h; i++){for (int j = 0; j < project_val_arry[i]; j++){hist_im.ptr(i)[w - 1 - j] = 0; //第h-1-j行第i个数据}}//imshow("project_h", hist_im);//step2. 字符分割//vector coiList;int startIndex = 0;int endIndex = 0;bool inBlock = false;//是否遍历到了字符区内for (int i = 0; i < h; ++i){if (!inBlock && project_val_arry[i] != 0)//进入字符区了{inBlock = true;startIndex = i;//cout << "startIndex is " << startIndex << endl;}else if (project_val_arry[i] == 0 && inBlock)//进入空白区了{endIndex = i;inBlock = false;Mat coiImg = src(Rect(0, startIndex, w, endIndex + 1 - startIndex));coiList.push_back(coiImg);}}return 0;
}

搭建模板库

/*搭建模板库*/
vectormodel_h;
horizontal_projection(binimg, model_h);
vector model_v;
for (int i = 0; i < model_h.size(); i++)
{vertical_projection(model_h[i],model_v);
}
for (int j = 0; j < model_v.size(); j++)
{char model_name[100];resize(model_v[j], model_v[j], Size(150, 300), 0, 0, INTER_NEAREST);sprintf_s(model_name,"E:/C语言/opencv/基于opencv垂直投影法的图像数字识别/数字/%d.png",j);imwrite(model_name,model_v[j]);
}

 【模板库载入

/*载入模板库*/
vector img_model;
for (int i = 0; i < 10; i++)
{char model_name[100];sprintf_s(model_name, "E:/C语言/opencv/基于opencv垂直投影法的图像数字识别/数字/%d.png", i);Mat model_img=imread(model_name);img_model.push_back(model_img);
}

【保存分割图像】

/*保存分割图像*/
vector img_segh;
horizontal_projection(binimg, img_segh);
vector img_segv;
for (int i = 0; i < img_segh.size(); i++)
{vertical_projection(img_segh[i],img_segv);
}
for (int j = 0; j < img_segv.size(); j++)
{resize(img_segv[j], img_segv[j], img_model[0].size(), 0, 0, INTER_NEAREST);char seg_name[100];sprintf_s(seg_name, "E:/C语言/opencv/基于opencv垂直投影法的图像数字识别/切割图像/%d.png", j);imwrite(seg_name, img_segv[j]);
}

载入分割图像】 

/*载入分割图像*/
vector img_i;
for (int i = 0; i < img_segv.size(); i++)
{char i_name[100];sprintf_s(i_name, "E:/C语言/opencv/基于opencv垂直投影法的图像数字识别/切割图像/%d.png", i);Mat i_img = imread(i_name);img_i.push_back(i_img);
}

【图像识别】

图像识别采用欧氏距离判断两个图像的相似度,欧氏距离越小,图像相似度越高。

/*图像特征*/
void getFeature(Mat m, float *a)
{int W, H;//存储图像m的宽和高int i, j;float b[25];W = m.cols;H = m.rows;for (i = 0; i < 25; i++)b[i] = 0;//将图像分为5×5个子块,计算每个子块内所有像素的平均值for(i = 0; i < H;i++ ){for(j=0;j(i, j) == 255){b[i / (W / 5) * 5 + j / (H / 5)]++;}}//计算当前像素块的平均值for (i = 0; i < 25; i++){a[i] = b[i] / ((W / 5)*(H / 5));}
}/*欧氏距离*/
float ouDistance(float a[25], float b[25])
{int i;float distance = 0;//欧氏距离计算公式for (i = 0; i < 25; i++)distance += (a[i] - b[i])*(a[i] - b[i]);distance = sqrt(distance);return distance;
}
vector seq;//顺序存放识别结果
for (int i = 0; i < img_i.size(); i++)
{vectordistance;//顺序存放欧氏距离float a[25], b[25]; //存储图像像素平均值getFeature(img_i[i], a);for (int j = 0; j < img_model.size(); j++){//计算两个图片的差值getFeature(img_model[j], b);distance.push_back(ouDistance(a, b));}float min = 0;int min_seq = 0;//记录最小的欧氏距离对应的数字min = distance[0];for (int k = 0; k < distance.size(); k++){if (distance[k] < min){min = distance[k];min_seq = k;}}seq.push_back(min_seq);
}//输出结果
cout << "识别结果为:";
for (int i = 0; i < seq.size(); i++)
{cout << seq[i];
}
cout << endl;

 

内容总结

1、图像载入:imread

2、图像灰度化:cvtColor

3、图像二值化:threshold

4、图像分割:垂直投影法、水平投影法

5、图像识别:欧氏距离与模板匹配

6、改进:只能进行白底黑字简单图像进行识别,识别内容未分行输出

参考文章

https://blog.csdn.net/sss_369/article/details/89857499

https://blog.csdn.net/weixin_41743247/article/details/90635931

https://blog.csdn.net/m0_37543178/article/details/82658020

OpenCV编程案例详解-中国工信出版集团-李立宗


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部