计算机视觉class3
来源:互联网 发布:少女时代十周年知乎 编辑:程序博客网 时间:2024/06/17 03:39
要点
1.边缘检测
2.二值图像及其边界提取(各分割图像的面积、边界的长度)
3.区域骨架提取(细化)
解释
1.边缘检测
(1)概念:边缘检测的目的是标识数字图像中亮度变化明显的点。
(2)方法:①基于查找—>寻找图像一阶导数信息,通过导数的最大(小)值来检测边缘。
②基于零穿越—->寻找图像二阶导数信息,通过二阶导数零穿越来寻找边界。
核心的思想都是利用灰度值的不连续性和相似性。
(3)流程:1)Filter 对噪声有强敏感性,常用的滤波方法是高斯,用离散化的高斯函数产生归一化的高斯核。
2)Enhancement 确定各点邻域强度的变化值,目的是将局部强度值有显著变化的点凸显出来。
3)Detection(Location) 对凸显出来的显著点进行处理(过滤),工程中,常用阈值法。
(4)几种算法:
①Sobel算子
反映的是一阶导数信息,在离散域上表现为差分算子,在图像上运用Sobel算子能够得到对应的灰度矢量。
转换成核卷积的方式:(3×3)
如果梯度G大于某一阈值,则认为该点(x,y)为边缘点。
Summary:Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。
②Laplace算子
反映的是二阶导数信息,一个二维图像函数的Laplace变换是各向同性的二阶导数。
转换成模板的形式:
Tips:由Laplace算子获得图像锐化效果,由mask中心系数负正分为:
Summary:从模板形式容易看出,如果在图像中一个较暗的区域中出现了一个亮点,那么用Laplace运算就会使这个亮点变得更亮。用二次微分正峰和负峰之间的过零点来确定,对孤立点或端点更为敏感,因此特别适用于以突出图像中的孤立点、孤立线或线端点为目的的场合。
③Scharr滤波器
Scharr滤波器其实相当于Sobel算子,相当于Sobel算子采取固定核大小的实现。在程序编写中,也是基于Sobel算子,只不过把核的大小选取这一步骤给省略了。
④Canny算子
state-of-art technology,Canny于1986年开发出,并创立了边缘检测计算理论,解释了边缘检测技术是如何工作的。他提出了最优边缘检测的三个主要评价标准。
1)低错误率。标识出尽可能多的边缘,并减小噪声误报。
2)高定位性。标识的边缘与实际边缘尽可能接近。
3)最小响应。图像的边缘只能标识一次,可能存在的图像噪声不应标识为边缘。
操作步骤:1)Denoise. 利用高斯平滑滤波器卷积降噪。
2)Sobel算子。按照Sobel算子的步骤来操作,计算梯度幅值和方向。
在方向选取上取4个角度之一进行量化。以45度为限。
3)Restriction. 非极大值抑制,滤除非边缘像素,寻找像素点局部最大值,将非极大值点所对应的灰度值置为0。
4)Threshold. 使用滞后阈值,即有一个高阈值和一个低阈值,在高阈值图像中把边缘链接成轮廓,当到达轮廓的端点时,该算法会在断点的8邻域点中寻找满足低阈值的点,再根据此点收集新的边缘,直到整个图像边缘闭合。
********************************华丽丽的分割线********************************************************
Conculsion:所有的边缘检测算法都是基于导数(梯度)的信息去实现的,在主方法确定的情况下,运用各种滤波手段对图像中的奇异点进行滤除,消除噪声与其它因素所造成的像素值突变的干扰,再利用阈值法来达到高质量的边缘识别。
2.边界提取
用到了形态学操作,主要涉及到腐蚀与膨胀。
简单而言,形态学操作就是基于形状的一系列图像处理操作。
腐蚀与膨胀能够完成很多功能:消除噪声;分割出独立的图像元素;求出图像梯度;找到图像中明显的极大值与极小值区域等等。
注意:腐蚀与膨胀是对白色区域而言的(二值图像,非黑即白)。膨胀是对图像中的高亮部分进行膨胀,类似于扩张,能得到比原图更大的高亮区域,腐蚀则相反,类似于蚕食,得到比原图更小的高亮区域。
从数学角度来看,膨胀或腐蚀操作就是将图像(或某一部分)与核进行卷积。核B的大小也叫结构体。
要得到边界,可以用膨胀后的图减去原图,或者原图减去腐蚀后的图。
3.骨架提取(图像细化)
简单来说,图像的细化就是经过一层层的剥离,去掉一些点,但要保持原来的形状,一直到能得到图像的骨架。
Zhang-Suen细化算法通常是一个迭代算法,整个迭代过程分为两步:
Step One:循环所有前景像素点,对符合如下条件的像素点标记为删除:
2 <= N(p1) <=6
S(P1) = 1
P2 * P4 * P6 = 0
P4 * P6 * P8 = 0
其中N(p1)表示跟P1相邻的8个像素点中,为前景像素点的个数
S(P1)表示从P2 ~ P9 ~ P2像素中出现0~1的累计次数,其中0表示背景,1表示前景
完整的P1 ~P9的像素位置与举例如下:
其中 N(p1) = 4, S(P1) = 3, P2*P4*P6=0*0*0=0, P4*P6*P8=0*0*1=0, 不符合条件,无需标记为删除。
Step Two:跟Step One很类似,条件1、2完全一致,只是条件3、4稍微不同,满足如下条件的像素P1则标记为删除,条件如下:
2 <= N(p1) <=6
S(P1) = 1
P2 * P4 * P8 = 0
P2 * P6 * P8 = 0
循环上述两步骤,直到两步中都没有像素被标记为删除为止,输出的结果即为二值图像细化后的骨架。
参考:CSDN图像细化
代码
1.边缘检测
①Sobel算子
#include <opencv2\opencv.hpp>using namespace cv;Mat g_srcImage, g_GrayImage, g_dstImage;Mat g_sobelGradient_x, g_sobelGradient_y;Mat g_sobelAbsGradient_x, g_sobelAbsGradient_y;int g_sobelKernelSize = 1;static void on_Sobel(int, void*){ Sobel(g_GrayImage, g_sobelGradient_x, -1, 1, 0, (2 * g_sobelKernelSize + 1), 1, 1, BORDER_DEFAULT); convertScaleAbs(g_sobelGradient_x, g_sobelAbsGradient_x); Sobel(g_GrayImage, g_sobelGradient_y, -1, 0, 1, (2 * g_sobelKernelSize + 1), 1, 1, BORDER_DEFAULT); convertScaleAbs(g_sobelGradient_y, g_sobelAbsGradient_y); addWeighted(g_sobelAbsGradient_x, 0.5, g_sobelAbsGradient_y, 0.5, 0, g_dstImage); imshow("result", g_dstImage);}int main(){ system("color 2F"); g_srcImage = imread("F:\\c_code\\week6_1\\Release\\1.png"); imshow("srcImage", g_srcImage); cvtColor(g_srcImage, g_GrayImage, COLOR_BGR2GRAY); imshow("srcGrayImage", g_GrayImage); namedWindow("result", 1); createTrackbar("value:", "result", &g_sobelKernelSize, 5, on_Sobel); on_Sobel(0, 0); while (char(waitKey(1) != 'q')) { } return 0;}
结果:
②Laplace算子
#include <opencv2\opencv.hpp>using namespace cv;Mat g_srcImage, g_grayImage, g_dstImage;int g_LaplaceValue = 0;static void on_Laplacian(int, void*){ Laplacian(g_grayImage, g_dstImage, CV_16S, 2*g_LaplaceValue+1, 1, 0, BORDER_DEFAULT); convertScaleAbs(g_dstImage, g_dstImage); imshow("result", g_dstImage);}int main(){ system("color 2F"); g_srcImage = imread("F:\\c_code\\week6_1\\Release\\1.png"); imshow("srcImage", g_srcImage); cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY); imshow("srcGrayImage", g_grayImage); GaussianBlur(g_grayImage, g_grayImage, Size(3, 3), 0, 0, BORDER_DEFAULT); namedWindow("result", 1); createTrackbar("value:", "result", &g_LaplaceValue, 8, on_Laplacian); on_Laplacian(0, 0); while (char(waitKey(1) != 'q')) { } return 0;}
结果:
③Scharr滤波器
#include <opencv2\opencv.hpp>using namespace cv;Mat g_srcImage, g_GrayImage, g_dstImage;Mat g_scharrGradient_x, g_scharrGradient_y;Mat g_scharrAbsGradient_x, g_scharrAbsGradient_y;static void on_Scharr(){ Scharr(g_GrayImage, g_scharrGradient_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT); convertScaleAbs(g_scharrGradient_x, g_scharrAbsGradient_x); Scharr(g_GrayImage, g_scharrGradient_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT); convertScaleAbs(g_scharrGradient_y, g_scharrAbsGradient_y); addWeighted(g_scharrAbsGradient_x, 0.5, g_scharrAbsGradient_y, 0.5, 0, g_dstImage); imshow("result", g_dstImage);}int main(){ system("color 2F"); g_srcImage = imread("F:\\c_code\\week6_1\\Release\\1.png"); imshow("srcImage", g_srcImage); cvtColor(g_srcImage, g_GrayImage, COLOR_BGR2GRAY); imshow("srcGrayImage", g_GrayImage); namedWindow("result", 1); on_Scharr(); while (char(waitKey(1) != 'q')) { } return 0;}
结果:
④Canny算子
#include <opencv2\opencv.hpp>#include <iostream>using namespace cv;//-------------------------------------------------------Mat g_srcImage, g_srcGrayImage, g_dstImage;Mat g_cannyValue;int g_cannyLowThreshold = 1;//---------------------------------------------------static void on_Canny(int, void*){ blur(g_srcGrayImage, g_cannyValue, Size(3, 3)); Canny(g_cannyValue, g_cannyValue, g_cannyLowThreshold, g_cannyLowThreshold * 3, 3); g_dstImage = Scalar::all(0); g_srcImage.copyTo(g_dstImage, g_cannyValue); imshow("result", g_dstImage);}int main(){ system("color 2F"); g_srcImage = imread("F:\\c_code\\week6_1\\Release\\1.png"); imshow("srcImage", g_srcImage); cvtColor(g_srcImage, g_srcGrayImage, COLOR_BGR2GRAY); imshow("srcGrayImage", g_srcGrayImage); namedWindow("result", 1); createTrackbar("value:", "result", &g_cannyLowThreshold, 120, on_Canny); on_Canny(0, 0); while (char(waitKey(1) != 'q')) { } return 0;}
结果:
2.边界提取
#include <opencv2\opencv.hpp>#include <iostream>using namespace std;using namespace cv;int main(){ Mat srcImage = imread("F:\\c_code\\week6_2\\week6_2\\1.png",0); imshow("GrayImage", srcImage); Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); Mat out; erode(srcImage, out, element); imshow("erodeImage", out); Mat dstImage = srcImage - out; imshow("dstImage", dstImage); vector<vector<Point>> contours; vector<Vec4i> hier; double area = 0; double length = 0; findContours(dstImage, contours,RETR_EXTERNAL, CHAIN_APPROX_NONE); for (int i = 0; i < (int)contours.size(); i++) { area = contourArea(contours[i]); length = arcLength(contours[i],true); cout << "index " << i << " area is " << area; cout << " length is " << length << endl; } waitKey(0); return 0;}
结果:
3.骨架提取(图像细化)
#include <opencv2\opencv.hpp>#include <iostream>using namespace cv;void chao_thinimage(Mat &srcimage)//单通道、二值化后的图像 { vector<Point> deletelist1; int Zhangmude[9]; int nl = srcimage.rows; int nc = srcimage.cols; while (true) { for (int j = 1; j < (nl - 1); j++) { uchar* data_last = srcimage.ptr<uchar>(j - 1); uchar* data = srcimage.ptr<uchar>(j); uchar* data_next = srcimage.ptr<uchar>(j + 1); for (int i = 1; i < (nc - 1); i++) { if (data[i] == 255) { Zhangmude[0] = 1; if (data_last[i] == 255) Zhangmude[1] = 1; else Zhangmude[1] = 0; if (data_last[i + 1] == 255) Zhangmude[2] = 1; else Zhangmude[2] = 0; if (data[i + 1] == 255) Zhangmude[3] = 1; else Zhangmude[3] = 0; if (data_next[i + 1] == 255) Zhangmude[4] = 1; else Zhangmude[4] = 0; if (data_next[i] == 255) Zhangmude[5] = 1; else Zhangmude[5] = 0; if (data_next[i - 1] == 255) Zhangmude[6] = 1; else Zhangmude[6] = 0; if (data[i - 1] == 255) Zhangmude[7] = 1; else Zhangmude[7] = 0; if (data_last[i - 1] == 255) Zhangmude[8] = 1; else Zhangmude[8] = 0; int whitepointtotal = 0; for (int k = 1; k < 9; k++) { whitepointtotal = whitepointtotal + Zhangmude[k]; } if ((whitepointtotal >= 2) && (whitepointtotal <= 6)) { int ap = 0; if ((Zhangmude[1] == 0) && (Zhangmude[2] == 1)) ap++; if ((Zhangmude[2] == 0) && (Zhangmude[3] == 1)) ap++; if ((Zhangmude[3] == 0) && (Zhangmude[4] == 1)) ap++; if ((Zhangmude[4] == 0) && (Zhangmude[5] == 1)) ap++; if ((Zhangmude[5] == 0) && (Zhangmude[6] == 1)) ap++; if ((Zhangmude[6] == 0) && (Zhangmude[7] == 1)) ap++; if ((Zhangmude[7] == 0) && (Zhangmude[8] == 1)) ap++; if ((Zhangmude[8] == 0) && (Zhangmude[1] == 1)) ap++; if (ap == 1) { if ((Zhangmude[1] * Zhangmude[7] * Zhangmude[5] == 0) && (Zhangmude[3] * Zhangmude[5] * Zhangmude[7] == 0)) { deletelist1.push_back(Point(i, j)); } } } } } } if (deletelist1.size() == 0) break; for (size_t i = 0; i < deletelist1.size(); i++) { Point tem; tem = deletelist1[i]; uchar* data = srcimage.ptr<uchar>(tem.y); data[tem.x] = 0; } deletelist1.clear(); for (int j = 1; j < (nl - 1); j++) { uchar* data_last = srcimage.ptr<uchar>(j - 1); uchar* data = srcimage.ptr<uchar>(j); uchar* data_next = srcimage.ptr<uchar>(j + 1); for (int i = 1; i < (nc - 1); i++) { if (data[i] == 255) { Zhangmude[0] = 1; if (data_last[i] == 255) Zhangmude[1] = 1; else Zhangmude[1] = 0; if (data_last[i + 1] == 255) Zhangmude[2] = 1; else Zhangmude[2] = 0; if (data[i + 1] == 255) Zhangmude[3] = 1; else Zhangmude[3] = 0; if (data_next[i + 1] == 255) Zhangmude[4] = 1; else Zhangmude[4] = 0; if (data_next[i] == 255) Zhangmude[5] = 1; else Zhangmude[5] = 0; if (data_next[i - 1] == 255) Zhangmude[6] = 1; else Zhangmude[6] = 0; if (data[i - 1] == 255) Zhangmude[7] = 1; else Zhangmude[7] = 0; if (data_last[i - 1] == 255) Zhangmude[8] = 1; else Zhangmude[8] = 0; int whitepointtotal = 0; for (int k = 1; k < 9; k++) { whitepointtotal = whitepointtotal + Zhangmude[k]; } if ((whitepointtotal >= 2) && (whitepointtotal <= 6)) { int ap = 0; if ((Zhangmude[1] == 0) && (Zhangmude[2] == 1)) ap++; if ((Zhangmude[2] == 0) && (Zhangmude[3] == 1)) ap++; if ((Zhangmude[3] == 0) && (Zhangmude[4] == 1)) ap++; if ((Zhangmude[4] == 0) && (Zhangmude[5] == 1)) ap++; if ((Zhangmude[5] == 0) && (Zhangmude[6] == 1)) ap++; if ((Zhangmude[6] == 0) && (Zhangmude[7] == 1)) ap++; if ((Zhangmude[7] == 0) && (Zhangmude[8] == 1)) ap++; if ((Zhangmude[8] == 0) && (Zhangmude[1] == 1)) ap++; if (ap == 1) { if ((Zhangmude[1] * Zhangmude[3] * Zhangmude[5] == 0) && (Zhangmude[3] * Zhangmude[1] * Zhangmude[7] == 0)) { deletelist1.push_back(Point(i, j)); } } } } } } if (deletelist1.size() == 0) break; for (size_t i = 0; i < deletelist1.size(); i++) { Point tem; tem = deletelist1[i]; uchar* data = srcimage.ptr<uchar>(tem.y); data[tem.x] = 0; } deletelist1.clear(); }}int main(){ //把一幅图像转换成二值图像 Mat srcImage = imread("F:\\c_code\\week6_3\\week6_3\\1.png",0); for (int y = 0; y < srcImage.rows; y++) { uchar* data = srcImage.ptr<uchar>(y); for (int x = 0; x < srcImage.cols; x++) { if (data[x]>128) data[x] = 255; else data[x] = 0; } } imshow("srcImage", srcImage); chao_thinimage(srcImage); imshow("dstImage", srcImage); waitKey(0); return 0;}
结果:
最后参考了毛星云的《opencv3编程入门》。
总算写完了,接下来看部电影放松下O(∩_∩)O
- 计算机视觉class3
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- 计算机视觉
- opencv-python 绘制直方图和均衡化
- flask-wtf表单中PasswordField无法回传显示密码问题解决方法
- java:多网卡环境下获取MAC地址
- MyBatis-扩展-PageHelpler分页插件使用
- String、StringBuffer与StringBuilder之间区别
- 计算机视觉class3
- 在Unity中制作图片字(艺术字)
- 计蒜客 汽车费用 (完全背包)
- ubuntu常用命令
- Type 'com/vmware/vim25/RuntimeFault' (constant pool 304) is not assignable to 'java/lang/Throwable'
- 最简单的自定义Toolbar SearchView样式方法
- istringstream、ostringstream、stringstream 类介绍 .
- 虚拟IP与ARP协议
- 初识机房收费系统