前言:

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_TEMP
int 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;
//对每个点 s
for ( 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边缘检测的更多相关文章

  1. 每天进步一点点------Sobel算子(1)

    void MySobel(IplImage* gray, IplImage* gradient) { /* Sobel template a00 a01 a02 a10 a11 a12 a20 a21 ...

  2. 彻底理解数字图像处理中的卷积-以Sobel算子为例

    彻底理解数字图像处理中的卷积-以Sobel算子为例 作者:FreeBlues 修订记录 2016.08.04 初稿完成 概述 卷积在信号处理领域有极其广泛的应用, 也有严格的物理和数学定义. 本文只讨 ...

  3. Canny边缘检测算法(基于OpenCV的Java实现)

    目录 Canny边缘检测算法(基于OpenCV的Java实现) 绪论 Canny边缘检测算法的发展历史 Canny边缘检测算法的处理流程 用高斯滤波器平滑图像 彩色RGB图像转换为灰度图像 一维,二维 ...

  4. 每天进步一点点------Sobel算子(3)基于彩色图像边缘差分的运动目标检测算法

    摘  要: 针对目前常用的运动目标提取易受到噪声影响.易出现阴影和误检漏检等情况,提出了一种基于Sobel算子的彩色边缘图像检测和帧差分相结合的检测方法.首先用Sobel算子提取视频流中连续4帧图像的 ...

  5. 【OpenCV新手教程之十二】OpenCV边缘检測:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/25560901 作者:毛星云(浅墨) ...

  6. [OpenCV入门教程之十二】OpenCV边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

    http://blog.csdn.net/poem_qianmo/article/details/25560901 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog ...

  7. 学习 opencv---(11)OpenC 边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器

    本篇文章中,我们将一起学习OpenCV中边缘检测的各种算子和滤波器——Canny算子,Sobel算子,Laplace算子以及Scharr滤波器.文章中包含了五个浅墨为大家准备的详细注释的博文配套源代码 ...

  8. EasyPR--开发详解(3)高斯模糊、灰度化和Sobel算子

    在上篇文章中我们了解了PlateLocate的过程中的所有步骤.在本篇文章中我们对前3个步骤,分别是高斯模糊.灰度化和Sobel算子进行分析. 一.高斯模糊 1.目标 对图像去噪,为边缘检测算法做准备 ...

  9. 图像边缘检测——Sobel算子

    边缘是图像最基本的特征,其在计算机视觉.图像分析等应用中起着重要的作用,这是因为图像的边缘包含了用于识别的有用信息,是图像分析和模式识别的主要特征提取手段. 1.何为“图像边缘”? 在图像中,“边缘” ...

随机推荐

  1. The Morning after Halloween uva1601

    这道题思路还是比较清晰的,建图加bfs或双向bfs,其实后者比前者少了将近一半的时间.. 建图可以把某一点所拥有邻接点长度(数目)记录在数组0这个位置,因为这道题使用vector会超时. #inclu ...

  2. c# SQL事务

    SQL事务执行 SqlTransaction   sqlTransaction   =   sqlConnection.BeginTransaction();    SqlCommand   sqlC ...

  3. Codeforces Round #412 (Div. 2)ABCD

    tourist的剧毒contest,题干长到让人不想做... A.看不太懂题意直接看下面input output note n组里有两数不一样的一组就rated 否则单调不增为maybe,否则unra ...

  4. Spring Boot学习总结(4)——使用Springloaded进行热部署

    我在开发的时候,总是会及时对自己的程序进行测试,总是频繁的重启web server,容器不烦我们都觉得烦了. dependencys目录下增加: <dependency> <grou ...

  5. ubuntu 关于sublime text3的一些应用

    安装 如今能够通过ppa的方法来安装sublime text3 了,个人感觉就是有有点儿慢,毕竟要update一下. sudo add-apt-repository ppa:webupd8team/s ...

  6. NJUPT JAVA语言 流处理程序设计

    一. 实验目的和要求 实验目的和要求:要求学生能在学习和理解课堂学习内容中JAVA流编程理论的基础上,学习并逐步掌握JAVA流程序的编写和调试,学习依据处理需求对不同流的正确选择使用和组合用法. 实验 ...

  7. leetcode第一刷_Convert Sorted Array to Binary Search Tree

    晕.竟然另一样的一道题.换成sorted array的话.找到中间位置更加方便了. TreeNode *sortTree(vector<int> &num, int start, ...

  8. HDOJ 2196 Computer 树的直径

    由树的直径定义可得,树上随意一点到树的直径上的两个端点之中的一个的距离是最长的... 三遍BFS求树的直径并预处理距离....... Computer Time Limit: 1000/1000 MS ...

  9. vijos - P1279Leave-绿光(数学归纳法 + python)

    P1279Leave-绿光 Accepted 标签:[显示标签] 背景 期待这一份幸运,和一份冲劲,多么奇异的际遇--. 燕姿在演唱完绿光这首歌后,出给了姿迷一个考题. 北欧有一个传说! 人一生中能看 ...

  10. Fiddler手机抓包工具设置过滤域名

    需求:我想用fiddler抓包只抓test.sis.1course.cn; pre.schoolis.cn; sistest02.schoolis.cn;这几个域名下的请求 设置步骤:https:// ...