AcademySoftwareFoundation / OpenImageIO

Reading, writing, and processing images in a wide variety of file formats, using a format-agnostic API, aimed at VFX applications.
https://openimageio.readthedocs.org
Apache License 2.0
1.95k stars 585 forks source link

[HELP] Converting a DDS normal map to jpeg #3525

Open yetigit opened 2 years ago

yetigit commented 2 years ago

How do I convert this kind of normal map here to jpeg using iconvert or oiiotool ? normaltext.zip

expected output: Wood_Normal

lgritz commented 2 years ago

What have you tried so far? Did something not work as expected?

yetigit commented 2 years ago

well tried to convert it with no options and got a grey output most of my attempts gave me grey's one time I tried with oiiotool --ch "R,G,B" and had a red and green output because the tool basically made a 0' blue channel since apparently the file didn't have one. problem with that last one is that 1. I don't get all the colors and 2. it loses the information of the blue channel ?

lgritz commented 2 years ago

There definitely appears to be some problems with OIIO's interpretation and conversion of that file. I'm not sure what's up with that yet. The input seems to be (according to OIIO) a 2-channel file, but maybe that's not correct.

yetigit commented 2 years ago

could manage to get my normal converted to png with the standalone nvidia tool https://developer.nvidia.com/nvidia-texture-tools-exporter it has some command line stuff in its install dir :

explorer_b2uWvNlFjT

they also have nvddsinfo.exe so I don't know if it's of any help but it sorta describes the dds texture

C:\Program Files\NVIDIA Corporation\NVIDIA Texture Tools>nvddsinfo.exe "C:\Users\baidh\Downloads\Bistro_v5_2\Textures\Wood_Normal.dds"
Flags: 0x000A1007
        DDSD_CAPS
        DDSD_PIXELFORMAT
        DDSD_WIDTH
        DDSD_HEIGHT
        DDSD_LINEARSIZE
        DDSD_MIPMAPCOUNT
Height: 2048
Width: 2048
Depth: 0
Linear size: 4194304
Mipmap count: 12
Pixel Format:
        Flags: 0x80000004
                DDPF_FOURCC
                DDPF_NORMAL
        FourCC: 'ATI2' (0x32495441)
        Swizzle: 'A2XY' (0x59583241)
        Red mask:   0x00000000
        Green mask: 0x00000000
        Blue mask:  0x00000000
        Alpha mask: 0x00000000
Caps:
        Caps 1: 0x00401008
                DDSCAPS_COMPLEX
                DDSCAPS_TEXTURE
                DDSCAPS_MIPMAP
        Caps 2: 0x00000000
        Caps 3: 0x00000000
        Caps 4: 0x00000000
Version:
        NVIDIA Texture Tools 2.0.8
aras-p commented 2 years ago

So your DDS file is a two-channel format ("ATI2" aka BC5), i.e. the file only contains X & Y components of the normal map, and whatever uses the texture is expected to compute the Z component from these using math.

It's a good question what should the conversion done by OIIO even do. JPEGs can't be two-channel as far as I know, so "something" should get written into the blue channel. But should it be the full "reconstructed z" value? How would OIIO even know that the texture is actually a normal map, and not just "some other" type of two-channel texture?

lgritz commented 2 years ago

JPEG (ok, to be pedantic, the JFIF file format, since "JPEG" itself refers to the compression method and the group that defined it) only supports 1 and 3 channel images.

Our JPEG writer, rather than be really brittle, tries to handle this JPEG restriction on number of channels as gracefully as possible. It seems obvious that if you try to write an RGBA or RGB+others image, the JPEG writer should just write the RGB portion and drop the channels it doesn't support. But what should it do with a 2-channel image? Currently, it drops the second and outputs the first as a grayscale JPEG. Perhaps what I was thinking at the time is that a 2-channel image is likely to be Gray+Alpha, and since JFIF files don't have any provision for alpha, drop it and only write the gray, analogous to what we do with the automatic RGBA -> RGB case. But in this case the channels mean something else entirely, and perhaps a better solution would be to write a 3-channel RGB? But I'm not 100% sure how to know that, unless we can assume that ATI2 DDS files are always meant to be 2-channel normal maps?

As a further aside, this is a texture map of some kind, and JPEG is a horrible format for texture maps -- first of all, because the JPEG compression will cause data loss and possible artifacts in the images, and second because JPEG/JFIF files don't support MIP mapping, so as far as OIIO is concerned, a JPEG is just an "image", and not a proper "texture."

aras-p commented 2 years ago

But what should it do with a 2-channel image? Currently, it drops the second and outputs the first as a grayscale JPEG. Perhaps what I was thinking at the time is that a 2-channel image is likely to be Gray+Alpha, and since JFIF files don't have any provision for alpha, drop it and only write the gray, analogous to what we do with the automatic RGBA -> RGB case. But in this case the channels mean something else entirely, and perhaps a better solution would be to write a 3-channel RGB? But I'm not 100% sure how to know that, unless we can assume that ATI2 DDS files are always meant to be 2-channel normal maps?

In general case you can't assume that ATI2/BC5 DDS files will contain normal maps -- they often contain anything that needs "two uncorrelated channels". That said, my experience is that they more often contain something else than Luminance+Alpha; in fact the dds loader plugin in OIIO indicates the channel list as "R, G" for them.

Perhaps when faced with a 2-channel image, the OIIO JPG writer should decide to "downgrade" it to 1 channel only if the channel list has "Y, Alpha". If it's something like "R, G" then it should pad to an all-zeroes blue image.

lgritz commented 2 years ago

I'm not sure it's a "fix" for this issue -- or what a "fix" even means considering there is a lot of ambiguity in what it means to convert this kind of DDS to JPEG -- but I have submitted the following two PRs inspired by this situation, that I suppose help in some small way:

I think that put together, these would make oiiotool in.dds -o out.jpg for this type of DDS file have more intuitive behavior. (There are still some reasons why this is a bad idea -- JPEGs don't support MIPmaps and thus aren't proper "textures" by our reckoning, and also you should expect the lossy compression, 8 bits only, and compulsory sRGB encoding of JPEG to be poor choices for a normal map.)

Let me know if you think these effectively should close this issue, or if you think there is something additional that OIIO could reasonably do for these files.

aras-p commented 2 years ago

I think both of these changes make a lot of sense.

And yes, taking a ATI2/BC5 normal map (which is already lossy compression) and converting that into a JPG (which introduces more, and different compression artifacts) is not something that makes a lot of sense. At least converting the input to some lossless format (PNG, TIF, TGA, whatever) would be better quality wise.

That said, I think the improvements above are useful in more cases.

yetigit commented 2 years ago

I would suggest have a flag like the nvidia texture tool indicating this is to be treated as a normal map more precisely an nvidia-normal-map , so maybe something like : --nvnormalmap

lgritz commented 2 years ago

@yetigit What does that mean? If the flag is supplied, what exactly should we do differently when converting the image from DDS to, say, jpeg?