Sobel算子取代:基于特定点方向的canny边缘检测
前言:
Canny边缘检测使用了Sobel算子,计算dx和dy两个方向,对于特定方向的边缘检测,可以作少量修改。
代码:
计算特定方向上的边缘
void CannyOrient( cv::Mat &_src, cv::Mat &_dst,cv::Point2f &seed,double low_thresh, double high_thresh,int aperture_size, bool L2gradient ){low_thresh = 0; high_thresh = 1;//使用特定种子方向上的点寻找方向梯度FindBestGradient( _src, _dst,seed,aperture_size, L2gradient );//二值化,生成边缘图像//计算直方图,寻找极大极小边缘点,根据波峰和波谷int hh[256];memset(hh,0,256*sizeof(int ) );int minV = 255;int maxV = 0;cvWish::CalHist(_dst,1,minV,maxV,hh);cv::Mat canvas( 600 ,256*4, CV_8U, 1);for(int i=0;i< 256; ++i){int Pos = ( (int)(hh[i]) ) %( canvas.rows);canvas.at<uchar>( canvas.rows - Pos-1,i*4 ) =255;}cv::imshow("canvas",canvas);cv::waitKey(1);return;}
寻找特定边缘:
//在种子点方向上寻找合适的梯度,用于寻找边缘void FindBestGradient( cv::Mat &_src, cv::Mat &_dst,cv::Point2f &seed,int aperture_size, bool L2gradient ){//角度矩阵cv::Mat df = cv::Mat::zeros( _src.rows,_src.cols, CV_32FC1 );//梯度矩阵cv::Mat dg = cv::Mat::zeros( _src.rows,_src.cols, CV_32FC1 );//原始图像cv::Mat ds = _src.clone();//目标图像 uchar型cv::Mat dd = _src.clone();//1.根据角度计算梯度//得到梯度矩阵//使用N*1的算子int n = aperture_size;//必须为奇数//对每个柱进行初始化//搜索柱:在射线方向上搜索l_Search 个像素;宽度为 int l_Search = n;int w_Search = 1;std::vector<std::vector<std::pair<cv::Point ,float> > > beam;beam.resize( l_Search );for (int i=0;i< beam.size();++i){beam[i].resize(w_Search);}//初始化柱//设定系数//生成模板double gap = 2.0/ (n-1);std::vector< double > mask(l_Search);for (int i=0;i< mask.size();++i){mask[i] = -1 + i*gap ;//std::cout<< " mask[i]:" << mask[i] ;}//2.生成角度图像//在射线方向上寻找//方法不是太好,但是没有寻找到简单有效的方法for ( int y=0 ;y< ds.rows;++y ){float* ptr = (float*)( df.data + y * df.step);for ( int x=0; x< ds.cols; ++x ){//计算角度//float ag = (float)(cvWish::cosCv(seed,cv::Point2f( x,y ) ) );//df.at<float>(y ,x) = (float)(cvWish::cosCv(seed,cv::Point2f( x,y ) ) );*ptr = (float)(cvWish::cosCv(seed,cv::Point2f( x,y ) ) );++ptr;}}//计算差值-导数for (int y=0 ;y< ds.rows;++y){float* pf = (float*)( df.data + y * df.step);float* pg = (float*)( dg.data + y * dg.step);unsigned char* pd = (unsigned char*)( dd.data + y * dd.step);for (int x=0;x< ds.cols;++x ){//计算角度//cvWish::BeamInit(l_Search,w_Search,cv::Point2f( x,y ),df.at<float >(y,x),beam,0);//0表示从中部开始搜索cvWish::BeamInit(l_Search,w_Search,cv::Point2f( x,y ), *pf ,beam,0);//0表示从中部开始搜索cvWish::BeamNormal(dg.cols, dg.rows , beam);#ifdef SHOW_TEMPint ii =0;for (;ii<beam.size() ;++ii){int j=0;for (;j<beam[ii].size() ;++j){//canvasSrc.at<cv::Vec3b>(beam[ii][j].first.y ,beam[ii][j].first.x )[0] =255 ;//canvasSrc.at<cv::Vec3b>(beam[ii][j].first.y ,beam[ii][j].first.x )[1] =0 ;//canvasSrc.at<cv::Vec3b>(beam[ii][j].first.y ,beam[ii][j].first.x )[2] =0 ;} }//cv::imshow("edgeEvolution",canvasSrc);//cv::waitKey(1);
#endif//dg.at<float >(y,x)= 0;//for ( int k =0; k< l_Search; ++k ){// dg.at<float >(y,x) += (float)( mask[k]* ds.at<unsigned char>(beam[k][0].first.y,beam[k][0].first.x) );//}//int s = abs ( ( (int)(dg.at<float >(y,x) ) )%255 ) ;//dd.at<unsigned char >( y, x ) =(unsigned char) (s);*pg = 0;for ( int k =0; k< l_Search; ++k ){*pg += (float)( mask[k]* ds.at<unsigned char>(beam[k][0].first.y,beam[k][0].first.x) );}int s = abs ( ( (int)(*pg ) )%255 ) ;*pd = (unsigned char) (s);++pf;++pg;++pd;}}ds.copyTo(_dst);return;}
辅助代码:
//功能: 初始化任意角度的一个方柱,大小已经确定:l_Search*w_Search//沿射线方向 寻找 一个柱//默认 参数 0 从中部开始//参数 1代表 从底部开始;参数 2代表从top开始void BeamInit( const int l_Search, const int w_Search,const cv::Point2f &pc, const float angle,std::vector<std::vector<std::pair<cv::Point ,float> > > &beam,const int bottomOrTop ){assert (l_Search%2 >0);//确定是奇数assert (w_Search%2 >0);assert ( beam.size() == l_Search);//不改变大小assert ( beam[0].size() == w_Search);//往角度方向延长cv::Point2f ps(0,0);const float angleVert = angle+ PI_1_2 < PI_4_2? ( angle+ PI_1_2): ( angle+ PI_1_2)- PI_4_2;cv::Point2f pIdx(0,0);switch (bottomOrTop){case 0://往底部移动ps.y = pc.y-(0- sin(angle)*l_Search/2);ps.x = pc.x-(0- cos(angle)*l_Search/2);//往左边移动ps.y -= 0- sin(angleVert)*w_Search/2;ps.x -= 0- cos(angleVert)*w_Search/2;//对每个点计算for (int i=0;i< beam.size();++i ){pIdx.y = ps.y +(0- sin(angle)*i);pIdx.x = ps.x +(0- cos(angle)*i);for (int j=0;j< beam[i].size();++j ){beam[i][j].first.y = pIdx.y +(0- sin(angleVert)*j);beam[i][j].first.x = pIdx.x +(0- cos(angleVert)*j);}}//int iwCenter = w_Search>>1;//int ilCenter = l_Search>>1;先算十字中间//for ( int i=0; i< beam.size(); ++i )//{// beam[i][iwCenter].first.x = 0- cos(angle)*i;;//}break;case 1://往底部移动//默认底部,因此不需要移动ps.y = pc.y;//-(0- sin(angle)*l_Search/2);ps.x = pc.x;//-(0- cos(angle)*l_Search/2);//往左边移动ps.y -= 0- sin(angleVert)*w_Search/2;ps.x -= 0- cos(angleVert)*w_Search/2;//对每个点计算for (int i=0;i< beam.size();++i ){pIdx.y = ps.y +(0+ sin(angle)*i);pIdx.x = ps.x +(0+ cos(angle)*i);for (int j=0;j< beam[i].size();++j ){beam[i][j].first.y = pIdx.y +(0- sin(angleVert)*j);beam[i][j].first.x = pIdx.x +(0- cos(angleVert)*j);}}break;case 2://往底部移动//默认顶部,因此需要移动到底ps.y = pc.y -(0- sin(angle)*l_Search);ps.x = pc.x -(0- cos(angle)*l_Search);//往左边移动ps.y -= 0- sin(angleVert)*w_Search/2;ps.x -= 0- cos(angleVert)*w_Search/2;//对每个点计算for (int i=0;i< beam.size();++i ){pIdx.y = ps.y +(0- sin(angle)*i);pIdx.x = ps.x +(0- cos(angle)*i);for (int j=0;j< beam[i].size();++j ){beam[i][j].first.y = pIdx.y +(0- sin(angleVert)*j);beam[i][j].first.x = pIdx.x +(0- cos(angleVert)*j);}}break;default://和case 0 相同,默认设定为中间位置//往底部移动ps.y = pc.y-(0- sin(angle)*l_Search/2);ps.x = pc.x-(0- cos(angle)*l_Search/2);//往左边移动ps.y -= 0- sin(angleVert)*w_Search/2;ps.x -= 0- cos(angleVert)*w_Search/2;//对每个点计算for (int i=0;i< beam.size();++i ){pIdx.y = ps.y +(0- sin(angle)*i);pIdx.x = ps.x +(0- cos(angle)*i);for (int j=0;j< beam[i].size();++j ){beam[i][j].first.y = pIdx.y +(0- sin(angleVert)*j);beam[i][j].first.x = pIdx.x +(0- cos(angleVert)*j);}}break;}return;}
//对柱规整,使其不超出边界void BeamNormal( const int width ,const int height ,std::vector<std::vector<std::pair<cv::Point ,float> > > &beam){int w = width -1;int h = height -1;//对每个点 sfor ( int i=0;i< beam.size();++i ){for (int j=0;j< beam[i].size();++j ){if (beam[i][j].first.x < 0 ){beam[i][j].first.x = 0;}if (beam[i][j].first.y < 0 ){beam[i][j].first.y = 0;}if (beam[i][j].first.x >w ){beam[i][j].first.x = w;}if (beam[i][j].first.y > h ){beam[i][j].first.y = h;}}}return;}
代码效果:
Sobel算子取代:基于特定点方向的canny边缘检测相关推荐
- candy算子python_Python-计算机视觉中的Canny边缘检测方法
今天的想法是用Canny边缘检测算法,建立一种可以勾画出图像上任何物体的边缘的算法. 首先,我们来描述一下Canny边缘检测器:Canny边缘检测算子是一种边缘检测算子,它采用多级算法检测图像中广泛的 ...
- OpenCV_08 边缘检测:Sobel检测算子+Laplacian算子+Canny边缘检测
1 原理 边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点.图像属性中的显著变化通常反映了属性的重要事件和变化.边缘的表现形式如下图所示: 图像边缘检测大幅度 ...
- 差分近似图像导数算子之Sobel算子
背景引言 图像处理中,一个最基本并且最重要的卷积就是导数的计算,一般用来表达微分最常用的操作是Sobel算子,可以包含任意阶的微分以及融合偏导.主要用作为边缘检测.在技术上,它是一离散性差分算子,用来 ...
- 【CV】Sobel算子简介
引言 Sobel算子是一种常用的边缘检测算法,是一种离散性差分算子,用差分近似代替梯度.对x求1阶差分用来检测竖直边缘,同样的对y求1阶差分用来检测水平边缘. sobel算子对垂直和水平方向上的排列表 ...
- sobel算子_OpenCV图像处理专栏十八 | 手动构造Sobel算子完成边缘检测
1. 前言 众所周知,在传统的图像边缘检测算法中,最常用的一种算法是利用Sobel算子完成的.Sobel算子一共有 个,一个是检测水平边缘的算子,另一个是检测垂直边缘的算子. 2. Sobel算子优缺 ...
- OpenCV-Python教程(6)(7)(8): Sobel算子 Laplacian算子 Canny边缘检测
OpenCV-Python教程(6.Sobel算子) 本篇文章介绍如何用OpenCV-Python来使用Sobel算子. 提示: 转载请详细注明原作者及出处,谢谢! 本文介绍使用OpenCV-Pyth ...
- opencv-python图像处理 ----图像梯度、Sobel算子
一.图像的梯度处理 1.Sobel算子 梯度可以按照x方向或者y方向求梯度,其实就是在看像素点的差异变化情况,比如黑白物体的交界,其像素值变化差异是非常大的. 求梯度计算使用的函数就叫做Sobel算子 ...
- OpenCV 边缘检测之Sobel算子
文章目录 Sobel算子(索贝尔算子) Sobel算子定义: Sobel算子作用: Sobel卷积因子 Sobel卷积计算公式: 相关API Sobel() convertScaleAbs() add ...
- 应用sobel算子算法c语言,Canny算子与Sobel算子求图像边缘的C代码实现
*Canny算子与Sobel算子求图像边缘笔记* 1.Canny求边缘算法原理简述 Canny检测边缘主要分为以下 四个算法步骤: A:噪声去除 canny算子是通过对每个像素点求一阶导数来找到梯度明 ...
最新文章
- xlrd,xlwt模塊
- boost::math::quadrature::tanh_sinh用法的测试程序
- 江苏电信:SOC建设介绍
- Google CVPR 2019最新成果!用神经架构搜索实现更好的目标检测
- java两种不同单例模式_关于Java里的两种单例模式
- ASP.NET前台table通过Ajax获取绑定后台查询的json数据
- java中输出5个数_编写一个程序,要求用户输入5个数字,并输出这些数字中最大的数字和这些数字中最小的数字...
- 月薪14.5K...转行测试还是考公考研?律师小哥是这样选择的...
- .net 怎么在控制器action中返回一个试图_ASP.NET Core MVC/WebAPI中另辟蹊径的全局统一异常处理方式...
- 神泣服务器维护公告,《神泣》官方网站—创天互娱
- Cherno OpenGL 教程
- java方法建议不超过多少行,Java方法不应超过15行
- 中职学校计算机课听课记录表,中职听课记录
- Kubernetes 安全容器技术 kata gvisor
- 2017春节~人生智慧箴言
- 拉链表断链、交叉链判断及处理方式
- 塑源码是什么_源码是什么意思啊
- 14款超时尚的HTML5时钟动画
- 转:SWOT分析法与职业生涯规划
- 我不敢再哭了,因为我怕自己成为职场上的杨超越
热门文章
- 开发者论坛一周精粹(第二十期) :晒往期云栖大会的照片或感想,赢2017杭州云栖大会门票...
- 去广告,原来可以如此简单——ADSafe 3.5.4.520 精简版
- 怎样在Ubuntu系统安装可用的QQ
- 2/2 pymysql:基础操作总结
- 用 chown 和 chmod 修改目录所属用户及权限
- nginx proxy_pass末尾神奇的/
- CNN-2: AlexNet 卷积神经网络模型
- 网络编程—网络基础概览、socket,TCP/UDP协议
- MySql 事务+异常处理+异常抛出
- EJS脚本中AES应用