【问题标题】:connected components in OpenCVOpenCV 中的连接组件
【发布时间】:2012-09-23 04:51:20
【问题描述】:

我正在寻找一个 OpenCV 函数,它可以找到连接的组件并对其执行一些任务(例如获取像素数、轮廓、对象中的像素列表等)

OpenCV(C++)有没有类似MatLab的regionprops的功能?

【问题讨论】:

    标签: c++ matlab image-processing opencv computer-vision


    【解决方案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&lt;int&gt; frame_label(total_pix);std::vector&lt;int&gt; 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);
        
                // ...
            }
        

        【讨论】:

          猜你喜欢
          • 2015-05-20
          • 2014-04-13
          • 1970-01-01
          • 1970-01-01
          • 2012-03-06
          • 2018-08-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多