头文件:
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <opencv2/opencv.hpp>
#include <iostream>
#include <stdio.h>
#include <math.h>
#define pi 3.1415926
using namespace cv;
using namespace std;
滤波函数:
void RemoveSmallRegion(Mat& Src, Mat& Dst, int AreaLimit, int AreaTop, int CheckMode, int NeihborMode)
{
int RemoveCount = 0; //记录除去的个数
//记录每个像素点检验状态的标签,0代表未检查,1代表正在检查,2代表检查不合格(需要反转颜色),3代表检查合格或不需检查
Mat Pointlabel = Mat::zeros(Src.size(), CV_8UC1);
if (CheckMode == 1)
{
//cout<<"Mode: 去除小区域. ";
for (int i = 0; i < Src.rows; ++i)
{
uchar* iData = Src.ptr<uchar>(i);
uchar* iLabel = Pointlabel.ptr<uchar>(i);
for (int j = 0; j < Src.cols; ++j)
{
if (iData[j] < 10)
{
iLabel[j] = 3;
}
}
}
}
else
{
//cout<<"Mode: 去除孔洞. ";
for (int i = 0; i < Src.rows; ++i)
{
uchar* iData = Src.ptr<uchar>(i);
uchar* iLabel = Pointlabel.ptr<uchar>(i);
for (int j = 0; j < Src.cols; ++j)
{
if (iData[j] > 10)
{
iLabel[j] = 3;
}
}
}
}
vector<Point2i> NeihborPos; //记录邻域点位置
NeihborPos.push_back(Point2i(-1, 0));
NeihborPos.push_back(Point2i(1, 0));
NeihborPos.push_back(Point2i(0, -1));
NeihborPos.push_back(Point2i(0, 1));
if (NeihborMode == 1)
{
//cout<<"Neighbor mode: 8邻域."<<endl;
NeihborPos.push_back(Point2i(-1, -1));
NeihborPos.push_back(Point2i(-1, 1));
NeihborPos.push_back(Point2i(1, -1));
NeihborPos.push_back(Point2i(1, 1));
}
//else cout<<"Neighbor mode: 4邻域."<<endl;
int NeihborCount = 4 + 4 * NeihborMode;
int CurrX = 0, CurrY = 0;
//开始检测
for (int i = 0; i < Src.rows; ++i)
{
uchar* iLabel = Pointlabel.ptr<uchar>(i);
for (int j = 0; j < Src.cols; ++j)
{
if (iLabel[j] == 0)
{
//********开始该点处的检查**********
vector<Point2i> GrowBuffer; //堆栈,用于存储生长点
GrowBuffer.push_back(Point2i(j, i));
Pointlabel.at<uchar>(i, j) = 1;
int CheckResult = 0; //用于判断结果(是否超出大小),0为未超出,1为超出
for (int z = 0; z<int(GrowBuffer.size()); z++)
{
for (int q = 0; q<NeihborCount; q++) //检查四个邻域点
{
CurrX = GrowBuffer.at(z).x + NeihborPos.at(q).x;
CurrY = GrowBuffer.at(z).y + NeihborPos.at(q).y;
if (CurrX >= 0 && CurrX<Src.cols&&CurrY >= 0 && CurrY<Src.rows) //防止越界
{
if (Pointlabel.at<uchar>(CurrY, CurrX) == 0)
{
GrowBuffer.push_back(Point2i(CurrX, CurrY)); //邻域点加入buffer
Pointlabel.at<uchar>(CurrY, CurrX) = 1; //更新邻域点的检查标签,避免重复检查
}
}
}
}
if ((int(GrowBuffer.size())>AreaLimit)&(int(GrowBuffer.size())<AreaTop)) CheckResult = 2; //判断结果(是否超出限定的大小),1为未超出,2为超出
else { CheckResult = 1; RemoveCount++; }
for (int z = 0; z<int(GrowBuffer.size()); z++) //更新Label记录
{
CurrX = GrowBuffer.at(z).x;
CurrY = GrowBuffer.at(z).y;
Pointlabel.at<uchar>(CurrY, CurrX) += CheckResult;
}
//********结束该点处的检查**********
}
}
}
CheckMode = 255 * (1 - CheckMode);
//开始反转面积过小的区域
for (int i = 0; i < Src.rows; ++i)
{
uchar* iData = Src.ptr<uchar>(i);
uchar* iDstData = Dst.ptr<uchar>(i);
uchar* iLabel = Pointlabel.ptr<uchar>(i);
for (int j = 0; j < Src.cols; ++j)
{
if (iLabel[j] == 2)
{
iDstData[j] = CheckMode;
}
else if (iLabel[j] == 3)
{
iDstData[j] = iData[j];
}
}
}
//cout<<RemoveCount<<" objects removed."<<endl;
}
主函数:
- 在获得的烟草区域内选取任意一点,以该点画圆,半径逐步增加,设定条件(在该圆中的烟草区域/圆的面积<某阈值),当不符合时,将圆进行空白区域中心的反向半径方向移动,直到满足条件(设定最大移动范围);
- 以此方法,在宽度方向上获得最大内接圆,烟草宽度即为圆的直径;
- 相同方法取多点,分别检测宽度(阈值),最后计算均值;
int main()
{
char filename[100];
for (int w = 1; w <= 1; w++)//
{
sprintf_s(filename, "../../准确性/1.bmp", w);//../../重复性/11-%d.bmp
cout << filename<<" ";
Mat imgsrc = imread(filename, 1);
Mat img_gray, img_by, img_by1;
cvtColor(imgsrc, img_gray, CV_BGR2GRAY);//彩色转灰度图
adaptiveThreshold(img_gray, img_by, 255, ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY_INV, 9, 10);
RemoveSmallRegion(img_by, img_by, 100, 3000, 1, 0);
vector<vector<Point>> contours;
findContours(img_by, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);//查找最外侧轮廓
//1最大外接圆
//int dist = 0;
//int maxdist = 0;
//Point center;
//for (int i = 0; i<imgsrc.cols; i++)
//{
// for (int j = 0; j<imgsrc.rows; j++)
// {
// dist = pointPolygonTest(contours[0], cv::Point(i, j), true);
// if (dist <= 0)
// continue;
// if (dist>maxdist)
// {
// maxdist = dist;
// center = cv::Point(i, j);
// }
// }
//}
//circle(img_by, center, maxdist, Scalar(0),-1);
//imshow("zc", img_gray);
//2
for (int q = 0; q < contours.size(); q++)
{
RNG rng;//随机数
Point2f point;
double radius_all = 0;
for (int p = 0; p < 4; p++)
{
int num = rng.uniform(0, contours[q].size());
point = Point2f(contours[q][num].x, contours[q][num].y);//随机点
double radius;//半径
double resulit_limit = 0.9;
for (radius = 2.0; radius < 5.0; radius += 0.1)//随机圆变化起点
{
int baidian_num = 0;
int baidian_x = 0;
int baidian_y = 0;
restart:
for (int i = point.y - radius; i < point.y + radius; i++)//计算区域在圆内像素个数
for (int j = point.x - radius; j < point.x + radius; j++)
{
if (img_by.at<uchar>(i, j) == 255 && ((point.y - i)*(point.y - i) + (point.x - j)*(point.x - j) < radius*radius))
{
baidian_num++;
baidian_x += j;
baidian_y += i;
}
}
double result = baidian_num / (pi*radius*radius);//计算占比
if (result < resulit_limit)//移动圆心位置
{
point.x = point.x + (baidian_x / baidian_num - point.x) / 2;//x方向
point.y = point.y + (baidian_y / baidian_num - point.y) / 2;//y方向
goto restart;
}
else if ((result > resulit_limit) && (result < 1))
{
if ((baidian_x / baidian_num - point.x) *(baidian_x / baidian_num - point.x) + (baidian_y / baidian_num - point.y) * (baidian_y / baidian_num - point.y) < 0.5)
break;
}
else if (result == 1)
continue;//半径自增
}
radius_all += radius;
}
cout << radius_all / 4.0 * 2.0 << " pixels" << endl;
}
}
system("pause");
}
测试图像:
运行结果: