前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >图像处理基础-图像边缘检测

图像处理基础-图像边缘检测

作者头像
公号sumsmile
发布2020-11-03 10:13:21
1.2K0
发布2020-11-03 10:13:21
举报
文章被收录于专栏:音视频技术学习笔记

一、插曲

图像处理经常会用到这张赫赫有名的图片,这位lena女士的照片原本用在“花花公子”的杂志封面上,机缘巧合被当做测试素材,广泛用在图形处理领域。

原图是一张半裸的全身照,截取了头像部分,有兴趣可以去网上找找

参考:Lena.jpg

IEEE图像处理汇刊的主编David C.Munson总结了两点原因: 1.该图适度的混合了细节、平滑区域、阴影和纹理,从而能很好的测试各种图像处理算法。 2.Lenna是个美女,对于图象处理界的研究者来说,美女图可以有效的吸引他们来做研究

图像边缘检测的算法有很多,包括传统的模板算⼦(Sobel、Roberts、Prewitt、Laplace)、形态学边缘检测、经典的 Canny 边缘检测及基于深度学习的边缘检测算法等。这篇文章讲两个有代表性的算子:sobel边缘检测和canny边缘检测

二、sobel边缘检测

2.1算法原理

基于梯度是最基本的边缘检测算法,存在较大误差和不稳定性。

Sobel 模板算⼦是 Irwin Sobel 在 1968 年发表的论⽂ An Isotropic 3×3 Image Gradient Operator中提出的⼀种⼀阶导数模板算⼦,⽤来计算图像灰度函数的近似梯度。

其中,Gx表⽰⽔平⽅向的卷积模板,Gy表⽰垂直⽅向的卷积模板

2.2 实现效果:

不同阈值

实现代码见文末

三、canny边缘检测

Canny边缘检测是 John Canny在 1986年⾸次提出的⼀种改进的边缘检测⽅法。该⽅法主要通过图像信号函数的极⼤值来判断图像的边缘像素点,与基本的 Sobel 模板算⼦等相⽐,其具有低错误率、⾼定位性等优点,因⽽被⼴泛应⽤。 算法实现步骤:

1. 降噪-高斯滤波平滑处理

2. 梯度计算

使⽤⼀阶导数算⼦(⼀般⽤ Sobel 模板算⼦)计算灰度图像中每个像素点在⽔平和垂直⽅向上的导数GX、GY,得出梯度向量(GX,GY),最后得到该像素点的梯度幅度G和相位⾓D 相位角后面用来根据梯度方向取临近点

3.非极大值抑制

将当前像素的梯度值与其在梯度⽅向上的邻域像素的梯度值做对⽐,如果当前像素的梯度值为最⼤值,则保留该点的梯度信息,否则将该点删除或将像素值置为0

4. 双阈值边缘检测和边缘连接

设置(min, max)范围,抑高于max视为边缘,低于min过滤掉。居于中间,进入第4步过滤,取临近点对比

实现效果:

范围(5, 50)

完整代码见文末

完整代码

sobel边缘检测

代码语言:javascript
复制
#include"f_Sobel.h"
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<stdio.h>
#include"Commen.h"

int f_Sobel(unsigned char *srcData, int width, int height,int stride, int threshold)
{
    int ret = 0;
    unsigned char *dstData = (unsigned char*)malloc(sizeof(unsigned char) * height * stride);
    memset(dstData, 255, sizeof(unsigned char) * height * stride);
    int x, y, i, k, pos;
    int hValue, vValue, value;
    unsigned char *pSrcL0;
    unsigned char *pSrcL1;
    unsigned char *pSrcL2;
    unsigned char *pDstL;
    unsigned char SqrtValue[65026];
    pSrcL0 = srcData;
    pSrcL1 = srcData + stride;
    pSrcL2 = srcData + stride * 2;
    pDstL  = dstData + stride;
    for (i = 0; i < 65026; i++)
    {
      SqrtValue[i] = (unsigned char)(sqrt((float)i) < threshold ? 0 : 255);
         // ps中这一行改成
         // SqrtValue[i] = (unsigned char)(255 - (int)sqrt((float)i));
        // 如果颜色相近sqrt算出来接近0,255-0得255,接近白色
        // sqrt算出来很大,255 - 255接近0, 显示成黑色,识别为边缘
        // 猜测人眼对白底黑字比较敏感,所以用255 - ?取反计算显示
    } 
    for (y = 1; y < height - 1; y++)
    {        
        for (x = 1; x < width - 1; x++)
        {
            pos = x * 4;
            hValue = (-pSrcL0[pos - 4] + pSrcL0[pos + 4] - 2 * pSrcL1[pos - 4] + 2 * pSrcL1[pos + 4] - pSrcL2[pos - 4] + pSrcL2[pos + 4]);
            vValue = (pSrcL0[pos - 4] + 2 * pSrcL0[pos] + pSrcL0[pos + 4] - pSrcL2[pos - 4] - 2 * pSrcL2[pos] - pSrcL2[pos + 4]);
            k = hValue * hValue + vValue * vValue;
            k = MIN2(k, 65025);
            pDstL[pos] = SqrtValue[k];
            pos++;
            hValue = (-pSrcL0[pos - 4] + pSrcL0[pos + 4] - 2 * pSrcL1[pos - 4] + 2 * pSrcL1[pos + 4] - pSrcL2[pos - 4] + pSrcL2[pos + 4]);
            vValue = (pSrcL0[pos - 4] + 2 * pSrcL0[pos] + pSrcL0[pos + 4] - pSrcL2[pos - 4] - 2 * pSrcL2[pos] - pSrcL2[pos + 4]);
            k = hValue * hValue + vValue * vValue;
            k = MIN2(k, 65025);
            pDstL[pos] = SqrtValue[k];
            pos++;
            hValue = (-pSrcL0[pos - 4] + pSrcL0[pos + 4] - 2 * pSrcL1[pos - 4] + 2 * pSrcL1[pos + 4] - pSrcL2[pos - 4] + pSrcL2[pos + 4]);
            vValue = (pSrcL0[pos - 4] + 2 * pSrcL0[pos] + pSrcL0[pos + 4] - pSrcL2[pos - 4] - 2 * pSrcL2[pos] - pSrcL2[pos + 4]);
            k = hValue * hValue + vValue * vValue;
            k = MIN2(k, 65025);
            pDstL[pos] = SqrtValue[k];
        }
        pSrcL0 += stride;
        pSrcL1 += stride;
        pSrcL2 += stride;
        pDstL  += stride;
    }
    memcpy(srcData, dstData, sizeof(unsigned char) * height * stride);
    free(dstData);
    return ret;
}

canny边缘检测

代码语言:javascript
复制
#include"f_Canny.h"
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<stdio.h>
#include"Commen.h"


//单通道灰度化
static int f_GrayOneChannel(unsigned char* srcData, unsigned char* grayData, int width, int height, int stride)
{
    int ret = 0;
    int i, j, gray, offset;
    offset = stride - (width * 4);
    unsigned char* pSrc = srcData;
    unsigned char* pGray = grayData;
    for(j = 0; j < height; j++)
    {
        for(i = 0; i < width; i++)
        {
            gray = (pSrc[2] + pSrc[1] + pSrc[0]) / 3;
            *pGray = gray;
            pSrc += 4;
            pGray ++;
        }
        pSrc += offset;
    }
    return ret;
};
//梯度相位角获取  
static void GetGradientDegree(unsigned char* srcBytes, int width, int height, float gradient[], unsigned char degree[], float* GradientMax)  
{  
    float gx, gy;  
    int temp, pos;  
    float div;  
    float PI = 3.1415926f;
    float t = 180.0f/PI;
    for (int j = 1; j < height - 1; j++)  
    {  
        for (int i = 1; i < width - 1; i++)  
        {  
            pos = i + j * width;
            gx = srcBytes[pos + 1 - width] + srcBytes[pos + 1] + srcBytes[pos + 1] + srcBytes[pos + 1 + width] - srcBytes[pos - 1 - width] - (srcBytes[pos - 1] + srcBytes[pos - 1]) - srcBytes[pos - 1 + width];  
            gy = srcBytes[pos - 1 - width] + srcBytes[pos - width] + srcBytes[pos - width] + srcBytes[pos + 1 - width] - srcBytes[pos - 1 + width] - (srcBytes[pos + width] + srcBytes[pos + width]) - srcBytes[pos + 1 + width];  
            // 求出每个点的梯度值幅度
            gradient[pos] = (float)sqrt((float)(gx * gx + gy * gy));  
            if (*GradientMax < gradient[pos])  
            {  
                *GradientMax = gradient[pos];  
            }  
            if (gx == 0)  
            {  
                temp = (gy == 0) ? 0 : 90;  
            }  
            else  
            {  
                div = gy / gx;  
                if (div < 0)  
                {  
                    temp = (int)(180 - atan(-div) * t);  
                }  
                else  
                {  
                    temp = (int)(atan(div) * t);  
                }  
                if (temp < 22.5f)  
                {  
                    temp = 0;  
                }  
                else if (temp < 67.5f)  
                {  
                    temp = 45;  
                }  
                else if (temp < 112.5f)  
                {  
                    temp = 90;  
                }  
                else if (temp < 157.5f)  
                {  
                    temp = 135;  
                }  
                else  
                    temp = 0;  
            }  
            degree[pos] = temp;  
        }  
    }  
    
} ; 
//非极大值抑制  
static void NonMaxMini(unsigned char* srcBytes, int width, int height, float gradient[], float GradientMax, unsigned char degree[])  
{  
    float leftPixel = 0, rightPixel = 0;
    int pos;
    for (int j = 1; j < height - 1; j++)  
    {  
        for (int i = 1; i < width - 1; i++)  
        {  
            pos = i + j * width;
            switch (degree[pos])  
            {  
                case 0:  
                    leftPixel = gradient[pos - 1];  
                    rightPixel = gradient[pos + 1];  
                    break;  
                case 45:  
                    leftPixel = gradient[pos - 1 + width];  
                    rightPixel = gradient[pos + 1 - width];  
                    break;  
                case 90:  
                    leftPixel = gradient[pos + width];  
                    rightPixel = gradient[pos - width];  
                    break;  
                case 135:  
                    leftPixel = gradient[pos + 1 + width];  
                    rightPixel = gradient[pos - 1 - width];  
                    break;  
                default:  
                    break;  
            }  
            if ((gradient[pos] < leftPixel) || (gradient[pos] < rightPixel))  
            {  
                srcBytes[pos] = 0;  
            }  
            else  
            {  
                srcBytes[pos] = (int)(255.0f * gradient[pos] / GradientMax);  
            }  
        }  
    }  
};  
//双阈值边缘判断  
static void TwoThreshouldJudge(unsigned char* srcBytes, int width, int height, int highThreshold, int lowThreshould)  
{  
    int pos = 0;
    for (int j = 1; j < height - 1; j++)  
    {  
        for (int i = 1; i < width - 1; i++)  
        {  
            pos = i + j * width;
            if (srcBytes[pos] > highThreshold)  
            {  
                srcBytes[pos] = 255;  
            }  
            else if (srcBytes[pos] < lowThreshould)  
            {  
                srcBytes[pos] = 0;  
            }  
            else  
            {  
                // 8领域都小于高阈值,视为噪点,剔除掉
                if (srcBytes[pos - 1 - width] < highThreshold && srcBytes[pos - width] < highThreshold && srcBytes[pos + 1 - width] < highThreshold && srcBytes[pos - 1] < highThreshold  
                    && srcBytes[pos + 1] < highThreshold && srcBytes[pos - 1 + width] < highThreshold && srcBytes[pos + width] < highThreshold && srcBytes[pos + 1 + width] < highThreshold)  
                {  
                    srcBytes[pos] = 0;  
                }  
                else  
                    srcBytes[pos] = 255;  
            }  
        }  
    }  
}; 
/************************************************************
*Function:  Canny edge detection
*Description: Canny edge detection
*Params:    
*srcData:  image bgr data     
*width  :image width
*height :image height
*stride :image stride
*highThreshold:[0,255]
*lowThreshold: [0,255],default 0.4 * highThreshold
*Return :0-OK,or failed    
************************************************************/
int f_CannyEdgedetection(unsigned char* srcData, int width ,int height, int stride, int highThreshold,int lowThreshold)
{
    int ret = 0;
    int i, j, offset, pos, temp, size;
    unsigned char* pSrc = srcData;
    size = width * height;
    unsigned char* grayData = (unsigned char*)malloc(sizeof(unsigned char) * size);
    memset(grayData, 0, sizeof(unsigned char) * size);
    offset = stride - width * 4;
    //gray
    f_GrayOneChannel(srcData, grayData, width, height, stride);
    //gauss fiter
    for(j = 0; j < height; j++)
    {
        for(i = 0; i < width; i++)
        {
            pos = i + j * width;
            if(i == 0 || j == 0 || i == width - 1 || j == height - 1)
            {
                grayData[pos] = 0;
            }
            else
            {
                temp = ((grayData[pos] << 2) + grayData[pos - width - 1] + grayData[pos + 1 - width] + grayData[pos - 1 + width] + grayData[pos + 1 + width] + grayData[pos - width] + grayData[pos - width] + grayData[pos - 1] + grayData[pos - 1] + grayData[pos + width] + grayData[pos + width] + grayData[pos + 1] + grayData[pos + 1]) >> 4;  
                grayData[pos] = temp;
            }
        }
    }
    //gradient
    float* gradient = (float*)malloc(sizeof(float) * size);
    memset(gradient, 0, sizeof(float) * size);
    unsigned char* degree = (unsigned char*)malloc(sizeof(unsigned char) * size);
    memset(degree, 0, sizeof(unsigned char) * size);
    float GradientMax = 0;
    GetGradientDegree(grayData, width, height, gradient, degree, &GradientMax);
    //none max value 
    NonMaxMini(grayData, width, height, gradient,GradientMax,degree);
    //two threshold judgement
    TwoThreshouldJudge(grayData,width, height,highThreshold,lowThreshold);
    //recovery
    for(j = 0; j < height; j++)
    {
        for(i = 0; i < width; i++)
        {
            pSrc[0] = pSrc[1] = pSrc[2] = grayData[i + j * width];
            pSrc += 4;
        }
        pSrc += offset;
    }
    free(grayData);
    free(gradient);
    free(degree);
    return ret;
};
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、插曲
  • 二、sobel边缘检测
    • 2.1算法原理
      • 2.2 实现效果:
      • 三、canny边缘检测
        • 1. 降噪-高斯滤波平滑处理
          • 2. 梯度计算
            • 3.非极大值抑制
              • 4. 双阈值边缘检测和边缘连接
                • 实现效果:
            • 完整代码
              • sobel边缘检测
                • canny边缘检测
                相关产品与服务
                图像处理
                图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档