【学习笔记】OpenCV+C++(七)

 边缘处理
             卷积边缘问题
                图像卷积的时候边界像素,不能被卷积操作,原因在于边界像素没有完全跟kernel重叠,所以当3X3滤波时候有1个像素的边缘没有被处理,5X5滤波的时候有2个像素的边缘没有被处理。
             处理边缘
                在卷积开始之前增加边缘像素,填充的像素值为0或者RGB黑色,比如3x3在四周个填充1个像素的边缘,这样就确保图像的边缘被处理,在卷积处理之后再去掉这些边缘,openCV中默认的处理方法是:BORDER_DEFAULT,此外常用的还有如下几种:
                   BORDER_CONSTANT---填充边缘用指定像素值
                   BORDER_REPLICATE---填充边缘像素用已知的边缘像素值
                   BORDER_WRAP---用另外一边的像素来补偿填充

             API说明---给图像添加边缘API
                 copyMakeBorder(
                     Mat src,//输入图像
                     Mat dst,//添加边缘图像
                     int top,//边缘长度,一般上下左右都去相同值
                     int bottom,
                     int left,
                     int right,
                     int borderType,//边缘类型
                     Scalar value
                 )

代码演示:

#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
int main(int argc, char** argv) {
    Mat src, dst;
    src = imread("C:\\Users\\Lenovo\\Desktop\\毕业设计\\AAAAAAA\\1.jpg");
    if (!src.data) {
        printf("could not load image...\n");
        return -1;
    }
    char INPUT_WIN[] = "input image";
    char OUTPUT_WIN[] = "Border image";
    namedWindow(INPUT_WIN, CV_WINDOW_AUTOSIZE);
    namedWindow(OUTPUT_WIN,CV_WINDOW_AUTOSIZE);
    imshow(INPUT_WIN, dst);

    int top = (int)(0.05 * src.rows);
    int left = (int)(0.05 * src.cols);
    int right = (int)(0.05 * src.cols);
    int bottom =(int)(0.05 * src.rows);
    RNG rng(12345);
    int borderType = BORDER_DEFAULT;

    int c = 0;
    while (true) {
        c = waitKey(500);
        if ((char)c == 27) {//ESC退出
            break;
        }
        if ((char)c == 'r') {
            borderType = BORDER_REPLICATE;
        }
        else if ((char)c == 'w') {
            borderType = BORDER_WRAP;
        }
        else if ((char)c == 'c') {
            borderType = BORDER_CONSTANT;
        }
        else {
            borderType = BORDER_DEFAULT;
        }
        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_WIN, dst);
    }

    waitKey(0);
    return 0;
}

Sobel算子
   卷积应用---图像边缘提取
       边缘是什么--是像素值发生跃迁的地方,是图像显著特征之一,在图像特征提取、对象检测、模式识别等方面都有重要作用
       如何捕捉/提取边缘---对图像求它的一阶导数
       delta=f(x) - f(x-1) ,delta越大,说明像素在x方向变化越大,边缘信号越强。
    Sobel算子
       是离散微分算子(discrete differentiation operator),用来计算图像灰度的近似梯度
       Sobel算子功能集合高斯平滑和微分求导
       又被称为一阶微分算子,求导算子,在水平和垂直两个方向上求导,得到图像X方向与Y方向梯度图像
    拉普拉斯算子是二阶的求导算子
       水平梯度                 垂直梯度
          Gx = -1  0   1         Gy =  -1  -2  -1
               -2  0   2                0   0   0
               -1  0   1                1   2   1

            根据权重扩大差异

            最终图像梯度
               G  =   (Gx * Gx  +  Gy * Gy)的开平方
              实际上采用的近似算法: G = |Gx| + |Gy|
      Sobel算子
        求取倒数的近似值,kernel=3时不是很准确,使用改进版本的Scharr函数,算子如下:
                -3  0  3                  -3  -10  -3
            Gx =-10 0  10            Gy = 0    0    0
                -3  0  3                   3   10   3
        API说明cv::Sobel
            cv::Sobel(
                InputArray src,//输入图像
                OutputArray dst,//输出图像,大小与输入图像一致
                int depth,//输出图像深度
                int dx,//x方向,几阶导数
                int dy,//y方向,几阶导数
                int ksize,//Sobel算子kernel大小,必须是1,3,5,7
                double scale = 1,
                double delta = 0,
                int borderType = BORDER_DEFAULT
            )

            Input depth()   Output depth(ddepth)
            CV_8U            -1/CV_16S/CV_32F/CV_64F
            CV_16U/CV_16S    -1/CV_32F/CV_64F
            CV_32F           -1/CV_32F/CV_64F
            CV_64F           -1/CV_64F


        OpenCV改进版本的Scharr
            API说明cv::Scharr
              cv::Scharr(
                  InputArray src,  //输入图像
                  OutputArray dst, //输出图像,大小与输入图像一致
                  int depth,       //输出图像深度
                  int dx,          //x方向,几阶导数
                  int dy,          //Y方向,几阶导数
                  double scale = 1,
                  double delta = 0,
                  int borderType = BORDER_DEFAULT
              )

        缺点:
            对噪声敏感,容易受到影响

        高斯模糊降噪,平滑
        变成灰度图像
        求梯度 x 和  y
        混合 x y
        振幅图像


代码演示:

#include<opencv2/opencv.hpp>
            #include<iostream>
            #include<math.h>
            using namespace cv;
            int main(int argc,char** argv){
                Mat src,dst;
                src = imread("");
                if(!src.data){
                    printf("could not load image...\n");
                    return -1;
                }
                char INPUT_WIN[] = "Input image";
                char OUTPUT_WIN[] = "Output image";
                namedWindow(INPUT_WIN,CV_WINDOW_AUTOSIZE);
                imshow(INPUT_WIN,src);

                Mat gray_src;
                GaussianBlur(src,dst,Size(3,3),0,0);
                cvtColor(dst,gray_src,CV_BGR2GRAY);
                imshow("gray image",gray_src);

                Mat xgrad,ygrad;
                //原始版本
                //Sobel(gray_src,xgrad,CV_16S,1,0,3);
                //Sobel(gray_src,ygrad,CV_16S,0,1,3);

                //改进版本
                Scharr(gray_src,xgrad,CV_16S,1,0);
                Scharr(gray_src,ygrad,CV_16S,0,1);
                convertScaleAbs(xgrad,xgrad);
                convertScaleAbs(ygrad,ygrad);
                imshow("xgrad image",xgrad);
                imshow("ygrad image",ygrad);

                //第一种算法
                //Mat xygrad;
                //addWeighted(xgrad,0.5,ygrad,0.5,0.xygrad);
                //imshow("Final image",xygrad);

                //第二种算法,效果更好
                Mat xygrad = Mat(xgrad.size(),xgrad.type());
                int width = xgrad.cols;
                int height = xgrad.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);
                    }
                }

                waitKey(0);
                return 0;
            }

Laplance算子
        理论
            解释:在二阶导数的时候,最大变化的值为零,即边缘是零值。通过二阶导数计算,依据此理论我们可以计算图像二阶导数,提取边缘。

            Laplance算子:
              Laplance(f) = f(x)的二阶导 + f(y)的二阶导
              相关API  cv::Laplance

            处理流程:
               高斯模糊---去噪声(GaussianBlur())
               转换为灰度图像cvtColor()
               拉普拉斯---二阶导数计算(laplacian())
               取绝对值converScaleAbs()
               显示结果

 #include<opencv2/opencv.hpp>
            #include<iostream>
            #include<math.h>
            using namespace cv;
            int main(int argc,char** argv){
                Mat src,dst;
                src = imread();
                if(!src.data){
                    printf("could not load image\n");
                    return -1;
                }
                namedWindow("input image",CV_WINDOW_AUTOSIZE);
                imshow("input image",src);

                Mat gray_src,edg_image;
                GaussianBlur(src,dst,Size(3,3),0,0);
                cvtColor(dst,gray_src,CV_BGR2GRAY);
                Laplacian(gray_src,edg_image,CV_16S,3);
                convertScaleAbs(edg_image,edg_image);
                //图像二值化
                threshold(edg_image,edg_image,0,255,THRESH_OTSU|THRESH_BINARY);
                namedWindow("output image",CV_WINDOW_AUTOSIZE);
                imshow("output image",edg_image);



                waitKey(0);
                return 0;
            }

Canny边缘检测
       Canny算法介绍
           Canny是边缘检测算法,在1986年提出的
           是一个很好的边缘提取算法
           很常用也很实用的图像处理方法
         步骤:cv::Canny
            高斯模糊---GaussianBlur
            灰度转换---cvtColor
            计算梯度---Sobel/Scharr
            非最大信号抑制
            高低阈值输出二值图像
          非最大信号抑制:
             Gx = -1  0  1      Gy = -1  -2  -1
                  -2  0  2            0   0   0
                  -1  0  1            1   2   1
            G  = Gx * Gx + Gy * Gy 的平方根
            角度 = arctan (Gx/Gy)
            其中黄色区域的取值范围为0~22.5与157.5~180
            绿色区域取值范围22.5~67.5
            蓝色区域取值范围67.5~112.5
            红色区域取值范围112.5~157.5

            高低阈值输出二值图像
               T1,T2为阈值,凡是高于T2的都保留,凡是小于T1都丢弃,从高于T2的像素出发,凡是大于T1而且相互连接的都保留。最终得到一个输出二值图像

               推荐的高低阈值比值为T2:T1 = 3;1/2:1其中T2为高阈值,T1为低阈值
       APIcv::Canny()
            Canny(
                InputArray src,//8-bit的输入图像
                OutputArray edges,//输出边缘图像,一般都是二值图像,背景是黑色
                double threshold1,//低阈值,常取高阈值的1/2或者1/3
                double threshold2,//高阈值
                int aptertureSize,//Sobel算子的size,通常3x3取值3
                bool L2gradient//选择true表示L2来归一化,否则用L1归一化
            )

 #include<opencv2/opencv.hpp>
            #include<iostream>
            #include<math.h>
            using namespace cv;
            Mat src,dst,gray_src;
            int t1_value = 50;
            int max_value = 255;
            void Canny_Demo(int,void*);
            int main(int argc,char** argv){
                src = imread();
                if(!src.data){
                    printf("could not load image\n");
                    return -1;
                }
                namedWindow("input image",CV_WINDOW_AUTOSIZE);
                imshow("input image",src);

                cvtColor(src,gray_src,CV_BGR2GRAY);
                createTrackbar("Threshold Value","output image",&t1_value,max_value,Canny_Demo);
                Canny_Demo(0,0);
               
                waitKey(0);
                return 0;
            }
            void Canny_Demo(int,void*){
                Mat edge_output;
                blur(gray_src,gray_src,Size(3,3),Point(-1,-1),BORDER_DEFAULT);
                Canny(gray_src,edge_output,t1_value,t1_value * 2,3,false);
                dst.create(src.size(),src.type());
                src.copyTo(dst,edge_output);
                imshow("output image",dst);
            }

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值