C++ OpenCV 中的连接组件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12688524/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
connected components in OpenCV
提问by EyalG
I am looking for an OpenCV function that can find connected components and perform a few tasks on them ( like getting the number of pixels, contour, list of pixels in the object etc.. )
我正在寻找一个 OpenCV 函数,它可以找到连接的组件并对其执行一些任务(例如获取像素数、轮廓、对象中的像素列表等。)
Is there a function of OpenCV (C++) that is similar to MatLab's regionprops ?
是否有类似于 MatLab 的 regionprops 的 OpenCV (C++) 函数?
回答by Alessandro Jacopson
Starting from version 3.0, OpenCV has connectedComponents
function.
从 3.0 版本开始,OpenCV 有了connectedComponents
功能。
回答by mpenkov
Have a look at the cvFindContoursfunction. It's very versatile -- it can find both interior and exterior contours, and return the results in a variety of formats (e.g. flat list vs. tree structure). Once you have the contours, functions like cvContourAreaallow you to determine basic properties of the connected component corresponding to a particular contour.
看看cvFindContours函数。它非常通用——它可以找到内部和外部轮廓,并以各种格式返回结果(例如平面列表与树结构)。一旦你有了轮廓,像cvContourArea这样的函数允许你确定与特定轮廓相对应的连接组件的基本属性。
If you prefer to use the newer C++ interface (as opposed to the older C-style interface I described above), then the function names are similar.
如果您更喜欢使用较新的 C++ 接口(而不是我上面描述的较旧的 C 样式接口),那么函数名称是相似的。
回答by DXM
set -std=c++0x option when compiling
编译时设置 -std=c++0x 选项
.h file
.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 file
.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
}
回答by Dan
If you don't mind using an external library that uses OpenCV, you can do that using cvBlobsLib.
如果您不介意使用使用 OpenCV 的外部库,您可以使用cvBlobsLib来做到这一点。
A library to perform binary images connected component labelling (similar to regionprops Matlab function). It also provides functions to manipulate, filter and extract results from the extracted blobs, see features section for more information.
一个执行二进制图像连接组件标记的库(类似于 regionprops Matlab 函数)。它还提供了从提取的 blob 中操作、过滤和提取结果的功能,有关更多信息,请参阅功能部分。
回答by Ido
Following DXM's code above which assumes 4-connected components, here is a version for 'findCC' that detects 8-connected components.
按照上面假设有 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;
}
}
回答by ChangYun Du
You can use cv::connectedComponentsWithStats()function.
您可以使用cv::connectedComponentsWithStats()函数。
Here is an example.
这是一个例子。
// ...
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);
// ...
}