OpenCV,如何使用点数组来平滑和采样轮廓? [英] OpenCV, how to use arrays of points for smoothing and sampling contours?
问题描述
我有一个问题,让我围绕平滑和抽样轮廓在OpenCV(C ++ API)。
假设我有从 cv :: findContours
中检索的点序列(例如应用于此图像:
>
最终,我想要
- 使用不同的内核来平滑点序列。
- To smooth a sequence of points using different kernels.
- To resize the sequence using different types of interpolations.
平滑后,我希望有一个结果:
>
我也考虑在 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> >轮廓;
std :: vector< cv :: Vec4i>层次;
cv :: findContours(ori,contour,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_NONE);
for(int k = 0; k <100; k + = 2){
cv :: Mat smoothCont;
smoothCont = cv :: Mat(contoururs [0]);
std :: cout<< smoothCont.rows<<\t<< smoothCont.cols<< std :: endl;
/ *尝试平滑:不修改数组* /
// cv :: GaussianBlur(smoothCont,smoothCont,cv :: Size(k + 1,1),k);
/ *尝试抽样:调整失败(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()
正如你所看到的,它倾向于保存极值点(我要删除):
Epsilon = 0
Epsilon = 6
>
Epsilon = 12
Epsilon = 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]);
我还在寻找解决方案来插值/抽样我的轮廓。
您的高斯模糊不工作,因为您在列方向模糊,但只有一列。使用 GaussianBlur()
导致OpenCV中的功能未实现错误,当尝试将向量复制到 cv :: Mat
(这可能是为什么你有这个奇怪的 resize()
在你的代码),但一切工作正常使用 cv :: blur
,无需 resize()
。尝试大小(0,41)例如。使用 cv :: BORDER_WRAP
似乎也不工作,但这里是另一个人的线程,发现了一个解决方法。
更多的事情:你说你的轮廓可能会小得多。这种方式平滑你的轮廓会收缩它。极端情况是 k = size_of_contour
,这产生一个点。所以不要选择你的k太大。
I have a problem to get my head around smoothing and sampling contours in OpenCV (C++ API).
Lets say I have got sequence of points retrieved from cv::findContours
(for instance applied on this this image:
Ultimately, I want
After smoothing, I hope to have a result like :
I also considered drawing my contour in a cv::Mat
, filtering the Mat (using blur or morphological operations) and re-finding the contours, but is slow and suboptimal. So, ideally, I could do the job using exclusively the point sequence.
I read a few posts on it and naively thought that I could simply convert a std::vector
(of cv::Point
) to a cv::Mat
and then OpenCV functions like blur/resize would do the job for me... but they did not.
Here is what I tried:
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;
}
Could anyone explain how to do this ?
In addition, since I am likely to work with much smaller contours, I was wondering how this approach would deal with border effect (e.g. when smoothing, since contours are circular, the last elements of a sequence must be used to calculate the new value of the first elements...)
Thank you very much for your advices,
Edit:
I also tried cv::approxPolyDP()
but, as you can see, it tends to preserve extremal points (which I want to remove):
Epsilon=0
Epsilon=6
Epsilon=12
Epsilon=24
Edit 2:
As suggested by Ben, it seems that cv::GaussianBlur()
is not supported but cv::blur()
is. It looks very much closer to my expectation. Here are my results using it:
k=13
k=53
k=103
To get around the border effect, I did:
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]);
I am still looking for solutions to interpolate/sample my contour.
Your Gaussian blurring doesn't work because you're blurring in column direction, but there is only one column. Using GaussianBlur()
leads to a "feature not implemented" error in OpenCV when trying to copy the vector back to a cv::Mat
(that's probably why you have this strange resize()
in your code), but everything works fine using cv::blur()
, no need to resize()
. Try Size(0,41) for example. Using cv::BORDER_WRAP
for the border issue doesn't seem to work either, but here is another thread of someone who found a workaround for that.
Oh... one more thing: you said that your contours are likely to be much smaller. Smoothing your contour that way will shrink it. The extreme case is k = size_of_contour
, which results in a single point. So don't choose your k too big.
这篇关于OpenCV,如何使用点数组来平滑和采样轮廓?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!