一,卷积的边缘问题
先看一幅图:
定义了一个
(
3
∗
3
)
(3 * 3)
(3∗3)的卷积核在 源数据上进行卷积操作,源 上面的黄色部分为卷积核的开始位置,输出 上面的黄色部分为卷积核的结束部分,但是卷积核的中心点(锚点)覆盖不了源数据的四边的边缘值(如上图的锚点盲区,输出的红色部分)。
总结:
图像卷积的时候边界像素,不能被卷积操作,原因在于边界像素没有跟 kernel 完全重复或者没有被kernel 的锚点覆盖,所以当
(
3
∗
3
)
(3 * 3)
(3∗3)滤波时,边缘有 1个像素没有被卷积处理,
(
5
∗
5
)
(5 * 5)
(5∗5)时就会有2个边缘像素没有被处理掉,
(
7
∗
7
)
(7 * 7)
(7∗7)就会有3个 … …
二,OpenCv的解决方法
在卷积开始之前增加边缘像素,填充的像素值为0 或者 RGB黑色,比如在 ( 3 ∗ 3 ) (3 * 3) (3∗3)的四周各填充一个像素的边缘,这样就缺包图像的边缘被处理,在卷积之后再去掉这些边缘。opencv中默认的处理方法是:BorderTypes.Default ,除此之外常用的还有:
- BorderTypes.Constant;填充边缘用指定的像素值。
- BorderTypes.Replicate;填充边缘像素用已知的边缘像素值。
- BorderTypes.Wrap;用另一边的像素来补偿填充。
API :Cv2.CopyMakeBorder() ,在图像周围形成边框
参数 | 说明 |
---|---|
InputArray src | The source image |
OutputArray dst | 输出图像;将具有与src相同的类型和大小size (src.cols+left+right,src.rows+top+bottom) |
int top | Specify how much pixels in each direction from the source image rectangle one needs to extrapolate (指定从源图像矩形每个方向需要推断多少像素)边缘长度,一般上下左右都取相同的值 |
int bottom | 同上 |
int left | 同上 |
int right | 同上 |
BorderTypes borderType | 边缘处理类型 (枚举类型) |
Scalar? value = null | 颜色,默认是 null |
三,代码
#region 边缘处理
/// <summary>
/// 图像边缘处理类型
/// </summary>
private static void ImageEdgeProcessing(string path)
{
Mat src_Mat = new Mat(path, ImreadModes.AnyColor | ImreadModes.AnyDepth);
Mat dst_Mat = new Mat();
#region 边缘处理四个类型
//定义四个方向像素,边缘宽度相对于源图像的 0.05
int top = (int)(0.05 * src_Mat.Rows);
int botton = (int)(0.05 * src_Mat.Rows);
int left = (int)(0.05 * src_Mat.Cols);
int right = (int)(0.05 * src_Mat.Cols);
//定义随机数
RNG r = new RNG(12345);
//int borderType =(int) BorderTypes.Default;
BorderTypes borderType = new BorderTypes();
borderType = BorderTypes.Default;
//Cv2.ImShow("src", src);
int ch = 0;
while (true)
{
ch = Cv2.WaitKey(500);
if ((char)ch == 27)// ESC建退出
{
break;
}
else if ((char)ch == 'r')
{
borderType = BorderTypes.Replicate;//填充边缘像素用已知的边缘像素值
}
else if ((char)ch == 'w')
{
borderType = BorderTypes.Wrap;//用另外一边的像素来补偿填充
}
else if ((char)ch == 'c')
{
borderType = BorderTypes.Constant;//填充边缘用指定像素值
}
else if ((char)ch == 'd')
{
borderType = BorderTypes.Default;//默认边缘处理
}
Scalar color = new Scalar(r.Uniform(0, 255), r.Uniform(0, 255), r.Uniform(0, 255));
Cv2.CopyMakeBorder(src_Mat, dst_Mat, top, botton, left, right, borderType, color);
Window w = new Window("dst", WindowMode.Normal);
Cv2.ImShow("dst", dst_Mat);
}
#endregion
//Cv2.GaussianBlur(src, dst, new Size(5, 5), 5, 5, BorderTypes.Wrap);
//using (new Window("dst", WindowMode.Normal, dst_Mat))
//{
// Cv2.WaitKey(0);
//}
}
#endregion
BorderTypes.Default效果
BorderTypes.Replicate效果
BorderTypes.Wrap效果
BorderTypes.Constant效果
在进行模糊处理时也可以指定边缘处理类型
最后一个参数指定边缘处理类型
Cv2.GaussianBlur(src_Mat, dst_Mat, new Size(5, 5), 5, 5, BorderTypes.Wrap);
上面的边缘处理没有指定边缘宽度,只是1个像素的变化我们看不出来而已。