c++ - 使用 HSV 范围逐像素过滤红色轮廓

标签 c++ opencv

我正在尝试计算仅红色轮廓的均值和标准差。我怀疑 Vec3b 的红色 Hue 值的 HSV 像素存储在 0-10 和 165-179 之间。

这是我的代码:

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

在这个特殊情况下我遇到了一个问题:enter image description here

右边是原始图像,左边显示的是 HSV 垫,边缘检测和箭头指向我在过滤后绘制的轮廓垫。

这是源图像:enter image description here

过滤完成后,我只计算平均值和标准差。

我感觉我的 0-10 和 165-179 范围不正确。任何建议或进一步的改进都会有很大帮助。

谢谢。

最佳答案

快速测试表明范围是正确的。没有所有轮廓提取的东西,如果我只使用 0-10 和 165-179 范围过滤颜色,我会在输入图像的中低范围内得到两个红色框。

您看到的轮廓伪影实际上可能来自 JPEG 伪影(如果您放大白框和红框之间的界限,您可以看到它是渐变的而不是锐利的,由于 JPEG 压缩),以及您仅在 Hue channel 中进行阈值处理的事实。在低饱和度下,许多您不想要的灰色实际上会适合您的色相阈值。解决方案是同时过滤 S 和 V channel 中的像素值。

在您的代码中,这意味着更改行 if ((h <= 10 || h >= 165 && h <= 180) && ((s > 0) && (v > 0))) {if ((h <= 10 || h >= 165 && h <= 180) && ((s > 50) && (v > 50))) {

值 50 适用于该特定示例图像,但正确的值当然取决于您的输入图像。

关于c++ - 使用 HSV 范围逐像素过滤红色轮廓,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46969487/

相关文章:

c++ - gcc 链接的默认库?

c++ - 使用 OpenCV 的边界框

opencv - 比 cv::putText() 更好的向 ca cv::Mat 添加文本的方法?

成功清理和构建后 JavaCV 无法运行

c++ - OpenCV - 随机森林示例

c++ - 为什么这个c++程序会导致系统崩溃?

c++ - 为什么不同文件中的类在没有标题的情况下找不到彼此?

C++ 智能指针自己的实现

c++ - 带有 CV_SHAPE_ELLIPSE 的 OpenCV cv::getStructuringElement 产生一个黑色方 block

c++ - 如何通过一些图像处理在带有 Windows iot 的 Raspberry pi 上显示来自相机的视频流