将各个轮廓点分成它的HSV通道以执行附加操作

时间:2017-09-28 01:10:41

标签: c++ opencv

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

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

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

2 个答案:

答案 0 :(得分:2)

您可以简单地将轮廓绘制到与原始图像大小相同的空白图像上以创建蒙版,然后使用它来遮罩图像(在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]]

     

First stddev:
  [[7.92835204]
   [11.78682811]
   [9.61549043]]

有三个值;每个频道一个。

另一种选择是查找所有值;轮廓是一个点数组,因此您可以使用轮廓数组中每个轮廓的那些点索引图像,并将它们存储在单个数组中,然后在这些数据上找到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]]

     

First stddev:
  [[7.92835204]
   [11.78682811]
   [9.61549043]]

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

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

答案 1 :(得分:0)

这是用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;
}