熟悉OpenCV的朋友都知道OpenCV可以用来识别很多东西,今天我们就以基本的数字识别来探索OpenCV的识别之路。
大致的步骤如下:
(1)首先要加载一幅含有数字的图片,并对它进行二值化。
(2)寻找数字的大致轮廓。
(3)对找到的数字轮廓按照输入图片的顺序进行排序。
(4)根据上一步找到的顺序对数字轮廓进行分割,将单个数字轮廓提取出来。
(5)模板匹配
1、图片二值化处理
输入一幅图片并进行二值化处理,方便下一步找到轮廓。
Mat srcImage=imread("E://pictured//photo.jpg");
imshow(''原图",srcImage);
Mat grayImage,binImage;
cvtColor(srcImage,grayImage,COLOR_BGR2GRAY);
threshold(grayImage,binImage,100,255,CV_THRESH_BINARY_INV);//这个CV_THRESH_BINARY_INV参数的选择是根据输入图片的数字颜色和背景颜色决定的。
2、寻找数字的轮廓
Mat conImage = Mat::zeros(binImage.size(), binImage.type());//寻找轮廓,必须指定为寻找外部轮廓。目的是为了限定一个数字只有一个轮廓。
vector
vector
findContours(binImage, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//指定CV_RETR_EXTERNAL寻找数字的外轮廓
drawContours(conImage, contours, -1, 255);//绘制轮廓
3、对找到的轮廓进行排序
(1)定义一个新的Rect类,存放轮廓的外接矩形方便接下来的排序
class Rect
{
public:
Rect(){}
~Rect(){}
Rect(Rect &temp):Rc(temp){} //比较矩形左上角的横坐标,以便排序
bool operator<(Rect &rect)
{
if (this->Rc.x < rect.Rc.x)
{
return true;
}
else
{
return false;
}
} //重载赋值运算符
Rect operator=(Rect &rect)
{
this->Rc = rect.Rc;
return *this;
} //获取矩形
Rect getRect()
{
return Rc;
}
private: Rect Rc;//存放矩形
};
(2)冒泡排序法
for (int i = 0; i < sort_rect.size(); i++) //对矩形进行排序找到数字的排列顺序
{
for (int j = i + 1; j < sort_rect.size(); j++)
{
if (sort_rect[j] < sort_rect[i])
{
Rect temp = sort_rect[j];
sort_rect[j] = sort_rect[i];
sort_rect[i] = temp;
}
}
}
4、初始化数字模板
for (int i = 0; i < 10; i++)
{
Mat ROI = conImage(sort_rect[i].getRect());
Mat dstROI;
resize(ROI, dstROI, Size(40, 50),0, 0, INTER_NEAREST);
char name[64];
sprintf(name, "E;//pictured//%d.jpg", i); dstROI);
imwrite(name, dstROI);
}
5、数字分割
vector
for (int i = 0; i < sort_rect.size(); i++)
{
Mat ROI;
ROI = conImage(sort_rect[i].getRect());
Mat dstROI = Mat::zeros(myTemplate[0].size(),myTemplate[0].type());
resize(ROI, dstROI, myTemplate[0].size(), 0, 0, INTER_NEAREST);
myROI.push_back(dstROI);
}
6、模板匹配
(1)首先进行比较,使用adsdiff计算模板和待识别数字的差值,差值最小的即为最匹配的数字,
int getPiexSum(Mat &image)//求图片的像素和
{
int sum = 0;
for (int i = 0; i < image.cols; i++)
{
for (int j = 0; j < image.rows; j++)
{
sum += image.at
}
}
return sum;
}
(2)匹配结果并输出
vector
for (int i = 0; i < myROI.size(); i++)
{
Mat subImage;
int sum = 0;
int min = 100000;
int min_seq = 0;//记录最小的和对应的数字
for (int j = 0; j < 10; j++)
{
absdiff(myROI[i], myTemplate[j], subImage); //计算两个图片的差值
sum = getPiexSum(subImage);
if (sum < min)
{
min = sum;
min_seq = j;
}
sum = 0;
}
seq.push_back(min_seq);
} //输出结果
cout << "识别结果为:";
for (int i = 0; i < seq.size(); i++)
{
cout << seq[i];
}
cout << endl;
存在的问题与不足:发现有很多影响准确率的因素,例如数字颜色与背景颜色,要识别的字体的大小与控制台输出字体大小的差异。