【问题标题】:How to get pixel value from the contoured image with mask?如何从带有蒙版的轮廓图像中获取像素值?
【发布时间】:2019-02-26 08:50:22
【问题描述】:

我尝试从轮廓图像中提取平均像素值(R、G、B)。但是,我的问题是当我应用下面的代码时,观察到了一些奇怪的值。

int main(){
cv::Mat star = imread("C:\\Users\\PC\\Desktop\\star\\starcircle.png");
cv::Mat mask = cv::Mat::zeros(star.rows, star.cols, CV_8UC1);
cv::Mat frame;
double b, g, r = 0.0;

cv::imshow("Original", star);

cv::cvtColor(star, frame, CV_BGR2HSV);

cv::inRange(frame, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);

cv::imshow("mask", mask);

cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
result.setTo(cv::Scalar(0, 0, 0));

star.copyTo(result, mask);  

cv::Scalar temp = mean(mask);

cout << "avg_R: " << temp[2] << " \n"; // red value
cout << "avg_G: " << temp[1] << " \n"; // green value
cout << "avg_B: " << temp[0] << " \n\n"; // blue value 

cv::imshow("result", result);
cv::waitKey(-1);
return 0;

}

我得到了如下结果的正确图像。

我只想读取黄色部分的像素值,而不是蒙版之外的像素值。

我还有另一个代码用于读取黄色部分中的像素值,但它显示了相同的结果。

int main(){
cv::Mat star = imread("C:\\Users\\PC\\Desktop\\star\\starcircle.png");
cv::Mat mask = cv::Mat::zeros(star.rows, star.cols, CV_8UC1);
cv::Mat frame;
double b, g, r = 0.0;

cv::imshow("Original", star);

cv::cvtColor(star, frame, CV_BGR2HSV);

cv::inRange(frame, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);

cv::imshow("mask", mask);

cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
result.setTo(cv::Scalar(0, 0, 0));

star.copyTo(result, mask);  

int hei = star.rows;
int wid = star.cols;

int corow = hei * wid;

double b, g, r = 0.0;

for (int x = 0; x < hei; x++) {
    for (int y = 0; y < wid; y++) {
        if (mask.at<unsigned char>(x, y) > 0) {
            b += result.at<Vec3b>(x, y)[0];
            g += result.at<Vec3b>(x, y)[1];
            r += result.at<Vec3b>(x, y)[2];

        }
        else {

        }


    }
}

cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
cout << "avg_R: " << r / corow << " \n"; // red value
cout << "avg_G: " << g / corow << " \n"; // green value
cout << "avg_B: " << b / corow << " \n\n"; // blue value

}

请帮我修改错误。

提前谢谢你。

【问题讨论】:

    标签: c++ opencv


    【解决方案1】:

    一些事情:

    • 您的变量名称和Mat 类型至少令人困惑。为变量使用正确的名称,并尽可能使用Mat_&lt;T&gt;(我会说总是)。
    • 要获得平均值,您应该除以掩码中的像素数,而不是总像素数。
    • 你应该考虑使用cv::mean
    • 您需要cv::waitKey() 才能真正看到您的cv::imshow

    检查代码:

    #include <opencv2\opencv.hpp>
    
    int main()
    {
        cv::Mat3b star = cv::imread("path/to/image");   
        cv::imshow("Original", star);
    
        cv::Mat3b hsv;
        cv::cvtColor(star, hsv, cv::COLOR_BGR2HSV);
    
        cv::Mat1b mask;
        cv::inRange(hsv, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);
        cv::imshow("mask", mask);
    
        // Change to 'false' to see how to use the 'cv::mask' approach
        if (true)
        {
            double blue, green, red = 0.0;
            int counter = 0;
            for (int r = 0; r < star.rows; r++)
            {
                for (int c = 0; c < star.cols; c++)
                {
                    if (mask(r, c) > 0)
                    {
                        ++counter;
                        blue += star(r, c)[0];
                        green += star(r, c)[1];
                        red += star(r, c)[2];
                    }
                }
            }
    
            // Avoid division by 0
            if (counter > 0)
            {
                blue /= counter;
                green /= counter;
                red /= counter;
            }
    
            std::cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
            std::cout << "avg_R: " << red << " \n"; 
            std::cout << "avg_G: " << green << " \n"; 
            std::cout << "avg_B: " << blue << " \n\n"; 
        }
        else
        {
            cv::Scalar mean_value = cv::mean(star, mask);
            double blue = mean_value[0];
            double green = mean_value[1];
            double red = mean_value[2];
    
            std::cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
            std::cout << "avg_R: " << red << " \n"; // red value
            std::cout << "avg_G: " << green << " \n"; // green value
            std::cout << "avg_B: " << blue << " \n\n"; // blue value
        }
        cv::waitKey();
    }
    

    【讨论】:

    • cv::waitKey() OP 知道这一点,因为 OP 发布了屏幕截图。
    • @BahramdunAdil 谢谢,但是 1)这不是答案的重点,2)在他的一个 sn-ps OP 中没有使用它。再重复一次也没坏处;)
    • 你能解释一下if (true)是什么,在这种情况下else子句永远不会被执行吗?
    • 所以最好在一行上留下评论,以便读者清楚,否则会造成混乱。谢谢!
    • @my bad... 红色的r 和遍历行的r 之间存在冲突... 现在应该没问题了
    【解决方案2】:

    我在您的代码中发现了几个错误:

    cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
    result.setTo(cv::Scalar(0, 0, 0));
    star.copyTo(result, mask);  
    cv::Scalar temp = mean(mask);
    

    如果结果是 CV_8UC1 类型,那么你复制到一个通道? (CV_8U 中的 C1 表示一个通道)。然后你使用star.type(),其中要设置的值应该是......你也做了一个掩码的平均值,这会给你一个只有一个通道集的标量,因为它是CV_8UC1类型的二进制图像......为了让它工作,它应该是:

    cv::Mat result = cv::Mat(star.rows, star.cols, star.type(), cv::Scalar::all(0));
    star.copyTo(result, mask);  
    cv::Scalar temp = mean(result);
    

    对于第二部分,可以像这样添加它,但是如果您还没有修复之前的错误...我认为如果幸运的话,它应该会在某些时候给您分段错误或奇怪的结果。最后的结果部分是这样的:

    cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
    cout << "avg_R: " << r / corow << " \n"; // red value
    cout << "avg_G: " << g / corow << " \n"; // green value
    cout << "avg_B: " << b / corow << " \n\n"; // blue value
    

    但是 corow 应该是掩码的非零点,所以应该是:

    corow = cv::countNonZero(mask);
    cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
    cout << "avg_R: " << r / corow << " \n"; // red value
    cout << "avg_G: " << g / corow << " \n"; // green value
    cout << "avg_B: " << b / corow << " \n\n"; // blue value
    

    如果不是,它会给你一个较小的数字,因为它被一个数字除以一个数字,其中包括没有贡献的黑点。

    作为一个额外的说明,你应该使用更多的 OpenCV 函数......在这种情况下 cv::mean 做同样的事情,如果不是你可以用 sum 和除法简化它:

     cv::Scalar summed = cv::sum(result); 
     cv::Scalar mean = summed / static_cast<double>(cv::countNonZero(mask));
     std::cout << "$$ Red(R), Green(G), Blue(B) $$" << std::endl << std::endl;
     std::cout << "avg_R: " << mean[2] << std::endl; // red value
     std::cout << "avg_G: " << mean[1] << std::endl; // green value
     std::cout << "avg_B: " << mean[0] << std::endl << std::endl; // blue value
    

    这是假设您执行了star.copyTo(result, mask);

    【讨论】:

    • OMG 这正是我想要的。我真的很感谢你的回答。非常感谢!!
    【解决方案3】:
    1. 了解cv::Mat::at:第一行,第二列。不是 (x, y)!

    2. cv::mean:它可以与面具一起使用。

    3. 右初始化:double b = 0.0, g = 0.0, r = 0.0;

    4. int corow = 0;如果掩码> 0,则在循环内部++corow。

    【讨论】:

    • 好的。但它需要重命名 x -> y 和 y -> x。变量名称必须从上下文中清楚。
    猜你喜欢
    • 2013-06-03
    • 2021-01-27
    • 1970-01-01
    • 1970-01-01
    • 2019-11-29
    • 1970-01-01
    • 2019-08-15
    • 2013-02-06
    • 2020-03-29
    相关资源
    最近更新 更多