1
votes

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

1 Answers

2
votes

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.