diff --git a/resources/maps/landmask.jpg b/resources/maps/landmask.jpg new file mode 100644 index 000000000..6a287127f Binary files /dev/null and b/resources/maps/landmask.jpg differ diff --git a/resources/scripted_compos/sst_landmask.lua b/resources/scripted_compos/sst_landmask.lua new file mode 100644 index 000000000..37885149d --- /dev/null +++ b/resources/scripted_compos/sst_landmask.lua @@ -0,0 +1,57 @@ +-- Generate a SST Product +-- with land blacked out +-- Should work with any calibrated +-- radiometer + +function init() + sat_proj = get_sat_proj() + img_landmask = image8.new() + img_landmask:load_jpeg(get_resource_path("maps/landmask.jpg")) + equ_proj = EquirectangularProj.new() + equ_proj:init(img_landmask:width(), img_landmask:height(), -180, 90, 180, -90) + cfg_maxval = lua_vars["maxval"] + cfg_minval = lua_vars["minval"] + return 3 +end + +function process() + pos = geodetic_coords_t.new() + + jetlut = image8_lut_jet() + -- get_calibrated_image(3, "temperature", 278.2, 292.0) + + for x = 0, rgb_output:width() - 1, 1 do + for y = 0, rgb_output:height() - 1, 1 do + get_channel_values(x, y) + + if not sat_proj:get_position(x, y, pos) then + x2, y2 = equ_proj:forward(pos.lon, pos.lat) + + mappos = y2 * img_landmask:width() + x2 + + mval = img_landmask:get(img_landmask:width() * img_landmask:height() + mappos) / 255.0 + + + if mval < 0.1 then + val = get_channel_value(1) + + lutpos = val * 255 + + if lutpos >= jetlut:width() then + lutpos = jetlut:width() - 1 + end + + if lutpos < 0 then + lutpos = 0 + end + + set_img_out(0, x, y, jetlut:get(jetlut:width() * 0 + lutpos) / 255.0) + set_img_out(1, x, y, jetlut:get(jetlut:width() * 1 + lutpos) / 255.0) + set_img_out(2, x, y, jetlut:get(jetlut:width() * 2 + lutpos) / 255.0) + end + end + end + + set_progress(x, rgb_output:width()) + end +end diff --git a/satdump_cfg.json b/satdump_cfg.json index 251e479d7..941dbb2d6 100644 --- a/satdump_cfg.json +++ b/satdump_cfg.json @@ -501,6 +501,22 @@ 1, 0 ] + }, + "Sea Surface Temperature": { + "channels": "cch4", + "lua": "scripted_compos/sst_landmask.lua", + "lua_vars": { + "minval": 0, + "maxval": 1 + }, + "calib_cfg": { + "cch4": { + "type": "temperature", + "min": 270.65, + "max": 313.15 + } + }, + "autogen": false } }, "project_channels": { diff --git a/src-core/common/image/composite.cpp b/src-core/common/image/composite.cpp index 81bb7f603..d373edc44 100644 --- a/src-core/common/image/composite.cpp +++ b/src-core/common/image/composite.cpp @@ -332,6 +332,22 @@ namespace image { return channelValues[x]; }; lua["get_channel_values"] = [channelValues, &inputChannels, &channelNumbers, &f](size_t x, size_t y) { get_channel_vals(channelValues, inputChannels, channelNumbers, f, y, x); }; + lua["get_calibrated_image"] = [img_pro](int ch, std::string type, float min, float max) + { + satdump::ImageProducts::calib_vtype_t ctype; + + if(type == "auto") + ctype = satdump::ImageProducts::CALIB_VTYPE_AUTO; + else if(type == "albedo") + ctype = satdump::ImageProducts::CALIB_VTYPE_ALBEDO; + else if(type == "radiance") + ctype = satdump::ImageProducts::CALIB_VTYPE_RADIANCE; + else if(type == "temperature") + ctype = satdump::ImageProducts::CALIB_VTYPE_TEMPERATURE; + + return img_pro->get_calibrated_image(ch, nullptr, ctype, {min, max}); }; + lua["get_calibrated_value"] = [img_pro](int ch, int x, int y) + { return img_pro->get_calibrated_value(ch, x, y); }; lua["set_progress"] = [&progress](float x, float y) { if(progress != nullptr) *progress = x / y; }; diff --git a/src-core/common/lua/lua_utils.cpp b/src-core/common/lua/lua_utils.cpp index 2aa6259b4..d93ae5611 100644 --- a/src-core/common/lua/lua_utils.cpp +++ b/src-core/common/lua/lua_utils.cpp @@ -99,6 +99,8 @@ namespace lua_utils { bindImageType(lua, "image8"); bindImageType(lua, "image16"); + + lua["image8_lut_jet"] = &image::LUT_jet; } void bindGeoTypes(sol::state &lua) diff --git a/src-core/products/image_products.cpp b/src-core/products/image_products.cpp index 0dc87e02e..9cf36de95 100644 --- a/src-core/products/image_products.cpp +++ b/src-core/products/image_products.cpp @@ -194,13 +194,15 @@ namespace satdump double wn = get_wavenumber(image_index); if (range.first == 0 && range.second == 0) + { range = get_calibration_default_radiance_range(image_index); - if (get_calibration_type(image_index) == CALIB_RADIANCE) - { - if (vtype == CALIB_VTYPE_TEMPERATURE) - range = {radiance_to_temperature(range.first, wn), - radiance_to_temperature(range.second, wn)}; + if (get_calibration_type(image_index) == CALIB_RADIANCE) + { + if (vtype == CALIB_VTYPE_TEMPERATURE) + range = {radiance_to_temperature(range.first, wn), + radiance_to_temperature(range.second, wn)}; + } } logger->trace("Generating calibrated image channel %d. Range %f %f. Type %d", image_index + 1, range.first, range.second, vtype); diff --git a/src-interface/viewer/image_handler.cpp b/src-interface/viewer/image_handler.cpp index 25de56d48..e2444f5c0 100644 --- a/src-interface/viewer/image_handler.cpp +++ b/src-interface/viewer/image_handler.cpp @@ -267,6 +267,7 @@ namespace satdump cfg.channels = rgb_compo_cfg.channels; cfg.lua = rgb_compo_cfg.lua; cfg.lua_vars = rgb_compo_cfg.lua_vars; + cfg.calib_cfg = rgb_compo_cfg.calib_cfg; equalize_image = rgb_compo_cfg.equalize; invert_image = rgb_compo_cfg.invert; @@ -394,22 +395,25 @@ namespace satdump ImGui::Begin("Display Range Control", &range_window, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize); ImGui::SetWindowSize(ImVec2(200 * ui_scale, 115 * ui_scale)); bool buff = false; - double tmp_min = is_temp && products->get_calibration_type(active_channel_id) ? radiance_to_temperature(disaplay_ranges[active_channel_id].first, products->get_wavenumber(active_channel_id)) : (disaplay_ranges[active_channel_id].first * (products->get_calibration_type(active_channel_id) ? 1 : 100)); - double tmp_max = is_temp && products->get_calibration_type(active_channel_id) ? radiance_to_temperature(disaplay_ranges[active_channel_id].second, products->get_wavenumber(active_channel_id)) : (disaplay_ranges[active_channel_id].second * (products->get_calibration_type(active_channel_id) ? 1 : 100)); + double tmp_min = (disaplay_ranges[active_channel_id].first * (products->get_calibration_type(active_channel_id) ? 1 : 100)); + double tmp_max = (disaplay_ranges[active_channel_id].second * (products->get_calibration_type(active_channel_id) ? 1 : 100)); ImGui::SetNextItemWidth(120 * ui_scale); buff |= ImGui::InputDouble("Minium", &tmp_min, 0, 0, is_temp && products->get_calibration_type(active_channel_id) ? "%.1f K" : (products->get_calibration_type(active_channel_id) ? "%.2f W·sr-1·m-2" : "%.2f%% Albedo"), ImGuiInputTextFlags_EnterReturnsTrue); ImGui::SetNextItemWidth(120 * ui_scale); buff |= ImGui::InputDouble("Maximum", &tmp_max, 0, 0, is_temp && products->get_calibration_type(active_channel_id) ? "%.1f K" : (products->get_calibration_type(active_channel_id) ? "%.2f W·sr-1·m-2" : "%.2f%% Albedo"), ImGuiInputTextFlags_EnterReturnsTrue); if (buff) { - disaplay_ranges[active_channel_id].first = is_temp && products->get_calibration_type(active_channel_id) ? temperature_to_radiance(tmp_min, products->get_wavenumber(active_channel_id)) : (tmp_min / (products->get_calibration_type(active_channel_id) ? 1 : 100)); - disaplay_ranges[active_channel_id].second = is_temp && products->get_calibration_type(active_channel_id) ? temperature_to_radiance(tmp_max, products->get_wavenumber(active_channel_id)) : (tmp_max / (products->get_calibration_type(active_channel_id) ? 1 : 100)); + disaplay_ranges[active_channel_id].first = (tmp_min / (products->get_calibration_type(active_channel_id) ? 1 : 100)); + disaplay_ranges[active_channel_id].second = (tmp_max / (products->get_calibration_type(active_channel_id) ? 1 : 100)); update_needed = true; asyncUpdate(); } if (ImGui::Button("Default")) { - disaplay_ranges[active_channel_id] = products->get_calibration_default_radiance_range(active_channel_id); + disaplay_ranges[active_channel_id] = + is_temp ? std::pair{radiance_to_temperature(products->get_calibration_default_radiance_range(active_channel_id).first, products->get_wavenumber(active_channel_id)), + radiance_to_temperature(products->get_calibration_default_radiance_range(active_channel_id).second, products->get_wavenumber(active_channel_id))} + : products->get_calibration_default_radiance_range(active_channel_id); update_needed = true; asyncUpdate(); } @@ -430,8 +434,6 @@ namespace satdump { ImGui::SetCursorPosY(y + i * 49 * ui_scale); std::pair actual_ranges = disaplay_ranges[active_channel_id]; - if (is_temp) - actual_ranges = {radiance_to_temperature(actual_ranges.first, products->get_wavenumber(active_channel_id)), radiance_to_temperature(actual_ranges.second, products->get_wavenumber(active_channel_id))}; ImGui::Text("%.3f", actual_ranges.second - (double)i * abs(actual_ranges.first - actual_ranges.second) / 9.0); } ImGui::EndGroup(); @@ -462,6 +464,12 @@ namespace satdump { if (ImGui::Selectable("Radiance", !is_temp)) { + if (is_temp) + { + disaplay_ranges[active_channel_id].first = temperature_to_radiance(disaplay_ranges[active_channel_id].first, products->get_wavenumber(active_channel_id)); + disaplay_ranges[active_channel_id].second = temperature_to_radiance(disaplay_ranges[active_channel_id].second, products->get_wavenumber(active_channel_id)); + } + asyncUpdate(); is_temp = false; } @@ -470,6 +478,12 @@ namespace satdump if (ImGui::Selectable("Temperature", is_temp)) { + if (!is_temp) + { + disaplay_ranges[active_channel_id].first = radiance_to_temperature(disaplay_ranges[active_channel_id].first, products->get_wavenumber(active_channel_id)); + disaplay_ranges[active_channel_id].second = radiance_to_temperature(disaplay_ranges[active_channel_id].second, products->get_wavenumber(active_channel_id)); + } + asyncUpdate(); is_temp = true; } @@ -538,7 +552,7 @@ namespace satdump #ifdef _MSC_VER if (default_path == ".") { - char* cwd; + char *cwd; cwd = _getcwd(NULL, 0); if (cwd != 0) default_path = cwd; @@ -551,11 +565,7 @@ namespace satdump products->instrument_name + "_" + (select_image_id == 0 ? "composite" : ("ch" + channel_numbers[select_image_id - 1])) + ".png"; #ifndef __ANDROID__ - auto result = pfd::save_file("Save Image", default_name, { - "PNG Files", "*.png", - "JPEG 2000 Files", "*.j2k", - "JPEG Files", "*.jpg *.jpeg" - }); + auto result = pfd::save_file("Save Image", default_name, {"PNG Files", "*.png", "JPEG 2000 Files", "*.j2k", "JPEG Files", "*.jpg *.jpeg"}); while (!result.ready(1000)) std::this_thread::sleep_for(std::chrono::milliseconds(1));