文章目录

    • 1.实现目标
    • 2.实现过程
      • 2.1 数据准备
      • 2.2 创建项目
      • 2.3 dcmtk库集成
      • 2.4 流程&原理
      • 2.5 材质
      • 2.6 应用实现
    • 3.参考资料

1.实现目标

本文在UE5中读取本地的dicom文件,解析像素值、窗宽窗位等信息,生成2D纹理,在UE场景中实现简单的2D医学影像可视化

在这里插入图片描述

2.实现过程

包括数据准备,dicom三方库在UE工程中的集成,dicom文件中像素值,窗宽窗位Tag的解析,纹理的生成,显示处理的材质等,以实现最终在UE场景中显示2D医学影像

2.1 数据准备

(1)基于开源的dicom数据,链接在文章第三部分参考资料中

在这里插入图片描述

(2)在Radiant Viewer中选择一张dicom查看,如下图所示,也是此次本地使用的单张单帧dicom测试数据

在这里插入图片描述

2.2 创建项目

创建引擎自带的 C++项目,这里不再赘述

在这里插入图片描述

2.3 dcmtk库集成

由于gdcm库在集成过程中,有较多的冲突需要解决,为了集成方便起见,所以本文这里直接对dcmtk库进行集成。(直接使用github上编译好的库,也可以下载源码自己本地编译)

(1)以插件的形式集成三方库,这也是目前UE官方所推荐的方式。这里使用的是空白的插件模板。

在这里插入图片描述
在这里插入图片描述

(2)插件目录,其中后续使用的usf等shader相关文件,都放在Shaders文件夹中。
第三方库dcmtk的相关内容,都放在插件source中的ThirdParty文件夹下

在这里插入图片描述

(3)下载dcmtk库,直接从github上下载dcmtkrelease版本(https://github.com/DCMTK/dcmtk/releases)
其中包含了需要的头文件,以及静态库lib和动态库dll等。(也可以下载源码,自己本地编译)

在这里插入图片描述

(4)在插件的Build.cs中配置对三方库的引用,防止找不到头文件或者库等报错

在这里插入图片描述

插件的build.cs完整代码如下所示:

// Copyright Epic Games, Inc. All Rights Reserved.using System.IO;
using UnrealBuildTool;public class DicomVIS : ModuleRules
{public DicomVIS(ReadOnlyTargetRules Target) : base(Target){PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;PublicIncludePaths.AddRange(new string[] {// ... add public include paths required here ...});PrivateIncludePaths.AddRange(new string[] {// ... add other private include paths required here ...});PublicDependencyModuleNames.AddRange(new string[]{"Core","ImageWrapper"// ... add other public dependencies that you statically link with here ...});PrivateDependencyModuleNames.AddRange(new string[]{"CoreUObject","Engine","Slate","SlateCore",// for custom shaders"RenderCore"});DynamicallyLoadedModuleNames.AddRange(new string[]{// ... add any modules that your module loads dynamically here ...});PublicIncludePaths.AddRange(new string[] { Path.Combine(ModuleDirectory, "ThirdParty/dcmtk/include")	});string libPath = Path.Combine(ModuleDirectory, "ThirdParty/dcmtk/lib");string[] allLibs = Directory.Exists(libPath) ? Directory.GetFiles(libPath, "*.lib") : new string[0];PublicAdditionalLibraries.AddRange(allLibs);string dllPath = Path.Combine(ModuleDirectory, "ThirdParty/dcmtk/bin");string[] allDlls = Directory.Exists(dllPath) ? Directory.GetFiles(dllPath, "*.dll") : new string[0];foreach (string currentDll in allDlls){string dllName = Path.GetFileName(currentDll);PublicDelayLoadDLLs.Add(dllName);// copy dll to project directory binaries dirstring targetDllPath = Path.Combine(PluginDirectory, "../../Binaries/Win64/" + dllName);RuntimeDependencies.Add(targetDllPath, currentDll);}}
}

(5)集成过程中可能会遇到的问题

① 引入dcmtk库相关头文件时,verify、check等宏冲突

报错如下所示:

在这里插入图片描述

解决方法:

// DCMTK uses their own verify and check macros.
// Also, they include some effed up windows headers which for example include min and max macros for that
// extra bit of _screw you_
#pragma push_macro("verify")
#pragma push_macro("check")
#undef verify
#undef check#include "dcmtk/dcmdata/dcdatset.h"
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dcpixel.h"
#include "dcmtk/dcmimgle/dcmimage.h"#pragma pop_macro("verify")
#pragma pop_macro("check")

"UpdateResourceW": 不是 "UTexture2D" 的成员,原因是dcmtk中引用了windows的相关头文件WinBase.h中已经定义了相关的宏

在这里插入图片描述

报错如下所示:

在这里插入图片描述

解决方法:前置声明

在这里插入图片描述

2.4 流程&原理

使用dcmtk库解析dicom数据中的像素数据,以及窗宽窗位等数据,再按照dicom标准的显示处理流程处理即可。
(完整源码在本文2.6部分,此部分仅作为原理说明)

(1)使用dcmtk库解析dicom数据中的像素值,和窗宽窗位tag信息,这三个信息是显示的必要参数

直接根据相关tag解析即可,本文这里使用的无符号的16bit测试数据,其他类型暂无考虑(原理相同,参数修改下即可)

在这里插入图片描述

(2)窗宽窗位特殊处理

在上一篇文章中介绍了常规的dicom数据显示流程,即先根据slopeintercept进行modality变换,在根据windowCenterwindowWidth等进行VOI变换。但本文这里是直接将没有经过modality变换的原始像素值直接生成了2D纹理,后续的显示处理都在shader中处理,所以这里直接对窗宽窗位应用modality反变换,这样就在shader中节约了一次modality变换的消耗

在这里插入图片描述

(3)创建纹理并更新像素数据

在这里插入图片描述

(4)最终显示:使用材质处理完成后,在PlaneStaticMesh的载体上显示即可

2.5 材质

关于dicom显示过程中的一些特殊处理,如VOI变换等,可以使用Material Function等方式,为了后续使用的方便,本文这里使用usfshader方式实现

(1)在插件中添加对shader文件夹的映射,以便可以在材质编辑器中使用。

// find shaders
FString shaderDir = FPaths::Combine(FPaths::ProjectPluginsDir(), "DicomVIS/Shaders/");
AddShaderSourceDirectoryMapping("/DicomVIS", shaderDir);

在这里插入图片描述

(2)在UE编辑器中创建材质,并设置为Unlit无光照模式,以避免UE中光照对dicom医学影像造成的颜色偏差等

在这里插入图片描述

(3)该材质的主要节点如下所示。其中SliceTex参数为dicom像素值生成的2D纹理,WindowWidthWindowCenter为窗宽窗位信息,DataRange为数据的范围,如本文使用的G16格式纹理,则该值为65535;若材质为G8格式的纹理,则该值为255

在这里插入图片描述

(4)新建usf,用于dicom显示过程中的VOI变换

float GetVOITransformation(float inputValue, float windowCenter, float windowWidth, float range)
{return saturate((inputValue - windowCenter + 0.5 / range) / (windowWidth - 1.0 / range) + 0.5);
}

在这里插入图片描述

(5)新建自定义材质节点,并设置输入和输出的参数类型

在这里插入图片描述

(6)设置自定义材质节点的Code包含头文件路径,以便可以在材质中使用usf中的函数

在这里插入图片描述

2.6 应用实现

包括具体的实现步骤,可以在UE中Editor环境下加载Dicom数据,生成2D纹理和最终显示。
本文这里只演示了16位无符号的dicom数据处理流程,其他类型的类似,需要修改生成材质的格式等参数即可

(1)dicom解析,以及生成纹理的相关功能,本文这里基于ActorCompoent实现,具体C++代码如下所示:

SliceDataLoader.h:

// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Engine/StaticMeshActor.h"
// DCMTK uses their own verify and check macros.
// Also, they include some effed up windows headers which for example include min and max macros for that
// extra bit of _screw you_
#pragma push_macro("verify")
#pragma push_macro("check")
#undef verify
#undef check#include "dcmtk/dcmdata/dcdatset.h"
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dcpixel.h"
#include "dcmtk/dcmimgle/dcmimage.h"#pragma pop_macro("verify")
#pragma pop_macro("check")#include "SliceDataLoader.generated.h"#define UpdateResource UpdateResourceUCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class DICOMVIS_API USliceDataLoader : public UActorComponent
{GENERATED_BODY()public:	// Sets default values for this component's propertiesUSliceDataLoader();// The dicom data path, that is the relative path of ue game project directory.UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dicom")FString TargetFilePath = "Data/test.dcm";// Plane static mesh componentUPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dicom")AStaticMeshActor* pPlaneMesh = nullptr;// 2d slice materialUPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dicom")UMaterial* pMaterial = nullptr;// The dicom pixel data texture2DUPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dicom")UTexture2D* pTexture2D;// Window Center of dicomUPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dicom")float WindowCenter;// Window width of dicomUPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Dicom")float WindowWidth;// The min value of dicom dataUPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dicom")float Min = 0;// The range of dicom data, range = max - minUPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dicom")float Range = 1;// slope value of dicomUPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dicom")float Slope = 0;// intercept value of dicomUPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dicom")float Intercept = 0;// dicom image widthUPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dicom")int Width = 0;// dicom image heightUPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dicom")int Height = 0;// dicom image depthUPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dicom")int Depth = 0;// Load dicom data from load file path, parse pixel data to texture2DUFUNCTION(CallInEditor, BlueprintCallable, Category = "Dicom")void LoadDicom();protected:// Called when the game startsvirtual void BeginPlay() override;// use dicom pixel data to generate texture (uint16)void GenerateTexture(UTexture2D*& pTexture, uint32 width, uint32 height, uint16* pixelData);public:	// Called every framevirtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};

SliceDataLoader.cpp:

// Fill out your copyright notice in the Description page of Project Settings.#include "SliceDataLoader.h"
#include <Kismet/KismetSystemLibrary.h>
#include <ImageUtils.h>// Sets default values for this component's properties
USliceDataLoader::USliceDataLoader()
{// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features// off to improve performance if you don't need them.PrimaryComponentTick.bCanEverTick = true;// ...
}void USliceDataLoader::LoadDicom()
{FString dicomFilePath = UKismetSystemLibrary::GetProjectDirectory() + TargetFilePath;if (!FPaths::FileExists(dicomFilePath)){UE_LOG(LogTemp, Error, TEXT("dicom file is not exist, please check!"));return;}// ToDo: use other thread to processAsyncTask(ENamedThreads::GameThread, [=, this]() {DcmFileFormat fileFormat;if (fileFormat.loadFile(TCHAR_TO_ANSI(*dicomFilePath)).good()){UE_LOG(LogTemp, Log, TEXT("dicom file loaded successfully!"));DcmDataset* dataset = fileFormat.getDataset();dataset->chooseRepresentation(EXS_LittleEndianImplicit, nullptr);Float64 windowCenter;dataset->findAndGetFloat64(DCM_WindowCenter, windowCenter);this->WindowCenter = windowCenter;Float64 windowWidth;dataset->findAndGetFloat64(DCM_WindowWidth, windowWidth);this->WindowWidth = windowWidth;Float64 slope;dataset->findAndGetFloat64(DCM_RescaleSlope, slope);this->Slope = slope;Float64 intercept;dataset->findAndGetFloat64(DCM_RescaleIntercept, intercept);this->Intercept = intercept;Uint16 bitsAllocated;dataset->findAndGetUint16(DCM_BitsAllocated, bitsAllocated);this->Depth = bitsAllocated;Uint8 pixelRepresentation = 0;dataset->findAndGetUint8(DCM_PixelRepresentation, pixelRepresentation);bool isSigned = pixelRepresentation == 1;DicomImage* dcmImage = new DicomImage(TCHAR_TO_ANSI(*dicomFilePath));if (dcmImage->getStatus() != EIS_Normal){UE_LOG(LogTemp, Error, TEXT("dicom file image loaded failed!"));return;}const int width = dcmImage->getWidth();this->Width = width;const int height = dcmImage->getHeight();this->Height = height;DcmElement* pixelDataElement;dataset->findAndGetElement(DCM_PixelData, pixelDataElement);// Tips:current just support r16 formatif (bitsAllocated == 16){TArray<int> resArray;if (!isSigned){uint16* pixelData = nullptr;pixelDataElement->getUint16Array(pixelData);this->GenerateTexture(this->pTexture2D, this->Width, this->Height, pixelData);}}}});
}// Called when the game starts
void USliceDataLoader::BeginPlay()
{Super::BeginPlay();// ...}void USliceDataLoader::GenerateTexture(UTexture2D*& pTexture, uint32 width, uint32 height, uint16* pixelData)
{//FImageUtils::CreateTexture2DEPixelFormat pixelFormat = EPixelFormat::PF_G16;if (pTexture == nullptr){pTexture = UTexture2D::CreateTransient(width, height, pixelFormat);pTexture->AddToRoot();pTexture->MipGenSettings = TMGS_NoMipmaps;pTexture->CompressionSettings = TC_Grayscale;// srgb may not effect of 16 bit.pTexture->SRGB = true;pTexture->NeverStream = true;pTexture->Filter = TextureFilter::TF_Nearest;pTexture->AddressX = TextureAddress::TA_Clamp;pTexture->AddressY = TextureAddress::TA_Clamp;}FTexture2DMipMap& mipMap = pTexture->GetPlatformData()->Mips[0];uint16* byteArray = static_cast<uint16*>(mipMap.BulkData.Lock(LOCK_READ_WRITE));long size = width * height;FMemory::Memcpy(byteArray, pixelData, size * sizeof(uint16));mipMap.BulkData.Unlock();pTexture->UpdateResource();// 更新材质if (pMaterial){UMaterialInstanceDynamic* pMaterialInsDy = UMaterialInstanceDynamic::Create(pMaterial, this);pMaterialInsDy->SetTextureParameterValue(FName("SliceTex"), pTexture);// inverset tranform window center and width by slope and intercept;FFloat16 transWL = (this->WindowCenter - this->Intercept) / this->Slope * 1 / 65535.0;FFloat16 transWW = (this->WindowWidth) / this->Slope * 1 / 65535.0;pMaterialInsDy->SetScalarParameterValue(FName("WindowCenter"), transWL);pMaterialInsDy->SetScalarParameterValue(FName("WindowWidth"), transWW);pMaterialInsDy->SetScalarParameterValue(FName("DataRange"), 65535.0);auto pStaticMeshComponent = pPlaneMesh->GetStaticMeshComponent();pStaticMeshComponent->SetMaterial(0, pMaterialInsDy);}
}// Called every frame
void USliceDataLoader::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}

(2)在场景中添加Plane,用于影像在UE中显示的载体

在这里插入图片描述

(3)在UE场景中添加任意的Actor,并添加上文创建的SliceDataLoader组件

在这里插入图片描述

(4)在该组件的细节面板中,选择测试用的dicom文件的相对路径,以及需要应用的材质(上文创建的用于dicom显示处理的材质)等参数

在这里插入图片描述

(5)Editor下点击Load Dicom按钮,即可实现对本地Dicom文件的加载解析,2D纹理的生成,以及最终在UE场景中显示正确的Dicom影像

在这里插入图片描述

3.参考资料

  • 【UE4】使用动态库(DLL)提示找不到dll该怎么解决呢:传送门
  • 医学图像数据集集锦(附下载):传送门
  • Download Train and Test Data:传送门
  • Volume Rendering (Raymarching) Plugin for Unreal Engine:传送门
  • DCMTK:传送门
  • GDCM:传送门
  • UTexture::UpdateResource() overwrote by winbase.h #define:传送门
  • UE4 #include <windows.h>之后UpdateResource报错:传送门
  • UE4custom node引用自定义.ush .usf文件:传送门
  • UE4 Gamma校正、sRGB、Linear:传送门

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

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

相关文章

lua(xlua)基础知识点记录一

1. 关于 (…) 操作符 编译阶段优化&#xff1a;Lua 编译器会对常量字符串进行优化处理&#xff0c;将连续的字符串拼接操作 (…) 合并为单个字符串。这种优化仅适用于编译期确定的常量字符串&#xff0c;不适用于运行时生成的动态字符串。 示例&#xff1a;local str "He…

【Python数据采集】Python爬取小红书搜索关键词下面的所有笔记的内容、点赞数量、评论数量等数据,绘制词云图、词频分析、数据分析

Python爬取小红书搜索关键词下面的所有笔记的内容、点赞数量、评论数量等数据&#xff0c;绘制词云图、词频分析、数据分析 使用 Python 编写一个简单的爬虫程序来从小红书抓取与指定关键词相关的笔记数据&#xff0c;并对这些数据进行基本的数据分析&#xff0c;包括词云图和…

最大子数组和问题-详解Kadane算法

最大子数组和问题-详解Kadane算法一、问题定义与暴力解法1.1 问题描述1.2 暴力解法的低效性二、Kadane算法的核心原理2.1 动态规划思想的应用2.2 优化空间复杂度三、Kadane算法的Java实现3.1 基础版本&#xff08;处理所有情况&#xff09;3.2 算法正确性验证四、Kadane算法的变…

Mongoose网络库深度解析:从单线程到多线程的架构演进

0. 引言&#xff1a;C/C网络编程的困境与突破 在C/C开发领域&#xff0c;网络编程一直是一个令人头疼的问题。与Python的requests库或Go的net/http包不同&#xff0c;C/C缺乏统一的包管理体系和标准化的网络API。开发者往往需要面对gcc/msvc版本差异、平台兼容性问题、以及各种…

Jfinal+SQLite处理 sqlite数据库执行FIND_IN_SET报错

方法一原代码sql " and FIND_IN_SET(s.M_ID," ids ")"; 修改为 sql " where s.M_ID"getInSql(ids);public static String getInSql(String ids) {String[] idArray ids.split(",");StringBuilder sql new StringBuilder(" I…

day24——Java高级技术深度解析:单元测试、反射、注解与动态代理

文章目录一、单元测试&#xff1a;JUnit框架精要1.1 单元测试核心概念1.2 JUnit快速入门实战基础步骤&#xff1a;断言机制验证结果1.3 JUnit核心注解解析二、反射机制&#xff1a;框架设计的基石2.1 反射核心概念2.2 获取Class对象的三种方式2.3 反射操作类成分获取并执行构造…

网页的性能优化,以及具体的应用场景

下面是每个性能优化技术的具体应用场景示例&#xff0c;结合代码说明如何在实际项目中使用这些优化方法&#xff1a; 1. 批量DOM操作与DocumentFragment 应用场景&#xff1a;动态渲染大量列表项&#xff08;如评论区、商品列表&#xff09; 问题&#xff1a;逐个添加DOM元素会…

Fiddler 中文版 API 调试与性能优化实践 官方中文网全程支持

在现代开发中&#xff0c;性能问题往往是产品上线后最容易被忽视的一环&#xff0c;尤其是API接口性能。一旦接口响应时间过长或在高并发场景下出现性能瓶颈&#xff0c;可能直接影响用户体验和系统稳定性。对于开发者来说&#xff0c;如何精确地找到瓶颈所在&#xff0c;如何模…

嵌入式硬件篇---机械臂运动学解算(3自由度)

实际 3 自由度机械臂的解算是机器人控制的核心&#xff0c;涉及运动学正解&#xff08;关节角度→末端位姿&#xff09;和逆解&#xff08;目标位姿→关节角度&#xff09;。以下从结构建模、解算方法、代码实现和应用场景四个维度详细展开&#xff0c;结合工业级机械臂的典型场…

在摄像机视图中想像在普通 3D 视口里那样随意移动

有两条最常用的方法&#xff1a;1. 「锁定相机到视图」(Lock Camera to View)步骤进入相机视图&#xff1a;按 Numpad 0&#xff08;若无数字键盘&#xff0c;可在 Edit → Preferences → Input 勾选 Emulate Numpad 后用主键盘 0&#xff09;。右侧呼出 N 面板&#xff0c;切…

An End-to-End Attention-Based Approach for Learning on Graphs NC 2025

NC 2025 | 一种基于端到端注意力机制的图学习方法 Nature Communications IF=15.7 综合性期刊 1区 参考:https://mp.weixin.qq.com/s/cZ-d8Sf8wtQ9wfcGOFimCg 今天介绍一篇发表在 Nature Communications 的图学习论文《An end-to-end attention-based approach for learnin…

【牛客刷题】小红的数字串

文章目录 一、题目描述 1.1 输入描述 1.2 输出描述 1.3 示例1 二、高效解法 2.1 核心算法设计 2.2 算法设计理念 2.2.1 算法流程详解 2.2.2 复杂度分析 2.3 算法优势分析 2.3.1 关键优化点 2.3.2 正确性验证 2.4 边界处理 2.5 总结与扩展 一、题目描述 小红拿到了一个数字串(由…

微算法科技技术创新,将量子图像LSQb算法与量子加密技术相结合,构建更加安全的量子信息隐藏和传输系统

随着信息技术的发展&#xff0c;数据的安全性变得尤为重要。在传统计算模式下&#xff0c;即便采用复杂的加密算法&#xff0c;也难以完全抵御日益增长的网络攻击威胁。量子计算技术的出现为信息安全带来了新的解决方案。然而&#xff0c;量子图像处理领域仍面临复杂度高、效率…

博客摘录「 Springboot入门到精通(超详细文档)」2025年7月4日

1.Spring Boot返回Json数据及数据封装1. Controller 中使用RestController注解即可返回 Json 格式的数据首先看看RestController注解包含了什么东西&#xff0c; ResponseBody 注解是将返回的数据结构转换为 Json 格式Target({ElementType.TYPE}) Retention(RetentionPolicy.RU…

企业安全防护:堡垒机技术解析

目录 一、堡垒机&#xff1a;企业IT运维的安全守门人 1.1 核心价值矩阵 1.2堡垒机典型部署架构 二、堡垒机如何构建安全防线 2.1 四层防护体系 2.2 关键工作流程 三、堡垒机关键技术指标对比表 四、智能堡垒机的发展趋势 一、堡垒机&#xff1a;企业IT运维的安全守门人…

传输层协议 TCP

TCP 协议TCP 全称为 "传输控制协议(Transmission Control Protocol"). 人如其名, 要对数据的传输进行一个详细的控制TCP 协议段格式源/目的端口号: 表示数据是从哪个进程来, 到哪个进程去32 位序号/32 位确认号4 位 TCP 报头长度: 表示该 TCP 头部有多少个 32 位 bit…

RT-Thread的概念和移植

一、操作系统的概念 操作系统&#xff08;英语&#xff1a;Operating System&#xff0c;缩写&#xff1a;OS&#xff09;是一组主管并控制计算机操作、运用和运行硬件、软件资源和提供公共服务来组织用户交互的相互关联的系统软件程序。根据运行的环境&#xff0c;操作系统可以…

基于单片机倾角测量仪/角度测量/水平仪

传送门 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目速选一览表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目功能速览 概述 本设计实现了一种基于单片机的高精度数字倾角测量仪。系统核心由倾角传感器&#xff08;ADXL345倾…

深度学习 -- 初步认识Torch

深度学习 – 初步认识Torch 文章目录深度学习 -- 初步认识Torch一&#xff0c;认识人工智能1.1 人工智能的本质1.2 人工智能的实现过程二&#xff0c;认识Torch2.1简介2.2 概述2.3 Tensor的创建2.3.1 torch.tensor2.3.2 torch.Tensor三&#xff0c;创建线性和随机张量3.1创建线…

BGP的“聪明选路”遇上了TCP的“路径洁癖”,需人工调和

在路由器R1上有两条外网&#xff0c;WAN1和WAN2。R1上做了域名分流功能&#xff0c;全局网址分到WAN1&#xff0c;指定域名分到WAN2&#xff08;优先级更高&#xff09;。症状是用户反馈部分网页无法打开。于是各种检查尝试...... 2天过去了......最终结论是&#xff1a;即使S…