534 lines
24 KiB
Python
534 lines
24 KiB
Python
import threading
|
|
import time
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
|
|
import cv2
|
|
import numpy as np
|
|
import screeninfo # Import the screeninfo library
|
|
import queue # Import queue
|
|
import os
|
|
import logging
|
|
|
|
# Configure logging
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
|
# Constants
|
|
MFD_WIDTH = 484
|
|
MFD_HEIGHT = 484
|
|
SAR_WIDTH = 2048
|
|
SAR_HEIGHT = 2048
|
|
SAR_DATA_TYPE = np.uint16
|
|
INITIAL_SAR_WIDTH = 1024 # Initial SAR display width
|
|
INITIAL_SAR_HEIGHT = 1024 # Initial SAR display height
|
|
MFD_FPS = 25 # Target MFD FPS
|
|
TKINTER_MIN_WIDTH = 484
|
|
TKINTER_MIN_HEIGHT = 484
|
|
|
|
# --- SAR Georeferencing Information (Example) ---
|
|
SAR_CENTER_LAT = 40.7128 # Example latitude of the SAR image center
|
|
SAR_CENTER_LON = -74.0060 # Example longitude of the SAR image center
|
|
SAR_IMAGE_SIZE_KM = 50.0 # Example size of the SAR image (50km x 50km)
|
|
|
|
# --- Available Color Palettes ---
|
|
COLOR_PALETTES = ["GRAY", "AUTUMN", "BONE", "JET", "WINTER", "RAINBOW", "OCEAN", "SUMMER", "SPRING", "COOL", "HSV",
|
|
"HOT"]
|
|
|
|
# --- Image File Paths ---
|
|
MFD_IMAGE_PATH = "MFD_img8_000000.bmp" # Replace with your MFD image path
|
|
SAR_IMAGE_PATH = "SAR_geo_img16_000000.tif" # Replace with your SAR image path
|
|
|
|
|
|
class App:
|
|
def __init__(self, root):
|
|
self.root = root
|
|
self.root.title("Grifo E Control Panel") # Set application title
|
|
self.root.minsize(TKINTER_MIN_WIDTH, TKINTER_MIN_HEIGHT) # Set minimum size
|
|
|
|
# --- Queues for SAR Images and Mouse Coordinates ---
|
|
self.sar_queue = queue.Queue(maxsize=5)
|
|
self.mouse_queue = queue.Queue(maxsize=20)
|
|
|
|
# --- Get Screen Information ---
|
|
screen = screeninfo.get_monitors()[0] # Use the primary screen
|
|
screen_width = screen.width
|
|
screen_height = screen.height
|
|
|
|
# --- Window Placement (Initial values) ---
|
|
self.tkinter_x = 10
|
|
self.tkinter_y = MFD_HEIGHT + 20 # Place Tkinter window below MFD
|
|
self.root.geometry(f"+{self.tkinter_x}+{self.tkinter_y}")
|
|
|
|
self.mfd_x = self.tkinter_x
|
|
self.mfd_y = 10
|
|
|
|
self.sar_x = self.tkinter_x + TKINTER_MIN_WIDTH + 20 # Place SAR window to the right of Tkinter
|
|
self.sar_y = 10
|
|
|
|
# --- Initial SAR Contrast and Brightness ---
|
|
self.sar_contrast = 1.0
|
|
self.sar_brightness = 0
|
|
self.sar_palette = "GRAY"
|
|
|
|
# --- Status Bar ---
|
|
self.statusbar = ttk.Label(self.root, text="Ready | MFD FPS: 0 | SAR FPS: N/A", relief=tk.SUNKEN,
|
|
anchor=tk.W)
|
|
self.statusbar.pack(side=tk.BOTTOM, fill=tk.X)
|
|
|
|
# --- Control Frame ---
|
|
self.control_frame = ttk.Frame(self.root, padding=10)
|
|
self.control_frame.pack(side=tk.TOP, fill=tk.X)
|
|
|
|
# --- SAR Parameters Frame ---
|
|
self.sar_params_frame = ttk.Labelframe(self.control_frame, text="SAR Parameters", padding=10)
|
|
self.sar_params_frame.pack(side=tk.LEFT, padx=10, fill=tk.X)
|
|
|
|
# --- SAR Info Frame ---
|
|
self.sar_info_frame = ttk.Labelframe(self.control_frame, text="SAR Info", padding=10)
|
|
self.sar_info_frame.pack(side=tk.TOP, padx=10, fill=tk.X)
|
|
|
|
# --- Layout all the parameter widgets in a grid ---
|
|
row = 0
|
|
|
|
# --- Test Image Checkbox ---
|
|
self.test_image_var = tk.IntVar(value=0) # Initial value: Static image
|
|
self.test_image_check = ttk.Checkbutton(self.sar_params_frame, text="Test Image",
|
|
variable=self.test_image_var, command=self.update_image_mode)
|
|
self.test_image_check.grid(row=row, column=0, columnspan=2, sticky=tk.W, padx=5, pady=2)
|
|
row += 1
|
|
|
|
# --- SAR Size Control ---
|
|
self.sar_size_label = ttk.Label(self.sar_params_frame, text="SAR Size:")
|
|
self.sar_size_label.grid(row=row, column=0, sticky=tk.W, padx=5, pady=2)
|
|
|
|
self.sar_size_factors = ["1:1", "1:2", "1:3", "1:5", "1:10"]
|
|
self.sar_size_combo = ttk.Combobox(self.sar_params_frame, values=self.sar_size_factors, state="readonly",
|
|
width=5)
|
|
self.sar_size_combo.set("1:2") # Initial value (1/2 of the original size)
|
|
self.sar_size_combo.grid(row=row, column=1, sticky=tk.E, padx=5, pady=2)
|
|
self.sar_size_combo.bind("<<ComboboxSelected>>", self.update_sar_size)
|
|
row += 1
|
|
|
|
# --- Contrast Control ---
|
|
self.contrast_label = ttk.Label(self.sar_params_frame, text="Contrast:")
|
|
self.contrast_label.grid(row=row, column=0, sticky=tk.W, padx=5, pady=2)
|
|
self.contrast_scale = ttk.Scale(self.sar_params_frame, orient=tk.HORIZONTAL, length=200,
|
|
from_=0.1, to=3.0, command=self.update_contrast, value=1.0)
|
|
self.contrast_scale.grid(row=row, column=1, sticky=tk.E, padx=5, pady=2)
|
|
row += 1
|
|
|
|
# --- Brightness Control ---
|
|
self.brightness_label = ttk.Label(self.sar_params_frame, text="Brightness:")
|
|
self.brightness_label.grid(row=row, column=0, sticky=tk.W, padx=5, pady=2)
|
|
self.brightness_scale = ttk.Scale(self.sar_params_frame, orient=tk.HORIZONTAL, length=200,
|
|
from_=-100, to=100, command=self.update_brightness, value=0)
|
|
self.brightness_scale.grid(row=row, column=1, sticky=tk.E, padx=5, pady=2)
|
|
row += 1
|
|
|
|
# --- Color Palette Control ---
|
|
self.palette_label = ttk.Label(self.sar_params_frame, text="Palette:")
|
|
self.palette_label.grid(row=row, column=0, sticky=tk.W, padx=5, pady=2)
|
|
self.palette_combo = ttk.Combobox(self.sar_params_frame, values=COLOR_PALETTES, state="readonly", width=8)
|
|
self.palette_combo.set("GRAY") # Initial value
|
|
self.palette_combo.grid(row=row, column=1, sticky=tk.E, padx=5, pady=2)
|
|
self.palette_combo.bind("<<ComboboxSelected>>", self.update_sar_palette)
|
|
row += 1
|
|
|
|
# --- SAR Center Lat/Lon Label ---
|
|
self.sar_center_label = ttk.Label(self.sar_info_frame,
|
|
text=f"Center: Lat={SAR_CENTER_LAT:.4f}, Lon={SAR_CENTER_LON:.4f}")
|
|
self.sar_center_label.pack(side=tk.TOP, anchor=tk.W)
|
|
|
|
# --- Mouse Lat/Lon Label ---
|
|
self.mouse_latlon_label = ttk.Label(self.sar_info_frame, text="Mouse: Lat=N/A, Lon=N/A")
|
|
self.mouse_latlon_label.pack(side=tk.TOP, anchor=tk.W)
|
|
|
|
# --- Initial Image Data ---
|
|
self.mfd_image_data = self.load_image(MFD_IMAGE_PATH, expected_dtype=np.uint8)
|
|
self.sar_image_data = self.load_image(SAR_IMAGE_PATH, expected_dtype=SAR_DATA_TYPE)
|
|
self.current_sar = cv2.normalize(self.sar_image_data, None, 0, 255, cv2.NORM_MINMAX,
|
|
cv2.CV_8U) # Current SAR for display
|
|
self.adjusted_sar = self.current_sar.copy() # Create a copy for contrast/brightness adjustments
|
|
|
|
# --- Translation Offset (for simulation) ---
|
|
self.mfd_x_offset = 0
|
|
self.sar_x_offset = 0
|
|
|
|
# --- FPS Counter ---
|
|
self.mfd_frame_count = 0
|
|
self.mfd_start_time = time.time()
|
|
self.sar_update_time = time.time() # Time of last SAR update
|
|
self.sar_frame_count = 0
|
|
self.mfd_fps = 0.0 # Initial MFD FPS
|
|
self.sar_fps = 0.0 # Initial SAR FPS
|
|
self.last_mouse_update = time.time()
|
|
|
|
# --- Initial SAR Size ---
|
|
self.sar_display_width = INITIAL_SAR_WIDTH
|
|
self.sar_display_height = INITIAL_SAR_HEIGHT
|
|
self.resized_sar = cv2.resize(self.adjusted_sar, (self.sar_display_width, self.sar_display_height),
|
|
interpolation=cv2.INTER_AREA)
|
|
|
|
# --- Window Initialization Flags ---
|
|
self.mfd_window_initialized = False
|
|
self.sar_window_initialized = False
|
|
self.sar_mouse_callback_set = False # Flag to track if the callback has been set
|
|
|
|
# --- LUT Initialization ---
|
|
self.brightness_contrast_lut = None # Initialize LUT
|
|
self.update_brightness_contrast_lut() # Initial LUT calculation
|
|
|
|
# --- Test Image Buffers ---
|
|
self.test_mfd_image = np.zeros((MFD_HEIGHT, MFD_WIDTH), dtype=np.uint8)
|
|
self.test_sar_image = np.zeros((SAR_HEIGHT, SAR_WIDTH), dtype=SAR_DATA_TYPE)
|
|
|
|
# --- Image Loading Thread ---
|
|
self.image_loading_thread = threading.Thread(target=self.load_images_and_generate_test_data, daemon=True)
|
|
self.image_loading_thread.start()
|
|
|
|
# --- Start Image Display ---
|
|
self.update_mfd()
|
|
self.update_sar()
|
|
self.process_sar_queue()
|
|
self.process_mouse_queue()
|
|
|
|
def load_images_and_generate_test_data(self):
|
|
"""Loads images and generates test data in a separate thread."""
|
|
self.set_status("Loading images...")
|
|
self.mfd_image_data = self.load_image(MFD_IMAGE_PATH, expected_dtype=np.uint8)
|
|
self.sar_image_data = self.load_image(SAR_IMAGE_PATH, expected_dtype=SAR_DATA_TYPE)
|
|
self.generate_test_images() # Generate initial test images
|
|
self.set_initial_sar_image() # Set the initial SAR image
|
|
self.set_status("Ready") # Back to ready status
|
|
|
|
def set_initial_sar_image(self):
|
|
"""Sets the initial SAR image for display."""
|
|
self.current_sar = cv2.normalize(self.sar_image_data, None, 0, 255, cv2.NORM_MINMAX,
|
|
cv2.CV_8U) # Current SAR for display
|
|
self.adjusted_sar = self.current_sar.copy() # Create a copy for contrast/brightness adjustments
|
|
self.resized_sar = cv2.resize(self.adjusted_sar, (self.sar_display_width, self.sar_display_height),
|
|
interpolation=cv2.INTER_AREA)
|
|
|
|
def generate_test_images(self):
|
|
"""Generates random test images for MFD and SAR."""
|
|
self.test_mfd_image = np.random.randint(0, 256, size=(MFD_HEIGHT, MFD_WIDTH), dtype=np.uint8)
|
|
self.test_sar_image = np.random.randint(0, 65536, size=(SAR_HEIGHT, SAR_WIDTH), dtype=SAR_DATA_TYPE).astype(
|
|
SAR_DATA_TYPE)
|
|
self.resized_sar = cv2.resize(cv2.normalize(self.test_sar_image, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U), (self.sar_display_width, self.sar_display_height),
|
|
interpolation=cv2.INTER_AREA)
|
|
|
|
def load_image(self, path, expected_dtype):
|
|
"""Loads an image from the given path and ensures the correct data type."""
|
|
if not os.path.exists(path):
|
|
print(f"Error: Image file not found at {path}")
|
|
logging.error(f"Image file not found at {path}")
|
|
# Create a placeholder image instead
|
|
if expected_dtype == np.uint8:
|
|
return np.zeros((MFD_HEIGHT, MFD_WIDTH), dtype=np.uint8)
|
|
else:
|
|
return np.zeros((SAR_HEIGHT, SAR_WIDTH), dtype=SAR_DATA_TYPE)
|
|
|
|
img = cv2.imread(path, cv2.IMREAD_ANYDEPTH) # Load as is
|
|
|
|
if img is None:
|
|
print(f"Error: Could not load image at {path}")
|
|
logging.error(f"Could not load image at {path}")
|
|
# Create a placeholder image instead
|
|
if expected_dtype == np.uint8:
|
|
return np.zeros((MFD_HEIGHT, MFD_WIDTH), dtype=np.uint8)
|
|
else:
|
|
return np.zeros((SAR_HEIGHT, SAR_WIDTH), dtype=SAR_DATA_TYPE)
|
|
|
|
if img.dtype != expected_dtype:
|
|
print(f"Warning: Converting image at {path} from {img.dtype} to {expected_dtype}")
|
|
logging.warning(f"Converting image at {path} from {img.dtype} to {expected_dtype}")
|
|
img = img.astype(expected_dtype)
|
|
return img
|
|
|
|
def update_mfd(self):
|
|
"""Updates the MFD image with translation and calculates FPS, limiting to MFD_FPS."""
|
|
try:
|
|
start_time = time.time()
|
|
if self.test_image_var.get() == 1:
|
|
# Use test image and simulate translation
|
|
self.mfd_x_offset = (self.mfd_x_offset + 1) % MFD_WIDTH
|
|
translated_image = np.roll(self.test_mfd_image, -self.mfd_x_offset, axis=1)
|
|
delay = max(1, int(1000 / MFD_FPS)) # Target delay for MFD_FPS
|
|
else:
|
|
# Use loaded image
|
|
translated_image = self.mfd_image_data
|
|
delay = 1 # Update as fast as possible when not in test mode
|
|
|
|
# Create and move window only once
|
|
if not self.mfd_window_initialized:
|
|
cv2.imshow("MFD", translated_image)
|
|
try:
|
|
cv2.moveWindow("MFD", self.mfd_x, self.mfd_y)
|
|
self.mfd_window_initialized = True
|
|
except cv2.error as e:
|
|
print(f"Error moving MFD window on initialization: {e}")
|
|
logging.error(f"Error moving MFD window on initialization: {e}")
|
|
else:
|
|
cv2.imshow("MFD", translated_image) # Just update the image
|
|
|
|
# Calculate FPS
|
|
self.mfd_frame_count += 1
|
|
elapsed_time = time.time() - self.mfd_start_time
|
|
if elapsed_time >= 1.0: # Update FPS every second
|
|
self.mfd_fps = self.mfd_frame_count / elapsed_time
|
|
self.mfd_start_time = time.time()
|
|
self.mfd_frame_count = 0
|
|
self.update_status() # Update status bar
|
|
|
|
self.root.after(delay, self.update_mfd) # Schedule the next update
|
|
|
|
except Exception as e:
|
|
print(f"Error updating MFD: {e}")
|
|
logging.exception(f"Error updating MFD: {e}")
|
|
|
|
def update_sar(self):
|
|
"""Updates the SAR image with translation."""
|
|
try:
|
|
if self.test_image_var.get() == 1:
|
|
# Use test image and simulate translation
|
|
self.sar_x_offset = (self.sar_x_offset + 1) % self.sar_display_width
|
|
translated_image = np.roll(self.resized_sar, -self.sar_x_offset, axis=1) # shift columns
|
|
else:
|
|
# Use loaded image
|
|
translated_image = self.resized_sar
|
|
|
|
# Create and move window only once
|
|
if not self.sar_window_initialized:
|
|
cv2.imshow("SAR", translated_image)
|
|
try:
|
|
cv2.moveWindow("SAR", self.sar_x, self.sar_y)
|
|
self.sar_window_initialized = True
|
|
except cv2.error as e:
|
|
print(f"Error moving SAR window on initialization: {e}")
|
|
logging.error(f"Error moving SAR window on initialization: {e}")
|
|
|
|
# Set mouse callback *after* the window is created
|
|
cv2.setMouseCallback("SAR", self.sar_mouse_callback)
|
|
self.sar_mouse_callback_set = True
|
|
|
|
else:
|
|
cv2.imshow("SAR", translated_image) # Just update the image
|
|
|
|
# Calculate SAR FPS
|
|
self.sar_frame_count += 1
|
|
elapsed_time = time.time() - self.sar_update_time
|
|
if elapsed_time >= 1.0:
|
|
self.sar_fps = self.sar_frame_count / elapsed_time
|
|
self.sar_update_time = time.time()
|
|
self.sar_frame_count = 0
|
|
self.update_status() # Update status bar
|
|
|
|
self.root.after(5000, self.update_sar)
|
|
|
|
except Exception as e:
|
|
print(f"Error updating SAR: {e}")
|
|
logging.exception(f"Error updating SAR: {e}")
|
|
|
|
def update_sar_size(self, event=None):
|
|
"""Updates the SAR image size based on the combobox selection."""
|
|
selected_size = self.sar_size_combo.get()
|
|
if selected_size == "1:1":
|
|
self.sar_display_width = SAR_WIDTH
|
|
self.sar_display_height = SAR_HEIGHT
|
|
elif selected_size == "1:2":
|
|
self.sar_display_width = INITIAL_SAR_WIDTH
|
|
self.sar_display_height = INITIAL_SAR_HEIGHT
|
|
elif selected_size == "1:3":
|
|
self.sar_display_width = SAR_WIDTH // 3
|
|
self.sar_display_height = SAR_HEIGHT // 3
|
|
elif selected_size == "1:5":
|
|
self.sar_display_width = SAR_WIDTH // 5
|
|
self.sar_display_height = SAR_HEIGHT // 5
|
|
elif selected_size == "1:10":
|
|
self.sar_display_width = SAR_WIDTH // 10
|
|
self.sar_display_height = SAR_HEIGHT // 10
|
|
|
|
if self.test_image_var.get() == 1:
|
|
self.generate_test_images() #Regenerate the test images
|
|
else:
|
|
self.set_initial_sar_image()
|
|
|
|
# Resize the adjusted image
|
|
self.resized_sar = cv2.resize(self.adjusted_sar, (self.sar_display_width, self.sar_display_height),
|
|
interpolation=cv2.INTER_AREA)
|
|
print(f"New SAR size: {self.sar_display_width}x{self.sar_display_height}")
|
|
self.update_sar() # Update display
|
|
|
|
# Update SAR window position in case the size changed
|
|
self.sar_x = self.tkinter_x + TKINTER_MIN_WIDTH + 20 # Place SAR window to the right of Tkinter
|
|
self.sar_y = 10
|
|
|
|
def update_contrast(self, value):
|
|
"""Updates the SAR image contrast based on the slider value."""
|
|
try:
|
|
self.sar_contrast = float(value)
|
|
self.update_brightness_contrast_lut()
|
|
self.update_sar_display()
|
|
except ValueError:
|
|
pass
|
|
|
|
def update_brightness(self, value):
|
|
"""Updates the SAR image brightness based on the slider value."""
|
|
try:
|
|
self.sar_brightness = int(float(value))
|
|
self.update_brightness_contrast_lut()
|
|
self.update_sar_display()
|
|
except ValueError:
|
|
pass
|
|
|
|
def update_sar_palette(self, event=None):
|
|
"""Updates the SAR image palette based on the combobox selection."""
|
|
self.sar_palette = self.palette_combo.get()
|
|
self.update_sar_display()
|
|
|
|
def update_brightness_contrast_lut(self):
|
|
"""Calculates the Look-Up Table (LUT) for brightness and contrast adjustment."""
|
|
self.brightness_contrast_lut = np.array([
|
|
np.clip(i * self.sar_contrast + self.sar_brightness, 0, 255).astype(np.uint8) for i in range(256)
|
|
])
|
|
|
|
def update_sar_display(self):
|
|
"""Applies contrast, brightness, and palette adjustments to the SAR image and triggers SAR update."""
|
|
|
|
# Apply contrast and brightness using LUT
|
|
if self.test_image_var.get() == 1:
|
|
adjusted_image = cv2.LUT(cv2.normalize(self.test_sar_image, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U),
|
|
self.brightness_contrast_lut)
|
|
else:
|
|
adjusted_image = cv2.LUT(self.current_sar, self.brightness_contrast_lut)
|
|
|
|
# Apply color Palette
|
|
if self.sar_palette != "GRAY":
|
|
adjusted_image = self.apply_color_palette(adjusted_image, self.sar_palette)
|
|
|
|
self.adjusted_sar = adjusted_image.copy() # Store the adjusted image
|
|
# Resize the adjusted image
|
|
self.resized_sar = cv2.resize(self.adjusted_sar, (self.sar_display_width, self.sar_display_height),
|
|
interpolation=cv2.INTER_AREA)
|
|
# Force image display update
|
|
self.put_sar_queue(self.resized_sar)
|
|
|
|
def apply_color_palette(self, image, palette):
|
|
"""Applies the given color palette to the image"""
|
|
|
|
try:
|
|
colormap = getattr(cv2, f"COLORMAP_{palette.upper()}")
|
|
colorized_image = cv2.applyColorMap(image, colormap)
|
|
return colorized_image
|
|
except AttributeError:
|
|
print(f"Error: Colormap '{palette}' not found in OpenCV.")
|
|
logging.error(f"Colormap '{palette}' not found in OpenCV.")
|
|
return image # Return the original image if the colormap is invalid
|
|
except Exception as e:
|
|
print(f"Error applying colormap: {e}")
|
|
logging.exception(f"Error applying colormap: {e}")
|
|
return image
|
|
|
|
def set_sar_info(self, message):
|
|
"""Updates the SAR info label in Tkinter."""
|
|
self.sar_info_label.config(text=message)
|
|
|
|
def update_status(self, ):
|
|
"""Updates the status bar with MFD and SAR FPS (and seconds per image for SAR)."""
|
|
status_text = f"Ready | MFD FPS: {self.mfd_fps:.2f} | SAR FPS: {self.sar_fps:.2f}"
|
|
|
|
# Add seconds per image for SAR
|
|
if self.sar_fps > 0:
|
|
seconds_per_image = 1 / self.sar_fps
|
|
status_text += f" ({seconds_per_image:.2f} s/img)"
|
|
else:
|
|
status_text += " (N/A s/img)"
|
|
|
|
self.statusbar.config(text=status_text)
|
|
|
|
def set_status(self, message):
|
|
"""Updates the status bar."""
|
|
self.statusbar.config(text=message)
|
|
|
|
def set_new_sar_image(self, image):
|
|
"""Function to set a new Sar image"""
|
|
self.sar_image_data = image
|
|
self.current_sar = cv2.normalize(self.sar_image_data, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
|
|
self.adjusted_sar = self.current_sar.copy() # Update the adjusted image too
|
|
self.resized_sar = cv2.resize(self.adjusted_sar, (self.sar_display_width, self.sar_display_height),
|
|
interpolation=cv2.INTER_AREA)
|
|
# Force update to image
|
|
self.put_sar_queue(self.resized_sar)
|
|
|
|
def sar_mouse_callback(self, event, x, y, flags, param):
|
|
"""Callback function for mouse events on the SAR window."""
|
|
if event == cv2.EVENT_MOUSEMOVE:
|
|
# Convert pixel coordinates to normalized coordinates (0.0 - 1.0)
|
|
normalized_x = x / self.sar_display_width
|
|
normalized_y = y / self.sar_display_height
|
|
|
|
# Convert normalized coordinates to kilometers from the center
|
|
km_x = (normalized_x - 0.5) * SAR_IMAGE_SIZE_KM
|
|
km_y = (0.5 - normalized_y) * SAR_IMAGE_SIZE_KM # Y axis is inverted
|
|
|
|
# Convert kilometers to latitude and longitude offset
|
|
lat_offset = km_y / 111.0 # Approximate km to latitude conversion
|
|
lon_offset = km_x / (111.0 * np.cos(np.radians(SAR_CENTER_LAT))) # Approximate km to longitude
|
|
|
|
# Calculate latitude and longitude
|
|
latitude = SAR_CENTER_LAT + lat_offset
|
|
longitude = SAR_CENTER_LON + lon_offset
|
|
|
|
# Put the coordinates in the queue
|
|
try:
|
|
self.mouse_queue.put((latitude, longitude), block=False)
|
|
except queue.Full:
|
|
pass # Drop value
|
|
|
|
def update_mouse_latlon_label(self, latitude, longitude):
|
|
"""Updates the Tkinter label with the mouse latitude and longitude."""
|
|
self.mouse_latlon_label.config(text=f"Mouse: Lat={latitude:.4f}, Lon={longitude:.4f}")
|
|
|
|
def put_sar_queue(self, image):
|
|
"""Function to put the image on the queue"""
|
|
try:
|
|
self.sar_queue.put(image, block=False)
|
|
except queue.Full:
|
|
pass
|
|
|
|
def process_sar_queue(self):
|
|
"""This function get the new Sar image from queue and displays"""
|
|
try:
|
|
image = self.sar_queue.get(block=False)
|
|
cv2.imshow("SAR", image)
|
|
except queue.Empty:
|
|
pass
|
|
self.root.after(1, self.process_sar_queue) # Loop in the main Thread
|
|
|
|
def process_mouse_queue(self):
|
|
"""Processes the mouse coordinate queue and updates the Tkinter label."""
|
|
try:
|
|
latitude, longitude = self.mouse_queue.get(block=False)
|
|
# Limit the update rate to 10 times per second (100 ms interval)
|
|
current_time = time.time()
|
|
if current_time - self.last_mouse_update >= 0.1:
|
|
self.update_mouse_latlon_label(latitude, longitude)
|
|
self.last_mouse_update = current_time
|
|
except queue.Empty:
|
|
pass
|
|
self.root.after(1, self.process_mouse_queue)
|
|
|
|
def update_image_mode(self):
|
|
"""Updates the image mode based on the checkbox value and reload images if needed."""
|
|
if self.test_image_var.get() == 1:
|
|
self.generate_test_images() # Generate test images when test mode is enabled
|
|
else:
|
|
self.set_initial_sar_image() # Reset the SAR image
|
|
self.update_mfd()
|
|
self.update_sar()
|
|
|
|
if __name__ == "__main__":
|
|
root = tk.Tk()
|
|
app = App(root)
|
|
root.mainloop()
|
|
cv2.destroyAllWindows() |