OpenCV2马拉松第17圈——边缘检測(Canny边缘检測)
收入囊中
- 利用OpenCV Canny函数进行边缘检測
- 掌握Canny算法基本理论
- 分享Java的实现




这就是非极大值抑制的工作原理。
- ////////////////////第一种情况///////////////////////
- ///////// g1 g2 /////////////
- ///////// C /////////////
- ///////// g3 g4 /////////////
- /////////////////////////////////////////////////////
- ////////////////////另外一种情况///////////////////////
- ///////// g1 /////////////
- ///////// g2 C g3 /////////////
- ///////// g4 /////////////
- /////////////////////////////////////////////////////
- ////////////////////第三种情况///////////////////////
- ///////// g1 g2 /////////////
- ///////// C /////////////
- ///////// g4 g3 /////////////
- /////////////////////////////////////////////////////
- ////////////////////第四种情况///////////////////////
- ///////// g1 /////////////
- ///////// g4 C g2 /////////////
- ///////// g3 /////////////
- /////////////////////////////////////////////////////
在上个步骤中,产生了梯度幅值, (upper and lower):
假设一个像素点的梯度值大于upper,则是边界
假设一个像素点的梯度值小于lower,则不是边界
假设介于两者之间,仅当这个点和边界点连通才会被觉得是边界点
依据高阈值得到一个边缘图像,这样一个图像含有非常少的假边缘。可是因为阈值较高。产生的图像边缘可能不闭合。为解决这样一个问题採用了另外一个低阈值。在高阈值图像中把边缘链接成轮廓,当到达轮廓的端点时。该算法会在断点的8邻域点中寻找满足低阈值的点。再依据此点收集新的边缘,直到整个图像边缘闭合。
Canny推荐upper:lower 的比例为 2:1 或者 3:1.
小提示:由于,所以最高阀值不是255而是360!还有处于[lower,upper]的点检測是否是边界肯定要放在最后一步。也就是[0,lower),(upper,360]的点都处理完再处理。这样才干判连通!
-
C++: void Canny(InputArray image,
OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false )
-
- image – 单通道8比特图像
- edges – 输出图像。和src有同样的大小类型
- threshold1 – 低阀值
- threshold2 – 高阀值
- apertureSize – Sobel算子的大小
- L2gradient – 一个标识位,一旦设置就启用更准确的
norm
去计算图像梯度,默认使用
norm
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream> using namespace cv; /// Global variables
Mat src, src_gray;
Mat dst, detected_edges; int lowThreshold;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
const char* window_name = "Edge Map"; static void CannyThreshold(int, void*)
{
blur( src_gray, detected_edges, Size(3,3) );
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
dst = Scalar::all(0);
//detected_edges是mask,仅仅有detected_edges被置上(边缘)。才会从原始彩色图像copy到dot中,所以展示的是彩色边缘
src.copyTo( dst, detected_edges);
imshow( window_name, dst );
} int main( int, char** argv )
{
src = imread( argv[1] );
dst.create( src.size(), src.type() );
cvtColor( src, src_gray, CV_BGR2GRAY ); namedWindow( window_name, CV_WINDOW_AUTOSIZE );
createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold ); CannyThreshold(0, 0); waitKey(0);
return 0;
}
效果图:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWJjZDE5OTI3MTln/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" width="454" height="528" alt="">
C的实现http://blog.csdn.net/likezhaobin/article/details/6892629
public int[][] CannyEdgeDetect(int[][] oldmat)
{ int[][] tempI = gaussFilter(oldmat); //////////////////Õ¨—˘ø…“‘”√≤ªÕ¨µƒºÏ≤‚∆˜/////////////////////////
///// P[i,j]=(S[i,j+1]-S[i,j]+S[i+1,j+1]-S[i+1,j])/2 /////
///// Q[i,j]=(S[i,j]-S[i+1,j]+S[i,j+1]-S[i+1,j+1])/2 /////
/////////////////////////////////////////////////////////////////
float[][] P = new float[height][width]; //xœÚ∆´µº ˝
float[][] Q = new float[height][width]; //yœÚ∆´µº ˝
int[][] M = new int[height][width]; //Û∂»∑˘÷µ
float[][] Theta = new float[height][width]; //Û∂»∑ΩœÚ
//º∆À„x,y∑ΩœÚµƒ∆´µº ˝
for(int i=0; i<(width-1); i++)
{
for(int j=0; j<(height-1); j++)
{
P[j][i] = (float)(tempI[j][Math.min(i+1, width-1)] - tempI[j][i] + tempI[Math.min(j+1, height-1)][Math.min(i+1, width-1)] - tempI[Math.min(j+1, height-1)][i])/2;
Q[j][i] = (float)(tempI[j][i] - tempI[Math.min(j+1, height-1)][i] + tempI[j][Math.min(i+1, width-1)] - tempI[Math.min(j+1, height-1)][Math.min(i+1, width-1)])/2; }
}
//º∆À„Û∂»∑˘÷µ∫ÕÛ∂»µƒ∑ΩœÚ
for(int i=0; i<width; i++)
{
for(int j=0; j<height; j++)
{
M[j][i] = (int)(Math.sqrt(P[j][i]*P[j][i] + Q[j][i]*Q[j][i])+0.5);
//System.out.println(M[j][i]);
Theta[j][i] = (float) (Math.atan2(Q[j][i], P[j][i]) * 57.3);
if(Theta[j][i] < 0)
Theta[j][i] += 360; //Ω´’‚∏ˆΩ«∂»◊™ªªµΩ0~360∑∂Œß
}
} int[][] N = new int[height][width]; //∑«º´¥Û÷µ“÷÷∆Ω·π˚
int g1=0, g2=0, g3=0, g4=0; //”√”⁄Ω¯––≤Â÷µ£¨µ√µΩ—«œÒÀÿµ„◊¯±Í÷µ
double dTmp1=0.0, dTmp2=0.0; //±£¥Ê¡Ω∏ˆ—«œÒÀÿµ„≤Â÷µµ√µΩµƒª“∂» ˝æ›
double dWeight=0.0; //≤Â÷µµƒ»®÷ÿ //±flΩÁ≥ı ºªØ
for(int i=0; i<width; i++)
{
N[0][i] = 0;
N[height-1][i] = 0;
}
for(int j=0; j<height; j++)
{
N[j][0] = 0;
N[j][width-1] = 0;
} //Ω¯––æ÷≤ø◊Ó¥Û÷µ—∞’“
for(int i=1; i<(width-1); i++)
{
for(int j=1; j<(height-1); j++)
{
if(M[j][i] == 0)
N[j][i] = 0; //»Áπ˚µ±«∞Û∂»∑˘÷µŒ™0£¨‘Ú≤ª «æ÷≤ø◊Ó¥Û∂‘∏√µ„∏≥Œ™0
else
{
//////// ◊œ»≈–∂œ Ù”⁄ƒ«÷÷«Èøˆ£¨»ª∫Û∏˘æ›«Èøˆ≤Â÷µ///////
////////////////////µ⁄“ª÷÷«Èøˆ///////////////////////
///////// g1 g2 /////////////
///////// C /////////////
///////// g3 g4 /////////////
/////////////////////////////////////////////////////
if( ((Theta[j][i]>=90)&&(Theta[j][i]<135)) ||
((Theta[j][i]>=270)&&(Theta[j][i]<315)))
{
//////∏˘æ›–±¬ ∫ÕÀƒ∏ˆ÷–º‰÷µΩ¯––≤Â÷µ«ÛΩ‚
g1 = M[j-1][i-1];
g2 = M[j-1][i];
g3 = M[j+1][i];
g4 = M[j+1][i+1];
dWeight = Math.abs(P[j][i])/Math.abs(Q[j][i]); //∑¥’˝«–
dTmp1 = g1*dWeight+g2*(1-dWeight);
dTmp2 = g4*dWeight+g3*(1-dWeight);
}
////////////////////µ⁄∂˛÷÷«Èøˆ///////////////////////
///////// g1 /////////////
///////// g2 C g3 /////////////
///////// g4 /////////////
/////////////////////////////////////////////////////
else if( ((Theta[j][i]>=135)&&(Theta[j][i]<180)) ||
((Theta[j][i]>=315)&&(Theta[j][i]<360)))
{
g1 = M[j-1][i-1];
g2 = M[j][i-1];
g3 = M[j][i+1];
g4 = M[j+1][i+1];
dWeight = Math.abs(Q[j][i])/Math.abs(P[j][i]); //’˝«–
dTmp1 = g2*dWeight+g1*(1-dWeight);
dTmp2 = g4*dWeight+g3*(1-dWeight);
}
////////////////////µ⁄»˝÷÷«Èøˆ///////////////////////
///////// g1 g2 /////////////
///////// C /////////////
///////// g4 g3 /////////////
/////////////////////////////////////////////////////
else if( ((Theta[j][i]>=45)&&(Theta[j][i]<90)) ||
((Theta[j][i]>=225)&&(Theta[j][i]<270)))
{
g1 = M[j-1][i];
g2 = M[j-1][i+1];
g3 = M[j+1][i];
g4 = M[j+1][i-1];
dWeight = Math.abs(P[j][i])/Math.abs(Q[j][i]); //∑¥’˝«–
dTmp1 = g2*dWeight+g1*(1-dWeight);
dTmp2 = g3*dWeight+g4*(1-dWeight);
}
////////////////////µ⁄Àƒ÷÷«Èøˆ///////////////////////
///////// g1 /////////////
///////// g4 C g2 /////////////
///////// g3 /////////////
/////////////////////////////////////////////////////
else if( ((Theta[j][i]>=0)&&(Theta[j][i]<45)) ||
((Theta[j][i]>=180)&&(Theta[j][i]<225)))
{
g1 = M[j-1][i+1];
g2 = M[j][i+1];
g3 = M[j+1][i-1];
g4 = M[j][i-1];
dWeight = Math.abs(Q[j][i])/Math.abs(P[j][i]); //’˝«–
dTmp1 = g1*dWeight+g2*(1-dWeight);
dTmp2 = g3*dWeight+g4*(1-dWeight);
}
}
//////////Ω¯––æ÷≤ø◊Ó¥Û÷µ≈–∂œ£¨≤¢–¥»ÎºÏ≤‚Ω·π˚////////////////
if((M[j][i]>=dTmp1) && (M[j][i]>=dTmp2))
N[j][i] = 128;
else
N[j][i] = 0; //System.out.println(N[j][i]);
}
} //À´∑ß÷µºÏ≤‚ µœ÷
int []nHist = new int[1024];
int nEdgeNum; //ø…ƒ‹±flΩÁ ˝
int nMaxMag = 0; //◊Ó¥ÛÛ∂» ˝
int nHighCount; //Õ≥º∆÷±∑ΩÕº
for(int i=0;i<1024;i++)
nHist[i] = 0;
for(int i=0; i<width; i++)
{
for(int j=0; j<height; j++)
{
if(N[j][i]==128)
nHist[M[j][i]]++;
}
} //ªÒ»°◊Ó¥ÛÛ∂»∑˘÷µº∞«±‘⁄±fl‘µµ„∏ˆ ˝
nEdgeNum = nHist[0];
nMaxMag = 0; //ªÒ»°◊Ó¥ÛµƒÃ›∂»÷µ
for(int i=1; i<1024; i++) //Õ≥º∆æ≠π˝°∞∑«◊Ó¥Û÷µ“÷÷∆°±∫Û”–∂‡…ŸœÒÀÿ
{
if(nHist[i] != 0) //Û∂»Œ™0µƒµ„ «≤ªø…ƒ‹Œ™±flΩÁµ„µƒ
{
nMaxMag = i;
}
nEdgeNum += nHist[i]; //æ≠π˝non-maximum suppression∫Û”–∂‡…ŸœÒÀÿ
} //º∆À„¡Ω∏ˆ∑ß÷µ
float dRatHigh = 0.79f;
float dThrHigh;
float dThrLow;
float dRatLow = 0.5f;
nHighCount = (int)(dRatHigh * nEdgeNum + 0.5);
int k = 1;
nEdgeNum = nHist[1];
while((k<(nMaxMag-1)) && (nEdgeNum < nHighCount))
{
k++;
nEdgeNum += nHist[k];
}
dThrHigh = k; //∏fl„–÷µ
dThrLow = (int)((dThrHigh) * dRatLow + 0.5); //µÕ„–÷µ
System.out.println("high = " + dThrHigh + "low = " + dThrLow);
//dThrHigh = 10;
//dThrLow = 5; //Ω¯––±fl‘µºÏ≤‚
int cx = width;
int cy = height;
for(int i=0; i<width; i++)
{
for(int j=0; j<height; j++)
{
if((N[j][i]==128) && (M[j][i] >= dThrHigh))
{
N[j][i] = 255;
TraceEdge(j, i, (int)(dThrLow+0.5), N, M);
}
}
} //Ω´ªπ√ª”–…Ë÷√Œ™±flΩÁµƒµ„…Ë÷√Œ™∑«±flΩÁµ„
for(int i=0; i<width; i++)
{
for(int j=0; j<height; j++)
{
if(N[j][i] != 255)
{
N[j][i] = 0 ; // …Ë÷√Œ™∑«±flΩÁµ„
}
N[j][i] = N[j][i] + (N[j][i] << 8) + (N[j][i] << 16);
}
} return N; } /*
* cancy ∏®÷˙∫Ø ˝
*/
private void TraceEdge(int y, int x, int nThrLow, int[][] pResult, int[][] pMag)
{
//∂‘8¡⁄”ÚœÒÀÿΩ¯––≤È—Ø
int[] xNum = {1,1,0,-1,-1,-1,0,1};
int[] yNum = {0,1,1,1,0,-1,-1,-1};
int yy,xx,k;
for(k=0;k<8;k++)
{
yy = y+yNum[k];
xx = x+xNum[k];
if(pResult[yy][xx]==128 && pMag[yy][xx]>=nThrLow )
{
//∏√µ„…ËŒ™±flΩÁµ„
pResult[yy][xx] = 255;
//“‘∏√µ„Œ™÷––ƒ‘ŸΩ¯––∏˙◊Ÿ
TraceEdge(yy,xx,nThrLow,pResult,pMag);
}
}
}
OpenCV2马拉松第17圈——边缘检測(Canny边缘检測)的更多相关文章
- OpenCV2马拉松第15圈——边缘检測(Laplace算子,LOG算子)
收入囊中 拉普拉斯算子 LOG算子(高斯拉普拉斯算子) OpenCV Laplacian函数 构建自己的拉普拉斯算子 利用拉普拉斯算子进行图像的锐化 葵花宝典 在OpenCV2马拉松第14圈--边缘检 ...
- OpenCV2马拉松第14圈——边缘检測(Sobel,prewitt,roberts)
收入囊中 差分在边缘检測的角色 Sobel算子 OpenCV sobel函数 OpenCV Scharr函数 prewitt算子 Roberts算子 葵花宝典 差分在边缘检測究竟有什么用呢?先看以下的 ...
- openCV2马拉松第19圈——Harris角点检測(自己实现)
计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/26824529 收入囊中 使用OpenCV的con ...
- OpenCV2马拉松第22圈——Hough变换直线检測原理与实现
计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/27220445 收入囊中 Hough变换 概率Ho ...
- OpenCV2马拉松第24圈——轮廓提取
计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/27979267 收入囊中 在图片中找到轮廓而且描绘 ...
- OpenCV2马拉松第5圈——线性滤波
收入囊中 这里的非常多内容事实上在我的Computer Vision: Algorithms and ApplicationsのImage processing中都有讲过 相关和卷积工作原理 边界处理 ...
- OpenCV2马拉松第13圈——模版匹配
收入囊中 在http://blog.csdn.net/abcd1992719g/article/details/25505315这里,我们已经学习了怎样利用反向投影和meanshift算法来在图像中查 ...
- OpenCV2马拉松第2圈——读写图片
收入囊中 用imread读取图片 用nameWindow和imshow展示图片 cvtColor彩色图像灰度化 imwrite写图像 Luv色彩空间转换 初识API 图像读取接口 image = im ...
- OpenCV2马拉松第10圈——直方图反向投影(back project)
收入囊中 灰度图像的反向投影 彩色图像的反向投影 利用反向投影做object detect 葵花宝典 什么是反向投影?事实上没有那么高大上! 在上一篇博文学到,图像能够获得自己的灰度直方图. 反向投影 ...
随机推荐
- 两个input可能会用到的小方法
1.一个普通的input元素,在不被 form包裹的时候,如何跳转或搜索 var oInput = document.getElementsByTagName('input')[0]; oInput. ...
- opencv边缘滤波
2018-03-0422:16:11 import cv2 as cv import numpy as np def bi_demo (image): print ("ceshi" ...
- V形
<!doctype html><html><head><meta charset="utf-8"><title>无标题文 ...
- PHP开发心得三
1, JSON在调用json_decode前要检查其中是否含有反斜杠“\”等特殊字符 比如下面这段代码,返回的就是空值,非常坑爹吧. $res = {"Ret":"1&q ...
- 调度kettle使用taskctl我该怎么部署
转载自: http://www.taskctl.com/forum/detail_133.html 最近在QQ群看到有小伙伴在问用taskctl调度kettle,都要安装些什么呢?都支持哪些平台上的k ...
- jQuery,遍历表格每个单元格数据。
<table class="table table-hover table-bordered"> <thead> <tr> <th > ...
- CAD控件:网上打开dwg文件时,对dwg文件路径加密的功能
梦想CAD控件2015.03.12最新更新 1. 增加控件状态栏文字,自定义功能, C++接口为 : CStatusBarInformationReactor::CreatePro ...
- 代码静态分析工具-splint的学习与使用[转]
代码静态分析工具--splint的学习与使用[转] 引言 最近在项目中使用了静态程序分析工具PC-Lint,体会到它在项目实施中带给开发人员的方便.PC-Lint是一款针对C/C++语言.window ...
- 深入理解DOM事件类型系列——剪贴板事件
定义 剪贴板操作包括剪切(cut).复制(copy)和粘贴(paste)这三个操作,快捷键分别是ctrl+x.ctrl+c.ctrl+v.当然也可以使用鼠标右键菜单进行操作 对象事件 关于这3个操作共 ...
- ERROR: Field 'PostId' doesn't have a default value Exception in thread "main" org.hibernate.exception.GenericJDBCException: could not execute statement
例子: Post p = new Post(); p.setPostId(3); p.setPostName("技术"); 在执行数据保持时提示session.save(p); 的 ...