|
1
|
#!/usr/bin/env python
|
|
2
|
# -*- coding: utf-8 -*-
|
|
3
|
|
|
4
|
import cv2
|
|
5
|
import numpy as np
|
|
6
|
import os
|
|
7
|
from config_reader import ConfigReader
|
|
8
|
|
|
9
|
|
|
10
|
class MainAlphaBlender(object):
|
|
11
|
def __init__(self, config_path="config.json"):
|
|
12
|
try:
|
|
13
|
self.__config_reader = ConfigReader(config_path)
|
|
14
|
self.blend_width = self.__config_reader.get_blend_width()
|
|
15
|
self.gamma_value = self.__config_reader.get_gamma_value()
|
|
16
|
self.method = self.__config_reader.get_blend_method()
|
|
17
|
self.output_dir = self.__config_reader.get_output_dir()
|
|
18
|
self.preview = self.__config_reader.get_preview()
|
|
19
|
self.image_path = self.__config_reader.get_image_path()
|
|
20
|
except FileNotFoundError:
|
|
21
|
self.blend_width = 200
|
|
22
|
self.gamma_value = 1.4
|
|
23
|
self.method = "cosine"
|
|
24
|
self.output_dir = "Results"
|
|
25
|
self.preview = True
|
|
26
|
self.image_path = "OriginalImages"
|
|
27
|
self.update_paths()
|
|
28
|
|
|
29
|
def update_paths(self):
|
|
30
|
self.left_image_path = os.path.join(self.image_path, "Left.jpg")
|
|
31
|
self.right_image_path = os.path.join(self.image_path, "Right.jpg")
|
|
32
|
|
|
33
|
def create_alpha_gradient(self, blend_width, side, method="cosine"):
|
|
34
|
if method == 'linear':
|
|
35
|
alpha_gradient = np.linspace(0, 1, blend_width)
|
|
36
|
elif method == 'cosine':
|
|
37
|
t = np.linspace(0, np.pi, blend_width)
|
|
38
|
alpha_gradient = (1 - np.cos(t**0.85)) / 2
|
|
39
|
elif method == 'quadratic':
|
|
40
|
t = np.linspace(0, 1, blend_width)
|
|
41
|
alpha_gradient = t**2
|
|
42
|
elif method == 'sqrt':
|
|
43
|
t = np.linspace(0, 1, blend_width)
|
|
44
|
alpha_gradient = np.sqrt(t)
|
|
45
|
elif method == 'log':
|
|
46
|
t = np.linspace(0, 1, blend_width)
|
|
47
|
alpha_gradient = np.log1p(9 * t) / np.log1p(9)
|
|
48
|
elif method == 'sigmoid':
|
|
49
|
t = np.linspace(0, 1, blend_width)
|
|
50
|
alpha_gradient = 1 / (1 + np.exp(-12 * (t - 0.5)))
|
|
51
|
alpha_gradient = (alpha_gradient - alpha_gradient.min()) / (alpha_gradient.max() - alpha_gradient.min())
|
|
52
|
else:
|
|
53
|
raise ValueError("Invalid method: choose from 'linear', 'cosine', 'quadratic', 'sqrt', 'log', or 'sigmoid'")
|
|
54
|
if side == 'right':
|
|
55
|
alpha_gradient = 1 - alpha_gradient
|
|
56
|
return alpha_gradient
|
|
57
|
|
|
58
|
def gamma_correction(self, image, gamma):
|
|
59
|
img_float = image.astype(np.float32) / 255.0
|
|
60
|
mean_intensity = np.mean(img_float)
|
|
61
|
adaptive_gamma = gamma * (0.5 / (mean_intensity + 1e-5))
|
|
62
|
adaptive_gamma = np.clip(adaptive_gamma, 0.8, 2.0)
|
|
63
|
corrected = np.power(img_float, 1.0 / adaptive_gamma)
|
|
64
|
return np.uint8(np.clip(corrected * 255, 0, 255))
|
|
65
|
|
|
66
|
def alpha_blend_edge(self, image, blend_width, side, method="cosine"):
|
|
67
|
height, width, _ = image.shape
|
|
68
|
blended_image = image.copy()
|
|
69
|
alpha_gradient = self.create_alpha_gradient(blend_width, side, method)
|
|
70
|
if side == 'right':
|
|
71
|
roi = blended_image[:, width - blend_width:]
|
|
72
|
elif side == 'left':
|
|
73
|
roi = blended_image[:, :blend_width]
|
|
74
|
else:
|
|
75
|
raise ValueError("Side must be 'left' or 'right'")
|
|
76
|
gradient_3d = alpha_gradient[np.newaxis, :, np.newaxis]
|
|
77
|
gradient_3d = np.tile(gradient_3d, (height, 1, 3))
|
|
78
|
if side == 'right':
|
|
79
|
blended_image[:, width - blend_width:] = (roi * gradient_3d).astype(np.uint8)
|
|
80
|
else:
|
|
81
|
blended_image[:, :blend_width] = (roi * gradient_3d).astype(np.uint8)
|
|
82
|
return blended_image
|
|
83
|
|
|
84
|
def show_preview(self, left_image, right_image, scale=0.5):
|
|
85
|
h = min(left_image.shape[0], right_image.shape[0])
|
|
86
|
left_resized = cv2.resize(left_image, (int(left_image.shape[1]*scale), int(h*scale)))
|
|
87
|
right_resized = cv2.resize(right_image, (int(right_image.shape[1]*scale), int(h*scale)))
|
|
88
|
combined = np.hstack((left_resized, right_resized))
|
|
89
|
cv2.imshow("Preview (Left + Right)", combined)
|
|
90
|
cv2.waitKey(0)
|
|
91
|
cv2.destroyAllWindows()
|
|
92
|
|
|
93
|
def run(self):
|
|
94
|
try:
|
|
95
|
os.makedirs(self.output_dir, exist_ok=True)
|
|
96
|
left_img = cv2.imread(self.left_image_path, cv2.IMREAD_COLOR)
|
|
97
|
right_img = cv2.imread(self.right_image_path, cv2.IMREAD_COLOR)
|
|
98
|
if left_img is None or right_img is None:
|
|
99
|
raise FileNotFoundError(f"Could not read images from '{self.image_path}'. Check path.")
|
|
100
|
left_blended = self.alpha_blend_edge(left_img, self.blend_width, side='right', method=self.method)
|
|
101
|
right_blended = self.alpha_blend_edge(right_img, self.blend_width, side='left', method=self.method)
|
|
102
|
left_gamma = self.gamma_correction(left_blended, self.gamma_value)
|
|
103
|
right_gamma = self.gamma_correction(right_blended, self.gamma_value)
|
|
104
|
left_output_path = os.path.join(self.output_dir, f"{self.method}_left_gamma.jpg")
|
|
105
|
right_output_path = os.path.join(self.output_dir, f"{self.method}_right_gamma.jpg")
|
|
106
|
cv2.imwrite(left_output_path, left_gamma)
|
|
107
|
cv2.imwrite(right_output_path, right_gamma)
|
|
108
|
if self.preview:
|
|
109
|
self.show_preview(left_gamma, right_gamma)
|
|
110
|
return (True, f"Images saved successfully in '{self.output_dir}'.")
|
|
111
|
except (FileNotFoundError, ValueError) as e:
|
|
112
|
return (False, str(e))
|
|
113
|
except Exception as e:
|
|
114
|
return (False, f"An unexpected error occurred: {e}")
|
|
115
|
finally:
|
|
116
|
cv2.destroyAllWindows()
|
|
117
|
|