background:
There is a demand point in the project, which involves two modules, A and B
A:
Draw an ROI in the image area, which may be of any shape, and then A will pass some points of the contour to module B, and let B calculate the parameters
B:
Get all point data in this ROI
Now think of two ways to implement this module
Common to both approaches:
Since the points provided by A may be discontinuous, an interpolation may be required. Of course, this can be done or not, depending on the specific error requirements
Interpolation basic logic
struct CvPointCompare
{
template<typename T> bool operator()(const T &p1, const T &p2)
{
return (fabs(static_cast<float>(p1.x - p2.x)) < 0.0001f) ? (p1.y < p2.y) : (p1.x < p2.x);
}
};
// 输入点集合,得到闭合线或者非闭合线
void GetLineByPoints(const std::vector<CvPoint> &Pt, bool bIsCircle, std::set<cv::Point2f, CvPointCompare> &sLine)
{
sLine.clear(); // 清除集合中的元素
int iSum = Pt.size();
if (iSum > 0)
{
if (bIsCircle) // 环形的时候
{
for (int i = 0; i < iSum; i++)
{
((iSum - 1) == i) ? GetLineByTwoPoints(Pt[iSum - 1], Pt[0], sLine) : GetLineByTwoPoints(Pt[i], Pt[i + 1], sLine);
}
}
else // 非环形的时候
{
for (int i = 0; i < iSum - 1; i++)
{
GetLineByTwoPoints(Pt[i], Pt[i + 1], sLine);
}
}
sLine.insert(cvPointTo32f(Pt.back())); // 插入最后一个点(防止遗漏,非闭合跟只有一个点的时候会出现遗漏)
}
}
void GetTraceSelectedPoints(std::vector<CvPoint> &vBoundaryArea, std::vector<CvPoint> &vArea)
{
// 需要自定义排序
std::set<cv::Point2f, CvPointCompare> sOutLine; // 轮廓点集合
GetLineByPoints(vBoundaryArea, true, sOutLine); // 获取插值后的轮阔点集合
}
and then follow up
Method 1: Test if each point is inside the region
Basic logic:
void GetTraceSelectedPoints(std::vector<CvPoint> &vBoundaryArea, std::vector<CvPoint> &vArea)
{
// 需要自定义排序
std::set<cv::Point2f, CvPointCompare> sOutLine; // 轮廓点集合
GetLineByPoints(vBoundaryArea, true, sOutLine); // 获取插值后的轮阔点集合
// 计算轮廓内所有点
std::vector<cv::Point2f> vTmp(sOutLine.begin(), sOutLine.end()); // 转存到容器(set已经实现了排序过程)
float fMinY = vTmp.front().y;
float fMaxY = vTmp.front().y;
for (cv::Point2f &pt : vTmp)
{
if (pt.y > fMaxY)
{
fMaxY = pt.y;
}
else if (pt.y < fMinY)
{
fMinY = pt.y;
}
}
CvPoint LeftTop = cvPointFrom32f(cvPoint2D32f(vTmp.front().x, fMinY));
CvPoint RightDown = cvPointFrom32f(cvPoint2D32f(vTmp.back().x, fMaxY));
vArea.clear();
for (int y = LeftTop.y; y <= RightDown.y; y++)
{
for (int x = LeftTop.x; x <= RightDown.x; x++)
{
cv::Point2f PCur = cvPoint2D32f(x, y);
if (!(pointPolygonTest(vTmp, PCur, false) < 0)) // 判断当前点是否在轮廓内
{
vArea.push_back(cvPointFrom32f(PCur));
}
}
}
}
Method 2: directly get back to the internal point of the contour
int main()
{
std::vector<uint8_t> v_data(500 * 500, 0);
cv::Mat cv_back_end = cv::Mat(500, 500, CV_8UC1, v_data.data());
// 这个位置就是模拟前面插值后的轮廓点
std::vector<std::vector<cv::Point>> v_points = { {cv::Point(98, 2), cv::Point(40, 40), cv::Point(2, 98), cv::Point(98, 98) } };
//直接将轮廓内的所有点绘制在back_end上,这里的关键点就是thickness参数(最后一个参数)设置成了-1,设置成-1,opencv就会将轮廓内填充起来
cv::drawContours(cv_back_end, v_points, -1, cv::Scalar(255), -1);
}