Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added experimental image viewer #1123

Merged
merged 13 commits into from
Dec 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 60 additions & 163 deletions apps/yimage/yimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
#include <yocto/yocto_image.h>
#include <yocto/yocto_math.h>
#if YOCTO_OPENGL == 1
#include <yocto_gui/yocto_imgui.h>
#include <yocto_gui/yocto_opengl.h>
#include <yocto_gui/yocto_imageviewer.h>
#endif
using namespace yocto;

Expand Down Expand Up @@ -102,7 +101,7 @@ image<vec4f> filter_bilateral(
return filtered;
}

bool make_image_preset(const string& type, image<vec4f>& img, string& error) {
bool make_image_preset(const string& type_, image<vec4f>& img, string& error) {
auto set_region = [](image<vec4f>& img, const image<vec4f>& region,
const vec2i& offset) {
for (auto j = 0; j < region.height(); j++) {
Expand All @@ -113,6 +112,8 @@ bool make_image_preset(const string& type, image<vec4f>& img, string& error) {
}
};

auto type = path_basename(type_);

auto size = vec2i{1024, 1024};
if (type.find("sky") != type.npos) size = {2048, 1024};
if (type.find("images2") != type.npos) size = {2048, 1024};
Expand Down Expand Up @@ -241,7 +242,12 @@ bool make_image_preset(const string& type, image<vec4b>& img, string& error) {
return true;
}

bool is_image_preset_hdr(const string& type) {
bool is_preset_filename(const string& filename) {
return path_extension(filename) == ".ypreset";
}

bool is_preset_hdr(const string& type_) {
auto type = path_basename(type_);
return type.find("sky") != string::npos && type.find("sun") != string::npos;
}

Expand All @@ -260,60 +266,53 @@ struct convert_params {

// convert images
bool convert_images(const convert_params& params, string& error) {
// determine image type
auto is_hdr_image = is_hdr_filename(params.image) ||
(path_extension(params.image) == ".ypreset" &&
is_image_preset_hdr(path_basename(params.image)));
auto is_hdr_output = is_hdr_filename(params.output);

// load
auto hdr = image<vec4f>{};
auto ldr = image<vec4b>{};
if (path_extension(params.image) == ".ypreset") {
if (!make_image_preset(path_basename(params.image), hdr, error))
return false;
if (!is_hdr_image) {
ldr = rgb_to_srgbb(hdr);
hdr = {};
if (is_preset_filename(params.image)) {
if (is_preset_hdr(params.image)) {
if (!make_image_preset(path_basename(params.image), hdr, error))
return false;
} else {
if (!make_image_preset(path_basename(params.image), ldr, error))
return false;
}
} else if (is_hdr_image) {
} else if (is_hdr_filename(params.image)) {
if (!load_image(params.image, hdr, error)) return false;
} else {
if (!load_image(params.image, ldr, error)) return false;
}

// resize if needed
if (params.width != 0 || params.height != 0) {
if (is_hdr_image) {
if (!hdr.empty()) {
hdr = resize_image(hdr, params.width, params.height);
} else {
ldr = resize_image(ldr, params.width, params.height);
}
}

// tonemap if needed
if (is_hdr_image != is_hdr_output) {
if (is_hdr_image) {
ldr = tonemap_imageb(hdr, params.exposure, params.filmic);
hdr = {};
} else {
hdr = srgb_to_rgb(ldr);
ldr = {};
}
is_hdr_image = !is_hdr_image;
if (!hdr.empty() && !is_hdr_filename(params.output)) {
ldr = tonemap_imageb(hdr, params.exposure, params.filmic);
hdr = {};
}
if (!ldr.empty() && is_hdr_filename(params.output)) {
hdr = srgb_to_rgb(ldr);
ldr = {};
}

// apply logo
if (params.logo) {
if (is_hdr_image) {
if (!hdr.empty()) {
hdr = add_logo(hdr);
} else {
ldr = add_logo(ldr);
}
}

// save
if (is_hdr_image) {
if (!hdr.empty()) {
if (!save_image(params.output, hdr, error)) return false;
} else {
if (!save_image(params.output, ldr, error)) return false;
Expand All @@ -325,9 +324,9 @@ bool convert_images(const convert_params& params, string& error) {

// convert params
struct view_params {
string image = "image.png";
string output = "out.png";
bool logo = false;
vector<string> images = {"image.png"};
string output = "out.png";
bool logo = false;
};

#ifndef YOCTO_OPENGL
Expand All @@ -339,143 +338,41 @@ bool view_images(const view_params& params, string& error) {

#else

struct view_state {
// original data
string filename = "image.png";
string outname = "out.png";

// image data
image<vec4f> source = {};

// diplay data
image<vec4f> display = {};
float exposure = 0;
bool filmic = false;
colorgrade_params params = {};
bool colorgrade = false;

// viewing properties
ogl_image* glimage = new ogl_image{};
ogl_image_params glparams = {};

~view_state() {
if (glimage) delete glimage;
}
};

// convert images
bool view_images(const view_params& params, string& error) {
// load
auto img = image<vec4f>{};
if (path_extension(params.image) == ".ypreset") {
if (!make_image_preset(path_basename(params.image), img, error))
return false;
} else {
if (!load_image(params.image, img, error)) return false;
}

auto update_display = [](view_state* app) {
if (app->display.imsize() != app->source.imsize())
app->display = app->source;
if (app->colorgrade) {
colorgrade_image_mt(app->display, app->source, true, app->params);
// open viewer
auto viewer_guard = make_imageview("yimage");
auto viewer = viewer_guard.get();

// set image
for (auto& filename : params.images) {
// load
auto hdr = image<vec4f>{};
auto ldr = image<vec4b>{};
if (is_preset_filename(filename)) {
if (is_preset_hdr(filename)) {
if (!make_image_preset(path_basename(filename), hdr, error))
return false;
} else {
if (!make_image_preset(path_basename(filename), ldr, error))
return false;
}
} else if (is_hdr_filename(filename)) {
if (!load_image(filename, hdr, error)) return false;
} else {
tonemap_image_mt(app->display, app->source, app->exposure, app->filmic);
if (!load_image(filename, ldr, error)) return false;
}
};

// preparing display
auto app_guard = std::make_unique<view_state>();
auto app = app_guard.get();
app->filename = params.image;
app->outname = params.output;
app->source = img;

// update display
update_display(app);

// callbacks
auto callbacks = gui_callbacks{};
callbacks.clear_cb = [app](gui_window* win, const gui_input& input) {
clear_image(app->glimage);
};
callbacks.draw_cb = [app](gui_window* win, const gui_input& input) {
app->glparams.window = input.window_size;
app->glparams.framebuffer = input.framebuffer_viewport;
if (!is_initialized(app->glimage)) {
init_image(app->glimage);
set_image(app->glimage, app->display, false, false);
}
std::tie(app->glparams.center, app->glparams.scale) = camera_imview(
app->glparams.center, app->glparams.scale, app->display.imsize(),
app->glparams.window, app->glparams.fit);
draw_image(app->glimage, app->glparams);
};
callbacks.widgets_cb = [app, &update_display](
gui_window* win, const gui_input& input) {
auto edited = 0;
if (begin_header(win, "tonemap")) {
edited += draw_slider(win, "exposure", app->exposure, -5, 5);
edited += draw_checkbox(win, "filmic", app->filmic);
end_header(win);
}
if (begin_header(win, "colorgrade")) {
auto& params = app->params;
edited += draw_checkbox(win, "apply colorgrade", app->colorgrade);
edited += draw_slider(win, "exposure", params.exposure, -5, 5);
edited += draw_coloredit(win, "tint", params.tint);
edited += draw_slider(win, "lincontrast", params.lincontrast, 0, 1);
edited += draw_slider(win, "logcontrast", params.logcontrast, 0, 1);
edited += draw_slider(win, "linsaturation", params.linsaturation, 0, 1);
edited += draw_checkbox(win, "filmic", params.filmic);
continue_line(win);
edited += draw_checkbox(win, "srgb", params.srgb);
edited += draw_slider(win, "contrast", params.contrast, 0, 1);
edited += draw_slider(win, "saturation", params.saturation, 0, 1);
edited += draw_slider(win, "shadows", params.shadows, 0, 1);
edited += draw_slider(win, "midtones", params.midtones, 0, 1);
edited += draw_slider(win, "highlights", params.highlights, 0, 1);
edited += draw_coloredit(win, "shadows color", params.shadows_color);
edited += draw_coloredit(win, "midtones color", params.midtones_color);
edited += draw_coloredit(
win, "highlights color", params.highlights_color);
end_header(win);
}
if (begin_header(win, "inspect")) {
draw_slider(win, "zoom", app->glparams.scale, 0.1, 10);
draw_checkbox(win, "fit", app->glparams.fit);
auto ij = image_coords(input.mouse_pos, app->glparams.center,
app->glparams.scale, app->source.imsize());
draw_dragger(win, "mouse", ij);
auto img_pixel = zero4f, display_pixel = zero4f;
if (ij.x >= 0 && ij.x < app->source.width() && ij.y >= 0 &&
ij.y < app->source.height()) {
img_pixel = app->source[{ij.x, ij.y}];
display_pixel = app->display[{ij.x, ij.y}];
}
draw_coloredit(win, "image", img_pixel);
draw_dragger(win, "display", display_pixel);
end_header(win);
}
if (edited) {
update_display(app);
if (!is_initialized(app->glimage)) init_image(app->glimage);
set_image(app->glimage, app->display, false, false);
}
};
callbacks.uiupdate_cb = [app](gui_window* win, const gui_input& input) {
// handle mouse
if (input.mouse_left && !input.widgets_active) {
app->glparams.center += input.mouse_pos - input.mouse_last;
}
if (input.mouse_right && !input.widgets_active) {
app->glparams.scale *= powf(
2, (input.mouse_pos.x - input.mouse_last.x) * 0.001f);
// push image to the viewer
if (!hdr.empty()) {
set_image(viewer, filename, hdr);
} else {
set_image(viewer, filename, ldr);
}
};
}

// run ui
run_ui({1280, 720}, "yimage", callbacks);
// run view
run_view(viewer);

// done
return true;
Expand Down Expand Up @@ -623,7 +520,7 @@ int main(int argc, const char* argv[]) {

auto cli_view = add_command(cli, "view", "View images");
add_optional(cli_view, "output", view.output, "Output image", "o");
add_positional(cli_view, "image", view.image, "Input image");
add_positional(cli_view, "images", view.images, "Input images");

auto cli_diff = add_command(cli, "diff", "Diff two images");
add_optional(cli_diff, "signal", diff.signal, "Signal a diff as error");
Expand Down
1 change: 0 additions & 1 deletion apps/yimageviews/yimageviews.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ int main(int argc, const char* argv[]) {
// prepare application
auto app_guard = std::make_unique<app_state>();
auto app = app_guard.get();
auto filenames = vector<string>{};

// command line options
auto cli = make_cli("yimgviews", "view images");
Expand Down
21 changes: 17 additions & 4 deletions libs/yocto/yocto_commonio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,20 @@ static bool parse_clivalue(
return false;
}

static bool parse_clivalue(
json_value& value, const vector<string>& args, const json_value& schema) {
auto type = schema.value("type", "string");
if (type == "array") {
value = json_array{};
for (auto& arg : args) {
if (!parse_clivalue(value.emplace_back(), arg, schema.at("items")))
return false;
}
return true;
}
return false;
}

static bool set_clivalues(
const json_value& js, cli_setter& value, string& error) {
auto cli_error = [&error](const string& message) {
Expand Down Expand Up @@ -1286,10 +1300,9 @@ static bool parse_cli(json_value& value, const json_value& schema_,
if (name.empty()) return cli_error("too many positional arguments");
auto& property = schema.at("properties").at(name);
if (property.value("type", "string") == "array") {
for (auto pos = idx; args.size(); pos++) {
if (!parse_clivalue(value[name].emplace_back(), args[pos], property))
return cli_error("bad value for " + name);
}
auto array_args = vector<string>(args.begin() + idx, args.end());
if (!parse_clivalue(value[name], array_args, property))
return cli_error("bad value for " + name);
idx = args.size();
} else if (property.value("type", "string") != "object") {
if (!parse_clivalue(value[name], args[idx], property))
Expand Down
8 changes: 6 additions & 2 deletions libs/yocto/yocto_commonio.h
Original file line number Diff line number Diff line change
Expand Up @@ -2770,17 +2770,21 @@ inline void add_positional(const cli_command& cmd, const string& name,
std::is_same_v<T, int64_t> || std::is_same_v<T, int32_t> ||
std::is_same_v<T, uint64_t> ||
std::is_same_v<T, uint32_t> || std::is_same_v<T, float> ||
std::is_same_v<T, double> || std::is_enum_v<T>,
std::is_same_v<T, double>,
"unsupported type");
auto& schema = get_clischema(cmd.cli.schema, cmd.path);
auto& setter = get_clisetter(cmd.cli.setter, cmd.path);
schema["cli_positional"].push_back(name);
if (req) schema["required"].push_back(name);
auto& property = schema["properties"][name];
property["title"] = name;
property["type"] = cli_gettype<T>();
property["type"] = "array";
property["description"] = usage;
property["default"] = to_json(value);
auto& item = property["items"];
item["title"] = name;
item["type"] = cli_gettype<T>();
item["description"] = usage;
for (auto& choice : choices) {
property["enum"].push_back(to_json(choice));
}
Expand Down
7 changes: 7 additions & 0 deletions libs/yocto/yocto_image.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,13 @@ void tonemap_image_mt(image<vec4f>& ldr, const image<vec4f>& hdr,
ldr[{i, j}] = tonemap(hdr[{i, j}], exposure, filmic, srgb);
});
}
void tonemap_image_mt(image<vec4b>& ldr, const image<vec4f>& hdr,
float exposure, bool filmic, bool srgb) {
parallel_for(hdr.width(), hdr.height(), [&](int i, int j) {
ldr[{i, j}] = float_to_byte(tonemap(hdr[{i, j}], exposure, filmic, srgb));
});
}

vec3f colorgrade(
const vec3f& rgb_, bool linear, const colorgrade_params& params) {
auto rgb = rgb_;
Expand Down
Loading