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

2023-11-10

链接: 原文出处
作者: FreeBlues


概述

卷积在信号处理领域有极其广泛的应用, 也有严格的物理和数学定义. 本文只讨论卷积在数字图像处理中的应用.

在数字图像处理中, 有一种基本的处理方法:线性滤波. 待处理的平面数字图像可被看做一个大矩阵, 图像的每个像素对应着矩阵的每个元素, 假设我们平面的分辨率是 1024*768, 那么对应的大矩阵的行数= 1024, 列数=768.

用于滤波的是一个滤波器小矩阵(也叫卷积核), 滤波器小矩阵一般是个方阵, 也就是 行数 和 列数 相同, 比如常见的用于边缘检测的 Sobel 算子 就是两个 3*3 的小矩阵.

进行滤波就是对于大矩阵中的每个像素, 计算它周围像素和滤波器矩阵对应位置元素的乘积, 然后把结果相加到一起, 最终得到的值就作为该像素的新值, 这样就完成了一次滤波.

图像卷积计算示意图:

图像卷积计算示意图

对图像大矩阵和滤波小矩阵对应位置元素相乘再求和的操作就叫卷积(Convolution)协相关(Correlation).

协相关(Correlation)和卷积(Convolution)很类似, 两者唯一的差别就是卷积在计算前需要翻转卷积核, 而协相关则不需要翻转.


以 Sobel 算子为例

Sobel 算子 也叫 Sobel 滤波, 是两个 3*3 的矩阵, 主要用来计算图像中某一点在横向/纵向上的梯度, 看了不少网络上讲解 Sobel 算子 的文章, 发现人们常常把它的横向梯度矩阵和纵向梯度矩阵混淆. 这可能与 Sobel 算子 在它的两个主要应用场景中的不同用法有关.

Sobel 算子的两个梯度矩阵: Gx 和 Gy

这里以 Wiki 资料为准, Sobel 算子 有两个滤波矩阵: Gx 和 Gy, Gx 用来计算横向的梯度, Gy 用来计算纵向的梯度, 下图就是具体的滤波器:

sobel滤波器

注意:这里列出的这两个梯度矩阵对应于横向从左到右, 纵向从上到下的坐标轴, 也就是这种:

原点
O ——-> x轴
|
|
|
V y轴

Sobel 算子的用途
它可以用来对图像进行边缘检测, 或者用来计算某个像素点的法线向量. 这里需要注意的是:

  • 边缘检测时: Gx 用于检测纵向边缘, Gy 用于检测横向边缘.
  • 计算法线时: Gx 用于计算法线的横向偏移, Gy用于计算法线的纵向偏移.

计算展开

假设待处理图像的某个像素点周围的像素如下:

左上像素 上边像素 右上像素
左边像素 中心像素 右边像素
坐下像素 下边像素 右下像素

那么用 Gx 计算展开为:
横向新值 = (-1) x [左上] + (-2) x [左] + (-1) x [左下] + 1 x [右上] + 2 x [右] + 1 x [右下]

Gy 计算展开为:
纵向新值 = (-1) x [左上] + (-2) x [上] + (-1) x [右] + 1 x [左下] + 2 x [下] + 1 x [右下]

前面说过, 做图像卷积时需要翻转卷积核, 但是我们上面的计算过程没有显式翻转, 这是因为 Sobel 算子 绕中心元素旋转 180 度后跟原来一样. 不过有些 卷积核 翻转后就变了, 下面我们详细说明如何翻转卷积核.

卷积核翻转

前面说过, 图像卷积计算, 需要先翻转卷积核, 也就是绕卷积核中心旋转 180度, 也可以分别沿两条对角线翻转两次, 还可以同时翻转行和列, 这3种处理都可以得到同样的结果.

对于第一种卷积核翻转方法, 一个简单的演示方法是把卷积核写在一张纸上, 用笔尖固定住中心元素, 旋转 180 度, 就看到翻转后的卷积核了.

下面演示后两种翻转方法, 示例如下:

假设原始卷积核为:

a b c
d e f
g h i

方法2:沿两条对角线分别翻转两次

先沿左下角到右上角的对角线翻转, 也就是 a和i, b和f, d和h交换位置, 结果为:

i f c
h e b
g d a

再沿左上角到右下角的对角线翻转, 最终用于计算的卷积核为:

i h g
f e d
c b a

方法3:同时翻转行和列

在 Wiki 中对这种翻转的描述:

convolution is the process of flipping both the rows and columns of the kernel and then multiplying locationally similar entries and summing.

也是把卷积核的行列同时翻转, 我们可以先翻转行, 把 a b cg h i 互换位置, 结果为:

g h i
d e f
a b c

再翻转列, 把 g d ai f c 互换位置, 结果为:

i h g
f e d
c b a

在 Wiki 中有一个计算展开式, 也说明了这种翻转:

翻转

注意:这里要跟矩阵乘法区分开, 这里只是借用了矩阵符号, 实际做的是对应项相乘, 再求和.


图像边缘像素的处理

以上都默认待处理的像素点周围都有像素, 但是实际上图像边缘的像素点周围的像素就不完整, 比如顶部的像素在它上方就没有像素点了, 而图像的四个角的像素点的相邻像素更少, 我们以一个图像矩阵为例:

左上角 右上角
左侧
左下角 右下角

位于左上角的像素点的周围就只有右侧和下方有相邻像素, 遇到这种情况, 就需要补全它所缺少的相邻像素, 具体补全方法请参考下一节的代码.

用GPU进行图像卷积

如果在 CPU 上实现图像卷积算法需要进行4重循环, 效率比较差, 所以我们试着把这些卷积计算放到 GPU 上, 用 shader 实现, 结果发现性能相当好, 而且因为顶点着色器和片段着色器 本质就是一个循环结构, 我们甚至不需要显式的循环, 代码也清晰了很多.

图像卷积在代码中的实际应用, 下面是一个 GLSL 形式的着色器, 它可以根据纹理贴图生成对应的法线图:

// 用 sobel 算子生成法线图 generate normal map with sobel operator

genNormal1 = {
vertexShader = [[
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;

varying vec2 vTexCoord;
varying vec4 vColor;
varying vec4 vPosition;

uniform mat4 modelViewProjection;

void main()
{
    vColor = color;
    vTexCoord = texCoord;
    vPosition = position;
    gl_Position = modelViewProjection * position;
}
]],

fragmentShader = [[
precision highp float;

varying vec2 vTexCoord;
varying vec4 vColor;
varying vec4 vPosition;

// 纹理贴图
uniform sampler2D tex;
uniform sampler2D texture;

//图像横向长度-宽度, 图像纵向长度-高度
uniform float w;
uniform float h;

float clamp1(float, float);
float intensity(vec4);

float clamp1(float pX, float pMax) {
    if (pX > pMax) 
        return pMax;
    else if (pX < 0.0)
        return 0.0;
    else
        return pX;   
}

float intensity(vec4 col) {
    // 计算像素点的灰度值
    return 0.3*col.x + 0.59*col.y + 0.11*col.z;
}

void main() {
    // 横向步长-每像素点宽度,纵向步长-每像素点高度
    float ws = 1.0/w ;
    float hs = 1.0/h ;
    float c[10];
    vec2 p = vTexCoord;
    lowp vec4 col = texture2D( texture, p );

    // sobel operator
    // position.      Gx.            Gy
    // 1 2 3     |-1. 0. 1.|   |-1. -2. -1.|
    // 4 5 6     |-2. 0. 2.|   | 0.  0.  0.|
    // 7 8 9     |-1. 0. 1.|   | 1.  2.  1.|
    // 右上角,右,右下角

c[3] = intensity(texture2D( texture, vec2(clamp(p.x+ws,0.,w), clamp(p.y+hs,0.,h) )));
c[6] = intensity(texture2D( texture, vec2(clamp1(p.x+ws,w), clamp1(p.y,h))));
c[9] = intensity(texture2D( texture, vec2(clamp1(p.x+ws,w), clamp1(p.y-hs,h))));

// 上, 下
c[2] = intensity(texture2D( texture, vec2(clamp1(p.x,w), clamp1(p.y+hs,h))));
c[8] = intensity(texture2D( texture, vec2(clamp1(p.x,w), clamp1(p.y-hs,h))));

// 左上角, 左, 左下角
c[1] = intensity(texture2D( texture, vec2(clamp1(p.x-ws,w), clamp1(p.y+hs,h))));
c[4] = intensity(texture2D( texture, vec2(clamp1(p.x-ws,w), clamp1(p.y,h)))); 
c[7] = intensity(texture2D( texture, vec2(clamp1(p.x-ws,w), clamp1(p.y-hs,h))));

    // 先进行 sobel 滤波, 再把范围从 [-1,1] 调整到 [0,1]
    // 注意: 比较方向要跟坐标轴方向一致, 横向从左到右, 纵向从下到上
    float dx = (c[3]+2.*c[6]+c[9]-(c[1]+2.*c[4]+c[7]) + 1.0) / 2.0;
    float dy = (c[7]+2.*c[8]+c[9]-(c[1]+2.*c[2]+c[3]) + 1.0) / 2.0;
    float dz = (1.0 + 1.0) / 2.0;

    gl_FragColor = vec4(vec3(dx,dy,dz), col.a);

}
]]
}

参考
图像卷积与滤波的一些知识点
Sobel Derivatives
Wiki:Kernel (image processing)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

彻底理解数字图像处理中的卷积-以Sobel算子为例 的相关文章

  • 图像紫边消除(depurple)

    图像紫边广泛存在于目前的手机摄像头 数码相机 监控摄像头等数字成像系统所得图像中 当我们使用这些设备在逆光 大光圈等条件下拍摄时 所得图像的局部区域 特别是高反差区域 亮暗对比反差很大的图像区域 比如天空 灯管与物体相接的边缘 会比较容易观
  • 图像的二值化分割,otsu类间方差法

    二值化图像指图像中的每个像素只取两个离散的值之一 用数学公式表示为 公式中 f x y 表示一幅数字图像 X Y表示该图像中某像素的坐标值 T为 二值化的阈值 表示经过阈值运算后的二值化图像 这里0和1仅仅是一个抽象表示 并非实际像素值 它
  • c#图像几何特征匹配

    using System using System Collections Generic using System Linq using System Text using System Threading Tasks using Ope
  • gstreamer中tee如何实现动态增减支路(预览+截图+录像)

    系列文章目录 Gstreamer中获取帧数据的方式 gstreamer中如何使用probe 探针 获取帧数据 gstreamer拉流rtsp使用appsink获取帧数据 预览 截图 gstreamer中如何使用fakesink获取帧数据 预
  • R-CNN史上最全讲解

    文章目录 一 初识R CNN 网络结构 二 训练步骤 1 RP的确定 2 模型pre training 3 Fine Tunning 4 提取并保存RP的特征向量 5 SVM的训练 6 bbox regression的训练 三 测试步骤 s
  • 图像处理 --- 一、认识图像处理

    声明 本系列文档由学习哔站视频总结而得 后续会逐渐添加相对应的示例代码 python 1 什么是图像与图像处理 百闻不如一见 图像是客观对象的一种相似性的 生动性的描述或写真 是人类社会活动中最常用的信息载体 或者说图像是客观对象的一种表示
  • 数字图像处理(入门篇)六 图像数据预处理之坐标变化

    目录 1 平移 2 镜像 3 旋转 4 缩放 图像的坐标变换又称为图像的几何计算 常见的基本变换包括 平移 旋转 镜像和缩放等等 1 平移 1 代码 使用OpenCV仿射变换函数 cv2 warpAffine 实现平移操作 import n
  • MATLAB实现五种边缘检测

    一 原理 常用的边缘检测算法有拉普拉斯边缘检测算法 Robert边缘检测算子 Sobel边缘检测算子 Prewitt边缘检测算子 Canny边缘检测算子 二 代码 filename pathname uigetfile jpg bmp gi
  • (详细步骤和代码)利用A100 GPU加速Tensorflow

    利用A100 GPU加速Tensorflow NVIDIA A100 基于 NVIDIA Ampere GPU 架构 提供一系列令人兴奋的新功能 第三代张量核心 多实例 GPU MIG 和第三代 NVLink Ampere Tensor C
  • 方框滤波,均值滤波,高斯滤波

    邻域算子 局部算子 是利用给定像素周围的像素值的决定此像素的最终输出值的一种算子 对于邻域算子 除了用于局部色调调整以外 还可以用于图像滤波 实现图像的平滑和锐化 图像边缘增强或者图像噪声的去除 而线性邻域滤波是一种常用的邻域算子 像素的输
  • 图像二值化

    文章目录 1 图像二值化 2 图像二值化方法及Python实现 2 1 简单二值法 2 2 平均值法 2 3 双峰法 2 4 OTSU法 3 opencv python中二值化方法的应用 3 1 简单阈值分割 Simple Threshol
  • PR-RL:Portrait Relighting via Deep Reinforcement Learning

    文章目录 Title PR RL Portrait Relighting via Deep Reinforcement Learning 1 Article 1 1 Abstract and Introduction 1 2 Conclus
  • 基于Matlab实现图像融合技术(附上多个仿真源码+数据)

    图像融合技术是一种将多幅图像融合为一幅图像的方法 使得这幅融合图像包含原始图像的所有信息 近年来 图像融合技术已经广泛应用于图像分割 变换和裁剪等领域 本文将介绍如何使用Matlab实现图像融合技术 实现步骤 首先 我们需要了解图像融合的基
  • cv2.minAreaRect()

    功能 求出在点集下的最小面积矩形 输入 格式 points array shape n 1 2 解释 其中points是点集 数据类型为ndarray array x1 y1 x2 y2 xn yn 输出 格式 rect tuple x y
  • Halcon直线检测

    1 Halcon最常用的直线检测算子 add metrology object line measure 利用Halcon封装好的模型不仅可以检测直线 还可以检测圆 椭圆 矩形等 下面介绍下其余的直线检测的算子 需要配合 skeleton
  • 友思特分享 | CamSim相机模拟器:极大加速图像处理开发与验证过程

    来源 友思特 机器视觉与光电 友思特分享 CamSim相机模拟器 极大加速图像处理开发与验证过程 原文链接 https mp weixin qq com s IED7Y6R8WE4HmnTiRY8lvg 欢迎关注虹科 为您提供最新资讯 随着
  • Matlab图像处理系列——图像复原之噪声模型仿真

    微信公众号上线 搜索公众号 小灰灰的FPGA 关注可获取相关源码 定期更新有关FPGA的项目以及开源项目源码 包括但不限于各类检测芯片驱动 低速接口驱动 高速接口驱动 数据信号处理 图像处理以及AXI总线等 本节目录 一 图像复原的模型 二
  • 【图像配准】

    非配对配准 Non rigid registration 和配对配准 Rigid registration 是医学图像配准中常用的两种方法 它们有着不同的含义和应用 非配对配准 Non rigid registration 非配对配准是指将
  • Matlab图像处理系列——图像复原之噪声模型仿真

    微信公众号上线 搜索公众号 小灰灰的FPGA 关注可获取相关源码 定期更新有关FPGA的项目以及开源项目源码 包括但不限于各类检测芯片驱动 低速接口驱动 高速接口驱动 数据信号处理 图像处理以及AXI总线等 本节目录 一 图像复原的模型 二
  • 图像分割-Grabcut法(C#)

    版权声明 本文为博主原创文章 转载请在显著位置标明本文出处以及作者网名 未经作者允许不得用于商业目的 本文的VB版本请访问 图像分割 Grabcut法 CSDN博客 GrabCut是一种基于图像分割的技术 它可以用于将图像中的前景和背景分离

随机推荐

  • 6-1 使用函数求素数和 (14 分)

    使用函数求素数和 prime p 其中函数prime当用户传入参数p为素数时返回True 否则返回False PrimeSum m n 函数PrimeSum返回区间 m n 内所有素数的和 题目保证用户传入的参数1 lt m
  • css实现气泡对话框

    实现气泡聊天框 我介绍两个方法 第一种 利用旋转 首先设置一个小正方形利用transform属性将其旋转45度 当我们把一个长方形方框和旋转后的小正方形置于一起就可以都得到理想效果 效果图如下 如上图所示 露出来的三角只是小正方形的一角 代
  • Node.js express项目生成器

    使用流程 1 在任意目录打开终端 输入指令 cnpm i express generator g 全局安装 express项目生成器 1 1查看模块安装的路径 输入指令 npm config ls C Users 12015 AppData
  • Java微信APP支付-支付结果通知

    上一章讲了微信APP支付统一下单接口的开发 这一章我们讲支付结果通知接口的开发 这一接口是微信异步调用我们的接口 告之我们支付已经成功了 然后我们补录门店 电商订单 更新APP订单支付信息等业务逻辑 官方的API地址 https pay w
  • linux内核中socket读取和接收的缓冲区大小

    linux内核中socket读取和接收的缓冲区大小 1 socket内核缓冲区大小可用getsockopt获取 2 socket内核缓冲区大小可用setsockopt设置 缓冲区的大小为设置的值的2倍 具体设置代码入下 3 socket缓冲
  • 整数奇偶排序

    整数奇偶排序 问题描述 蒜术师给了你一个 10 个整数的序列 要求对其重新排序 排序要求 奇数在前 偶数在后 奇数按从大到小排序 偶数按从小到大排序 输入格式 输入一行 包含 10 10 个整数 彼此以一个空格分开 每个整数的范围是大于等于
  • Linux入坑教程

    服务器安装所需环境 Linux 一 安装基础环境 1 1 JDK 1 2 Mysql 1 3 FastDFS 1 4 Redis 1 5 GIT 1 6 Node 二 Basic commands 基础命令 2 1 查询命令 2 1 1 查
  • 僵尸进程~

    僵尸进程 1 僵尸进程概述 什么是僵尸进程 在Linux系统中 任何一个子进程在调用exit 函数结束运行后 内核会释放该进程的所有资源 包括占用的内存和打开的文件等 同时 也会留下一个叫做僵尸进程 Zombie 的数据结构 Zombie中
  • HIDL详解-Android10.0 HwBinder通信原理(二)

    Android取经之路 的源码都基于Android Q 10 0 进行分析 Android取经之路 系列文章 系统启动篇 Android系统架构Android是怎么启动的Android 10 0系统启动之init进程Android10 0系
  • sentinel搭建与使用

    下载 https github com alibaba Sentinel releases 启动 脚本 java jar Dserver port 9013 Dsentinel dashboard auth username sentine
  • ERROR in ./src/main.jsModule build failed (from ./node_modules/babel-loader/lib/index.js):Error: e

    ERROR in src main js Module build failed from node modules babel loader lib index js Error error 0308010C digital envelo
  • mysql数据库连接

    一 自带的客户端命令行 直接输入密码即可连接 二 使用口令连接 1 切换目录 输入cd C web mysql 8 0 11 winx64 bin 2 登录 输入mysql u root p 3 输入密码 root 数据库密码 连接成功
  • 【Docker】ubuntu20.04 X86机器搭建NVIDIA ARM64 TX2的Docker镜像

    文章目录 1 设置ubuntu为清华源 1 1 备份源文件 1 2 替换清华源 1 3 更新清华源 2 Ubuntu Docker 安装 3 安装qemu 4 安装Nvidia TX2 Docker镜像 5 如何使用TX2容器 6 参考资料
  • ubuntu下载使用mtcnn和facenet并运行demo

    首先搭建好环境 ubuntu18 04 python3 6 5 tensorflow1 8 0 opencv3 4 3 pip install tqdm为了显示进度条 主要在这两个网站上学习 github上有很多教程和样例 以一般选择星星最
  • Windows使用ssh登入远程服务器(包含mac版)

    windows 首先Windows是没有ssh这个命令的 所以我们先要使Windows可以使用ssh命令 下载openssh for Winodws http linux linuxidc com index php folder MjAx
  • SpringBoot工程使用logback-spring.xml

    在SpringBoot工程中 推荐使用logback spring xml来替换logback xml 原因是SpringBoot加载logback xml是在application yml之前 所以在yml里面的信息不会被logback
  • Python疫情数据可视化分析+数据预测(pandas+pyecharts+statsmodels+matplotlib+sql)

    1 MySQL数据库获取数据 此处的原始数据表是全国各省的实时数据集 现在只获取江苏省份的数据 engine create engine mysq conn 具体内容以自己上就可以为准 select data select from tab
  • C++---类成员变量定义为引用

    摘要 类成员变量是可以定义为引用类型的 但是我们需要注意一下用法 note1 在类中定义引用变量 必须要在初始化列表中初始化该成员变量 const 类型数据成员也必须在初始化列表中进行初始化 include
  • synchronized (成员变量) 和 synchronized (静态成员变量)

    synchronized 成员变量 和 synchronized 静态成员变量 在同步方面有不同的效果 在 Java 中 每个对象都有一个 内置锁 或 对象锁 也称为 monitor 对象 它可以用来同步代码块或方法 使用 synchron
  • 彻底理解数字图像处理中的卷积-以Sobel算子为例

    链接 原文出处 作者 FreeBlues 概述 卷积在信号处理领域有极其广泛的应用 也有严格的物理和数学定义 本文只讨论卷积在数字图像处理中的应用 在数字图像处理中 有一种基本的处理方法 线性滤波 待处理的平面数字图像可被看做一个大矩阵 图