用C++计算单通道直方图的平均值和标准差

6
我想要计算一个HSV图像的直方图的均值和标准差,但我只想对V通道进行直方图和计算。
我已经阅读了一些关于如何为一组通道完成这项工作的示例,并尝试了这些方法,但当我尝试执行程序时,我对是否正确创建仅使用一个通道的直方图感到困惑,因为程序总是崩溃。
以下是我目前的代码(变量test是cv :: Mat图像,可以是您想用来重现问题的任何图像)。 我可能错过了一些明显的东西,for循环可能在值范围方面不正确,但我以前没有使用过C ++进行此操作。
        cv::cvtColor(test, test, CV_BGR2HSV);


        int v_bins = 50;
        int histSize[] = { v_bins };
        cv::MatND hist;

        float v_ranges[] = { 0, 255};
        cv::vector<cv::Mat> channel(3);
        split(test, channel);

        const float* ranges[] = { v_ranges };
        int channels[] = {0};

        cv::calcHist(&channel[2], 1, channels, cv::Mat(), hist, 1, histSize, ranges, true, false); //histogram calculation

        float mean=0;

        float rows= hist.size().height;
        float cols = hist.size().width;

        for (int v = 0; v < v_bins; v++)
        {
            std::cout << hist.at<float>(v, v) << std::endl;;
            mean = mean + hist.at<float>(v);
        }

        mean = mean / (rows*cols);
        std::cout << mean<< std::endl;;

cv::vectorcv::Mat channel(3); ? - sturkmen
我认为这是必要的,因为HSV被分成了3个通道,而我只想要直方图的V通道。 - bardsleyta
我想知道是否有cv::vector?或者std::vector。 - sturkmen
@sturkmen 在 OpenCV 3.0 之前的版本中,cv::vector 是可以使用的。详情请参见此处 - Miki
2个回答

7
你可以简单使用cv::meanStdDev函数,该函数计算数组元素的平均值和标准差。
请注意,meanstddev参数均为cv::Scalar类型,因此您需要执行mean[0]stddev[0]以获得单通道数组hist的双精度值。
下面的代码将阐明它的用法:
#include <opencv2\opencv.hpp>
#include <iostream>

int main()
{
    cv::Mat test = cv::imread("path_to_image");

    cv::cvtColor(test, test, CV_BGR2HSV);

    int v_bins = 50;
    int histSize[] = { v_bins };
    cv::MatND hist;

    float v_ranges[] = { 0, 255 };
    cv::vector<cv::Mat> channel(3);
    split(test, channel);

    const float* ranges[] = { v_ranges };
    int channels[] = { 0 };

    cv::calcHist(&channel[2], 1, channels, cv::Mat(), hist, 1, histSize, ranges, true, false); //histogram calculation

    cv::Scalar mean, stddev;
    cv::meanStdDev(hist, mean, stddev);

    std::cout << "Mean: " << mean[0] << "   StdDev: " << stddev[0] << std::endl;

    return 0;
}

更新

您可以按照它们的定义计算平均值和标准差:

double dmean = 0.0;
double dstddev = 0.0;

// Mean standard algorithm
for (int i = 0; i < v_bins; ++i)
{
    dmean += hist.at<float>(i);
}
dmean /= v_bins;

// Standard deviation standard algorithm
std::vector<double> var(v_bins);
for (int i = 0; i < v_bins; ++i)
{
    var[i] = (dmean - hist.at<float>(i)) * (dmean - hist.at<float>(i));
}
for (int i = 0; i < v_bins; ++i)
{
    dstddev += var[i];
}
dstddev = sqrt(dstddev / v_bins);

std::cout << "Mean: " << dmean << "   StdDev: " << dstddev << std::endl;

您将得到与OpenCV meanStdDev相同的值。


好的。我得到了一个输出,但是对我来说,我得到的结果看起来相当奇怪。我得到了69271.2的平均值和86937.1的标准差。这对于直方图来说真实吗?还是出了什么问题? - bardsleyta
@bardsleyta 我更新了答案,并按照定义计算了均值和标准差。你会发现结果与OpenCV相同。如果你不希望得到这些结果,那么可能你就不应该在直方图上计算均值和标准差,而是在图像上进行计算。 - Miki
好的。我似乎误读了均值和标准差函数,试图在直方图上执行它,而不是在图像上执行。对于造成的混淆,我感到抱歉。 - bardsleyta
什么是v_bins? - Hossein
@Hossein,“v”通道的箱数。请参见第一个代码片段。 - Miki
@Miki:非常感谢。 - Hossein

1

在计算直方图的统计数据时要小心。如果你只运行meanStdDev,你将得到每个区间值的平均值和标准差,这并不能告诉你太多信息。

很可能你想要的是强度的平均值和标准差。

因此,如果你想从直方图(或一组直方图)中推导出图像的平均值和标准差,则可以使用以下代码:

// assume histogram is of type cv::Mat and comes from cv::calcHist

double s = 0;
double total_hist = 0;

for(int i=0; i < histogram.total(); ++i){
    s += histogram.at<float>(i) * (i + 0.5); // bin centre
    total_hist += histogram.at<float>(i);
}

double mean = s / total_hist;

double t = 0;
for(int i=0; i < histogram.total(); ++i){
    double x = (i - mean);
    t += histogram.at<float>(i)*x*x;
}
double stdev = std::sqrt(t / total_hist);

从平均值的定义中:

mean = sum(x * p(x)) // expectation
std = sqrt(sum( p(x)*(x - mean)**2 ) // sqrt(variance)

均值是x的期望值。因此,histogram[x]/sum(histogram)给出了p(x)。标准差的定义类似于方差variance。数字稍微简单一些,因为像素只能取整数值并且间距为单位间隔。

请注意,如果您想使用accumulate选项计算一批图像的归一化统计数据,则这也很有用。

改编自:如何从直方图计算标准偏差?(Python、Matplotlib)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接