1
|
#!/usr/bin/env python3
|
2
|
# -*- coding: utf-8 -*-
|
3
|
|
4
|
import configparser
|
5
|
import cv2
|
6
|
import numpy as np
|
7
|
import os
|
8
|
import logging
|
9
|
|
10
|
# Setup logging
|
11
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
12
|
|
13
|
|
14
|
# ConfigReader class definition
|
15
|
class ConfigReader:
|
16
|
def __init__(self, config_path):
|
17
|
self.config_parser = configparser.ConfigParser()
|
18
|
self.config_parser.read(config_path)
|
19
|
|
20
|
def getImageName(self):
|
21
|
return str(self.config_parser['DEFAULT']['image_name'])
|
22
|
|
23
|
def getProjectedImageWidth(self):
|
24
|
return int(self.config_parser['DEFAULT']['projected_image_width'])
|
25
|
|
26
|
def getProjectedOverlapWidth(self):
|
27
|
return int(self.config_parser['DEFAULT']['projected_overlap_width'])
|
28
|
|
29
|
def getGamma(self):
|
30
|
return float(self.config_parser['DEFAULT']['gamma'])
|
31
|
|
32
|
def getImageSide(self):
|
33
|
return int(self.config_parser['DEFAULT']['image_side'])
|
34
|
|
35
|
def getTransparencyFactor(self):
|
36
|
return float(self.config_parser['DEFAULT'].get('transparency_factor', 1.0))
|
37
|
|
38
|
|
39
|
# MainDisplay class definition
|
40
|
class MainDisplay:
|
41
|
def readImage(self, image_path):
|
42
|
# Loads an image from the specified path with 3 channels (BGR)
|
43
|
image = cv2.imread(image_path, cv2.IMREAD_COLOR)
|
44
|
if image is None:
|
45
|
logging.error(f"Error loading image at {image_path}")
|
46
|
else:
|
47
|
logging.info(f"Loaded image {image_path} with shape: {image.shape}")
|
48
|
return image
|
49
|
|
50
|
def setImage(self, image):
|
51
|
# Placeholder for image processing if necessary
|
52
|
# Here you can modify or prepare the image before displaying
|
53
|
logging.info(f"Setting image with shape: {image.shape}")
|
54
|
return image
|
55
|
|
56
|
|
57
|
# MaskCreator class definition
|
58
|
class MaskCreator:
|
59
|
def __init__(self, image, transparency_factor):
|
60
|
self.__image = image
|
61
|
self.__alpha_gradient = None
|
62
|
self.__gamma_corrected = None
|
63
|
self.result_image = None
|
64
|
self.__mask = None
|
65
|
self.transparency_factor = transparency_factor
|
66
|
|
67
|
def smoothstep(self, edge0, edge1, x):
|
68
|
# Smoothstep function for smoother transition (non-linear gradient)
|
69
|
x = np.clip((x - edge0) / (edge1 - edge0), 0, 1)
|
70
|
return x * x * (3 - 2 * x)
|
71
|
|
72
|
def create_mask(self, image_side, mask_width, image_width):
|
73
|
self.__mask = self.__image.shape[1] * mask_width // image_width
|
74
|
gradient = np.linspace(0, 1, self.__mask)
|
75
|
|
76
|
# Apply smoothstep to create a smoother gradient
|
77
|
smooth_gradient = self.smoothstep(0, 1, gradient)
|
78
|
|
79
|
# Apply transparency factor to adjust the strength of the blending
|
80
|
smooth_gradient = smooth_gradient * self.transparency_factor
|
81
|
|
82
|
if image_side == 1:
|
83
|
# Gradient from transparent to opaque (middle to right)
|
84
|
self.__alpha_gradient = smooth_gradient
|
85
|
elif image_side == 0:
|
86
|
# Gradient from opaque to transparent (left to middle)
|
87
|
self.__alpha_gradient = smooth_gradient[::-1] # Reverse for the left side
|
88
|
|
89
|
def gammaCorrection(self, gamma):
|
90
|
# Apply gamma correction
|
91
|
inv_gamma = 1.0 / gamma
|
92
|
table = np.array([((i / 255.0) ** inv_gamma) * 255
|
93
|
for i in np.arange(256)]).astype("uint8")
|
94
|
self.__gamma_corrected = cv2.LUT(self.__image, table)
|
95
|
logging.info(f"Applied gamma correction with gamma={gamma}")
|
96
|
|
97
|
# Save gamma corrected image for inspection
|
98
|
cv2.imwrite("gamma_corrected.png", self.__gamma_corrected)
|
99
|
|
100
|
def alpha_blending(self, image_side):
|
101
|
# Ensure the result image is a black background before blending
|
102
|
if image_side == 1: # Right side
|
103
|
# Set the area where blending will happen to black
|
104
|
self.result_image[:, :self.__mask] = 0 # Set the ROI to black
|
105
|
|
106
|
roi = self.result_image[:, :self.__mask].astype(np.float32)
|
107
|
gamma_corrected = self.__gamma_corrected[:, :self.__mask].astype(np.float32)
|
108
|
alpha = self.__alpha_gradient.reshape(1, -1, 1).astype(np.float32)
|
109
|
blended = (alpha * gamma_corrected + (1 - alpha) * roi)
|
110
|
blended = np.clip(blended, 0, 255).astype(np.uint8)
|
111
|
self.result_image[:, :self.__mask] = blended
|
112
|
logging.info(f"Applied alpha blending on the right side with mask width {self.__mask}")
|
113
|
# Save blended region for debugging
|
114
|
cv2.imwrite("blended_right_side.png", blended)
|
115
|
|
116
|
elif image_side == 0: # Left side
|
117
|
# Set the area where blending will happen to black
|
118
|
self.result_image[:, -self.__mask:] = 0 # Set the ROI to black
|
119
|
|
120
|
roi = self.result_image[:, -self.__mask:].astype(np.float32)
|
121
|
gamma_corrected = self.__gamma_corrected[:, -self.__mask:].astype(np.float32)
|
122
|
alpha = self.__alpha_gradient.reshape(1, -1, 1).astype(np.float32)
|
123
|
blended = (alpha * gamma_corrected + (1 - alpha) * roi)
|
124
|
blended = np.clip(blended, 0, 255).astype(np.uint8)
|
125
|
self.result_image[:, -self.__mask:] = blended
|
126
|
logging.info(f"Applied alpha blending on the left side with mask width {self.__mask}")
|
127
|
# Save blended region for debugging
|
128
|
cv2.imwrite("blended_left_side.png", blended)
|
129
|
|
130
|
|
131
|
def process_image(config_path, main_display):
|
132
|
"""
|
133
|
Processes an image based on the provided configuration.
|
134
|
|
135
|
Args:
|
136
|
config_path (str): Path to the configuration file.
|
137
|
main_display (MainDisplay): Instance of MainDisplay for image operations.
|
138
|
|
139
|
Returns:
|
140
|
tuple: Processed image and its corresponding name.
|
141
|
"""
|
142
|
# Load configuration
|
143
|
config_reader = ConfigReader(config_path)
|
144
|
|
145
|
# Retrieve configuration parameters
|
146
|
mask_width = config_reader.getProjectedOverlapWidth()
|
147
|
image_width = config_reader.getProjectedImageWidth()
|
148
|
gamma = config_reader.getGamma()
|
149
|
image_side = config_reader.getImageSide()
|
150
|
image_path = config_reader.getImageName()
|
151
|
transparency_factor = config_reader.getTransparencyFactor()
|
152
|
|
153
|
# Determine image side name
|
154
|
if image_side == 0:
|
155
|
image_name = 'left'
|
156
|
elif image_side == 1:
|
157
|
image_name = 'right'
|
158
|
else:
|
159
|
logging.error(f"Invalid ImageSide value in {config_path}. Use 0 for left image, 1 for right image.")
|
160
|
return None, None
|
161
|
|
162
|
# Load image
|
163
|
image = main_display.readImage(image_path)
|
164
|
if image is None:
|
165
|
logging.error(f"Image loading failed for {image_path}. Skipping...")
|
166
|
return None, None
|
167
|
|
168
|
# Initialize result image
|
169
|
result_image = main_display.setImage(image).copy()
|
170
|
cv2.imwrite("initial_result_image.png", result_image) # Save initial image
|
171
|
|
172
|
# Initialize MaskCreator
|
173
|
mask_creator = MaskCreator(image, transparency_factor)
|
174
|
|
175
|
# Apply image modifications
|
176
|
mask_creator.create_mask(image_side, mask_width, image_width)
|
177
|
mask_creator.gammaCorrection(gamma)
|
178
|
mask_creator.result_image = result_image
|
179
|
mask_creator.alpha_blending(image_side)
|
180
|
|
181
|
# Save final result for inspection
|
182
|
cv2.imwrite("final_result_image.png", mask_creator.result_image)
|
183
|
|
184
|
return mask_creator.result_image, image_name
|
185
|
|
186
|
|
187
|
def save_image(image, name):
|
188
|
"""
|
189
|
Save the processed image to the project folder.
|
190
|
|
191
|
Args:
|
192
|
image (np.ndarray): The image to be saved.
|
193
|
name (str): Name of the image (left or right).
|
194
|
"""
|
195
|
# Define the output path
|
196
|
output_dir = os.path.join(os.getcwd(), "processed_images")
|
197
|
os.makedirs(output_dir, exist_ok=True)
|
198
|
|
199
|
# Create the output file name
|
200
|
output_path = os.path.join(output_dir, f"processed_image_{name}.png")
|
201
|
|
202
|
# Save the image
|
203
|
cv2.imwrite(output_path, image)
|
204
|
logging.info(f"Saved processed image: {output_path}")
|
205
|
|
206
|
|
207
|
def main():
|
208
|
# Initialize MainDisplay
|
209
|
main_display = MainDisplay()
|
210
|
|
211
|
# Define configuration files for left and right images
|
212
|
config_files = ["config_left.ini", "config_right.ini"]
|
213
|
|
214
|
# Dictionary to store processed images
|
215
|
processed_images = {}
|
216
|
|
217
|
# Process each configuration
|
218
|
for config_file in config_files:
|
219
|
processed_image, image_name = process_image(config_file, main_display)
|
220
|
if processed_image is not None and image_name is not None:
|
221
|
processed_images[image_name] = processed_image
|
222
|
|
223
|
# Save all processed images to the project folder
|
224
|
for name, img in processed_images.items():
|
225
|
save_image(img, name)
|
226
|
|
227
|
|
228
|
if __name__ == "__main__":
|
229
|
main()
|