Загрузка изображения с библиотекой stb_image, поскольку float выдает неверные значения - PullRequest
0 голосов
/ 16 января 2020

Когда я загружаю изображение как плавающее, используя STB_Image , значения кажутся отключенными. Я создал изображение, чтобы проверить его Test image. (Используемый здесь код RGB [127, 255, 32])
Когда я загружаю это изображение как unsigned char, используя stbi_load(), я получаю правильные значения. Но когда я загружаю его как float, используя stbi_loadf(), я получаю неправильные значения, которые на самом деле не имеют смысла для меня.

Это код, который я использовал для тестирования:

#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;
}

Тестовый вывод, который я получаю, таков:

[0,22, 1,00, 0,01, 1,00]
[0,50, 1,00, 0,13, 1,00]

Теоретически это должно вывести те же значения, нижнее значение - правильное, в обеих строках, но по какой-то причине это не так.
Теперь я могу, конечно, использовать значения без знака и написать себе функцию, которая преобразует все в правильные значения с плавающей точкой, но я чувствую, что должен быть способ сделать это, просто используя сам STB_Image.

1 Ответ

1 голос
/ 16 января 2020

Из комментариев заголовка stb_image.h:

// 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);

Это означает, что функция stbi_loadf() использует встроенную гамма-коррекцию для возврата значений с плавающей запятой.

Формула гамма-коррекции выглядит например:

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

Когда вы вводите [127, 255, 32] через формулу гамма-коррекции, вы получаете результат, который вы видите:

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

Вы можете установить гамма-коэффициент stb_image с помощью stbi_ldr_to_hdr_gamma(1.0f) как быстрое исправление.

Однако stb_image определяет stbi_loadf() специально для поддержки нелинейных кодированных изображений (HDR или гамма-коррекция). Если бы вы загружали фактическое изображение HDR с помощью этой функции, установка гаммы на 1.0 отрицательно скажется на точности изображения при его рисовании; Поэтому лучше использовать stbi_loadf(), только если вам действительно нужно изображение, которое будет загружено с гамма-коррекцией.

Теперь я могу, конечно, использовать значения без знака и записать Я сама функция, которая преобразует все в правильные значения с плавающей точкой, но я чувствую, что должен быть способ сделать это, просто используя сам STB_Image.

Ручное преобразование данных 8-битного изображения в число с плавающей запятой, используя Vout = (float)Vin / 255f - это очевидное сообщение читателям вашего кода, что данные изображения не с гамма-коррекцией. Использование stbi_loadf() с stbi_ldr_to_hdr_gamma(1.0f) может скрыть эту важную деталь.

...