Project

General

Profile

Files » main_alpha_blender.py

Zhi Jie YEW, 11/06/2025 02:43 PM

 
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

    
(4-4/8)