OpenCV 中的连接组件
Posted
技术标签:
【中文标题】OpenCV 中的连接组件【英文标题】:connected components in OpenCV 【发布时间】:2012-09-23 04:51:20 【问题描述】:我正在寻找一个 OpenCV 函数,它可以找到连接的组件并对其执行一些任务(例如获取像素数、轮廓、对象中的像素列表等)
OpenCV(C++)有没有类似MatLab的regionprops的功能?
【问题讨论】:
【参考方案1】:如果您不介意使用使用 OpenCV 的外部库,您可以使用 cvBlobsLib。
执行二进制图像连接组件标记的库 (类似于 regionprops Matlab 函数)。它还提供功能 从提取的 blob 中操作、过滤和提取结果, 有关详细信息,请参阅功能部分。
【讨论】:
谢谢!如何将它与 cvMat 一起使用? @EyalG 如果您查看examples,您会发现他们使用 IplImage,所以我想您也可以轻松使用 cvMat。 您不需要为此使用单独的库。 OpenCV 本身就可以很好地处理连接的组件。 我同意 misha,cv::findContours 明确找到连接的组件。【参考方案2】:看看cvFindContours 函数。它非常通用——它可以找到内部和外部轮廓,并以多种格式返回结果(例如平面列表与树结构)。获得轮廓后,cvContourArea 等函数允许您确定与特定轮廓相对应的连接组件的基本属性。
如果您更喜欢使用较新的 C++ 接口(与我上面描述的较旧的 C 样式接口相反),那么函数名称是 similar。
【讨论】:
不要将findContours
用于连接的组件。它非常缓慢。【参考方案3】:
编译时设置 -std=c++0x 选项
.h 文件
//connected_components.h
#ifndef CONNECTED_COMPONENTS_H_
#define CONNECTED_COMPONENTS_H_
#include <opencv2/core/core.hpp>
#include <memory>
class DisjointSet
private:
std::vector<int> m_disjoint_array;
int m_subset_num;
public:
DisjointSet();
DisjointSet(int size);
~DisjointSet();
int add(); //add a new element, which is a subset by itself;
int find(int x); //return the root of x
void unite(int x, int y);
int getSubsetNum(void);
;
class ConnectedComponent
private:
cv::Rect m_bb;
int m_pixel_count;
std::shared_ptr< std::vector<cv::Point2i> > m_pixels;
public:
ConnectedComponent();
ConnectedComponent(int x, int y);
~ConnectedComponent();
void addPixel(int x, int y);
int getBoundingBoxArea(void) const;
cv::Rect getBoundingBox(void) const;
int getPixelCount(void) const;
std::shared_ptr< const std::vector<cv::Point2i> > getPixels(void) const;
;
void findCC(const cv::Mat& src, std::vector<ConnectedComponent>& cc);
#endif //CONNECTED_COMPONENTS_H_
.cc 文件
//connected_components.cpp
#include "connected_components.h"
using namespace std;
/** DisjointSet **/
DisjointSet::DisjointSet() :
m_disjoint_array(),
m_subset_num(0)
DisjointSet::DisjointSet(int size) :
m_disjoint_array(),
m_subset_num(0)
m_disjoint_array.reserve(size);
DisjointSet::~DisjointSet()
//add a new element, which is a subset by itself;
int DisjointSet::add()
int cur_size = m_disjoint_array.size();
m_disjoint_array.push_back(cur_size);
m_subset_num ++;
return cur_size;
//return the root of x
int DisjointSet::find(int x)
if (m_disjoint_array[x] < 0 || m_disjoint_array[x] == x)
return x;
else
m_disjoint_array[x] = this->find(m_disjoint_array[x]);
return m_disjoint_array[x];
// point the x and y to smaller root of the two
void DisjointSet::unite(int x, int y)
if (x==y)
return;
int xRoot = find(x);
int yRoot = find(y);
if (xRoot == yRoot)
return;
else if (xRoot < yRoot)
m_disjoint_array[yRoot] = xRoot;
else
m_disjoint_array[xRoot] = yRoot;
m_subset_num--;
int DisjointSet::getSubsetNum()
return m_subset_num;
/** ConnectedComponent **/
ConnectedComponent::ConnectedComponent() :
m_bb(0,0,0,0),
m_pixel_count(0),
m_pixels()
m_pixels = std::make_shared< std::vector<cv::Point2i> > ();
ConnectedComponent::ConnectedComponent(int x, int y) :
m_bb(x,y,1,1),
m_pixel_count(1),
m_pixels()
m_pixels = std::make_shared< std::vector<cv::Point2i> > ();
ConnectedComponent::~ConnectedComponent(void)
void ConnectedComponent::addPixel(int x, int y)
m_pixel_count++;
// new bounding box;
if (m_pixel_count == 0)
m_bb = cv::Rect(x,y,1,1);
// extend bounding box if necessary
else
if (x < m_bb.x )
m_bb.width+=(m_bb.x-x);
m_bb.x = x;
else if ( x > (m_bb.x+m_bb.width) )
m_bb.width=(x-m_bb.x);
if (y < m_bb.y )
m_bb.height+=(m_bb.y-y);
m_bb.y = y;
else if ( y > (m_bb.y+m_bb.height) )
m_bb.height=(y-m_bb.y);
m_pixels->push_back(cv::Point(x,y));
int ConnectedComponent::getBoundingBoxArea(void) const
return (m_bb.width*m_bb.height);
cv::Rect ConnectedComponent::getBoundingBox(void) const
return m_bb;
std::shared_ptr< const std::vector<cv::Point2i> > ConnectedComponent::getPixels(void) const
return m_pixels;
int ConnectedComponent::getPixelCount(void) const
return m_pixel_count;
/** find connected components **/
void findCC(const cv::Mat& src, std::vector<ConnectedComponent>& cc)
if (src.empty()) return;
CV_Assert(src.type() == CV_8U);
cc.clear();
int total_pix = src.total();
int frame_label[total_pix];
DisjointSet labels(total_pix);
int root_map[total_pix];
int x, y;
const uchar* cur_p;
const uchar* prev_p = src.ptr<uchar>(0);
int left_val, up_val;
int cur_idx, left_idx, up_idx;
cur_idx = 0;
//first logic loop
for (y = 0; y < src.rows; y++ )
cur_p = src.ptr<uchar>(y);
for (x = 0; x < src.cols; x++, cur_idx++)
left_idx = cur_idx - 1;
up_idx = cur_idx - src.size().width;
if ( x == 0)
left_val = 0;
else
left_val = cur_p[x-1];
if (y == 0)
up_val = 0;
else
up_val = prev_p[x];
if (cur_p[x] > 0)
//current pixel is foreground and has no connected neighbors
if (left_val == 0 && up_val == 0)
frame_label[cur_idx] = (int)labels.add();
root_map[frame_label[cur_idx]] = -1;
//current pixel is foreground and has left neighbor connected
else if (left_val != 0 && up_val == 0)
frame_label[cur_idx] = frame_label[left_idx];
//current pixel is foreground and has up neighbor connect
else if (up_val != 0 && left_val == 0)
frame_label[cur_idx] = frame_label[up_idx];
//current pixel is foreground and is connected to left and up neighbors
else
frame_label[cur_idx] = (frame_label[left_idx] > frame_label[up_idx]) ? frame_label[up_idx] : frame_label[left_idx];
labels.unite(frame_label[left_idx], frame_label[up_idx]);
//endif
else
frame_label[cur_idx] = -1;
//end for x
prev_p = cur_p;
//end for y
//second loop logic
cur_idx = 0;
int curLabel;
int connCompIdx = 0;
for (y = 0; y < src.size().height; y++ )
for (x = 0; x < src.size().width; x++, cur_idx++)
curLabel = frame_label[cur_idx];
if (curLabel != -1)
curLabel = labels.find(curLabel);
if( root_map[curLabel] != -1 )
cc[root_map[curLabel]].addPixel(x, y);
else
cc.push_back(ConnectedComponent(x,y));
root_map[curLabel] = connCompIdx;
connCompIdx++;
//end for x
//end for y
【讨论】:
total_pix
不是一个常量表达式 - 至少 Visual Studio 2012 会编译它,因为它需要一个编译时常量来在堆栈上创建一个数组。使用 std::vector<int> frame_label(total_pix);
和 std::vector<int> root_map(total_pix);
代替为我解决了这个问题。
你能在下面评论marko.ristin的问题吗?
不相交集非常高效,解决方案结构良好!太棒了!
ConnectedComponent::ConnectedComponent(int x, int y) : m_bb(x,y,1,1), m_pixel_count(1), m_pixels() m_pixels = std::make_shared > (); /* 没有添加 (x,y) 到 m_pixels 的错误 */ 我认为这里有一个错误,对吧?【参考方案4】:
从 3.0 版本开始,OpenCV 具有connectedComponents
功能。
【讨论】:
【参考方案5】:按照上面假设 4 个连接组件的 DXM 代码,这里是一个用于检测 8 个连接组件的“findCC”版本。
void findCC(const cv::Mat& src, std::vector<ConnectedComponent>& cc)
if (src.empty()) return;
CV_Assert(src.type() == CV_8U);
cc.clear();
int total_pix = int(src.total());
int *frame_label = new int[total_pix];
DisjointSet labels(total_pix);
int *root_map = new int[total_pix];
int x, y;
const uchar* cur_p;
const uchar* prev_p = src.ptr<uchar>(0);
int left_val, up_val, up_left_val, up_right_val;
int cur_idx, left_idx, up_idx, up_left_idx, up_right_idx;
cur_idx = 0;
//first logic loop
for (y = 0; y < src.rows; y++)
cur_p = src.ptr<uchar>(y);
for (x = 0; x < src.cols; x++, cur_idx++)
left_idx = cur_idx - 1;
up_idx = cur_idx - src.size().width;
up_left_idx = up_idx - 1;
up_right_idx = up_idx + 1;
if (x == 0)
left_val = 0;
else
left_val = cur_p[x - 1];
if (y == 0)
up_val = 0;
else
up_val = prev_p[x];
if (x == 0 || y == 0)
up_left_val = 0;
else
up_left_val = prev_p[x-1];
if (x == src.cols - 1 || y == 0)
up_right_val = 0;
else
up_right_val = prev_p[x+1];
if (cur_p[x] > 0)
//current pixel is foreground and has no connected neighbors
if (left_val == 0 && up_val == 0 && up_left_val == 0 && up_right_val == 0)
frame_label[cur_idx] = (int)labels.add();
root_map[frame_label[cur_idx]] = -1;
//Current pixel is foreground and has at least one neighbor
else
vector<int> frame_lbl;
frame_lbl.reserve(4);
//Find minimal label
int min_frame_lbl = INT_MAX;
int valid_entries_num = 0;
if (left_val != 0)
frame_lbl.push_back(frame_label[left_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[left_idx]);
valid_entries_num++;
if (up_val != 0)
frame_lbl.push_back(frame_label[up_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[up_idx]);
valid_entries_num++;
if (up_left_val != 0)
frame_lbl.push_back(frame_label[up_left_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[up_left_idx]);
valid_entries_num++;
if (up_right_val != 0)
frame_lbl.push_back(frame_label[up_right_idx]);
min_frame_lbl = min(min_frame_lbl, frame_label[up_right_idx]);
valid_entries_num++;
CV_Assert(valid_entries_num > 0);
frame_label[cur_idx] = min_frame_lbl;
//Unite if necessary
if (valid_entries_num > 1)
for (size_t i = 0; i < frame_lbl.size(); i++)
labels.unite(frame_lbl[i], min_frame_lbl);
//endif
else
frame_label[cur_idx] = -1;
//end for x
prev_p = cur_p;
//end for y
//second loop logic
cur_idx = 0;
int curLabel;
int connCompIdx = 0;
for (y = 0; y < src.size().height; y++)
for (x = 0; x < src.size().width; x++, cur_idx++)
curLabel = frame_label[cur_idx];
if (curLabel != -1)
curLabel = labels.find(curLabel);
if (root_map[curLabel] != -1)
cc[root_map[curLabel]].addPixel(x, y);
else
cc.push_back(ConnectedComponent(x, y));
root_map[curLabel] = connCompIdx;
connCompIdx++;
//end for x
//end for y
//Free up allocated memory
delete[] frame_label;
delete[] root_map;
【讨论】:
【参考方案6】:你可以使用cv::connectedComponentsWithStats()函数。
这是一个例子。
// ...
cv::Mat labels, stats, centroids;
int connectivity = 8; // or 4
int label_count = cv::connectedComponentsWithStats(src, labels, stats, centroids, connectivity);
for (int i = 0; i < label_count; i++)
int x = stats.at<int>(i, cv::CC_STAT_LEFT);
int y = stats.at<int>(i, cv::CC_STAT_TOP);
int w = stats.at<int>(i, cv::CC_STAT_WIDTH);
int h = stats.at<int>(i, cv::CC_STAT_HEIGHT);
int area = stats.at<int>(i, cv::CC_STAT_AREA);
double cx = centroids.at<double>(i, 0);
double cy = centroids.at<double>(i, 1);
// ...
【讨论】:
以上是关于OpenCV 中的连接组件的主要内容,如果未能解决你的问题,请参考以下文章