【问题标题】:openCV: different results when matrix-multiplication with overloaded operator and withoutopenCV:使用重载运算符和不使用矩阵乘法时的不同结果
【发布时间】:2021-08-18 20:19:18
【问题描述】:

我在 openCV 中有一个这样的矩阵:cv::Mat matrix = cv::Mat::zeros(5, 5, CV_32FC3);

然后,我想以两种不同的方式进行矩阵乘法,第一种是通道方式,第二种是重载运算符。

// VERSION 1:

for (int row_count = 0; row_count < image.rows; row_count++)
{
  for (int col_count = 0; col_count < image.cols; col_count++)
  {
    for (int channel_count = 0; channel_count < 3; channel_count++)
    {
      matrix.at<cv::Vec3b>(row_count, col_count)[channel_count] = some_image.at<cv::Vec3b>(row_count, col_count)[channel_count] * other_image.at<float>(row_count, col_count);
    }
  } 
}

// VERSION 2:

for (int row_count = 0; row_count < image.rows; row_count++)
{
  for (int col_count = 0; col_count < image.cols; col_count++)
  {
    matrix.at<cv::Vec3b>(row_count, col_count) = some_image.at<cv::Vec3b>(row_count, col_count) * other_image.at<float>(row_count, col_count);
  } 
}

为什么我在这里得到不同的结果?

编辑:(这里是一个例子 thx@Frank)

int main() {
  cv::Vec3b data(127, 81, 24);
  float ratio = 0.25f;

  cv::Vec3b manual;
  manual[0] = data[0] * ratio;
  manual[1] = data[1] * ratio;
  manual[2] = data[2] * ratio;

  cv::Vec3b overloaded = data * ratio;

  std::cout << manual << " vs " << overloaded << "\n";
}

输出:

[31, 20, 6] vs [32, 20, 6]

【问题讨论】:

  • 我可以建议一个更简单的 mcve:gcc.godbolt.org/z/c1P5GqPGG。输出为[31, 20, 6] vs [32, 20, 6] 。更改太大了,让我觉得仅仅为它编辑问题就很舒服,但我强烈建议您进行更改。
  • 您必须将其寻址为other_image.at&lt;Vec3f&gt; 而不是other_image.at&lt;float&gt;,因为您的图像是CV_32FC3。目前,您没有从浮点矩阵中处理​​正确的像素位置,因为您正在处理它,就好像它是单通道一样,但它有 3 个通道。
  • @Micka 很明显other_image 是单通道图像。
  • 好吧抱歉,那么您的地址matrix.at&lt;cv::Vec3b&gt; 错误,因为根据cv::Mat matrix = cv::Mat::zeros(5, 5, CV_32FC3); 它是CV_32FC3 但您将其地址为Vec3b 而不是Vec3f
  • @Micka 你说得对,OP 可能想要这样做,但由于他们在两个版本中都这样做,这并不能解释他们所经历的不一致。

标签: c++ opencv matrix operator-overloading


【解决方案1】:

当涉及整数类型时,OpenCV 几乎在任何地方都使用cv::saturate_cast&lt;&gt;。您可能认为这只会改变溢出和下溢的行为方式,但它也会影响浮点值的强制转换方式。

如果要二进制兼容opencv内部代码,手动做涉及整数标量的操作时需要撒上一堆cv::saturate_cast&lt;&gt;

int main() {
  cv::Vec3b data(127, 81, 24);
  float ratio = 0.25f;

  cv::Vec3b manual;
  manual[0] = cv::saturate_cast<uint8_t>(data[0] * ratio);
  manual[1] = cv::saturate_cast<uint8_t>(data[1] * ratio);
  manual[2] = cv::saturate_cast<uint8_t>(data[2] * ratio);

  cv::Vec3b overloaded = data * ratio;

  std::cout << manual << " vs " << overloaded << "\n";
}

这会按预期生成:[32, 20, 6] vs [32, 20, 6]

详情:,根据文档: https://docs.opencv.org/4.5.2/db/de0/group__core__utils.html#gab93126370b85fda2c8bfaf8c811faeaf

它执行从一种原始类型到另一种的高效且准确的转换。

saturate_cast 是一个有点误导性的名称,因为它不仅使值饱和,而且还努力使演员表尽可能准确。把它想象成quantize_cast&lt;&gt;

原始的 C++ 行为是在将浮点值转换为整数时向零舍入,这是您在计算事物时想要的。另一方面,cv::saturate_cast&lt;&gt; 将它们四舍五入为 最近的整数。在处理量化时,这在数值上更准确,这也是 OpenCV 在大多数情况下使用整数的方式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-09
    • 1970-01-01
    • 2016-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多