首页 文章

将各个轮廓点分成HSV通道以执行其他操作

提问于
浏览
0

我目前正在尝试计算轮廓中点的平均HSV . 我做了一些研究并遇到了分割功能,它允许将图像的垫片分解成它的通道,但轮廓数据类型是点的矢量 . 这是一个代码示例 .

findcontours(detected_edges,contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);    
vector<vector<Point>> ContourHsvChannels(3);
split(contours,ContourHsvChannels);

基本上,目标是将轮廓的每个点分成HSV通道,这样我就可以对它们进行操作 . 任何指导将不胜感激 .

2 回答

  • 0

    您可以简单地将轮廓绘制到与原始图像大小相同的空白图像上以创建蒙版,然后使用它来遮罩图像(在HSV或您想要的任何颜色空间中) . mean()函数接收 mask 参数,以便您只获得掩码突出显示的值的平均值 .

    如果您还想要标准偏差,可以使用meanStdDev()函数,它也接受 mask .

    这是Python中的一个例子:

    import cv2
    import numpy as np
    
    # read image, ensure binary
    img = cv2.imread('fg.png', 0)
    img[img>0] = 255
    
    # find contours in the image
    contours = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)[1]
    
    # create an array of blank images to draw contours on
    n_contours = len(contours)
    contour_imgs = [np.zeros_like(img) for i in range(n_contours)]
    
    # draw each contour on a new image
    for i in range(n_contours):
        cv2.drawContours(contour_imgs[i], contours, i, 255)
    
    # color image of where the HSV values are coming from
    color_img = cv2.imread('image.png')
    hsv = cv2.cvtColor(color_img, cv2.COLOR_BGR2HSV)
    
    # find the means and standard deviations of the HSV values for each contour
    means = []
    stddevs = []
    for cnt_img in contour_imgs:
        mean, stddev = cv2.meanStdDev(hsv, mask=cnt_img)
        means.append(mean)
        stddevs.append(stddev)
    
    print('First mean:')
    print(means[0])
    print('First stddev:')
    print(stddevs[0])
    

    第一个意思是:[[146.3908046] [51.2183908] [202.95402299]]第一个stddev:[[7.92835204] [11.78682811] [9.61549043]]

    有三个值;每个 Channels 一个 .


    另一种选择是查找所有值;轮廓是一个点数组,因此您可以使用轮廓数组中每个轮廓的那些点索引图像,并将它们存储在各个数组中,然后在这些点上找到 meanStdDev()mean() (而不用掩码) . 对于例如(再次在Python中,抱歉):

    # color image of where the HSV values are coming from
    color_img = cv2.imread('image.png')
    hsv = cv2.cvtColor(color_img, cv2.COLOR_BGR2HSV)
    
    # read image, ensure binary
    img = cv2.imread('fg.png', 0)
    img[img>0] = 255
    
    # find contours in the image
    contours = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)[1]
    
    means = []
    stddevs = []
    for contour in contours:
        contour_colors = []
        n_points = len(contour)
        for point in contour:
            x, y = point[0]
            contour_colors.append(hsv[y, x])
        contour_colors = np.array(contour_colors).reshape(1, n_points, 3)
        mean, stddev = cv2.meanStdDev(contour_colors)
        means.append(mean)
        stddevs.append(stddev)
    
    print('First mean:')
    print(means[0])
    print('First stddev:')
    print(stddevs[0])
    

    第一个意思是:[[146.3908046] [51.2183908] [202.95402299]]第一个stddev:[[7.92835204] [11.78682811] [9.61549043]]

    所以这给出了相同的值 . 在Python中,我只是简单地为平均值和标准偏差创建空白列表并附加到它们 . 在C中,您可以为每个创建一个 std::vector<cv::Vec3b> (假设为 uint8 图像,否则为 Vec3f 或其他任何适当的图像) . 然后在循环内部我创建另一个空白列表来保存每个轮廓的颜色;再次这将是一个 std::vector<cv::Vec3b> ,然后在每个循环中对该向量运行 meanStdDev() ,并将该值附加到均值和标准偏差向量 . 您不必追加,您可以轻松地获取轮廓的数量和每个轮廓中的点数并预先分配速度,然后只需索引到这些向量而不是追加 .

    在Python中,第二个例子中的内存效率更高;而不是存储整个空白 Mat 我们只存储一些值 . 然而,后端OpenCV方法对于屏蔽操作非常快速,因此您必须自己在C中测试速度差异,看看哪种方式更好 . 随着轮廓数量的增加,我想象第二种方法的好处会增加 . 如果您同时采取两种方法,请告诉我们您的结果!

  • 2

    这是用c写的解决方案

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

相关问题