StableDiffusion 연결 작업을 진행하던 중, Img2Img ( Image to Image) 변환 기능을 사용하는 노드를 제공하기 위해 UE5 에서 가장 자주 쓰이는 이미지 형식인 UTextur2D 형식 에셋에서 PNG 이미지를 추출하는 방법을 고민해봤다.
우선 이 작업을 진행하기 위해, 본래 PNG 는 어떤 형식을 가지고 있는지,
UE5 에서 PNG 형식 이미지를 어떻게 사용하는지 확인할 필요가 있었다.
PNG 이미지의 종류
PNG 24비트 형식과 PNG 32비트 형식의 차이는 다음과 같습니다:
PNG 24 비트 | PNG 32 비트 | |
---|---|---|
비트 단위 | 8 bits | 8 bits |
컬러 디프스 | RGB (24 bits) | RGB (24 bits) + 알파 (8 bits) |
압축 방식 | 무손실 압축 | 무손실 압축 |
이미지 용량 | 작음 | 큼 |
알파 채널 | 없음 | 포함 |
지원 소프트웨어 | 대부분 지원 | 대부분 지원 |
PNG 24비트 형식은 RGB 컬러 디프스만 가지고 있으며, 이미지 용량이 작고 대부분의 소프트웨어에서 지원됩니다. 하지만 알파 채널이 없기 때문에 투명도를 다룰 수 없습니다.
반면, PNG 32비트 형식은 RGB 컬러 디프스와 알파 채널을 가지고 있으며, 투명도를 다룰 수 있습니다. 하지만 이미지 용량이 더 크고, 일부 소프트웨어에서는 지원되지 않을 수 있습니다.
UE5에서의 PNG 사용
UE5에서 24비트 이미지는 RGB 채널만 포함하고 있기 때문에, FColor 형식을 사용해 Texture 를 무손실 데이터 저장을 하는 방식으로 통합해 사용합니다. 따라서 24 비트 PNG 이미지의 형식을 32비트 형식으로 변환할 때, 알파 채널은 0으로 채워집니다.
따라서, 24비트 PNG 이미지를 UTexture2D 객체로 로드하고 다시 저장하면, UE5에서는 내부적으로 32비트 형식으로 변환됩니다. 따라서, 동일한 24비트 PNG 이미지를 로드하고 저장해도, 저장된 파일은 32비트 PNG 형식이 됩니다.
UE5 UTexture2D 에서의 이미지 압축
UE5의 UTexture2D에서 기본 압축 형식은 데카르트 1차원 텍스처(DXT1) 압축이며, 이를 EPixelFormat::PF_DXT1로 나타냅니다. DXT1은 4:1 압축 비율을 갖으며, 압축이 적용된 텍스처 데이터를 해제할 때 원래의 색상 데이터가 손실되어 이미지 품질이 저하될 수 있습니다. 그러나 DXT1은 메모리를 효율적으로 사용할 수 있기 때문에 게임에서 널리 사용됩니다. DXT1을 사용하는 경우 압축률은 높지만 색상 제한 때문에 세밀한 색상 처리가 어려울 수 있습니다. 따라서 DXT1은 색상이 적은 이미지나 벽과 같은 단색 이미지에 적합합니다.
https://namu.wiki/w/S3 Texture Compression
따라서, 만약 기본 압축이 DXT1 으로 적용되어 있는 UTexture2D 이미지를 PNG 로 저장하기 위해서는 색상 손상이 발생할 수 있으며, Stable Diffusion 처럼 외부 플랫폼에 대한 이미지 전송 체계에서 사용하기에는 부적합합니다. 그러므로 게임 내에서 쓰는 이미지가 아닌 다른 목적의 이미지를 사용할 경우, 옵션을 다음과 같이 무손실 압축으로 수정하는 것이 적절합니다.
(혹은 다른 방법을 사용하여 이미지 압축 방식을 변경해 주어도 됩니다.)
압축 방식이 수정된 상태라면, UTexture2D 에서 다음과 같은 코드로 PNG 데이터로 추출이 가능합니다.
if (!Texture || !Texture->Resource || !Texture->Resource->TextureRHI)
{
UE_LOG(LogTemp, Warning, TEXT("Invalid texture"));
return FString();
}
// Only supporting uncompressed textures for now
if (Texture->GetPlatformData() == nullptr ||
Texture->GetPlatformData()->PixelFormat != PF_B8G8R8A8)
{
//. maybe Dxt1 to PNG
return FString();
}
FTexture2DMipMap& Mip = Texture->PlatformData->Mips[0];
const void* Data = Mip.BulkData.LockReadOnly();
int32 Width = Mip.SizeX;
int32 Height = Mip.SizeY;
TArray<FColor> PixelData;
PixelData.SetNumUninitialized(Width * Height);
FMemory::Memcpy(PixelData.GetData(), Data, Width * Height * sizeof(FColor));
Mip.BulkData.Unlock();
EPixelFormat TextureFormat = Texture->GetPixelFormat(0);
// Convert FColor data to RGBA8 data
TArray<uint8> RawData_PNG32;
RawData_PNG32.SetNumUninitialized(PixelData.Num() * sizeof(FColor));
uint8* DestPtr = RawData_PNG32.GetData();
const FColor* SrcPtr = PixelData.GetData();
for (int32 i = 0; i < PixelData.Num(); i++)
{
*DestPtr++ = (uint8)(SrcPtr->B);
*DestPtr++ = (uint8)(SrcPtr->G);
*DestPtr++ = (uint8)(SrcPtr->R);
*DestPtr++ = (uint8)(SrcPtr->A);
SrcPtr++;
}
// Create the image wrapper
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
// Set the raw data
ImageWrapper->SetRaw(RawData_PNG32.GetData(), RawData_PNG32.Num(), Width, Height, ERGBFormat::BGRA, 8);
// Encode the image
TArray64<uint8> CompressedData = ImageWrapper->GetCompressed();
FString FilePath = "D:\\TestImages\\Test.png";
FFileHelper::SaveArrayToFile(CompressedData, *FilePath);
*참고 :
이 과정을 거치면, 원본 데이터가 PNG24인 경우, PNG32로 저장됩니다. (FColor의 RGBA값 반영)
Extracting PNG Files from UE5's UTexture2D
While working on the StalbeDiffusion connection, I wondered how to extract PNG images from the UTexture2D format asset, which is the most commonly used image format in UE5, in order to provide nodes that use the Img2Img (Image to Image) conversion function.
First, I needed to know what format PNG originally has and how to use PNG format images in UE5.
Types of PNG Images
The difference between PNG 24-bit format and PNG 32-bit format is as follows:
PNG 24 | PNG 32 | |
---|---|---|
bit | 8 bits | 8 bits |
Color Diffs | RGB (24 bits) | RGB (24 bits) + A(8 bits) |
Compression | Lossless Compression | Lossless Compression |
Size | small | big |
Alpha channel | no using | using |
Software | almost support | almost support |
PNG 24-bit format only has RGB color depth, and is small in image size and supported by most software. However, it cannot handle transparency because it does not have an alpha channel.
On the other hand, PNG 32-bit format has both RGB color depth and an alpha channel, so it can handle transparency. However, its image size is larger, and it may not be supported by some software.
Using PNG in UE5
Since 24-bit images in UE5 only contain RGB channels, Texture is integrated using FColor format to store lossless data. Therefore, when converting the 24-bit PNG image format to a 32-bit format, the alpha channel is filled with 0.
Therefore, when loading and saving a 24-bit PNG image as a UTexture2D object, UE5 internally converts it to a 32-bit format. Therefore, even if the same 24-bit PNG image is loaded and saved, the saved file becomes a 32-bit PNG format.
Image Compression in UE5 UTexture2D
The default compression format for UTexture2D in UE5 is DXT1, which compresses textures in one dimension, and is represented by EPixelFormat::PF_DXT1. DXT1 has a compression ratio of 4:1, and when uncompressed texture data that has been compressed is released, the original color data may be lost, resulting in a decrease in image quality. However, DXT1 is widely used in games because it can efficiently use memory. Since DXT1 has color limitations, it may be difficult to handle subtle color processing. Therefore, DXT1 is suitable for colorless images or single-color images such as walls.
Therefore, if you want to save a UTexture2D image that applies default compression as a PNG, color distortion may occur, and it is not suitable for use in an image transmission system for external platforms such as Stable Diffusion outside the game. Therefore, when using images for purposes other than in the game, it is appropriate to modify the option to lossless compression as shown below.
If the compression method has been modified, PNG data can be extracted from UTexture2D using the following code:
if (!Texture || !Texture->Resource || !Texture->Resource->TextureRHI)
{
UE_LOG(LogTemp, Warning, TEXT("Invalid texture"));
return FString();
}
// Only supporting uncompressed textures for now
if (Texture->GetPlatformData() == nullptr ||
Texture->GetPlatformData()->PixelFormat != PF_B8G8R8A8)
{
//. maybe Dxt1 to PNG
return FString();
}
FTexture2DMipMap& Mip = Texture->PlatformData->Mips[0];
const void* Data = Mip.BulkData.LockReadOnly();
int32 Width = Mip.SizeX;
int32 Height = Mip.SizeY;
TArray<FColor> PixelData;
PixelData.SetNumUninitialized(Width * Height);
FMemory::Memcpy(PixelData.GetData(), Data, Width * Height * sizeof(FColor));
Mip.BulkData.Unlock();
EPixelFormat TextureFormat = Texture->GetPixelFormat(0);
// Convert FColor data to RGBA8 data
TArray<uint8> RawData_PNG32;
RawData_PNG32.SetNumUninitialized(PixelData.Num() * sizeof(FColor));
uint8* DestPtr = RawData_PNG32.GetData();
const FColor* SrcPtr = PixelData.GetData();
for (int32 i = 0; i < PixelData.Num(); i++)
{
*DestPtr++ = (uint8)(SrcPtr->B);
*DestPtr++ = (uint8)(SrcPtr->G);
*DestPtr++ = (uint8)(SrcPtr->R);
*DestPtr++ = (uint8)(SrcPtr->A);
SrcPtr++;
}
// Create the image wrapper
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
// Set the raw data
ImageWrapper->SetRaw(RawData_PNG32.GetData(), RawData_PNG32.Num(), Width, Height, ERGBFormat::BGRA, 8);
// Encode the image
TArray64<uint8> CompressedData = ImageWrapper->GetCompressed();
FString FilePath = "D:\\\\TestImages\\\\Test.png";
FFileHelper::SaveArrayToFile(CompressedData, *FilePath);
If this process is performed, the original data will be saved as PNG32 if the original data is PNG24. (RGBA value of FColor is reflected)
'UE5 개발' 카테고리의 다른 글
UE5 에서 Singleton 서브시스템 사용하기 (UE SubSystem) (0) | 2023.04.10 |
---|---|
Unreal의 Property Specifiers (0) | 2023.04.10 |
SDAPI_사용 영상 (0) | 2023.03.21 |
UE5에 StableDiffusion 연결 (0) | 2023.03.21 |
댓글