简体   繁体   中英

Extract OpenCV Contour Array from Blob Detection in C++

I'm playing around with blob detection techniques in C++ and OpenCV but I'm having trouble extracting the contour information in a usable format.

Currently I have all contour data stored as an array in a cv::Point format; however, I wish to extract the coordinates and store them in an array under a variable so I can manipulate this data as needed.

My code looks like this:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/opencv.hpp"
#include <iostream>
#define _USE_MATH_DEFINES
#include <math.h>

using namespace cv;
using namespace std;

int main(int argc, const char** argv)
{
    cv::Mat src = cv::imread("frame-1.jpg");
    if (src.empty())
        return -1;

    cv::Mat gray;
    cv::cvtColor(~src, gray, CV_BGR2GRAY);

    cv::threshold(gray, gray, 160, 255, cv::THRESH_BINARY);

    // Find all contours
    std::vector<std::vector<cv::Point> > contours;
    cv::findContours(gray.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    // Fill holes in each contour
    cv::drawContours(gray, contours, -1, CV_RGB(255, 255, 255), -1);

    cout << contours.size();

std::vector<cv::Point> minXY(contours.size()); // Top Left Point of Bounding Box
std::vector<cv::Point> maxXY(contours.size()); // Bottom Right Point of Bounding Box
for (int i = 0; i < contours.size()){
    minXY[i] = maxXY[i] = contours[i][0];  // Assumes the contour has at least 1 point
    for(int j = 1; j < contours[i].size(); ++j){
         if (contours[i][j].x < minXY[i].x){
            minXY[i].x = contours[i][j].x;
         } else if (contours[i][j].x > maxXY[i].x){
            maxXY[i].x = contours[i][j].x;
         }
         if (contours[i][j].y < minXY[i].y){
            minXY[i].y = contours[i][j].y;
         } else if (contours[i][j].y > maxXY[i].y){
            maxXY[i].y = contours[i][j].y;
         }
    }
}

    namedWindow("MyWindow", CV_WINDOW_AUTOSIZE);
    imshow("MyWindow", gray);

    waitKey(0);

    destroyWindow("MyWindow");

    return 0;
}

The result of contours[2] gives an array with contour coordinates of the third blob; however, I want to extract this array into a normal variable so I can actually use it. I'm assuming this means some sort of cv::Point conversion?

UPDATE: I should probably clarify that I'd like the coordinates of largest blob's contour so that I can manipulate the data such as finding the smallest x point, finding the average x point, find the centroid of the blob etc.

Currently I have been able to find all the contours and then identify the largest blob, as well as identify its contour coordinates. However, I haven't found a way to search through this array to find the least x value, or a method to sum just the x coordinates. I would ultimately also like to find the most efficient method of finding the blob's centroid (using bounding box, moments, simple arithmetic or other method.)

** contours[2] spits out an array in the form:

[100, 267]
[101, 270]
[102, 271]

I want to find a way to search the left column (ie. all the x values) to find the smallest and largest etc. I've tried several methods but none have gotten me close. Surely there's an easy solution to this. It seems all my problems stem from the fact that the contour array is in the form of cv::Point.

If you want the points that make up the 3rd contour you can do the following:

std::vector<cv::Point> my_contour = contours[2];

or you can just loop over all of the countours by doing:

for (int i = 0; i < contours.size(); ++i){
   double avg_x(0), avg_y(0); // average of contour points
   for (int j = 0; j < contours[i].size(); ++j){
      // Do whatever you need to do with the points in the ith contour
      avg_x += contours[i][j].x;
      avg_y += contours[i][j].y;
   }
   avg_x /= contours[i].size();
   avg_y /= contours[i].size();
   cout << avg_x << " " << avg_y << endl;
}

From your comment you also wanted to calculate the centroid of the contour.

You can find the moments of each blob using OpenCV

std::vector<cv::Moment> moments(contours.size());
std::vector<cv::Point> centroids(contours.size());
for (int i = 0; i < contours.size()){
    moments[i] = cv::moments(contours[i], false);
    centroids[i] = cv::Point(moments[i].m10/moments[i].m00, moments[i].m01/moments[i].m00);
}

The 0th moment is the area of the blob, first moments the centroid and 2nd moments can be used to find orientation and eccentricity of the blob.

Wikipedia is a good starting place for learning more about image moments. https://en.wikipedia.org/wiki/Image_moment

Using OpenCV you can get the Bounding Box and enclosing circle of a contour by first approximating it's contour as a polygon.

The following code is from OpenCV

std::vector<vector<cv::Point> > contours_poly(contours.size());
std::vector<cv::Rect> boundingRect (contours.size());
std::vector<cv::Point> center(contours.size());
std::vector<float> radius(contours.size());
for (int i = 0; i < contours.size(); ++i){
  cv::approxPolyDP(cv::Mat(contours[i]), contours_poly[i], 3, true);
  boundingRect[i] = cv::boundingRect(cv::Mat(contours_poly[i]));
  cv::MinEnclosingCircle((cv::Mat)contours_poly[i], center[i], radius[i]);
}

Or you can do it yourself (for bounding box).

std::vector<cv::Point> minXY(contours.size()); // Top Left Point of Bounding Box
std::vector<cv::Point> maxXY(contours.size()); // Bottom Right Point of Bounding Box
for (int i = 0; i < contours.size()){
    minXY[i] = maxXY[i] = contours[i][0];  // Assumes the contour has at least 1 point
    for(int j = 1; j < contours[i].size(); ++j){
         if (contours[i][j].x < minXY[i].x){
            minXY[i].x = contours[i][j].x;
         } else if (contours[i][j].x > maxXY[i].x){
            maxXY[i].x = contours[i][j].x;
         }
         if (contours[i][j].y < minXY[i].y){
            minXY[i].y = contours[i][j].y;
         } else if (contours[i][j].y > maxXY[i].y){
            maxXY[i].y = contours[i][j].y;
         }
    }
}

For contour[i]:

The most left point is at MinXY[i].x

the right most point is at MaxXY[i].x

the top most point is at MinXY[i].y (Note Y axis is inverted)

the bottom most point is at MaxXY[i].y

EDIT: To answer Modified Question.

float min_x = contours[2][0].x;
for (int i = 1; i < countours[2].size(); ++i){
    if (contours[2][i].x < min_x){
        min_x = contours[2][i].x;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM