sinanonur commited on
Commit
fb45150
·
1 Parent(s): 6a32dba

updated script to be used as library

Browse files
Files changed (1) hide show
  1. film_simulation.py +41 -44
film_simulation.py CHANGED
@@ -31,7 +31,7 @@ def create_curve(curve_data):
31
  def load_film_profiles_from_json(json_path):
32
  with open(json_path, 'r') as f:
33
  profiles_data = json.load(f)
34
-
35
  profiles = {}
36
  for name, data in profiles_data.items():
37
  color_curves = {
@@ -60,7 +60,6 @@ def apply_color_curves(image, curves):
60
  return result
61
 
62
  def interpolate_circular(x, y, new_x):
63
- # Interpolate considering the circular nature of hue values (0-360 degrees)
64
  x_extended = np.concatenate((x, x + 360))
65
  y_extended = np.concatenate((y, y))
66
  interp_func = interp1d(x_extended, y_extended, kind='cubic')
@@ -73,33 +72,24 @@ def apply_advanced_curve(image, advanced_curve):
73
  hue_shifts = np.array(advanced_curve['hue_shifts'])
74
  value_multipliers = np.array(advanced_curve['value_multipliers'])
75
 
76
- hue = hsv_image[:,:,0] * 360 # Convert to degrees
77
  saturation = hsv_image[:,:,1]
78
  value = hsv_image[:,:,2]
79
 
80
- # Interpolate saturation multipliers
81
  interp_saturation_multipliers = interpolate_circular(hue_values, saturation_multipliers, hue)
82
-
83
- # Apply saturation multipliers with a curve to prevent blowout
84
  max_saturation = 1.0
85
  interp_saturation_multipliers = np.clip(interp_saturation_multipliers, 0, max_saturation / saturation)
86
-
87
  saturation *= interp_saturation_multipliers
88
 
89
- # Interpolate hue shifts and apply them
90
  interp_hue_shifts = interpolate_circular(hue_values, hue_shifts, hue)
91
  hue = (hue + interp_hue_shifts) % 360
92
 
93
- # Interpolate value multipliers and apply them
94
  interp_value_multipliers = interpolate_circular(hue_values, value_multipliers, hue)
95
-
96
- # Apply value multipliers with a curve to prevent blowout
97
  max_value = 1.0
98
  interp_value_multipliers = np.clip(interp_value_multipliers, 0, max_value / value)
99
-
100
  value *= interp_value_multipliers
101
 
102
- hsv_image[:,:,0] = hue / 360 # Convert back to [0, 1] range
103
  hsv_image[:,:,1] = saturation
104
  hsv_image[:,:,2] = value
105
 
@@ -108,23 +98,23 @@ def apply_advanced_curve(image, advanced_curve):
108
  def apply_chromatic_aberration_pil(img, strength):
109
  width, height = img.size
110
  center_x, center_y = width // 2, height // 2
111
-
112
  r, g, b = img.split()
113
-
114
  def create_displacement(x, y):
115
  return int(strength * ((x - center_x) ** 2 + (y - center_y) ** 2) ** 0.5 / (width + height))
116
-
117
  r = r.transform(img.size, Image.AFFINE, (1, 0, create_displacement(0, 0), 0, 1, 0))
118
  b = b.transform(img.size, Image.AFFINE, (1, 0, -create_displacement(0, 0), 0, 1, 0))
119
-
120
  return Image.merge("RGB", (r, g, b))
121
 
122
  def add_film_grain(image, amount=0.1, size=1):
123
  width, height = image.size
124
- grain = np.random.normal(0, amount, (height//size, width//size, 3))
125
  grain = np.repeat(np.repeat(grain, size, axis=0), size, axis=1)
126
  grain = grain[:height, :width, :]
127
-
128
  img_array = np.array(image).astype(np.float32) / 255.0
129
  grainy_image = np.clip(img_array + grain, 0, 1) * 255
130
  return Image.fromarray(grainy_image.astype(np.uint8))
@@ -133,40 +123,39 @@ def adjust_color_temperature(image, temperature):
133
  r_multiplier = 1 + (temperature - 6500) / 100 * 0.01
134
  b_multiplier = 1 - (temperature - 6500) / 100 * 0.01
135
  g_multiplier = 1
136
-
137
  r, g, b = image.split()
138
-
139
  r = r.point(lambda i: min(255, int(i * r_multiplier)))
140
  g = g.point(lambda i: min(255, int(i * g_multiplier)))
141
  b = b.point(lambda i: min(255, int(i * b_multiplier)))
142
-
143
  return Image.merge('RGB', (r, g, b))
144
 
145
  def apply_base_color(image, base_color):
146
  base = Image.new('RGB', image.size, base_color)
147
- return Image.blend(image, base, 0.1) # Adjust blend factor as needed
148
 
149
  def cross_process(image):
150
  contrast_enhancer = ImageEnhance.Contrast(image)
151
  image = contrast_enhancer.enhance(1.5)
152
-
153
  r, g, b = image.split()
154
  r = r.point(lambda i: min(255, int(i * 1.2)))
155
  g = g.point(lambda i: int(i * 0.9))
156
  b = b.point(lambda i: min(255, int(i * 1.1)))
157
-
158
  image = Image.merge('RGB', (r, g, b))
159
-
160
  saturation_enhancer = ImageEnhance.Color(image)
161
  image = saturation_enhancer.enhance(1.3)
162
-
163
  return image
164
 
165
  def apply_film_profile(img, profile, chroma_override=None, blur_override=None, color_temp=6500, cross_process_flag=False, curve_type="auto"):
166
  img_array = np.array(img).astype(np.float32) / 255.0
167
-
168
  img_linear = colour.models.eotf_sRGB(img_array)
169
-
170
  if curve_type == "advanced" or (curve_type == "auto" and profile.advanced_curve):
171
  img_color_adjusted = apply_advanced_curve(img_linear, profile.advanced_curve)
172
  elif curve_type == "color" or (curve_type == "auto" and not profile.advanced_curve):
@@ -176,45 +165,53 @@ def apply_film_profile(img, profile, chroma_override=None, blur_override=None, c
176
  img_color_adjusted = apply_color_curves(img_linear, profile.color_curves)
177
  if profile.advanced_curve:
178
  img_color_adjusted = apply_advanced_curve(img_color_adjusted, profile.advanced_curve)
179
-
180
  img_srgb = colour.models.eotf_inverse_sRGB(img_color_adjusted)
181
-
182
  img_pil = Image.fromarray((img_srgb * 255).astype(np.uint8))
183
-
184
  enhancer = ImageEnhance.Contrast(img_pil)
185
  img_contrast = enhancer.enhance(profile.contrast)
186
-
187
  enhancer = ImageEnhance.Color(img_contrast)
188
  img_saturated = enhancer.enhance(profile.saturation)
189
-
190
  chroma_strength = chroma_override if chroma_override is not None else profile.chromatic_aberration
191
  if chroma_strength > 0:
192
  img_saturated = apply_chromatic_aberration_pil(img_saturated, chroma_strength)
193
-
194
  blur_amount = blur_override if blur_override is not None else profile.blur
195
  if blur_amount > 0:
196
  img_saturated = img_saturated.filter(ImageFilter.GaussianBlur(radius=blur_amount))
197
-
198
  img_saturated = apply_base_color(img_saturated, profile.base_color)
199
-
200
  img_saturated = add_film_grain(img_saturated, amount=profile.grain_amount, size=profile.grain_size)
201
-
202
  img_saturated = adjust_color_temperature(img_saturated, color_temp)
203
-
204
  if cross_process_flag:
205
  img_saturated = cross_process(img_saturated)
206
-
207
  return img_saturated
208
 
 
 
 
 
 
 
 
 
 
 
 
209
  def process_image(args):
210
  image_path, profile, chroma_override, blur_override, color_temp, cross_process_flag, curve_type, output_filename = args
211
  with Image.open(image_path) as img:
212
  exif_data = img.getexif()
213
-
214
  orientation = exif_data.get(274, 1)
215
  if orientation in [3, 6, 8]:
216
  img = img.rotate({3: 180, 6: 270, 8: 90}[orientation], expand=True)
217
-
218
  processed_image = apply_film_profile(img, profile, chroma_override, blur_override, color_temp, cross_process_flag, curve_type)
219
 
220
  if exif_data:
@@ -231,12 +228,12 @@ def get_memory_usage():
231
  def get_optimal_pool_size(target_memory_usage=75):
232
  available_memory = 100 - get_memory_usage()
233
  cpu_count = multiprocessing.cpu_count()
234
-
235
  for i in range(cpu_count, 0, -1):
236
  estimated_memory_usage = get_memory_usage() + (available_memory / cpu_count) * i
237
  if estimated_memory_usage <= target_memory_usage:
238
  return i
239
-
240
  return 1
241
 
242
  if __name__ == "__main__":
 
31
  def load_film_profiles_from_json(json_path):
32
  with open(json_path, 'r') as f:
33
  profiles_data = json.load(f)
34
+
35
  profiles = {}
36
  for name, data in profiles_data.items():
37
  color_curves = {
 
60
  return result
61
 
62
  def interpolate_circular(x, y, new_x):
 
63
  x_extended = np.concatenate((x, x + 360))
64
  y_extended = np.concatenate((y, y))
65
  interp_func = interp1d(x_extended, y_extended, kind='cubic')
 
72
  hue_shifts = np.array(advanced_curve['hue_shifts'])
73
  value_multipliers = np.array(advanced_curve['value_multipliers'])
74
 
75
+ hue = hsv_image[:,:,0] * 360
76
  saturation = hsv_image[:,:,1]
77
  value = hsv_image[:,:,2]
78
 
 
79
  interp_saturation_multipliers = interpolate_circular(hue_values, saturation_multipliers, hue)
 
 
80
  max_saturation = 1.0
81
  interp_saturation_multipliers = np.clip(interp_saturation_multipliers, 0, max_saturation / saturation)
 
82
  saturation *= interp_saturation_multipliers
83
 
 
84
  interp_hue_shifts = interpolate_circular(hue_values, hue_shifts, hue)
85
  hue = (hue + interp_hue_shifts) % 360
86
 
 
87
  interp_value_multipliers = interpolate_circular(hue_values, value_multipliers, hue)
 
 
88
  max_value = 1.0
89
  interp_value_multipliers = np.clip(interp_value_multipliers, 0, max_value / value)
 
90
  value *= interp_value_multipliers
91
 
92
+ hsv_image[:,:,0] = hue / 360
93
  hsv_image[:,:,1] = saturation
94
  hsv_image[:,:,2] = value
95
 
 
98
  def apply_chromatic_aberration_pil(img, strength):
99
  width, height = img.size
100
  center_x, center_y = width // 2, height // 2
101
+
102
  r, g, b = img.split()
103
+
104
  def create_displacement(x, y):
105
  return int(strength * ((x - center_x) ** 2 + (y - center_y) ** 2) ** 0.5 / (width + height))
106
+
107
  r = r.transform(img.size, Image.AFFINE, (1, 0, create_displacement(0, 0), 0, 1, 0))
108
  b = b.transform(img.size, Image.AFFINE, (1, 0, -create_displacement(0, 0), 0, 1, 0))
109
+
110
  return Image.merge("RGB", (r, g, b))
111
 
112
  def add_film_grain(image, amount=0.1, size=1):
113
  width, height = image.size
114
+ grain = np.random.normal(0, amount, (height//size + 1, width//size + 1, 3))
115
  grain = np.repeat(np.repeat(grain, size, axis=0), size, axis=1)
116
  grain = grain[:height, :width, :]
117
+
118
  img_array = np.array(image).astype(np.float32) / 255.0
119
  grainy_image = np.clip(img_array + grain, 0, 1) * 255
120
  return Image.fromarray(grainy_image.astype(np.uint8))
 
123
  r_multiplier = 1 + (temperature - 6500) / 100 * 0.01
124
  b_multiplier = 1 - (temperature - 6500) / 100 * 0.01
125
  g_multiplier = 1
126
+
127
  r, g, b = image.split()
128
+
129
  r = r.point(lambda i: min(255, int(i * r_multiplier)))
130
  g = g.point(lambda i: min(255, int(i * g_multiplier)))
131
  b = b.point(lambda i: min(255, int(i * b_multiplier)))
132
+
133
  return Image.merge('RGB', (r, g, b))
134
 
135
  def apply_base_color(image, base_color):
136
  base = Image.new('RGB', image.size, base_color)
137
+ return Image.blend(image, base, 0.1)
138
 
139
  def cross_process(image):
140
  contrast_enhancer = ImageEnhance.Contrast(image)
141
  image = contrast_enhancer.enhance(1.5)
142
+
143
  r, g, b = image.split()
144
  r = r.point(lambda i: min(255, int(i * 1.2)))
145
  g = g.point(lambda i: int(i * 0.9))
146
  b = b.point(lambda i: min(255, int(i * 1.1)))
147
+
148
  image = Image.merge('RGB', (r, g, b))
149
+
150
  saturation_enhancer = ImageEnhance.Color(image)
151
  image = saturation_enhancer.enhance(1.3)
152
+
153
  return image
154
 
155
  def apply_film_profile(img, profile, chroma_override=None, blur_override=None, color_temp=6500, cross_process_flag=False, curve_type="auto"):
156
  img_array = np.array(img).astype(np.float32) / 255.0
 
157
  img_linear = colour.models.eotf_sRGB(img_array)
158
+
159
  if curve_type == "advanced" or (curve_type == "auto" and profile.advanced_curve):
160
  img_color_adjusted = apply_advanced_curve(img_linear, profile.advanced_curve)
161
  elif curve_type == "color" or (curve_type == "auto" and not profile.advanced_curve):
 
165
  img_color_adjusted = apply_color_curves(img_linear, profile.color_curves)
166
  if profile.advanced_curve:
167
  img_color_adjusted = apply_advanced_curve(img_color_adjusted, profile.advanced_curve)
168
+
169
  img_srgb = colour.models.eotf_inverse_sRGB(img_color_adjusted)
 
170
  img_pil = Image.fromarray((img_srgb * 255).astype(np.uint8))
171
+
172
  enhancer = ImageEnhance.Contrast(img_pil)
173
  img_contrast = enhancer.enhance(profile.contrast)
174
+
175
  enhancer = ImageEnhance.Color(img_contrast)
176
  img_saturated = enhancer.enhance(profile.saturation)
177
+
178
  chroma_strength = chroma_override if chroma_override is not None else profile.chromatic_aberration
179
  if chroma_strength > 0:
180
  img_saturated = apply_chromatic_aberration_pil(img_saturated, chroma_strength)
181
+
182
  blur_amount = blur_override if blur_override is not None else profile.blur
183
  if blur_amount > 0:
184
  img_saturated = img_saturated.filter(ImageFilter.GaussianBlur(radius=blur_amount))
185
+
186
  img_saturated = apply_base_color(img_saturated, profile.base_color)
 
187
  img_saturated = add_film_grain(img_saturated, amount=profile.grain_amount, size=profile.grain_size)
 
188
  img_saturated = adjust_color_temperature(img_saturated, color_temp)
189
+
190
  if cross_process_flag:
191
  img_saturated = cross_process(img_saturated)
192
+
193
  return img_saturated
194
 
195
+ def process_images(image, profiles_json, chroma_override=None, blur_override=None, color_temp=6500, cross_process_flag=False, curve_type="auto"):
196
+ film_profiles = load_film_profiles_from_json(profiles_json)
197
+ input_path_base = "output"
198
+
199
+ processed_images = []
200
+ for profile_name, profile in film_profiles.items():
201
+ processed_image = apply_film_profile(image, profile, chroma_override, blur_override, color_temp, cross_process_flag, curve_type)
202
+ processed_images.append(processed_image)
203
+
204
+ return processed_images
205
+
206
  def process_image(args):
207
  image_path, profile, chroma_override, blur_override, color_temp, cross_process_flag, curve_type, output_filename = args
208
  with Image.open(image_path) as img:
209
  exif_data = img.getexif()
210
+
211
  orientation = exif_data.get(274, 1)
212
  if orientation in [3, 6, 8]:
213
  img = img.rotate({3: 180, 6: 270, 8: 90}[orientation], expand=True)
214
+
215
  processed_image = apply_film_profile(img, profile, chroma_override, blur_override, color_temp, cross_process_flag, curve_type)
216
 
217
  if exif_data:
 
228
  def get_optimal_pool_size(target_memory_usage=75):
229
  available_memory = 100 - get_memory_usage()
230
  cpu_count = multiprocessing.cpu_count()
231
+
232
  for i in range(cpu_count, 0, -1):
233
  estimated_memory_usage = get_memory_usage() + (available_memory / cpu_count) * i
234
  if estimated_memory_usage <= target_memory_usage:
235
  return i
236
+
237
  return 1
238
 
239
  if __name__ == "__main__":