OpencvSharp 算子学习教案之 - Cv2.CartToPolar
大家好,Opencv在很多工程项目中都会用到,而OpencvSharp则是以C#开发与实现的Opencv操作库,对.NET开发人员友好,但很多API的中文资料、应用场景及常见坑点等缺乏系统性归纳,因此这系列博客将给大家带来Cv2及Mat对象全系列算子学习教案,供大家参考学习。
Cv2.CartToPolar
- 教案版本:V1.0
- 面向对象:OpenCvSharp 初学者
- 所属模块:core
- 源码位置:OpenCvSharp/Cv2/Cv2_core.cs:1799
摘要:CartToPolar 会把 x/y 分量逐元素转换成幅值和角度。本文用坐标轴上的四个向量演示长度和方向如何被恢复,并解释 angleInDegrees 的输出单位。
1. 函数名称(带参数签名)
publicstaticvoidCartToPolar(InputArrayx,InputArrayy,OutputArraymagnitude,OutputArrayangle,boolangleInDegrees=false)2. 函数用途
Cv2.CartToPolar的作用,是把直角坐标里的 x/y 分量转换成极坐标里的幅值和角度。
这个重载最常见的用途有:
- 从向量分量恢复长度和方向。
- 把图像中的位移、梯度或流场转成更容易理解的形式。
- 和 PolarToCart 一起学习坐标之间的互相转换。
OpenCV 的文档语义还说明了:x和y必须有相同的尺寸和类型;输出的magnitude与angle会和输入保持同样的尺寸与类型。
3. 函数公式
如果把 x 分量记为xxx,y 分量记为yyy,那么:
magnitude=x2+y2 magnitude = \sqrt{x^2 + y^2}magnitude=x2+y2
angle=atan2(y,x) angle = \operatorname{atan2}(y, x)angle=atan2(y,x)
如果angleInDegrees = true,那么角度会以度数形式输出;否则会以弧度形式输出。
4. 函数原理说明
CartToPolar的处理过程可以理解为:
- 读取当前位置的 x 分量。
- 读取当前位置的 y 分量。
- 用勾股定理算出向量长度。
- 用
atan2算出向量角度。 - 把结果分别写入幅值矩阵和角度矩阵。
对初学者来说,最重要的是把“长度”和“方向”拆开理解,这样后面学习 Phase 时会更轻松。
5. 参数含义解析
| 参数名 | 类型 | 必填 | 含义 |
|---|---|---|---|
| x | InputArray | 是 | 输入 x 分量矩阵 |
| y | InputArray | 是 | 输入 y 分量矩阵 |
| magnitude | OutputArray | 是 | 输出幅值矩阵 |
| angle | OutputArray | 是 | 输出角度矩阵 |
| angleInDegrees | bool | 否 | true 表示角度按度数输出,false 表示按弧度输出 |
补充说明:
x和y必须同尺寸、同类型。magnitude和angle的尺寸、类型会与输入保持一致。- 如果你需要更直观地理解角度,通常会把
angleInDegrees设为 true。 - 这个函数特别适合和 PolarToCart 做成一对来学习。
6. 应用场景列表
| 场景名 | 场景说明 | 典型用途 |
|---|---|---|
| 场景A:向量恢复 | 从 x/y 恢复长度和方向 | 几何分析、运动估计 |
| 场景B:梯度分析 | 把分量变成幅值和角度 | 图像边缘、光流 |
| 场景C:逆向学习 | 和 PolarToCart 对照理解 | OpenCvSharp 入门 |
7. 函数使用示例
下面的 Console 程序演示Cv2.CartToPolar。为了让数值更容易理解,我们让输入向量刚好落在坐标轴上,这样输出角度会非常直观。
usingSystem.Globalization;usingSystem.Text;usingOpenCvSharp;internalstaticclassProgram{/// <summary>/// 程序入口。/// </summary>privatestaticvoidMain(){// 让控制台正确显示中文。Console.OutputEncoding=Encoding.UTF8;RunCartToPolarScenario();}/// <summary>/// 演示 CartToPolar 的逐元素坐标恢复。/// </summary>privatestaticvoidRunCartToPolarScenario(){constboolangleInDegrees=true;varxData=newdouble[,]{{1.0,0.0},{-3.0,0.0},};varyData=newdouble[,]{{0.0,2.0},{0.0,-4.0},};usingvarx=CreateMat(xData);usingvary=CreateMat(yData);usingvarmagnitude=newMat();usingvarangle=newMat();// CartToPolar 会把每个位置的 x/y 分量转换成幅值和角度。Cv2.CartToPolar(x,y,magnitude,angle,angleInDegrees);varsamplePoints=new[]{newPoint(0,0),newPoint(1,0),newPoint(0,1),newPoint(1,1),};PrintHeader("CartToPolar 逐元素坐标恢复","本示例使用度数作为角度单位,这样可以直接读出 0、90、180、270 度的方向。\n");PrintMatrix("x",xData);PrintMatrix("y",yData);PrintMatrix("magnitude",ReadMatrix(magnitude));PrintMatrix("angle",ReadMatrix(angle));foreach(varpointinsamplePoints){varxValue=xData[point.Y,point.X];varyValue=yData[point.Y,point.X];varactualMagnitude=magnitude.At<double>(point.Y,point.X);varactualAngle=angle.At<double>(point.Y,point.X);varexpected=PolarFromCartesian(xValue,yValue);PrintComparison("幅值",point,actualMagnitude,expected.Magnitude);PrintComparison("角度",point,actualAngle,expected.AngleDegrees);}Console.WriteLine("教学结论:CartToPolar 适合把向量分量恢复成“有多长、朝哪个方向”这两个最基础的量。\n");}/// <summary>/// 把二维数组写入 Mat。/// </summary>/// <param name="values">二维数组。</param>/// <returns>CV_64FC1 矩阵。</returns>privatestaticMatCreateMat(double[,]values){returnMat.FromPixelData(values.GetLength(0),values.GetLength(1),MatType.CV_64FC1,values);}/// <summary>/// 手工计算直角坐标到极坐标的结果。/// </summary>/// <param name="x">x 分量。</param>/// <param name="y">y 分量。</param>/// <returns>幅值和角度(度)。</returns>privatestatic(doubleMagnitude,doubleAngleDegrees)PolarFromCartesian(doublex,doubley){varmagnitude=Math.Sqrt(x*x+y*y);varangleRadians=Math.Atan2(y,x);if(angleRadians<0){angleRadians+=Math.PI*2.0;}return(magnitude,angleRadians*180.0/Math.PI);}/// <summary>/// 把 Mat 读回 double[,] 二维数组。/// </summary>/// <param name="source">输入矩阵。</param>/// <returns>double 二维数组。</returns>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>/// <param name="title">标题。</param>/// <param name="description">说明。</param>privatestaticvoidPrintHeader(stringtitle,stringdescription){Console.WriteLine(title);Console.WriteLine(description);Console.WriteLine(newstring('-',40));}/// <summary>/// 打印一个矩阵。/// </summary>/// <param name="title">标题。</param>/// <param name="matrix">矩阵。</param>privatestaticvoidPrintMatrix(stringtitle,double[,]matrix){Console.WriteLine(title);Console.WriteLine(FormatMatrixText(matrix));}/// <summary>/// 打印采样点比较结果。/// </summary>/// <param name="label">说明标签。</param>/// <param name="point">采样点。</param>/// <param name="actual">实际结果。</param>/// <param name="expected">期望结果。</param>privatestaticvoidPrintComparison(stringlabel,Pointpoint,doubleactual,doubleexpected){Console.WriteLine(label);Console.WriteLine($"采样点:({point.X},{point.Y})");Console.WriteLine($"实际结果:{FormatValue(actual)}");Console.WriteLine($"期望结果:{FormatValue(expected)}");Console.WriteLine($"是否一致:{Math.Abs(actual-expected)<=1e-6}");Console.WriteLine();}/// <summary>/// 把双精度数值格式化成更适合阅读的字符串。/// </summary>/// <param name="value">待格式化的值。</param>/// <param name="numericFormat">格式字符串。</param>/// <returns>格式化后的字符串。</returns>privatestaticstringFormatValue(doublevalue,stringnumericFormat="F6"){returnvalue.ToString(numericFormat,CultureInfo.InvariantCulture);}/// <summary>/// 把矩阵打印成多行文本。/// </summary>/// <param name="matrix">二维矩阵。</param>/// <param name="numericFormat">格式字符串。</param>/// <returns>矩阵文本。</returns>privatestaticstringFormatMatrixText(double[,]matrix,stringnumericFormat="F3"){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(numericFormat,CultureInfo.InvariantCulture));if(col<matrix.GetLength(1)-1){sb.Append(", ");}}sb.AppendLine("]");}returnsb.ToString();}}8. 注意事项
x和y必须同尺寸、同类型。- 输出的 magnitude 和 angle 会和输入保持同样的尺寸和类型。
atan2会自动处理象限,所以角度比手动用Math.Atan(y / x)更稳妥。- 如果你要和 PolarToCart 对照,最好统一角度单位。
9. 调优建议
- 先用坐标轴上的向量做示例,最容易看懂。
- 如果你关心方向,先看 angle;如果你关心长度,先看 magnitude。
- 和 Phase 放在一起看,可以很快理解它们的区别。
- 先看 WPF 预览,再看控制台数字,理解会更快。
10. 运行说明
- 如果你在控制台工程里运行本文示例,直接把代码放进
Program.cs即可。 - 如果你在本仓库里学习,请直接打开 WPF 控件
Cv2.CartToPolar,点击按钮查看结果。 - WPF 示例会把 x、y、magnitude、angle 四个矩阵并排展示出来。
11. 常见错误排查
- 把 CartToPolar 当成统计函数,它其实是逐元素变换。
- 忘记 x 和 y 必须同尺寸、同类型。
- 把弧度和度数混淆,导致角度解释错误。
- 以为输出会变成一个标量,其实输出仍然是矩阵。