边缘的检测

边缘检测效果,是一种用于突出图像中的边缘,使物体的轮廓更加明显的图像处理技术,边缘检测的主要目的是找到图像中亮度变化显著的区域,这些区域通常对应于物体的边界,边缘检测相当于利用 Shader 代码自动给屏幕图像进行描边处理

1、边缘检测基本原理

计算每个像素的灰度值,用灰度值结合卷积核进行卷积运算,得到该像素的梯度值,梯度值越大越靠近边界,越趋近于描边颜色;梯度值越小表明不是边界位置,越趋近于原始颜色
关键知识点:灰度值、卷积、卷积核、梯度值

灰度值

由于人眼对不同颜色的敏感度不同,所以在计算平均值时不会直接使用算数平均(R+G+B)/3
在图形学中我们一般使用加权平均法来计算灰度值
所谓加权平均法就是通过对不同数据分配不同权重,计算出更符合实际情况的平均值
下面是基于 Rec. 709标准 计算的灰度值(高清电视和许多数字图像格式中常用的标准)
灰度值 L = 0.2126*R + 0.7152*G + 0.0722*B

卷积、卷积核、梯度值

卷积是一种数学计算方式,首先通过一个比喻来理解卷积在边缘检测中的作用,它就像是要用一个放大镜(卷积核)在图片上移动,放大镜(卷积核)的作用是帮助我们看到图,片上的细微变化。当我们用这个放大镜(卷积核)扫描整张图片时,它能帮助我们发现图片上哪些地方颜色变化突然,这些突然变化的地方往往就是物体的边缘了

假设我们有一张 5x5 的图像,每一个格子代表一个像素,格子中的数据表示该像素的灰度值,假设我们有一个 3x3 的卷积核(放大镜)

如果你想要求出图中红色格子的梯度值(值越大表示越靠近边缘),如果你想要求出图中红色格子的梯度值(值越大表示越靠近边缘)进行如下计算:

最终算出来的结果就表示该像素的梯度值,我们便可以用该值决定边缘效果了

从卷积的计算方式我们可以得知,其中卷积核(也被称为边缘检测因子)是非常重要的一个元素,在图形学中,有三种常用的卷积核(边缘检测因子),他们分别是:

  • Roberts 算子:由拉里·罗伯茨(Larry Roberts)于1965年提出
  • Prewitt 算子:由约翰·普雷维特(John Prewitt)于1970年提出
  • Sobel 算子:由欧文·索贝尔(Irwin Sobel)于1968年提出

图形学中最常用的还是Sobel算子,因为它更适合高精度的边缘检测

可以看到三种算子都包含了两个方向的卷积核,他们分别用来检测水平和竖直方向上的边缘信息
边缘检测的卷积计算时,只需要对每个像素进行两次卷积计算即可,这样就可以得到两个方向的梯度值 Gx 和 Gy,而该像素的整体梯度值 G = abs(Gx) + abs(Gy)

已经知道了通过卷积获取梯度值的基本原理那么我们只需要定义一个描边颜色,利用Shader中的内置函数lerp,在原始颜色和描边颜色之间利用梯度值进行插值即可
最终颜色 = lerp(原始颜色,描边颜色,梯度值)
梯度值越大表明越接近边缘,则颜色越接近描边颜色;反之越接近原始颜色

2、如何得到当前像素周围8个像素位置

想要获取当前像素周围8个像素的位置,我们需要Unity 提供给我们用于访问纹理对应的每个纹素(像素)的大小 的变量——float4 纹理名_TexelSize
类似 纹理名_ST(用于获取纹理缩放偏移的变量)
其中的xyzw分别代表(假设纹理宽高为 1024 * 768)

  • x:1/纹理宽度 = 1/1024
  • y:1/纹理高度 = 1/768
  • z:纹理宽度 = 1024
  • w:纹理高度 = 768

可以利用 float4 纹理名_TexelSize 纹素 信息得到当前像素周围8个像素位置,可以进行uv坐标偏移计算,在 顶点着色器函数 或者 片元着色器函数 中计算都行,但是建议在顶点着色器函数中计算,可以节约计算量,片元着色器中直接使用插值的结果也不会影响纹理坐标的计算结果

3、边缘检测的实现

Shader "Unlit/EdgeDetection"
{Properties{_MainTex ("Texture", 2D) = "white" {}_EdgeColor ("EdgeColor", Color) = (0, 0, 0, 0)_EdgeFactor ("EdgeFactor", Float) = 1}SubShader{Tags { "RenderType"="Opaque" }Pass{ZTest AlwaysCull OffZWrite OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct v2f{half2 uv[9] : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;half4 _MainTex_TexelSize;fixed4 _EdgeColor;fixed _EdgeFactor;v2f vert (appdata_base v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);half2 uv = v.texcoord;o.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1, -1);o.uv[1] = uv + _MainTex_TexelSize.xy * half2(-1, 0);o.uv[2] = uv + _MainTex_TexelSize.xy * half2(-1, 1);o.uv[3] = uv + _MainTex_TexelSize.xy * half2(0, -1);o.uv[4] = uv + _MainTex_TexelSize.xy * half2(0, 0);o.uv[5] = uv + _MainTex_TexelSize.xy * half2(0, 1);o.uv[6] = uv + _MainTex_TexelSize.xy * half2(1, -1);o.uv[7] = uv + _MainTex_TexelSize.xy * half2(1, 0);o.uv[8] = uv + _MainTex_TexelSize.xy * half2(1, 1);return o;}// 计算灰度值fixed4 calcuminance(fixed4 color){return 0.2126 * color.r + 0.71522 * color.g + 0.0722 * color.b;}half Sobel(v2f o){half Gx[9] = {-1, -2, -1,0,   0,  0,1,   2,  1};half Gy[9] = {-1,  0,  1,-2,  0,  2,-1,  0,  1};half L;half edgeX = 0;half edgeY = 0;for (int i = 0; i < 9; i++){// 采样后获取灰度值L = calcuminance(tex2D(_MainTex, o.uv[i]));edgeX += L * Gx[i];edgeY += L * Gy[i];}return abs(edgeX) + abs(edgeY);}fixed4 frag (v2f i) : SV_Target{half edge = Sobel(i);return lerp(tex2D(_MainTex, i.uv[4]), _EdgeColor, edge * _EdgeFactor);}ENDCG}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EdgeDetection : PostEffectBase
{public Color EdgeColor;[Range(0, 1)]public float EdgeFactor;protected override void UpdateProperty() {if (material != null) {material.SetFloat("_EdgeFactor", EdgeFactor);material.SetColor("_EdgeColor", EdgeColor);}}
}

 4、边缘检测的纯色背景功能

边缘描边时,有时只想保留描边的边缘线,不想要显示原图的背景颜色,比如把整个背景变为白色、黑色、等等自定义颜色,而抛弃掉原本图片的颜色信息,效果就像是一张描边图片

需要做以下:

(1)新属性声明

  • 添加 背景颜色程度变量 _BackgroundExtent 0表示保留图片原始颜色,1表示完全抛弃图片原始颜色,0~1之间可以自己控制保留程度
  • 添加自定义背景颜色 _BackgroundColor,定义用于替换图片原始颜色的颜色

(2)修改片元着色器

  • 利用插值运算,记录纯色背景中像素描边颜色
  • 利用插值运算,在 原始图片描边 和 纯色图片描边 之间用程度变量进行控制
Shader "Unlit/EdgeDetection"
{Properties{_MainTex ("Texture", 2D) = "white" {}_EdgeColor ("EdgeColor", Color) = (0, 0, 0, 0)_EdgeFactor ("EdgeFactor", Float) = 1_BackgroundExtent ("BackgroundExtent", Range(0, 1)) = 0_BackgroundColor ("BackgroundColor", Color) = (1, 1, 1, 1)}SubShader{Tags { "RenderType"="Opaque" }Pass{ZTest AlwaysCull OffZWrite OffCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct v2f{half2 uv[9] : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;half4 _MainTex_TexelSize;fixed4 _EdgeColor;fixed _EdgeFactor;fixed _BackgroundExtent;fixed4 _BackgroundColor;v2f vert (appdata_base v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);half2 uv = v.texcoord;o.uv[0] = uv + _MainTex_TexelSize.xy * half2(-1, -1);o.uv[1] = uv + _MainTex_TexelSize.xy * half2(-1, 0);o.uv[2] = uv + _MainTex_TexelSize.xy * half2(-1, 1);o.uv[3] = uv + _MainTex_TexelSize.xy * half2(0, -1);o.uv[4] = uv + _MainTex_TexelSize.xy * half2(0, 0);o.uv[5] = uv + _MainTex_TexelSize.xy * half2(0, 1);o.uv[6] = uv + _MainTex_TexelSize.xy * half2(1, -1);o.uv[7] = uv + _MainTex_TexelSize.xy * half2(1, 0);o.uv[8] = uv + _MainTex_TexelSize.xy * half2(1, 1);return o;}// 计算灰度值fixed4 calcuminance(fixed4 color){return 0.2126 * color.r + 0.71522 * color.g + 0.0722 * color.b;}half Sobel(v2f o){half Gx[9] = {-1, -2, -1,0,   0,  0,1,   2,  1};half Gy[9] = {-1,  0,  1,-2,  0,  2,-1,  0,  1};half L;half edgeX = 0;half edgeY = 0;for (int i = 0; i < 9; i++){// 采样后获取灰度值L = calcuminance(tex2D(_MainTex, o.uv[i]));edgeX += L * Gx[i];edgeY += L * Gy[i];}return abs(edgeX) + abs(edgeY);}fixed4 frag (v2f i) : SV_Target{half edge = Sobel(i);fixed4 withEdgeColor = lerp(tex2D(_MainTex, i.uv[4]), _EdgeColor, edge * _EdgeFactor);fixed4 onlyEdgeColor = lerp(_BackgroundColor, _EdgeColor, edge * _EdgeFactor);return lerp(withEdgeColor, onlyEdgeColor, _BackgroundExtent);}ENDCG}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class EdgeDetection : PostEffectBase
{public Color EdgeColor;public Color BackgroundColor;[Range(0, 1)]public float EdgeFactor;[Range(0, 1)]public float BackgroundExtent;protected override void UpdateProperty() {if (material != null) {material.SetFloat("_EdgeFactor", EdgeFactor);material.SetFloat("_BackgroundExtent", BackgroundExtent);material.SetColor("_EdgeColor", EdgeColor);material.SetColor("_BackgroundColor", BackgroundColor);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/10992.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

架构篇(05理解架构的服务演化)

目录 学习前言 一、服务演化简介 二、方向一&#xff1a;架构服务化 单体分层架构 面向服务架构 - SOA 微服务架构 - Microservices 云原生架构 - Cloud Native 三、方向二&#xff1a;部署容器编排化 虚拟机 容器 Kubernetes 与编排 四、参考文献 学习前言 Kubern…

娶老婆花了30万彩礼,结婚2个月,她前夫给我20万,让我老婆和他生孩子!

我叫李志强&#xff0c;今年32岁&#xff0c;在一家物流公司当经理。去年我娶了我老婆张美玲&#xff0c;为了这场婚礼&#xff0c;我花了30万彩礼。美玲比我小3岁&#xff0c;是个护士&#xff0c;长得漂亮又温柔&#xff0c;我觉得自己找到了真爱。 结婚前&#xff0c;美玲就…

基于SpringBoot的国风服装商城系统+LW示例参考

1.项目介绍 系统角色&#xff1a;管理员、普通用户功能模块&#xff1a;管理员&#xff08;用户管理、商品管理、分类管理、订单管理、系统管理、在线客服等&#xff09;&#xff0c;普通用户&#xff08;登录注册、个人中心、评价管理、收藏管理、订单管理等、咨询服务等&…

GB/T 43206—2023信息安全技术信息系统密码应用测评要求(五)

文章目录 附录AA.1 概述A.2 密钥产生A.3 密钥分发A.4 密钥存储A.5 密钥使用A.6 密钥更新A.7 密钥归档A. 8 密钥撤销A.9 密钥备份A.10 密钥恢复A.11 密钥销毁 附录B附录C 附录A A.1 概述 密钥管理对于保证密钥全生存周期的安全性至关重要 ,可以保证密钥(除公开密钥外) 不被非授…

jmeter常用配置元件介绍总结之前置处理器、测试片段

系列文章目录 安装jmeter jmeter常用配置元件介绍总结之前置处理器、测试片段 6.前置处理器6.1用户参数6.2取样器超时6.3.测试片段6.4JSR223 PreProcessor6.5.JDBC PreProcessor 6.前置处理器 在取样器请求之前执行的操作&#xff0c;优先级比取样器高&#xff0c;用来处理一些…

【IT人物系列】之Java之父

前言 当今世界由无数的人构成&#xff0c;其中有些人做了一些改变世界的事情&#xff0c;比如&#xff1a;乔布斯缔造了Apple帝国&#xff0c;‌詹姆斯高斯林创造了Java语言等。正是这些优秀的人做的这些优秀的事情&#xff0c;让这个世界更加美好。因此他们值得铭记。 从今天…

鸿蒙开发基础入门

一、熟悉目录结构 二、ArkTS语法介绍 ArkTS是为构建高性能应用设计的编程语言&#xff0c;语法继承TypeScript&#xff0c;并进行了优化&#xff0c;拥有更强的类型约束ArkTS提供了声明式UI范式&#xff0c;符合移动开发的最新趋势 ArkTS摒弃了部分影响运行时的性能的语法&…

大数据机器学习算法和计算机视觉应用01:博弈论基础

Game Theory 2-player Zero Sum GameMinimax Optimal StrategiesVon Neumann’s Minimax TheoremLower Bounds for Randomized AlgorithmsGeneral sum games, Nash quilibria (p.s:该系列是国际交流学术公开课的笔记&#xff0c;主讲人是Carnegie Melon University的终身教授…

如何安装和配置JDK17

教程目录 零、引言1、新特性概览2、性能优化3、安全性增强4、其他改进5、总结 一、下载安装二、环境配置三、测试验证 零、引言 JDK 17&#xff08;Java Development Kit 17&#xff09;是Java平台的一个重要版本&#xff0c;它带来了许多新特性和改进&#xff0c;进一步提升了…

【C++进阶】智能指针的使用及原理(1)

1. 智能指针的使用场景分析 下面程序中我们可以看到&#xff0c;new了以后&#xff0c;我们也delete了&#xff0c;但是因为抛异常导&#xff0c;后面的delete没有得到执行&#xff0c;所以就内存泄漏了&#xff0c;所以我们需要new以后捕获异常&#xff0c;捕获到异常后delete…

计算机课程管理:Spring Boot实现的工程认证路径

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于工程教育认证的计算机课程管理平台的开发全过程。通过分析基于工程教育认证的计算机课程管理平台管理的不足&#xff0c;创建了一个计算机管理基于工程教育认…

【人工智能训练师】综合案例 HBase与Hive的集成

9.1 HBase与Hive 任务目的 简单回顾了解hive 了解hive与hbase的区别 任务清单 任务1&#xff1a;hive简介 任务2&#xff1a;hbase与hive的区别 任务步骤 任务1&#xff1a;hive简介   什么是Hive呢&#xff1f; Apache Hive是一个构建在Hadoop基础设施之上的数据仓库。 构…

基于STM32的图像处理监控系统

1. 引言 随着物联网和智能家居的普及&#xff0c;图像处理和监控系统在安全防范、家庭监控等方面应用越来越广泛。本项目旨在使用STM32开发板和OV7670摄像头模块搭建一个简单的图像处理监控系统。系统能够捕获图像并进行基本的处理与展示。 2. 环境准备2.1 硬件需求 - STM32开…

QML-简单项目实战一

一、简介 使用QML创建一个简单的登录界面&#xff0c;代码内容来源于bilibili中的视频。 实现效果图如下&#xff1a; 二、实现步骤 1. 核心控件和布局管理和登录事件处理 import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Window 2.12 /*1. 核心控件和布局…

字节青训-小F的永久代币卡回本计划、

目录 一、小F的永久代币卡回本计划 问题描述 测试样例 解题思路&#xff1a; 问题理解&#xff1a; 数学公式&#xff1a; 代码实现&#xff1a; 最终代码&#xff1a; 运行结果&#xff1a; 二、构造特定数组的逆序拼接 问题描述 测试样例 解题思路&#xff1a;…

[ Linux 命令基础 4 ] Linux 命令详解-文本处理命令

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

06:(寄存器开发)对上电/复位的SystemInit函数进行分析

SystemInit函数分析 通过第5章的时钟树的学习&#xff0c;基本了解了SystemClock总线&#xff0c;AHB总线&#xff0c;APB1总线&#xff0c;APB2总线的时钟频率。那么单片机一上电或者按下复位时&#xff0c;这些总线的时钟频率是如何变化的喃&#xff1f; 这和STM32的启动文件…

C++ : STL容器(适配器)之stack、queue剖析

STL容器适配器之stack、queue剖析 一、stack、queue的接口&#xff08;一&#xff09;stack 接口说明&#xff08;二&#xff09;queue 接口说明 二、stack、queue的模拟实现&#xff08;一&#xff09;stack、queue是容器适配器stack、queue底层默认容器--deque1、deque概念及…

三菱QD77MS定位模块外部信号选择功能

“外部信号选择功能” 是在使用上/下限限位信号和近点狗信号的情况下&#xff0c;从以下信号中选择的功能。 "QD77MS的外部输入信号 "伺服放大器的外部输入信号 "经由 CPU 的外部输入信号(QD77MS 的缓冲存储器) 经由 CPU 的外部输入信号(QD77MS 的缓冲存储器)的…

Vue3-06_路由

路由 后台路由是根据请求url&#xff0c;匹配请求处理的后台模块&#xff08;路径&#xff09; 前台根据访问路径&#xff0c;决定显示的内容。 路由就是&#xff1a; 访问hash 与内容的对应关系 路由的工作方式 用户点击页面的路由链接导致url地址栏中的Hash值发生了变化前…