输入图片:
预期产出:
我打算装入三个(或一些数量)多边形(对于这种情况,矩形)来表示此图像中的“大”白色斑点。在输出图像中绘制的矩形是根据我对白色区域的感知。我不认为算法会提出这些相同的bouding区域。我希望的是在白色像素簇周围安装一些紧密的多边形。
我的初始解决方案包括找到此图像的轮廓,并通过找到每个轮廓中的点的凸包来在每个轮廓周围拟合闭合的凸多边形。
然而,由于白色区域高度碎裂,边缘内部和边缘有黑色区域,cv2.findContours返回的轮廓数量非常高(约500左右)。因此,装配凸包不会改善白色区域的形状。白色区域大多保留其原始的抽象形状。我的目标是将白色区域的许多小轮廓合并为一个包含轮廓的整体,然后我可以在其上装入凸包。
我该如何解决这个问题?我应该首先在轮廓点上使用聚类算法来找到彼此靠近的轮廓吗?
答案 0 :(得分:4)
首先需要在此图像上执行形态学关闭(即扩张,然后是侵蚀)。这将关闭图像中所有微小的“洞”,同时保留各个组件的形状和大小。与之相反,当侵蚀之后是扩张时,它会消除图像中的噪点。我正在研究一个类似的图像,我不得不进行扩张+侵蚀多达10次,以使我的组件均匀。完成后,使用连接的组件或查找轮廓。这肯定会使轮廓数从400减少到20-30。
其次,你提到你需要3个集群。虽然两个小集群(由红线覆盖)可能合并为一个。我用它做的是你希望你的每个簇尽可能紧密地适应它的边界矩形。因此,我建议您设置阈值效率(比如80%)并使用分层聚类将每个连接的组件合并到一个集群中。当您的白色像素占用其边界矩形(群集)的空间不到80%时,您将停止群集并获取群集。
答案 1 :(得分:2)
在找到轮廓之前,您可以先放大图像。 膨胀导致明亮区域生长。您可以将其视为在图像中的每个现有白色像素周围添加白色像素。这样,相邻的明亮形状就会合并。 见http://docs.opencv.org/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.html
您还可以再次模糊和阈值,但模糊可能比扩张更昂贵,具体取决于模糊量。
答案 2 :(得分:1)
您可以使用x y坐标作为每个白点和三个群集的特征来使用kmeans聚类。然后取出所得三个簇的凸包。您可能必须尝试不同的起点并选择最佳结果。
见http://docs.opencv.org/modules/core/doc/clustering.html#kmeans
答案 3 :(得分:1)
您可以在形状周围绘制近似轮廓,直到连接所有需要的区域。有了这个,我实际上正在侵蚀图像。如果你在那些连接的区域周围画一个船体,你会得到你的红色矩形。
重复直到你最大的三个船体都有一些必需的属性(例如,如果它们覆盖99%的所有白点)
#include <vector>
using std::vector;
#include <algorithm>
using std::sort;
#include <string>
using std::string;
using std::to_string;
#include <iostream>
using std::clog;
using std::endl;
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
typedef vector<Point> Polygon;
typedef vector<Polygon> Polygons;
Mat mFrame;
Mat mOrig;
mFrame = imread("R2TsZ.png");
mFrame.copyTo(mOrig);
Mat mOrigHull;
Mat mOut;
int fileCounter = 0;
while(true){
clog<< "image read"<< endl;
cvtColor(mFrame, mOut, CV_BGR2GRAY);
clog<< "image grayscaled"<< endl;
Polygons contours;
Polygons aContours;
Polygons hulls;
OutputArray hierarchy = {};
findContours(mOut, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
clog<< contours.size()<< " contours found"<< endl;
sort(contours.begin(), contours.end(), [](auto p1, auto p2){
return contourArea(p1) > contourArea(p2);
});
clog<< "contours sorted"<< endl;
aContours.resize(contours.size());
hulls.resize(contours.size());
for(size_t i = 0; i < aContours.size() - 1; ++ i){
approxPolyDP(contours[i], aContours[i], 20, true);
drawContours(mFrame, aContours, i, Scalar(255, 255, 255), 10);
convexHull(aContours[i], hulls[i], true);
}
mOrig.copyTo(mOrigHull);
for(size_t i = 0; i < 3; ++ i){
drawContours(mOrigHull, hulls, i, Scalar(0, 0, 255), 10);
}
imshow("out", mOrigHull);
int key = waitKey() & 0xff;
if(key == 27){
return EXIT_SUCCESS;
}
if(key == 'p'){
string file = "test_" + to_string(++ fileCounter) + ".png";
imwrite(file, mOrigHull);
clog<< file<< " saved."<< endl;
}
}
}
在opencv的这个tutorial中查看更多内容。