【问题标题】:How can i draw boundary across a particular colour in opencv?如何在opencv中跨特定颜色绘制边界?
【发布时间】:2015-07-29 09:01:47
【问题描述】:

假设我有一张图片。我基本上想在我想要的特定颜色上划出边界。我知道该颜色的 hsv 最小和最大标量值。但我不知道如何进一步进行。

#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include<stdio.h>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main(int argc, char** argv)
{
    VideoCapture cap(0);
    while(true)
    {
    Mat img;
    cap.read(img);
    Mat dst;
    Mat imghsv;
    cvtColor(img, imghsv, COLOR_BGR2HSV);
    inRange(imghsv,
           Scalar(0, 30, 0),
           Scalar(20, 150, 255),
           dst
           );
    imshow("name",dst);
    if (waitKey(30) == 27) //wait for 'esc' key press for 30ms
       {
            cout << "esc key is pressed by user" << endl;
            break;
       }
    }
}

inrange 函数运行良好,但我无法在白色范围内绘制边界(我的意思是指定范围内的任何像素)

【问题讨论】:

  • 您应该发布更多信息:参考图像、编程语言、您的 HSV 界限
  • mcve
  • 既然你用一些代码更新了你的问题,我也用一些代码更新了我的答案!检查一下,看看它是否满足您的需求。 (下次,请通知人们您更新了问题并评论他们的答案)

标签: opencv image-processing opencv-contour


【解决方案1】:

你需要先对颜色进行分割,然后找到分割后图像的轮廓。

分割颜色

在 HSV 中工作通常是分割颜色的好主意。一旦你有了正确的上下边界,你就可以轻松地分割颜色。 一个简单的方法是使用inRange。 例如,您可以找到如何使用它here

寻找边界

一旦你有了二进制掩码(通过分割获得),你可以使用findContours找到它的边界。可以参考thisthis了解如何使用findContours检测边界,drawContours绘制边界。


更新

这是一个关于如何在分段对象上绘制轮廓的工作示例。 我用了一些形态来清洁面具,并将跟踪颜色改为蓝色,但你可以放上你喜欢的颜色。

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

using namespace std;
using namespace cv;

int main(int argc, char** argv)
{
    VideoCapture cap(0);
    while (true)
    {
        Mat img;
        cap.read(img);
        Mat dst;
        Mat imghsv;
        cvtColor(img, imghsv, COLOR_BGR2HSV);
        inRange(imghsv, Scalar(110, 100, 100), Scalar(130, 255, 255), dst); // Detect blue objects


        // Remove some noise using morphological operators
        Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(7,7));
        morphologyEx(dst, dst, MORPH_OPEN, kernel);

        // Find contours
        vector<vector<Point>> contours;
        findContours(dst.clone(), contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

        // Draw all contours (green)
        // This 
        drawContours(img, contours, -1, Scalar(0,255,0));

        // If you want to draw a contour for a particular one, say the biggest...

        // Find the biggest object
        if (!contours.empty())
        {
            int idx_biggest = 0;
            int val_biggest = contours[0].size();

            for (int i = 0; i < contours.size(); ++i)
            {
                if (val_biggest < contours[i].size())
                {
                    val_biggest = contours[i].size();
                    idx_biggest = i;
                }
            }

            // Draw a single contour (blue)
            drawContours(img, contours, idx_biggest, Scalar(255,0,0));

            // You want also the rotated rectangle (blue) ?

            RotatedRect r = minAreaRect(contours[idx_biggest]);

            Point2f pts[4];
            r.points(pts);
            for (int j = 0; j < 4; ++j)
            {
                line(img, pts[j], pts[(j + 1) % 4], Scalar(0, 0, 255), 2);
            }
        }

        imshow("name", dst);
        imshow("image", img);

        if (waitKey(30) == 27) //wait for 'esc' key press for 30ms
        {
            cout << "esc key is pressed by user" << endl;
            break;
        }
    }
}

【讨论】:

    【解决方案2】:

    如果您想要检测特定的色调,那么您可以创建一个蒙版以仅从原始图像中选择特定的颜色。

    在色调通道(img)上:

    cv::Mat mask = cv::Mat::zeros(img.size(),CV_8UC1);
    
    for(int i=0;i<img.rows;i++){
        for(int j=0;j<img.cols;i++){
            if(img.at<uchar>(i,j)==(uchar)specific_hue){
                mask.at<uchar>(i,j)=(uchar)255;
            }
        }
    }
    
    color_img.copyTo(masked_image, mask);
    

    如果您想要不那么严格的东西,您可以在颜色周围定义一个范围,以允许更多图像通过遮罩。

    cv::Mat mask = cv::Mat::zeros(img.size(),CV_8UC1);
    int threshold = 5;
    
    for(int i=0;i<img.rows;i++){
        for(int j=0;j<img.cols;i++){
            if((img.at<uchar>(i,j)>(uchar)(specific_hue - threshold)) && (img.at<uchar>(i,j)<(uchar)(specific_hue + threshold))){
                mask.at<uchar>(i,j)=(uchar)255;
            }
        }
    }
    
    color_img.copyTo(masked_image, mask);
    

    【讨论】:

    • 您知道inRange 确实做到了这一点吗?另外,我在答案中没有看到关于绘制边界的部分。
    • 不,我不知道这个功能,因为我自己做的很快。谢谢(你的)信息。另一方面,对于轮廓,我发现遮罩可以是一个很好的开始,然后应用你想要的轮廓(作者没有指定)。例如,我正在考虑类似 fitEllipse 但我同意我的答案缺少该信息。
    • 虽然您的循环通常可以正常工作,但对于分割 HSV 颜色,您还应该添加对 S 和 V 值的约束,通常两者都 > 100(或者您也分割非常深到黑色的颜色)。因此,对于您的工作方法,您还应该包括它。您还对行进行了两次迭代,第二个循环应该是j&lt;img.cols; ++j。一个小提示:使用Mat1b mask(img.size(), uchar(0));,这样您就可以通过mask(i,j) 访问它。其他提示:不要使用 C cast,即使用 uchar(xxx) 而不是 (uchar)xxx
    猜你喜欢
    • 1970-01-01
    • 2019-01-23
    • 2021-12-23
    • 2021-09-27
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多