opencv边缘检测

       提到的图像平滑,从信号处理的角度来看,实际上是一种“低通滤波器”,数字图像的边缘,通常都是像素值变化剧烈的区域 (“高频”),故可将边缘检测视为一种 “高通滤波器”。

mat

第一个参数是rows,该矩阵的行数;第二个参数是cols,该矩阵的列数;第三个参数是该矩阵元素的类型。这句话表示创建一个大小为240×320的矩阵,里面的元素为8位unsigned型,通道数(channel)有3个。

Sobel

    Sobel用于检测水平,垂直方向的边缘,Sobel算子并没有将图像的主体与背景严格地区分开来,即Sobel算子没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人满意。

void Sobel(InputArray src, OutputArray dst,int ddepth, int dx, int dy, int ksize=3, double scale=1, double delta=0,intborderType=BORDER_DEFAULT )

    参数       

       src –输入图像。

       dst –输出图像,与输入图像同样大小,拥有同样个数的通道。

       ddepth –

       输出图片深度;下面是输入图像支持深度和输出图像支持深度的关系:

                src.depth() = CV_8U, ddepth =-1/CV_16S/CV_32F/CV_64F

                src.depth() = CV_16U/CV_16S,ddepth = -1/CV_32F/CV_64F

                src.depth() = CV_32F, ddepth =-1/CV_32F/CV_64F

                src.depth() = CV_64F, ddepth =-1/CV_64F

       当 ddepth为-1时,输出图像将和输入图像有相同的深度。输入8位图像则会截取顶端的导数。

       xorder – x方向导数运算参数。

       yorder – y方向导数运算参数。

       ksize – Sobel内核的大小,可以是:1,3,5,7。

       scale –可选的缩放导数的比例常数。

       delta –可选的增量常数被叠加到导数中。

       borderType –用于判断图像边界的模式。

 convertScaleAbs

void convertScaleAbs(InputArray src,OutputArray dst, double alpha=1, double beta=0)

    参数       

   src –输入数组。

   dst –输出数组。

   alpha –可选缩放比例常数。

   beta –可选叠加到结果的常数。

addWeighted

opencv 通过 addWeighted 函数实现图片的线性融合。

cvAddWeighted( const CvArr* src1, doublealpha,  const CvArr* src2, doublebeta,  double gamma, CvArr* dst );

      src1  //第一个原数组.     

alpha //第一个数组元素的权值 

src2 //第二个原数组 

beta //第二个数组元素的权值 

dst //输出数组 

gamma //添加的常数项。 

函数 cvAddWeighted 计算两数组的加权值的和: 

dst(I)=src1(I)*alpha+src2(I)*beta+gamma

例子:

#include <iostream>

#include <stdio.h>

 

#include "opencv/cv.h"

#include "opencv2/objdetect.hpp"

#include "opencv2/highgui.hpp"

#include "opencv2/imgproc.hpp"

 

extern "C"

{

#include "libavcodec/avcodec.h"

#include "libavformat/avformat.h"

#include "libswscale/swscale.h"

#include "libavutil/avutil.h"

};

using namespace std;

using namespace cv;

int main(int argc, char **argv)

{

   Mat grad;

   char* window_name = "Sobel Demo - Simple Edge Detector";

   int scale = 1;

   int delta = 0;

   int ddepth = CV_16S;

    if(0)

    {

       Mat yuvImg;

       int width = 288;

       int height = 352;

       string pic = "/home/otvcloud/zf/code-blocks/getpic/1.yuv";

        FILE* fp = fopen(pic.c_str(),"rb");

       const int len = width*height;

       char * sbuf = new char[len];

       int nread = fread(sbuf,1,len ,fp);

       yuvImg.create(width,height, CV_8UC1);

       const int framesize = width * height;

       memcpy(yuvImg.data, sbuf, framesize*sizeof(unsigned char));

       Mat grad_x, grad_y;

       Mat abs_grad_x, abs_grad_y;

       Sobel( yuvImg, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );

       convertScaleAbs( grad_x, abs_grad_x );

       Sobel( yuvImg, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );

       convertScaleAbs( grad_y, abs_grad_y );

       // 准确的梯度:Mat norm,dir;  2 计算L2范数和方向, cartToPolar(sobelX,sobelY,norm,dir);

       addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );

       namedWindow( window_name, CV_WINDOW_AUTOSIZE );

       imshow( window_name, grad );

       waitKey(0);

    }else{

       string pic = "/home/otvcloud/zf/code-blocks/getpic/1.jpg";

       Mat src,src_gray;

       src = imread( pic.c_str());

       if( !src.data )

       {

           return -1;

       }

         //高斯模糊

       GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );

       //转成灰度图

       cvtColor( src, src_gray, CV_RGB2GRAY );

       namedWindow( window_name, CV_WINDOW_AUTOSIZE );

       Mat grad_x, grad_y;

       Mat abs_grad_x, abs_grad_y;

       //在 x 和 y 方向分别”求导“

       //Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT);

       Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT);

       convertScaleAbs( grad_x, abs_grad_x );

       //Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT);

        Sobel( src_gray, grad_y, ddepth, 0, 1, 3,scale, delta, BORDER_DEFAULT );

       convertScaleAbs( grad_y, abs_grad_y );

       //将两个方向的梯度相加来求取近似梯度。

       addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );

       cout << "M = " << endl << " "<< grad << endl << endl;

        imshow( window_name, grad );

       waitKey(0);

    }

   return 0;

}

 注:可以通过参数 -1 来设定输出图像的深度(数据类型)与原图像保持一致,但是在代码中使用的却是 cv2.CV_64F,这是为什么呢?想象一下一个从黑到白的边界的导数是整数,而一个从白到黑的边界点导数却是负数。如果原图像的深度是np.int8 时,所有的负值都会被截断变成 0,换句话说就是把边界丢失掉。所以如果这两种边界你都想检测到,最好的的办法就是将输出的数据类型设置的更高,比如 cv2.CV_16S, cv2.CV_64F 等。取绝对值然后再把它转回到 cv2.CV_8U。下面的示例演示了输出图片的深度不同造成的不同效果。

Scharr 卷积核

  当卷积核大小为 3x3 时,使用 sobel 卷积核来计算并不是很精确,此时常用 Scharr 卷积核来代替。

  对于 Scharr 函数,要求 dx 和 dy 都 >= 0 且 dx + dy ==1,假如 dx 和 dy 都设为 1,则会抛出异常。

  因此,对于 Sobel 和 Scharr 函数,通常各自求其 x 和 y 方向的导数,然后通过加权来进行边缘检测。

拉普拉斯算子 (Laplace)

  索贝尔算子 (Sobel) 和拉普拉斯算子(Laplace) 都是用来对图像进行边缘检测的,不同之处在于,前者是求一阶导,后者是求二阶导。

例子:

  在进行 Sobel,Laplacian 和 Canny 边缘检测之前,统一调用 GaussianBlur 来降低图像噪声。

#include"opencv2/imgproc/imgproc.hpp"

#include"opencv2/highgui/highgui.hpp"

 using namespace std;

using namespace cv;

 int main()

{

   Mat src, src_gray, dst;

    src = imread("E:/Edge/bird.jpg");

   if(src.empty())

       return -1;

    namedWindow("Original", CV_WINDOW_AUTOSIZE);

   namedWindow("Sobel", CV_WINDOW_AUTOSIZE);

   namedWindow("Laplace", CV_WINDOW_AUTOSIZE);

   namedWindow("Canny", CV_WINDOW_AUTOSIZE);

    imshow("Original", src);

    Mat grad_x, grad_y, abs_grad_x, abs_grad_y;

    GaussianBlur(src, src, Size(3,3),0);

   cvtColor(src,src_gray,COLOR_BGR2GRAY);

    Sobel(src_gray, grad_x,CV_16S,0,1);        // use CV_16S to avoid overflow

   convertScaleAbs( grad_x, abs_grad_x );

   Sobel(src_gray, grad_y,CV_16S,1,0);        // use CV_16S to avoid overflow

   convertScaleAbs( grad_y, abs_grad_y );

   addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst );

   imshow("Sobel", dst);

   imwrite("Sobel.jpg",dst);

    Laplacian(src_gray,dst,-1,3);

   imshow("Laplace", dst);

   imwrite("Laplace.jpg",dst);

    Canny(src_gray,dst,100,300);

   imshow("Canny",dst);

   imwrite("Canny.jpg",dst);

    waitKey(0);

}

Canny边缘检

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"

using namespace cv;
using namespace std;

Mat src,src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);

void thresh_callback(int, void* );

int main( int, char** argv )
{
  // 读图
  src = imread("Pillnitz.jpg", IMREAD_COLOR);
  if (src.empty())
      return -1;

  // 转化为灰度图
  cvtColor(src, src_gray, COLOR_BGR2GRAY );
  blur(src_gray, src_gray, Size(3,3) );
 
  // 显示
  namedWindow("Source", WINDOW_AUTOSIZE );
  imshow( "Source", src );

  // 滑动条
  createTrackbar("Canny thresh:", "Source", &thresh, max_thresh, thresh_callback );

  // 回调函数
  thresh_callback( 0, 0 );

  waitKey(0);
}

// 回调函数
void thresh_callback(int, void* )
{
  Mat canny_output;
  vector<vector<Point> > contours;
  vector<Vec4i> hierarchy;
 
  // canny 边缘检测
  Canny(src_gray, canny_output, thresh, thresh*2, 3);
 
  // 寻找轮廓
  findContours( canny_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );

  Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3);
 
  // 画出轮廓
  for( size_t i = 0; i< contours.size(); i++ ) {
      Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
      drawContours( drawing, contours, (int)i, color, 2, 8, hierarchy, 0, Point() );
  }

  namedWindow( "Contours", WINDOW_AUTOSIZE );
  imshow( "Contours", drawing );
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值