使用扩展Sobel算子计算一阶、二阶、三阶或混合图像的导数。当核的大小ksize不为1时,用ksize X ksize的可分离核求导;为1时,用 3 X 1 或 1 X 3的核(不进行高斯平滑)只对一阶或二阶的x轴或y同求导。
当ksze = FILTER_SCHARR(即-1)时,使用Scharr 3x3的核获得更精确的结果,具体见下文
该函数通过将图像与适当的核卷积来计算图像导数:dst = ∂ x o r d e r + y o r d e r src ∂ x x o r d e r ∂ y y o r d e r \texttt{dst} = \frac{\partial^{xorder+yorder} \texttt{src}}{\partial x^{xorder} \partial y^{yorder}} dst=∂xxorder∂yyorder∂xorder+yordersrc
Sobel算子组合了高斯平滑和微分,具有一定的搞噪能力。常用的参数有(xorder =1,yorder =0,ksize = 3)或(xorder = 0,yorder = 1,ksize = 3)来计算第一个x或y的图像导数。
水平方向(x轴)的核为垂直方向(y轴)的核为 [ − 1 0 1 − 2 0 2 − 1 0 1 ] \begin{bmatrix} -1 & 0 & 1 \\ -2 & 0 & 2 \\ -1 & 0 & 1\end{bmatrix} −1−2−1000121
[ − 1 − 2 − 1 0 0 0 1 2 1 ] \begin{bmatrix} -1 & -2 & -1 \\ 0 & 0 & 0 \\ 1 & 2 & 1\end{bmatrix} −101−202−101
void Sobel(InputArray src,
OutputArray dst,
MatType ddepth,
int xorder,
int yorder,
int ksize = 3,
double scale = 1.0,
double delta = 0.0,
BorderTypes borderType = BorderTypes.Reflect101)
参数 | 说明 |
---|---|
InputArray src | 输入图像, 一般为灰度图 |
OutputArray dst | 输出图像。大小与通道数与输入图像一致 |
MatType ddepth | 期望的输出深度,具体见Depth combinations表 |
int xorder | 导数x的阶数 |
int yorder | 导数y的阶数 |
int ksize | 核大小,必须为-1、1、3、5或7(-1时使用Scharr 3X3) |
double scale | 计算的导数值的可选比例因子,默认为1。 |
double delta | 计算结果上加该值,可理解为调亮(正数)或调暗(负数) |
BorderTypes borderType | 边框外像素取值方法,不支持Wrap |
Sobel垂直边缘
获取用于计算图像导数的滤波系数。当ksize=FILTER_SCHARR(即-1)时,返回 3 x 3的Scharr核,其它情况返回Soble内核。通常结果用于SepFilter2D。
void GetDerivKernels(OutputArray kx,
OutputArray ky,
int dx,
int dy,
int ksize,
bool normalize = false,
MatType? ktype = null)
参数 | 说明 |
---|---|
OutputArray kx | 行滤波系数的输出矩阵,类型为ktype |
OutputArray ky | 列滤波系数的输出矩阵,类型为ktype |
int dx | 导数x的阶数 |
int dy | 导数y的阶数 |
int ksize | 核大小,可为-1,1,3,5,7 |
bool normalize | 是否标准化(按比例缩小)滤波系数。理论上,系数应该有分母 = 2 k s i z e ∗ 2 − d x − d y − 2 =2^{ksize*2-dx-dy-2} =2ksize∗2−dx−dy−2。如果对浮点图像滤波,则设为true。 如果是8位图像,结果为16位,则设为False。默认为False |
MatType? ktype | 滤波系数的类型。CV_32F或CV_64F。null时为CV_32F |
使用Scharr算子计算图像的x、y轴一阶导数。
等价于 Scharr(src, dst, ddepth, dx, dy, scale, delta, borderType) \texttt{Scharr(src, dst, ddepth, dx, dy, scale, delta, borderType)} Scharr(src, dst, ddepth, dx, dy, scale, delta, borderType)
水平方向(x轴)的核为 Sobel(src, dst, ddepth, dx, dy, -1, scale, delta, borderType) \texttt{Sobel(src, dst, ddepth, dx, dy, -1, scale, delta, borderType)} Sobel(src, dst, ddepth, dx, dy, -1, scale, delta, borderType)
[ − 3 0 3 − 10 0 10 − 3 0 3 ] \begin{bmatrix} -3 & 0 & 3 \\ -10 & 0 & 10 \\ -3 & 0 & 3\end{bmatrix} −3−10−30003103
垂直方向(y轴)的核为
[ − 3 − 10 3 0 0 0 3 10 3 ] \begin{bmatrix} -3 & -10 & 3 \\ 0 & 0 & 0 \\ 3 & 10 & 3\end{bmatrix} −303−10010303
void Scharr(InputArray src,
OutputArray dst,
MatType ddepth,
int xorder,
int yorder,
double scale = 1.0,
double delta = 0.0,
BorderTypes borderType = BorderTypes.Reflect101)
参数 | 说明 |
---|---|
InputArray src | 输入图像, 一般为灰度图 |
OutputArray dst | 输出图像。大小与通道数与输入图像一致 |
MatType ddepth | 期望的输出深度,具体见Depth combinations表 |
int xorder | 导数x的阶数 |
int yorder | 导数y的阶数 |
double scale | 计算的导数值的可选比例因子,默认为1。 |
double delta | 计算结果上加该值,可理解为调亮(正数)或调暗(负数) |
BorderTypes borderType | 边框外像素取值方法,不支持Wrap |
Sobel边缘检测:效率高,有一定抗噪,但对纹理细节不够精确,
Canny边缘检测:精度比Sobel高,最效率没Sobel高
Mat src;
string winName = "Sobel & Scharr Demo";
string funType = "S";
public void Run(ParamBase paramBase)
{
//测试GetDerivKernels结果
using var dx = new Mat();
using var dy = new Mat();
Cv2.GetDerivKernels(dx, dy, 1, 0, -1,false);
//控制台输出dx、dy
Utils.Dump(dx);
Utils.Dump(dy);
//弹出文件选择对话框
if (!Utils.SelectFile(out string fileName)) fileName = ImagePath.Lena;
//var fileName = ImagePath.Lena;
src = Cv2.ImRead(fileName, ImreadModes.Color);
if (src.Empty()) throw new Exception($"图像打开有误:{fileName}");
//高斯滤波
Cv2.GaussianBlur(src, src, new Size(3, 3), 0, 0);
//一般对灰度图进行Sobel或SChar边缘检测
Cv2.CvtColor(src, src, ColorConversionCodes.BGR2GRAY);
Cv2.NamedWindow(winName, WindowFlags.Normal);
Cv2.CreateTrackbar("kSize 2n-1", winName, 4, KSizeOnChanged);
Cv2.SetTrackbarPos("kSize 2n-1", winName, 2);
Cv2.CreateTrackbar("scale n/10", winName, 100, SCaleOnChanged);
Cv2.SetTrackbarPos("scale n/10", winName, 10);
Cv2.CreateTrackbar("Delta n-50", winName, 100, DeltaOnChanged);
Cv2.SetTrackbarPos("Delta n-50", winName, 50);
bool loop = true;
while (loop)
{
var c = (Char)Cv2.WaitKey(50);
switch(c)
{
case 's':
case 'S':
funType= "S";
OnChanged();
break;
case 'c':
case 'C':
funType = "C";
OnChanged();
break;
case 'q':
case 'Q':
case (Char)27:
loop = false;
break;
default:
break;
}
}
Cv2.DestroyAllWindows();
}
private void OnChanged()
{
using var gradX = new Mat();
using var gradY = new Mat();
string text = "";
if (funType == "S")
{
Cv2.SetWindowTitle(winName, "Sobel 按S/C切换,Esc退出");
Cv2.Sobel(src, gradX, MatType.CV_16S, 1, 0, kSize, scale, delta);
Cv2.Sobel(src, gradY, MatType.CV_16S, 0, 1, kSize, scale, delta);
text = $"kSize={kSize},scale={scale.ToString("0.0")},delta={delta}";
}
else
{
Cv2.SetWindowTitle(winName, "Scharr 按S/C切换,Esc退出");
Cv2.Scharr(src, gradX, MatType.CV_16S, 1, 0, scale, delta);
Cv2.Scharr(src, gradY, MatType.CV_16S, 0, 1, scale, delta);
text = $"scale={scale.ToString("0.0")},delta={delta}";
}
Cv2.ConvertScaleAbs(gradX, gradX);
Cv2.ImShow("水平边缘", gradX);
Cv2.ConvertScaleAbs(gradY, gradY);
Cv2.ImShow("垂直边缘", gradY);
using var dst = new Mat();
//合并水平、垂直图像
Cv2.AddWeighted(gradX, 0.5, gradY, 0.5, 0, dst);
Utils.PutText(dst, text);
Cv2.ImShow(winName, dst);
}
int kSize = 3;
private void KSizeOnChanged(int pos, IntPtr userData)
{
kSize = pos * 2 - 1;
OnChanged();
}
double scale = 1;
private void SCaleOnChanged(int pos,IntPtr userData)
{
scale = pos / 10.0D;
OnChanged();
}
double delta = 0;
private void DeltaOnChanged(int pos,IntPtr userData)
{
delta = pos - 50;
OnChanged();
}
OpenCvSharp函数示例(目录)
参考
https://docs.opencv.org/4.7.0/d2/d2c/tutorial_sobel_derivatives.html