计算机视觉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:循环所有前景像素点,对符合如下条件的像素点标记为删除:

  1. 2 <= N(p1) <=6

  2. S(P1) = 1

  3. P2 * P4 * P6 = 0

  4. 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则标记为删除,条件如下:

  1. 2 <= N(p1) <=6

  2. S(P1) = 1

  3. P2 * P4 * P8 = 0

  4. 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

原创粉丝点击