HDRテクスチャの受け渡しについて
UE4からOpenCVにテクスチャを渡す時、RenderTargetのテクスチャがHDR画像だとおかしなことになるみたいなことを以前書いたけど、今考えたら単にデータ型が違うだけで、HDR画像をそのまま処理できたら黒飛び問題とかがマシになる
RenderTargetのテクスチャ設定でHDRのチェックを入れると、DetailsタブのFormatがB8G8R8A8からFloatRGBAになることがわかる
Unreal Engine | ERawImageFormat::Type
UE4が扱う画像フォーマットの形式一覧はこれで、テクスチャ生成した時のログを見ると、
LogTexture:Display: Building textures: T_TRender512_3 (RGBA16F)
と出ているので、正確にはRGBA16Fで出力されている、普通の32bitfloatならそのままmatに渡せそうなのにめんどくさい
UTexture2Dに対してHasHDRSource()メソッドを使用するとHDRにチェック入ってるかどうかが返ってくるので、処理をわけれそう
今の実装は、
Texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_ONLY)
をFColorにキャストして、R,G,Bを見ているのだけれど、FColorのR,G,Bはuint8なので、このままだとRGBが丸められてしまう
Unreal Engine | FFloat16Color
Unreal Engine | FLinearColor
どうしようと思ったらちゃんと構造体が用意されているので、FFloat16Colorを使用してみる
FFloat16からfloatへの変換は
GetFloat()でいけそうなのでどんな感じか確認していく
FFloat16Color* Data = static_cast<FFloat16Color*>(Texture->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_ONLY));
これでRGBA16FのテクスチャデータがFFloat16Colorになる、各要素へのアクセスは
SrcData[i].B.GetFloat() SrcData[i].G.GetFloat() SrcData[i].R.GetFloat()
これで見れる、数字はuint8のものから1/255で返ってきているので、そのままMat(CV_32FC3)に代入すれば計算で使えるんじゃないでしょうか
Mat HDRTexture2Mat(UTexture2D* Src) {
// テクスチャサイズ取得
Size size(Src->PlatformData->Mips[0].SizeX, Src->PlatformData->Mips[0].SizeY);
// Mat作成
Mat img(size, CV_32FC3);
int ch = img.channels();
FFloat16Color* SrcData = static_cast<FFloat16Color*>(Src->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_ONLY));
for (int y = 0, i = 0; y < size.height; y++)
{
float* ptr = img.ptr<float>(y);
for (int x = 0; x < size.width; x++, i++)
{
ptr[x * ch + 0] = SrcData[i].B.GetFloat();
ptr[x * ch + 1] = SrcData[i].G.GetFloat();
ptr[x * ch + 2] = SrcData[i].R.GetFloat();
}
}
Src->PlatformData->Mips[0].BulkData.Unlock();
return img;
}
これで動いたけど、OpenCV側のデータアクセス方法が若干闇い、HDRじゃない方はキャストとloop中の処理をちょっと変える
Mat Texture2Mat(UTexture2D* Src) {
// テクスチャサイズ取得
Size size(Src->PlatformData->Mips[0].SizeX, Src->PlatformData->Mips[0].SizeY);
// Mat作成
Mat img(size, CV_8UC3);
int ch = img.channels();
FColor* SrcData = static_cast<FColor*>(Src->PlatformData->Mips[0].BulkData.Lock(LOCK_READ_ONLY));
for (int y = 0, i = 0; y < size.height; y++)
{
uchar* ptr = img.ptr<uchar>(y);
for (int x = 0; x < size.width; x++, i++)
{
ptr[x * ch + 0] = SrcData[i].B;
ptr[x * ch + 1] = SrcData[i].G;
ptr[x * ch + 2] = SrcData[i].R;
}
}
Src->PlatformData->Mips[0].BulkData.Unlock();
img.convertTo(img, CV_32FC3, 1.0f / 255);
return img;
}
で、ここまで作ってフィルターとかかけてもトーンジャンプとか酷いので、あれと思って直接データ値を比較したら、全くHDRのデータが来ていない
RenderTargetのCaptureSourceでHDRかポストプロセス付きのLDRかを指定するところがあって、ここもHDRに変更するとちゃんと0-1範囲外のデータも来るようになった
いや、ポストプロセス付きのHDRデータがほしいんだけど無理なんスかね・・・・・・・・・・・・・(死)