OpencvSharp 算子学习教案之 - Cv2.Multiply

OpencvSharp 算子学习教案之 - Cv2.Multiply

大家好,Opencv在很多工程项目中都会用到,而OpencvSharp则是以C#开发与实现的Opencv操作库,对.NET开发人员友好,但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳,因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案,供大家参考学习。

Cv2.Multiply

  • 教案版本:V1.0
  • 面向对象:OpenCvSharp 初学者
  • 所属模块:core
  • 源码位置:OpenCvSharp/Cv2/Cv2_core.cs:185

1. 函数名称(带参数签名)

csharp 复制代码
public static void Multiply(
    InputArray src1,
    InputArray src2,
    OutputArray dst,
    double scale = 1,
    int dtype = -1)

2. 函数用途

Cv2.Multiply 用来做逐元素乘法。它不是矩阵乘法,而是把两个输入矩阵中对应位置的元素一一相乘,再乘上 scale 系数。

这个函数常见于:

  1. 像素强度放大或缩小。
  2. 归一化前后的数值变换。
  3. 混合深度输入时,把结果提升到更安全的输出类型。
  4. 观察 CV_32S 输出时"不会像 8U 那样饱和裁剪"的特殊行为。

3. 函数公式

对每个元素 III,OpenCV 的基本行为可以写成:

dst(I)=saturate⁡(scale⋅src1(I)⋅src2(I)) dst(I) = \operatorname{saturate}(scale \cdot src1(I) \cdot src2(I)) dst(I)=saturate(scale⋅src1(I)⋅src2(I))

其中 saturate 表示按输出深度做类型转换与裁剪。

3.1 输出深度的提示

  1. 当两个输入深度相同,而且你不想改变输出类型时,dtype 可以保持默认的 -1
  2. 当输入深度不同,或者你希望把结果直接放到更安全的类型里时,应该显式指定 dtype
  3. CV_32S 是一个特殊输出类型,OpenCV 的实现和测试都把它当作"不会做常规饱和裁剪"的分支来处理。

4. 函数原理说明

Multiply 的本质是逐元素计算,不是线性代数里的矩阵乘法。

对初学者来说,最重要的理解点有四个:

  1. src1src2 是位置一一对应地相乘。
  2. scale 是额外的比例系数,会同时影响所有元素。
  3. dtype 决定最终结果放进什么类型里。
  4. 如果你输出到 CV_8U,大于 255 的结果会被压到 255;如果输出到 CV_32S,这一点通常不会发生。

OpenCV 内部对这个函数会根据输入深度和 dtype 选择不同的处理路径,因此它既能处理普通同深度输入,也能在你明确指定输出类型后处理混合深度数据。

5. 参数含义解析

参数名 类型 必填 含义
src1 InputArray 第一个输入数组
src2 InputArray 第二个输入数组
dst OutputArray 输出结果
scale double 额外缩放系数
dtype int 输出深度,默认 -1 表示尽量沿用输入深度

6. 应用场景列表

场景名 场景说明 典型用途
场景A:8U 饱和乘法 两个 8 位矩阵相乘,观察结果如何被压到 255 像素叠乘、亮度增强
场景B:scale 缩放因子 scale = 0.5 观察乘积如何整体缩小 归一化、强度缩放
场景C:混合深度与 dtype 提升 8U 和 16U 输入显式提升到 CV_32F 混合精度处理
场景D:CV_32S 特殊行为 用 32 位有符号输出保留更大的整数乘积 大数值分析、特殊类型验证

7. 函数使用示例(与 WPF 场景一一对应)

说明:下面四段代码与 WPF 控件中的四个场景一一对应。示例尽量保持初学者友好,重点展示 scaledtype 和输出深度的影响。

7.1 场景A:8U 饱和乘法

csharp 复制代码
using OpenCvSharp;

// 两张 8 位无符号矩阵,元素相乘后很容易超过 255。
var src1Data = new byte[,]
{
    { 20, 15 },
    { 12, 30 },
};

var src2Data = new byte[,]
{
    { 20, 18 },
    { 25, 12 },
};

using var src1 = Mat.FromPixelData(2, 2, MatType.CV_8UC1, src1Data);
using var src2 = Mat.FromPixelData(2, 2, MatType.CV_8UC1, src2Data);
using var dst = new Mat();

// 20 * 20 = 400,12 * 25 = 300,这些值都会被压到 255。
Cv2.Multiply(src1, src2, dst, dtype: MatType.CV_8U);

7.2 场景B:scale 缩放因子

csharp 复制代码
using OpenCvSharp;

// 这里使用会产生半整数结果的数据,方便看出 scale 的作用。
var src1Data = new byte[,]
{
    { 3, 5 },
    { 7, 9 },
};

var src2Data = new byte[,]
{
    { 5, 7 },
    { 11, 13 },
};

using var src1 = Mat.FromPixelData(2, 2, MatType.CV_8UC1, src1Data);
using var src2 = Mat.FromPixelData(2, 2, MatType.CV_8UC1, src2Data);
using var dst = new Mat();

// 先做逐元素乘法,再整体乘以 0.5。
Cv2.Multiply(src1, src2, dst, scale: 0.5, dtype: MatType.CV_32F);

// 3 * 5 * 0.5 = 7.5
// 5 * 7 * 0.5 = 17.5

7.3 场景C:混合深度与 dtype 提升

csharp 复制代码
using OpenCvSharp;

// 一个 8 位无符号矩阵和一个 16 位无符号矩阵。
var src1Data = new byte[,]
{
    { 7, 9 },
    { 11, 13 },
};

var src2Data = new ushort[,]
{
    { 100, 200 },
    { 300, 400 },
};

using var src1 = Mat.FromPixelData(2, 2, MatType.CV_8UC1, src1Data);
using var src2 = Mat.FromPixelData(2, 2, MatType.CV_16UC1, src2Data);
using var dst = new Mat();

// 混合深度时,显式把输出提升到 CV_32F 更稳妥。
Cv2.Multiply(src1, src2, dst, scale: 0.25, dtype: MatType.CV_32F);

// 7 * 100 * 0.25 = 175.0
// 13 * 400 * 0.25 = 1300.0

7.4 场景D:CV_32S 特殊行为

csharp 复制代码
using OpenCvSharp;

// 32 位有符号整数输入,用来演示更大的乘积不会像 8U 那样被压到 255。
var src1Data = new int[,]
{
    { 30000, 40000 },
    { 50000, 60000 },
};

var src2Data = new int[,]
{
    { 2, 3 },
    { 4, 5 },
};

using var src1 = Mat.FromPixelData(2, 2, MatType.CV_32SC1, src1Data);
using var src2 = Mat.FromPixelData(2, 2, MatType.CV_32SC1, src2Data);
using var dst = new Mat();

// 输出仍然保持 32S,这样 60000、120000 这样的值可以完整保留。
Cv2.Multiply(src1, src2, dst, dtype: MatType.CV_32S);

// 30000 * 2 = 60000
// 60000 * 5 = 300000

8. 函数使用注意事项

  1. Multiply 是逐元素乘法,不是矩阵乘法。
  2. scale 会参与最终结果,做归一化或缩放时非常有用。
  3. src1src2 必须同尺寸、同通道数。
  4. 当输入深度不同的时候,请显式指定 dtype,不要只依赖默认值。
  5. 如果输出是 CV_8U,大结果会被饱和压到 255;如果输出是 CV_32S,常规饱和裁剪通常不会发生。

9. 参数调优建议

  1. 想观察饱和行为时,优先使用 CV_8U 输出。
  2. 想看真实的缩放结果时,优先使用 CV_32F 输出。
  3. 混合深度演示时,把 dtype 显式写成 CV_32F,这样最容易看懂。
  4. 如果结果里出现大量 255,不一定是乘法错了,更多时候是输出类型太窄。
  5. 如果你要验证 CV_32S 的特殊行为,建议使用仍在 int32 范围内但明显大于 255 的数值。

10. 示例代码运行说明(按场景关键逻辑)

场景A(8U 饱和乘法)

  1. 准备两个 CV_8UC1 矩阵。
  2. 调用 Cv2.Multiply(src1, src2, dst, dtype: MatType.CV_8U)
  3. 观察乘积是否被压到 255。

场景B(scale 缩放因子)

  1. 准备两个 CV_8UC1 矩阵。
  2. scale 设为 0.5
  3. 输出用 CV_32F,便于观察半整数结果。

场景C(混合深度与 dtype 提升)

  1. 准备一个 CV_8UC1 输入和一个 CV_16UC1 输入。
  2. 显式把输出设为 CV_32F
  3. 观察混合深度数据如何被安全地提升到浮点结果。

场景D(CV_32S 特殊行为)

  1. 准备两个 CV_32SC1 输入。
  2. 输出也设为 CV_32S
  3. 观察大于 255 的整数乘积能否被完整保留。

11. 常见错误排查

  1. 错误:把 Multiply 当成矩阵乘法
    • 排查:Multiply 是逐元素乘法,不是 GEMM
  2. 错误:忘记 scale 会直接影响输出
    • 排查:检查调用里是否写了 scale,默认值是 1
  3. 错误:输出仍然是 CV_8U,结果被压成 255
    • 排查:把 dtype 改成 CV_32FCV_32S 再看。
  4. 错误:混合深度时不指定 dtype
    • 排查:显式指定输出深度,避免 OpenCV 选择不符合预期的类型。
  5. 错误:以为 CV_32S 也会像 CV_8U 一样饱和裁剪
    • 排查:CV_32S 是特殊路径,应该单独验证。

对应 WPF 演示控件与样例代码:

  • Features/Cv2Multiply/Cv2MultiplyControl.xaml
  • Samples/Cv2Multiply/MultiplySaturationSample.cs
  • Samples/Cv2Multiply/MultiplyScaleSample.cs
  • Samples/Cv2Multiply/MultiplyMixedDepthPromotionSample.cs
  • Samples/Cv2Multiply/MultiplyInt32SpecialSample.cs
相关推荐
CompaqCV2 小时前
OpencvSharp 算子学习教案之 - Cv2.Subtract 重载2
学习·c#·opencvsharp算子·opencv教程
小陈phd2 小时前
多模态大模型学习笔记(三十五)——OCR全景认知:从字符识别到多模态理解的百年演进
笔记·学习·ocr
小饕2 小时前
RAG学习之 - 检索质量评估指标详解:从概念到代码实战
开发语言·人工智能·python·学习
TTGGGFF2 小时前
SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习
学习·英语学习·生词本
回忆2012初秋2 小时前
C# 射线算法:判断GPS点是否在车辆工作区域内
linux·算法·c#
_李小白2 小时前
【OSG学习笔记】Day 52: FadeText
笔记·学习
The Shio2 小时前
上位机对接设备协议踩坑指南
网络·单片机·嵌入式硬件·物联网·c#·.net
CompaqCV3 小时前
OpencvSharp 算子学习教案之 - Cv2.Add
学习·c#·opencvsharp算子
CompaqCV3 小时前
OpencvSharp 算子学习教案之 - Cv2.Subtract 重载3
学习·c#·opencvsharp算子·opencv教程