UE5.4 用自带OpenCV4.55读取png、MP4、摄像头并在ui中显示的方法

server/2024/9/20 3:52:25/ 标签: ue5, ui, java

创建c++项目,项目build.cs中开启模块:

// Copyright Epic Games, Inc. All Rights Reserved.using UnrealBuildTool;public class OpencvT : ModuleRules
{public OpencvT(ReadOnlyTargetRules Target) : base(Target){PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput","OpenCV","OpenCVHelper","UMG","Slate","SlateCore","ImageWrapper" });PrivateDependencyModuleNames.AddRange(new string[] {  });}
}

编译器中创建userwidget的c++类,BP_userwidget的蓝图用户界面,BP_userwidget中添加image小部件,在userwidget.h中添加绑定image,userwidget.cpp中不用添加其他代码。

// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "Blueprint/UserWidget.h"
#include "Components/Image.h"
#include "MyUserWidget.generated.h"/*** */
UCLASS()
class OPENCVT_API UMyUserWidget : public UUserWidget
{GENERATED_BODY()UMyUserWidget(const FObjectInitializer& ObjectInitializer);virtual void NativeConstruct() override;public:UPROPERTY(BlueprintReadWrite, meta = (BindWidget))UImage* cv_Image;
};

创建actor c++类,添加头文件,创建userwidget实例,用来设置绑定的image,创建bp_用户界面实例,添加到视口,将actor类放入场景中,运行。

需要注意:cv加载数据为空或不正确时会引起崩溃

// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyOpenCVClass.generated.h"UCLASS()
class OPENCVT_API AMyOpenCVClass : public AActor
{GENERATED_BODY()public:// Sets default values for this actor's propertiesAMyOpenCVClass();protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;public:// Called every framevirtual void Tick(float DeltaTime) override;UPROPERTY(EditAnywhere)UMaterialInterface* BaseMaterial;UPROPERTY(BlueprintReadOnly,VisibleAnywhere)UTexture2D* test2d;//这里需要在编辑器内将bp_用户界面添加到NewWidgetBlueprintUPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI")TSubclassOf<UUserWidget> NewWidgetBlueprint;
};
#include "OpencvT/Public/MyOpenCVClass.h"#if WITH_OPENCV
#include "PreOpenCVHeaders.h"
#include "opencv2/opencv.hpp"
#include "PostOpenCVHeaders.h"
#endif//UpdateResource()需要的头文件
#include "ImageWrapperHelper.h"#include "MyUserWidget.h"// Sets default values
AMyOpenCVClass::AMyOpenCVClass()
{PrimaryActorTick.bCanEverTick = true;
}// Called when the game starts or when spawned
void AMyOpenCVClass::BeginPlay()
{Super::BeginPlay();FString filepath = TEXT("d:/abc.png");UE_LOG(LogTemp, Warning, TEXT("Trying to load image from: %s"), *filepath);// 使用 OpenCV 读取图像 (确保读取为 BGR 格式)cv::Mat img = cv::imread(TCHAR_TO_UTF8(*filepath));if (img.empty()){UE_LOG(LogTemp, Warning, TEXT("Failed to load image from: %s"), *filepath);return;}UE_LOG(LogTemp, Warning, TEXT("Successfully loaded image from: %s"), *filepath);// 将 BGR 图像转换为 BGRA 格式cv::Mat imgBGRA;cv::cvtColor(img, imgBGRA, cv::COLOR_BGR2BGRA);// 创建 UTexture2DUTexture2D* Texture2D = UTexture2D::CreateTransient(imgBGRA.cols, imgBGRA.rows, PF_B8G8R8A8);if (!Texture2D){UE_LOG(LogTemp, Warning, TEXT("texture CreateTransient failed"));return;}// 将 cv::Mat 数据逐行复制到 UTexture2D,确保内存对齐FTexture2DMipMap& MipMap = Texture2D->GetPlatformData()->Mips[0];void* TextureData = MipMap.BulkData.Lock(LOCK_READ_WRITE);int32 SrcPitch = imgBGRA.cols * imgBGRA.elemSize();int32 DstPitch = MipMap.SizeX * 4;for (int32 Row = 0; Row < imgBGRA.rows; ++Row){FMemory::Memcpy((uint8*)TextureData + Row * DstPitch, imgBGRA.ptr(Row), SrcPitch);}MipMap.BulkData.Unlock();Texture2D->UpdateResource();// 将 Texture2D 传递给 UI 部件test2d = Texture2D;// 确保 test2d 不为 nullif (test2d){UE_LOG(LogTemp, Warning, TEXT("test2d is valid, ready to set in UI"));}else{UE_LOG(LogTemp, Warning, TEXT("test2d is not valid"));}// 创建并设置 UI 小部件UUserWidget* bpwidget = CreateWidget<UUserWidget>(GetWorld(), NewWidgetBlueprint);UMyUserWidget* MyUserWidget = Cast<UMyUserWidget>(bpwidget);if (MyUserWidget && MyUserWidget->cv_Image){MyUserWidget->cv_Image->SetBrushFromTexture(test2d);MyUserWidget->cv_Image->Brush.ImageSize = FVector2D(test2d->GetSizeX(), test2d->GetSizeY());}bpwidget->AddToViewport();
}// Called every frame
void AMyOpenCVClass::Tick(float DeltaTime)
{Super::Tick(DeltaTime);
}

opencv读取MP4并播放的方法:UI部分不用修改,只需要修改OpenCV部分与tick部分

#include "OpencvT/Public/MyOpenCVClass.h"#if WITH_OPENCV
#include "PreOpenCVHeaders.h"
#include "opencv2/opencv.hpp"
#include "PostOpenCVHeaders.h"
#endif#include "ImageWrapperHelper.h"
#include "MyUserWidget.h"// 构造函数:设置默认值
AMyOpenCVClass::AMyOpenCVClass()
{PrimaryActorTick.bCanEverTick = true;
}// OpenCV VideoCapture
cv::VideoCapture VideoCapture;
float FrameTime;
bool bIsVideoPlaying = false;
float AccumulatedTime = 0.0f;cv::Mat Frame;// Called when the game starts or when spawned
void AMyOpenCVClass::BeginPlay()
{Super::BeginPlay();// 设置视频文件路径FString filepath = TEXT("d:/JSC.mp4");UE_LOG(LogTemp, Warning, TEXT("尝试加载视频文件: %s"), *filepath);// 使用 OpenCV 读取视频VideoCapture.open(TCHAR_TO_UTF8(*filepath));if (!VideoCapture.isOpened()){UE_LOG(LogTemp, Warning, TEXT("无法加载视频文件: %s"), *filepath);return;}UE_LOG(LogTemp, Warning, TEXT("成功加载视频文件: %s"), *filepath);// 从视频文件中获取帧速率double FPS = VideoCapture.get(cv::CAP_PROP_FPS);if (FPS <= 0){UE_LOG(LogTemp, Warning, TEXT("无法获取帧速率,默认设置为30 FPS"));FPS = 30.0; // 如果获取失败,默认设置为30 FPS}FrameTime = 1.0f / FPS;bIsVideoPlaying = true;UE_LOG(LogTemp, Warning, TEXT("视频帧速率: %f FPS"), FPS);// 初始化纹理int width = static_cast<int>(VideoCapture.get(cv::CAP_PROP_FRAME_WIDTH));int height = static_cast<int>(VideoCapture.get(cv::CAP_PROP_FRAME_HEIGHT));UTexture2D* Texture2D = UTexture2D::CreateTransient(width, height, PF_B8G8R8A8);if (!Texture2D){UE_LOG(LogTemp, Warning, TEXT("纹理创建失败"));return;}test2d = Texture2D;// 创建并设置 UI 小部件UUserWidget* bpwidget = CreateWidget<UUserWidget>(GetWorld(), NewWidgetBlueprint);UMyUserWidget* MyUserWidget = Cast<UMyUserWidget>(bpwidget);if (MyUserWidget && MyUserWidget->cv_Image){MyUserWidget->cv_Image->SetBrushFromTexture(test2d);MyUserWidget->cv_Image->Brush.ImageSize = FVector2D(test2d->GetSizeX(), test2d->GetSizeY());}bpwidget->AddToViewport();
}// Called every frame
void AMyOpenCVClass::Tick(float DeltaTime)
{Super::Tick(DeltaTime);if (!VideoCapture.isOpened() || !bIsVideoPlaying){return;}// 累积时间AccumulatedTime += DeltaTime;// 检查是否需要更新视频帧if (AccumulatedTime >= FrameTime){if (VideoCapture.read(Frame)){// 转换 BGR 图像为 BGRA 格式cv::Mat FrameBGRA;cv::cvtColor(Frame, FrameBGRA, cv::COLOR_BGR2BGRA);// 将 cv::Mat 数据逐行复制到 UTexture2D,确保内存对齐FTexture2DMipMap& MipMap = test2d->GetPlatformData()->Mips[0];void* TextureData = MipMap.BulkData.Lock(LOCK_READ_WRITE);int32 SrcPitch = FrameBGRA.cols * FrameBGRA.elemSize();int32 DstPitch = MipMap.SizeX * 4;for (int32 Row = 0; Row < FrameBGRA.rows; ++Row){FMemory::Memcpy(static_cast<uint8*>(TextureData) + Row * DstPitch, FrameBGRA.ptr(Row), SrcPitch);}MipMap.BulkData.Unlock();test2d->UpdateResource();}// 重置累积时间,但保留超出部分以保持同步AccumulatedTime -= FrameTime;}
}// Called when the actor is destroyed or game ends
void AMyOpenCVClass::EndPlay(const EEndPlayReason::Type EndPlayReason)
{Super::EndPlay(EndPlayReason);// 停止视频捕捉并清理资源if (VideoCapture.isOpened()){VideoCapture.release();UE_LOG(LogTemp, Warning, TEXT("VideoCapture released successfully"));}bIsVideoPlaying = false;
}

OpenCV调用摄像头的方法:

#include "OpencvT/Public/MyOpenCVClass.h"#if WITH_OPENCV
#include "PreOpenCVHeaders.h"
#include "opencv2/opencv.hpp"
#include "PostOpenCVHeaders.h"
#endif#include "ImageWrapperHelper.h"
#include "MyUserWidget.h"// 构造函数:设置默认值
AMyOpenCVClass::AMyOpenCVClass()
{PrimaryActorTick.bCanEverTick = true;
}// OpenCV VideoCapture
cv::VideoCapture VideoCapture;
float FrameTime;
bool bIsVideoPlaying = false;
float AccumulatedTime = 0.0f;cv::Mat Frame;// Called when the game starts or when spawned
void AMyOpenCVClass::BeginPlay()
{Super::BeginPlay();// 打开摄像头(通常使用索引 0 表示默认摄像头)int CameraIndex = 0;UE_LOG(LogTemp, Warning, TEXT("尝试打开摄像头: %d"), CameraIndex);// 使用 OpenCV 打开摄像头VideoCapture.open(CameraIndex);if (!VideoCapture.isOpened()){UE_LOG(LogTemp, Warning, TEXT("无法打开摄像头: %d"), CameraIndex);return;}UE_LOG(LogTemp, Warning, TEXT("成功打开摄像头: %d"), CameraIndex);// 获取摄像头的帧速率(注意:某些摄像头可能不支持帧速率查询)double FPS = VideoCapture.get(cv::CAP_PROP_FPS);if (FPS <= 0){UE_LOG(LogTemp, Warning, TEXT("无法获取摄像头帧速率,默认设置为30 FPS"));FPS = 30.0; // 如果获取失败,默认设置为30 FPS}FrameTime = 1.0f / FPS;bIsVideoPlaying = true;UE_LOG(LogTemp, Warning, TEXT("摄像头帧速率: %f FPS"), FPS);// 初始化纹理int width = static_cast<int>(VideoCapture.get(cv::CAP_PROP_FRAME_WIDTH));int height = static_cast<int>(VideoCapture.get(cv::CAP_PROP_FRAME_HEIGHT));UTexture2D* Texture2D = UTexture2D::CreateTransient(width, height, PF_B8G8R8A8);if (!Texture2D){UE_LOG(LogTemp, Warning, TEXT("纹理创建失败"));return;}test2d = Texture2D;// 创建并设置 UI 小部件UUserWidget* bpwidget = CreateWidget<UUserWidget>(GetWorld(), NewWidgetBlueprint);UMyUserWidget* MyUserWidget = Cast<UMyUserWidget>(bpwidget);if (MyUserWidget && MyUserWidget->cv_Image){MyUserWidget->cv_Image->SetBrushFromTexture(test2d);MyUserWidget->cv_Image->Brush.ImageSize = FVector2D(test2d->GetSizeX(), test2d->GetSizeY());}bpwidget->AddToViewport();
}// Called every frame
void AMyOpenCVClass::Tick(float DeltaTime)
{Super::Tick(DeltaTime);if (!VideoCapture.isOpened() || !bIsVideoPlaying){return;}// 累积时间AccumulatedTime += DeltaTime;// 检查是否需要更新摄像头帧if (AccumulatedTime >= FrameTime){if (VideoCapture.read(Frame)){// 转换 BGR 图像为 BGRA 格式cv::Mat FrameBGRA;cv::cvtColor(Frame, FrameBGRA, cv::COLOR_BGR2BGRA);// 将 cv::Mat 数据逐行复制到 UTexture2D,确保内存对齐FTexture2DMipMap& MipMap = test2d->GetPlatformData()->Mips[0];void* TextureData = MipMap.BulkData.Lock(LOCK_READ_WRITE);int32 SrcPitch = FrameBGRA.cols * FrameBGRA.elemSize();int32 DstPitch = MipMap.SizeX * 4;for (int32 Row = 0; Row < FrameBGRA.rows; ++Row){FMemory::Memcpy(static_cast<uint8*>(TextureData) + Row * DstPitch, FrameBGRA.ptr(Row), SrcPitch);}MipMap.BulkData.Unlock();test2d->UpdateResource();}// 重置累积时间,但保留超出部分以保持同步AccumulatedTime -= FrameTime;}
}// Called when the actor is destroyed or game ends
void AMyOpenCVClass::EndPlay(const EEndPlayReason::Type EndPlayReason)
{Super::EndPlay(EndPlayReason);// 停止摄像头捕捉并清理资源if (VideoCapture.isOpened()){UE_LOG(LogTemp, Warning, TEXT("Attempting to release VideoCapture..."));VideoCapture.release();UE_LOG(LogTemp, Warning, TEXT("VideoCapture released successfully"));}else{UE_LOG(LogTemp, Warning, TEXT("VideoCapture was not open when EndPlay was called."));}// 处理其他资源,例如 UI 小部件if (test2d){// 在此处处理任何需要清理的纹理资源UE_LOG(LogTemp, Warning, TEXT("Releasing texture resources..."));test2d->ReleaseResource();test2d = nullptr;}bIsVideoPlaying = false;
}
使用ncnn时会与ReleaseResource()函数冲突需要加入#undef UpdateResource取消冲突宏定义
#include "ncnn/net.h"
// 取消冲突宏定义
#undef UpdateResource
使用test2d->ReleaseResource();函数时需要加入#undef UpdateResource

http://www.ppmy.cn/server/102072.html

相关文章

【学习总结】JVM篇

JVM JVM基础知识 主力机型 HotSpot VM HotSpot虚拟机时OpenJDK和OracleJDK中默认的Java虚拟机。它最初并非由Sun公司所开发&#xff0c;而是由一家名为“Longview Technologies”的小公司设计。Sun公司注意到这款虚拟机在即时编译等多个方面有着优秀的理念和实际成果&#…

golang gin框架中创建自定义中间件的2种方式总结 - func(*gin.Context)方式和闭包函数方式定义gin中间件

在gin框架中&#xff0c;我们可以通过2种方式创建自定义中间件&#xff1a; 1. 直接定义一个类型为 func(*gin.Context)的函数或者方法 这种方式是我们常用的方式&#xff0c;也就是定义一个参数为*gin.Context的函数或者方法。定义的方法就是创建一个 参数类型为 gin.Handler…

xiaomi pad 6PRO 小米平板6 pro hyperOS降级 澎湃os 降级MIUI 14 教程 免解锁BL 降级,168小时解锁绑定

小米平板 6 Pro 机型代号 &#xff1a;liuqin 降级MIUI 14 小米澎湃 OS 正式版 澎湃OS安卓发布日期卡刷包线刷包OS1.0.7.0.UMYCNXM14.02024-07-13miui_LIUQIN_OS1.0.7.0.UMYCNXM_d618a5c980_14.0.zipliuqin_images_OS1.0.7.0.UMYCNXM_20240705.0000.00_14.0_cn_8cbf5920be.…

【学习笔记】Day 16-17

一、进度概述 1、ddnet_main 相关代码学习&#xff08;预计 3-4 天&#xff09; 二、详情 1、顶层结构 关于代码顶层结构的一些思考和总结&#xff0c;其中下图为师兄代码的文件结构 总结&#xff1a; 对于一个优秀的代码&#xff0c;其文件结构一定也是清晰的&#…

HarmonyOS Developer之五星好评

rating rating为评分条组件&#xff0c;用来实现用户使用感受的衡量标准条(如&#xff1a;五星好评) 在pages/index目录下的hml文件中创建一个rating组件: HTML <!-- xxx.hml --> <div class"container"><rating></rating> </div>…

类和对象(下)(1)

类和对象&#xff08;下&#xff09; 再探构造函数 我们之前在实现构造函数的时候&#xff0c;初始化成员变量使用的方式都是在函数体内进行赋值&#xff0c;其实构造函数初始化成员变量还有一种方式&#xff1a;初始化列表。 初始化列表不只是为了写得方便&#xff0c;还能解…

【Kubernetes】k8s集群图形化管理工具之rancher

目录 一.Rancher概述 1.Rancher简介 2.Rancher与k8s的关系及区别 3.Rancher具有的优势 二.Rancher的安装部署 1.实验准备 2.安装 rancher 3.rancher的浏览器使用 一.Rancher概述 1.Rancher简介 Rancher 是一个开源的企业级多集群 Kubernetes 管理平台&#xff0c;实…

Nginx与Tomcat的区别

Nginx与Tomcat的区别 —— 经验笔记 引言 在现代Web开发中&#xff0c;选择合适的服务器软件对于构建高性能、可靠的应用程序至关重要。Nginx 和 Tomcat 是两种常见的服务器软件&#xff0c;尽管它们都可以被归类为Web服务器&#xff0c;但它们的设计目标和应用场景有着本质的…

WordPress网站速度优化

提升网站速度对用户体验和搜索引擎排名至关重要。无论你是新手博主&#xff0c;还是经验丰富的网站开发人员&#xff0c;要想优化WordPress网站&#xff0c;需要长时间的努力和坚持。以下是按入门、中级和专家级别介绍的12个实用方法&#xff0c;帮助你提升WordPress网站的整体…

zigbee笔记:十、ZStack(2.3.0-1.4.0)的OSAL使用分析

zigbee笔记&#xff1a;九中&#xff0c;我们已经学会了利用模板&#xff0c;定制自己的个性开发工程&#xff0c;本文为协议栈&#xff08;ZStack-CC2530-2.3.0-1.4.0&#xff09;代码使用分析笔记&#xff0c;来进一步掌握协议栈的使用。 一、协议栈使用知识点 1、协调器、路…

fetch跨域请求数据的前端设置和后端php的header设置

跨源请求&#xff0c;也称为CORS&#xff08;Cross-Origin Resource Sharing&#xff09;请求&#xff0c;是Web开发中常见的一种需求&#xff0c;允许一个网页的JavaScript代码向与该网页不同源的服务器发出HTTP请求。以下是使用JavaScript中的fetch函数进行跨源请求的一个基本…

【51单片机】让AI识别电路图,帮你进行编码(以51单片机为例)

让AI识别电路图,帮你进行编码&#xff08;以51单片机为例&#xff09; ​ 这里使用的AI大模型使用的是 Copilot。&#xff08;两个前提&#xff1a;1. 科学上网、2. 有微软账号&#xff09; 今天测试了一下Copilot识别图片的能力&#xff0c;能力还是可圈可点的。 首先准备一…

C语言日常练习 Day17

目录 一、找出一个二维数组的鞍点 二、有一篇文章&#xff0c;共有3行文字&#xff0c;每行有80个字符。要求分别统计出其中的英文大写字母、小写字母、数字、空格以及其他字符的个数 三、有一行电文&#xff0c;已按下面规律编译成密码&#xff1a;A->Z,a->z,B->Y,…

wordpress视频模板

视频背景wordpress官网主题 红色风格的wordpress主题&#xff0c;首页视频背景&#xff0c;鼠标滚动翻转展示内容&#xff0c;适合公司官网使用。 https://www.jianzhanpress.com/?p7288 MCN传媒wordpress主题 红色风格wordpress大视频背景主题&#xff0c;适合做直播业务的…

【Harmony OS 4.0】水果排行榜案例

ets/example2/Models export class FruitsData {id: stringname: stringvote: stringconstructor(id: string, name: string, vote: string) {this.id idthis.name namethis.vote vote} }ets/example2/TitleComponent // app标题子组件 import APPContext from ohos.app.a…

【ARM 芯片 安全与攻击 5.4 -- Meltdown 攻击与防御介绍】

文章目录 什么是 Meltdown 攻击?Meltdown 攻击的基本原理Meltdown 攻击代码示例Meltdown 攻击在芯片中的应用应用场景Meltdown 攻击与瞬态攻击、测信道攻击的关系针对 Meltdown 攻击的防御硬件级防御Summary什么是 Meltdown 攻击? Meltdown 攻击是一种利用处理器乱序执行(o…

【网络编程】组播的实现(C语言,linux,Ubuntu)

组播 1> 组播也是实现一对多的通信方式&#xff0c;对于广播而言&#xff0c;网络需要对每个消息进行复制转发&#xff0c;会占用大量的带宽&#xff0c;导致网络拥塞 2> 组播可以实现小范围的数据传播&#xff1a;将需要接收数据的接收端加入多播组&#xff0c;发送端…

Linux---05---用户组权限

课程回顾 vim编辑器 本章重点 文件权限 用户管理 用户组管理 一、文件权限 由于Linux是一个多人多任务的系统&#xff0c;因此经常会出现同一台机器同时有多个人进行操作&#xff0c;为了考虑每个人的隐私权以及每个人喜好的工作环境&#xff0c;所以文件的权限归属就至关…

50Kg大载重六旋翼无人机技术详解

50Kg大载重六旋翼无人机的机体结构设计是确保其高承载能力和飞行稳定性的基础。该机体通常采用轻质高强度的材料如碳纤维、铝合金或复合材料构建&#xff0c;以在保证结构强度的同时减轻整机重量。设计过程中&#xff0c;需考虑空气动力学原理&#xff0c;优化机臂布局、机身形…

Leetcode面试经典150题-189.轮转数组

解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {public void rotate(int[] nums, int k) {/**向右轮转 1 步: [7,1,2,3,4,5,6]向右轮转 2 步: [6,7,1,2,3,4,5]向右轮转 3 步: [5,6,7,1,2,3,4]....向右轮转 7 步: [1,2,3,4,5,6,7] *//**既然反转数组的倍数是不需…