1
|
import cv2 as cv
|
2
|
import numpy as np
|
3
|
import tkinter as tk
|
4
|
from tkinter import filedialog, messagebox
|
5
|
import os
|
6
|
from PIL import Image, ImageTk
|
7
|
import configparser
|
8
|
from ImageIO import ImageIO
|
9
|
|
10
|
class Commands:
|
11
|
## \brief A class to handle various commands related to image processing and display.
|
12
|
#
|
13
|
# The Commands class provides methods for loading, displaying, and adjusting the overlap region
|
14
|
# for images within a graphical user interface (GUI). It also supports image blending for overlapping
|
15
|
# regions with smooth gradient transitions.
|
16
|
#
|
17
|
|
18
|
def __init__(self, gui, shared_data):
|
19
|
## \brief Initializes the Commands instance.
|
20
|
#
|
21
|
# Sets up the GUI interface, shared data, and ImageIO for image operations.
|
22
|
#
|
23
|
# \param gui The GUI instance used for displaying images and receiving user input.
|
24
|
# \param shared_data The shared data structure that holds images and other state information.
|
25
|
# \return Nothing returned.
|
26
|
self.gui = gui
|
27
|
self.shared_data = shared_data
|
28
|
self.io = ImageIO(shared_data)
|
29
|
|
30
|
def load_left_image(self):
|
31
|
## \brief Loads the left image from a file.
|
32
|
#
|
33
|
# Opens a file dialog for the user to select a left image file. If a file is selected, it is loaded
|
34
|
# into the shared data.
|
35
|
#
|
36
|
# \warning This method does not check the file format or image content.
|
37
|
# \return Nothing returned.
|
38
|
filepath = filedialog.askopenfilename(title="Select Left Image")
|
39
|
if filepath:
|
40
|
self.shared_data.left_image = self.io.load_image(filepath)
|
41
|
print("Left image loaded.")
|
42
|
|
43
|
def load_right_image(self):
|
44
|
## \brief Loads the right image from a file.
|
45
|
#
|
46
|
# Opens a file dialog for the user to select a right image file. If a file is selected, it is loaded
|
47
|
# into the shared data.
|
48
|
#
|
49
|
# \warning This method does not check the file format or image content.
|
50
|
# \return Nothing returned.
|
51
|
filepath = filedialog.askopenfilename(title="Select Right Image")
|
52
|
if filepath:
|
53
|
self.shared_data.right_image = self.io.load_image(filepath)
|
54
|
print("Right image loaded.")
|
55
|
|
56
|
def smooth_gradient(self, length, rate):
|
57
|
## \brief Creates a smooth gradient for the overlap region.
|
58
|
#
|
59
|
# Generates a gradient that transitions from full brightness to zero using a non-linear quadratic falloff.
|
60
|
# The gradient is scaled by the specified rate and clipped to be between 0 and 1.
|
61
|
#
|
62
|
# \param length The length of the gradient.
|
63
|
# \param rate A scaling factor to control the gradient's intensity.
|
64
|
# \return A numpy array containing the gradient.
|
65
|
x = np.linspace(0, 1, length)
|
66
|
gradient = 1 - (x ** 2)
|
67
|
gradient = np.clip(gradient * rate, 0, 1)
|
68
|
return gradient
|
69
|
|
70
|
def show_left_image(self):
|
71
|
## \brief Displays the left image with a smooth gradient applied to the overlap region.
|
72
|
#
|
73
|
# If a left image is available, this method applies a smooth gradient to its overlapping region
|
74
|
# and displays it on the GUI canvas. If no left image is loaded, an error message is shown.
|
75
|
#
|
76
|
# \return Nothing returned.
|
77
|
if self.shared_data.left_image is not None:
|
78
|
self.shared_data.isLeft = True
|
79
|
left = self.shared_data.left_image.copy()
|
80
|
height = left.shape[0]
|
81
|
left_overlap_start = left.shape[1] - self.shared_data.overlap_region
|
82
|
gradient_alpha = np.linspace(1, 0, self.shared_data.overlap_region)
|
83
|
gradient_alpha = np.tile(gradient_alpha, (height, 1))
|
84
|
left_overlap = left[:, left_overlap_start:, :].copy()
|
85
|
left_overlap = (left_overlap * gradient_alpha[:, :, np.newaxis]).astype(np.uint8)
|
86
|
left[:, left_overlap_start:, :] = left_overlap
|
87
|
self.io.show_image(left, canvas=self.gui.canvas)
|
88
|
print("Left image displayed.")
|
89
|
else:
|
90
|
messagebox.showerror("Error", "No left image loaded!")
|
91
|
|
92
|
def show_right_image(self):
|
93
|
## \brief Displays the right image with a smooth gradient applied to the overlap region.
|
94
|
#
|
95
|
# If a right image is available, this method applies a smooth gradient to its overlapping region
|
96
|
# and displays it on the GUI canvas. If no right image is loaded, an error message is shown.
|
97
|
#
|
98
|
# \return Nothing returned.
|
99
|
if self.shared_data.right_image is not None:
|
100
|
self.shared_data.isLeft = False
|
101
|
right = self.shared_data.right_image.copy()
|
102
|
height = right.shape[0]
|
103
|
right_overlap_end = self.shared_data.overlap_region
|
104
|
gradient_alpha = np.linspace(0, 1, right_overlap_end)
|
105
|
gradient_alpha = np.tile(gradient_alpha, (height, 1))
|
106
|
right_overlap = right[:, :right_overlap_end, :].copy()
|
107
|
right_overlap = (right_overlap * gradient_alpha[:, :, np.newaxis]).astype(np.uint8)
|
108
|
right[:, :right_overlap_end, :] = right_overlap
|
109
|
self.io.show_image(right, canvas=self.gui.canvas)
|
110
|
print("Right image displayed.")
|
111
|
else:
|
112
|
messagebox.showerror("Error", "No right image loaded!")
|
113
|
|
114
|
def show_left_original(self):
|
115
|
## \brief Displays the original left image without any modifications.
|
116
|
#
|
117
|
# If a left image is loaded, it displays the original left image on the GUI canvas.
|
118
|
#
|
119
|
# \return Nothing returned.
|
120
|
if self.shared_data.left_image is not None:
|
121
|
self.shared_data.isLeft = True
|
122
|
left = self.shared_data.left_image.copy()
|
123
|
self.io.show_image(left, canvas=self.gui.canvas)
|
124
|
print("Left image displayed.")
|
125
|
else:
|
126
|
messagebox.showerror("Error", "No left image loaded!")
|
127
|
|
128
|
def show_right_original(self):
|
129
|
## \brief Displays the original right image without any modifications.
|
130
|
#
|
131
|
# If a right image is loaded, it displays the original right image on the GUI canvas.
|
132
|
#
|
133
|
# \return Nothing returned.
|
134
|
if self.shared_data.right_image is not None:
|
135
|
self.shared_data.isLeft = False
|
136
|
right = self.shared_data.right_image.copy()
|
137
|
self.io.show_image(right, canvas=self.gui.canvas)
|
138
|
print("Right image displayed.")
|
139
|
else:
|
140
|
messagebox.showerror("Error", "No right image loaded!")
|
141
|
|
142
|
def set_overlap_region(self):
|
143
|
## \brief Sets the overlap region for image processing.
|
144
|
#
|
145
|
# Updates the overlap region value from the GUI input, updates the shared data, and
|
146
|
# redisplays the current image (left or right) with the new overlap setting.
|
147
|
#
|
148
|
# \return Nothing returned.
|
149
|
self.shared_data.overlap_region = self.gui.overlap.get()
|
150
|
print(f"Overlap region set to {self.shared_data.overlap_region}")
|
151
|
if self.shared_data.isLeft:
|
152
|
self.show_left_image()
|
153
|
else:
|
154
|
self.show_right_image()
|
155
|
|
156
|
def increase_overlap(self):
|
157
|
## \brief Increases the overlap region value by one.
|
158
|
#
|
159
|
# Increments the overlap region by one and redisplays the current image with the updated overlap.
|
160
|
#
|
161
|
# \return Nothing returned.
|
162
|
current_overlap = self.gui.overlap.get()
|
163
|
self.gui.overlap.set(current_overlap + 1)
|
164
|
self.set_overlap_region()
|
165
|
if self.shared_data.isLeft:
|
166
|
self.show_left_image()
|
167
|
else:
|
168
|
self.show_right_image()
|
169
|
|
170
|
def decrease_overlap(self):
|
171
|
## \brief Decreases the overlap region value by one.
|
172
|
#
|
173
|
# Decrements the overlap region by one (ensuring it does not go negative) and redisplays
|
174
|
# the current image with the updated overlap.
|
175
|
#
|
176
|
# \return Nothing returned.
|
177
|
current_overlap = self.gui.overlap.get()
|
178
|
self.gui.overlap.set(max(0, current_overlap - 1))
|
179
|
self.set_overlap_region()
|
180
|
if self.shared_data.isLeft:
|
181
|
self.show_left_image()
|
182
|
else:
|
183
|
self.show_right_image()
|