简体   繁体   中英

Loading image with stb_image library as float outputs wrong values

When I load an image as float using STB_Image the values seem to be off. I created an image to test it测试图片 . (The used RGB code here is [127, 255, 32])
When I load this image as unsigned char using stbi_load() I get the correct values. But when I load it as float using stbi_loadf() I get wrong values which don't really make sense to me.

This is the code I used for testing:

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#include <sstream>
#include <iomanip>
#include <iostream>
#include <string>

struct ColorF {
  float r;
  float g;
  float b;
  float a;

  std::string toString() {
    std::stringstream stream;

    stream << std::fixed << std::setprecision(2) << "[" << this->r << ", " << this->g << ", " << this->b << ", " << this->a << "]";

    return stream.str();
  }
};

struct ColorUC {
  unsigned char r;
  unsigned char g;
  unsigned char b;
  unsigned char a;

  std::string toString() {
    std::stringstream stream;

    stream << "[" << (float) this->r / 255.0f << ", " << (float) this->g / 255.0f << ", " << (float) this->b / 255.0f << ", " << (float) this->a / 255.0f << "]";

    return stream.str();
  }
};

int main() {
  int width, height, channels;
  float* image = stbi_loadf("test.png", &width, &height, &channels, STBI_rgb_alpha);

  // print content of first pixel of the image
  std::cout << ((ColorF*) image)->toString() << std::endl;

  unsigned char* jpeg = stbi_load("test.png", &width, &height, &channels, STBI_rgb_alpha);

  // print content of first pixel of the image
  std::cout << ((ColorUC*) jpeg)->toString() << std::endl;  

  stbi_image_free(image);
  stbi_image_free(jpeg);

  return 0;
}

The test output I get is this:

[0.22, 1.00, 0.01, 1.00]
[0.50, 1.00, 0.13, 1.00]

In theory this should print out the same values, the bottom one being the correct one, on both lines but for some reason it doesn't.
Now i could of course use the unsigned char values and write myself a function that converts everything to the proper float values but I feel like there should be a way to do this just using STB_Image itself.

From the stb_image.h header comments:

// If you load LDR images through this interface, those images will
// be promoted to floating point values, run through the inverse of
// constants corresponding to the above:
//
//     stbi_ldr_to_hdr_scale(1.0f);
//     stbi_ldr_to_hdr_gamma(2.2f);

That means the stbi_loadf() function uses built-in gamma correction to return its float values.

The gamma correction formula looks like this:

Vout = (Vin / 255)^γ; Where γ = 2.2

When you put [127, 255, 32] through the gamma correction formula, you get the result you are seeing:

R: 0.22 = (127 / 255)^2.2
G: 1.00 = (255 / 255)^2.2 
B: 0.01 = ( 32 / 255)^2.2

You can set the stb_image gamma factor with stbi_ldr_to_hdr_gamma(1.0f) as a quick fix.

However, stb_image defines the stbi_loadf() specifically to support non-linear encoded images (HDR or gamma corrected). If you were to load an actual HDR image with that function, setting the gamma to 1.0 would negatively impact the accuracy of the image when drawn; So it is best to only use stbi_loadf() if you actually need the image to be loaded with gamma correction.

Now i could of course use the unsigned char values and write myself a function that converts everying to the proper float values but I feel like there should be a way to do this just using STB_Image itself.

Manually converting the 8-bit image data to a float using Vout = (float)Vin / 255f is an obvious tell to readers of your code that the image data is not gamma corrected. Using stbi_loadf() with stbi_ldr_to_hdr_gamma(1.0f) can obscure this important detail.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM