satdump/src-core/handler/image/image_handler.cpp

376 lines
14 KiB
C++
Raw Normal View History

2025-01-01 16:16:02 +01:00
#include "image_handler.h"
#include "core/style.h"
2025-04-18 14:54:51 +02:00
#include "imgui/imgui.h"
2025-04-14 19:03:42 +01:00
#include "imgui/imgui_stdlib.h"
#include "logger.h"
2025-01-01 16:16:02 +01:00
2025-04-14 19:03:42 +01:00
#include "common/image/brightness_contrast.h"
#include "common/image/earth_curvature.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-02-03 07:48:52 +01:00
#include "../vector/shapefile_handler.h"
2025-04-17 23:21:41 +02:00
#include "common/utils.h"
2025-03-26 10:59:03 +01:00
#include "core/config.h"
2025-04-14 19:03:42 +01:00
#include "imgui/pfd/pfd_utils.h" // TODOREWORK
2025-03-21 10:18:36 +01:00
2025-03-24 21:55:25 +01:00
// TODOREWORK!
2025-04-06 13:17:30 +02:00
#include "handler/vector/shapefile_handler.h"
2025-03-24 21:55:25 +01:00
#include "resources.h"
#include <cstddef>
2025-03-24 21:55:25 +01:00
2025-01-01 16:16:02 +01:00
namespace satdump
{
namespace viewer
{
2025-04-14 19:03:42 +01:00
ImageHandler::ImageHandler() { handler_tree_icon = "\uf7e8"; }
2025-02-07 22:57:27 +01:00
ImageHandler::ImageHandler(image::Image img)
{
handler_tree_icon = "\uf7e8";
updateImage(img);
2025-01-01 16:16:02 +01:00
}
2025-04-06 10:27:54 +02:00
ImageHandler::ImageHandler(image::Image img, std::string name)
{
handler_tree_icon = "\uf7e8";
updateImage(img);
image_name = name;
}
2025-01-01 16:16:02 +01:00
ImageHandler::~ImageHandler()
{
2025-03-21 10:18:36 +01:00
if (file_save_thread.joinable())
file_save_thread.join();
2025-01-01 16:16:02 +01:00
}
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("Median Blur", &median_blur_img);
2025-03-22 16:03:14 +01:00
needs_to_update |= ImGui::Checkbox("Despeckle", &despeckle_img);
needs_to_update |= ImGui::Checkbox("Rotate 180", &rotate180_image);
2025-04-17 23:21:41 +02:00
needs_to_update |= ImGui::Checkbox("Geo Correct", &geocorrect_image); // TODOREWORK Disable if it can't be?
2025-01-02 17:04:32 +01:00
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);
2025-04-02 20:19:19 +01:00
needs_to_update |= ImGui::Checkbox("Invert", &invert_img);
2025-01-02 17:04:32 +01:00
2025-04-03 19:12:28 +02:00
needs_to_update |= ImGui::Checkbox("Brightness/Constrast", &brightness_contrast_image);
2025-03-22 17:41:03 +01:00
if (brightness_contrast_image)
{
2025-04-18 14:54:51 +02:00
ImGui::SliderFloat("Brightness", &brightness_contrast_brightness_image, -2, 2);
needs_to_update |= ImGui::IsItemDeactivatedAfterEdit();
ImGui::SliderFloat("Contrast", &brightness_contrast_constrast_image, -2, 2);
needs_to_update |= ImGui::IsItemDeactivatedAfterEdit();
2025-03-22 17:41:03 +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();
}
2025-03-09 10:16:40 +01:00
}
void ImageHandler::drawMenuBar()
{
2025-03-21 10:18:36 +01:00
bool needs_to_be_disabled = is_processing || file_save_thread_running;
if (needs_to_be_disabled)
style::beginDisabled();
2025-03-09 10:16:40 +01:00
if (ImGui::MenuItem("Save Image"))
{
2025-03-21 10:18:36 +01:00
auto fun = [this]()
{
file_save_thread_running = true;
// TODOREWORK!!!!
2025-03-26 10:59:03 +01:00
std::string save_type = "png";
config::tryAssignValueFromSatdumpGeneralConfig(save_type, "image_format");
2025-04-17 23:21:41 +02:00
std::string saved_at = save_image_dialog(getSaneName(), ".", "Save Image", &get_current_img(), &save_type);
2025-03-21 10:18:36 +01:00
if (saved_at == "")
logger->info("Save cancelled");
else
logger->info("Saved current image at %s", saved_at.c_str());
file_save_thread_running = false;
};
if (file_save_thread.joinable())
file_save_thread.join();
file_save_thread = std::thread(fun);
2025-03-09 10:16:40 +01:00
}
2025-03-21 10:18:36 +01:00
2025-03-24 21:55:25 +01:00
// TODOREWORK move out?!
if (ImGui::BeginMenu("Add Overlay"))
{
if (ImGui::MenuItem("Shores"))
2025-04-17 23:21:41 +02:00
addSubHandler(std::make_shared<ShapefileHandler>(resources::getResourcePath("maps/ne_10m_coastline.shp")));
2025-03-24 21:55:25 +01:00
if (ImGui::MenuItem("Borders"))
2025-04-17 23:21:41 +02:00
addSubHandler(std::make_shared<ShapefileHandler>(resources::getResourcePath("maps/ne_10m_admin_0_countries.shp")));
2025-03-24 21:55:25 +01:00
if (ImGui::MenuItem("Cities"))
2025-04-17 23:21:41 +02:00
logger->error("TODOREWORK GeoJSON!"); // TODOREWORK
// addSubHandler(std::make_shared<ShapefileHandler>(resources::getResourcePath("maps/ne_10m_coastline.shp")));
2025-03-24 21:55:25 +01:00
ImGui::EndMenu();
}
2025-03-21 10:18:36 +01:00
if (needs_to_be_disabled)
style::endDisabled();
2025-03-09 10:16:40 +01:00
}
2025-01-02 17:04:32 +01:00
2025-03-09 10:16:40 +01:00
void ImageHandler::drawContents(ImVec2 win_size)
{
ImGui::BeginChild("ContentChild", win_size, false, ImGuiWindowFlags_MenuBar);
2025-01-02 17:04:32 +01:00
if (imgview_needs_update)
{
image_view.update(get_current_img());
imgview_needs_update = false;
2025-01-03 16:26:39 +01:00
2025-01-27 21:35:27 +01:00
image_view.mouseCallback = [this](float x, float y)
2025-01-03 16:26:39 +01:00
{
2025-01-27 21:35:27 +01:00
if (correct_fwd_lut.size() > 0 && x > 0 && x < correct_fwd_lut.size())
x = correct_fwd_lut[x];
2025-01-03 16:26:39 +01:00
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());
}
2025-01-17 18:12:23 +01:00
if (image_proj_valid)
{
geodetic::geodetic_coords_t pos;
if (image_proj.inverse(x, y, pos))
{
ImGui::Text("Lat : Invalid!");
ImGui::Text("Lon : Invalid!");
}
else
{
ImGui::Text("Lat : %f", pos.lat);
ImGui::Text("Lon : %f", pos.lon);
}
}
2025-01-03 16:26:39 +01:00
ImGui::EndTooltip();
};
2025-01-02 17:04:32 +01:00
}
if (ImGui::BeginMenuBar())
{
image_view.zoom_in_next |= ImGui::MenuItem("\ueb81");
image_view.zoom_out_next |= ImGui::MenuItem("\ueb82");
ImGui::EndMenuBar();
}
image_view.draw(ImGui::GetContentRegionAvail());
ImGui::EndChild();
2025-01-01 16:16:02 +01:00
}
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);
2025-04-02 20:19:19 +01:00
invert_img = getValueOrDefault(p["invert"], false);
2025-01-02 21:30:41 +01:00
median_blur_img = getValueOrDefault(p["median_blur"], false);
2025-03-22 16:03:14 +01:00
despeckle_img = getValueOrDefault(p["despeckle"], false);
2025-03-25 23:36:21 +01:00
rotate180_image = getValueOrDefault(p["rotate180"], rotate180_image);
2025-03-22 16:03:14 +01:00
geocorrect_image = getValueOrDefault(p["geocorrect"], false);
2025-03-22 17:41:03 +01:00
brightness_contrast_image = getValueOrDefault(p["brightness_contrast"], false);
brightness_contrast_brightness_image = getValueOrDefault(p["brightness_contrast_brightness"], 0);
brightness_contrast_constrast_image = getValueOrDefault(p["brightness_contrast_constrast"], 0);
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;
2025-04-02 20:19:19 +01:00
p["invert"] = invert_img;
2025-01-02 19:07:13 +01:00
p["median_blur"] = median_blur_img;
2025-03-22 16:03:14 +01:00
p["despeckle"] = despeckle_img;
p["rotate180"] = rotate180_image;
p["geocorrect"] = geocorrect_image;
2025-03-22 17:41:03 +01:00
p["brightness_contrast"] = brightness_contrast_image;
p["brightness_contrast_brightness"] = brightness_contrast_brightness_image;
p["brightness_contrast_constrast"] = brightness_contrast_constrast_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
{
2025-01-17 18:12:23 +01:00
image::set_metadata(image, {});
2025-01-03 16:26:39 +01:00
image = img;
process();
}
2025-04-17 23:21:41 +02:00
std::string ImageHandler::getSaneName()
{
std::string img_name = image_name;
replaceAllStr(img_name, " ", "_");
replaceAllStr(img_name, "/", "_");
replaceAllStr(img_name, "\\", "_");
return img_name;
}
// TODOREWORK?
void ImageHandler::saveResult(std::string directory) { image::save_img(get_current_img(), directory + "/" + getSaneName()); }
2025-01-02 17:04:32 +01:00
void ImageHandler::do_process()
{
2025-04-17 23:21:41 +02:00
bool image_needs_processing = equalize_img | equalize_perchannel_img | white_balance_img | normalize_img | invert_img | median_blur_img | rotate180_image | geocorrect_image |
2025-04-14 19:03:42 +01:00
despeckle_img | brightness_contrast_image /*OVERLAY*/;
2025-01-27 21:35:27 +01:00
correct_fwd_lut.clear();
2025-02-03 07:48:52 +01:00
correct_rev_lut.clear();
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);
2025-04-02 20:19:19 +01:00
if (invert_img)
image::linear_invert(curr_image);
2025-01-02 17:04:32 +01:00
if (median_blur_img)
image::median_blur(curr_image);
2025-03-22 16:03:14 +01:00
if (despeckle_img)
image::kuwahara_filter(curr_image);
2025-03-22 17:41:03 +01:00
if (brightness_contrast_image)
2025-04-17 23:21:41 +02:00
image::brightness_contrast(curr_image, brightness_contrast_brightness_image, brightness_contrast_constrast_image);
2025-01-05 19:57:26 +01:00
if (rotate180_image)
curr_image.mirror(true, true);
2025-01-27 21:35:27 +01:00
if (geocorrect_image)
{ // TODOREWORK handle disabling projs, etc
bool success = false;
2025-04-17 23:21:41 +02:00
curr_image = image::earth_curvature::perform_geometric_correction(curr_image, success, &correct_rev_lut, &correct_fwd_lut);
2025-01-27 21:35:27 +01:00
if (!success)
2025-02-03 07:48:52 +01:00
{
2025-01-27 21:35:27 +01:00
correct_fwd_lut.clear();
2025-02-03 07:48:52 +01:00
correct_rev_lut.clear();
}
2025-01-27 21:35:27 +01:00
image::set_metadata_proj_cfg(curr_image, {});
}
2025-01-02 17:04:32 +01:00
}
else
curr_image.clear();
2025-02-03 07:48:52 +01:00
////////////////////////
subhandlers_mtx.lock();
2025-02-07 23:55:33 +01:00
bool image_has_overlays = false;
2025-02-03 07:48:52 +01:00
2025-02-07 23:55:33 +01:00
for (auto &h : subhandlers)
if (h->getID() == "shapefile_handler")
image_has_overlays = true;
2025-02-03 07:48:52 +01:00
2025-02-07 23:55:33 +01:00
if (image_has_overlays)
2025-02-03 07:48:52 +01:00
{
2025-03-09 10:16:40 +01:00
if (curr_image.size() == 0)
{
curr_image = image;
logger->critical("COPYING IMAGE!\n"); // TODOREWORK
}
2025-02-07 23:55:33 +01:00
nlohmann::json cfg = image::get_metadata_proj_cfg(image);
cfg["width"] = curr_image.width();
cfg["height"] = curr_image.height();
2025-03-09 10:16:40 +01:00
std::unique_ptr<proj::Projection> p = std::make_unique<proj::Projection>();
*p = cfg;
p->init(1, 0);
2025-02-07 23:55:33 +01:00
double rotate180 = rotate180_image;
auto corr_lut = correct_rev_lut;
2025-04-17 23:21:41 +02:00
auto pfunc = [&p, rotate180, corr_lut](double lat, double lon, double h, double w) mutable -> std::pair<double, double>
2025-02-03 07:48:52 +01:00
{
2025-02-07 23:55:33 +01:00
double x, y;
2025-04-17 23:21:41 +02:00
if (p->forward(geodetic::geodetic_coords_t(lat, lon, 0, false), x, y) || x < 0 || x >= w || y < 0 || y >= h)
2025-02-07 23:55:33 +01:00
return {-1, -1};
else
2025-02-03 07:48:52 +01:00
{
2025-02-07 23:55:33 +01:00
if (corr_lut.size() > 0)
{
if (x < corr_lut.size())
x = corr_lut[x];
else
return {-1, -1};
}
if (rotate180)
return {w - 1 - x, h - 1 - y};
2025-02-03 07:48:52 +01:00
else
2025-02-07 23:55:33 +01:00
return {x, y};
2025-02-03 07:48:52 +01:00
}
2025-02-07 23:55:33 +01:00
};
2025-02-03 07:48:52 +01:00
2025-02-07 23:55:33 +01:00
for (auto &h : subhandlers)
2025-02-03 07:48:52 +01:00
{
2025-02-07 23:55:33 +01:00
if (h->getID() == "shapefile_handler")
{
ShapefileHandler *sh_h = (ShapefileHandler *)h.get();
logger->critical("Drawing OVERLAY!");
sh_h->draw_to_image(curr_image, pfunc);
}
2025-02-03 07:48:52 +01:00
}
}
subhandlers_mtx.unlock();
////////////////////////
// Update ImgView
2025-02-07 23:55:33 +01:00
has_second_image = image_needs_processing | image_has_overlays;
2025-01-02 17:04:32 +01:00
imgview_needs_update = true;
2025-01-03 16:26:39 +01:00
2025-01-17 18:12:23 +01:00
image_proj_valid = false;
if (image::has_metadata_proj_cfg(image))
2025-01-03 16:26:39 +01:00
{
2025-01-17 18:12:23 +01:00
image_proj = image::get_metadata_proj_cfg(image);
2025-02-14 11:43:14 +01:00
if (image_proj.init(0, 1))
image_proj_valid = true;
2025-01-03 16:26:39 +01:00
}
image_calib_valid = false;
2025-01-17 18:12:23 +01:00
if (image::has_metadata_calib_cfg(image))
2025-01-03 16:26:39 +01:00
{
2025-01-17 18:12:23 +01:00
image_calib = image::get_metadata_calib_cfg(image);
2025-01-03 16:26:39 +01:00
image_calib_valid = true;
}
2025-01-02 17:04:32 +01:00
}
2025-04-14 19:03:42 +01:00
} // namespace viewer
} // namespace satdump