1
|
import tkinter as tk
|
2
|
|
3
|
from tkinter import filedialog, messagebox
|
4
|
|
5
|
from tkinter import ttk
|
6
|
|
7
|
import configparser
|
8
|
|
9
|
import cv2
|
10
|
|
11
|
import numpy as np
|
12
|
|
13
|
import os
|
14
|
|
15
|
import logging
|
16
|
|
17
|
from PIL import Image, ImageTk
|
18
|
|
19
|
# Setup logging
|
20
|
|
21
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
22
|
|
23
|
# ConfigReader class definition
|
24
|
|
25
|
class ConfigReader:
|
26
|
|
27
|
def __init__(self, config_path=None):
|
28
|
# Assume some initialization using config_path
|
29
|
self.config_path = config_path
|
30
|
# Add other initialization logic here
|
31
|
self.config_parser = configparser.ConfigParser()
|
32
|
if config_path is not None and os.path.exists(config_path):
|
33
|
self.config_parser.read(config_path)
|
34
|
|
35
|
def getImageName(self):
|
36
|
|
37
|
return str(self.config_parser['DEFAULT']['image_name'])
|
38
|
|
39
|
def getProjectedImageWidth(self):
|
40
|
|
41
|
return int(self.config_parser['DEFAULT']['projected_image_width'])
|
42
|
|
43
|
def getProjectedOverlapWidth(self):
|
44
|
|
45
|
return int(self.config_parser['DEFAULT']['projected_overlap_width'])
|
46
|
|
47
|
def getGamma(self):
|
48
|
|
49
|
return float(self.config_parser['DEFAULT']['gamma'])
|
50
|
|
51
|
def getImageSide(self):
|
52
|
|
53
|
return int(self.config_parser['DEFAULT']['image_side'])
|
54
|
|
55
|
def getTransparencyFactor(self):
|
56
|
|
57
|
return float(self.config_parser['DEFAULT'].get('transparency_factor', 1.0))
|
58
|
|
59
|
def setParameters(self, parameters):
|
60
|
# The parameters is a dictionary with key-value pairs
|
61
|
if 'DEFAULT' not in self.config_parser:
|
62
|
self.config_parser['DEFAULT'] = {}
|
63
|
|
64
|
for key, value in parameters.items():
|
65
|
self.config_parser['DEFAULT'][key] = str(value)
|
66
|
|
67
|
def saveConfig(self, path):
|
68
|
with open(path, 'w') as configfile:
|
69
|
self.config_parser.write(configfile)
|
70
|
|
71
|
|
72
|
# MainDisplay class definition
|
73
|
|
74
|
class MainDisplay:
|
75
|
|
76
|
def readImage(self, image_path):
|
77
|
|
78
|
# Loads an image from the specified path with 3 channels (BGR)
|
79
|
|
80
|
image = cv2.imread(image_path, cv2.IMREAD_COLOR)
|
81
|
|
82
|
if image is None:
|
83
|
|
84
|
logging.error(f"Error loading image at {image_path}")
|
85
|
|
86
|
else:
|
87
|
|
88
|
logging.info(f"Loaded image {image_path} with shape: {image.shape}")
|
89
|
|
90
|
return image
|
91
|
|
92
|
def setImage(self, image):
|
93
|
|
94
|
# Placeholder for image processing if necessary
|
95
|
|
96
|
# Here you can modify or prepare the image before displaying
|
97
|
|
98
|
logging.info(f"Setting image with shape: {image.shape}")
|
99
|
|
100
|
return image
|
101
|
|
102
|
# MaskCreator class definition
|
103
|
|
104
|
class MaskCreator:
|
105
|
|
106
|
def __init__(self, image, transparency_factor):
|
107
|
|
108
|
self.__image = image
|
109
|
|
110
|
self.__alpha_gradient = None
|
111
|
|
112
|
self.__gamma_corrected = None
|
113
|
|
114
|
self.result_image = None
|
115
|
|
116
|
self.__mask = None
|
117
|
|
118
|
self.transparency_factor = transparency_factor
|
119
|
|
120
|
def smoothstep(self, edge0, edge1, x):
|
121
|
|
122
|
# Smoothstep function for smoother transition (non-linear gradient)
|
123
|
|
124
|
x = np.clip((x - edge0) / (edge1 - edge0), 0, 1)
|
125
|
|
126
|
return x * x * (3 - 2 * x)
|
127
|
|
128
|
def createMask(self, image_side, mask_width, image_width):
|
129
|
|
130
|
self.__mask = self.__image.shape[1] * mask_width // image_width
|
131
|
|
132
|
gradient = np.linspace(0, 1, self.__mask)
|
133
|
|
134
|
# Apply smoothstep to create a smoother gradient
|
135
|
|
136
|
smooth_gradient = self.smoothstep(0, 1, gradient)
|
137
|
|
138
|
# Apply transparency factor to adjust the strength of the blending
|
139
|
|
140
|
smooth_gradient = smooth_gradient * self.transparency_factor
|
141
|
|
142
|
if image_side == 1:
|
143
|
|
144
|
# Gradient from transparent to opaque (middle to right)
|
145
|
|
146
|
self.__alpha_gradient = smooth_gradient
|
147
|
|
148
|
elif image_side == 0:
|
149
|
|
150
|
# Gradient from opaque to transparent (left to middle)
|
151
|
|
152
|
self.__alpha_gradient = smooth_gradient[::-1] # Reverse for the left side
|
153
|
|
154
|
def gammaCorrection(self, gamma):
|
155
|
|
156
|
# Apply gamma correction
|
157
|
|
158
|
inv_gamma = 1.0 / gamma
|
159
|
|
160
|
table = np.array([((i / 255.0) ** inv_gamma) * 255
|
161
|
|
162
|
for i in np.arange(256)]).astype("uint8")
|
163
|
|
164
|
self.__gamma_corrected = cv2.LUT(self.__image, table)
|
165
|
|
166
|
logging.info(f"Applied gamma correction with gamma={gamma}")
|
167
|
|
168
|
# Save gamma corrected image for inspection
|
169
|
|
170
|
cv2.imwrite("gamma_corrected.png", self.__gamma_corrected)
|
171
|
|
172
|
def alphaBlending(self, image_side):
|
173
|
|
174
|
"""
|
175
|
|
176
|
Applies alpha blending on the gamma-corrected image.
|
177
|
|
178
|
Combines the gamma-corrected part of the image with a black background using the alpha gradient mask.
|
179
|
|
180
|
"""
|
181
|
|
182
|
# Initialize result_image to be the gamma-corrected image
|
183
|
|
184
|
self.result_image = self.__gamma_corrected.copy()
|
185
|
|
186
|
if image_side == 1: # Right side
|
187
|
|
188
|
# Create a region of interest (ROI) where blending will occur (right side of the image)
|
189
|
|
190
|
roi = self.result_image[:, :self.__mask].astype(np.float32)
|
191
|
|
192
|
# Create black background for blending
|
193
|
|
194
|
black_background = np.zeros_like(roi, dtype=np.float32)
|
195
|
|
196
|
# Apply the alpha mask to blend gamma-corrected image with black background
|
197
|
|
198
|
alpha = self.__alpha_gradient.reshape(1, -1, 1).astype(np.float32)
|
199
|
|
200
|
blended = (alpha * roi + (1 - alpha) * black_background)
|
201
|
|
202
|
blended = np.clip(blended, 0, 255).astype(np.uint8)
|
203
|
|
204
|
# Place the blended region back in the result image
|
205
|
|
206
|
self.result_image[:, :self.__mask] = blended
|
207
|
|
208
|
logging.info(f"Applied alpha blending on the right side with mask width {self.__mask}")
|
209
|
|
210
|
# Save blended region for debugging
|
211
|
|
212
|
cv2.imwrite("blended_right_side.png", blended)
|
213
|
|
214
|
elif image_side == 0: # Left side
|
215
|
|
216
|
# Create a region of interest (ROI) where blending will occur (left side of the image)
|
217
|
|
218
|
roi = self.result_image[:, -self.__mask:].astype(np.float32)
|
219
|
|
220
|
# Create black background for blending
|
221
|
|
222
|
black_background = np.zeros_like(roi, dtype=np.float32)
|
223
|
|
224
|
# Apply the alpha mask to blend gamma-corrected image with black background
|
225
|
|
226
|
alpha = self.__alpha_gradient.reshape(1, -1, 1).astype(np.float32)
|
227
|
|
228
|
blended = (alpha * roi + (1 - alpha) * black_background)
|
229
|
|
230
|
blended = np.clip(blended, 0, 255).astype(np.uint8)
|
231
|
|
232
|
# Place the blended region back in the result image
|
233
|
|
234
|
self.result_image[:, -self.__mask:] = blended
|
235
|
|
236
|
logging.info(f"Applied alpha blending on the left side with mask width {self.__mask}")
|
237
|
|
238
|
# Save blended region for debugging
|
239
|
|
240
|
cv2.imwrite("blended_left_side.png", blended)
|
241
|
|
242
|
|
243
|
def processImage(image_path, params, main_display):
|
244
|
"""
|
245
|
Processes an image based on the provided parameters.
|
246
|
|
247
|
Args:
|
248
|
image_path (str): Path to the image file.
|
249
|
params (dict): Parameters for image processing.
|
250
|
main_display (MainDisplay): Instance of MainDisplay for image operations.
|
251
|
|
252
|
Returns:
|
253
|
tuple: Processed image and its corresponding name.
|
254
|
"""
|
255
|
|
256
|
# Retrieve parameters from params dictionary
|
257
|
mask_width = params.get('projected_overlap_width')
|
258
|
image_width = params.get('projected_image_width')
|
259
|
gamma = params.get('gamma')
|
260
|
image_side = params.get('image_side')
|
261
|
transparency_factor = params.get('transparency_factor')
|
262
|
|
263
|
# Determine image side name
|
264
|
if image_side == 0:
|
265
|
image_name = 'left'
|
266
|
elif image_side == 1:
|
267
|
image_name = 'right'
|
268
|
else:
|
269
|
logging.error(f"Invalid ImageSide value. Use 0 for left image, 1 for right image.")
|
270
|
return None, None
|
271
|
|
272
|
# Load image
|
273
|
image = main_display.readImage(image_path)
|
274
|
|
275
|
if image is None:
|
276
|
logging.error(f"Image loading failed for {image_path}. Skipping...")
|
277
|
return None, None
|
278
|
|
279
|
# Initialize result image
|
280
|
result_image = main_display.setImage(image).copy()
|
281
|
cv2.imwrite("initial_result_image.png", result_image) # Save initial image
|
282
|
|
283
|
# Initialize MaskCreator
|
284
|
mask_creator = MaskCreator(image, transparency_factor)
|
285
|
|
286
|
# Apply image modifications
|
287
|
mask_creator.createMask(image_side, mask_width, image_width)
|
288
|
mask_creator.gammaCorrection(gamma)
|
289
|
mask_creator.result_image = result_image
|
290
|
mask_creator.alphaBlending(image_side)
|
291
|
|
292
|
# Save final result for inspection
|
293
|
cv2.imwrite("final_result_image.png", mask_creator.result_image)
|
294
|
|
295
|
return mask_creator.result_image, image_name
|
296
|
|
297
|
|
298
|
def saveImage(image, name):
|
299
|
|
300
|
"""
|
301
|
|
302
|
Save the processed image to the project folder.
|
303
|
|
304
|
Args:
|
305
|
|
306
|
image (np.ndarray): The image to be saved.
|
307
|
|
308
|
name (str): Name of the image (left or right).
|
309
|
|
310
|
"""
|
311
|
|
312
|
# Define the output path
|
313
|
|
314
|
output_dir = os.path.join(os.getcwd(), "processed_images")
|
315
|
|
316
|
os.makedirs(output_dir, exist_ok=True)
|
317
|
|
318
|
# Create the output file name
|
319
|
|
320
|
output_path = os.path.join(output_dir, f"processed_image_{name}.png")
|
321
|
|
322
|
# Save the image
|
323
|
|
324
|
cv2.imwrite(output_path, image)
|
325
|
|
326
|
logging.info(f"Saved processed image: {output_path}")
|
327
|
|
328
|
# GUI Application class definition
|
329
|
|
330
|
class ImageProcessingApp:
|
331
|
def __init__(self, root):
|
332
|
self.root = root
|
333
|
self.root.title("Image Processing Application")
|
334
|
|
335
|
# Initialize variables
|
336
|
self.left_image_path = ""
|
337
|
self.right_image_path = ""
|
338
|
self.left_params = {}
|
339
|
self.right_params = {}
|
340
|
self.processed_images = {}
|
341
|
|
342
|
# Create notebook (tabs)
|
343
|
self.notebook = ttk.Notebook(root)
|
344
|
self.notebook.pack(expand=True, fill='both')
|
345
|
|
346
|
# Create frames for each tab
|
347
|
self.tab_left = ttk.Frame(self.notebook)
|
348
|
self.tab_right = ttk.Frame(self.notebook)
|
349
|
self.tab_settings = ttk.Frame(self.notebook)
|
350
|
self.tab_preview = ttk.Frame(self.notebook)
|
351
|
|
352
|
self.notebook.add(self.tab_left, text='Left Image')
|
353
|
self.notebook.add(self.tab_right, text='Right Image')
|
354
|
self.notebook.add(self.tab_settings, text='Settings')
|
355
|
self.notebook.add(self.tab_preview, text='Preview')
|
356
|
|
357
|
# Setup each tab
|
358
|
self.setupLeftTab()
|
359
|
self.setupRightTab()
|
360
|
self.setupSettingsTab()
|
361
|
self.setupPreviewTab()
|
362
|
|
363
|
# Process and Save Button
|
364
|
self.process_button = tk.Button(root, text="Process and Save Images", command=self.processAndSave)
|
365
|
self.process_button.pack(pady=10)
|
366
|
|
367
|
# Progress Bar
|
368
|
self.progress_bar = ttk.Progressbar(root, orient="horizontal", length=400, mode="determinate")
|
369
|
self.progress_bar.pack(pady=10)
|
370
|
|
371
|
def setupLeftTab(self):
|
372
|
frame = self.tab_left
|
373
|
|
374
|
# Select Image Button
|
375
|
btn_select = tk.Button(frame, text="Select Left Image", command=self.selectLeftImage)
|
376
|
btn_select.pack(pady=5)
|
377
|
|
378
|
# Display selected image path
|
379
|
self.left_image_label = tk.Label(frame, text="No image selected", wraplength=300, anchor="w", justify="left")
|
380
|
self.left_image_label.pack(padx=10, pady=5)
|
381
|
|
382
|
# Parameters for left image
|
383
|
params_frame = tk.LabelFrame(frame, text="Left Image Parameters", padx=10, pady=10)
|
384
|
params_frame.pack(padx=10, pady=10, fill="x")
|
385
|
|
386
|
# Projected Image Width
|
387
|
tk.Label(params_frame, text="Projected Image Width:").grid(row=0, column=0, sticky="e")
|
388
|
self.left_projected_image_width = tk.Entry(params_frame)
|
389
|
self.left_projected_image_width.insert(0, "800")
|
390
|
self.left_projected_image_width.grid(row=0, column=1, pady=2, sticky="w")
|
391
|
|
392
|
# Projected Overlap Width
|
393
|
tk.Label(params_frame, text="Projected Overlap Width:").grid(row=1, column=0, sticky="e")
|
394
|
self.left_projected_overlap_width = tk.Entry(params_frame)
|
395
|
self.left_projected_overlap_width.insert(0, "100")
|
396
|
self.left_projected_overlap_width.grid(row=1, column=1, pady=2, sticky="w")
|
397
|
|
398
|
# Gamma
|
399
|
tk.Label(params_frame, text="Gamma:").grid(row=2, column=0, sticky="e")
|
400
|
self.left_gamma = tk.Entry(params_frame)
|
401
|
self.left_gamma.insert(0, "1.0")
|
402
|
self.left_gamma.grid(row=2, column=1, pady=2, sticky="w")
|
403
|
|
404
|
# Image Side
|
405
|
tk.Label(params_frame, text="Image Side:").grid(row=3, column=0, sticky="e")
|
406
|
self.left_image_side = tk.IntVar(value=0) # Default to left
|
407
|
tk.Radiobutton(params_frame, text="Left", variable=self.left_image_side, value=0).grid(row=3, column=1, sticky="w")
|
408
|
tk.Radiobutton(params_frame, text="Right", variable=self.left_image_side, value=1).grid(row=3, column=1, padx=60, sticky="w")
|
409
|
|
410
|
# Transparency Factor
|
411
|
tk.Label(params_frame, text="Transparency Factor:").grid(row=4, column=0, sticky="e")
|
412
|
self.left_transparency_factor = tk.Entry(params_frame)
|
413
|
self.left_transparency_factor.insert(0, "1.0")
|
414
|
self.left_transparency_factor.grid(row=4, column=1, pady=2, sticky="w")
|
415
|
|
416
|
def setupRightTab(self):
|
417
|
frame = self.tab_right
|
418
|
|
419
|
# Select Image Button
|
420
|
btn_select = tk.Button(frame, text="Select Right Image", command=self.selectRightImage)
|
421
|
btn_select.pack(pady=5)
|
422
|
|
423
|
# Display selected image path
|
424
|
self.right_image_label = tk.Label(frame, text="No image selected", wraplength=300, anchor="w", justify="left")
|
425
|
self.right_image_label.pack(padx=10, pady=5)
|
426
|
|
427
|
# Parameters for right image
|
428
|
params_frame = tk.LabelFrame(frame, text="Right Image Parameters", padx=10, pady=10)
|
429
|
params_frame.pack(padx=10, pady=10, fill="x")
|
430
|
|
431
|
# Projected Image Width
|
432
|
tk.Label(params_frame, text="Projected Image Width:").grid(row=0, column=0, sticky="e")
|
433
|
self.right_projected_image_width = tk.Entry(params_frame)
|
434
|
self.right_projected_image_width.insert(0, "800")
|
435
|
self.right_projected_image_width.grid(row=0, column=1, pady=2, sticky="w")
|
436
|
|
437
|
# Projected Overlap Width
|
438
|
tk.Label(params_frame, text="Projected Overlap Width:").grid(row=1, column=0, sticky="e")
|
439
|
self.right_projected_overlap_width = tk.Entry(params_frame)
|
440
|
self.right_projected_overlap_width.insert(0, "100")
|
441
|
self.right_projected_overlap_width.grid(row=1, column=1, pady=2, sticky="w")
|
442
|
|
443
|
# Gamma
|
444
|
tk.Label(params_frame, text="Gamma:").grid(row=2, column=0, sticky="e")
|
445
|
self.right_gamma = tk.Entry(params_frame)
|
446
|
self.right_gamma.insert(0, "1.0")
|
447
|
self.right_gamma.grid(row=2, column=1, pady=2, sticky="w")
|
448
|
|
449
|
# Image Side
|
450
|
tk.Label(params_frame, text="Image Side:").grid(row=3, column=0, sticky="e")
|
451
|
self.right_image_side = tk.IntVar(value=1) # Default to right
|
452
|
tk.Radiobutton(params_frame, text="Left", variable=self.right_image_side, value=0).grid(row=3, column=1, sticky="w")
|
453
|
tk.Radiobutton(params_frame, text="Right", variable=self.right_image_side, value=1).grid(row=3, column=1, padx=60, sticky="w")
|
454
|
|
455
|
# Transparency Factor
|
456
|
tk.Label(params_frame, text="Transparency Factor:").grid(row=4, column=0, sticky="e")
|
457
|
self.right_transparency_factor = tk.Entry(params_frame)
|
458
|
self.right_transparency_factor.insert(0, "1.0")
|
459
|
self.right_transparency_factor.grid(row=4, column=1, pady=2, sticky="w")
|
460
|
|
461
|
def setupSettingsTab(self):
|
462
|
frame = self.tab_settings
|
463
|
|
464
|
# Configurations can be saved or loaded here
|
465
|
saveConfig_btn = tk.Button(frame, text="Save Configuration", command=self.saveConfiguration)
|
466
|
saveConfig_btn.pack(pady=10)
|
467
|
|
468
|
load_config_btn = tk.Button(frame, text="Load Configuration", command=self.loadConfiguration)
|
469
|
load_config_btn.pack(pady=10)
|
470
|
|
471
|
def setupPreviewTab(self):
|
472
|
frame = self.tab_preview
|
473
|
|
474
|
# Labels to display images
|
475
|
self.original_image_label = tk.Label(frame, text="Original Image")
|
476
|
self.original_image_label.pack(side="left", padx=10, pady=10)
|
477
|
|
478
|
self.processed_image_label = tk.Label(frame, text="Processed Image")
|
479
|
self.processed_image_label.pack(side="right", padx=10, pady=10)
|
480
|
|
481
|
def selectLeftImage(self):
|
482
|
path = filedialog.askopenfilename(title="Select Left Image",
|
483
|
filetypes=[("Image files", "*.png *.jpg *.jpeg *.bmp")])
|
484
|
if path:
|
485
|
self.left_image_path = path
|
486
|
self.left_image_label.config(text=os.path.basename(path))
|
487
|
logging.info(f"Selected left image: {path}")
|
488
|
|
489
|
def selectRightImage(self):
|
490
|
path = filedialog.askopenfilename(title="Select Right Image",
|
491
|
filetypes=[("Image files", "*.png *.jpg *.jpeg *.bmp")])
|
492
|
if path:
|
493
|
self.right_image_path = path
|
494
|
self.right_image_label.config(text=os.path.basename(path))
|
495
|
logging.info(f"Selected right image: {path}")
|
496
|
|
497
|
def saveConfiguration(self):
|
498
|
config = ConfigReader()
|
499
|
|
500
|
# Left Image Parameters
|
501
|
if self.left_image_path:
|
502
|
config.setParameters({
|
503
|
'image_name': self.left_image_path,
|
504
|
'projected_image_width': self.left_projected_image_width.get(),
|
505
|
'projected_overlap_width': self.left_projected_overlap_width.get(),
|
506
|
'gamma': self.left_gamma.get(),
|
507
|
'image_side': self.left_image_side.get(),
|
508
|
'transparency_factor': self.left_transparency_factor.get()
|
509
|
})
|
510
|
|
511
|
# Right Image Parameters
|
512
|
if self.right_image_path:
|
513
|
config.setParameters({
|
514
|
'image_name': self.right_image_path,
|
515
|
'projected_image_width': self.right_projected_image_width.get(),
|
516
|
'projected_overlap_width': self.right_projected_overlap_width.get(),
|
517
|
'gamma': self.right_gamma.get(),
|
518
|
'image_side': self.right_image_side.get(),
|
519
|
'transparency_factor': self.right_transparency_factor.get()
|
520
|
})
|
521
|
|
522
|
# Save to file
|
523
|
save_path = filedialog.asksaveasfilename(title="Save Configuration",
|
524
|
defaultextension=".ini",
|
525
|
filetypes=[("INI files", "*.ini")])
|
526
|
if save_path:
|
527
|
config.saveConfig(save_path)
|
528
|
messagebox.showinfo("Success", f"Configuration saved to {save_path}")
|
529
|
logging.info(f"Configuration saved to {save_path}")
|
530
|
|
531
|
def loadConfiguration(self):
|
532
|
load_path = filedialog.askopenfilename(title="Load Configuration",
|
533
|
filetypes=[("INI files", "*.ini")])
|
534
|
if load_path and os.path.exists(load_path):
|
535
|
config = ConfigReader(load_path)
|
536
|
|
537
|
# Load left image parameters
|
538
|
self.left_image_path = config.getImageName()
|
539
|
if self.left_image_path and os.path.exists(self.left_image_path):
|
540
|
self.left_image_label.config(text=os.path.basename(self.left_image_path))
|
541
|
self.left_projected_image_width.delete(0, tk.END)
|
542
|
self.left_projected_image_width.insert(0, config.getProjectedImageWidth())
|
543
|
self.left_projected_overlap_width.delete(0, tk.END)
|
544
|
self.left_projected_overlap_width.insert(0, config.getProjectedOverlapWidth())
|
545
|
self.left_gamma.delete(0, tk.END)
|
546
|
self.left_gamma.insert(0, config.getGamma())
|
547
|
self.left_image_side.set(config.getImageSide())
|
548
|
self.left_transparency_factor.delete(0, tk.END)
|
549
|
self.left_transparency_factor.insert(0, config.getTransparencyFactor())
|
550
|
|
551
|
# Load right image parameters
|
552
|
self.right_image_path = config.getImageName()
|
553
|
if self.right_image_path and os.path.exists(self.right_image_path):
|
554
|
self.right_image_label.config(text=os.path.basename(self.right_image_path))
|
555
|
self.right_projected_image_width.delete(0, tk.END)
|
556
|
self.right_projected_image_width.insert(0, config.getProjectedImageWidth())
|
557
|
self.right_projected_overlap_width.delete(0, tk.END)
|
558
|
self.right_projected_overlap_width.insert(0, config.getProjectedOverlapWidth())
|
559
|
self.right_gamma.delete(0, tk.END)
|
560
|
self.right_gamma.insert(0, config.getGamma())
|
561
|
self.right_image_side.set(config.getImageSide())
|
562
|
self.right_transparency_factor.delete(0, tk.END)
|
563
|
self.right_transparency_factor.insert(0, config.getTransparencyFactor())
|
564
|
|
565
|
messagebox.showinfo("Success", f"Configuration loaded from {load_path}")
|
566
|
logging.info(f"Configuration loaded from {load_path}")
|
567
|
else:
|
568
|
messagebox.showerror("Error", "Failed to load configuration.")
|
569
|
|
570
|
def processAndSave(self):
|
571
|
# Validate inputs
|
572
|
if not self.left_image_path or not self.right_image_path:
|
573
|
messagebox.showerror("Error", "Please select both left and right images.")
|
574
|
return
|
575
|
|
576
|
# Collect parameters for left image
|
577
|
left_params = {
|
578
|
'image_name': self.left_image_path,
|
579
|
'projected_image_width': int(self.left_projected_image_width.get()),
|
580
|
'projected_overlap_width': int(self.left_projected_overlap_width.get()),
|
581
|
'gamma': float(self.left_gamma.get()),
|
582
|
'image_side': self.left_image_side.get(),
|
583
|
'transparency_factor': float(self.left_transparency_factor.get())
|
584
|
}
|
585
|
|
586
|
# Collect parameters for right image
|
587
|
right_params = {
|
588
|
'image_name': self.right_image_path,
|
589
|
'projected_image_width': int(self.right_projected_image_width.get()),
|
590
|
'projected_overlap_width': int(self.right_projected_overlap_width.get()),
|
591
|
'gamma': float(self.right_gamma.get()),
|
592
|
'image_side': self.right_image_side.get(),
|
593
|
'transparency_factor': float(self.right_transparency_factor.get())
|
594
|
}
|
595
|
|
596
|
# Initialize main display
|
597
|
main_display = MainDisplay()
|
598
|
|
599
|
# Process left image
|
600
|
self.progress_bar["value"] = 0
|
601
|
self.root.update_idletasks()
|
602
|
left_processed, left_name = processImage(self.left_image_path, left_params, main_display)
|
603
|
if left_processed is not None and left_name is not None:
|
604
|
self.processed_images[left_name] = left_processed
|
605
|
saveImage(left_processed, left_name)
|
606
|
self.progress_bar["value"] += 50
|
607
|
self.root.update_idletasks()
|
608
|
|
609
|
# Process right image
|
610
|
right_processed, right_name = processImage(self.right_image_path, right_params, main_display)
|
611
|
if right_processed is not None and right_name is not None:
|
612
|
self.processed_images[right_name] = right_processed
|
613
|
saveImage(right_processed, right_name)
|
614
|
self.progress_bar["value"] += 50
|
615
|
self.root.update_idletasks()
|
616
|
|
617
|
# Display images
|
618
|
self.displayPreview()
|
619
|
|
620
|
# Finalize progress
|
621
|
self.progress_bar["value"] = 100
|
622
|
self.root.update_idletasks()
|
623
|
messagebox.showinfo("Processing Complete", "Images processed and saved successfully.")
|
624
|
|
625
|
def displayPreview(self):
|
626
|
# Display the first processed image as preview
|
627
|
if self.processed_images:
|
628
|
# For simplicity, display the first image
|
629
|
name, img = next(iter(self.processed_images.items()))
|
630
|
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
631
|
pil_img = Image.fromarray(img_rgb)
|
632
|
pil_img = pil_img.resize((400, 300), Image.ANTIALIAS)
|
633
|
img_tk = ImageTk.PhotoImage(pil_img)
|
634
|
self.processed_image_label.config(image=img_tk)
|
635
|
self.processed_image_label.image = img_tk
|
636
|
|
637
|
# Load and display original image
|
638
|
original_img = cv2.imread(self.left_image_path if name == 'left' else self.right_image_path, cv2.IMREAD_COLOR)
|
639
|
if original_img is not None:
|
640
|
original_rgb = cv2.cvtColor(original_img, cv2.COLOR_BGR2RGB)
|
641
|
pil_original = Image.fromarray(original_rgb)
|
642
|
pil_original = pil_original.resize((400, 300), Image.ANTIALIAS)
|
643
|
original_tk = ImageTk.PhotoImage(pil_original)
|
644
|
self.original_image_label.config(image=original_tk)
|
645
|
self.original_image_label.image = original_tk
|
646
|
|
647
|
def displayImage(self, processed_images):
|
648
|
# This method can be expanded to display multiple images if needed
|
649
|
pass
|
650
|
|
651
|
def main():
|
652
|
|
653
|
root = tk.Tk()
|
654
|
|
655
|
app = ImageProcessingApp(root)
|
656
|
|
657
|
root.mainloop()
|
658
|
|
659
|
if __name__ == "__main__":
|
660
|
|
661
|
main()
|