当前位置: 首页 > news >正文

【开发日志】2023.04 ZENO----Composite----CompNormalMap

NormalMap-Online (cpetry.github.io)https://cpetry.github.io/NormalMap-Online/

CompNormalMap

将灰度图像转换为法线贴图

将灰度图像转换为法线贴图是一种常见的技术,用于在实时图形渲染中增加表面细节。下面是一个简单的方法来将灰度图像转换为法线贴图:

  1. 加载灰度图像,并将其转换为浮点数值范围[0, 1]。

  2. 对于每个像素,计算其相邻像素与其距离,并计算它们之间的斜率。这可以通过使用Sobel算子或其他边缘检测算法来完成。

  3. 将每个像素的斜率向量归一化为单位长度,并转换为范围[-1, 1]内的值。

  4. 将每个像素的斜率向量转换为法线向量。法线向量可以通过使用以下公式计算得出:$normal = (2R - 1, 2G - 1, B)$,其中R,G和B分别是像素的红、绿和蓝通道值。由于我们的灰度图像没有颜色信息,我们可以将R,G和B设置为0.5,以得到一个朝向Z轴正方向的法线。

  5. 存储每个像素的法线向量作为输出法线贴图。

需要注意的是,这个简单的方法只能生成表面细节的近似值,并且可能会在处理像素边缘和细节时出现不准确的结果。更复杂的技术可以使用高斯过滤和其他方法来平滑法线图并产生更精细的结果。

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 将灰度图像转换为法线贴图 将灰度图像转换为法线贴图是一种常见的技术&#xff0c;用于在实时图形渲染中增加表面细节。下面是一个简单的方法来将灰度图像转换为法线贴图&…...

春秋云境:CVE-2022-28525 (文件上传漏洞)

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

【软件测试二】开发模型和测试模型,BUG概念篇

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

短视频app开发:如何实现视频直播功能

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

[架构之路-174]-《软考-系统分析师》-5-数据库系统-7-数据仓库技术与数据挖掘技术

5 . 7 数据仓库技术 数据仓库是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合&#xff0c;用于支持管理决策。近年来&#xff0c;人们对数据仓库技术的关注程度越来越尚&#xff0c;其原因是过去的几十年中&#xff0c;建设了无数的应用系统&#xff0c;积累了…...

销售高品质 FKM EPDM NBR 硅胶 O 形密封圈

O形圈常用于各种行业&#xff0c;包括汽车、航空航天和制造业。它们是由不同材料制成的圆环&#xff0c;用于将两个或多个组件密封在一起。用于制造O形圈的材料是决定其有效性和耐用性的重要因素。在本文中&#xff0c;我们将讨论用于制作O形圈的不同类型的材料。 1.丁腈橡胶(…...

Linux环境变量:不可或缺的系统组成部分

目录标题 引言&#xff08;Introduction&#xff09;Linux环境变量的概念&#xff08;Concept of Linux Environment Variables&#xff09;环境变量的作用与重要性&#xff08;Roles and Importance of Environment Variables&#xff09; Linux环境变量基础&#xff08;Linux…...

FFmpeg命令行解析

目录标题 一、引言&#xff08;Introduction&#xff09;1.1 FFmpeg简介&#xff08;Overview of FFmpeg&#xff09;1.2 FFmpeg命令行的应用场景&#xff08;Application Scenarios of FFmpeg Command Line&#xff09; 二、FFmpeg命令行基础&#xff08;FFmpeg Command Line …...

机器学习——为什么逻辑斯特回归(logistic regression)是线性模型

问&#xff1a;逻辑斯蒂回归是一种典型的线性回归模型。 答&#xff1a;正确。逻辑斯蒂回归是一种典型的线性回归模型。它通过将线性回归模型的输出结果映射到[0,1]区间内&#xff0c;表示某个事物发生的概率&#xff0c;从而适用于二分类问题。具体地说&#xff0c;它使用sig…...

从输入URL到页面展示到底发生了什么

刚开始写这篇文章还是挺纠结的&#xff0c;因为网上搜索“从输入url到页面展示到底发生了什么”&#xff0c;你可以搜到一大堆的资料。而且面试这道题基本是必考题&#xff0c;二月份面试的时候&#xff0c;虽然知道这个过程发生了什么&#xff0c;不过当面试官一步步追问下去的…...

Qt connect传参方式及lambda函数传参方式详解

Qt connect传参方式及lambda函数传参方式详解 Qt是一种流行的跨平台C应用程序框架&#xff0c;它提供了许多有用的工具和函数来帮助开发人员构建高效的图形用户界面和其他应用程序。其中&#xff0c;Qt Connect函数是用于连接信号和槽的重要函数之一&#xff0c;它可以在Qt应用…...

如何在硬盘上恢复已经删除的照片?

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

Unity日记22(携程概念)

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

01-Linux-磁盘分区与目录配置

1. 主引导纪录和磁盘分区表 1.1 MBR分区表 启动引导程序记录区与分区表都放在磁盘的第一个扇区&#xff08;512B&#xff09; 由于分区表仅占 64B&#xff0c;因此最多能有四组记录区&#xff0c;每组记录区记录了该区段的起始与结束的柱面号码。 缺点如下&#xff1a; 操作…...

连接器信号完整性仿真教程 二

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

基于深度学习的图片上色(Opencv,Pytorch,CNN)

文章目录 1. 前言2.图像格式&#xff08;RGB&#xff0c;HSV&#xff0c;Lab&#xff09;2.1 RGB2.2 hsv2.3 Lab 3. 生成对抗网络&#xff08;GAN&#xff09;3.1 生成网络&#xff08;Unet&#xff09;3.2 判别网络&#xff08;resnet18&#xff09; 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.多种方法计算π尾语 &#x1f49d; 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 1.进制转换 功能&#xff1a; 获取十进制整数的二进制串&#xff0c;相当于内置函数bin。 算法分析&#xff1a; 对2辗转相除&…...

Spring Boot入门与进阶

本文将为您详细讲解Spring Boot的入门与进阶知识&#xff0c;包括Spring Boot的简介、环境搭建、基本功能以及高级特性&#xff0c;并配以丰富的代码示例&#xff0c;帮助大家快速掌握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…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

2023赣州旅游投资集团

单选题 1.“不登高山&#xff0c;不知天之高也&#xff1b;不临深溪&#xff0c;不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

HDFS分布式存储 zookeeper

hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架&#xff0c;允许使用简单的变成模型跨计算机对大型集群进行分布式处理&#xff08;1.海量的数据存储 2.海量数据的计算&#xff09;Hadoop核心组件 hdfs&#xff08;分布式文件存储系统&#xff09;&a…...

JS设计模式(4):观察者模式

JS设计模式(4):观察者模式 一、引入 在开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个对象的状态变化需要自动通知其他对象&#xff0c;比如&#xff1a; 电商平台中&#xff0c;商品库存变化时需要通知所有订阅该商品的用户&#xff1b;新闻网站中&#xff0…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

Web中间件--tomcat学习

Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机&#xff0c;它可以执行Java字节码。Java虚拟机是Java平台的一部分&#xff0c;Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...