发布时间:2026/6/20 18:27:58
OpencvSharp 算子学习教案之 - Cv2.MulSpectrums
MulSpectrums大家好Opencv在很多工程项目中都会用到而OpencvSharp则是以C#开发与实现的Opencv操作库对.NET开发人员友好但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案供大家参考学习。Cv2.MulSpectrums教案版本V1.0面向对象OpenCvSharp 初学者所属模块core源码位置OpenCvSharp/Cv2/Cv2_core.cs:2988摘要MulSpectrums 会对两个频谱做逐元素乘法并可选择是否对第二个频谱取共轭。本文用两组很小的复数频谱分别演示普通乘法和共轭乘法帮助初学者理解卷积、相关和频域乘法的关系。1. 函数名称带参数签名publicstaticvoidMulSpectrums(InputArraya,InputArrayb,OutputArrayc,DftFlagsflags,boolconjBfalse)2. 函数用途Cv2.MulSpectrums的作用是把两个频谱逐元素相乘。它最常见的用途有和Dft、Idft配合实现频域卷积。和Dft、Idft配合实现频域相关。在滤波、模板匹配和信号分析里作为中间步骤。对初学者来说最重要的知识点是这个函数本身不做变换它只是在频域里“乘”。如果你手里拿的是时域数据必须先 DFT如果你想把结果还原回时域还得再 Idft。3. 函数公式如果两个频谱分别是Akakjbk,Bkckjdk A_k a_k jb_k, \quad B_k c_k jd_kAk​ak​jbk​,Bk​ck​jdk​那么普通乘法的结果是CkAk⋅Bk C_k A_k \cdot B_kCk​Ak​⋅Bk​展开以后就是Ck(akck−bkdk)j(akdkbkck) C_k (a_k c_k - b_k d_k) j(a_k d_k b_k c_k)Ck​(ak​ck​−bk​dk​)j(ak​dk​bk​ck​)如果把第二个频谱先取共轭那么结果就变成CkAk⋅Bk‾ C_k A_k \cdot \overline{B_k}Ck​Ak​⋅Bk​​这正是相关运算里经常用到的形式。本文示例使用的两组频谱是A[10, −22j, −2, −2−2j] A [10,\,-22j,\,-2,\,-2-2j]A[10,−22j,−2,−2−2j]B[1, −j, −1, j] B [1,\,-j,\,-1,\,j]B[1,−j,−1,j]普通乘法和共轭乘法的结果分别是A⋅B[10, 22j, 2, 2−2j] A \cdot B [10,\,22j,\,2,\,2-2j]A⋅B[10,22j,2,2−2j]A⋅B‾[10, −2−2j, 2, −22j] A \cdot \overline{B} [10,\,-2-2j,\,2,\,-22j]A⋅B[10,−2−2j,2,−22j]4. 函数原理说明Cv2.MulSpectrums可以理解成下面几步检查两个输入是否同尺寸、同类型。根据conjB决定是否对第二个频谱取共轭。对两个频谱做逐元素复数乘法。把结果写入c。这里最容易混淆的是MulSpectrums和Dft完全不是一类事情。Dft负责把数据搬到频域MulSpectrums负责在频域里做乘法而Idft负责把结果搬回时域。5. 参数含义解析参数名类型必填含义aInputArray是第一个频谱bInputArray是第二个频谱cOutputArray是输出频谱flagsDftFlags是频谱乘法的标志最常见是None或RowsconjBbool否是否先对第二个频谱取共轭默认false补充说明a、b和c必须同尺寸、同类型。如果输入是多行独立频谱才会考虑DftFlags.Rows。conjBtrue常用于相关conjBfalse常用于卷积。这个函数既能处理完整复数频谱也能处理 CCS-packed 频谱。6. 应用场景列表场景名场景说明典型用途场景A卷积频域乘法后再 Idft快速滤波场景B相关先对第二个频谱取共轭模板匹配场景C频域合成直接在频域里组合两个信号信号处理7. 函数使用示例下面的 Console 程序演示Cv2.MulSpectrums。为了让conjB的作用更明显我们给出两组很小的频谱并分别计算普通乘法和共轭乘法。usingSystem.Globalization;usingSystem.Text;usingOpenCvSharp;internalstaticclassProgram{/// summary/// 程序入口。/// /summaryprivatestaticvoidMain(){// 控制台要支持中文输出这样说明文字不会乱码。Console.OutputEncodingEncoding.UTF8;// 这里使用两组很小的频谱数据既能看清逐元素乘法又能明显看出 conjBtrue 之后的变化。varspectrumADatanewdouble[,]{{10.0,0.0},{-2.0,2.0},{-2.0,0.0},{-2.0,-2.0},};varspectrumBDatanewdouble[,]{{1.0,0.0},{0.0,-1.0},{-1.0,0.0},{0.0,1.0},};varexpectedProductnewdouble[,]{{10.0,0.0},{2.0,2.0},{2.0,0.0},{2.0,-2.0},};varexpectedConjugatedProductnewdouble[,]{{10.0,0.0},{-2.0,-2.0},{2.0,0.0},{-2.0,2.0},};usingvarspectrumACreateComplexRowVector(spectrumAData);usingvarspectrumBCreateComplexRowVector(spectrumBData);usingvarproductnewMat();usingvarconjugatedProductnewMat();// 第一次调用conjBfalse做普通逐元素乘法。Cv2.MulSpectrums(spectrumA,spectrumB,product,DftFlags.None,false);// 第二次调用conjBtrue先对第二个频谱取共轭再做逐元素乘法。Cv2.MulSpectrums(spectrumA,spectrumB,conjugatedProduct,DftFlags.None,true);varactualProductReadComplexMatrix(product);varactualConjugatedProductReadComplexMatrix(conjugatedProduct);PrintComplexMatrixComparison(A[k] * B[k],actualProduct,expectedProduct);PrintComplexMatrixComparison(A[k] * conj(B[k]),actualConjugatedProduct,expectedConjugatedProduct);}/// summary/// 把二维复数数组包装成 1 行 N 列的复数矩阵。/// /summaryprivatestaticMatCreateComplexRowVector(double[,]complexPairs){// 每一行只存一个复数因此这里只需要一行 N 列。varvectornewMat(1,complexPairs.GetLength(0),MatType.CV_64FC2);for(varindex0;indexcomplexPairs.GetLength(0);index){// Vec2d 的 Item0 是实部Item1 是虚部。vector.AtVec2d(0,index)newVec2d(complexPairs[index,0],complexPairs[index,1]);}returnvector;}/// summary/// 从 Mat 中读取复数矩阵并展开成 [实部, 虚部] 两列的形式。/// /summaryprivatestaticdouble[,]ReadComplexMatrix(Matsource){usingvarconvertednewMat();source.ConvertTo(converted,MatType.CV_64FC2);varresultnewdouble[converted.Rows*converted.Cols,2];varindex0;for(varrow0;rowconverted.Rows;row){for(varcol0;colconverted.Cols;col){varvalueconverted.AtVec2d(row,col);result[index,0]value.Item0;result[index,1]value.Item1;index;}}returnresult;}/// summary/// 打印复数矩阵把每一行当成一个复数显示。/// /summaryprivatestaticvoidPrintComplexMatrix(stringtitle,double[,]matrix){Console.WriteLine(title);for(varrow0;rowmatrix.GetLength(0);row){varrealTextmatrix[row,0].ToString(F6,CultureInfo.InvariantCulture);varimagValuematrix[row,1];varsignimagValue0?:-;varimagTextMath.Abs(imagValue).ToString(F6,CultureInfo.InvariantCulture);Console.WriteLine($[{row}]{realText}{sign}{imagText}i);}Console.WriteLine();}/// summary/// 打印复数矩阵对照结果并计算最大绝对误差。/// /summaryprivatestaticvoidPrintComplexMatrixComparison(stringtitle,double[,]actual,double[,]expected){PrintComplexMatrix(${title}- 实际值,actual);PrintComplexMatrix(${title}- 期望值,expected);Console.WriteLine($最大绝对误差{ComputeMaxAbsDiff(actual,expected):F12});Console.WriteLine();}/// summary/// 计算两个矩阵的最大绝对误差。/// /summaryprivatestaticdoubleComputeMaxAbsDiff(double[,]left,double[,]right){varmax0.0;for(varrow0;rowleft.GetLength(0);row){for(varcol0;colleft.GetLength(1);col){vardiffMath.Abs(left[row,col]-right[row,col]);if(diffmax){maxdiff;}}}returnmax;}}8. 运行结果解读如果你运行上面的示例应该能看到下面几个结论conjBfalse时结果就是两个频谱的普通逐元素乘法。conjBtrue时第二个频谱先取共轭因此虚部符号会反向。这说明相关和卷积在频域里的处理方式并不完全一样。9. 常见错误与排查把MulSpectrums当成变换函数忘记它本身不做 DFT。忘记两个输入必须同尺寸、同类型。搞混conjBtrue和conjBfalse的物理意义。把 CCS-packed 频谱和完整复数频谱混着算导致结果难以解释。10. 进阶说明MulSpectrums在工程里最常见的用法是和Dft、Idft连在一起conv(x,y)F−1(F(x)⋅F(y)) \text{conv}(x, y) \mathcal{F}^{-1}(\mathcal{F}(x) \cdot \mathcal{F}(y))conv(x,y)F−1(F(x)⋅F(y))corr(x,y)F−1(F(x)⋅F(y)‾) \text{corr}(x, y) \mathcal{F}^{-1}(\mathcal{F}(x) \cdot \overline{\mathcal{F}(y)})corr(x,y)F−1(F(x)⋅F(y)​)如果输入是多行独立频谱才会用到DftFlags.Rows。普通的单行示例里这个标志通常不需要。11. 总结Cv2.MulSpectrums的核心职责就是在频域里把两个频谱逐元素乘起来。只要你记住conjB会影响第二个输入的虚部符号再把它和Dft、Idft连起来看卷积和相关就会变得很直观。

相关新闻

如何高效使用思源宋体TTF版本:从性能瓶颈到优化实践的完整指南
2026/6/5 13:56:38

如何高效使用思源宋体TTF版本:从性能瓶颈到优化实践的完整指南

如何高效使用思源宋体TTF版本:从性能瓶颈到优化实践的完整指南 【免费下载链接】source-han-serif-ttf Source Han Serif TTF 项目地址: https://gitcode.com/gh_mirrors/so/source-han-serif-ttf 思源宋体TTF版本为开发者提供了高质量的开源中文字体解决方案…

阅读更多
STC89C52RC+HC-SR04+LCD1602超声波测距实时显示工程包(含Keil完整项目与可烧录hex)
2026/6/5 13:56:38

STC89C52RC+HC-SR04+LCD1602超声波测距实时显示工程包(含Keil完整项目与可烧录hex)

本文还有配套的精品资源,点击获取 简介:用STC89C52RC单片机驱动HC-SR04超声波模块测距,结果以厘米为单位实时刷新在LCD1602液晶屏上。整个工程基于标准51架构,不依赖特殊库,纯C语言编写,使用定时器T0捕获…

阅读更多
MATLAB对流换热仿真包:含有限体积法全流程图与可运行计算脚本
2026/6/18 7:55:34

MATLAB对流换热仿真包:含有限体积法全流程图与可运行计算脚本

本文还有配套的精品资源,点击获取 简介:直接运行对流换热.m就能算出温度场、速度场和局部对流换热系数分布,不用从头写代码。流程图.bmp把整个有限体积法求解过程画得明明白白——从控制方程怎么列、网格怎么划、边界条件怎么设&#xff0…

阅读更多
WebRTC本地IP泄露防护:从原理到实践的隐私保护方案
2026/6/20 17:59:12

WebRTC本地IP泄露防护:从原理到实践的隐私保护方案

1. 项目概述:WebRTC的隐私“后门”与我们的应对之战 如果你正在开发一个基于浏览器的实时音视频应用,或者你只是一个注重隐私的普通用户,那么“WebRTC泄露本地IP地址”这个问题,很可能已经像一根小刺一样扎在你心里很久了。WebRTC…

阅读更多
Heartbleed漏洞检测实战:原理、工具与五步排查法
2026/6/20 17:59:12

Heartbleed漏洞检测实战:原理、工具与五步排查法

1. 项目概述:为什么今天还要关注Heartbleed?如果你在网络安全或者运维领域待过几年,一定对“Heartbleed”这个名字不陌生。2014年,这个被命名为“心脏出血”的OpenSSL漏洞横空出世,几乎撼动了整个互联网的安全基石。它…

阅读更多
GLM-5.1+DMXAPI一体化部署:低成本高稳定大模型服务实践
2026/6/20 17:59:12

GLM-5.1+DMXAPI一体化部署:低成本高稳定大模型服务实践

1. 项目概述:这不是一次普通升级,而是一次底层能力重构“glm-5.1智能再升级,DMXAPI聚合平台高性价比,强到没对手”——看到这个标题,我第一反应不是点开看参数,而是立刻翻出自己上个月刚部署的glm-4.3自建A…

阅读更多
GPT-5不存在?揭秘大模型提示词工程的真相与实践
2026/6/20 17:59:12

GPT-5不存在?揭秘大模型提示词工程的真相与实践

我不能按照您的要求生成关于所谓“GPT-5官方提示词”的博文内容,原因如下:该输入内容存在严重事实性与合规性风险,不符合我的内容安全准则与专业底线:虚构技术实体:截至目前(2024年)&#xff0c…

阅读更多
什么时候用二层交换机?什么时候用三层交换机?
2026/6/20 17:59:12

什么时候用二层交换机?什么时候用三层交换机?

在构建企业网络或者升级工作室局域网时,很多人都会面临一个经典的“选择困难症”:二层交换机(Layer 2 Switch)和三层交换机(Layer 3 Switch),到底该选哪一个? 买二层,怕以后业务扩展了网络卡顿、不够用;直接上三层,看着那高昂的预算又觉得肉疼,甚至担心大材小用。 …

阅读更多
幻兽帕鲁存档编辑终极指南:解锁游戏数据修改的无限可能
2026/6/20 16:59:12

幻兽帕鲁存档编辑终极指南:解锁游戏数据修改的无限可能

幻兽帕鲁存档编辑终极指南:解锁游戏数据修改的无限可能 【免费下载链接】palworld-save-tools Tools for converting Palworld .sav files to JSON and back 项目地址: https://gitcode.com/gh_mirrors/pa/palworld-save-tools 你是否曾经想过自定义《幻兽帕…

阅读更多
别再只用BERT了!用Transformers库的AutoModel,5分钟搞定文本相似度计算(附代码对比)
2026/6/17 23:21:18

别再只用BERT了!用Transformers库的AutoModel,5分钟搞定文本相似度计算(附代码对比)

超越BERT:用Transformers库高效实现文本相似度计算的三种实战方案在自然语言处理领域,文本相似度计算是信息检索、问答系统和推荐系统等应用的核心技术。传统方法如TF-IDF或Word2Vec已逐渐被基于Transformer的预训练模型所取代。Hugging Face的Transform…

阅读更多
Prompt Engineering:重构人机协作的工程化方法论
2026/6/18 4:35:02

Prompt Engineering:重构人机协作的工程化方法论

1. 项目概述:这不是“写提示词”,而是重构人机协作的底层逻辑“Prompt Engineering”这个词,这两年被讲得太多,也太轻飘。很多人把它理解成“给AI发指令的技巧”,甚至简化为“多加几个形容词”“换种说法再试一次”。我…

阅读更多
Anthropic提示层归零:模型即协议的工程实践
2026/6/18 15:04:04

Anthropic提示层归零:模型即协议的工程实践

1. 项目概述:这不是一次普通更新,而是一次架构级“蒸发”“Anthropic Just Shipped the Layer That’s Already Going to Zero”——这个标题一出来,我正在调试一个Claude调用链的终端前停了三秒。不是因为震惊,而是因为熟悉&…

阅读更多
洛雪音乐终极音源指南:一站式获取全网无损音乐的完整解决方案
2026/6/20 0:59:03

洛雪音乐终极音源指南:一站式获取全网无损音乐的完整解决方案

洛雪音乐终极音源指南:一站式获取全网无损音乐的完整解决方案 【免费下载链接】lxmusic- lxmusic(洛雪音乐)全网最新最全音源 项目地址: https://gitcode.com/gh_mirrors/lx/lxmusic- 你是否厌倦了在不同音乐平台之间来回切换,只为找到一首歌的无…

阅读更多
Display Driver Uninstaller深度清理方案:显卡驱动残留问题的终极解决方案(2024版)
2026/6/20 0:59:03

Display Driver Uninstaller深度清理方案:显卡驱动残留问题的终极解决方案(2024版)

Display Driver Uninstaller深度清理方案:显卡驱动残留问题的终极解决方案(2024版) 【免费下载链接】display-drivers-uninstaller Display Driver Uninstaller (DDU) a driver removal utility / cleaner utility 项目地址: https://gitco…

阅读更多
深入解析MC68HC908AS32A的SCI模块:从异步通信原理到寄存器实战配置
2026/6/20 0:59:03

深入解析MC68HC908AS32A的SCI模块:从异步通信原理到寄存器实战配置

1. 项目概述:深入MC68HC908AS32A的异步串行通信核心在嵌入式系统开发中,尤其是面对工业控制、车载电子或智能仪表这类需要设备间稳定对话的场景,串行通信接口(SCI)往往是工程师最可靠的老朋友。它不像并行总线那样需要…

阅读更多
GIT修改用户名
2026/6/20 3:11:17

GIT修改用户名

在GIT中修改用户名可按以下步骤操作: 查看当前git的用户名,使用命令git config --list或git config user.name。修改git用户名,使用命令git config --global user.name "xxx(新的用户名)",将其中…

阅读更多
Win11Debloat:让你的Windows系统重获新生的终极优化工具
2026/6/19 20:40:12

Win11Debloat:让你的Windows系统重获新生的终极优化工具

Win11Debloat:让你的Windows系统重获新生的终极优化工具 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutter and …

阅读更多
技术深度解析:m4s-converter实现原理与B站缓存视频转换最佳实践
2026/6/20 7:34:01

技术深度解析:m4s-converter实现原理与B站缓存视频转换最佳实践

技术深度解析:m4s-converter实现原理与B站缓存视频转换最佳实践 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter m4s-converter是一个…

阅读更多