如何通过UDP将Python图像发送至Unreal Engine并生成Texture?
问题:UDP传输图像到Unreal Engine并转为Texture的接收处理问题
我希望通过UDP从Python脚本向Unreal Engine发送图像,在Unreal中加载并创建为Texture,但目前在接收图像环节遇到了问题。
Python UDP发送脚本
import cv2, imutils, socket import numpy as np import time import base64 BUFF_SIZE = 65536 server_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_RCVBUF,BUFF_SIZE) host_name = socket.gethostname() host_ip = '127.0.0.1'# socket.gethostbyname(host_name) print(host_ip) port = 54001 socket_address = (host_ip,port) server_socket.bind(socket_address) print('Listening at:',socket_address) vid = cv2.VideoCapture(0) # replace 'rocket.mp4' with 0 for webcam fps,st,frames_to_count,cnt = (0,0,20,0) while True: msg,client_addr = server_socket.recvfrom(BUFF_SIZE) print('GOT connection from ',client_addr) WIDTH=400 while(vid.isOpened()): _,frame = vid.read() frame = imutils.resize(frame,width=WIDTH) encoded,buffer = cv2.imencode('.jpg',frame,[cv2.IMWRITE_JPEG_QUALITY,80]) #message = base64.b64encode(buffer) server_socket.sendto(bytes(buffer),client_addr) frame = cv2.putText(frame,'FPS: '+str(fps),(10,40),cv2.FONT_HERSHEY_SIMPLEX,0.7,(0,0,255),2) cv2.imshow('TRANSMITTING VIDEO',frame) key = cv2.waitKey(1) & 0xFF if key == ord('q'): server_socket.close() break if cnt == frames_to_count: try: fps = round(frames_to_count/(time.time()-st)) st=time.time() cnt=0 except: pass cnt+=1
Unreal Engine C++ UDP接收代码
void Audp_module::BeginPlay() { Super::BeginPlay(); SocketSubsystem = nullptr; if (SocketSubsystem == nullptr) SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM); SendSize = 2 * 1024 * 1024; BufferSize = 2 * 1024 * 1024; FIPv4Address iPAdress; FIPv4Address::Parse("127.0.0.1",iPAdress); LocalEndpoint = FIPv4Endpoint(iPAdress, LocalPort); FIPv4Address::Parse(IP, RemoteAddress); RemoteEndpoint = FIPv4Endpoint(RemoteAddress, RemotePort); Socket = nullptr; if (SocketSubsystem != nullptr) { if (Socket == nullptr) { Socket = FUdpSocketBuilder(SocketDescription) .AsNonBlocking() .AsReusable() .BoundToEndpoint(LocalEndpoint) .WithReceiveBufferSize(SendSize) .WithReceiveBufferSize(BufferSize) .WithBroadcast(); } } } void Audp_module::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); SocketSubsystem->DestroySocket(Socket); Socket = nullptr; SocketSubsystem = nullptr; } void Audp_module::Tick(float DeltaTime) { Super::Tick(DeltaTime); FString t = "Hello"; sendMessage(t); // Send Message Test Listen(); // Listen for messages } void Audp_module::Listen() { TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr(); uint32 Size; if(Socket != nullptr) { while (Socket->HasPendingData(Size)) { uint8* Recv = new uint8[Size]; int32 BytesRead = 0; ReceivedData.SetNumUninitialized(FMath::Min(Size, 65507u)); Recv = ReceivedData.GetData(); Socket->RecvFrom(ReceivedData.GetData(), ReceivedData.Num(), BytesRead, *targetAddr); char ansiiData[1024]; memcpy(ansiiData, ReceivedData.GetData(), BytesRead); ansiiData[BytesRead] = 0; FString data = ANSI_TO_TCHAR(ansiiData); //FString data = FString::FromInt(BytesRead); const auto encoded = FBase64::Encode(*data); GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, encoded); UE_LOG(LogTemp, Verbose, TEXT("Received: %s"), *encoded); //FString data = ANSI_TO_TCHAR(ansiiData); GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, "Message by UDP: " + data); } } else { Socket = FUdpSocketBuilder(SocketDescription) .AsNonBlocking() .AsReusable() .BoundToEndpoint(LocalEndpoint) .WithReceiveBufferSize(SendSize) .WithReceiveBufferSize(BufferSize) .WithBroadcast(); } }
问题分析与解决方案
当前接收端逻辑存在核心错误:Python实际发送的是原始JPEG二进制数据(代码中已注释base64编码),但Unreal端错误地将二进制数据当作ANSI字符串处理,还额外执行了一次base64编码,完全偏离了正确流程。以下是修正步骤:
1. 统一传输格式(二选一)
选项A:保持原始JPEG二进制传输(用当前Python代码)
无需修改Python发送逻辑,直接传输bytes(buffer)即可。
选项B:改用base64编码传输
取消Python中注释的代码,发送base64字符串:
message = base64.b64encode(buffer) server_socket.sendto(message, client_addr)
2. 重构Unreal接收与转换逻辑
修正后的Listen函数
void Audp_module::Listen() { TSharedRef<FInternetAddr> targetAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr(); uint32 Size; if(Socket != nullptr) { while (Socket->HasPendingData(Size)) { ReceivedData.SetNumUninitialized(Size); int32 BytesRead = 0; // 接收原始二进制数据 Socket->RecvFrom(ReceivedData.GetData(), ReceivedData.Num(), BytesRead, *targetAddr); TArray<uint8> JpegData; // 根据传输格式选择处理方式 if (/* 使用base64传输 */ false) { FString Base64Str = FString(ANSI_TO_TCHAR(reinterpret_cast<const char*>(ReceivedData.GetData()))); FBase64::Decode(Base64Str, JpegData); } else { // 直接使用原始JPEG二进制数据 JpegData = ReceivedData; } // 转换为Texture2D ConvertJpegToTexture(JpegData); } } else { Socket = FUdpSocketBuilder(SocketDescription) .AsNonBlocking() .AsReusable() .BoundToEndpoint(LocalEndpoint) .WithReceiveBufferSize(SendSize) .WithReceiveBufferSize(BufferSize) .WithBroadcast(); } }
添加JPEG转Texture2D的工具函数
void Audp_module::ConvertJpegToTexture(const TArray<uint8>& JpegData) { if (JpegData.Num() == 0) return; // 加载JPEG为图像数据 TSharedPtr<FImage> Image = MakeShareable(new FImage()); if (!FImageUtils::LoadImageFromRawData(Image, JpegData.GetData(), JpegData.Num())) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Failed to load JPEG data")); return; } // 创建临时Texture2D UTexture2D* Texture = UTexture2D::CreateTransient(Image->SizeX, Image->SizeY, PF_B8G8R8A8); if (!Texture) return; // 复制图像数据到纹理内存 void* TextureData = Texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_WRITE); FMemory::Memcpy(TextureData, Image->GetData(), Image->GetDataSize()); Texture->PlatformData->Mips[0].BulkData.Unlock(); // 更新纹理资源 Texture->UpdateResource(); GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("Texture created successfully")); // 此处可将Texture赋值给材质、UI等 // Example: YourMaterialInstance->SetTextureParameterValue("InputTexture", Texture); }
3. 额外注意事项
- UDP单包最大传输限制为64KB左右,若图像过大需在Python端分片,Unreal端重组
- 确保Unreal的Socket接收缓冲区足够大,避免丢包
- 测试时先单帧传输验证流程,再扩展到视频流
内容的提问来源于stack exchange,提问作者potu1304
相关产品推荐
相关产品推荐

