基于Opencv的数字图像分割算法
本文主要介绍了一种使用opencv库来对一串连续数字的分割和存储,其中主要运用了灰度化,局部二值化,纵向和横向投影法。
首先对图片进行滤波,开运算和局部二值化,得到二值化图如下:
利用横向投影求和,确定出数字的上边界和下边界,并将图片截取出来:
利用纵向投影求和,确定出每个数字的左右边界,并利用容器将每个数字的左右边界存储起来,最终确定出每个数字和新建图坐标之间的对应关系将图截取出来:
代码如下(传入灰度图):
bool contour(Mat image) {
Mat img_gau, img_open, img_seg, img_edge;
// 高斯模糊
GaussianBlur(image, img_gau, Size(7, 7), 0, 0);
//imshow("绘制方框", image);
开运算
Mat element = getStructuringElement(MORPH_RECT, Size(image.rows, image.cols));
morphologyEx(img_gau, img_open, MORPH_OPEN, element);
addWeighted(img_gau, 1, img_open, -1, 0, img_open);
// 阈值分割
//threshold(img_open, img_seg, 20, 255, THRESH_BINARY_INV);
adaptiveThreshold(img_open, img_seg, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 21, 10);
imshow("1", img_seg);
int row_dst = img_seg.rows;
int col_dst = img_seg.cols;
int mid_sp = 0,mid_cz=0;
vector<int> mid_sph,mid_czh;
for (int i = 0; i < row_dst; i++)
{
for (int j = 0; j < col_dst; j++)
{
mid_sp = mid_sp + img_seg.at<uchar>(i, j);
}
cout << "第" << i + 1 << "行的水平投影为:" << mid_sp << endl;//144000
mid_sph.push_back(mid_sp);
mid_sp = 0;
}
int sp1=0, sp2=0;
for (int i = 0; i < mid_sph.size(); i++)
{
if (mid_sph[i] != 0 && sp1 == 0)
{
sp1 = i;
}
else if(mid_sph[i] == 0 && sp1 != 0){
sp2 = i;
break;
}
}
int spc;
spc = abs(sp1 - sp2);
Mat new_sp_img = Mat::zeros(Size(500,spc),CV_8UC1);
for (int i = 0; i < new_sp_img.rows; i++) {
for (int j = 0; j < new_sp_img.cols;j++) {
new_sp_img.at<uchar>(i, j) = img_seg.at<uchar>(i+ sp1, j);
}
}
imshow("ll", new_sp_img);
for (int i = 0; i < col_dst; i++)
{
for (int j = 0; j < row_dst; j++)
{
mid_cz = mid_cz + img_seg.at<uchar>(j, i);
}
cout << "第" << i + 1 << "列的垂直投影为:" << mid_cz << endl;//144000
mid_czh.push_back(mid_cz);
mid_cz = 0;
}
vector<Vec2i> czd;
int cz1 = 0, cz2 = 0;
for (int i = 0; i < mid_czh.size(); i++)
{
if (mid_czh[i] != 0 && cz1 == 0)
{
cz1 = i;
}
else if (mid_czh[i] == 0 && cz1 != 0) {
cz2 = i;
czd.push_back(Vec2i(cz1,cz2));
cz1 = 0;
}
}
Mat new_cz_img = Mat::zeros(Size(spc, spc), CV_8UC1);
int czd_zs = 0;
//new_sp_img
for (int i = 0; i < czd.size(); i++) {
int sub_cz = (spc-abs(czd[i][0] - czd[i][1]))/2;
for (int j = 0; j < spc; j++) {
czd_zs = czd[i][0];
for (int k = 0; k < spc; k++) {
if(k< sub_cz)
{
new_cz_img.at<uchar>(j, k) = 0;
}
else if (czd_zs <= czd[i][1]) {
new_cz_img.at<uchar>(j, k) = new_sp_img.at<uchar>(j, czd_zs++);
}
else
{
new_cz_img.at<uchar>(j, k) = 0;
}
}
}
Mat final_cc;
resize(new_cz_img, final_cc, Size(28, 28));
imwrite("number" + to_string(i) + ".jpg", final_cc);
imshow("number" + to_string(i), final_cc);
}
imshow("绘制方框", img_seg);for (int i = 0; i < col_dst; i++)
{
for (int j = 0; j < row_dst; j++)
{
mid_cz = mid_cz + img_seg.at<uchar>(j, i);
}
cout << "第" << i + 1 << "列的垂直投影为:" << mid_cz << endl;//144000
mid_czh.push_back(mid_cz);
mid_cz = 0;
}
vector<Vec2i> czd;
int cz1 = 0, cz2 = 0;
for (int i = 0; i < mid_czh.size(); i++)
{
if (mid_czh[i] != 0 && cz1 == 0)
{
cz1 = i;
}
else if (mid_czh[i] == 0 && cz1 != 0) {
cz2 = i;
czd.push_back(Vec2i(cz1,cz2));
cz1 = 0;
}
}
Mat new_cz_img = Mat::zeros(Size(spc, spc), CV_8UC1);
int czd_zs = 0;
//new_sp_img
for (int i = 0; i < czd.size(); i++) {
int sub_cz = (spc-abs(czd[i][0] - czd[i][1]))/2;
for (int j = 0; j < spc; j++) {
czd_zs = czd[i][0];
for (int k = 0; k < spc; k++) {
if(k< sub_cz)
{
new_cz_img.at<uchar>(j, k) = 0;
}
else if (czd_zs <= czd[i][1]) {
new_cz_img.at<uchar>(j, k) = new_sp_img.at<uchar>(j, czd_zs++);
}
else
{
new_cz_img.at<uchar>(j, k) = 0;
}
}
}
imwrite("number" + to_string(i) + ".jpg", final_cc);
imshow("number" + to_string(i), final_cc);
}
waitKey(0);
return true;
}
感谢学姐的大力支持和帮助!