OpenGL入门最后一章观察矩阵(照相机)
前面的一篇文章笔者向大家介绍了模型变化矩阵,投影矩阵。现在只剩下最后一个观察矩阵没有和大家讲了。此片文章就为大家介绍OpenGL入门篇的最后一个内容。
观察矩阵
前面的篇章当中,我们看到了即使没有观察矩阵,我们也能对绘制出来的模型有一个很好的控制,那么观察矩阵存在的意义又是什么了?那么我们现在来想象一下这样一件事,我们的屏幕上绘制了特别多的方块,现在我们要把它们全部向屏幕的右边移动一定的距离,如果是前面的做法的话所有的模型都要再乘上一个向右平移的矩阵,这样做完全没有问题,只是这会让程序的效率变得很差,而且你的GPU除了计算一下像素颜色之外基本上就没有做其他任何事,把绝大部分的任务都交给了CPU,这样的任务分配是不合理的,所以我们需要上传一个观察矩阵ViewMatrix给GPU,让他把所有的顶点按照某一个方向进行平移,这样我们的任务就合理分配了。(什么样的任务应该交给CPU,什么样的任务又交给GPU这里也有不少东西需要研究)
看过资料的朋友们都知道,在OpenGL当中想用创建一个观察矩阵只需要使用glm::lookAt函数即可
glm::lookAt(m_Position, m_Forward, m_Up);
//glm::vec3 m_Position 照相机的位置
//glm::vec3 m_Forward 观察的位置
//glm::vec3 m_Up 相机屏幕的上方的方向
这样我们就得到了一个观察矩阵了,看起来也没什么好说的对不对。但是困难的往往是处理与之相关的数学问题,前面说我们要让屏幕上的所有的方块全部向右移动一段距离,那么我们应该怎么设置这个观察矩阵了?直接给答案
glm::vec3 m_Position(-0.3f,0.0f,3.0f)
glm::vec3 m_Forward(-0.3f,0.0f,0.0f);
glm::vec3 m_Up(0.0f,1.0f,0.0f);glm::lookAt(m_Position, m_Forward, m_Up);
可能有的朋友们就要问了,相机为什么要往左边移动,并且看向左边了?要回答这个问题也很简单,大家现在把手机拿出来打开照相机,然后手机屏幕与电脑屏幕保持水平,现在把手机水平向左开始移动,你就会发现手机屏幕中的物体朝着反方向进行移动了,如果再把手机对准刚才位置,你就会发现所有的物体都行了旋转拉绳,这当然不是我们想要的结果,所以我们要让手机继续朝向正前方。观察矩阵和我们的照相机的工作原理一摸一样,所以观察矩阵也可以叫作相机矩阵。
制作一个可以观察物体的矩阵
观察矩阵其实介绍到这里也就算是结束了,我们现在来做一个可以实际使用的相机。比如说我现在制作了一个立方体,每一个面的颜色都不一样,它刚被绘制出来的时候我只能看见一两个面,但是我想观察其他的面,如果说我去修改模型让它自己转过来,这也有点太蠢了!所以我们要做一个进行鼠标控制的照相机。
我的打算是,让照相机沿着一个球面进行运动,鼠标坐标的变化来确定照相机在球体坐标上的夹角。

鼠标x方向上的偏移量决定角的大小,鼠标y方向上的偏移量决定
角的大小,有了理论的基础过后我们看一下具体如何实现。
照相机类:
Camera.h
#pragma once
#include<glm/glm.hpp>class Camera {
public:Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yam = 0.0f, float pitch =90.0f);Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch);glm::mat4 GetViewMatrix();void ProcessMouseMovement(float xoffset, float yoffset, bool constrainPitch = true);void ProcessMouseScroll(float yoffset);inline float GetCameraZoom() const { return m_Zoom; }
private:void UpdateCameraVectors();private:glm::vec3 m_Position, m_Front, m_Up, m_Right, m_WorldUp;float m_Yaw, m_Pitch;float m_MouseSensitivity;float m_Zoom;
};
Camera.cpp
#include<glm/gtc/matrix_transform.hpp>
#include<iostream>#include"Camera.h"Camera::Camera(glm::vec3 position ,glm::vec3 up,float yam,float pitch):m_Front(glm::vec3(0.0f,0.0f,-1.0f)),m_MouseSensitivity(0.1f),m_Zoom(45.0f) {m_Position = position;m_WorldUp = up;m_Yaw = yam;m_Pitch = pitch;UpdateCameraVectors();
}Camera::Camera(float posX, float posY, float posZ, float upX, float upY, float upZ, float yaw, float pitch):m_Front(glm::vec3(0.0f,0.0f,-1.0f)),m_MouseSensitivity(0.1f),m_Zoom(45.0f) {m_Position = glm::vec3(posX, posY, posZ);m_WorldUp = glm::vec3(upX, upY, upZ);m_Yaw = yaw;m_Pitch = pitch;UpdateCameraVectors();
}glm::mat4 Camera::GetViewMatrix() {return glm::lookAt(m_Position, glm::vec3(0.0f, 0.0f, 0.0f), m_Up);
}void Camera::ProcessMouseMovement(float xoffset, float yoffset, bool constrainPitch){xoffset *= m_MouseSensitivity;yoffset *= m_MouseSensitivity;m_Yaw += xoffset;m_Pitch += yoffset;if (constrainPitch) {m_Pitch = m_Pitch > 179.0f ? 179.0f : m_Pitch;m_Pitch = m_Pitch < 0.1f ? 0.1f : m_Pitch;}UpdateCameraVectors();
}void Camera::ProcessMouseScroll(float yoffset) {m_Zoom -= yoffset;m_Zoom = m_Zoom < 1.0f ? 1.0f : m_Zoom;m_Zoom = m_Zoom > 45.0f ? 45.0f : m_Zoom;
}void Camera::UpdateCameraVectors() {glm::vec3 position(0.0f,0.0f,0.0f);position.x = 5.0f * sinf(glm::radians(m_Yaw)) * sinf(glm::radians(m_Pitch));position.y = 5.0f * cosf(glm::radians(m_Pitch));position.z = 5.0f * cosf(glm::radians(m_Yaw)) * sinf(glm::radians(m_Pitch));m_Front = glm::normalize(-position);m_Position = position;m_Right = glm::normalize(glm::cross(m_Front, m_WorldUp));m_Up = glm::normalize(glm::cross(m_Right, m_Front));
}
除此以外,我们还需要处理鼠标输入函数
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn) {static float lastX = 320.0f, lastY = 240.0f;static bool firstMouse = true;float xpos = static_cast<float>(xposIn);float ypos = static_cast<float>(yposIn);if (firstMouse) {lastX = xpos;lastY = ypos;firstMouse = false;}float xoffset = xpos - lastX;float yoffset = lastY - ypos;lastX = xpos, lastY = ypos;camera.ProcessMouseMovement(xoffset, yoffset);
}void scroll_callback(GLFWwindow* window, double xoffsetIn, double yoffsetIn) {camera.ProcessMouseScroll(static_cast<float>(yoffsetIn));
}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)run = false;
}
好的主函数,贴在下面,我们就得到了一个可以绕着物体圆转的照相了,因为窗口隐藏了光标,想要推出的画按Esc键即可。
#include<glad/glad.h>
#include<GLFW/glfw3.h>#include<iostream>
#include<glm/gtc/matrix_transform.hpp>#include"Shader.h"
#include"Camera.h"static Camera camera(glm::vec3(0.0f, 0.0f, 5.0f));
static bool run = true;
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
void mouse_callback(GLFWwindow* window, double xposIn, double yposIn);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);int main() {glfwInit();GLFWwindow* window = glfwCreateWindow(640, 480, "Triangles", NULL, NULL);glfwMakeContextCurrent(window);glfwSetCursorPosCallback(window, mouse_callback);glfwSetScrollCallback(window, scroll_callback);glfwSetKeyCallback(window, key_callback);glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);//需要初始化GLADif (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {std::cout << "Failed to initialize GLAD" << std::endl;return -1;}float vertexes[] = {//front surface-0.5f, -0.5f, 0.5f, 1.0f, 1.0f,0.588f,0.2784f, //00.5f, -0.5f, 0.5f, 1.0f, 1.0f,0.588f,0.2784f, //10.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.588f,0.2784f, //2-0.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.588f,0.2784f, //3//back surface-0.5f, -0.5f, -0.5f, 1.0f, 0.933f,0.9098f,0.6666f, //40.5f, -0.5f, -0.5f, 1.0f, 0.933f,0.9098f,0.6666f, //50.5f, 0.5f, -0.5f, 1.0f, 0.933f,0.9098f,0.6666f, //6-0.5f, 0.5f, -0.5f, 1.0f, 0.933f,0.9098f,0.6666f, //7//up surface-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.749f,1.0f, //80.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.749f,1.0f, //90.5f, 0.5f, -0.5f, 1.0f, 0.0f,0.749f,1.0f, //10-0.5f, 0.5f, -0.5f, 1.0f, 0.0f,0.749f,1.0f, //11//down surface-0.5f, -0.5f, 0.5f, 1.0f, 0.498f,1.0f,0.0f, //120.5f, -0.5f, 0.5f, 1.0f, 0.498f,1.0f,0.0f, //130.5f, -0.5f, -0.5f, 1.0f, 0.498f,1.0f,0.0f, //14-0.5f, -0.5f, -0.5f, 1.0f, 0.498f,1.0f,0.0f, //15//left surface-0.5f, -0.5f, -0.5f, 1.0f, 0.5f,0.5f,0.145f, //16-0.5f, -0.5f, 0.5f, 1.0f, 0.5f,0.5f,0.145f, //17-0.5f, 0.5f, 0.5f, 1.0f, 0.5f,0.5f,0.145f, //18-0.5f, 0.5f, -0.5f, 1.0f, 0.5f,0.5f,0.145f, //19//right surface0.5f, -0.5f, -0.5f, 1.0f, 1.0f,0.8941f,0.7686f, //200.5f, -0.5f, 0.5f, 1.0f, 1.0f,0.8941f,0.7686f, //210.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.8941f,0.7686f, //220.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.8941f,0.7686f //24}; unsigned int indexes[] = {//front surface0,1,2,2,3,0,//back surface4,5,6,6,7,4,//up surface8,9,10,10,11,8,//down surface12,13,14,14,15,12,//left surface16,17,18,18,19,16,//right surface20,21,22,22,23,20};glEnable(GL_DEPTH_TEST);unsigned int buffer = 0, vertexArray = 0, indexBuffer = 0;glCreateVertexArrays(1, &vertexArray);glBindVertexArray(vertexArray);glCreateBuffers(1, &buffer);glBindBuffer(GL_ARRAY_BUFFER, buffer);glBufferData(GL_ARRAY_BUFFER, sizeof(vertexes), vertexes, GL_STATIC_DRAW);glEnableVertexAttribArray(0);glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), NULL);glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (const void*)(4 * sizeof(float)));glCreateBuffers(1, &indexBuffer);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexes), indexes, GL_STATIC_DRAW);Shader* pShader = new Shader("assets/shaders/TextureShader.glsl");while (!glfwWindowShouldClose(window) && run) {glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glm::mat4 project = glm::perspective(glm::radians(camera.GetCameraZoom()), 640.0f / 480.0f, 0.1f, 100.0f);glm::mat4 view = camera.GetViewMatrix();glm::mat4 ViewProject = project * view;pShader->Bind();pShader->UploadUniformat4("u_ViewProject", ViewProject);glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, NULL);glfwSwapBuffers(window);glfwPollEvents();}delete pShader;glfwDestroyWindow(window);glfwTerminate();
}void mouse_callback(GLFWwindow* window, double xposIn, double yposIn) {static float lastX = 320.0f, lastY = 240.0f;static bool firstMouse = true;float xpos = static_cast<float>(xposIn);float ypos = static_cast<float>(yposIn);if (firstMouse) {lastX = xpos;lastY = ypos;firstMouse = false;}float xoffset = xpos - lastX;float yoffset = lastY - ypos;lastX = xpos, lastY = ypos;camera.ProcessMouseMovement(xoffset, yoffset);
}void scroll_callback(GLFWwindow* window, double xoffsetIn, double yoffsetIn) {camera.ProcessMouseScroll(static_cast<float>(yoffsetIn));
}void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)run = false;
}
注意
你可能注意到了,相机是不能绕过Y轴顶点的,但这里要声明的是并不是笔者的代码写错了,这是因为,如果绕道Y轴顶点,那么你看向的方向和世界向上的方向就平行了,这两个向量叉乘的结果是一个0向量,也就是根本没办法计算相机的右侧是那一边,相机的上边也同样没办法计算。这是纯数学问题导致的,纯数学问题也是最难解决的问题,现在多软件的相机使用都是四元数来解决这个问,有兴趣的朋友可以去查找相关资料。
总结
此片文章看完OpenGL入门基础篇已经写完了,相信你也可以做一个像样的游戏出来了,比如以前红白机上的一些游戏,可能觉得很Low,但凡事都要一步一个脚印,游戏也是一路发展过来的,没有多少人生来就特别牛。笔者在这里感谢大家的支持。
相关文章:
OpenGL入门最后一章观察矩阵(照相机)
前面的一篇文章笔者向大家介绍了模型变化矩阵,投影矩阵。现在只剩下最后一个观察矩阵没有和大家讲了。此片文章就为大家介绍OpenGL入门篇的最后一个内容。 观察矩阵 前面的篇章当中,我们看到了即使没有观察矩阵,我们也能对绘制出来的模型有一…...
ES6中定义私有属性详解
在ES6中,定义私有属性的方式相对传统的JavaScript有所不同。ES6并没有提供直接的语法来定义私有属性,但可以通过几种方法间接实现私有属性。 1. 使用Symbol来模拟私有属性 Symbol是一种新的数据类型,可以作为对象的键,并且它的值…...
工业5G路由器让无人机数据传输 “飞” 起来
无人机上搭载5G通信模块,该模块与工业5G路由器通过5G网络建立连接。无人机的飞控系统、传感器以及摄像头等设备采集到的数据,如飞行姿态、高度、速度、环境图像、温度湿度等,经过编码、加密、调制等处理后转换为适合5G网络传输的信号形式。 …...
面试经典150题——滑动窗口
文章目录 1、长度最小的子数组1.1 题目链接1.2 题目描述1.3 解题代码1.4 解题思路 2、无重复字符的最长子串2.1 题目链接2.2 题目描述2.3 解题代码2.4 解题思路 3、串联所有单词的子串3.1 题目链接3.2 题目描述3.3 解题代码3.4 解题思路 4、最小覆盖子串4.1 题目链接4.2 题目描…...
MiFlash 线刷工具下载合集
MiFlash 线刷工具下载合集 MiFlash 线刷工具下载合集 – MIUI历史版本相较于小米助手的刷机功能,线刷还是偏好使用 MiFlash。特点是界面简单纯粹,有自定义高级选项,可以选择刷机不上 BL 锁,自定义刷机脚本,EDL 刷机模…...
【MySQL高级】第1-4章
第1章 存储过程 1.1 什么是存储过程? 存储过程可称为过程化SQL语言,是在普通SQL语句的基础上增加了编程语言的特点,把数据操作语句(DML)和查询语句(DQL)组织在过程化代码中,通过逻辑判断、循环等操作实现复杂计算的程序语言。 换…...
课程设计项目之基于Python实现围棋游戏代码
项目介绍 游戏进去默认为九路玩法,当然也可以选择十三路或是十九路玩法 使用pycharam打开项目,pip安装模块并引用,然后运行即可, 代码每行都有详细的注释,可以做课程设计或者毕业设计项目参考 效果预览 源码下载 h…...
uni-app tab 双击事件监听
1、data中定义属性,用于临时记录点击次数 tabClick: {touchNum: 0 },2、添加页面事件监听方法 onTabItemTap(e) {this.tabClick.touchNumsetTimeout(()>{if(this.tabClick.touchNum > 2){// 双击执行代码区}this.tabClick.touchNum 0}, 250) },个人博客&am…...
如何在Maxscript脚本中检查磁盘可用空间?
在我们实际工作中,有时需要在工作开始之前检查磁盘的可用空间,比如渲染。 当然,我们可以人工很容易查看电脑中各个磁盘的空间使用情况,但是,如果是Maxscript插件完成的工作,那么如何才能实现其工作之前对磁…...
pytorch梯度上下文管理器介绍
PyTorch 提供了多种梯度上下文管理器,用于控制自动梯度计算 (autograd) 的行为。这些管理器在训练、推理和特殊需求场景中非常有用,可以通过显式地启用或禁用梯度计算,优化性能和内存使用。 主要梯度上下文管理器 torch.no_grad(): 功能&am…...
Redis Stream:实时数据处理的高效解决方案
Redis Stream:实时数据处理的高效解决方案 引言 在当今这个数据驱动的时代,实时数据处理对于各种应用场景都至关重要。Redis,作为一个高性能的键值存储系统,自然也紧跟这一趋势,推出了Redis Stream——一种用于处理实…...
使用交换机构建简单局域网
创建交换机SW-1 交换机接口规划 序号交换机名接口连接设备接口类型1SW-1Ethernet 0/0/1Host-1默认2SW-1Ethernet 0/0/2Host-2默认3SW-1Ethernet 0/0/5Host-3默认4SW-1Ethernet 0/0/6Host-4默认 主机IP规划 Host-1:192.168.64.11/24,接入SW-1 Ethernet…...
基于MATLAB的冰箱水果保鲜识别系统
摘要:本作品旨在研究和实现基于MATLAB软件的冰箱水果保鲜识别系统,针对多种常见水果混合的图像进行处理和识别。首先,根据水果与背景的差异选择合适的阈值,对图像进行去噪和对比度增强,然后进行二值化处理。接下来&…...
Flink源码解析之:Flink On Yarn模式任务提交部署过程解析
Flink源码解析之:Flink On Yarn模式任务提交部署过程解析 一、Flink on Yarn部署模式概述 Apache Hadoop YARN 在许多数据处理框架中都很流行。 Flink 服务提交给 YARN 的 ResourceManager,后者会在 YARN NodeManagers 管理的机器上生成容器。 Flink 将…...
吊舱激光测距核心技术详解!
一、核心技术 吊舱激光测距的核心技术主要体现在激光发射与接收、信号处理与距离计算、以及数据校正与优化等方面。 激光发射与接收: 激光发射器:产生经过调制的激光束,该激光束具有特定的频率和波形。这些激光束被投射到目标物体上。 光…...
[ZJCTF 2019]NiZhuanSiWei
检查通过 file_get_contents 函数读取 $text 变量指定的文件内容是否等于字符串 "welcome to the zjctf"。 测试了一下直接传参,然后进入下一阶段,通过php伪协议读取useless.php发现不行,我们使用data协议将其输入进去试试 读取到…...
Kafka配置公网或NLB访问(TCP代理)
这套配置适用于TCP代理和公网访问 分几种场景,正常来说我们直接使用kafka IP地址访问就行,考虑到网络架构和环境安全,需要使用公网或代理访问kafka时就需要对kafka进行一些额外配置 EXTERNAL这个地址需要监听本地地址,之后kafka…...
大模型推理:vllm多机多卡分布式本地部署
文章目录 1、vLLM分布式部署 docker镜像构建通信环境配置 2、其他大模型部署工具3、问题记录参考文献 单台机器GPU资源不足以执行推理任务时,一个方法是模型蒸馏量化,结果就是会牺牲些效果。另一种方式是采用多台机器多个GPU进行推理,资源不…...
clickhouse-backup配置及使用(Linux)
一、下载地址 Releases Altinity/clickhouse-backup GitHub 二、上传到服务器解压安装 自行上传至服务器,解压命令: tar xvf clickhouse-backup-linux-amd64.tar.gz 三、创建软连接 sudo ln -sv build/linux/amd64/clickhouse-backup /usr/local/bin/…...
【YashanDB知识库】启动yasom时报错:sqlite connection error
本文内容来自YashanDB官网,原文内容请见 https://www.yashandb.com/newsinfo/7817893.html?templateId1718516 【标题】启动yasom时报错:sqlite connection error 【问题分类】安装部署 【关键字】错误码sqlite3.Error、yasom启动失败、共享集群 、u…...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
