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