it-roy-ru.com

подключенные компоненты в OpenCV

Я ищу функцию OpenCV, которая может найти подключенные компоненты и выполнить с ними несколько задач (например, получить количество пикселей, контур, список пикселей в объекте и т.д.)

Есть ли функция OpenCV (C++), которая похожа на regionprops MatLab?

37
EyalG

Начиная с версии 3.0, OpenCV имеет функцию connectedComponents .

26
Alessandro Jacopson

Посмотрите на cvFindContours функцию. Он очень универсален - он может находить как внутренние, так и внешние контуры и возвращать результаты в различных форматах (например, плоский список или древовидная структура). Когда у вас есть контуры, такие функции, как cvContourArea позволяют вам определять основные свойства подключенного компонента, соответствующие определенному контуру.

Если вы предпочитаете использовать более новый интерфейс C++ (в отличие от более старого интерфейса в стиле C, который я описал выше), тогда имена функций аналогичны .

22
mpenkov

установить параметр -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
}
8
DXM

Если вы не возражаете против использования внешней библиотеки, которая использует OpenCV, вы можете сделать это, используя cvBlobsLib .

Библиотека для выполнения бинарных изображений, связанных с маркировкой компонентов (аналогичен функции Matrob regionprops). Он также предоставляет функции манипулировать, фильтровать и извлекать результаты из извлеченных BLOB-объектов, см. раздел функций для получения дополнительной информации.

3
Dan

Следуя приведенному выше коду DXM, который предполагает 4-х компонентные компоненты, вот версия 'findCC', которая обнаруживает 8-ми компонентные компоненты.

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;

}

1
Ido