1

I'm trying to calculate the Mean & Std Deviation for red only contours. I suspect that HSV pixels for red Hue values of a Vec3b are stored from 0-10 and 165-179.

Here is my code:

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


using namespace cv;
using namespace std;

int main(int argc, char** argv) {


// Mat Declarations
// Mat img = imread("white.jpg");
// Mat src = imread("Rainbro.png");
Mat src = imread("multi.jpg");
// Mat src = imread("DarkRed.png");
Mat Hist;
Mat HSV;
Mat Edges;
Mat Grey;

vector<vector<Vec3b>> hueMEAN;
vector<vector<Point>> contours;

// Variables
int edgeThreshold = 1;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
int lowThreshold = 0;

// Windows
namedWindow("img", WINDOW_NORMAL);
namedWindow("HSV", WINDOW_AUTOSIZE);
namedWindow("Edges", WINDOW_AUTOSIZE);
namedWindow("contours", WINDOW_AUTOSIZE);

// Color Transforms
cvtColor(src, HSV, CV_BGR2HSV);
cvtColor(src, Grey, CV_BGR2GRAY);
// Perform Hist Equalization to help equalize Red hues so they stand out for 
// better Edge Detection

equalizeHist(Grey, Grey);


// Image Transforms
blur(Grey, Edges, Size(3, 3));
Canny(Edges, Edges, max_lowThreshold, lowThreshold * ratio, kernel_size);
findContours(Edges, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);

//Rainbro MAT
//Mat drawing = Mat::zeros(432, 700, CV_8UC1);

//Multi MAT
Mat drawing = Mat::zeros(630, 1200, CV_8UC1);

//Red variation Mat
//Mat drawing = Mat::zeros(600, 900, CV_8UC1);

vector <vector<Point>> ContourPoints;

/* This code for loops through all contours and assigns the value of the y coordinate as a parameter
for the row pointer in the HSV mat. The value vec3b pointer pointing to the pixel in the mat is accessed
and stored for any Hue value that is between 0-10 and 165-179 as Red only contours.*/

for (int i = 0; i < contours.size(); i++) {
    vector<Vec3b> vf;
    vector<Point> points;
    bool isContourRed = false;

    for (int j = 0; j < contours[i].size(); j++) {
        //Row Y-Coordinate of Mat from Y-Coordinate of Contour
        int MatRow = int(contours[i][j].y);
        //Row X-Coordinate of Mat from X-Coordinate of Contour
        int MatCol = int(contours[i][j].x);

        Vec3b *HsvRow = HSV.ptr <Vec3b>(MatRow);

        int h = int(HsvRow[int(MatCol)][0]);
        int s = int(HsvRow[int(MatCol)][1]);
        int v = int(HsvRow[int(MatCol)][2]);

        cout << "Coordinate: ";
        cout << contours[i][j].x;
        cout << ",";
        cout << contours[i][j].y << endl;
        cout << "Hue: " << h << endl;

        // Get contours that are only in the red spectrum Hue 0-10, 165-179
        if ((h <= 10 || h >= 165 && h <= 180) && ((s > 0) && (v > 0))) {
            cout << "Coordinate: ";
            cout << contours[i][j].x;
            cout << ",";
            cout << contours[i][j].y << endl;
            cout << "Hue: " << h << endl;

            vf.push_back(Vec3b(h, s, v));
            points.push_back(contours[i][j]);
            isContourRed = true;
        }

    }
    if (isContourRed == true) {
        hueMEAN.push_back(vf);
        ContourPoints.push_back(points);
    }
}

drawContours(drawing, ContourPoints, -1, Scalar(255, 255, 255), 2, 8);

// Calculate Mean and STD for each Contour
cout << "contour Means & STD of Vec3b:" << endl;
for (int i = 0; i < hueMEAN.size(); i++) {

    Scalar meanTemp = mean(hueMEAN.at(i));
    Scalar sdTemp;
    cout << i << ": " << endl;
    cout << meanTemp << endl;
    cout << " " << endl;
    meanStdDev(hueMEAN.at(i), meanTemp, sdTemp);
    cout << sdTemp << endl;
    cout << " " << endl;
}
cout << "Actual Contours: " << contours.size() << endl;
cout << "# Contours: " << hueMEAN.size() << endl;

imshow("img", src);
imshow("HSV", HSV);
imshow("Edges", Edges);
imshow("contours", drawing);
waitKey(0);

return 0;
}

I've come across an issue in this particular case: enter image description here

On the right is the original Image, The left displays the HSV mat, the Edge detection and an arrow is pointing to a contours Mat that I drew after the filtering.

Here is the source image: enter image description here

After the filtering is complete I just calculate the Mean and STD.

I have a feeling that my range is incorrect for 0-10 and 165-179. Any suggestions or further improvements would help a lot.

Thanks.

1 Answers1

2

A quick test shows me that the range is correct. Without all the contour extraction stuff, if I just filter the colors using 0-10 and 165-179 ranges, I get the two red boxes in the lower-middle range of your input image.

The contour artifact that you see might actually be coming from both a JPEG artifact (if you zoom in at the limit between the white and red box, you can see that it is gradual and not sharp, due to JPEG compression), and the fact that you are only thresholding in the Hue channel. At low saturation, many grey-ish colors which you don't want will actually be fitting within your hue threshold. The solution for that is to filter pixel values in the S and V channel as well.

In your code, that means changing the line if ((h <= 10 || h >= 165 && h <= 180) && ((s > 0) && (v > 0))) { to if ((h <= 10 || h >= 165 && h <= 180) && ((s > 50) && (v > 50))) {

The value 50 is working on that specific sample image, but of course the correct value will depend on your input image.

Ben
  • 441
  • 3
  • 10
  • Also, look at that answer (in python) for examples using the inRange function and masks, which will be significantly faster than looping through every image pixel yourself : https://stackoverflow.com/questions/30331944/finding-red-color-using-python-opencv – Ben Oct 27 '17 at 07:54
  • Thanks for the feedback I've done some thresholding with masks before to filter things out. The goal I wanted was to filter out red contours based on standard deviation only so if I'm gonna for loop through the contour anyway I might as well save performance and it at the same time. – Julien Popa-Liesz Oct 27 '17 at 17:14
  • If you notice the line between orange and dark red. The contours is not drawn for it? – Julien Popa-Liesz Oct 27 '17 at 20:09
  • I know there is a bit of a difference in performance for python and c++ being significantly always faster depending on the operation performed. A Mask still goes through every pixel for every operation which is optimized in opencv. I am using a pointer which is pretty fast. So instead of doing a mask and then storing and then making a calculation I thought this could be done in the same operation when already going through the pixels anyway. – Julien Popa-Liesz Oct 28 '17 at 08:55
  • The remark about looping was also to say that openCV functions are tested and validated, so using them for prototyping an algorithm is a good idea. Coming back to your algorithm, if I understand well you blur the image, do a gradient with a Canny algorithm and then find contours on it. Only then do you loop on contours and try to find red pixels in the original image (in HSV space). I would first have a look at intermediary images to make sure nothing unexpected happens. Also, why not filtering to keep red pixels before doing the gradient / findContours ? – Ben Oct 28 '17 at 18:51
  • You are correct with your understanding. I really appreciate you taking the time to look into this. Regarding your perspective on performing an intermediary step and filtering red pixels before doing a contour loop is something I'm looking for. The goal of my post was to see if I had correct Hue range for red and an acceptable algorithm for getting the red pixels of contours only. – Julien Popa-Liesz Oct 28 '17 at 21:31