关于之前已经写过一篇文章了,是关于字符提取的与识别的,本篇文章与上次的文章内容大致一致,只是比对数据库变更了,并且不仅只通过轮廓,还可以通过模板图的方式来进行手写字提取!
函数介绍,以及轮廓提取和识别流程这里就不细说,详细的可以参考我的上一篇文章:使用Opencv进行轮廓检测,字符提取,简单的直方图字符识别!
首先比对要有数据库图,这里我们先手写几个字作为数据库模板
(JPG格式)
鼠标手写字有点不顺,写的有点丑,见笑了哈!
然后我们在手写两个字,注意必须是比对模板里有的
由于比对模板里只有中国两个手写字,所以这里就写上中国两个字:
下面开始上代码进行手写字比对
(代码具体讲解参考上一篇博客)
结构体:
typedef struct p_image{
char c_name[256];
CvHistogram *img_zft;
}p_image;
main代码:
//打开要识别的图像
IplImage *image = cvLoadImage("d:\\1.jpg");
if (image == NULL){
printf("错误:无法打开该图像文件!");
}
//转换到灰度图
IplImage *img_gray = cvCreateImage(cvGetSize(image), image->depth, 1);
cvCvtColor(image, img_gray, CV_BGR2GRAY);
//二值化
IplImage *img_value = cvCreateImage(cvGetSize(img_gray), image->depth, 1);
cvThreshold(img_gray, img_value, 100, 255, CV_THRESH_BINARY_INV);
//寻找轮廓
CvMemStorage *pStorage = cvCreateMemStorage(0);
CvSeq *pConInner = NULL;
int num = cvFindContours(img_value, pStorage, &pConInner, sizeof(CvContour),
CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
IplImage* imgNo[9] = { NULL };
IplImage *i1[9] = { NULL };
//手写字提取
for (int i = 0; i < num; i++)
{
CvRect rc = cvBoundingRect(pConInner);
//cvDrawRect(image, cvPoint(rc.x, rc.y), cvPoint(rc.x + rc.width, rc.y + rc.height), CV_RGB(255, 0, 0)); //为了防止裁剪时出现矩阵颜色,所以将这段代码屏蔽掉!
imgNo[i] = cvCreateImage(cvSize(rc.width, rc.height), IPL_DEPTH_8U, 3);
cvSetImageROI(image, cvRect(rc.x, rc.y, rc.width, rc.height));//设置源图像ROI
cvCopy(image, imgNo[i]); //裁剪
pConInner = pConInner->h_next;
}
//将获取到的手写字保存起来,在读取出来,分别存储到对应的模板img文件夹里
//保存与读取
char name[256];
for (int i = 0; i < num; ++i){
sprintf(name, "d:\\img\\%d.jpg", i);
cvSaveImage(name, imgNo[i]);
}
char b[256];
for (int i = 0; i < num; ++i){
sprintf(b, "d:\\img\\%d.jpg", i);
imgNo[i] = cvLoadImage(b);
}
//灰度转换
for (int i = 0; i < num; ++i){
i1[i] = cvCreateImage(cvSize(imgNo[i]->width, imgNo[i]->height), 8, 1);
cvCvtColor(imgNo[i], i1[i], CV_BGR2GRAY);//CV_BGR2GRAY
}
//计算原图的直方图
int arr_size = 255; //定义一个变量用于表示直方图行宽
float hranges_arr[] = { 0, 255 }; //图像方块范围数组
float *phranges_arr = hranges_arr; //cvCreateHist参数是一个二级指针,所以要用指针指向数组然后传参
//创建直方图
CvHistogram *hist[9];
for (int i = 0; i < num; ++i){
hist[i] = cvCreateHist(1, &arr_size, CV_HIST_ARRAY, &phranges_arr, 1);//创建一个一维的直方图,行宽为255,多维密集数组,方块范围为0-255,bin均化
}
//计算直方图
for (int i = 0; i < num; ++i){
cvCalcHist(&i1[i], hist[i], 0, 0);
}
//手写字与图像关联
//加载模板图,图像存放在d盘img1文件夹中
IplImage* abcd_img[9] = { NULL };
char img_name[256] = { 0 };
for (int i = 0; i < num; ++i){
sprintf(img_name, "d:\\img1\\%d.jpg", i);
abcd_img[i] = cvLoadImage(img_name);
}
//转换灰度图
IplImage* abcd_img_hdt[9] = { NULL };
for (int i = 0; i < num; ++i){
abcd_img_hdt[i] = cvCreateImage(cvSize(abcd_img[i]->width, abcd_img[i]->height), 8, 1);
cvCvtColor(abcd_img[i], abcd_img_hdt[i], CV_BGR2GRAY);//CV_BGR2GRAY
}
//创建直方图
CvHistogram *hist_abcd[9];
for (int i = 0; i < num; ++i){
hist_abcd[i] = cvCreateHist(1, &arr_size, CV_HIST_ARRAY, &phranges_arr, 1);//创建一个一维的直方图,行宽为255,多维密集数组,方块范围为0-255,bin均化
}
//计算模板图的直方图
for (int i = 0; i < num; ++i){
cvCalcHist(&abcd_img_hdt[i], hist_abcd[i], 0, 0);
}
//关联图像
p_image *p[9] = { NULL };
for (int i = 0; i <= num; ++i){
p[i] = (p_image *)malloc(sizeof(p_image));
}
//手写字关联
char abcd[9][256] = { "中", "国"}; //定义一个二维字符数组,9行,每行256列(256个数据)
for (int i = 0; i <= num; ++i){
strcpy(p[i]->c_name,abcd[i]);
}
//直方图关联
for (int i = 0; i < num; ++i){
p[i]->img_zft = hist_abcd[i];
}
//匹配图像
//用于结果
char j_c[9][256];
for (int j = 0; j < num; ++j){
double Compare = cvCompareHist(hist[j], p[j]->img_zft, 0); //使用CV_COMP_CORREL方法进行对比
if (Compare >= 0.9){ //精准度达90%
strcpy(j_c[j], p[j]->c_name);
}
}
printf("检测到%d个手写字,分别是:\n", num);
for (int i = 0; i < num; ++i){
printf("%s\n", j_c[i]);
}
getchar();
运行结果:
注意前面说过,直方图是利用图像中明暗程度进行比较的,像这种黑白图,白就代表明,黑就代表暗,所以识别起来重叠几率较高,不建议使用模板比对,建议使用其他方法进行比对!