MATLAB和C++中使用OpenCV的像素值有何不同?

3

我看到有类似的问题,但并不能完全回答我的问题,所以这是我的问题。

在C++中使用OpenCV运行我提供的代码,它返回一个平均像素值为6.32。然而,当我打开图像并在MATLAB中使用mean函数时,它返回大约6.92左右的平均像素强度。正如您所见,我将OpenCV值转换为double以尝试缓解此问题,并发现OpenCV将图像加载为一组整数,而MATLAB将图像加载为十进制值,这些值大致相同,但显然不完全相同于整数。所以我的问题是,作为编码新手,哪个是正确的?我假设MATLAB返回更准确的值,如果是这样,我想知道是否有一种方法以相同的方式加载图像以避免差异。

谢谢,以下是代码

    Mat img = imread("Cells2.tif");
cv::cvtColor(img, img, CV_BGR2GRAY);
cv::imshow("stuff",img);
Mat dst;
if(img.channels() == 3)
{
    img.convertTo(dst, CV_64FC1);
}
else if (img.channels() == 1) 
{
    img.convertTo(dst, CV_64FC1);
}
cv::imshow("output",dst/255);
int NumPixels = img.total();


double avg;
double c = 0; 
double std;
    for(int y = 0; y < dst.cols; y++)
    { 

        for(int x = 0; x < dst.rows; x++)
        {
            c+=dst.at<double>(x,y)*255;
        }
    }

avg = c/NumPixels;
cout << "asfa = " << c << endl;
double deviation;

double var;
double z = 0;
double q;
    //for(int a = 0; a<= img.cols; a++)
for(int y = 0; y< dst.cols; y++)
    {
        //for(int b = 0; b<= dst.rows; b++)
        for(int x = 0; x< dst.rows; x++)
        {
            q=dst.at<double>(x,y);

            deviation = q - avg;
            z = z + pow(deviation,2);
            //cout << "q = " << q << endl;
        }

    }

var = z/(NumPixels);
std = sqrt(var);
cv::Scalar avgPixel = cv::mean(dst);

cout << "Avg Value = " << avg << endl;
cout << "StdDev = " << std << endl;
cout << "AvgPixel =" << avgPixel;

cvWaitKey(0);
return 0;

}


MATLAB 可能会通过假设最高值为 100% 的亮度来检测像素值的范围。如果最亮的值约为 233,这就可以解释差异。 - Ben Voigt
你能展示一下计算图像平均像素强度的Matlab代码吗? - Alexey
我只是使用 A = theimagefile.tif,然后使用预构建的 mean(A(:))。 - VeniVici
谢谢,您的测试图片是彩色还是灰度? - Alexey
@VeniVici:请看下面我的回答。 - Amro
显示剩余5条评论
4个回答

5
根据您的评论,图像似乎以16位深度存储。MATLAB按原样加载TIFF图像,而默认情况下 OpenCV 将图像加载为8位。这可能解释了您所看到的精度差异。
使用以下内容在OpenCV中打开图像:
cv::Mat img = cv::imread("file.tif", cv::IMREAD_ANYDEPTH|cv::IMREAD_ANYCOLOR);

在MATLAB中,它很简单:
img = imread('file.tif');

你需要意识到你正在使用的数据类型。在OpenCV中是CV_16U,在MATLAB中是uint16。因此,你需要相应地转换类型。
例如,在MATLAB中:
img2 = double(img) ./ double(intmax('uint16'));

将其转换为值范围为[0,1]的double图像

你可以在OpenCV中使用类似的转换:img.convertTo(img2, CV_64F, 1.0/65535); - Amro
非常感谢你,真的太棒了,这个解决方法非常好,确实是转换的问题。这个问题一直困扰着我,非常感谢你。 - VeniVici

0
  1. 如果满足某些条件,您正在转换图像,这可能会改变一些颜色值,而MATLAB可以选择不转换图像,而是使用原始图像。
  2. 颜色大多以十六进制格式表示,流行的实现方式为0xAARRGGBB或0xRRGGBBAA格式,因此32位整数就足够了(无符号/有符号无所谓,十六进制值仍然相同),创建一个64位变量,将所有32位变量相加,然后除以像素数量,这将得到一个相当准确的结果(对于16384 x 16384像素以下的图像(其中32位值表示一个像素的颜色),如果更大,则64位整数将不足)。

    long long total = 0;
    long long divisor = image.width * image.height;
    for(int x = 0; x < image.width; ++x)
    {
        for(int y = 0; x < image.height; ++x)
        {
            total += image.at(x,y).color;
        }
    }
    double avg = total / divisor;
    std::cout << "Average color value: " << avg << std::endl;
    

不行。你需要单独处理颜色平面,但在这种方法中它们会相互干扰(加法时两个平面都会被带上,除法时分数部分也会进入其他颜色平面并破坏结果)。 - Ben Voigt
是的,我试图编辑它以使其运行,但结果很奇怪。无论如何,我感谢你的帮助,但它没有用 :(。 - VeniVici
也许如果你告诉我结果是什么或者哪里出了问题,我可以编辑并修复它? :) 我在这里帮忙 ^.^ - user1182183
代码无法编译,我尽力修复了一些问题,但是 dst(将图像转换为灰度浮点数)显示需要一个类类型,我不确定如何修复它。long long total = 0;long long divisor = dst.cols * dst.rows;for(int x = 0; x < dst.cols; ++x){for(int y = 0; x < dst.rows; ++x) { total += dst.at(x,y).color; }}double avg = total / divisor;std::cout << "平均颜色值:" << avg << std::endl; - VeniVici
不确定如何在注释中更好地格式化这个,还是对stack不太熟悉 :(。 - VeniVici

0

不确定您在Matlab与OpenCV中使用平均值时遇到了什么困难。如果我正确理解了您的问题,您的目标是在OpenCV中实现Matlab的mean(image(:))方法。例如,在Matlab中,您可以执行以下操作:

>> image = imread('sheep.jpg')
>> avg = mean(image(:))

ans =

   119.8210

以下是如何在OpenCV中完成相同操作:

Mat image = imread("sheep.jpg");
Scalar avg_pixel;
avg_pixel = mean(image);
float avg = 0;
cout << "mean pixel (RGB): " << avg_pixel << endl;

for(int i; i<image.channels(); ++i) {
    avg = avg + avg_pixel[i]; 
}
avg = avg/image.channels();
cout << "mean, that's equivalent to mean(image(:)) in Matlab: " << avg << endl;

OpenCV 控制台输出:

mean pixel (RGB): [77.4377, 154.43, 127.596, 0]
mean, that's equivalent to mean(image(:)) in Matlab: 119.821

因此,在Matlab和OpenCV中的结果是相同的。

跟进 发现您的代码存在一些问题。

  • OpenCV与Matlab存储数据的方式不同。查看this answer以了解如何在OpenCV中访问像素的大致说明。例如:

    // 访问CV_F32C3类型图像像素的错误方法
    double pixel = image.at<double>(x,y); 
    
    // 正确的方法(像素值存储在向量中)
    // 注意,Vec3d被定义为:typedef Vec<double, 3> Vec3d;
    Vec3d pixel = image.at<Vec3d>(x, y);
    
  • 我发现另一个错误

    if(img.channels() == 3)
    {
        img.convertTo(dst, CV_64FC1); //应该是CV_64FC3,而不是CV_64FC1
    }
    

访问Mat元素可能会令人困惑。我建议先获取一本关于OpenCV的书籍,例如这本书,并阅读OpenCV教程和文档。希望这能有所帮助。


谢谢你找出那些愚蠢的错误。然而,这张图片是灰度的,这很好,你的代码将只会给出向量6.321,乘以255等于1612(这也是我的非常糟糕的平均循环找到的结果)。然而,在MATLAB中,这张相同的图片却给出了1776的平均值。我看到你的图片也给出了相同的平均值。为什么我的结果会返回如此不同的值呢?如果可以的话,这是一张灰度.tif图片。 - VeniVici
我刚使用lena.bmp在Matlab和OpenCV中进行了灰度处理,并且在OpenCV中,我的手写代码和cv::mean(img)返回了一个值为95.6564,而在MATLAB中返回了96.5...我也不知道为什么哈哈。 - VeniVici
所以,您首先将图像转换为灰度,然后计算其平均值? - Alexey
tif已经是灰度图像了,对于Lena,我将其保留为3通道,并按照您上面的代码计算平均值。这些是我发布的值。MATLAB返回的值比OpenCV中计算的值要高。我所做的唯一MATLAB操作就是加载图像并通过mean(A(:))取平均值。 - VeniVici
只需使用我在答案中提供的代码,并使用cv :: cvtColor(image,image,CV_BGR2GRAY);将测试图像转换为灰度图像,然后计算平均值。 在Matlab中对彩色图像lena.bmp进行测试时,请勿忘记使用image = rgb2gray(image)将图像转换为灰度图像,然后执行mean(image(:))。 我在Matlab和OpenCV中得到了相同的结果。 - Alexey

0

当您加载图像时,必须在两个环境(MATLAB和OpenCV)中使用类似的方法,以避免可能由任一环境默认执行的转换。


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