【开发日志】2023.04 ZENO----Composite----CompNormalMap
NormalMap-Online (cpetry.github.io)
https://cpetry.github.io/NormalMap-Online/
CompNormalMap
将灰度图像转换为法线贴图
将灰度图像转换为法线贴图是一种常见的技术,用于在实时图形渲染中增加表面细节。下面是一个简单的方法来将灰度图像转换为法线贴图:
-
加载灰度图像,并将其转换为浮点数值范围[0, 1]。
-
对于每个像素,计算其相邻像素与其距离,并计算它们之间的斜率。这可以通过使用Sobel算子或其他边缘检测算法来完成。
-
将每个像素的斜率向量归一化为单位长度,并转换为范围[-1, 1]内的值。
-
将每个像素的斜率向量转换为法线向量。法线向量可以通过使用以下公式计算得出:$normal = (2R - 1, 2G - 1, B)$,其中R,G和B分别是像素的红、绿和蓝通道值。由于我们的灰度图像没有颜色信息,我们可以将R,G和B设置为0.5,以得到一个朝向Z轴正方向的法线。
-
存储每个像素的法线向量作为输出法线贴图。
需要注意的是,这个简单的方法只能生成表面细节的近似值,并且可能会在处理像素边缘和细节时出现不准确的结果。更复杂的技术可以使用高斯过滤和其他方法来平滑法线图并产生更精细的结果。
Input:
Expect:
Output:
struct CompNormalMap : INode {virtual void apply() override {auto image = get_input<PrimitiveObject>("image");auto &ud = image->userData();int w = ud.get2<int>("w");int h = ud.get2<int>("h");using normal = std::tuple<float, float, float>;normal n = {0, 0, 1};float n0 = std::get<0>(n);float n1 = std::get<1>(n);float n2 = std::get<2>(n);std::vector<normal> normalmap;normalmap.resize(image->size());float gx = 0;float gy = 0;float gz = 1;for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {int idx = i * w + j;if (i == 0 || i == h || j == 0 || j == w) {normalmap[idx] = {0, 0, 1};}}}for (int i = 1; i < h-1; i++) {for (int j = 1; j < w-1; j++) {int idx = i * w + j;gx = (image->verts[idx+1][0] - image->verts[idx-1][0]) / 2.0f * 255;gy = (image->verts[idx+w][0] - image->verts[idx-w][0]) / 2.0f * 255;// 归一化法线向量float len = sqrt(gx * gx + gy * gy + gz * gz);gx /= len;gy /= len;gz /= len;// 计算光照值gx = 0.5f * (gx + 1.0f) ;gy = 0.5f * (gy + 1.0f) ;gz = 0.5f * (gz + 1.0f) ;normalmap[i * w + j] = {gx,gy,gz};}}for (int i = 0; i < h; i++) {for (int j = 0; j < w; j++) {int idx = i * w + j;image->verts[i * w + j][0] = std::get<0>(normalmap[i * w + j]);image->verts[i * w + j][1] = std::get<1>(normalmap[i * w + j]);image->verts[i * w + j][2] = std::get<2>(normalmap[i * w + j]);}}set_output("image", image);}
};
ZENDEFNODE(CompNormalMap, {{{"image"}},{{"image"}},{},{ "comp" },
});
cv
#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main()
{Mat grayImage = imread("gray_image.png", IMREAD_GRAYSCALE);if (grayImage.empty()){cerr << "Could not read input image" << endl;return -1;}Mat normalMap(grayImage.size(), CV_8UC3);for (int i = 1; i < grayImage.rows - 1; i++){for (int j = 1; j < grayImage.cols - 1; j++){double dx = grayImage.at<uchar>(i, j + 1) - grayImage.at<uchar>(i, j - 1);double dy = grayImage.at<uchar>(i + 1, j) - grayImage.at<uchar>(i - 1, j);Vec3b normal(dx, dy, 255);normalize(normal, normal);normalMap.at<Vec3b>(i, j) = normal * 127.5 + Vec3b(127.5, 127.5, 127.5);}}imwrite("normal_map.png", normalMap);return 0;
}
不调库
#include <iostream>
#include <fstream>
#include <cmath>using namespace std;int main() {// 读取灰度图ifstream input("input.bmp", ios::binary);if (!input) {cout << "无法打开文件" << endl;return 1;}char header[54];input.read(header, 54);int width = *(int*)(header + 18);int height = *(int*)(header + 22);int row_size = (width * 24 + 31) / 32 * 4;char* data = new char[row_size * height];input.read(data, row_size * height);input.close();// 计算法线图char* output = new char[row_size * height];for (int y = 1; y < height - 1; y++) {for (int x = 1; x < width - 1; x++) {// 计算梯度double dx = (data[(y + 1) * row_size + (x + 1) * 3] - data[(y - 1) * row_size + (x - 1) * 3]) / 255.0;double dy = (data[(y - 1) * row_size + (x + 1) * 3] - data[(y + 1) * row_size + (x - 1) * 3]) / 255.0;double dz = 1.0;// 归一化法线向量double length = sqrt(dx * dx + dy * dy + dz * dz);dx /= length;dy /= length;dz /= length;// 计算光照值double light = dx * 0.5 + dy * -0.5 + dz * 0.5 + 0.5;int value = int(light * 255);output[y * row_size + x * 3] = value;output[y * row_size + x * 3 + 1] = value;output[y * row_size + x * 3 + 2] = value;}}// 输出法线图ofstream of("output.bmp", ios::binary);of.write(header, 54);of.write(output, row_size * height);of.close();delete[] data;delete[] output;return 0;
}
#include <iostream>
#include <vector>// 计算Sobel算子
void sobel(const std::vector<float>& grayImage, int width, int height, std::vector<float>& dx, std::vector<float>& dy)
{dx.resize(width * height);dy.resize(width * height);for (int y = 1; y < height - 1; y++) {for (int x = 1; x < width - 1; x++) {float gx = -grayImage[(y - 1) * width + x - 1] + grayImage[(y - 1) * width + x + 1]- 2.0f * grayImage[y * width + x - 1] + 2.0f * grayImage[y * width + x + 1]- grayImage[(y + 1) * width + x - 1] + grayImage[(y + 1) * width + x + 1];float gy = grayImage[(y - 1) * width + x - 1] + 2.0f * grayImage[(y - 1) * width + x] + grayImage[(y - 1) * width + x + 1]- grayImage[(y + 1) * width + x - 1] - 2.0f * grayImage[(y + 1) * width + x] - grayImage[(y + 1) * width + x + 1];dx[y * width + x] = gx;dy[y * width + x] = gy;}}
}// 计算法向量
void normalMap(const std::vector<float>& grayImage, int width, int height, std::vector<float>& normal)
{std::vector<float> dx, dy;sobel(grayImage, width, height, dx, dy);normal.resize(width * height * 3);for (int y = 0; y < height; y++) {for (int x = 0; x < width; x++) {int i = y * width + x;float gx = dx[i];float gy = dy[i];float normalX = -gx;float normalY = -gy;float normalZ = 1.0f;float length = sqrt(normalX * normalX + normalY * normalY + normalZ * normalZ);normalX /= length;normalY /= length;normalZ /= length;normal[i * 3 + 0] = normalX;normal[i * 3 + 1] = normalY;normal[i * 3 + 2] = normalZ;}}
}int main()
{// 读取灰度图像std::vector<float> grayImage;int width, height;// TODO: 从文件中读取灰度图像到grayImage中,同时将宽度和高度存储在width和height中
相关文章:

【开发日志】2023.04 ZENO----Composite----CompNormalMap
NormalMap-Online (cpetry.github.io)https://cpetry.github.io/NormalMap-Online/ CompNormalMap 将灰度图像转换为法线贴图 将灰度图像转换为法线贴图是一种常见的技术,用于在实时图形渲染中增加表面细节。下面是一个简单的方法来将灰度图像转换为法线贴图&…...

春秋云境:CVE-2022-28525 (文件上传漏洞)
目录 一、题目 1.登录 2.burp抓包改包 3.蚁剑获取flag 一、题目 ED01CMSv20180505存在任意文件上传漏洞 英语不够 翻译来凑: 点击其他页面会Not Found 找不到: 先登录看看吧: 试试万能密码:admin:123 发现错误…...

【软件测试二】开发模型和测试模型,BUG概念篇
目录 1.软件的生命周期 2.瀑布模型 3.螺旋模型 4.增量,迭代 5.敏捷---scrum 1. 敏捷宣言 2.角色 6. 软件测试v模型 7.软件测试w模型 8.软件测试的生命周期 9.如何描述一个BUG 10.如何定义BUG的级别 11.BUG的生命周期 12.产生争执怎么办 1.软件的生命周期…...

短视频app开发:如何实现视频直播功能
短视频源码的实现 在短视频app开发中,实现视频直播功能需要借助短视频源码。短视频源码可以提供一个完整的视频直播功能模块,包括视频采集、编码、推流等。因此,我们可以选择一些开源的短视频源码,例如LFLiveKit、ijkplayer等&am…...

[架构之路-174]-《软考-系统分析师》-5-数据库系统-7-数据仓库技术与数据挖掘技术
5 . 7 数据仓库技术 数据仓库是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策。近年来,人们对数据仓库技术的关注程度越来越尚,其原因是过去的几十年中,建设了无数的应用系统,积累了…...
销售高品质 FKM EPDM NBR 硅胶 O 形密封圈
O形圈常用于各种行业,包括汽车、航空航天和制造业。它们是由不同材料制成的圆环,用于将两个或多个组件密封在一起。用于制造O形圈的材料是决定其有效性和耐用性的重要因素。在本文中,我们将讨论用于制作O形圈的不同类型的材料。 1.丁腈橡胶(…...
Linux环境变量:不可或缺的系统组成部分
目录标题 引言(Introduction)Linux环境变量的概念(Concept of Linux Environment Variables)环境变量的作用与重要性(Roles and Importance of Environment Variables) Linux环境变量基础(Linux…...
FFmpeg命令行解析
目录标题 一、引言(Introduction)1.1 FFmpeg简介(Overview of FFmpeg)1.2 FFmpeg命令行的应用场景(Application Scenarios of FFmpeg Command Line) 二、FFmpeg命令行基础(FFmpeg Command Line …...

机器学习——为什么逻辑斯特回归(logistic regression)是线性模型
问:逻辑斯蒂回归是一种典型的线性回归模型。 答:正确。逻辑斯蒂回归是一种典型的线性回归模型。它通过将线性回归模型的输出结果映射到[0,1]区间内,表示某个事物发生的概率,从而适用于二分类问题。具体地说,它使用sig…...

从输入URL到页面展示到底发生了什么
刚开始写这篇文章还是挺纠结的,因为网上搜索“从输入url到页面展示到底发生了什么”,你可以搜到一大堆的资料。而且面试这道题基本是必考题,二月份面试的时候,虽然知道这个过程发生了什么,不过当面试官一步步追问下去的…...
Qt connect传参方式及lambda函数传参方式详解
Qt connect传参方式及lambda函数传参方式详解 Qt是一种流行的跨平台C应用程序框架,它提供了许多有用的工具和函数来帮助开发人员构建高效的图形用户界面和其他应用程序。其中,Qt Connect函数是用于连接信号和槽的重要函数之一,它可以在Qt应用…...

如何在硬盘上恢复已经删除的照片?
可以从硬盘恢复删除的照片吗? 旅行后,许多人倾向于将照片保存到另一个储存设备作为副本或备份。例如,将它们存储在外部硬盘上或将图片传输到电脑。但是在整理照片的时候,很可能不小心把照片删掉了,尤其是使用外接硬…...

Unity日记22(携程概念)
目录 学习视频 携程 1异步 2调用方法 3优点 4停止方法 5返回值 实例:每过一秒打印当前运行时间 实例:停止数字打印携程 错误方法:(携程只能开一个) 参考方法 学习视频 https://www.bilibili.com/video/BV1eu…...

01-Linux-磁盘分区与目录配置
1. 主引导纪录和磁盘分区表 1.1 MBR分区表 启动引导程序记录区与分区表都放在磁盘的第一个扇区(512B) 由于分区表仅占 64B,因此最多能有四组记录区,每组记录区记录了该区段的起始与结束的柱面号码。 缺点如下: 操作…...

连接器信号完整性仿真教程 二
在连接器信号完整性仿真教程一中Step by Step演示了如何进行连接器信号完整性仿真,看完这片博文后应该可以做类似产品的仿真。如果说,看了这篇博文就学会了连接器信号完整性仿真,那就有点过了。有人也许会说信号完整性仿真难学,不…...

基于深度学习的图片上色(Opencv,Pytorch,CNN)
文章目录 1. 前言2.图像格式(RGB,HSV,Lab)2.1 RGB2.2 hsv2.3 Lab 3. 生成对抗网络(GAN)3.1 生成网络(Unet)3.2 判别网络(resnet18) 4. 数据集5. 模型训练与预…...

Python爬虫
目录 爬虫总览 准备工作 一、爬虫基础 1、爬虫前导 1.1、爬虫介绍 1.2、HTTP与HTTPS 1.3、URL 1.4、开发工具 1.5、爬虫流程 2、requests模块 2.1、简介 2.2、安装 2.3、发送请求 二、爬虫 爬虫总览 准备工作 一、爬虫基础 1、爬虫前导 1.1、爬虫介绍 概念&…...

python基础案例题:进制转换、字符串加密的实现、猜拳游戏、多种方法计算π
目录 前言1.进制转换2.字符串加密的实现3.猜拳游戏4.多种方法计算π尾语 💝 前言 嗨喽~大家好呀,这里是魔王呐 ❤ ~! 1.进制转换 功能: 获取十进制整数的二进制串,相当于内置函数bin。 算法分析: 对2辗转相除&…...
Spring Boot入门与进阶
本文将为您详细讲解Spring Boot的入门与进阶知识,包括Spring Boot的简介、环境搭建、基本功能以及高级特性,并配以丰富的代码示例,帮助大家快速掌握Spring Boot。 一、Spring Boot简介 Spring Boot是基于Spring框架的一种轻量级、快速开发的…...

servlet(1)—javaEE
文章目录 1.认识servlet2.使用servlet2.1创建项目2.2引入依赖2.3创建目录2.4编写代码2.5打包2.6部署2.7运行2.8验证 3.开发步骤4.部署方式4.1打包4.2安装插件 5.访问出错的情况5.1 4045.2 4055.3其他 6.servlet的三大生命周期方法7.servlet api7.1HttpServlet7.2HttpServletReq…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...

Axure 下拉框联动
实现选省、选完省之后选对应省份下的市区...