Skip to content

Commit

Permalink
mavpicviewer: WIP improved mosaic
Browse files Browse the repository at this point in the history
  • Loading branch information
rmackay9 committed Sep 7, 2024
1 parent 3cdb8af commit ea16945
Show file tree
Hide file tree
Showing 2 changed files with 266 additions and 2 deletions.
264 changes: 264 additions & 0 deletions MAVProxy/tools/mavpicviewer/mosaic_window2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
#!/usr/bin/env python3

'''
Picture Viewer Window
Displays a window for users to review a collection of images quickly
AP_FLAKE8_CLEAN
'''

from threading import Thread
from math import ceil
import cv2
import time
import os
import numpy as np
import wx.lib.scrolledpanel as scrolled
from MAVProxy.modules.lib import mp_util
if mp_util.has_wxpython:
from MAVProxy.modules.lib.wx_loader import wx
from MAVProxy.modules.lib.mp_menu import MPMenuTop
from MAVProxy.modules.lib.mp_menu import MPMenuItem
from MAVProxy.modules.lib.mp_menu import MPMenuSubMenu
from MAVProxy.modules.lib.mp_image import MPImage, MPImagePanel
from MAVProxy.modules.lib.mp_menu import MPMenuCallDirDialog


class mosaic_window2:
"""displays a mosaic of images"""

def __init__(self, filelist):

# determine if filelist is a string or a list of strings
self.filenumber = 0
if type(filelist) is str:
self.filelist = []
self.filelist.append(filelist)
else:
# use the first item in the list
self.filelist = filelist

# hardcoded thumbnail image size and number of columns
self.thumb_size = 100
self.thumb_columns = 5
self.thumb_rows = ceil(len(filelist) / self.thumb_columns)

# create image viewer
self.im = None
#self.update_image()

# create window
self.app = wx.App()
self.frame = wx.Frame(None, title="Mosaic", size=(650, 200))
self.frame.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))

# add menu
self.menu = wx.Menu()
self.menu.Append(1, "Open Folder", "Open a Folder of images")
self.menu_bar = wx.MenuBar()
self.menu_bar.Append(self.menu, "Menu")
self.frame.SetMenuBar(self.menu_bar)
#self.frame.Bind(wx.EVT_MENU, self.menu_set_api_key_show, id=1)

# add settings input window
self.settings_frame = wx.Frame(None, title="Input Settings", size=(560, 50))
self.settings_text_input = wx.TextCtrl(self.settings_frame, id=-1, pos=(10, 10), size=(450, -1),
style=wx.TE_PROCESS_ENTER, value="hello")
self.settings_set_button = wx.Button(self.settings_frame, id=-1, label="Set", pos=(470, 10), size=(75, 25))
#self.settings_frame.Bind(wx.EVT_BUTTON, self.settings_set_button_click, self.settings_set_button)
#self.settings_frame.Bind(wx.EVT_TEXT_ENTER, self.settings_set_button_click, self.settings_text_input)
#self.settings_frame.Bind(wx.EVT_CLOSE, self.apikey_close_button_click)

# add a scrolled panel
self.scrolled_panel = scrolled.ScrolledPanel(self.frame, -1, size=(600, 600), style=wx.TAB_TRAVERSAL)
self.scrolled_panel_sizer = wx.GridSizer(cols=5, hgap=5, vgap=5)

# add an image
#img1_path = self.filelist[1]
#self.wx_image1 = wx.Image(img1_path, wx.BITMAP_TYPE_ANY).Scale(100, 100)
#self.image = wx.StaticBitmap(self.scrolled_panel, wx.ID_ANY, wx.Bitmap(self.wx_image1))

#img2_path = self.filelist[2]
#self.wx_image2 = wx.Image(img2_path, wx.BITMAP_TYPE_ANY).Scale(100, 100)
#self.image2 = wx.StaticBitmap(self.scrolled_panel, wx.ID_ANY, wx.Bitmap(self.wx_image2))

#self.scrolled_panel_sizer.Add(self.image, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
#self.scrolled_panel_sizer.Add(self.image2, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)

# add images
for i in range(len(self.filelist)):
img_path = self.filelist[i]
wx_image = wx.Image(img_path, wx.BITMAP_TYPE_ANY).Scale(100, 100)
image = wx.StaticBitmap(self.scrolled_panel, wx.ID_ANY, wx.Bitmap(wx_image))
self.scrolled_panel_sizer.Add(image, proportion=0, flag=wx.EXPAND | wx.ALL, border=2)

self.scrolled_panel.SetSizer(self.scrolled_panel_sizer)
self.scrolled_panel.SetupScrolling(scroll_x=True, scroll_y=True)

# add a read-only reply text box
self.text_reply = wx.TextCtrl(self.frame, id=-1, size=(600, 80), style=wx.TE_READONLY | wx.TE_MULTILINE | wx.TE_RICH)

# add a cancel button
self.cancel_button = wx.Button(self.frame, id=-1, label="cancel", size=(75, 25))
#self.frame.Bind(wx.EVT_BUTTON, self.cancel_button_click , self.cancel_button)

# add a vertical and horizontal sizers
self.vert_sizer = wx.BoxSizer(wx.VERTICAL)
self.horiz_sizer = wx.BoxSizer(wx.HORIZONTAL)

self.horiz_sizer.Add(self.cancel_button, proportion=0, flag=wx.ALIGN_TOP | wx.ALL, border=5)
wx.CallAfter(self.cancel_button.Disable)

# set size hints and add sizer to frame
self.vert_sizer.Add(self.scrolled_panel, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
self.vert_sizer.Add(self.text_reply, proportion=0, flag=wx.EXPAND, border=5)
#self.vert_sizer.Add(self.image, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
#self.vert_sizer.Add(self.image2, proportion=0, flag=wx.EXPAND | wx.ALL, border=5)
self.vert_sizer.Add(self.horiz_sizer, proportion=0, flag=wx.EXPAND)
#self.frame.Bind(wx.EVT_SIZE, self.on_resize)
self.frame.SetSizer(self.vert_sizer)
self.frame.Layout()

# set focus on the input text box
self.text_reply.SetFocus()

# show frame
self.frame.Show()

# window loop (this does not return until the window is closed)
self.app.MainLoop()

self.thread = Thread(target=self.mosaic_window_loop, name='mosaic_window_loop')
self.thread.daemon = False
self.thread.start()

# main loop
#def mosaic_window_loop(self):
# """main thread"""
# while True:
# if self.im is None:
# break
# time.sleep(0.25)
# self.check_events()

# set window title
def set_title(self, title):
"""set image title"""
if self.im is None:
return
self.im.set_title(title)

# process window events
def check_events(self):
"""check for image events"""
if self.im is None:
return
if not self.im.is_alive():
self.im = None
return
for event in self.im.events():
if isinstance(event, MPMenuItem):
if event.returnkey == "openfolder":
self.cmd_openfolder()
elif event.returnkey == "fitWindow":
print("fitting to window")
self.im.fit_to_window()
elif event.returnkey == "fullSize":
print("full size")
self.im.full_size()
elif event.returnkey == "nextimage":
self.cmd_nextimage()
elif event.returnkey == "previmage":
self.cmd_previmage()
else:
debug_str = "event: %s" % event
self.set_title(debug_str)
continue
if event.ClassName == "wxMouseEvent":
if event.X is not None and event.Y is not None:
print("mosaic pixel x:%f y:%f" % (event.X, event.Y))

# display dialog to open a folder
def cmd_openfolder(self):
print("I will open a folder")

# display dialog to open a file
def cmd_openfile(self):
print("I will open a file")

# update current image to next image
def cmd_nextimage(self):
if self.filenumber >= len(self.filelist)-1:
print("picviewer: already at last image %d" % self.filenumber)
return
self.filenumber = self.filenumber+1
self.update_image()

# update current image to previous image
def cmd_previmage(self):
if self.filenumber <= 0:
print("picviewer: already at first image")
return
self.filenumber = self.filenumber - 1
self.update_image()

# update the mosaic of images
# should be called if filenumber is changed
def update_image(self):
# update filename
self.filename = self.filelist[self.filenumber]
base_filename = os.path.basename(self.filename)

# create image viewer if required
if self.im is None:
self.im = MPImage(title=base_filename,
mouse_events=True,
mouse_movement_events=True,
key_events=True,
can_drag=True,
can_zoom=False,
auto_size=False,
auto_fit=False)

# check if image viewer was created
if self.im is None:
print("picviewer: failed to create image viewer")
return

# set title to filename
self.set_title("Mosaic " + base_filename)

# create blank image
temp_image = cv2.imread(self.filename)
h, w, c = temp_image.shape
mosaic_image = 255 * np.ones(shape=(self.thumb_rows * self.thumb_size,
self.thumb_columns * self.thumb_size, c),
dtype=np.uint8)

# iterate through images and add thumbnails to mosaic
row = 0
col = 0
for i in range(len(self.filelist)):
image_filename = self.filelist[i]
image = cv2.imread(image_filename)
image_small = cv2.resize(image, (self.thumb_size, self.thumb_size), interpolation=cv2.INTER_AREA)
self.overlay_image(mosaic_image, image_small, col * self.thumb_size, row * self.thumb_size)
col = col + 1
if col >= self.thumb_columns:
col = 0
row = row + 1

# update image and colormap
self.im.set_image(mosaic_image)
self.im.set_colormap("None")

def overlay_image(self, img, img2, x, y):
'''overlay a 2nd image on a first image, at position x,y on the first image'''
(img_width, img_height) = self.image_shape(img2)
img[y:y+img_height, x:x+img_width] = img2

def image_shape(self, img):
'''return (w,h) of an image, coping with different image formats'''
height, width = img.shape[:2]
return (width, height)
4 changes: 2 additions & 2 deletions MAVProxy/tools/mavpicviewer/picviewer_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import time
import os
import piexif
import mosaic_window
import mosaic_window2

from MAVProxy.modules.lib import mp_util
from MAVProxy.modules.lib import mp_elevation
Expand Down Expand Up @@ -90,7 +90,7 @@ def __init__(self, filelist):
self.update_map()

# create mosaic of images
self.mosaic = mosaic_window.mosaic_window(self.filelist)
self.mosaic = mosaic_window2.mosaic_window2(self.filelist)

# create menu
self.menu = None
Expand Down

0 comments on commit ea16945

Please sign in to comment.