Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Texture2D.LoadImage() distorting colors

Discussion in 'Scripting' started by qqquantum, Nov 16, 2016.

  1. qqquantum

    qqquantum

    Joined:
    Sep 22, 2015
    Posts:
    6
    I need to load a PNG into a Texture2D and then read *precisely* the RGB values from each pixel.
    However Unity is distorting it big time. I am sure the original PNG does not have a color profile embedded. And it is read perfectly, with the correct RGB values, by all the image viewers I tried

    If I do
    Code (csharp):
    1.  
    2. Texture2D tex  = new Texture2D(128, 128, TextureFormat.RGB24, true);
    3. byte[] fileData;
    4. string filePath = @"c:\temp\testColor.png"
    5. if (File.Exists(filePath))
    6. {    
    7.        fileData = File.ReadAllBytes(filePath);      
    8.        tex.LoadImage(fileData)
    9. }
    10.  
    11. Color32[] pixels = tex.GetPixels32();
    12. Color32 testColor32 = pixels[16];
    13. Debug.Log(testColor32);
    14.  
    The RGB values printed are *not* the values present on the image. I created it myself using PngBitmapEncoder on Visual Studio (separate program) and am sure of the RGB values, which are well read by any image viewer (the test images have uniform color, so I can pick any pixel for test).

    This may have to do with Texture2D.LoadImage() compressing the image to DXT5. Is there a way to disable compression on load? Test image attached, its uniform RGB values are 0, 154, 10. Unity reads 0, 146, 7
    testColor.png
     
  2. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    LoadImage uses ARGB32 for PNG files by default. It will only do compression if you use DXT1 or DXT5 as the format. I'd start by omitting as much explicit info as possible and let Unity handle it.
    Code (csharp):
    1.  
    2. Texture2D tex = new Texture2D(1, 1);
    3. // LoadImage will default the format and resize the dimensions
    4. text.LoadImage(fileData);
    5.  
     
  3. qqquantum

    qqquantum

    Joined:
    Sep 22, 2015
    Posts:
    6
    I started by being very "omissive" and frankly there was no difference in the results whatsoever. I guess LoadImage() supersedes everything.
    I am transmitting many images per second through the network, so need to have compressed PNGs. I wish I could just let Unity handle it as you suggest, but it seems to mess up colors on load.
     
  4. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    So wait - the originals are compressed? It really just seems like you've got your formats mixed up.
     
  5. qqquantum

    qqquantum

    Joined:
    Sep 22, 2015
    Posts:
    6
    PNG supports lossless compression. Anyway it shouldn't matter. RGB values should be preserved. Unity can read (and display) the file but the values are a bit off.
     
  6. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,840
    Your Texture2D constructor is telling it to use mipmaps — I'd change that to false. Also, add one more parameter, which tells it to use the linear (true) or sRGB (false) color space. I suspect the color space is responsible for the slight change in color you're seeing.
     
  7. qqquantum

    qqquantum

    Joined:
    Sep 22, 2015
    Posts:
    6
    I tried all the combinations already. But you are right, I should not have posted this code with mipmaps on. Also tried right now to change the color space from sRGB to linear and back. No change. My guess is LoadImage() supersedes all these parameters and forces a color space.
     
    Tarrag likes this.
  8. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
  9. Imakhiil

    Imakhiil

    Joined:
    Oct 11, 2013
    Posts:
    96
    4 years later and this still seems to be an issue...
     
  10. Imakhiil

    Imakhiil

    Joined:
    Oct 11, 2013
    Posts:
    96
    So, to give some further findings.
    Loading a texture with loadimage results in graphics format with a value "88". Its unnamed (its not a part of the enum!).
    It happens regardless of textures original graphics format.
    Texture format, on the other hand is overwritten to ARGB32

    I presume this isn't intentional, since the texture seems to always end up compressed which renders LoadImage quite useless for a wide category of uses. The fact that "88" isn't even in the enum seems to point to a bug or an oversight too.
     
  11. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,452
    Well, if the image contains gamma information, Unity will use it to gamma correct the image. Since almost all png images usually have a gAMA chunk, pretty much all pngs would be affected in one way or the other. Though it depends on what gamma information has been stored. The PNG specification states:
    I guess it's not really a Unity issue since if gama correction information is present in an image, it should be used. When importing an image in Unity directly you actually have more options to control the import process compared to LoadImage.

    I've written a PNG chunk parser some time ago which can be used to remove, insert or modify chunks in a png file. Note that in the case of the linked question the problematic bit was the gamma information. However it's possible that other metadata could also cause issues. You could preprocess your image and remove unwanted chunks from the actual png file, or you could use it at runtime to strip out the unwanted chunks on the fly before passing the data to LoadImage
     
    JoeStrout likes this.
  12. Imakhiil

    Imakhiil

    Joined:
    Oct 11, 2013
    Posts:
    96
    I would argue that it is a Unity issue.
    If virtually all popular PNG file loaders (including ones from System.Drawing) as well as popular image editing programs (paint/paint.net/photoshop/gimp/aseprite) load the exact same RGB values and Unity doesn't, it should be either changed or documented in detail.

    Importing an image in Unity is all fine and dandy, except when you don't have the image at build time. Modifying it using external tools is also somewhat of a solution... Except when it's your users that are providing the image.

    I would like to see this behavior either modified or clarified as intended in the documentation.
    Especially the elusive "GraphicsFormat 88". I will most likely raise an issue about this topic once I find a day to make a project to present it.
     
  13. iileychen

    iileychen

    Joined:
    Oct 13, 2015
    Posts:
    96
    I just tested, it log RGBA(0, 154, 10, 255)
    Win10, Unity 2021.1.13f1c1, URP
     
  14. obbe

    obbe

    Joined:
    Apr 10, 2015
    Posts:
    7
    Still seems to be happening.
    I made a test image in Krita.

    When saving out as a png with 'Forced Convert to sRGB' ticked;
    After LoadImage the image comes across pixel perfect.

    When saving out with the 'Forced Convert to sRGB' UN-ticked;
    The colours are somewhat off.
    0,0,0 -> 0,0,0
    255,0,0 -> 255,0,0
    0,255,0 -> 0,255,0
    0,0,255 -> 0,0,255
    128,0,0 -> 116,0,0
    0,128,0 -> 0,128,0
    0,0,128 -> 0,0,122

    Red, Green and Blue are being treated differently.
    It looks like some sort of Gamma thing but the effect is far more subtle than sRGB / RGB.