opencv基本操作(四)

(一)自己定义线性滤波器

卷积和卷积如何工作,这个是知道的,卷积的作用就是模糊图像,提取边缘,锐化图像。常见的卷积核(算子)有robert算子,sobel算子,拉普拉斯算子,前两个算子是分x方向和y方向的,而拉普拉斯算子是提取整体图像的边缘。

提取边缘时,sobel比robert算子效果好,自定义线性滤波器用的API是filter2D(src,dst,图像深度8或32,卷积核模板,锚点)。

int main()
{
Mat src,dst,dstx,dsty;
src = imread("1234.png");
if (!src.data) {
printf("could not load image");
return -1;
}
namedWindow("input image", CV_WINDOW_AUTOSIZE);
imshow("input image", src);
//Robert_x 方向
//Mat kernel_x = (Mat_<int>(2, 2) << 1, 0, 0, -1);
//filter2D(src,dstx,-1,kernel_x,Point(-1,-1),0.0);
//namedWindow("image1", CV_WINDOW_AUTOSIZE);

//imshow("image1", dstx);


//Robert_y 方向
//Mat kernel_y = (Mat_<int>(2, 2) << 0, 1, -1, 0);
//filter2D(src, dsty, -1, kernel_y, Point(-1, -1), 0.0);
//namedWindow("image2", CV_WINDOW_AUTOSIZE);

//imshow("image2", dsty);


//sobel算子在x方向
//Mat kernel_x = (Mat_<int>(3, 3) << -1, 0, 1,-2,0,2,-1,0,1);
//filter2D(src, dstx, -1, kernel_x, Point(-1, -1), 0.0);
//namedWindow("image1", CV_WINDOW_AUTOSIZE);

//imshow("image1", dstx);


sobel算子在y方向
//Mat kernel_y = (Mat_<int>(3,3) << -1,2,-1,0,0,0,1,2,1);
//filter2D(src, dsty, -1, kernel_y, Point(-1, -1), 0.0);
//namedWindow("image2", CV_WINDOW_AUTOSIZE);
//imshow("image2", dsty);
//拉普拉斯算子


/*Mat kernel = (Mat_<int>(3, 3) << 0, -1,0,-1,4,-1,0,-1,0);
filter2D(src, dst, -1, kernel, Point(-1, -1), 0.0);
namedWindow("image2", CV_WINDOW_AUTOSIZE);
imshow("image2", dst);*/
//自定义卷积核
int c = 0;
int index = 0;
int ksize1 = 3;
while (true) {
c = waitKey(500);
if ((char)c ==27) {
break;
}
ksize1 = 4+ (ksize1 % 5) * 2 + 1;
Mat kernel = Mat::ones(Size(ksize1,ksize1),CV_32F)/(float)(ksize1*ksize1);
filter2D(src, dst, -1, kernel, Point(-1, -1));
index++;
imshow("blur", dst);
}
//waitKey(0);
return 0;

}

(二)处理边缘

图像在卷积的时候,边界像素不能被卷积到,原因在于边界像素没有完全跟kernel重叠,3*3滤波有一个像素没有处理到,5*5滤波有两个边缘像素没有处理到。

在卷积开始之前,增加边缘的像素,填充的像素值为0或者RGB黑色,比如在3*3四周各填充一个像素的电源,这样确保图像的边缘被处理,在卷积之后再去掉这些边缘,opencv中默认的处理方法是BORDER_DEAULT,

常用的方法有如下几种:

BORDER_CONSTANT用指定的像素填充边缘

BORDER_REPLICATE 用已知的像素填充边缘

BORDER_WRAP用另一边的像素填充边缘。

用到的API:copyMakeBorder(src,

                                          dst,

                                           int top//边缘长度,一般上下左右都取相同值

                                           int bottom,

                                           int left,

                                           int right,

                                           int borderType

                                          Scalar value  )


int main()
{
Mat src, dst;
src = imread("1234.png");
if (!src.data)
{
printf("could not load image");
return -1;
}
int top = (int)(0.05*src.rows);
int bottom = (int)(0.05*src.rows);
int left= (int)(0.05*src.cols);
int right = (int)(0.05*src.cols);
RNG rng(12345);
int c = 0;
int borderType = BORDER_DEFAULT;
while (true) {

c = waitKey(500);
if ((char)c == 27) {
break;
}
else if ((char)c == 'r') {
borderType = BORDER_REFLECT;
}
else if ((char)c == 'w') {
borderType = BORDER_WRAP;
}
else if ((char)c == 'c') {
borderType = BORDER_CONSTANT;
}
Scalar color = Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255));
copyMakeBorder(src,dst,top,bottom,left,right,borderType,color);
imshow("output image",dst);
}
//waitKey(0);
return 0;

}

(三)sobel算子

边缘是什么?是像素值发生变化的地方,是图像的显著性特征之一, 如何提取或者捕捉边缘,对图像求它的一阶导数,

delta=f(x)-f(x-1),delta越大,说明像素在x方向变化越大,边缘信号越强。

Sobel 算子是一个离散的一阶微分算子,用来计算图像灰度函数的近似梯度。

在空间域上Sobel算子很容易实现,执行速度快,对部分噪声具有平滑作用,还能够提供较为精确的边缘方向信息,缺点是边缘定位精度不够高。边缘是指一个物体与另一个物体的分界处,一般边缘内外处都会有灰度值上的差异,Sobel算子就是通过像素点空间邻域内上下,左右相邻点的灰度加权运算,求取物体边缘。




对于待检测边缘的图像I,分别在水平(X)方向和垂直方向(Y)方向求导,方法是分别图像I与卷积核Gx和Gy进行卷积,公式表述如下:

                                                                  

之后对求得的水平和垂直方向的梯度图像上的每一点执行:


    

或更为简单粗暴的:

   

G即为Sobel求得的梯度图像。

求导数的近似值有时候kernel=3不是很准确,opencv还提供了改进版本的Scharr。

sobel(src,dst,输出图像深度,x方向几阶导数,y方向几阶导数,sobel的kernel,)


int main()
{
Mat src, dst, src_gray,xgrad, ygrad;
src = imread("1.jpg");
if (!src.data)
{
printf("could not load image");
return -1;
}


imshow("input image", src);



GaussianBlur(src,dst,Size(3,3),0,0);//1.高斯滤波
cvtColor(dst,src_gray,CV_BGR2GRAY);//2.转为灰度图像
imshow("gray image", src_gray);


Scharr(src_gray, xgrad, CV_16S, 1, 0);//这个感觉效果不是很好。。。
Scharr(src_gray, ygrad, CV_16S, 0, 1);


//Sobel(src_gray,xgrad,CV_16S,1,0,3);//定义算子
//Sobel(src_gray,ygrad,CV_16S, 1, 0, 3);
convertScaleAbs(xgrad,xgrad);//求绝对值,防止负数被0截断
convertScaleAbs(ygrad, ygrad);


imshow("xgrad", xgrad);
imshow("ygrad ", ygrad);
Mat xygrad=Mat(xgrad.size(),xgrad.type());
int width = xgrad.cols;
int height = ygrad.rows;
for (int row = 0; row < height;row++)
{
for (int col = 0; col < width; col++)
{
int xg = xgrad.at<uchar>(row, col);
int yg = ygrad.at<uchar>(row, col);
int xy = xg + yg;
xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xy);
}
}


//addWeighted(xgrad,0.5,ygrad,0.5,0,xygrad);
imshow("final", xygrad);


//imshow("output image", dst);
waitKey(0);
return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值