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データがほしいんだけど無理なんスかね・・・・・・・・・・・・・(死)