이전 글에서는 opencv, 웹캠스트리밍 하는 법 대충 정리했는데 

원래 하려던건 언리얼 상에 가상 데스크톱 같이 만들고 싶었다.

 

듀얼 모니터 사용중에

주모니터 화면을 언리얼 화면상에 띄어보려고 시도하는데

windows.h 인클루드 시켰다가 동작안되서 맨붕왔지만

 

 

다행이 아래 링크 참조해서 문제 해결

 

 

 

https://liiyuulab.tistory.com/29

 

UE4 에서 windows.h 포함 헤더 include 시 주의점

나름 평화롭던? 나날을 보내며 UE4 게임 빌드 중 갑작스럽게 error C4003: not enough arguments for function-like macro invocation 'min' 라는 에러를 보게 되었다. 전에는 이런 에러가 한번도 난 적이 없어서 당황.

liiyuulab.tistory.com

 

 

 

 

게임모드해더에 

 

윈도우 헤더파일 인클루드하고,

GetScreenToCVMat 함수추가

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once


#include "Windows/AllowWindowsPlatformTypes.h"
#include <Windows.h>
#include "Windows/HideWindowsPlatformTypes.h"

#include "PreOpenCVHeaders.h"
#include <opencv2/opencv.hpp>
#include "PostOpenCVHeaders.h"

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "DesktopGameModeBase.generated.h"

/**
 * 
 */
UCLASS()
class HANDDESKTOP_API ADesktopGameModeBase : public AGameModeBase
{
	GENERATED_BODY()

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	cv::VideoCapture capture;
	cv::Mat image;

	UFUNCTION(BlueprintCallable)
	void ReadFrame();

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UTexture2D* imageTexture;
	UTexture2D* MatToTexture2D(const cv::Mat InMat);


	cv::Mat GetScreenToCVMat();
};

 

 

 

 

 

기존 리드프레임에선 웹캠 읽을건 아니니 주석처리하고

desktopImage 가져와서 텍스처로 변환

void ADesktopGameModeBase::ReadFrame()
{
	/*
	if (!capture.isOpened())
	{
		return;
	}
	capture.read(image);
	*/

	cv::Mat desktopImage = GetScreenToCVMat();
	imageTexture = MatToTexture2D(desktopImage);
}

 

 

 

윈도우 디바이스 컨텍스트 -> 비트맵 핸들 -> CV:Mat 변환 코드

cv::Mat ADesktopGameModeBase::GetScreenToCVMat()
{
	HDC hScreenDC = GetDC(NULL);
	HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
	int screenWidth = GetDeviceCaps(hScreenDC, HORZRES);
	int screenHeight = GetDeviceCaps(hScreenDC, VERTRES);

	HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, screenWidth, screenHeight);
	HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
	BitBlt(hMemoryDC, 0, 0, screenWidth, screenHeight, hScreenDC, 0, 0, SRCCOPY);
	SelectObject(hMemoryDC, hOldBitmap);

	cv::Mat matImage(screenHeight, screenWidth, CV_8UC4);
	GetBitmapBits(hBitmap, matImage.total() * matImage.elemSize(), matImage.data);

	return matImage;
}

 

여기서 주의할건

위 코드에서 만든 cv::Mat의 타입은 CV_8UC4 

 

기존 텍스처 변환 코드에서는 3채널에 대해서만 존재하므로 

4채널에 대해서 내용 추가

 

3채널의 경우 bgra 4채널로 변환하던거라

4채널 변환없이 그대로 사용하는 내용

UTexture2D* ADesktopGameModeBase::MatToTexture2D(const cv::Mat InMat)
{
	//create new texture, set its values
	UTexture2D* Texture = UTexture2D::CreateTransient(InMat.cols, InMat.rows, PF_B8G8R8A8);

	if (InMat.type() == CV_8UC3)//example for pre-conversion of Mat
	{
		cv::Mat bgraImage;
		//if the Mat is in BGR space, convert it to BGRA. There is no three channel texture in UE (at least with eight bit)
		cv::cvtColor(InMat, bgraImage, cv::COLOR_BGR2BGRA);

		//Texture->SRGB = 0;//set to 0 if Mat is not in srgb (which is likely when coming from a webcam)
		//other settings of the texture can also be changed here
		//Texture->UpdateResource();

		//actually copy the data to the new texture
		FTexture2DMipMap& Mip = Texture->GetPlatformData()->Mips[0];
		void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);//lock the texture data
		FMemory::Memcpy(Data, bgraImage.data, bgraImage.total() * bgraImage.elemSize());//copy the data
		Mip.BulkData.Unlock();
		Texture->PostEditChange();
		Texture->UpdateResource();
		return Texture;
	}
	else if (InMat.type() == CV_8UC4)
	{
		//actually copy the data to the new texture
		FTexture2DMipMap& Mip = Texture->GetPlatformData()->Mips[0];
		void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);//lock the texture data
		FMemory::Memcpy(Data, InMat.data, InMat.total() * InMat.elemSize());//copy the data
		Mip.BulkData.Unlock();
		Texture->PostEditChange();
		Texture->UpdateResource();
		return Texture;
	}
	//if the texture hasnt the right pixel format, abort.
	Texture->PostEditChange();
	Texture->UpdateResource();
	return Texture;
}

 

 

언리얼 엔진을 보조모니터에 놓고 실행시키면 주모니터 영역만 나온다.

 

잘나오긴 한데 기존 스크린을 640 x 480으로 해서 그런지 

실제 1920 x 1080 이미지와 맞지 않게 글자가 잘려나온다.

 

 

 

 

스크린(위젯 컴포넌트) 드로 사이즈를 FHD로 고치고

메인 위젯의 이미지 위젯도 FHD로 사이즈 수정

 

 

 

아까보다 화면이 커짐

 

 

 

 

 

 

화면 커지니 글자도 잘나온다.

 

 

 

 

+ Recent posts