0

Suppose I have an image. I basically want to make boundary across a particular colour that I want. I know the hsv minimum and maximum scalar values of that colour. But I don't know how to proceed further.

#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;
       }
    }
}

The inrange function works well but I am not able to draw a boundary across whatever is white (I mean whichever pixel is in the range specified)

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
Kaushik Nath
  • 106
  • 1
  • 11
  • you should post more information: a reference image, the programming language, your HSV bounds – Miki Jul 29 '15 at 11:04
  • i.e. an [mcve](http://stackoverflow.com/help/mcve) – kkuilla Jul 29 '15 at 13:06
  • 1
    Since you updated your question with some code, I updated my answer with some code, too! Check it out, and look if it meets your needs. (Next time, please notify people that you updated the question with a comment on the their answers) – Miki Aug 03 '15 at 23:45

2 Answers2

2

You need to first segment the color, and then find the contours of the segmented image.

SEGMENT THE COLOR

Working in HSV is in general a good idea to segment colors. Once you have the correct lower and upper boundary, you can easily segment the color. A simple approach is to use inRange. You can find how to use it here for example.

FIND BOUNDARIES

Once you have the binary mask (obtained through segmentation), you can find its boundaries using findContours. You can refer to this or this to know how to use findContours to detect the boundary, and drawContours to draw it.


UPDATE

Here a working example on how to draw a contour on segmented objects. I used some morphology to clean the mask, and changed to tracked color to be blue, but you can put your favorite color.

#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;
        }
    }
}
Community
  • 1
  • 1
Miki
  • 40,887
  • 13
  • 123
  • 202
0

If you want a particular hue to be detected then you can create a mask to select only the particular color from your original image.

on the hue channel (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);

If you want something less rigorous, you can define a range around the color to allow more image to pass through the 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);
AdMor
  • 91
  • 7
  • are you aware that `inRange` does this exactly? Also, I don't see in the answer the part about drawing boundaries. – Miki Jul 29 '15 at 11:07
  • No i didn't know the function, as doing it myself was pretty fast. Thanks for the info. For the contour on the other hand, i find that the mask can be a good start to then apply what you want for the contour (which wasn't specified by the author). I was thinking for example at something like fitEllipse But I agree that my answer lacks of that information. – AdMor Jul 29 '15 at 11:43
  • 1
    While your loop works ok in general, for segmenting HSV colors you should also add a constraint on S and V value, usually to have both > 100 (or you segment also very dark to black colors). So for your approach to work, you should also include that. You're also iterating two times on rows, second loop should be `j – Miki Jul 29 '15 at 11:51