[英]OpenCV, how to use arrays of points for smoothing and sampling contours?
我无法理解 OpenCV(C++ API)中的平滑和采样轮廓。 可以说我已经从cv::findContours
中检索到点序列(例如应用于这张图片:
最终,我想要
平滑后,我希望得到这样的结果:
我还考虑过在cv::Mat
中绘制我的轮廓,过滤 Mat(使用模糊或形态学操作)并重新找到轮廓,但速度很慢且次优。 所以,理想情况下,我可以只使用点序列来完成这项工作。
我读了一些关于它的帖子,天真地认为我可以简单地将一个std::vector
(属于cv::Point
)转换为一个cv::Mat
然后像模糊/调整大小这样的 OpenCV 函数会为我完成这项工作......但他们没有。
这是我尝试过的:
int main( int argc, char** argv ){
cv::Mat conv,ori;
ori=cv::imread(argv[1]);
ori.copyTo(conv);
cv::cvtColor(ori,ori,CV_BGR2GRAY);
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i > hierarchy;
cv::findContours(ori, contours,hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
for(int k=0;k<100;k += 2){
cv::Mat smoothCont;
smoothCont = cv::Mat(contours[0]);
std::cout<<smoothCont.rows<<"\t"<<smoothCont.cols<<std::endl;
/* Try smoothing: no modification of the array*/
// cv::GaussianBlur(smoothCont, smoothCont, cv::Size(k+1,1),k);
/* Try sampling: "Assertion failed (func != 0) in resize"*/
// cv::resize(smoothCont,smoothCont,cv::Size(0,0),1,1);
std::vector<std::vector<cv::Point> > v(1);
smoothCont.copyTo(v[0]);
cv::drawContours(conv,v,0,cv::Scalar(255,0,0),2,CV_AA);
std::cout<<k<<std::endl;
cv::imshow("conv", conv);
cv::waitKey();
}
return 1;
}
谁能解释如何做到这一点?
此外,由于我可能会使用更小的轮廓,所以我想知道这种方法将如何处理边界效应(例如,在平滑时,由于轮廓是圆形的,因此必须使用序列的最后一个元素来计算新的值第一个元素...)
非常感谢您的建议,
编辑:
我也试过cv::approxPolyDP()
但是,正如你所看到的,它倾向于保留极值点(我想删除):
小量=0
小量=6
小量=12
小量=24
编辑 2:正如 Ben 所建议的,似乎不支持cv::GaussianBlur()
但支持cv::blur()
。 它看起来非常接近我的期望。 这是我使用它的结果:
k=13
k=53
k=103
为了绕过边界效应,我做了:
cv::copyMakeBorder(smoothCont,smoothCont, (k-1)/2,(k-1)/2 ,0, 0, cv::BORDER_WRAP);
cv::blur(smoothCont, result, cv::Size(1,k),cv::Point(-1,-1));
result.rowRange(cv::Range((k-1)/2,1+result.rows-(k-1)/2)).copyTo(v[0]);
我仍在寻找对轮廓进行插值/采样的解决方案。
您的高斯模糊不起作用,因为您在列方向上模糊,但只有一列。 当尝试将向量复制回cv::Mat
时,使用GaussianBlur()
会导致 OpenCV 出现“功能未实现”错误(这可能就是为什么代码中有这个奇怪的resize()
的原因),但是使用cv::blur()
一切正常cv::blur()
,无需resize()
。 例如尝试 Size(0,41)。 使用cv::BORDER_WRAP
解决边界问题似乎也不起作用,但这是找到解决方法的人的另一个线程。
哦...还有一件事:你说你的轮廓可能会小得多。 以这种方式平滑你的轮廓会缩小它。 极端情况是k = size_of_contour
,这会导致单个点。 所以不要选择太大的k。
另一种可能性是使用 openFrameworks 使用的算法:
它遍历轮廓并基本上使用它周围的点应用低通滤波器。 应该以低开销完全按照您的意愿进行操作(没有理由对本质上只是轮廓的图像进行大过滤)。
approxPolyDP()怎么样?
它使用此算法来“平滑”轮廓(基本上摆脱轮廓的大部分点并留下代表轮廓的良好近似值的点)
来自 2.1 OpenCV 文档部分基本结构:
template<typename T>
explicit Mat::Mat(const vector<T>& vec, bool copyData=false)
您可能希望将第二个参数设置为true
:
smoothCont = cv::Mat(contours[0]);
然后重试(这样cv::GaussianBlur
应该可以修改数据)。
我知道这是很久以前写的,但是你有没有试过先大腐蚀然后大扩张(开口),然后找到 countours? 它看起来像一个简单快速的解决方案,但我认为它至少在某种程度上可行。
基本上轮廓的突然变化对应于高频内容。 平滑轮廓的一种简单方法是找到傅里叶系数,假设坐标形成复平面 x + iy,然后消除高频系数。
这适用于我。 边缘比以前更光滑:
medianBlur(mat, mat, 7)
morphologyEx(mat, mat, MORPH_OPEN, getStructuringElement(MORPH_RECT, Size(12.0, 12.0)))
val contours = getContours(mat)
这是 opencv4android 代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.