OpencvSharp 算子学习教案之 - Cv2.Log
大家好,Opencv在很多工程项目中都会用到,而OpencvSharp则是以C#开发与实现的Opencv操作库,对.NET开发人员友好,但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳,因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案,供大家参考学习。
Cv2.Log
- 教案版本:V1.0
- 面向对象:OpenCvSharp 初学者
- 所属模块:core
- 源码位置:OpenCvSharp/Cv2/Cv2_core.cs:1736
摘要:Log 会对每个像素逐元素计算自然对数,输出和源图保持相同的尺寸与类型。本文使用 1、2、4、8 作为输入,演示对数压缩如何让较大的数值变化变得更温和。
1. 函数名称(带参数签名)
publicstaticvoidLog(InputArraysrc,OutputArraydst)2. 函数用途
Cv2.Log的作用,是对输入矩阵中的每一个元素单独计算自然对数,然后把结果写入dst。
这个重载最常见的用途有:
- 把指数域的数据压缩到更小的范围。
- 在频域、统计和亮度处理中做对数变换。
- 帮助初学者理解“对数会压缩大数值差异”。
这里需要注意,OpenCV 的语义是log(abs(src)),所以本页示例选择正数输入,避免额外干扰。
3. 函数公式
对于输入矩阵src,这个重载返回的是:
dst(x,y)=ln(∣src(x,y)∣) dst(x, y) = \ln(|src(x, y)|)dst(x,y)=ln(∣src(x,y)∣)
当输入值为正时,它就等价于普通的自然对数。
4. 函数原理说明
Cv2.Log的处理过程可以理解成:
- 读取当前像素值。
- 对这个值求自然对数。
- 把结果写入目标矩阵。
- 对整张图的每个位置重复这个过程。
对初学者来说,最重要的是区分:
- 这是逐元素对数运算,不是全局统计。
- 输入值越大,输出增长越慢,这就是对数压缩。
- 本页示例避免使用 0 和负数,先把基础语义讲清楚。
- 输出结果仍然是一张图,不是一个标量。
5. 参数含义解析
| 参数名 | 类型 | 必填 | 含义 |
|---|---|---|---|
| src | InputArray | 是 | 输入矩阵,建议使用正数浮点数 |
| dst | OutputArray | 是 | 输出结果矩阵 |
补充说明:
dst的尺寸和类型会与src保持一致。- 由于
log(0)没有有限结果,所以示例避免使用 0。 - OpenCV 语义里使用的是绝对值对数。
- 这个函数特别适合解释动态范围压缩。
6. 应用场景列表
| 场景名 | 场景说明 | 典型用途 |
|---|---|---|
| 场景A:动态范围压缩 | 让大数值差异变得更温和 | 图像增强、频谱显示 |
| 场景B:指数逆变换 | 作为 Exp 的反向操作 | 对数域恢复 |
| 场景C:基础数学变换 | 学习逐元素单输入运算 | OpenCvSharp 入门 |
7. 函数使用示例
下面的 Console 程序演示Cv2.Log。为了让输出更容易看懂,我们使用 1、2、4、8 作为四个象限的输入值,这样对数结果依次变成 0、0.693、1.386、2.079。
usingSystem.Globalization;usingSystem.Text;usingOpenCvSharp;internalstaticclassProgram{/// <summary>/// 程序入口。/// </summary>privatestaticvoidMain(){// 让控制台正确显示中文。Console.OutputEncoding=Encoding.UTF8;RunLogScenario();}/// <summary>/// 演示 Log 的逐元素自然对数运算。/// </summary>privatestaticvoidRunLogScenario(){varsourceData=newdouble[,]{{1.0,1.0,2.0,2.0},{1.0,1.0,2.0,2.0},{4.0,4.0,8.0,8.0},{4.0,4.0,8.0,8.0},};usingvarsource=CreateMat(sourceData);usingvardestination=newMat();// Log 会对每个元素单独计算自然对数。Cv2.Log(source,destination);varexpected=ComputeExpectedLog(sourceData);varsamplePoints=new[]{newPoint(1,1),newPoint(3,1),newPoint(1,3),newPoint(3,3),};PrintHeader("Log 逐元素自然对数","这个示例演示每个像素计算自然对数后的结果。");PrintMatrix("source",sourceData);PrintMatrix("destination",ReadMatrix(destination));foreach(varpointinsamplePoints){PrintPointComparison(point,sourceData,expected,destination);}Console.WriteLine("教学结论:Log 适合压缩动态范围,也常常和 Exp 配合使用。\n");}/// <summary>/// 把二维托管数组写入 OpenCV Mat。/// </summary>privatestaticMatCreateMat(double[,]values){returnMat.FromPixelData(values.GetLength(0),values.GetLength(1),MatType.CV_64FC1,values);}/// <summary>/// 手工计算自然对数结果。/// </summary>privatestaticdouble[,]ComputeExpectedLog(double[,]source){varresult=newdouble[source.GetLength(0),source.GetLength(1)];for(varrow=0;row<source.GetLength(0);row++){for(varcol=0;col<source.GetLength(1);col++){result[row,col]=Math.Log(Math.Abs(source[row,col]));}}returnresult;}/// <summary>/// 把 Mat 读回托管数组,便于打印和对照。/// </summary>privatestaticdouble[,]ReadMatrix(Matsource){varresult=newdouble[source.Rows,source.Cols];for(varrow=0;row<source.Rows;row++){for(varcol=0;col<source.Cols;col++){result[row,col]=source.At<double>(row,col);}}returnresult;}/// <summary>/// 打印程序标题。/// </summary>privatestaticvoidPrintHeader(stringtitle,stringdescription){Console.WriteLine(title);Console.WriteLine(description);Console.WriteLine(newstring('-',40));}/// <summary>/// 打印一个矩阵。/// </summary>privatestaticvoidPrintMatrix(stringtitle,double[,]matrix){Console.WriteLine(title);Console.WriteLine(FormatMatrixText(matrix));}/// <summary>/// 打印逐元素对比结果。/// </summary>privatestaticvoidPrintPointComparison(Pointpoint,double[,]source,double[,]expected,Matactual){varactualValue=actual.At<double>(point.Y,point.X);varexpectedValue=expected[point.Y,point.X];Console.WriteLine($"采样点:({point.X},{point.Y})");Console.WriteLine($"source:{FormatValue(source[point.Y,point.X])}");Console.WriteLine($"实际结果:{FormatValue(actualValue)}");Console.WriteLine($"期望结果:{FormatValue(expectedValue)}");Console.WriteLine($"是否一致:{Math.Abs(actualValue-expectedValue)<=1e-6}");Console.WriteLine();}/// <summary>/// 把双精度数值格式化成更适合阅读的字符串。/// </summary>privatestaticstringFormatValue(doublevalue,stringnumericFormat="F6"){returnvalue.ToString(numericFormat,CultureInfo.InvariantCulture);}/// <summary>/// 把矩阵打印成多行文本,便于阅读。/// </summary>privatestaticstringFormatMatrixText(double[,]matrix){varsb=newStringBuilder();for(varrow=0;row<matrix.GetLength(0);row++){sb.Append("[");for(varcol=0;col<matrix.GetLength(1);col++){sb.Append(matrix[row,col].ToString("F3",CultureInfo.InvariantCulture));if(col<matrix.GetLength(1)-1){sb.Append(", ");}}sb.AppendLine("]");}returnsb.ToString();}}8. 注意事项
- Log 计算的是自然对数,不是常用对数。
- OpenCV 语义里使用的是绝对值对数。
- 输入值应当避免 0,便于初学者先看清楚数学含义。
- 输出矩阵和输入矩阵尺寸一致。
9. 调优建议
- 先从 1、2、4、8 这样的输入开始看结果。
- 如果你在做动态范围压缩,对数变换通常比线性缩放更能保留相对变化。
- 对比本页和 Exp 教案,最容易建立指数和对数的互逆关系。
- 先看控制台结果,再去看 WPF 预览,会更容易理解数值变化。
10. 运行说明
- 如果你在控制台工程里运行本文示例,直接把代码放进
Program.cs即可。 - 如果你在本仓库里学习,请直接打开 WPF 控件
Cv2.Log,点击按钮查看结果。 - WPF 示例会把源图和结果图并排展示,并对浮点值做归一化显示。
11. 常见错误排查
- 把 Log 当成常用对数。
- 忽略 OpenCV 使用的是
log(abs(src))。 - 使用 0 或负数输入,导致数学意义不直观。
- 以为输出会变成一个标量,其实输出仍然是一张矩阵。