2025-01-01 16:16:02 +01:00
|
|
|
#include "image_handler.h"
|
|
|
|
|
|
|
|
|
|
#include "imgui/imgui_stdlib.h"
|
|
|
|
|
#include "core/style.h"
|
|
|
|
|
|
2025-01-02 17:04:32 +01:00
|
|
|
#include "common/image/io.h" // TODOREWORK
|
|
|
|
|
#include "common/image/processing.h"
|
|
|
|
|
|
2025-01-02 19:07:13 +01:00
|
|
|
#include "nlohmann/json_utils.h"
|
|
|
|
|
|
2025-01-06 22:03:38 +01:00
|
|
|
// TODOREWORK
|
|
|
|
|
#include "common/projection/reprojector.h"
|
2025-01-07 19:07:51 +01:00
|
|
|
#include "common/widgets/json_editor.h"
|
|
|
|
|
|
|
|
|
|
// TODOREWORK
|
2025-01-09 13:35:23 +01:00
|
|
|
#include "common/projection/projs2/proj_json.h"
|
2025-01-07 19:07:51 +01:00
|
|
|
#include "common/projection/warp/warp.h"
|
|
|
|
|
#include "common/projection/warp/warp_bkd.h"
|
2025-01-09 13:35:23 +01:00
|
|
|
#include "projection/raytrace/gcp_compute.h"
|
|
|
|
|
|
2025-01-07 19:07:51 +01:00
|
|
|
#include "common/projection/gcp_compute/gcp_compute.h"
|
2025-01-06 22:03:38 +01:00
|
|
|
|
2025-01-01 16:16:02 +01:00
|
|
|
namespace satdump
|
|
|
|
|
{
|
|
|
|
|
namespace viewer
|
|
|
|
|
{
|
|
|
|
|
ImageHandler::ImageHandler()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ImageHandler::~ImageHandler()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ImageHandler::drawMenu()
|
|
|
|
|
{
|
2025-01-02 17:04:32 +01:00
|
|
|
bool needs_to_be_disabled = is_processing;
|
|
|
|
|
|
2025-01-01 16:16:02 +01:00
|
|
|
if (ImGui::CollapsingHeader("Image"))
|
|
|
|
|
{
|
2025-01-02 17:04:32 +01:00
|
|
|
bool needs_to_update = false;
|
|
|
|
|
|
|
|
|
|
if (needs_to_be_disabled)
|
|
|
|
|
style::beginDisabled();
|
|
|
|
|
|
|
|
|
|
needs_to_update |= ImGui::Checkbox("Equalize", &equalize_img);
|
|
|
|
|
needs_to_update |= ImGui::Checkbox("Individual Equalize", &equalize_perchannel_img);
|
|
|
|
|
needs_to_update |= ImGui::Checkbox("White Balance", &white_balance_img);
|
|
|
|
|
needs_to_update |= ImGui::Checkbox("Normalize", &normalize_img);
|
|
|
|
|
needs_to_update |= ImGui::Checkbox("Median Blur", &median_blur_img);
|
2025-01-05 19:57:26 +01:00
|
|
|
needs_to_update |= ImGui::Checkbox("Rotate 180", &rotate180_image);
|
2025-01-02 17:04:32 +01:00
|
|
|
|
2025-01-07 19:07:51 +01:00
|
|
|
ImGui::Separator();
|
|
|
|
|
needs_to_update |= ImGui::Checkbox("Warp Image", &warp_image);
|
2025-01-09 13:35:23 +01:00
|
|
|
needs_to_update |= ImGui::Checkbox("Old GCP Math", &old_gcp_math);
|
2025-01-07 19:07:51 +01:00
|
|
|
|
2025-01-06 22:03:38 +01:00
|
|
|
if (image_overlay_valib)
|
2025-01-07 19:07:51 +01:00
|
|
|
{
|
|
|
|
|
ImGui::Separator();
|
2025-01-06 22:03:38 +01:00
|
|
|
needs_to_update |= overlay_handler.drawUI(); // TODOREWORK
|
2025-01-07 19:07:51 +01:00
|
|
|
needs_to_update |= ImGui::Checkbox("Rotate 180", &rotate180_image);
|
|
|
|
|
auto proj_json = image::get_metadata_proj_cfg(image);
|
|
|
|
|
widgets::JSONTreeEditor(proj_json, "ProjCfg");
|
|
|
|
|
image::set_metadata_proj_cfg(image, proj_json);
|
|
|
|
|
}
|
2025-01-06 22:03:38 +01:00
|
|
|
|
2025-01-02 17:04:32 +01:00
|
|
|
if (needs_to_be_disabled)
|
|
|
|
|
style::endDisabled();
|
|
|
|
|
|
2025-01-03 16:26:39 +01:00
|
|
|
if (image_calib_valid)
|
|
|
|
|
{
|
|
|
|
|
ImGui::Text("Calibration Unit %s", image_calib.unit.c_str());
|
|
|
|
|
ImGui::Text("Calibration Min %f", image_calib.min);
|
|
|
|
|
ImGui::Text("Calibration Max %f", image_calib.max);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-02 17:04:32 +01:00
|
|
|
if (needs_to_update)
|
|
|
|
|
asyncProcess();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (imgview_needs_update)
|
|
|
|
|
{
|
|
|
|
|
image_view.update(get_current_img());
|
|
|
|
|
imgview_needs_update = false;
|
2025-01-03 16:26:39 +01:00
|
|
|
|
|
|
|
|
image_view.mouseCallback = [this](int x, int y)
|
|
|
|
|
{
|
|
|
|
|
auto &img = get_current_img();
|
|
|
|
|
ImGui::BeginTooltip(); // TODOREWORK
|
|
|
|
|
additionalMouseCallback(x, y);
|
|
|
|
|
ImGui::Text("Raw : %d", img.get(0, x, y));
|
|
|
|
|
if (image_calib_valid && image.channels() == 1)
|
|
|
|
|
{
|
|
|
|
|
double val = image_calib.getVal(img.getf(0, x, y));
|
|
|
|
|
ImGui::Text("Unit : %f %s", val, image_calib.unit.c_str());
|
|
|
|
|
}
|
|
|
|
|
ImGui::EndTooltip();
|
|
|
|
|
};
|
2025-01-02 17:04:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ImageHandler::drawMenuBar()
|
|
|
|
|
{
|
|
|
|
|
if (ImGui::MenuItem("Save Image"))
|
|
|
|
|
{
|
|
|
|
|
logger->warn("Saving image to /tmp/out.png!");
|
|
|
|
|
image::save_img(get_current_img(), "/tmp/out.png");
|
2025-01-01 16:16:02 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ImageHandler::drawContents(ImVec2 win_size)
|
|
|
|
|
{
|
|
|
|
|
image_view.draw(win_size);
|
|
|
|
|
}
|
2025-01-02 17:04:32 +01:00
|
|
|
|
2025-01-02 19:07:13 +01:00
|
|
|
void ImageHandler::setConfig(nlohmann::json p)
|
|
|
|
|
{
|
2025-01-02 21:30:41 +01:00
|
|
|
equalize_img = getValueOrDefault(p["equalize"], false);
|
2025-01-02 22:37:59 +01:00
|
|
|
equalize_perchannel_img = getValueOrDefault(p["individual_equalize"], false);
|
2025-01-02 21:30:41 +01:00
|
|
|
white_balance_img = getValueOrDefault(p["white_balance"], false);
|
|
|
|
|
normalize_img = getValueOrDefault(p["normalize"], false);
|
|
|
|
|
median_blur_img = getValueOrDefault(p["median_blur"], false);
|
2025-01-05 19:57:26 +01:00
|
|
|
rotate180_image = getValueOrDefault(p["rotate180_image"], false);
|
2025-01-02 19:07:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nlohmann::json ImageHandler::getConfig()
|
|
|
|
|
{
|
|
|
|
|
nlohmann::json p;
|
|
|
|
|
p["equalize"] = equalize_img;
|
2025-01-02 22:37:59 +01:00
|
|
|
p["individual_equalize"] = equalize_perchannel_img;
|
2025-01-02 19:07:13 +01:00
|
|
|
p["white_balance"] = white_balance_img;
|
|
|
|
|
p["normalize"] = normalize_img;
|
|
|
|
|
p["median_blur"] = median_blur_img;
|
2025-01-05 19:57:26 +01:00
|
|
|
p["rotate180_image"] = rotate180_image;
|
2025-01-02 19:07:13 +01:00
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-03 16:26:39 +01:00
|
|
|
void ImageHandler::updateImage(image::Image &img) // TODOREWORK
|
|
|
|
|
{
|
|
|
|
|
image = img;
|
2025-01-07 19:07:51 +01:00
|
|
|
image_proj_valid = image_overlay_valib = image::has_metadata_proj_cfg(image); // TODOREWORK
|
2025-01-03 16:26:39 +01:00
|
|
|
process();
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-02 17:04:32 +01:00
|
|
|
void ImageHandler::do_process()
|
|
|
|
|
{
|
|
|
|
|
bool image_needs_processing = equalize_img |
|
|
|
|
|
equalize_perchannel_img |
|
|
|
|
|
white_balance_img |
|
|
|
|
|
normalize_img |
|
2025-01-05 19:57:26 +01:00
|
|
|
median_blur_img |
|
2025-01-06 22:03:38 +01:00
|
|
|
rotate180_image |
|
2025-01-07 19:07:51 +01:00
|
|
|
overlay_handler.enabled() |
|
|
|
|
|
warp_image;
|
2025-01-02 17:04:32 +01:00
|
|
|
|
|
|
|
|
if (image_needs_processing)
|
|
|
|
|
{
|
|
|
|
|
curr_image = image;
|
|
|
|
|
|
|
|
|
|
if (equalize_img)
|
|
|
|
|
image::equalize(curr_image, false);
|
|
|
|
|
if (equalize_perchannel_img)
|
|
|
|
|
image::equalize(curr_image, true);
|
|
|
|
|
if (white_balance_img)
|
|
|
|
|
image::white_balance(curr_image);
|
|
|
|
|
if (normalize_img)
|
|
|
|
|
image::normalize(curr_image);
|
|
|
|
|
if (median_blur_img)
|
|
|
|
|
image::median_blur(curr_image);
|
2025-01-05 19:57:26 +01:00
|
|
|
if (rotate180_image)
|
|
|
|
|
curr_image.mirror(true, true);
|
2025-01-07 19:07:51 +01:00
|
|
|
|
|
|
|
|
if (image_proj_valid && warp_image)
|
|
|
|
|
{
|
2025-01-15 17:43:58 +01:00
|
|
|
curr_image = curr_image.to16bits();
|
2025-01-07 19:07:51 +01:00
|
|
|
auto prj_cfg = image::get_metadata_proj_cfg(image);
|
|
|
|
|
warp::WarpOperation operation;
|
2025-01-09 13:35:23 +01:00
|
|
|
prj_cfg["width"] = image.width(); // TODOREWORK move to metadata already?
|
|
|
|
|
prj_cfg["height"] = image.height(); // TODOREWORK move to metadata already?
|
|
|
|
|
operation.ground_control_points = old_gcp_math ? satdump::gcp_compute::compute_gcps(prj_cfg) : satdump::proj::compute_gcps(prj_cfg); // img.width(), img.height());
|
2025-01-07 19:07:51 +01:00
|
|
|
operation.input_image = &curr_image;
|
|
|
|
|
operation.output_rgba = true;
|
|
|
|
|
// TODO : CHANGE!!!!!!
|
|
|
|
|
int l_width = prj_cfg.contains("f_width") ? prj_cfg["f_width"].get<int>() : std::max<int>(image.width(), 512) * 10;
|
|
|
|
|
operation.output_width = l_width;
|
|
|
|
|
operation.output_height = l_width / 2;
|
|
|
|
|
|
|
|
|
|
logger->trace("Warping size %dx%d", l_width, l_width / 2);
|
|
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
|
satdump::warp::WarpResult result = satdump::warp::performSmartWarp(operation);
|
|
|
|
|
#else
|
|
|
|
|
warp::ImageWarper wrapper;
|
|
|
|
|
wrapper.op = operation;
|
|
|
|
|
wrapper.update();
|
|
|
|
|
satdump::warp::WarpResult result = wrapper.warp();
|
|
|
|
|
#endif
|
|
|
|
|
|
2025-01-09 13:35:23 +01:00
|
|
|
auto src_proj = ::proj::projection_t();
|
|
|
|
|
src_proj.type = ::proj::ProjType_Equirectangular;
|
2025-01-07 19:07:51 +01:00
|
|
|
src_proj.proj_offset_x = result.top_left.lon;
|
|
|
|
|
src_proj.proj_offset_y = result.top_left.lat;
|
|
|
|
|
src_proj.proj_scalar_x = (result.bottom_right.lon - result.top_left.lon) / double(result.output_image.width());
|
|
|
|
|
src_proj.proj_scalar_y = (result.bottom_right.lat - result.top_left.lat) / double(result.output_image.height());
|
|
|
|
|
|
|
|
|
|
image::set_metadata_proj_cfg(result.output_image, src_proj);
|
|
|
|
|
curr_image = result.output_image;
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-06 22:03:38 +01:00
|
|
|
if (image_overlay_valib && overlay_handler.enabled())
|
|
|
|
|
{
|
2025-01-09 13:35:23 +01:00
|
|
|
// auto pfunc = reprojection::setupProjectionFunction(curr_image.width(), curr_image.height(), image::get_metadata_proj_cfg(curr_image), rotate180_image);
|
|
|
|
|
auto cfg = image::get_metadata_proj_cfg(curr_image);
|
|
|
|
|
cfg["width"] = curr_image.width();
|
|
|
|
|
cfg["height"] = curr_image.height();
|
|
|
|
|
proj::Projection p = cfg;
|
|
|
|
|
p.init(1, 0);
|
|
|
|
|
bool rotate = rotate180_image;
|
|
|
|
|
auto pfunc = [p, rotate](double lat, double lon, double h, double w) mutable -> std::pair<double, double>
|
|
|
|
|
{
|
|
|
|
|
double x, y;
|
|
|
|
|
if (p.forward(geodetic::geodetic_coords_t(lat, lon, 0, false), x, y) || x < 0 || x >= w || y < 0 || y >= h)
|
|
|
|
|
return {-1, -1};
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (rotate)
|
|
|
|
|
return {w - 1 - x, h - 1 - y};
|
|
|
|
|
else
|
|
|
|
|
return {x, y};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-01-07 19:07:51 +01:00
|
|
|
overlay_handler.clear_cache();
|
2025-01-06 22:03:38 +01:00
|
|
|
overlay_handler.apply(curr_image, pfunc);
|
|
|
|
|
}
|
2025-01-02 17:04:32 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
curr_image.clear();
|
|
|
|
|
|
|
|
|
|
has_second_image = image_needs_processing;
|
|
|
|
|
imgview_needs_update = true;
|
2025-01-03 16:26:39 +01:00
|
|
|
|
|
|
|
|
auto &im = get_current_img();
|
|
|
|
|
if (image::has_metadata_proj_cfg(im))
|
|
|
|
|
{
|
2025-01-07 19:07:51 +01:00
|
|
|
// logger->critical(image::get_metadata_proj_cfg(im).dump(4));
|
2025-01-03 16:26:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
image_calib_valid = false;
|
|
|
|
|
if (image::has_metadata_calib_cfg(im))
|
|
|
|
|
{
|
|
|
|
|
image_calib = image::get_metadata_calib_cfg(im);
|
|
|
|
|
image_calib_valid = true;
|
|
|
|
|
}
|
2025-01-02 17:04:32 +01:00
|
|
|
}
|
2025-01-01 16:16:02 +01:00
|
|
|
}
|
|
|
|
|
}
|