Bootstrap

UE5 DownloadImage加载jpg失败的解决方法

DownloadImage加载jpg失败的解决方法

现象

用UE自带的 DownloadImage 无法下载成功,从 failure 引脚出来。
接入一个由监控器自动保存起的图像,有些可以正常加载成功,有些无法加载成功。
经调查问题出现在,有些由监控摄像头保存起来的图片的格式不符合UE的格式要求。有可能是没有MipMap的设置。
所以无法通过 【FImageUtils::ImportBufferAsTexture2D】将下载下来的图片二进制数据转换成Texture2D;
也无法通过将 【FFileHelper::SaveArrayToFile(ImageData, SavePath)】保存好的图片,通过
【UTexture2D
Texture = FImageUtils::ImportFileAsTexture2D(ImagePath);】导入生成 UTexture2D

解决方案

自己写一个download的方法,下载到图片的二进制数据,然后通过一个开源库 【stb_image.h】将图片重写入一个Texture2D
,重写的过程可以设定所需的一些格式。

具体方法

写一个DownloadImage的http请求,然后再写一个请求回调的方法,在请求成功后将二进制数据进行读取并写入 UTexture2D 对象

引入第三方库
在【Source】文件夹中创建第三方库文件夹,然后放入下载的库文件。只需要下载这一个文件就够了
下载地址
在这里插入图片描述
配置build.cs
在build.cs里增加一个引用文件的配置
在这里插入图片描述
在一个actor或者 UBlueprintAsyncActionBase 的子类里编写调用方法



DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDownloadAsyncNodeResult, UTexture2D*, tex2D);
/**
 * 
 */
UCLASS()
class JGF_5_API UJGF_Http : public UBlueprintAsyncActionBase
{
	GENERATED_BODY()

	UPROPERTY(BlueprintAssignable)
	FDownloadAsyncNodeResult OnSuccess;

	UPROPERTY(BlueprintAssignable)
	FDownloadAsyncNodeResult OnFail;

	FTimerHandle TimerHandle;
	
	UFUNCTION(BlueprintCallable, meta = (WorldContext = "WorldContextObject"))
	static UJGF_Http* CallDownloadImage(UObject* WorldContextObject,  FString url,FString path="Data/DownloadImg/");
	
	uint16 checkCount=0;
	UTexture2D* Text2D;


	void DownloadImage(const FString& ImageURL, const FString& SavePath);
	void OnImageDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful, FString SavePath);

};


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


#include "UtilityComponents/Http/JGF_Http.h"

#include "HttpModule.h"
#include "ImageUtils.h"
#include "Interfaces/IHttpResponse.h"
#include "Kismet/GameplayStatics.h"
#include "ThirdParty/stb_image/stb_image.h"

UJGF_Http* UJGF_Http::CallDownloadImage(UObject* WorldContextObject, FString url, FString path)
{
	auto node=NewObject<UJGF_Http>();
	FString FileName = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir());
	FileName = FileName.Append(path);
	node ->DownloadImage(url,FileName);
	auto check=[node,WorldContextObject,url]()
	{
		if(node->Text2D!=nullptr)
		{
			node->OnSuccess.Broadcast(node->Text2D);
			WorldContextObject->GetWorld()->GetTimerManager().ClearTimer(node->TimerHandle);
		}else
		{
			node->checkCount++;
			if(node->checkCount>100)
			{
				node->OnFail.Broadcast(nullptr);
				WorldContextObject->GetWorld()->GetTimerManager().ClearTimer(node->TimerHandle);
			}
		}
	};
	WorldContextObject->GetWorld()->GetTimerManager().SetTimer(node->TimerHandle,FTimerDelegate::CreateLambda(check),0.1f,true);
	return  node;
}

void UJGF_Http::DownloadImage(const FString& ImageURL, const FString& SavePath)
{
	// 创建 HTTP 请求对象
	TSharedRef<IHttpRequest> HttpRequest = FHttpModule::Get().CreateRequest();
	HttpRequest->OnProcessRequestComplete().BindUObject(this, &UJGF_Http::OnImageDownloadComplete, SavePath);
    
	HttpRequest->SetURL(ImageURL);
	HttpRequest->SetVerb("GET");
	HttpRequest->SetHeader(TEXT("Content-Type"), TEXT("image/jpeg"));
	HttpRequest->ProcessRequest();
}

void UJGF_Http::OnImageDownloadComplete(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful,
	FString SavePath)
{
	if (bWasSuccessful)
	{
		// 获取图片数据
		const TArray<uint8>& ImageData = Response->GetContent();
		
		// 将数据转换为 `stb_image` 支持的格式
		int32 Width, Height, Channels;
		unsigned char* ImageData_1 = stbi_load_from_memory(ImageData.GetData(), ImageData.Num(), &Width, &Height, &Channels, 0);
		if (!ImageData_1)
		{
			return;
		}
		UTexture2D* Texture = UTexture2D::CreateTransient(Width, Height);
		if (Texture)
		{
			// 锁定 BulkData 以便修改
			FTexture2DMipMap& Mip = Texture->PlatformData->Mips[0];
			void* TextureData = Mip.BulkData.Lock(LOCK_READ_WRITE);

			// 根据图片通道数处理图像数据
			FColor* Pixels = new FColor[Width * Height];
			int32 PixelIndex = 0;

			for (int32 y = 0; y < Height; ++y)
			{
				for (int32 x = 0; x < Width; ++x)
				{
					int32 Index = (y * Width + x) * Channels;

					// 确保按照正确的通道顺序进行复制
					if (Channels == 4)  // RGBA
					{
						Pixels[PixelIndex++] = FColor(ImageData_1[Index + 0], ImageData_1[Index + 1], ImageData_1[Index + 2], ImageData_1[Index + 3]);
					}
					else if (Channels == 3)  // RGB, 默认 alpha = 255
					{
						Pixels[PixelIndex++] = FColor(ImageData_1[Index + 0], ImageData_1[Index + 1], ImageData_1[Index + 2], 255);
					}
				}
			}

			// 将像素数据复制到纹理中
			FMemory::Memcpy(TextureData, Pixels, Width * Height * sizeof(FColor));

			// 解锁 BulkData
			Mip.BulkData.Unlock();

			// 释放临时图像数据和像素数据
			stbi_image_free(ImageData_1);
			delete[] Pixels;

			// 更新纹理资源
			Texture->UpdateResource();
		}

		if(Texture)
			Text2D=Texture;


		
		//Text2D= FImageUtils::ImportBufferAsTexture2D(ImageData);
		// 保存到本地文件
		//FFileHelper::SaveArrayToFile(ImageData, *SavePath);
        
		// 调用处理和加载图片的函数
		//ProcessAndLoadImage(SavePath);
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("Image download failed"));
	}
}

代码说明
在这里插入图片描述

注 【SavePath】参数暂时无用,并没有保存到本地
在actor中调用【CallDownloadImage】进行下载
在进而调用了【DownloadImage】进行http下载文件
完成后回调到【OnImageDownloadComplete】,进行数据处理
处理过程中,直接将二进制图片数据通过stb_image库重新转化一遍成 char* ,写入 FColor 再在写入 UTexture2D 对象RGB

;