componentes conectados en OpenCV

Estoy buscando una función OpenCV que pueda encontrar componentes conectados y realizar algunas tareas en ellos (como obtener el número de píxeles, el contorno, la lista de píxeles en el objeto, etc.)

¿Hay una función de OpenCV (C ++) que sea similar a regionprops de MatLab?

A partir de la versión 3.0, OpenCV tiene la función connectedComponents .

Eche un vistazo a la función cvFindContours . Es muy versátil, puede encontrar contornos interiores y exteriores y devolver los resultados en una variedad de formatos (por ejemplo, lista plana vs. estructura de árbol). Una vez que tenga los contornos, las funciones como cvContourArea le permiten determinar las propiedades básicas del componente conectado correspondiente a un contorno particular.

Si prefiere usar la interfaz C ++ más nueva (a diferencia de la interfaz C anterior que describí arriba), los nombres de las funciones son similares .

set -std = opción c ++ 0x al comstackr

archivo .h

 //connected_components.h #ifndef CONNECTED_COMPONENTS_H_ #define CONNECTED_COMPONENTS_H_ #include  #include  class DisjointSet { private: std::vector 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 > 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 > getPixels(void) const; }; void findCC(const cv::Mat& src, std::vector& cc); #endif //CONNECTED_COMPONENTS_H_ 

archivo .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 > (); } 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 > (); } 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.xx); 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.yy); 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 > 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& 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(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(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 } 

Si no le importa usar una biblioteca externa que use OpenCV, puede hacerlo usando cvBlobsLib .

Una biblioteca para realizar el etiquetado de componentes conectados a imágenes binarias ( similar a la función Matlab de regionprops ). También proporciona funciones para manipular, filtrar y extraer resultados de los blobs extraídos; consulte la sección de características para obtener más información.

Siguiendo el código anterior de DXM que supone componentes de 4 conexiones, aquí hay una versión para ‘findCC’ que detecta componentes conectados por 8.

 void findCC(const cv::Mat& src, std::vector& 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(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(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 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; 

}