openCV:使用重载运算符和不使用重载运算符进行矩阵乘法时的结果不同

openCV: different results when matrix-multiplication with overloaded operator and without

我在 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]

当涉及整数类型时,OpenCV 几乎无处不在地使用 cv::saturate_cast<>。您 认为 这应该只会改变上溢和下溢的行为方式,但它也会影响浮点值的强制转换方式。

如果想二进制兼容opencv内部代码,手动做涉及整数标量的运算时需要撒一堆cv::saturate_cast<>

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

It perform an efficient and accurate conversion from one primitive type to another.

saturate_cast 这个名字有点误导,因为它不仅使值饱和,而且还努力使转换尽可能准确。把它想象成 quantize_cast<> 而不是。

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