add stb_image_resize2.h

implement some Display P3 color space conversion stuff.
This commit is contained in:
Syoyo Fujita
2024-02-26 07:52:50 +09:00
parent f0fe0e39d4
commit 3893a6567f
6 changed files with 10725 additions and 36 deletions

View File

@@ -175,6 +175,7 @@ include src/external/simple_match/include/simple_match/simple_match.hpp
include src/external/simple_match/include/simple_match/utility.hpp include src/external/simple_match/include/simple_match/utility.hpp
include src/external/stb_image.h include src/external/stb_image.h
include src/external/stb_image_resize.h include src/external/stb_image_resize.h
include src/external/stb_image_resize2.h
include src/external/stb_image_write.h include src/external/stb_image_write.h
include src/external/stb_truetype.h include src/external/stb_truetype.h
include src/external/string_id/LICENSE include src/external/string_id/LICENSE

View File

@@ -49,6 +49,46 @@ using PrimvarReader_float2Map =
std::map<std::string, std::pair<const tinyusdz::Shader *, std::map<std::string, std::pair<const tinyusdz::Shader *,
const tinyusdz::UsdPrimvarReader_float2 *>>; const tinyusdz::UsdPrimvarReader_float2 *>>;
tinygltf::Material to_gltf_material(const tinyusdz::tydra::RenderMaterial &mat) {
tinygltf::Material out;
out.pbrMetallicRoughness.roughnessFactor = mat.surfaceShader.roughness.value;
return out;
}
bool to_gltf(const tinyusdz::tydra::RenderScene &rscene, const std::string &gltf_filename)
{
tinygltf::Model model;
tinygltf::Scene scene;
std::vector<tinygltf::Mesh> meshes;
tinygltf::Primitive primitive;
tinygltf::Asset asset;
asset.version = "2.0";
asset.generator = "usd_to_gltf example in TinyUSDZ";
model.scenes.push_back(scene);
// model.bufferViews
// model.buffers
// model.nodes
//model.meshes = meshes;
model.asset = asset;
// model.materials
tinygltf::TinyGLTF ctx;
bool ret = ctx.WriteGltfSceneToFile(&model,
gltf_filename,
true, // embedImages
true, // embedBuffers
true, // pretty print
false); // write binary glTF
return ret;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
if (argc < 2) { if (argc < 2) {
std::cout << "Need USD file.\n" std::cout << "Need USD file.\n"
@@ -158,5 +198,10 @@ int main(int argc, char **argv) {
std::cout << DumpRenderScene(render_scene) << "\n"; std::cout << DumpRenderScene(render_scene) << "\n";
if (!to_gltf(render_scene, "output.gltf")) {
std::cerr << "Failed to save scene as glTF\n";
return EXIT_FAILURE;
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

10365
src/external/stb_image_resize2.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -15,13 +15,15 @@
#define STB_IMAGE_RESIZE_IMPLEMENTATION #define STB_IMAGE_RESIZE_IMPLEMENTATION
#endif #endif
#include "external/stb_image_resize.h" //#include "external/stb_image_resize.h"
#include "external/stb_image_resize2.h"
#ifdef __clang__ #ifdef __clang__
#pragma clang diagnostic pop #pragma clang diagnostic pop
#endif #endif
#include "image-util.hh" #include "image-util.hh"
#include "value-types.hh"
#if defined(TINYUSDZ_WITH_COLORIO) #if defined(TINYUSDZ_WITH_COLORIO)
#include "external/tiny-color-io.h" #include "external/tiny-color-io.h"
@@ -428,7 +430,7 @@ bool srgb_8bit_to_linear_f32(const std::vector<uint8_t> &in_img, size_t width,
bool srgb_f32_to_linear_f32(const std::vector<float> &in_img, size_t width, bool srgb_f32_to_linear_f32(const std::vector<float> &in_img, size_t width,
size_t height, size_t height,
size_t channels, size_t channel_stride, size_t channels, size_t channel_stride,
std::vector<float> *out_img, float scale_factor, float bias) { std::vector<float> *out_img, const float scale_factor, const float bias, const float alpha_scale_factor, const float alpha_bias) {
if ((width == 0) || if ((width == 0) ||
(height == 0) || (height == 0) ||
@@ -465,8 +467,7 @@ bool srgb_f32_to_linear_f32(const std::vector<float> &in_img, size_t width,
// Apply linear conversion. // Apply linear conversion.
for (size_t c = channels; c < channel_stride; c++) { for (size_t c = channels; c < channel_stride; c++) {
size_t idx = channel_stride * width * y + channel_stride * x + c; size_t idx = channel_stride * width * y + channel_stride * x + c;
// TODO: Do we need to apply scale_factor and bias to linear-value(e.g. alpha) channel also? float f = in_img[idx] * alpha_scale_factor + alpha_bias;
float f = in_img[idx] * scale_factor + bias;
(*out_img)[idx] = f; (*out_img)[idx] = f;
} }
} }
@@ -579,4 +580,215 @@ bool f32_to_u8_image(const std::vector<float> &in_img, size_t width,
return true; return true;
} }
bool linear_displayp3_to_linear_sRGB(const std::vector<float> &in_img, size_t width,
size_t height, size_t channels,
std::vector<float> *out_img) {
// http://endavid.com/index.php?entry=79
// https://tech.metail.com/introduction-colour-spaces-dci-p3/
if (!out_img) {
return false;
}
if (channels > 4) {
return false;
}
if ((channels != 3) && (channels != 4)) {
return false;
}
if (in_img.size() != (width * height * channels)) {
return false;
}
out_img->resize(in_img.size());
if (channels == 3) {
for (size_t y = 0; y < height; y++) {
for (size_t x = 0; x < width; x++) {
float r, g, b;
r = in_img[3 * (y * width + x) + 0];
g = in_img[3 * (y * width + x) + 1];
b = in_img[3 * (y * width + x) + 2];
float out_rgb[3];
out_rgb[0] = 1.2249f * r - 0.2247f * g;
out_rgb[1] = -0.0420f * r + 1.0419f * g;
out_rgb[2] = -0.0197f * r - 0.0786f * g + 1.0979f * b;
// clamp
out_rgb[0] = (out_rgb[0] < 0.0f) ? 0.0f : out_rgb[0];
out_rgb[1] = (out_rgb[1] < 0.0f) ? 0.0f : out_rgb[1];
out_rgb[2] = (out_rgb[2] < 0.0f) ? 0.0f : out_rgb[2];
(*out_img)[3 * (y * width + x) + 0] = out_rgb[0];
(*out_img)[3 * (y * width + x) + 1] = out_rgb[1];
(*out_img)[3 * (y * width + x) + 2] = out_rgb[2];
}
}
} else { // rgba
for (size_t y = 0; y < height; y++) {
for (size_t x = 0; x < width; x++) {
float r, g, b, a;
r = in_img[4 * (y * width + x) + 0];
g = in_img[4 * (y * width + x) + 1];
b = in_img[4 * (y * width + x) + 2];
a = in_img[4 * (y * width + x) + 3];
float out_rgb[3];
out_rgb[0] = 1.2249f * r - 0.2247f * g;
out_rgb[1] = -0.0420f * r + 1.0419f * g;
out_rgb[2] = -0.0197f * r - 0.0786f * g + 1.0979f * b;
// clamp
out_rgb[0] = (out_rgb[0] < 0.0f) ? 0.0f : out_rgb[0];
out_rgb[1] = (out_rgb[1] < 0.0f) ? 0.0f : out_rgb[1];
out_rgb[2] = (out_rgb[2] < 0.0f) ? 0.0f : out_rgb[2];
(*out_img)[4 * (y * width + x) + 0] = out_rgb[0];
(*out_img)[4 * (y * width + x) + 1] = out_rgb[1];
(*out_img)[4 * (y * width + x) + 2] = out_rgb[2];
(*out_img)[4 * (y * width + x) + 3] = a;
}
}
}
return true;
}
bool linear_sRGB_to_linear_displayp3(const std::vector<float> &in_img, size_t width,
size_t height, size_t channels,
std::vector<float> *out_img) {
// http://endavid.com/index.php?entry=79
// https://tech.metail.com/introduction-colour-spaces-dci-p3/
if (!out_img) {
return false;
}
if (channels > 4) {
return false;
}
if ((channels != 3) && (channels != 4)) {
return false;
}
if (in_img.size() != (width * height * channels)) {
return false;
}
out_img->resize(in_img.size());
if (channels == 3) {
for (size_t y = 0; y < height; y++) {
for (size_t x = 0; x < width; x++) {
float r, g, b;
r = in_img[3 * (y * width + x) + 0];
g = in_img[3 * (y * width + x) + 1];
b = in_img[3 * (y * width + x) + 2];
float out_rgb[3];
out_rgb[0] = 0.8225f * r + 0.1774f * g;
out_rgb[1] = 0.0332f * r + 0.9669f * g;
out_rgb[2] = 0.0171f * r + 0.0724f * g + 0.9108f * b;
// clamp for just in case.
out_rgb[0] = (out_rgb[0] < 0.0f) ? 0.0f : out_rgb[0];
out_rgb[1] = (out_rgb[1] < 0.0f) ? 0.0f : out_rgb[1];
out_rgb[2] = (out_rgb[2] < 0.0f) ? 0.0f : out_rgb[2];
(*out_img)[3 * (y * width + x) + 0] = out_rgb[0];
(*out_img)[3 * (y * width + x) + 1] = out_rgb[1];
(*out_img)[3 * (y * width + x) + 2] = out_rgb[2];
}
}
} else { // rgba
for (size_t y = 0; y < height; y++) {
for (size_t x = 0; x < width; x++) {
float r, g, b, a;
r = in_img[4 * (y * width + x) + 0];
g = in_img[4 * (y * width + x) + 1];
b = in_img[4 * (y * width + x) + 2];
a = in_img[4 * (y * width + x) + 3];
float out_rgb[3];
out_rgb[0] = 0.8225f * r + 0.1774f * g;
out_rgb[1] = 0.0332f * r + 0.9669f * g;
out_rgb[2] = 0.0171f * r + 0.0724f * g + 0.9108f * b;
// clamp for just in case.
out_rgb[0] = (out_rgb[0] < 0.0f) ? 0.0f : out_rgb[0];
out_rgb[1] = (out_rgb[1] < 0.0f) ? 0.0f : out_rgb[1];
out_rgb[2] = (out_rgb[2] < 0.0f) ? 0.0f : out_rgb[2];
(*out_img)[4 * (y * width + x) + 0] = out_rgb[0];
(*out_img)[4 * (y * width + x) + 1] = out_rgb[1];
(*out_img)[4 * (y * width + x) + 2] = out_rgb[2];
(*out_img)[4 * (y * width + x) + 3] = a;
}
}
}
return true;
}
bool displayp3_f16_to_linear_f32(const std::vector<value::half> &in_img, size_t width,
size_t height,
size_t channels, size_t channel_stride,
std::vector<float> *out_img, const float scale_factor, const float bias, const float alpha_scale_factor, const float alpha_bias) {
if ((width == 0) ||
(height == 0) ||
(channels == 0) ||
(out_img == nullptr)) {
return false;
}
if (channel_stride == 0) {
channel_stride = channels;
} else {
if (channel_stride < channels) {
return false;
}
}
size_t dest_size = size_t(width) * size_t(height) * channel_stride;
if (dest_size > in_img.size()) {
return false;
}
out_img->resize(dest_size);
// assume input is in [0.0, 1.0]
for (size_t y = 0; y < height; y++) {
for (size_t x = 0; x < width; x++) {
for (size_t c = 0; c < channels; c++) {
size_t idx = channel_stride * width * y + channel_stride * x + c;
float in_val = value::half_to_float(in_img[idx]);
float f = in_val * scale_factor + bias;
(*out_img)[idx] = SrgbTransform::srgbToLinear(f); // Display P3 use the same transfer function with sRGB
}
// remainder(usually alpha channel)
// Apply linear conversion.
for (size_t c = channels; c < channel_stride; c++) {
size_t idx = channel_stride * width * y + channel_stride * x + c;
float in_val = value::half_to_float(in_img[idx]);
float f = in_val * alpha_scale_factor + alpha_bias;
(*out_img)[idx] = f;
}
}
}
return true;
}
} // namespace tinyusdz } // namespace tinyusdz

View File

@@ -5,7 +5,7 @@
// Currently sRGB color space conversion feature is provided. // Currently sRGB color space conversion feature is provided.
// //
// TODO // TODO
// - [ ] Image resize // - [ ] Image resize using stb_image_resize2
// - [ ] OIIO 3D LUT support through tinycolorio // - [ ] OIIO 3D LUT support through tinycolorio
// //
#pragma once #pragma once
@@ -16,6 +16,11 @@
namespace tinyusdz { namespace tinyusdz {
// forward decl
namespace value {
struct half;
};
/// ///
/// [0, 255] => [0.0, 1.0] /// [0, 255] => [0.0, 1.0]
/// ///
@@ -52,7 +57,7 @@ bool linear_f32_to_srgb_8bit(const std::vector<float> &in_img, size_t width,
std::vector<uint8_t> *out_img); std::vector<uint8_t> *out_img);
/// ///
/// Convert 8bit image in sRGB to fp32 image in linear color space. /// Convert 8bit image in sRGB to fp32 image in linear sRGB color space.
/// ///
/// @param[in] in_image Input image in sRGB color space. Image size = /// @param[in] in_image Input image in sRGB color space. Image size =
/// [width_byte_stride, height, channel_stride] /// [width_byte_stride, height, channel_stride]
@@ -75,15 +80,16 @@ bool srgb_8bit_to_linear_8bit(const std::vector<uint8_t> &in_img, size_t width,
size_t height, size_t channels, size_t channel_stride, size_t height, size_t channels, size_t channel_stride,
std::vector<uint8_t> *out_img); std::vector<uint8_t> *out_img);
// Input texel value is transformed as: x' = in_img * scale_factor + bias // Input texel value is transformed as: x' = in_img * scale_factor + bias for RGB
// alpha' = in_img * alpha_scale_factor + alpha_bias for alpha channel.
bool srgb_f32_to_linear_f32(const std::vector<float> &in_img, size_t width, bool srgb_f32_to_linear_f32(const std::vector<float> &in_img, size_t width,
size_t height, size_t channels, size_t channel_stride, size_t height, size_t channels, size_t channel_stride,
std::vector<float> *out_img, float scale_factor = 1.0f, float bias = 0.0f); std::vector<float> *out_img, const float scale_factor = 1.0f, const float bias = 0.0f, const float alpha_scale_factor = 1.0f, const float alpha_bias = 0.0f);
/// ///
/// Convert 8bit image in Rec.709 to fp32 image in linear color space. /// Convert 8bit image in Rec.709 to fp32 image in linear color space.
/// ///
/// @param[in] in_image Input image in Rec.709 color space. Image size = /// @param[in] in_img Input image in Rec.709 color space. Image size =
/// [width_byte_stride, height, channel_stride] /// [width_byte_stride, height, channel_stride]
/// @param[in] width Width pixels /// @param[in] width Width pixels
/// @param[in] width_byte_stride Width byte stride. 0 = Use `width` * /// @param[in] width_byte_stride Width byte stride. 0 = Use `width` *
@@ -103,6 +109,90 @@ bool rec709_8bit_to_linear_f32(const std::vector<uint8_t> &in_img, size_t width,
size_t channels, size_t channel_stride, size_t channels, size_t channel_stride,
std::vector<float> *out_img); std::vector<float> *out_img);
///
/// Convert fp16 image in Display P3(P3-D65) to fp32 image in linear Display P3 color space.
///
/// The conversion is identical to sRGB -> linear sRGB, since Display P3 uses same gamma curve(transfer function) with sRGB.
///
/// Input value is scaled by x' = x * scale + bias.
///
/// @param[in] in_img Input image in Display P3 color space. Image size =
/// [width_byte_stride, height, channel_stride]
/// @param[in] width Width pixels
/// @param[in] height Height pixels
/// @param[in] chanels Pixel channels to apply conversion. must be less than or
/// equal to `channel_stride`
/// @param[in] chanel_stride channel stride. For example, channels=3 and
/// channel_stride=4 to apply inverse gamma correction to RGB channel but apply
/// linear conversion to alpha channel for RGBA image.
/// @param[out] out_img Image in linear Display P3 color space. Image size is same with
/// `in_image`
/// @param[in] scale texel scale factor(RGB)
/// @param[in] bias texel bias(RGB)
/// @param[in] alpha_scale texel scale factor(alpha)
/// @param[in] alpha_bias texel bias(alpha)
///
/// @return true upon success. false when any parameter is invalid.
bool displayp3_f16_to_linear_f32(const std::vector<value::half> &in_img, size_t width,
size_t height, size_t channels, size_t channel_stride,
std::vector<float> *out_img, const float scale_factor = 1.0f, const float bias = 0.0f, const float alpha_scale_factor = 1.0f, const float alpha_bias = 0.0f);
///
/// Convert fp32 image in linear Display P3 color space to 10bit Display P3(10 bit for RGB, 2 bit for alpha, 32bit in total)
/// Apply gamma curve + quantize values.
///
/// Input image must be Mono, LA(Luminance + Alpha), RGB or RGBA.
///
/// Monochrome image is converted to RGB
///
/// When alpha channel is supplied, alpha value in [0.0, 1.0] is quantized to 2bit(~0.25 = 0, ~0.5 = 1, ~0.75 = 2, 0.75+ = 3)
///
/// @param[in] in_img Input image in linear Display P3 color space.
/// @param[in] width Width pixels
/// @param[in] height Height pixels
/// @param[in] channels 1 = mono, 2 = luminance(mono) + alpha, 3 = RGB, 4 = RGBA
/// @param[out] out_img Image in linear Display P3 color space. Image size is same with
/// `in_image`
bool linear_f32_to_displayp3_u10(const std::vector<float> &in_img, size_t width,
size_t height, size_t channels,
std::vector<uint32_t> *out_img);
///
/// Convert linear Display P3 color space to linear sRGB color space.
///
/// Input image must be RGB or RGBA.
/// (TODO: Support Mono, Lumi/Alpha)
///
/// When alpha channel is supplied, no conversion applied to alpha value.
///
/// @param[in] in_img Input image in linear Display P3 color space.
/// @param[in] width Width pixels
/// @param[in] height Height pixels
/// @param[in] channels 3 = RGB, 4 = RGBA
/// @param[out] out_img Image in linear sRGB color space. Image size is same with
/// `in_image`
bool linear_displayp3_to_linear_sRGB(const std::vector<float> &in_img, size_t width,
size_t height, size_t channels,
std::vector<float> *out_img);
///
/// Convert linear sRGB color space to linear Display P3 color space.
///
/// Input image must be RGB or RGBA.
/// (TODO: Support Mono, Lumi/Alpha)
///
/// When alpha channel is supplied, no conversion applied to alpha value.
///
/// @param[in] in_img Input image in linear sRGB color space.
/// @param[in] width Width pixels
/// @param[in] height Height pixels
/// @param[in] channels 3 = RGB, 4 = RGBA
/// @param[out] out_img Image in linear Display P3 color space. Image size is same with
/// `in_image`
bool linear_sRGB_to_linear_displayp3(const std::vector<float> &in_img, size_t width,
size_t height, size_t channels,
std::vector<float> *out_img);
/// ///
/// Resize fp32 image in linear color space. /// Resize fp32 image in linear color space.
/// ///

View File

@@ -166,6 +166,7 @@ struct BufferData {
// TODO: Stride // TODO: Stride
}; };
#if 0 // not used atm
// glTF-like Attribute // glTF-like Attribute
struct Attribute { struct Attribute {
std::string path; // Path string in Stage std::string path; // Path string in Stage
@@ -173,6 +174,7 @@ struct Attribute {
int64_t buffer_id{-1}; // index to buffer_id int64_t buffer_id{-1}; // index to buffer_id
}; };
#endif
// Compound of ComponentType x component // Compound of ComponentType x component
enum class VertexAttributeFormat { enum class VertexAttributeFormat {
@@ -1064,7 +1066,7 @@ class RenderScene {
std::vector<RenderMesh> meshes; std::vector<RenderMesh> meshes;
std::vector<Animation> animations; std::vector<Animation> animations;
std::vector<BufferData> std::vector<BufferData>
buffers; // Various data storage(e.g. primvar texcoords) buffers; // Various data storage(e.g. texel/image data).
// int64_t default_root_node{-1}; // index to `nodes`. `defaultPrim` in USD // int64_t default_root_node{-1}; // index to `nodes`. `defaultPrim` in USD
}; };
@@ -1462,26 +1464,6 @@ class RenderSceneConverter {
RenderSceneConverter(const RenderSceneConverter &rhs) = delete; RenderSceneConverter(const RenderSceneConverter &rhs) = delete;
RenderSceneConverter(RenderSceneConverter &&rhs) = delete; RenderSceneConverter(RenderSceneConverter &&rhs) = delete;
//void set_scene_config(const RenderSceneConverterConfig &config) {
// _scene_config = config;
//}
//void set_mesh_config(const MeshConverterConfig &config) {
// _mesh_config = config;
//}
//void set_material_config(const MaterialConverterConfig &config) {
// _material_config = config;
//}
//void set_asset_resoluition_resolver(AssetResolutionResolver &&rhs) {
// _asset_resolver = std::move(rhs);
//}
//void set_search_paths(const std::vector<std::string> &paths) {
// _asset_resolver.set_search_paths(paths);
//}
/// ///
/// All-in-one Stage to RenderScene conversion. /// All-in-one Stage to RenderScene conversion.
/// ///
@@ -1651,12 +1633,6 @@ class RenderSceneConverter {
bool GetSkeletonImpl(const tinyusdz::Prim &prim, bool GetSkeletonImpl(const tinyusdz::Prim &prim,
const tinyusdz::Skeleton *&out_skeleton); const tinyusdz::Skeleton *&out_skeleton);
//AssetResolutionResolver _asset_resolver;
//RenderSceneConverterConfig _scene_config;
//MeshConverterConfig _mesh_config;
//MaterialConverterConfig _material_config;
// const Stage *_stage{nullptr};
void PushInfo(const std::string &msg) { _info += msg; } void PushInfo(const std::string &msg) { _info += msg; }
void PushWarn(const std::string &msg) { _warn += msg; } void PushWarn(const std::string &msg) { _warn += msg; }
void PushError(const std::string &msg) { _err += msg; } void PushError(const std::string &msg) { _err += msg; }