<分区>
我正在学习一些 C++ 并尝试开发一个简单的软件来检测哪种类型的
数字已由用户写入。
我知道我想要它看起来像什么,但我不知道如何测量手指的粗细。测量不需要非常准确,几个像素太多或不够并不重要。
示例 iamge 如下所示:
sample image
图像总是以这样的方式裁剪,即它是可能的最小分辨率。它以灰度保存,但技术细节在这里并没有真正的意义。
那么,是否有一种有效的算法可以确定用于绘制图像的笔划大小?如果它可以每秒处理约 100 张图像(从内存中),那就太好了。
可以计算出数字轮廓的面积(A)和周长(P),则宽度~= A/(0.5*P)。请注意,对于数字“4,6,8,9,0”,轮廓内有孔,周长应包括孔的值。 (我也测试了骨架化的方法,即width ~= [area of object]/[area of skeleton],效果比较好,但是在github上找到的骨架化实现比较耗时。)
代码如下,速度够快
main.cpp
#include <iostream>
#include <string>
#include "digit_width.hpp"
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
// string filename = "all.jpg";
string filename = "SRmyi.png";
Mat img_raw = imread(filename, IMREAD_GRAYSCALE);
threshold(img_raw,img_raw,128,255,THRESH_BINARY);
vector<double> width_list;
vector<Point2f> center_list;
digit_width(img_raw, width_list, center_list);
for (size_t idx = 0; idx < width_list.size(); idx++)
{
cout << width_list[idx] << endl;
ostringstream oss;
oss << setprecision(4) << width_list[idx];
putText(img_raw, oss.str(), center_list[idx], FONT_HERSHEY_DUPLEX, 1.0, 128);
}
imwrite("new_"+filename, img_raw);
imshow("img",img_raw);
waitKey(0);
return 0;
}
digit_width.hpp
#include <opencv2/opencv.hpp>
#include <cmath>
using namespace std;
using namespace cv;
void digit_width(const Mat & img_bin, vector<double>& width_list, vector<Point2f>& center_list)
{
vector<std::vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(img_bin, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_NONE );
vector<vector<int> > contours_child;
for (size_t idx = 0; idx < hierarchy.size(); idx++)
{
if(hierarchy[idx][3] == -1)
{
vector<int> child;
child.push_back(idx);
if(hierarchy[idx][2] != -1)
{
int child_idx = hierarchy[idx][2];
child.push_back(child_idx);
while(hierarchy[child_idx][0] != -1)
{
child_idx = hierarchy[child_idx][0];
child.push_back(child_idx);
}
}
contours_child.push_back(child);
}
}
for(size_t idx = 0; idx < contours_child.size(); idx++)
{
double width = 0;
double perimeter = arcLength(contours[ contours_child[idx][0] ], true);
double area = contourArea(contours[ contours_child[idx][0] ]);
auto M = moments(contours[ contours_child[idx][0] ]);
float cX = M.m10 / M.m00;
float cY = M.m01 / M.m00;
Point2f pt = Point2f(cX, cY);
if( contours_child.size() > 1 )
{
for(size_t jdx=1; jdx<contours_child[idx].size(); jdx++)
{
perimeter += arcLength(contours[ contours_child[idx][jdx] ], true);
area -= contourArea(contours[ contours_child[idx][jdx] ]);
}
}
width = area / (0.5 * perimeter);
width_list.push_back( width );
center_list.push_back(pt);
}
}
结果如下图所示。