如何通过绘制边界框(或多边形)从图像中裁剪多个对象?

How to crop multiple objects from an image by drawing bounding boxes (or polygons)?

如何使用 OpenCV 使用鼠标绘制绑定框从图像中裁剪多个对象。请看下图:

我想 select 使用鼠标点击并裁剪可乐罐边界。

需要将多个多边形绘制到 select 个对象的解决方案:

我可以绘制单个多边形,但不能绘制多个多边形。这是代码:

void mouseHandler(int event, int x, int y, int, void*) {

if (event == EVENT_LBUTTONDOWN && !drag)
{
    if (flag1 == 0)
    {
        if (var == 0)
            img1 = img0.clone();
        point = Point(x, y);
        circle(img1, point, 2, Scalar(0, 0, 255), -1, 8, 0);
        pts[var] = point;
        var++;
        drag = 1;
        if (var>1)
            line(img1, pts[var - 2], point, Scalar(0, 0, 255), 2, 8, 0);

        imshow("Source", img1);
    }
}

if (event == EVENT_LBUTTONUP && drag)
{
    imshow("Source", img1);

    drag = 0;
}
if (event == EVENT_RBUTTONDOWN)
{
    flag1 = 1;
    img1 = img0.clone();
    for (int i = var; i < numpts; i++)
        pts[i] = point;

    if (var != 0)
    {
        const Point* pts3[1] = { &pts[0] };
        polylines(img1, pts3, &numpts, 1, 1, Scalar(0, 0, 0), 2, 8, 0);
    }

    for (int i = 0; i<var; i++)
    {
        minx = min(minx, pts[i].x);
        maxx = max(maxx, pts[i].x);
        miny = min(miny, pts[i].y);
        maxy = max(maxy, pts[i].y);
    }
    lenx = maxx - minx;
    leny = maxy - miny;

    imshow("Source", img1);
}

if (event == EVENT_RBUTTONUP)
{
    flag = var;

    final = Mat::zeros(img0.size(), CV_8UC3);
    res1 = Mat::zeros(img0.size(), CV_8UC1);
    const Point* pts4[1] = { &pts[0] };

    fillPoly(res1, pts4, &numpts, 1, Scalar(255, 255, 255), 8, 0);
    bitwise_and(img0, img0, final, res1);
    imshow("mask", res1);
    imwrite("mask.png", res1);

    imshow("Source", img1);



}
if (event == EVENT_MBUTTONDOWN)
{
    for (int i = 0; i < numpts; i++)
    {
        pts[i].x = 0;
        pts[i].y = 0;
    }
    var = 0;
    flag1 = 0;
    minx = INT_MAX; miny = INT_MAX; maxx = INT_MIN; maxy = INT_MIN;
    imshow("Source", img0);
    drag = 0;
}

}

int main() {

Mat src = imread("abc.jpg");

minx = INT_MAX; miny = INT_MAX; maxx = INT_MIN; maxy = INT_MIN;

img0 = src;

channel = img0.channels();

res1 = Mat::zeros(img0.size(), CV_8UC1);
final = Mat::zeros(img0.size(), CV_8UC3);
//////////// source image ///////////////////

namedWindow("Source", 1);
setMouseCallback("Source", mouseHandler, NULL);
imshow("Source", img0);
imshow("mask", res1);
waitKey(0);


img0.release();
img1.release();

}

如果只想画矩形,可以参考this answer中的代码。


如果需要绘制多边形,请看下面的代码。您可以使用鼠标左键单击来绘制多边形顶点。左键双击将关闭多边形,并开始一个新的多边形。

请注意,添加删除顶点的选项可能会有所帮助,例如删除离鼠标右键单击最近的顶点。

底部将向您展示如何保存裁剪后的图像。

代码:

#include <opencv2\opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

vector<vector<Point>> polygons;
bool bDraw;
vector<Point> poly;


Mat3b img;
Mat3b layer;
Mat3b working;


void CallBackFunc(int event, int x, int y, int flags, void* userdata)
{
    if (event == EVENT_LBUTTONDOWN)
    {
        //cout << "Left button of the mouse is clicked - position (" << x << ", " << y << ")" << endl;

        // Refresh
        working = layer.clone();

        if (!bDraw)
        {
            // Init your polygon
            poly.clear();
            bDraw = true;
        }

        // Add Current Point
        poly.push_back(Point(x, y));

        // Draw Poly
        for (size_t i = 1; i < poly.size(); ++i) {
            line(working, poly[i - 1], poly[i], Scalar(0, 255, 0));
        }
        // Draw Points
        for (size_t i = 0; i < poly.size(); ++i) {
            circle(working, poly[i], 3, Scalar(0, 0, 255));
        }

        // Update
        imshow("My Window", working);

    }
    else if (event == EVENT_MOUSEMOVE)
    {
        //cout << "Mouse move over the window - position (" << x << ", " << y << ")" << endl;

        // If drawing, update rect width and height
        if (!bDraw) return;

        // Refresh
        working = layer.clone();

        // Draw Poly
        for (size_t i = 1; i < poly.size(); ++i) {
            line(working, poly[i - 1], poly[i], Scalar(0, 255, 0));
        }
        // Draw Points
        for (size_t i = 0; i < poly.size(); ++i) {
            circle(working, poly[i], 3, Scalar(0, 0, 255));
        }
        // Draw Current line
        line(working, poly.back(), Point(x, y), Scalar(0, 255, 0));

        // Update
        imshow("My Window", working);
    }
    else if (event == EVENT_LBUTTONDBLCLK)
    {
        //cout << "Left button double clicked" << endl;

        // Refresh
        working = layer.clone();

        // Add Current Point
        poly.push_back(Point(x, y));

        // Save poly, draw it on layer
        polygons.push_back(poly);

        // Draw Poly
        for (size_t i = 1; i < poly.size(); ++i)
        {
            line(working, poly[i - 1], poly[i], Scalar(0, 255, 255));
        }
        // Draw closed poly
        line(working, poly.back(), poly.front(), Scalar(0, 255, 255));

        // Draw Points
        for (size_t i = 0; i < poly.size(); ++i) {
            circle(working, poly[i], 3, Scalar(0, 0, 255));
        }

        layer = working.clone();

        bDraw = false;

        // Update
        imshow("My Window", working);
    }
}

int main(int argc, char** argv)
{
    bool bDraw = false;

    // Read image from file 
    img = imread("path_to_image");

    // initialize your temp images
    layer = img.clone();
    working = img.clone();

    //if fail to read the image
    if (img.empty())
    {
        cout << "Error loading the image" << endl;
        return -1;
    }

    //Create a window
    namedWindow("My Window", 1);

    //set the callback function for any mouse event
    setMouseCallback("My Window", CallBackFunc, NULL);

    //show the image
    imshow("My Window", working);

    // Wait until user press 'q'
    while ((waitKey(1) & 0xFF) != 'q');

    // Create cropped images and show / save
    for (size_t i = 0; i < polygons.size(); ++i)
    {
        Mat3b out(img.rows, img.cols, Vec3b(0, 0, 0));
        Mat1b mask(img.rows, img.cols, uchar(0));
        drawContours(mask, polygons, i, Scalar(255), CV_FILLED);
        img.copyTo(out, mask);

        Rect box = boundingRect(polygons[i]);
        out = out(box).clone();

        imshow("Crop #" + to_string(i), out);
        waitKey(1);
        //imwrite("Crop #" + to_string(i), out);
    }

    waitKey();
    return 0;
}