File size: 5,962 Bytes
29fa6f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d947afe
 
29fa6f4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# CS5330 Lab 4
# This file includes functions for inserting a person into a pair of stereoscopic images

import os
from PIL import Image, ImageEnhance, ImageFilter
from enum import Enum
from segmentation import extract_person

# Define an enum class for the background (background must be chosen from the three provided images)
class Background(Enum):
    NATURE = os.path.join('background', 'spatial_sbs_2024-10-10_21-57-07-266Z.jpg')
    NEU = os.path.join('background', 'sbs_neu.jpg')
    GASTOWN = os.path.join('background', 'side_by_side_steam_clock.jpg')

    def get_background(self):
        return Image.open(self.value)


# Define an enum class for disparity (disparity must be chosen from close, medium or far)
class Disparity(Enum):
    CLOSE = 30
    MED = 50
    FAR = 100


# Add person onto background image
def overlay_person(background, person, x_pos, y_pos):
    background = background.convert("RGBA")
    person = person.convert("RGBA")

    # Match lighting and color between the background and the person
    match_lighting_and_color(background, person)

    # Paste the person onto the background
    background.paste(person, (x_pos, y_pos), person)

    return background


# Scale the person according to disparity to enhance depth illusion
def scale_person(person, disparity):
    # Define a scaling factor based on disparity
    if disparity == Disparity.FAR:
        scaling_factor = 0.75
    elif disparity == Disparity.MED:
        scaling_factor = 0.9
    else:
        scaling_factor = 1

    new_width = int(person.size[0] * scaling_factor)
    new_height = int(person.size[1] * scaling_factor)
    scaled_person = person.resize((new_width, new_height))
    return scaled_person


# Function to match color and lighting
def match_lighting_and_color(background, person):
    # Match brightness
    brightness_factor = get_brightness_factor(background, person)
    person = ImageEnhance.Brightness(person).enhance(brightness_factor)
    
    # Match contrast
    contrast_factor = get_contrast_factor(background, person)
    person = ImageEnhance.Contrast(person).enhance(contrast_factor)

    return person


# Calculate the ratio between background brightness and person brightness
def get_brightness_factor(background, person):
    # Get the average brightness of both images
    background_avg_brightness = calculate_avg_brightness(background)
    person_avg_brightness = calculate_avg_brightness(person)
    
    # Return the ratio of the brightness levels
    return background_avg_brightness / person_avg_brightness


# Calculate the ratio between background contrast and person contrast
def get_contrast_factor(background, person):
    # Get the average contrast of both images
    background_avg_contrast = calculate_avg_contrast(background)
    person_avg_contrast = calculate_avg_contrast(person)
    
    # Return the ratio of the contrast levels
    return background_avg_contrast / person_avg_contrast

# Calculate the average brightness of an image
def calculate_avg_brightness(image):
    grayscale_image = image.convert('L')
    pixels = list(grayscale_image.getdata())
    return sum(pixels) / len(pixels)

# Calculate the average contrast of an image
def calculate_avg_contrast(image):
    grayscale_image = image.convert('L')
    contrast_image = grayscale_image.filter(ImageFilter.FIND_EDGES)
    pixels = list(contrast_image.getdata())
    return sum(pixels) / len(pixels)


# Place the person on the pair of stereoscopic images
# Depth perception can be manipulated by adjusting disparity
# background: an enum, must be chosen from the three provided background
# person: the extracted person as a transparent png
# disparity: an enum, must be chosen from the close, medium or far
# The bottom of the person is always going to align with the bottom of the background
def insert_person_in_pair(background: Background, person, disparity: Disparity):
    # Get the background image according to the choice
    background_image = background.get_background()
    # Split the stereoscopic image into left and right images
    width, height = background_image.size
    left_image = background_image.crop((0, 0, width // 2, height))
    right_image = background_image.crop((width // 2, 0, width, height))
    person_height = person.size[1]

    # Define an appropriate position for the person according to background and depth perception
    # Ensure that the person appears at an appropriate position and blends in the background naturally
    y_pos = height - person_height
    if background == Background.NATURE:
        x_pos = 1750
        if disparity == Disparity.MED:
            x_pos += 200
            y_pos -= 25
        elif disparity == Disparity.FAR:
            x_pos += 300
            y_pos -= 200
    elif background == Background.NEU:
        x_pos = 1500
        if disparity == Disparity.MED:
            x_pos += 200
            y_pos += 50
        elif disparity == Disparity.FAR:
            x_pos += 300
            y_pos += 100
    else:
        x_pos = 350
        if disparity == Disparity.MED:
            x_pos += 300
            y_pos -= 25
        elif disparity == Disparity.FAR:
            x_pos += 400
            y_pos -= 50
    
    # Scale the person to enhance depth illusion
    person = scale_person(person, disparity)

    # Calculate position for the person in the right image based on disparity
    x_pos_right = x_pos - disparity.value

    # Overlay the person onto left and right images at calculated positions
    left_with_person = overlay_person(left_image.copy(), person, x_pos, y_pos)
    right_with_person = overlay_person(right_image.copy(), person, x_pos_right, y_pos)

    return left_with_person, right_with_person


# ==========Test case==========
# person_image = extract_person(Image.open('test_img/mona_lisa.jpg'))

# left, right = insert_person_in_pair(
#     background=Background.NATURE,
#     person=person_image,
#     disparity=Disparity.MED
# )