UE5.4.2中使用NCNN库及时pose捕获视频的方法示例

种地  金牌会员 | 2024-8-19 11:50:11 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 556|帖子 556|积分 1668

本文中使用的是ue5.4.2中官方自带的Opencv4.55,官方ncnn-20240410-windows-vs2022-shared库,ncnn-assets-master的模子库,举行及时pose捕获,并将关键点显示在UI中。
必要创建ue c++ 项目,新建空白插件NCNNPlugin,复制bin,include,lib到插件目次下的Source\NCNNPlugin中,在NCNNPlugin.Build.cs中加载include、lib、dll,将dll复制到插件目次下的Plugins\NCNNPlugin\Binaries\Win64中(Plugins就是插件目次),再复制一份到项目目次下的XXX\Binaries\Win64中(XXX就是项目目次),其他都不消管,在项目的build.cs中开启插件NCNNPlugin,到这里就完成了插件的创建,NCNN库的加载。
NCNNPlugin.Build.cs
  1. // Copyright Epic Games, Inc. All Rights Reserved.
  2. using System;
  3. using UnrealBuildTool;
  4. using System.IO;
  5. public class NCNNPlugin : ModuleRules
  6. {
  7.         public NCNNPlugin(ReadOnlyTargetRules Target) : base(Target)
  8.         {
  9.                 PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
  10.                 string NCNNPath = Path.Combine(ModuleDirectory, "");
  11.                
  12.                 Console.WriteLine("ModuleDirectory::::::" + ModuleDirectory);
  13.                
  14.                 PublicIncludePaths.AddRange(
  15.                         new string[] {
  16.                                 Path.Combine(ModuleDirectory,"include")
  17.                         }
  18.                         );
  19.                
  20.                 PublicAdditionalLibraries.AddRange(new string[]
  21.                 {
  22.                         Path.Combine(ModuleDirectory,"lib","ncnn.lib"),
  23.                 });
  24.                
  25.                 if (Target.Platform == UnrealTargetPlatform.Win64)
  26.                 {
  27.                         string DLLPath = Path.Combine(ModuleDirectory, "bin", "ncnn.dll");
  28.                         RuntimeDependencies.Add(DLLPath);
  29.                         Console.WriteLine("DLLPath::::::" + DLLPath);
  30.                        
  31.                         PublicDelayLoadDLLs.Add("ncnn.dll");
  32.                        
  33.                         // 将 DLL 文件复制到项目的二进制目录
  34.                         string ProjectBinariesDir = Path.Combine(Target.RelativeEnginePath, "Binaries", Target.Platform.ToString());
  35.                         RuntimeDependencies.Add(Path.Combine(ProjectBinariesDir, "ncnn.dll"), StagedFileType.NonUFS);
  36.             PublicDefinitions.Add("__ARM_NEON=0");
  37.             PublicDefinitions.Add("__SSE2__=0");
  38.             PublicDefinitions.Add("__ANDROID_API__=0");
  39.             PublicDefinitions.Add("__mips_msa=0");
  40.             PublicDefinitions.Add("__loongarch_sx=0");
  41.             PublicDefinitions.Add("__riscv_vector=0");
  42.             
  43.                 }
  44.         PrivateIncludePaths.AddRange(
  45.                         new string[] {
  46.                                 // ... add other private include paths required here ...
  47.                         }
  48.                         );
  49.                        
  50.                
  51.                 PublicDependencyModuleNames.AddRange(
  52.                         new string[]
  53.                         {
  54.                                 "Core",
  55.                                 // ... add other public dependencies that you statically link with here ...
  56.                         }
  57.                         );
  58.                        
  59.                
  60.                 PrivateDependencyModuleNames.AddRange(
  61.                         new string[]
  62.                         {
  63.                                 "CoreUObject",
  64.                                 "Engine",
  65.                                 "Slate",
  66.                                 "SlateCore",
  67.                                 // ... add private dependencies that you statically link with here ...       
  68.                         }
  69.                         );
  70.                
  71.                
  72.                 DynamicallyLoadedModuleNames.AddRange(
  73.                         new string[]
  74.                         {
  75.                                 // ... add any modules that your module loads dynamically here ...
  76.                         }
  77.                         );
  78.         }
  79. }
复制代码
项目的build.cs
  1. // Copyright Epic Games, Inc. All Rights Reserved.
  2. using UnrealBuildTool;
  3. public class NCNN_Thiredparty : ModuleRules
  4. {
  5.         public NCNN_Thiredparty(ReadOnlyTargetRules Target) : base(Target)
  6.         {
  7.                 PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
  8.        
  9.                 PublicDependencyModuleNames.AddRange(new string[]
  10.                 {
  11.                         "Core",
  12.                         "CoreUObject",
  13.                         "Engine",
  14.                         "InputCore",
  15.                         "EnhancedInput",
  16.                         "OpenCV",
  17.                         "OpenCVHelper",
  18.                         "UMG",
  19.                         "Slate",
  20.                         "SlateCore",
  21.                         "ImageWrapper",
  22.                         "NCNNPlugin"
  23.         });
  24.                 PrivateDependencyModuleNames.AddRange(new string[] {  });
  25.                 // Uncomment if you are using Slate UI
  26.                 // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
  27.                
  28.                 // Uncomment if you are using online features
  29.                 // PrivateDependencyModuleNames.Add("OnlineSubsystem");
  30.                 // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
  31.         }
  32. }
复制代码
NCNN库的使用
创建Object类NcnnDataClass类,ActorComponent类NCNNTrackComponent,UserWidget类MyCPPWidget类(必要编辑器内创建),BP_UserWidget类(内添加image小部件CVImage)
NcnnDataClass.h(NcnnDataClass.cpp中不消修改)
  1. // Fill out your copyright notice in the Description page of Project Settings.
  2. #pragma once
  3. #include "CoreMinimal.h"
  4. #include "UObject/Object.h"
  5. #if WITH_OPENCV
  6. #include "PreOpenCVHeaders.h"
  7. #include "opencv2/opencv.hpp"
  8. #include "PostOpenCVHeaders.h"
  9. #endif
  10. #include "NcnnDataClass.generated.h"
  11. struct KeyPoint {
  12.         cv::Point2f p;
  13.         float prob;
  14. };
  15. UCLASS()
  16. class NCNN_THIREDPARTY_API UNcnnDataClass : public UObject
  17. {
  18.         GENERATED_BODY()
  19. };
复制代码
MyCPPWidget.h(MyCPPWidget.cpp中不消修改)
  1. // Fill out your copyright notice in the Description page of Project Settings.
  2. #pragma once
  3. #include "CoreMinimal.h"
  4. #include "Blueprint/UserWidget.h"
  5. #include "Components/Image.h"
  6. #include "MyCPPWidget.generated.h"
  7. /**
  8. *
  9. */
  10. UCLASS()
  11. class NCNN_THIREDPARTY_API UMyCPPWidget : public UUserWidget
  12. {
  13.         GENERATED_BODY()
  14. public:
  15.         virtual void NativeConstruct() override;
  16.     //绑定BP_userwidget中的小部件
  17.         UPROPERTY(BlueprintReadWrite,meta = (BindWidget))
  18.         UImage* CVImage;
  19. };
复制代码
NCNNTrackComponent.h
  1. // Fill out your copyright notice in the Description page of Project Settings.
  2. #pragma once
  3. #include "CoreMinimal.h"
  4. #include "Components/ActorComponent.h"
  5. #include "ncnn/net.h"
  6. #if WITH_OPENCV
  7. #include "PreOpenCVHeaders.h"
  8. #include "opencv2/opencv.hpp"
  9. #include "PostOpenCVHeaders.h"
  10. #endif
  11. #include "NcnnDataClass.h"
  12. #include "NCNNTrackComponent.generated.h"
  13. UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
  14. class NCNN_THIREDPARTY_API UNCNNTrackComponent : public UActorComponent
  15. {
  16.         GENERATED_BODY()
  17. public:
  18.         // Sets default values for this component's properties
  19.         UNCNNTrackComponent();
  20. protected:
  21.         // Called when the game starts
  22.         virtual void BeginPlay() override;
  23.         virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
  24.        
  25. public:
  26.         // Called every frame
  27.         virtual void TickComponent(float DeltaTime, ELevelTick TickType,
  28.                                    FActorComponentTickFunction* ThisTickFunction) override;
  29.        
  30.         //NCNN
  31.         ncnn::Net posenet;
  32.         static int detect_posenet(const cv::Mat& bgr, std::vector<KeyPoint>& keypoints, ncnn::Net& posenet);
  33.         static void draw_pose(cv::Mat& image, const std::vector<KeyPoint>& keypoints, int radius = 3, const cv::Scalar& color = cv::Scalar(0, 255, 0));
  34.        
  35.         //UI
  36.     //这里需要在创建一个ACtor并添加NCNNTrack组件后在编辑器内指认BP_widget,目的是将该widget->AddToViewport();
  37.         UPROPERTY(EditAnywhere,BlueprintReadWrite)
  38.         TSubclassOf<UUserWidget> BP_widget;
  39.         UPROPERTY(BlueprintReadOnly)
  40.         UTexture2D* CVLoadImage;
  41.         // OpenCV VideoCapture
  42.         cv::VideoCapture VideoCapture;
  43.         float FrameTime;
  44.         bool bIsVideoPlaying = false;
  45.         float AccumulatedTime = 0.0f;
  46.         cv::Mat Frame;
  47.         cv::Mat FrameBGRA;
  48. };
复制代码
NCNNTrackComponent.cpp
  1. #include "NCNNTrackComponent.h"
  2. #include "MyCPPWidget.h"
  3. // 取消冲突宏定义
  4. #undef UpdateResource
  5. // Sets default values for this component's properties
  6. UNCNNTrackComponent::UNCNNTrackComponent()
  7. {
  8.     PrimaryComponentTick.bCanEverTick = true;
  9.     posenet.opt.use_vulkan_compute = true;
  10.     posenet.load_param("D:/ue_temp/NCNN_Thiredparty/pose.param");
  11.     posenet.load_model("D:/ue_temp/NCNN_Thiredparty/pose.bin");
  12. }
  13. // Called when the game starts
  14. void UNCNNTrackComponent::BeginPlay()
  15. {
  16.     Super::BeginPlay();
  17.    
  18.     //加载视频
  19.     FString filepath = TEXT("d:/dance.mp4");
  20.     UE_LOG(LogTemp, Warning, TEXT("尝试加载视频文件: %s"), *filepath);
  21.     // 使用 OpenCV 读取视频
  22.     VideoCapture.open(TCHAR_TO_UTF8(*filepath));
  23.     if (!VideoCapture.isOpened())
  24.     {
  25.         UE_LOG(LogTemp, Warning, TEXT("无法加载视频文件: %s"), *filepath);
  26.         return;
  27.     }
  28.     UE_LOG(LogTemp, Warning, TEXT("成功加载视频文件: %s"), *filepath);
  29.     // 从视频文件中获取帧速率
  30.     double FPS = VideoCapture.get(cv::CAP_PROP_FPS);
  31.     if (FPS <= 0)
  32.     {
  33.         UE_LOG(LogTemp, Warning, TEXT("无法获取帧速率,默认设置为30 FPS"));
  34.         FPS = 30.0; // 如果获取失败,默认设置为30 FPS
  35.     }
  36.     FrameTime = 1.0f / FPS;
  37.     bIsVideoPlaying = true;
  38.     UE_LOG(LogTemp, Warning, TEXT("视频帧速率: %f FPS"), FPS);
  39.     // 初始化纹理
  40.     int width = static_cast<int>(VideoCapture.get(cv::CAP_PROP_FRAME_WIDTH));
  41.     int height = static_cast<int>(VideoCapture.get(cv::CAP_PROP_FRAME_HEIGHT));
  42.     UTexture2D* Texture2D = UTexture2D::CreateTransient(width, height, PF_B8G8R8A8);
  43.     if (!Texture2D)
  44.     {
  45.         UE_LOG(LogTemp, Warning, TEXT("纹理创建失败"));
  46.         return;
  47.     }
  48.     CVLoadImage = Texture2D;
  49.     // 创建并设置 UI 小部件
  50.     UUserWidget* bpwidget = CreateWidget<UUserWidget>(GetWorld(), BP_widget);
  51.     UMyCPPWidget* CPPWidget = Cast<UMyCPPWidget>(bpwidget);
  52.     if (CPPWidget && CPPWidget->CVImage)
  53.     {
  54.         CPPWidget->CVImage->SetBrushFromTexture(CVLoadImage);
  55.         CPPWidget->CVImage->Brush.ImageSize = FVector2D(CVLoadImage->GetSizeX(), CVLoadImage->GetSizeY());
  56.     }
  57.     bpwidget->AddToViewport();
  58. }
  59. void UNCNNTrackComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
  60. {
  61.     Super::EndPlay(EndPlayReason);
  62.     if (VideoCapture.isOpened())
  63.     {
  64.         VideoCapture.release();
  65.         UE_LOG(LogTemp, Warning, TEXT("VideoCapture released successfully"));
  66.     }
  67.     bIsVideoPlaying = false;
  68. }
  69. void UNCNNTrackComponent::TickComponent(float DeltaTime, ELevelTick TickType,
  70.                                         FActorComponentTickFunction* ThisTickFunction)
  71. {
  72.     Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
  73.    
  74.     if (!VideoCapture.isOpened() || !bIsVideoPlaying)
  75.     {
  76.         return;
  77.     }
  78.     // 累积时间
  79.     AccumulatedTime += DeltaTime;
  80.     if (AccumulatedTime >= FrameTime)
  81.     {
  82.         if (VideoCapture.read(Frame))
  83.         {
  84.             // 调整帧大小至 256x512,这里需要注意视频尺寸需要与模型要求尺寸保持一致关键点才能在正确位置
  85.             cv::resize(Frame, Frame, cv::Size(256, 512));
  86.             std::vector<KeyPoint> keypoints;
  87.             detect_posenet(Frame, keypoints, posenet);
  88.             // 如果 FrameBGRA 为空或尺寸不匹配,则初始化
  89.             if (FrameBGRA.empty() || FrameBGRA.size() != Frame.size())
  90.             {
  91.                 FrameBGRA = cv::Mat(Frame.size(), CV_8UC4);
  92.             }
  93.             // 转换 BGR 图像为 BGRA 格式,并在 FrameBGRA 中保存
  94.             cv::cvtColor(Frame, FrameBGRA, cv::COLOR_BGR2BGRA);
  95.             draw_pose(FrameBGRA, keypoints);
  96.             // 更新纹理
  97.             FTexture2DMipMap& MipMap = CVLoadImage->GetPlatformData()->Mips[0];
  98.             void* TextureData = MipMap.BulkData.Lock(LOCK_READ_WRITE);
  99.             int32 SrcPitch = FrameBGRA.cols * FrameBGRA.elemSize();
  100.             int32 DstPitch = MipMap.SizeX * 4;
  101.             for (int32 Row = 0; Row < FrameBGRA.rows; ++Row)
  102.             {
  103.                 FMemory::Memcpy(static_cast<uint8*>(TextureData) + Row * DstPitch, FrameBGRA.ptr(Row), SrcPitch);
  104.             }
  105.             MipMap.BulkData.Unlock();
  106.             CVLoadImage->UpdateResource();
  107.         }
  108.         // 重置累积时间
  109.         AccumulatedTime -= FrameTime;
  110.     }
  111. }
  112. int UNCNNTrackComponent::detect_posenet(const cv::Mat& bgr, std::vector<KeyPoint>& keypoints, ncnn::Net& posenet)
  113. {
  114.     int w = bgr.cols;
  115.     int h = bgr.rows;
  116.     // 使用原始尺寸创建输入张量
  117.     ncnn::Mat in = ncnn::Mat::from_pixels(bgr.data, ncnn::Mat::PIXEL_BGR2RGB, w, h);
  118.     // 数据预处理:减去均值和归一化
  119.     const float mean_vals[3] = { 0.485f * 255.f, 0.456f * 255.f, 0.406f * 255.f };
  120.     const float norm_vals[3] = { 1 / 0.229f / 255.f, 1 / 0.224f / 255.f, 1 / 0.225f / 255.f };
  121.     in.substract_mean_normalize(mean_vals, norm_vals);
  122.     ncnn::Extractor ex = posenet.create_extractor();
  123.     ex.input("data", in);
  124.     ncnn::Mat out;
  125.     ex.extract("conv3_fwd", out);
  126.     // 解析heatmap以获取关键点
  127.     keypoints.clear();
  128.     for (int p = 0; p < out.c; p++) {
  129.         const ncnn::Mat m = out.channel(p);
  130.         float max_prob = 0.f;
  131.         int max_x = 0;
  132.         int max_y = 0;
  133.         for (int y = 0; y < m.h; y++) {
  134.             const float* ptr = m.row(y);
  135.             for (int x = 0; x < m.w; x++) {
  136.                 float prob = ptr[x];
  137.                 if (prob > max_prob) {
  138.                     max_prob = prob;
  139.                     max_x = x;
  140.                     max_y = y;
  141.                 }
  142.             }
  143.         }
  144.         KeyPoint keypoint;
  145.         keypoint.p = cv::Point2f(max_x * w / (float)m.w, max_y * h / (float)m.h);  // 按照原始图像尺寸调整关键点位置
  146.         keypoint.prob = max_prob;
  147.         keypoints.push_back(keypoint);
  148.     }
  149.     return 0;
  150. }
  151. void UNCNNTrackComponent::draw_pose(cv::Mat& image, const std::vector<KeyPoint>& keypoints, int radius,
  152.     const cv::Scalar& color)
  153. {
  154.     // draw joint
  155.     for (size_t i = 0; i < keypoints.size(); i++) {
  156.         const KeyPoint& keypoint = keypoints[i];
  157.         fprintf(stderr, "%.2f %.2f = %.5f\n", keypoint.p.x, keypoint.p.y, keypoint.prob);
  158.         if (keypoint.prob < 0.5f)
  159.             continue;
  160.         // 使用自定义半径和颜色绘制关键点
  161.         cv::circle(image, keypoint.p, radius, color, -1);
  162.     }
  163. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

种地

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表