2023. 8. 23. 17:37ㆍunreal engine
구우우우우우욷이 언리얼에서 기본으로 제공하는 웹캠 연결자가 있음에도 opencv를 쓰는 이유는
이후에 이미지에 postprocessing을 하기 위해서는 opencv가 훠어어어얼씬 편하기 때문이다...
#1. opencv 깔기
이미 언리얼에서는 opencv를 플러그인으로 제공하고 있다.
제공하는 기능이 좀 많이 허접♡ 해서 그렇지만,
opencv 홈페이지에 가서 다운받고 언리얼용으로 새로 맞춰서 빌드하고 임포트 과정에서 수많은 에러와 싸워가면서 겨우 성공하고... 이럴 필요가 없다는 이야기다.
opencv플러그인은 대충 여기 있다.
이걸 내 프로젝트로 들고 온다.
그리고 집어넣는다.
#2. webcam 읽어주는 widget
만들 때 https://github.com/VegetableWithChicken/OpenCVForUnreal/tree/5.0 를 많이 참고했다.
widget을 쓸 거기 때문에 UMG 모듈을 들고 와야 하고,
Texture에 덮어쓰는 부분이 있어서 RHI, RenderCore를 들고왔다...
*** OpenCVWidget.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Runtime/UMG/Public/Blueprint/UserWidget.h"
#include "Runtime/Engine/Classes/Engine/Texture2D.h"
#include "Runtime/Core/Public/HAL/RunnableThread.h"
#include "Runtime/Core/Public/HAL/Runnable.h"
#include "OpenCVHelper.h"
#if WITH_OPENCV
#include "PreOpenCVHeaders.h"
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include "PostOpenCVHeaders.h"
#endif // WITH_OPENCV
#include "OpenCVWidget.generated.h"
class FRunnable;
class FCameraReadThread;
UCLASS(BlueprintType)
class OPENCVHELPER_API UOpenCVWidget : public UUserWidget
{
GENERATED_BODY()
public:
UOpenCVWidget(const FObjectInitializer& ObjectInitializer);
virtual void NativeConstruct() override; //init
virtual void NativeDestruct() override; //destroy
// The device ID opened by the Video Stream
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "OpenCV")
int32 CameraID;
// The webcam size width and height (width, height)
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "OpenCV")
FVector2D CameraSize;
// Camera and read thread
cv::VideoCapture cap;
FCameraReadThread* ReadThread;
// Camera readframe and cv::Mat to unreal Texture
void ReadFrame();
UTexture2D* ConvertMat2Texture2D(const cv::Mat& inMat);
// Blueprint Event called every time the video frame is updated
UFUNCTION(BlueprintImplementableEvent, Category = "OpenCV")
void OnTextureUpdated(UTexture2D* outRGB);
};
class OPENCVHELPER_API FCameraReadThread :public FRunnable
{
public:
static FCameraReadThread* InitThread(UOpenCVWidget* inWidget)
{
if (!ReadInstance && FPlatformProcess::SupportsMultithreading())
{
ReadInstance = new FCameraReadThread(inWidget);
}
return ReadInstance;
}
public:
virtual bool Init() override
{
StopThreadCounter.Increment();
return true;
}
virtual uint32 Run() override
{
if (!ReadWidget)
{
UE_LOG(LogTemp, Warning, TEXT("There is no OpenCV Widget"));
return 1;
}
while (StopThreadCounter.GetValue())
{
ReadWidget->ReadFrame();
}
return 0;
}
virtual void Exit() override
{
}
virtual void Stop() override
{
if (ReadInstance)
{
ReadInstance->EnsureThread();
delete ReadInstance;
ReadInstance = nullptr;
}
}
void EnsureThread()
{
StopThreadCounter.Decrement();
if (ReadImageThread) {
ReadImageThread->WaitForCompletion();
}
}
protected:
FCameraReadThread(UOpenCVWidget* inWidget)
{
ReadWidget = inWidget;
ReadImageThread = FRunnableThread::Create(this, TEXT("ReadImageThread"));
}
~FCameraReadThread() {
};
private:
FRunnableThread* ReadImageThread;
UOpenCVWidget* ReadWidget;
static FCameraReadThread* ReadInstance;
FThreadSafeCounter StopThreadCounter;
};
*** OpenCVWidget.cpp
#include "OpenCVWidget.h"
//--------------------------------------------------------------------//
// for unreal widget basic functions
//--------------------------------------------------------------------//
UOpenCVWidget::UOpenCVWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
CameraID = 0;
CameraSize = FVector2D(320, 240);
cap = cv::VideoCapture();
}
void UOpenCVWidget::NativeConstruct()
{
Super::NativeConstruct();
//open the camera and read thread
Async<>(EAsyncExecution::Thread, [=]()
{
if (cap.open(CameraID))
{
UE_LOG(LogTemp, Warning, TEXT("OpenCamera Sucess"));
cap.set(cv::CAP_PROP_FRAME_WIDTH, CameraSize.X);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, CameraSize.Y);
cap.set(cv::CAP_PROP_FPS, 30);
ReadThread = FCameraReadThread::InitThread(this);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("OpenCamera Failed"));
}
FPlatformProcess::Sleep(0.01);
});
}
void UOpenCVWidget::NativeDestruct()
{
//Close camera and read thread
if (ReadThread)
{
ReadThread->Stop();
ReadThread = nullptr;
}
if (cap.isOpened())
{
cap.release();
}
Super::NativeDestruct();
}
//--------------------------------------------------------------------//
// for opencv camera thread capture
//--------------------------------------------------------------------//
void UOpenCVWidget::ReadFrame()
{
if (cap.isOpened())
{
cv::Mat frame;
cap.read(frame);
if (frame.empty()) return;
if (frame.channels() == 4)
{
cv::cvtColor(frame, frame, cv::COLOR_BGRA2RGB);
}
AsyncTask(ENamedThreads::GameThread, [=]()
{
UTexture2D* outTexture = ConvertMat2Texture2D(frame);
OnTextureUpdated(outTexture);
});
}
}
UTexture2D* UOpenCVWidget::ConvertMat2Texture2D(const cv::Mat& inMat)
{
int32 width = inMat.cols;
int32 height = inMat.rows;
int32 Channels = inMat.channels();
cv::Mat out;
cv::cvtColor(inMat, out, cv::COLOR_RGB2RGBA);
UTexture2D* NewTexture = UTexture2D::CreateTransient(width, height);
NewTexture->SRGB = 0;
//NewTexture >CompressionSettings = TextureCompressionSettings::TC_Displacementmap;
int DataSize = width * height * 4;
void* Datas = NewTexture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE);
FMemory::Memmove(Datas, out.data, DataSize);
NewTexture->PlatformData->Mips[0].BulkData.Unlock();
NewTexture->UpdateResource();
return NewTexture;
}
/*Thread Instance*/
FCameraReadThread* FCameraReadThread::ReadInstance = nullptr;
#3. 프로젝트에 적용하기
아까 만들었던 OpenCVWidget을 부모로 하는 위젯 블루프린트를 하나 만들어 주고,
Image를 한 개 박아넣는다.
이미지에 사용할 mat는 그냥 단순히 ui용 mat에서 texture하나 달아놓은 거다.
Texture를 Param으로 빼 놔야 material instance에서 갈아끼워 줄 수 있다.
헤더에서 정의했던
UFUNCTION(BlueprintImplementableEvent, Category = "OpenCV")
void OnTextureUpdated(UTexture2D* outRGB);
는 ReadFrame() 이 끝날 때 마다 호출된다. blueprint에서는 해당 이벤트를 받아서 material의 텍스처를 교환해 준다.
'unreal engine' 카테고리의 다른 글
외부 DLL 사용하기 (0) | 2024.04.12 |
---|---|
Tickable Object (0) | 2024.02.23 |
off-axis projection in unreal (0) | 2023.05.16 |
Animation이 적용된 Skeletal Mesh (0) | 2023.05.13 |
Gradient Material (0) | 2023.05.11 |