gokaygokay commited on
Commit
f6a9f5a
·
verified ·
1 Parent(s): d0de178

Create yellow_tint_cleaner.py

Browse files
Files changed (1) hide show
  1. yellow_tint_cleaner.py +213 -0
yellow_tint_cleaner.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image, ImageEnhance, ImageChops
2
+ import numpy as np
3
+
4
+ def normalize_gray(image: Image) -> Image:
5
+ """Normalize a grayscale image using histogram equalization."""
6
+ if image.mode != 'L':
7
+ image = image.convert('L')
8
+ img = np.asarray(image)
9
+ balanced_img = img.copy()
10
+ hist, bins = np.histogram(img.reshape(-1), 256, (0, 256))
11
+ bmin = np.min(np.where(hist > (hist.sum() * 0.0005)))
12
+ bmax = np.max(np.where(hist > (hist.sum() * 0.0005)))
13
+ balanced_img = np.clip(img, bmin, bmax)
14
+ balanced_img = ((balanced_img - bmin) / (bmax - bmin) * 255)
15
+ return Image.fromarray(balanced_img).convert('L')
16
+
17
+ def image_channel_split(image: Image, mode: str = 'RGBA') -> tuple:
18
+ """Split image into channels based on color mode."""
19
+ _image = image.convert('RGBA')
20
+ channel1 = Image.new('L', size=_image.size, color='black')
21
+ channel2 = Image.new('L', size=_image.size, color='black')
22
+ channel3 = Image.new('L', size=_image.size, color='black')
23
+ channel4 = Image.new('L', size=_image.size, color='black')
24
+
25
+ if mode == 'RGBA':
26
+ channel1, channel2, channel3, channel4 = _image.split()
27
+ elif mode == 'RGB':
28
+ channel1, channel2, channel3 = _image.convert('RGB').split()
29
+ elif mode == 'YCbCr':
30
+ channel1, channel2, channel3 = _image.convert('YCbCr').split()
31
+ elif mode == 'LAB':
32
+ channel1, channel2, channel3 = _image.convert('LAB').split()
33
+ elif mode == 'HSV':
34
+ channel1, channel2, channel3 = _image.convert('HSV').split()
35
+
36
+ return channel1, channel2, channel3, channel4
37
+
38
+ def image_channel_merge(channels: tuple, mode: str = 'RGB') -> Image:
39
+ """Merge channels back into an image based on color mode."""
40
+ channel1 = channels[0].convert('L')
41
+ channel2 = channels[1].convert('L')
42
+ channel3 = channels[2].convert('L')
43
+ channel4 = Image.new('L', size=channel1.size, color='white')
44
+
45
+ if mode == 'RGBA':
46
+ if len(channels) > 3:
47
+ channel4 = channels[3].convert('L')
48
+ ret_image = Image.merge('RGBA', [channel1, channel2, channel3, channel4])
49
+ elif mode == 'RGB':
50
+ ret_image = Image.merge('RGB', [channel1, channel2, channel3])
51
+ elif mode == 'YCbCr':
52
+ ret_image = Image.merge('YCbCr', [channel1, channel2, channel3]).convert('RGB')
53
+ elif mode == 'LAB':
54
+ ret_image = Image.merge('LAB', [channel1, channel2, channel3]).convert('RGB')
55
+ elif mode == 'HSV':
56
+ ret_image = Image.merge('HSV', [channel1, channel2, channel3]).convert('RGB')
57
+
58
+ return ret_image
59
+
60
+ def balance_to_gamma(balance: int) -> float:
61
+ """Convert color balance value to gamma value."""
62
+ return 0.00005 * balance * balance - 0.01 * balance + 1
63
+
64
+ def gamma_trans(image: Image, gamma: float) -> Image:
65
+ """Apply gamma correction to an image."""
66
+ if gamma == 1.0:
67
+ return image
68
+ img_array = np.array(image)
69
+ img_array = np.power(img_array / 255.0, gamma) * 255.0
70
+ return Image.fromarray(img_array.astype(np.uint8))
71
+
72
+ def RGB2RGBA(image: Image, mask: Image) -> Image:
73
+ """Convert RGB image to RGBA using provided mask."""
74
+ if image.mode != 'RGB':
75
+ image = image.convert('RGB')
76
+ if mask.mode != 'L':
77
+ mask = mask.convert('L')
78
+ return Image.merge('RGBA', (*image.split(), mask))
79
+
80
+ def chop_image_v2(background_image: Image, layer_image: Image, blend_mode: str, opacity: int) -> Image:
81
+ """Blend two images together with specified blend mode and opacity."""
82
+ if background_image.mode != 'RGB':
83
+ background_image = background_image.convert('RGB')
84
+ if layer_image.mode != 'RGB':
85
+ layer_image = layer_image.convert('RGB')
86
+
87
+ # Convert opacity to float (0-1)
88
+ opacity = opacity / 100.0
89
+
90
+ # Create a copy of the background image
91
+ result = background_image.copy()
92
+
93
+ # Apply blend mode
94
+ if blend_mode == "normal":
95
+ result = Image.blend(background_image, layer_image, opacity)
96
+ elif blend_mode == "multiply":
97
+ result = ImageChops.multiply(background_image, layer_image)
98
+ result = Image.blend(background_image, result, opacity)
99
+ elif blend_mode == "screen":
100
+ result = ImageChops.screen(background_image, layer_image)
101
+ result = Image.blend(background_image, result, opacity)
102
+ elif blend_mode == "overlay":
103
+ result = ImageChops.overlay(background_image, layer_image)
104
+ result = Image.blend(background_image, result, opacity)
105
+
106
+ return result
107
+
108
+ def auto_adjust(image: Image, strength: int = 100, brightness: int = 0,
109
+ contrast: int = 0, saturation: int = 0,
110
+ red: int = 0, green: int = 0, blue: int = 0,
111
+ mode: str = 'RGB') -> Image:
112
+ """
113
+ Apply automatic adjustments to an image.
114
+
115
+ Args:
116
+ image: PIL Image to adjust
117
+ strength: Overall strength of the adjustment (0-100)
118
+ brightness: Brightness adjustment (-100 to 100)
119
+ contrast: Contrast adjustment (-100 to 100)
120
+ saturation: Saturation adjustment (-100 to 100)
121
+ red: Red channel adjustment (-100 to 100)
122
+ green: Green channel adjustment (-100 to 100)
123
+ blue: Blue channel adjustment (-100 to 100)
124
+ mode: Color mode for processing ('RGB', 'lum + sat', 'luminance', 'saturation', 'mono')
125
+
126
+ Returns:
127
+ Adjusted PIL Image
128
+ """
129
+ def auto_level_gray(image):
130
+ """Apply auto levels to a grayscale image."""
131
+ gray_image = Image.new("L", image.size, color='gray')
132
+ gray_image.paste(image.convert('L'))
133
+ return normalize_gray(gray_image)
134
+
135
+ # Calculate adjustment factors
136
+ if brightness < 0:
137
+ brightness_offset = brightness / 100 + 1
138
+ else:
139
+ brightness_offset = brightness / 50 + 1
140
+
141
+ if contrast < 0:
142
+ contrast_offset = contrast / 100 + 1
143
+ else:
144
+ contrast_offset = contrast / 50 + 1
145
+
146
+ if saturation < 0:
147
+ saturation_offset = saturation / 100 + 1
148
+ else:
149
+ saturation_offset = saturation / 50 + 1
150
+
151
+ # Get color channel gammas
152
+ red_gamma = balance_to_gamma(red)
153
+ green_gamma = balance_to_gamma(green)
154
+ blue_gamma = balance_to_gamma(blue)
155
+
156
+ # Process image based on mode
157
+ if mode == 'RGB':
158
+ r, g, b, _ = image_channel_split(image, mode='RGB')
159
+ r = auto_level_gray(r)
160
+ g = auto_level_gray(g)
161
+ b = auto_level_gray(b)
162
+ ret_image = image_channel_merge((r, g, b), 'RGB')
163
+ elif mode == 'lum + sat':
164
+ h, s, v, _ = image_channel_split(image, mode='HSV')
165
+ s = auto_level_gray(s)
166
+ ret_image = image_channel_merge((h, s, v), 'HSV')
167
+ l, a, b, _ = image_channel_split(ret_image, mode='LAB')
168
+ l = auto_level_gray(l)
169
+ ret_image = image_channel_merge((l, a, b), 'LAB')
170
+ elif mode == 'luminance':
171
+ l, a, b, _ = image_channel_split(image, mode='LAB')
172
+ l = auto_level_gray(l)
173
+ ret_image = image_channel_merge((l, a, b), 'LAB')
174
+ elif mode == 'saturation':
175
+ h, s, v, _ = image_channel_split(image, mode='HSV')
176
+ s = auto_level_gray(s)
177
+ ret_image = image_channel_merge((h, s, v), 'HSV')
178
+ else: # mono
179
+ gray = image.convert('L')
180
+ ret_image = auto_level_gray(gray).convert('RGB')
181
+
182
+ # Apply color channel adjustments if not in mono mode
183
+ if (red or green or blue) and mode != "mono":
184
+ r, g, b, _ = image_channel_split(ret_image, mode='RGB')
185
+ if red:
186
+ r = gamma_trans(r, red_gamma).convert('L')
187
+ if green:
188
+ g = gamma_trans(g, green_gamma).convert('L')
189
+ if blue:
190
+ b = gamma_trans(b, blue_gamma).convert('L')
191
+ ret_image = image_channel_merge((r, g, b), 'RGB')
192
+
193
+ # Apply brightness, contrast, and saturation
194
+ if brightness:
195
+ brightness_image = ImageEnhance.Brightness(ret_image)
196
+ ret_image = brightness_image.enhance(factor=brightness_offset)
197
+
198
+ if contrast:
199
+ contrast_image = ImageEnhance.Contrast(ret_image)
200
+ ret_image = contrast_image.enhance(factor=contrast_offset)
201
+
202
+ if saturation:
203
+ color_image = ImageEnhance.Color(ret_image)
204
+ ret_image = color_image.enhance(factor=saturation_offset)
205
+
206
+ # Blend with original image based on strength
207
+ ret_image = chop_image_v2(image, ret_image, blend_mode="normal", opacity=strength)
208
+
209
+ # Handle RGBA mode
210
+ if image.mode == 'RGBA':
211
+ ret_image = RGB2RGBA(ret_image, image.split()[-1])
212
+
213
+ return ret_image