文章目录
前言
卷积和滤波需要在图像的边缘像素附近进行计算。由于边缘像素缺乏完整的邻域,就引发了一个重要问题:如何处理图像边缘以确保卷积核在边缘处也能正常应用。OpenCV 提供了多种边缘处理方法来决定如何对待图像边缘之外的区域。在本文中,将介绍几种常用的边缘处理方法,并通过代码示例演示如何在 OpenCV 中实现这些操作。提供完整的代码示例,以便学习使用。
1.理论基础
在进行卷积或其他基于邻域的图像处理操作时,卷积核会超出图像边缘,就需要处理那些超出图像范围的像素点。边缘处理方法决定了如何填充这些超出图像边缘的区域,以使得卷积操作在整个图像区域内都能得到应用。不同的边缘处理方法会对图像的最终效果产生不同的影响。
通过copyMakeBorder()实现边缘处理:
void copyMakeBorder(InputArray src, OutputArray dst,
int top, int bottom, int left, int right,
int borderType, const Scalar& value = Scalar());
borderType:
BORDER_CONSTANT:使用常数值填充边界。通常是黑色(像素值为 0),也可以指定其他填充值。
BORDER_REPLICATE:复制最近的边缘像素。这种方法将图像边缘处的像素值延伸到超出图像的区域。
BORDER_WRAP:边缘像素值从另一边镜像复制,类似于环绕或平铺效果。
BORDER_REFLECT:镜像反射边缘像素。反射后像素的排列是:fedcba|abcdefgh|hgfedcba。
BORDER_REFLECT_101:类似于 BORDER_REFLECT,但反射区域不包含镜像的边界。排列是:gfedcb|abcdefgh|gfedcba。
BORDER_DEFAULT:OpenCV 默认的边缘处理方式,通常等同于 BORDER_REFLECT_101
。
2.代码实现
使用到的图片lena.png
(1)BORDER_CONSTANT
cv::Mat constantBorder;
int top = (int)(0.05*image.rows);
int bottom = (int)(0.05*image.rows);
int left = (int)(0.05 * image.cols);
int right = (int)(0.05 * image.cols);
copyMakeBorder(image, constantBorder, top, bottom, left, right, BORDER_CONSTANT, Scalar(0));
cv::imshow("Constant Border Image", constantBorder);
结果展示:
边框区域被填充为黑色,突出显示了图像边缘与背景的明显界限。
(2)BORDER_REPLICATE
top,bottom,left,right延续(1)
// 使用复制边缘的边缘处理方法
cv::Mat replicateBorder;
copyMakeBorder(image, replicateBorder, top, bottom, left, right, BORDER_REPLICATE);
cv::imshow("Replicate Border Image", replicateBorder);
结果展示:
图像边界像素被复制并延伸,平滑地扩展图像内容,避免出现明显的边缘或色差。
(3)BORDER_WRAP
top,bottom,left,right延续(1)
// 使用环绕边缘的边缘处理方法
cv::Mat wrapBorder;
copyMakeBorder(image, wrapBorder, top, bottom, left, right, BORDER_WRAP);
cv::imshow("Wrap Border Image", wrapBorder);
结果展示:
边缘像素通过环绕方式从另一边复制过来,形成一种平铺的效果,使边缘和内容无缝连接。
(4)BORDER_REFLECT
top,bottom,left,right延续(1)
// 使用镜像反射的边缘处理方法
cv::Mat reflectBorder;
copyMakeBorder(image, reflectBorder, top, bottom, left, right, BORDER_REFLECT);
cv::imshow("Reflect Border Image", reflectBorder);
结果展示:
图像边缘通过镜像反射方式复制,使得边缘处的像素看起来是图像的一部分,从而保持视觉上的连续性和自然性。
(5)BORDER_REFLECT_101
top,bottom,left,right延续(1)
// 使用镜像反射101的边缘处理方法
cv::Mat reflect101Border;
copyMakeBorder(image, reflect101Border, top, bottom, left, right, BORDER_REFLECT_101);
cv::imshow("Reflect 101 Border Image", reflect101Border);
结果展示:
边缘像素通过镜像反射方式扩展,反射时不包含边界像素,确保了边缘与图像中心部分的平滑过渡,增强了视觉的自然感。
(6)BORDER_DEFAULT
top,bottom,left,right延续(1)
// 使用默认的边缘处理方法
cv::Mat defaultBorder;
copyMakeBorder(image, defaultBorder, top, bottom, left, right, BORDER_DEFAULT);
cv::imshow("Default Border Image", defaultBorder);
结果展示:
BORDER_DEFAULT
通常等同于 BORDER_REFLECT_101
,提供了一种平滑的边缘扩展方式,使得图像边缘看起来自然且无缝过渡。
3.完整代码
#include<opencv2/opencv.hpp>
#include<highgui.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
using namespace std;
void work_edges()
{
cv::Mat image;
image = imread("lena.png");
if (image.empty()) {
printf("could not find the image...\n");
return;
}
namedWindow("input image", cv::WINDOW_AUTOSIZE);
cv::imshow("input image", image);
cv::Mat constantBorder;
int top = (int)(0.05*image.rows);
int bottom = (int)(0.05*image.rows);
int left = (int)(0.05 * image.cols);
int right = (int)(0.05 * image.cols);
copyMakeBorder(image, constantBorder, top, bottom, left, right, BORDER_CONSTANT, Scalar(0));
cv::imshow("Constant Border Image", constantBorder);
// 使用复制边缘的边缘处理方法
cv::Mat replicateBorder;
copyMakeBorder(image, replicateBorder, top, bottom, left, right, BORDER_REPLICATE);
cv::imshow("Replicate Border Image", replicateBorder);
// 使用环绕边缘的边缘处理方法
cv::Mat wrapBorder;
copyMakeBorder(image, wrapBorder, top, bottom, left, right, BORDER_WRAP);
cv::imshow("Wrap Border Image", wrapBorder);
// 使用镜像反射的边缘处理方法
cv::Mat reflectBorder;
copyMakeBorder(image, reflectBorder, top, bottom, left, right, BORDER_REFLECT);
cv::imshow("Reflect Border Image", reflectBorder);
// 使用镜像反射101的边缘处理方法
cv::Mat reflect101Border;
copyMakeBorder(image, reflect101Border, top, bottom, left, right, BORDER_REFLECT_101);
cv::imshow("Reflect 101 Border Image", reflect101Border);
// 使用默认的边缘处理方法
cv::Mat defaultBorder;
copyMakeBorder(image, defaultBorder, top, bottom, left, right, BORDER_DEFAULT);
cv::imshow("Default Border Image", defaultBorder);
waitKey(0);
}
int main()
{
work_edges();
return 0;
}