Как слить капли/контуры

Я использую findContours для обнаружения blob. Теперь я бы объединил близкие и похожие капли вместе.

Вот несколько примеров изображений:

enter image description hereenter image description hereenter image description here

Возможно ли это с обычным Opencv?

Ответ 1

Входные изображения, которые вы нам дали, довольно просты в работе:

enter image description hereenter image description hereenter image description here

Первый шаг изолировать желтые капли от всего остального, и простая задача сегментации цвета может выполнить эту задачу. Вы можете посмотреть Сегментация и обнаружение объектов по цвету или Отслеживание цветные объекты в OpenCV, чтобы иметь представление о том, как это сделать.

enter image description hereenter image description hereenter image description here

Затем, время для слияния капли. Одним из методов, который может быть полезен, является ограничивающий прямоугольник, чтобы поместить все капли внутри прямоугольника. Обратите внимание на изображения ниже, что есть зеленый прямоугольник, окружающий капли:

enter image description hereenter image description hereenter image description here

После этого все, что вам нужно сделать, это заполнить прямоугольник цветом по вашему выбору, тем самым соединяя все капли. Я оставляю это в качестве домашней работы для вас.

Это самый быстрый и простой подход, о котором я мог думать. Следующий код демонстрирует, как добиться того, что я только что описал:

#include <cv.h>
#include <highgui.h>

#include <iostream>
#include <vector>

int main(int argc, char* argv[])
{
    cv::Mat img = cv::imread(argv[1]);
    if (!img.data)
    {
        std::cout "!!! Failed to open file: " << argv[1] << std::endl;
        return 0;
    }

    // Convert RGB Mat into HSV color space
    cv::Mat hsv;
    cv::cvtColor(img, hsv, CV_BGR2HSV);

    // Split HSV Mat into HSV components
    std::vector<cv::Mat> v;
    cv::split(hsv,v);

    // Erase pixels with low saturation
    int min_sat = 70;
    cv::threshold(v[1], v[1], min_sat, 255, cv::THRESH_BINARY);

    /* Work with the saturated image from now on */

// Erode could provide some enhancement, but I'm not sure.
//  cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
//  cv::erode(v[1], v[1], element);

    // Store the set of points in the image before assembling the bounding box
    std::vector<cv::Point> points;
    cv::Mat_<uchar>::iterator it = v[1].begin<uchar>();
    cv::Mat_<uchar>::iterator end = v[1].end<uchar>();
    for (; it != end; ++it)
    {
        if (*it) points.push_back(it.pos());
    }

    // Compute minimal bounding box
    cv::RotatedRect box = cv::minAreaRect(cv::Mat(points));

    // Display bounding box on the original image
    cv::Point2f vertices[4];
    box.points(vertices);
    for (int i = 0; i < 4; ++i)
    {
            cv::line(img, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 255, 0), 1, CV_AA);
    }

    cv::imshow("box", img);
    //cv::imwrite(argv[2], img);

    cvWaitKey(0);

    return 0;
}

Ответ 2

Я думаю, что сделал это, благодаря вашим данным программы я нашел это решение: (комментарии приветствуются)

vector<vector<Point> > contours;
    vector<vector<Point> > tmp_contours;
    findContours(detectedImg, tmp_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);

    vector<vector<Point> >::iterator it1;
    it1 = tmp_contours.begin();

    Mat test;
    test = Mat(FImage.size(), CV_32FC3);

    while (it1 != tmp_contours.end()) {
        vector<Point> approx1;
        approxPolyDP(Mat(*it1), approx1, 3, true);
        Rect box1 = boundingRect(approx1);
        float area1 = contourArea(approx1);



        if ((area1 > 50) && (area1 < 13000) && (box1.width < 100) && (box1.height < 120)) {

            vector<vector<Point> >::iterator it2;
            it2 = tmp_contours.begin();

            while (it2 != tmp_contours.end()) {
                vector<Point> approx2;
                approxPolyDP(Mat(*it2), approx2, 3, true);

                Moments m1 = moments(Mat(approx1), false);
                Moments m2 = moments(Mat(approx2), false);
                float x1 = m1.m10 / m1.m00;
                float y1 = m1.m01 / m1.m00;
                float x2 = m2.m10 / m2.m00;
                float y2 = m2.m01 / m2.m00;

                vector<Point> dist;
                dist.push_back(Point(x1, y1));
                dist.push_back(Point(x2, y2));
                float d = arcLength(dist, false);

                Rect box2 = boundingRect(approx2);
                if (box1 != box2) {

                    if (d < 25) {
                        //Method to merge the vectors
                        approx1 = mergePoints(approx1, approx2);
                    }

                }
                ++it2;

            }
            Rect b = boundingRect(approx1);
            rectangle(test, b, CV_RGB(125, 255, 0), 2);
            contours.push_back(approx1);
        }
        ++it1;
    }