光线追踪5- Surface normals and multiple objects

        首先,让我们获取一个表面法线,以便进行着色。这是一个垂直于交点处表面的向量。在我们的代码中,我们需要做一个重要的设计决定:法线向量是否为任意长度,还是将其归一化为单位长度。
        诱人的是,如果不需要法线向量具有单位长度,可以跳过涉及归一化向量的昂贵平方根操作。然而,在实践中,有三个重要观察结果。首先,如果以后需要单位长度的法线向量,那么最好一次性完成它,而不是为了每个需要单位长度的位置而一次又一次地进行“以防万一”。其次,在几个地方我们确实需要单位长度的法线向量。第三,如果你需要法线向量具有单位长度,那么通常可以通过对特定几何类的构造函数或hit()函数进行理解来有效地生成该向量。例如,球体的法线可以通过除以球体半径而完全避免使用平方根来制作单位长度。

鉴于所有这些情况,我们将采用所有法线向量都具有单位长度的策略。

对于一个球体而言,外向法线是指击中点减去中心点的方向:

在球表面上,这意味着从球中心指向您的矢量是笔直向上的。现在让我们将其加入代码中并进行着色。我们还没有任何灯光之类的东西,所以让我们用颜色映射来可视化法线。一种常见的技巧是将法线的每个分量映射到0到1的区间,并将(x,y,z)映射为(red,green,blue)。对于法线,我们需要击中点,而不仅仅是确定是否击中(这是我们目前正在计算的)。场景中只有一个球体,并且它直接位于相机前方,所以我们暂时不用担心t的负值。我们只假设最近的击中点(最小的t)就是我们想要的。通过对代码进行这些更改,我们可以计算并可视化法线n:

double hit_sphere(const point3& center, double radius, const ray& r) {
vec3 oc = r.origin() - center;    
auto a = dot(r.direction(), r.direction());    
auto b = 2.0 * dot(oc, r.direction());    
auto c = dot(oc, oc) - radius*radius;    
auto discriminant = b*b - 4*a*c;if(discriminant < 0){return -1.0; 
} 
else { 
return (-b - sqrt(discriminant) ) / (2.0*a); 
}
}
color ray_color(const ray& r) {
Auto t = hit_sphere(point3(0,0,-1), 0.5, r); 
if(t>0.0){ 
vec3 N = unit_vector(r.at(t) - vec3(0,0,-1)); 
return 0.5*color(N.x()+1, N.y()+1, N.z()+1); 
}vec3 unit_direction = unit_vector(r.direction());
auto a = 0.5*(unit_direction.y() + 1.0);
return (1.0-a)*color(1.0, 1.0, 1.0) + a*color(0.5, 0.7, 1.0);
}

这就是生成的图片:

 


6.2  简化光线与球体相交代码

让我们重新来看一下光线和球体的函数:

double hit_sphere(const point3& center, double radius, const ray& r) {    vec3 oc = r.origin() - center;    auto a = dot(r.direction(), r.direction());    auto b = 2.0 * dot(oc, r.direction());    auto c = dot(oc, oc) - radius*radius;    auto discriminant = b*b - 4*a*c;    if (discriminant < 0) {return -1.0;    } else {return (-b - sqrt(discriminant) ) / (2.0*a);}
}

光线和球体相交的代码(之前)

首先,回想一下,一个向量与自身的点积等于该向量的长度的平方。

其次,注意到方程中的b有一个因子为2。考虑一下如果b=2h时二次方程会发生什么变化。

我们现在可以将球体相交的代码简化为以下形式:

double hit_sphere(const point3& center, double radius, const ray& r) {    
vec3 oc = r.origin() - center;
auto a = r.direction().length_squared();
auto half_b = dot(oc, r.direction());
auto c = oc.length_squared() - radius*radius;
auto discriminant = half_b*half_b - a*c;
if (discriminant < 0) {return -1.0;} 
else {return (-half_b - sqrt(discriminant) ) / a;
}
}

6.3  An Abstraction for Hittable Objects

现在,多个球体怎么办?虽然使用球体数组的方式很诱人,但一个非常简洁的解决方案是为光线可能碰到的任何物体创建一个"抽象类",并将球体和球体列表都视为可击中的物体。关于该抽象类应该被称为什么有点困扰——如果不考虑"面向对象"编程,称之为"对象"会很好。通常会使用"表面(Surface)"一词,但它的弱点可能在于我们可能需要处理体积(如雾、云等)。"hittable"强调了将它们统一起来的成员函数。我对这些名称都不太满意,但我们将采用"hittable"。

这个"hittable"抽象类将具有一个接受光线作为参数的hit函数。大多数光线追踪器发现给hit添加一个有效的命中区间tmin至tmax很方便,因此只有当tmin<t<tmax时,命中才会"计数"。对于初始光线,这个区间是正数t,但正如我们将看到的,将tmin与tmax定义为区间可以简化我们的代码。一个设计问题是是否在命中物体时计算法线。随着搜索的进行,我们可能会命中更近的物体,而我们只需要最近物体的法线。我将选择简单的解决方案,计算一些我将存储在某个结构中的相关信息。以下是这个抽象类的实现:

#ifndef HITTABLE_H
#define HITTABLE_H#include "ray.h"class hit_record {  
public:
point3  p;    vec3 normal;    double t;
};class hittable {  public:    virtual ~hittable() = default;    virtual bool hit(const ray& r, double ray_tmin, double ray_tmax, hit_record& 	rec) const = 0;
};#endif

这是球体的实现代码:

#ifndef SPHERE_H
#define SPHERE_H
#include "hittable.h"
#include "vec3.h"class sphere : public hittable {  
public:    sphere(point3 _center, double _radius) : center(_center), radius(_radius) {}    
bool hit(const ray& r, double ray_tmin, double ray_tmax, hit_record& rec) const override {vec3 oc = r.origin() - center;auto a = r.direction().length_squared();auto half_b = dot(oc, r.direction());auto c = oc.length_squared() - radius*radius;auto discriminant = half_b*half_b - a*c;if (discriminant < 0) return false;auto sqrtd = sqrt(discriminant);// Find the nearest root that lies in the acceptable range.auto root = (-half_b - sqrtd) / a;if (root <= ray_tmin || ray_tmax <= root) {root = (-half_b + sqrtd) / a;if (root <= ray_tmin || ray_tmax <= root)return false;        }        rec.t = root;rec.p = r.at(rec.t);rec.normal = (rec.p - center) / radius;return true;}  private:    point3 center;    double radius;
};#endif

Listing 16: [sphere.h] The sphere class
 

6.4 正面面片与背面面片的区别
    关于法线的第二个设计决是它们是否应该始终指向外部。目前,找到的法线始终指向从中心到交点的方向(法线指向外部)。如果射线从外部与球体相交,法线则指向与射线相反的方向。如果射线从内部与球体相交,法线(始终指向外部)则指向与射线相同的方向。或者,我们可以让法线始终指向与射线相反的方向。如果射线在球体外部,法线将指向外部;但如果射线在球体内部,法线将指向内部

    图7:球体表面法线的可能方向  

我们需要从这些可能性中选择一个,因为最终我们希望确定射线是从表面的哪一侧射出的。这对于在每一侧上都以不同方式渲染的对象非常重要,例如双面纸上的文本或者内外部分明确的玻璃球。

如果我们决定法线始终指向外部,那么在给射线上色时,我们需要确定射线所在的一侧。我们可以通过将射线与法线进行比较来找到答案。如果射线和法线方向相同,射线在物体内部;如果射线和法线方向相反,射线在物体外部。这可以通过计算这两个向量的点积来确定,如果它们的点积为正数,那么射线在球体内部。

if (dot(ray_direction, outward_normal) > 0.0) {    // ray is inside the sphere   
...}else {    // ray is outside the sphere    
...}

   如果我们决定法线始终指向逆向的射线,我们将无法使用点积来确定射线在表面的哪一侧。相反,我们需要存储这个信息:

bool front_face;
if (dot(ray_direction, outward_normal) > 0.0) {
// ray is inside the sphere
normal = -outward_normal;
front_face = false;
} 
else {// ray is outside the spherenormal = outward_normal;front_face = true;
}

     我们可以设置让法线始终从表面“向外”指向,或者始终指向入射射线的反方向。这个决定取决于您是想在几何相交时确定表面的哪一侧,还是在着色时确定。在本书中,我们有比几何类型更多的材质类型,所以我们将选择较少的工作量,并将确定放在几何时间。这只是一种偏好问题,在文献中会看到两种实现方式。

我们在hit_record类中添加了front_face布尔变量。我们还将添加一个函数来解决这个计算:set_face_normal()。为了方便起见,我们假设传递给新的set_face_normal()函数的向量长度为单位长度。我们可以显式地对参数进行归一化,但如果几何代码这样做,效率更高,因为当您对特定几何有更多了解时,通常更容易处理。

class hit_record { 
public:    
point3 p;
vec3 normal;
double t;bool front_face;void set_face_normal(const ray& r, const vec3& outward_normal) {// Sets the hit record normal vector.// NOTE: the parameter `outward_normal` is assumed to have unit length.front_face = dot(r.direction(), outward_normal) < 0;normal = front_face ? outward_normal : -outward_normal;
}
};

     Listing 19: [hittable.h] Adding front-face tracking to hit_record

然后我们将表面边界确定添加到该类中:

class sphere : public hittable {public:
...bool hit(const ray& r, double ray_tmin, double ray_tmax, hit_record& rec) const   {...rec.t = root;rec.p = r.at(rec.t);vec3 outward_normal = (rec.p - center) / radius;rec.set_face_normal(r, outward_normal);return true;    }...
};

 Listing 20: [sphere.h] The sphere class with normal determination

 

6.5 hittable_list

我们有一个名为"hittable"的通用对象,光线可以与其相交。现在我们添加一个类,用于存储一系列"hittable"对象的列表:

#ifndef HITTABLE_LIST_H
#define HITTABLE_LIST_H#include "hittable.h"
#include <memory>
#include <vector>using std::shared_ptr;
using std::make_shared;class hittable_list : public hittable {public:
std::vector<shared_ptr<hittable>> objects;hittable_list() { }
hittable_list(shared_ptr<hittable> object) { add(object); }void clear() { objects.clear(); }void add(shared_ptr<hittable> object) {objects.push_back(object);
}bool hit(const ray& r, double ray_tmin, double ray_tmax, hit_record& rec) const override {hit_record temp_rec;bool hit_anything = false;auto closest_so_far = ray_tmax;for (const auto& object : objects) {if (object->hit(r, ray_tmin, closest_so_far, temp_rec)){hit_anything = true;closest_so_far = temp_rec.t;rec = temp_rec;}}return hit_anything;
}
};
#endif

Listing 21: [hittable_list.h] The hittable_list class

 

6.6 一些C++的新特性   

hittable_list 类的代码使用了两个C++特性,如果你平时不是C++程序员可能会遇到困扰:vector和shared_ptr。

shared_ptr<type>是指向已分配类型的指针,具有引用计数语义。每次将其值赋给另一个共享指针(通常是简单赋值),引用计数就会递增。当共享指针超出范围(例如在块或函数的结尾处),引用计数就会递减。一旦计数降至零,对象就会被安全地删除。

通常,共享指针首先使用新分配的对象进行初始化,类似于下面的示例:

shared_ptr<double> double_ptr = make_shared<double>(0.37);
shared_ptr<vec3> vec3_ptr = make_shared<vec3>(1.414214, 2.718281, 1.618034);
shared_ptr<sphere> sphere_ptr = make_shared<sphere>(point3(0,0,0), 1.0);

make_shared<thing>(thing_constructor_params ...) 分配一个新的 thing 类型的实例,使用构造函数参数。它返回一个 shared_ptr<thing>。

由于类型可以通过 make_shared<type>(...) 的返回类型自动推断出来,上述代码可以更简洁地使用 C++ 的 auto 类型推导器表示如下:

auto double_ptr = make_shared<double>(0.37);
auto vec3_ptr   = make_shared<vec3>(1.414214, 2.718281, 1.618034);
auto sphere_ptr = make_shared<sphere>(point3(0,0,0), 1.0);

    在我们的代码中,我们将使用 shared_ptr,因为它允许多个几何体共享一个公共实例(例如,一组使用相同颜色材质的球体),并且使内存管理自动化并更易于理解。

std::shared_ptr 包含在 <memory> 头文件中。

第二个您可能不熟悉的 C++ 特性是 std::vector。它是一个泛型的类似数组的集合,可以存储任意类型的元素。在上面的代码中,我们使用了一个 hittable 指针的集合。std::vector 会在添加更多值时自动扩展:objects.push_back(object) 将一个值添加到 std::vector 成员变量 objects 的末尾。

std::vector 包含在 <vector> 头文件中。

最后,在第21行的 using 语句告诉编译器我们将从 std 库中获取 shared_ptr 和 make_shared,因此我们在引用它们时不需要每次都加上 std:: 前缀。
 


6.7 常见的常量和实用函数

我们需要一些数学常数,在它们自己的头文件中方便地进行定义。现在我们只需要无穷大(infinity),但稍后我们还会在其中添加我们自己对圆周率(pi)的定义,因为我们以后会用到它。关于pi,没有标准的可移植定义,所以我们只需为其定义我们自己的常量。我们将把常见有用的常数和未来的实用函数放在rtweekend.h中,这是我们的主要通用头文件。

#ifndef RTWEEKEND_H
#define RTWEEKEND_H#include <cmath>
#include <limits>
#include <memory>// Usings 
Using std::shared_ptr;
using std::make_shared;
using std::sqrt;// Constants
const double infinity = std::numeric_limits<double>::infinity();
const double pi = 3.1415926535897932385;// Utility Functions
inline double degrees_to_radians(double degrees) {
return degrees * pi / 180.0;
}// Common Headers
#include "ray.h"
#include "vec3.h"#endif

新的main 函数

#include "rtweekend.h"
#include "color.h"
#include "hittable.h"
#include "hittable_list.h"
#include "sphere.h"#include <iostream>double hit_sphere(const point3& center, double radius, const ray& r) {... 
}color ray_color(const ray& r, const hittable& world) {
hit_record rec;if (world.hit(r, 0, infinity, rec)) {return 0.5 * (rec.normal + color(1,1,1));
}vec3 unit_direction = unit_vector(r.direction());
auto a = 0.5*(unit_direction.y() + 1.0);return (1.0-a)*color(1.0, 1.0, 1.0) + a*color(0.5, 0.7, 1.0);
}int main() {
// Image
auto aspect_ratio = 16.0 / 9.0;
int image_width = 400;// Calculate the image height, and ensure that it's at least 1.
int image_height = static_cast<int>(image_width / aspect_ratio);
image_height = (image_height < 1) ? 1 : image_height;// World
hittable_list world;
world.add(make_shared<sphere>(point3(0,0,-1), 0.5));
world.add(make_shared<sphere>(point3(0,-100.5,-1), 100));// Camera
auto focal_length = 1.0;
auto viewport_height = 2.0;
auto viewport_width = viewport_height * (static_cast<double>(image_width)/image_height);
auto camera_center = point3(0, 0, 0);// Calculate the vectors across the horizontal and down the vertical viewport edges.    auto viewport_u = vec3(viewport_width, 0, 0);
auto viewport_v = vec3(0, -viewport_height, 0);// Calculate the horizontal and vertical delta vectors from pixel to pixel.    
auto pixel_delta_u = viewport_u / image_width;
auto pixel_delta_v = viewport_v / image_height;// Calculate the location of the upper left pixel.
auto viewport_upper_left = camera_center - vec3(0, 0, focal_length) - viewport_u/2 - viewport_v/2;
auto pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);// Renderstd::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";
for (int j = 0; j < image_height; ++j) {std::clog << "\rScanlines remaining: " << (image_height - j) << ' ' << std::flush;for (int i = 0; i < image_width; ++i) {auto pixel_center = pixel00_loc + (i * pixel_delta_u) + (j * pixel_delta_v);auto ray_direction = pixel_center - camera_center;ray r(camera_center, ray_direction);
color pixel_color = ray_color(r, world);write_color(std::cout, pixel_color);}
}
std::clog << "\rDone.                 \n";
}

这会产生一张图片,实际上只是一个显示球体位置及其表面法线的可视化图像。这通常是查看几何模型的任何缺陷或特定特征的绝佳方式。

Image 5: Resulting render of normals-colored sphere with ground


 

6.8 An Interval Class

在我们继续之前,我们将实现一个区间类来管理具有最小值和最大值的实数区间。随着我们的进展,我们将经常使用这个类。 

#ifndef INTERVAL_H
#define INTERVAL_Hclass interval {public:
double min, max;interval() : min(+infinity), max(-infinity) {}  // Default interval is empty
interval(double _min, double _max) : min(_min), max(_max) {}bool contains(double x) const {return min <= x && x <= max;
}bool surrounds(double x) const {return min < x && x < max;
}static const interval empty, universe;
};const static interval empty   (+infinity, -infinity);
const static interval universe(-infinity, +infinity);#endif

                 Listing 26: [interval.h] Introducing the new interval class
 

class hittable_list : public hittable {public:
...
bool hit(const ray& r, interval ray_t, hit_record& rec) const override {hit_record temp_rec;bool hit_anything = false;auto closest_so_far = ray_t.max;for (const auto& object : objects) {if (object->hit(r, interval(ray_t.min, closest_so_far), temp_rec)) {hit_anything = true;closest_so_far = temp_rec.t;rec = temp_rec;}}return hit_anything;
}...
};

 Listing 29: [hittable_list.h] hittable_list::hit() using interval

class sphere : public hittable {public:
...
bool hit(const ray& r, interval ray_t, hit_record& rec) const override {...// Find the nearest root that lies in the acceptable range.auto root = (-half_b - sqrtd) / a;if (!ray_t.surrounds(root)) {root = (-half_b + sqrtd) / a;if (!ray_t.surrounds(root))return false;}        ...
}...
};

 Listing 30: [sphere.h] sphere using interval

...
color ray_color(const ray& r, const hittable& world) {hit_record rec;if (world.hit(r, interval(0, infinity), rec)) {return 0.5 * (rec.normal + color(1,1,1));
}vec3 unit_direction = unit_vector(r.direction());auto a = 0.5*(unit_direction.y() + 1.0);return (1.0-a)*color(1.0, 1.0, 1.0) + a*color(0.5, 0.7, 1.0);
}
...

                Listing 31: [main.cc] The new main using interval





 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频远程监控平台EasyCVR集成后播放只有一帧画面的原因排查与解决

智慧安防视频监控平台EasyCVR能在复杂的网络环境中&#xff08;专网、局域网、广域网、VPN、公网等&#xff09;将前端海量的设备进行统一集中接入与视频汇聚管理&#xff0c;平台可支持的接入协议包括&#xff1a;国标GB28181、RTSP/Onvif、RTMP&#xff0c;以及厂家的私有协议…

【STM32下UART协议的一些认识与使用方法】

STM32下UART协议的一些认识与使用方法 串口定义通用串行异步收发器 协议中相关的概念空闲位起始位数据位奇偶校验位三种校验方式BT接收数据的流程 停止位波特率总结 UART的三种工作方式UART控制器发送数据流程接收数据流程 UART初始化UART相关结构体和库函数 串口定义 通用串行…

揭秘共享旅游卡项目变现的6种方式,至少有4种适合创业者!

最近每天都有不少的朋友添加我&#xff0c;多数人会问一个问题&#xff0c;共享旅游卡这个项目好不好做&#xff1f;还有部分朋友会问&#xff0c;共享旅游卡有没有市场&#xff0c;是不是有我们所宣传的这样乐观&#xff1f; 从过年前开始&#xff0c;我一直在关注咱们共享旅…

基于Spring Boot + Vue的信息化在线教学平台

末尾获取源码作者介绍&#xff1a;大家好&#xff0c;我是墨韵&#xff0c;本人4年开发经验&#xff0c;专注定制项目开发 更多项目&#xff1a;CSDN主页YAML墨韵 学如逆水行舟&#xff0c;不进则退。学习如赶路&#xff0c;不能慢一步。 目录 一、项目简介 二、开发技术与环…

事务 失效的八种情况

在某些业务场景下&#xff0c;如果一个请求中&#xff0c;需要同时写入多张表的数据。为了保证操作的原子性&#xff08;要么同时成功&#xff0c;要么同时失败&#xff09;&#xff0c;避免数据不一致的情况&#xff0c;我们一般都会用到 spring 事务。 确实&#xff0c;sprin…

Android视角看鸿蒙第一课(工程目录)

Android视角看鸿蒙第一课&#xff08;工程目录&#xff09; 导读 鸿蒙马上就来了&#xff0c;这个工作很有可能落到Android开发的头上&#xff0c;既是机遇也是挑战&#xff0c;希望能跟上时代的浪潮&#xff0c;迫不得已开始学习鸿蒙开发&#xff0c;顺带分享记录下 我的学…

【python】对角线遍历

python系列文章目录 【python】基于cv2提取图片上的文本内容 【python】简单作图 【python】数组字符串等实用 【python】sort与sorted排序使用 【python】对角线遍历 python系列文章目录说明1.分析2.注意事项2.1 遍历2.2 区间2.3 顺序 3.代码实现 说明 给你一个大小为 m x n…

Java方法重载

重载 概念 重载就是在一个类中&#xff0c;有相同的函数名&#xff0c;但形参不同的函数 规则 方法名称必须相同参数列表必须不同&#xff08;个数不同、或类型不同、参数排列顺序不同等&#xff09;方法的返回值类型可以相同也可以不同仅仅返回类型不同不足以成为方法的重载 实…

MySQl基础入门③

上一遍内容 接下来我们都使用navicat软件来操作数据了。 1.新建数据库 先创建我门自己的一个数据库 鼠标右键点击bendi那个绿色海豚的图标&#xff0c;然后选择新建数据库。 数据库名按自己喜好的填&#xff0c;不要写中文&#xff0c; 在 MySQL 8.0 中&#xff0c;最优的字…

【MATLAB】语音信号识别与处理:卷积滑动平均滤波算法去噪及谱相减算法呈现频谱

1 基本定义 卷积滑动平均滤波算法是一种基于卷积操作的滤波方法&#xff0c;它通过对信号进行卷积运算来计算移动平均值&#xff0c;以消除噪声。该算法的主要思想是将滤波窗口的加权系数定义为一个卷积核&#xff0c;对信号进行卷积运算来得到平滑后的信号。这样可以有效地去…

Java基于微信小程序的高校讲座预约系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

项目打包至私有maven仓库

文章目录 step1: 下载源项目到本地&#xff0c;确保编译无问题step2: maven配置文件settings.xmlstep 3: 项目的pom.xml配置要发布的仓库地址step 4: 执行deploy命令即可将项目打包至远程仓库 step1: 下载源项目到本地&#xff0c;确保编译无问题 step2: maven配置文件setting…

Vector Search和专用Search Nodes:现已正式发布

我们非常高兴地推出了 Atlas Vector Search 和 Search Nodes 的正式发布版本 (GA)&#xff0c;为 Atlas 平台增添了更多价值。 自从在公开预览版中发布 Atlas Vector Search 和带有 Search Nodes 的专用基础架构以来&#xff0c;我们注意到&#xff0c;对于使用向量优化搜索节…

信息安全、网络安全以及数据安全三者之间的区别

随着信息技术的飞速发展&#xff0c;网络安全、信息安全、数据安全等词汇在平时出现的频率越来越高&#xff0c;尤其是数据安全&#xff0c;是大家都关心的一个重要话题。事实上&#xff0c;有很多人对网络安全、信息安全、数据安全的概念是区分不清的&#xff0c;下面由我帮大…

C#,回文分割问题(Palindrome Partitioning Problem)算法与源代码

1 回文串 “回文串”是一个正读和反读都一样的字符串&#xff0c;初始化标志flagtrue&#xff0c;比如“level”或者“noon”等等就是回文串。 2 回文分割问题 给定一个字符串&#xff0c;如果该字符串的每个子字符串都是回文的&#xff0c;那么该字符串的分区就是回文分区。…

DataLoader

import torchvision from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter# 准备的测试数据集 数据放在了CIFAR10文件夹下test_data torchvision.datasets.CIFAR10("./CIFAR10",trainFalse, transformtorchvision.transfor…

Qt入门(一)Qt概述

Qt是什么&#xff1f; Qt是一个跨平台应用开发框架。 Qt既包括了一系列的Qt库&#xff0c;还包括诸多配套的开发工具如QtCreater&#xff0c;GUI Designer。Qt本身是由C开发的&#xff0c;但是也提供了其他编程语言的接口。 Qt的定位以及同类 学一种技术&#xff0c;最重要的是…

PDF控件Spire.PDF for .NET【安全】演示:加密 PDF 文档

加密PDF是人们常用的保护PDF的方法。无论对于公司还是个人&#xff0c;使用PDF加密来设置一些限制都是必不可少的。为了使PDF文档可供未经授权的用户阅读但无法修改&#xff0c;加密的PDF文档需要两个密码&#xff1a;所有者密码和用户密码。本节将特别介绍一种通过 Spire.PDF …

从mysql 数据库表导入数据到elasticSearch的几种方式

从MySQL数据库导入数据到Elasticsearch有几种方式&#xff0c;主要包括以下几种&#xff1a; 1. 使用Logstash&#xff1a; Logstash是一个开源的数据收集引擎&#xff0c;可以用来从不同的数据源导入数据到Elasticsearch。它具有强大的数据处理能力和插件生态系统&…

ChatGPT在地学、GIS、气象、农业、生态、环境等领域中的应用

以ChatGPT、LLaMA、Gemini、DALLE、Midjourney、Stable Diffusion、星火大模型、文心一言、千问为代表AI大语言模型带来了新一波人工智能浪潮&#xff0c;可以面向科研选题、思维导图、数据清洗、统计分析、高级编程、代码调试、算法学习、论文检索、写作、翻译、润色、文献辅助…