上周在群里看到一个有趣的外包问题,废料最大化分割利用。用了两个个多小时写的一段分割代码,但是不是最大利用率,不过还行吧!仅供做类似问题的人参考参考!
先来看看问题描述:就是一块不规则的材料,我们要在这个不规则材料上进行切割半径一定圆,使得废料最大化被切割利用,大概应该听懂问题描述了吧,上个图你们就明白问题了。以下是我在模拟处理一块三角形废料的原型和结果。
以上是原图材料,然后在材料上切割指定半径的圆,使得切割的圆最多,这里模拟半径为10个像素,处理后得到以下图像。
二话不说,上代码看看。
bool BORING::detect(cv::Mat &f){
double cr = 10; // 定义圆的半径为10个像素
threshDev = 128; // 二值阈值
cv::Mat thresh;
binaryFrame(f, thresh); // 阈值图像
// 寻找材料的轮廓
std::vector<std::vector<cv::Point>> contours;
cv::findContours(thresh, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
for (int i = 0; i < contours.size(); i++){
//cv::drawContours(f, contours, i, cv::Scalar(255, 0, 0), 3);
}
// 对轮廓求外接矩形
cv::Rect r0 = cv::boundingRect(contours[0]);
//cv::rectangle(f, r0, cv::Scalar(255, 0, 0), 1);
// 取外接矩形减去半径后的新矩形
cv::Rect r1 = cv::Rect(r0.x + cr,r0.y + cr,r0.width - 2*cr,r0.height - 2*cr);
//cv::rectangle(f, r1, cv::Scalar(255, 0, 0), 1);
// 用圆心开始索引
// 必须让圆心处于材料内并且圆心到材料的最小距离必须大于半径才算可行(可切割)
bool lineHave = false;
std::vector<cv::Point> ptv;
ptv.clear();
for (int i = 0; i < r1.width; i++){
for (int j = 0; j < r1.height; j++){
cv::Point pt = cv::Point(r1.x + i, r1.y + j);
//It returns positive(inside), negative(outside), or zero(on an edge)
//value, correspondingly.When measureDist = false, the return value is + 1, -1, and 0
double ret = cv::pointPolygonTest(contours[0], pt, false);
if (ret == 1.){ // 圆心在材料内部
ret = cv::pointPolygonTest(contours[0], pt, true);
if (ret > cr){ // 圆心到材料的最小距离大于半径
ptv.push_back(pt);
j += 2*cr;
lineHave = true;
}
}
}
if (lineHave){
lineHave = false;
i += 2*cr;
}
}
for (int i = 0; i < ptv.size(); i++){
cv::circle(f, ptv[i], cr, cv::Scalar(255, 0, 0), -1);
}
char info[64];
sprintf(info, "%0.f Pixel Circle", cr);
cv::putText(f, info, cv::Point(30,30), cv::FONT_HERSHEY_PLAIN, 2.0, cv::Scalar(0, 0, 0), 2);
return true;
}
// 判断点是否在圆内
bool BORING::isPointInCircle(cv::Point pt, cv::Point center, double r){
if ((((pt.x - center.x) * (pt.x - center.x)) + ((pt.y - center.y) * (pt.y - center.y))) >= (r * r))
return false;
return true;
}
// 用于判断两个圆的关系 相交,相接,相离
bool BORING::isFriendTwoCircle(cv::Point center1, double r1, cv::Point center2, double r2){
if ((((center1.x - center2.x) * (center1.x - center2.x)) + ((center1.y - center2.y) * (center1.y - center2.y)))
>= ((r1 + r2) * (r1 + r2)))
return false;
return true;
}
代码,如上,再来几个材料图像原图和测试结果看看。