connected components in OpenCV

前端 未结 6 2019
既然无缘
既然无缘 2020-11-27 04:58

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 ob

相关标签:
6条回答
  • 2020-11-27 05:20

    You can use cv::connectedComponentsWithStats() function.

    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);
    
            // ...
        }
    
    0 讨论(0)
  • 2020-11-27 05:22

    Starting from version 3.0, OpenCV has connectedComponents function.

    0 讨论(0)
  • 2020-11-27 05:30

    set -std=c++0x option when compiling

    .h file

    //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

    //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
    }
    
    0 讨论(0)
  • 2020-11-27 05:32

    Following DXM's code above which assumes 4-connected components, here is a version for 'findCC' that detects 8-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 = 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;
    

    }

    0 讨论(0)
  • 2020-11-27 05:40

    If you don't mind using an external library that uses OpenCV, you can do that using 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.

    0 讨论(0)
  • 2020-11-27 05:46

    Have a look at the cvFindContours function. 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 cvContourArea allow you to determine basic properties of the connected component corresponding to a particular contour.

    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.

    0 讨论(0)
提交回复
热议问题