nick-leland commited on
Commit
8a01cdc
·
1 Parent(s): d533e21

Updated the app to adjust the image generation

Browse files
Files changed (3) hide show
  1. app.py +48 -25
  2. bulk_bulge_generation.py +68 -0
  3. transformation.py +157 -0
app.py CHANGED
@@ -3,27 +3,19 @@ import gradio as gr
3
  from PIL import Image
4
  from scipy import ndimage
5
  import matplotlib.pyplot as plt
 
6
 
7
- def apply_vector_field_transform(image, func, radius, center=(0.5, 0.5), strength=1, edge_smoothness=0.1):
8
- """
9
- Apply a vector field transformation to an image based on a given multivariate function.
10
-
11
- :param image: Input image as a numpy array (height, width, channels)
12
- :param func: A function that takes x and y as inputs and returns a scalar
13
- :param radius: Radius of the effect as a fraction of the image size
14
- :param center: Tuple (y, x) for the center of the effect, normalized to [0, 1]
15
- :param strength: Strength of the effect, scaled to image size
16
- :param edge_smoothness: Width of the smooth transition at the edge, as a fraction of the radius
17
- :return: Tuple of (transformed image as a numpy array, gradient vectors for vector field)
18
- """
19
  rows, cols = image.shape[:2]
20
  max_dim = max(rows, cols)
21
 
22
- # Convert normalized center to pixel coordinates
23
- center_y = int(center[0] * rows)
24
- center_x = int(center[1] * cols)
 
25
 
26
- # Convert normalized radius to pixel radius
27
  pixel_radius = int(max_dim * radius)
28
 
29
  y, x = np.ogrid[:rows, :cols]
@@ -39,8 +31,16 @@ def apply_vector_field_transform(image, func, radius, center=(0.5, 0.5), strengt
39
  # Calculate gradients
40
  gy, gx = np.gradient(z)
41
 
42
- # Create smooth transition mask
43
- mask = np.clip((radius - dist_from_center) / (radius * edge_smoothness), 0, 1)
 
 
 
 
 
 
 
 
44
 
45
  # Apply mask to gradients
46
  gx = gx * mask
@@ -123,7 +123,7 @@ def create_gradient_vector_field(gx, gy, image_shape, step=20, reverse=False):
123
 
124
  return vector_field
125
 
126
- def transform_image(image, func_choice, radius, center_x, center_y, strength, edge_smoothness, reverse_gradient=True, spiral_frequency=1):
127
  I = np.asarray(Image.open(image))
128
 
129
  def pinch(x, y):
@@ -133,9 +133,23 @@ def transform_image(image, func_choice, radius, center_x, center_y, strength, ed
133
  return np.arctan2(y, x)
134
 
135
  def bulge(x, y):
136
- r = np.sqrt(x**2 + y**2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  # return -1 / (r + 1)
138
- return -r
139
 
140
  def spiral(x, y, frequency=1):
141
  r = np.sqrt(x**2 + y**2)
@@ -148,10 +162,17 @@ def transform_image(image, func_choice, radius, center_x, center_y, strength, ed
148
  func = shift
149
  elif func_choice == "Bulge":
150
  func = bulge
151
- elif func_choice == "Shift":
152
  func = lambda x, y: spiral(x, y, frequency=spiral_frequency)
153
 
154
- transformed, (gx, gy) = apply_vector_field_transform(I, func, radius, (center_y, center_x), strength, edge_smoothness)
 
 
 
 
 
 
 
155
  vector_field = create_gradient_vector_field(gx, gy, I.shape[:2], reverse=reverse_gradient)
156
 
157
  return transformed, vector_field
@@ -160,12 +181,14 @@ demo = gr.Interface(
160
  fn=transform_image,
161
  inputs=[
162
  gr.Image(type="filepath"),
163
- gr.Dropdown(["Pinch", "Spiral", "Shift", "Bulge"], value="Bulge", label="Function"),
 
164
  gr.Slider(0, 0.5, value=0.25, label="Radius (as fraction of image size)"),
165
  gr.Slider(0, 1, value=0.5, label="Center X"),
166
  gr.Slider(0, 1, value=0.5, label="Center Y"),
167
  gr.Slider(0, 1, value=0.5, label="Strength"),
168
- gr.Slider(0, 1, value=0.5, label="Edge Smoothness")
 
169
  # gr.Checkbox(label="Reverse Gradient Direction"),
170
  ],
171
  outputs=[
 
3
  from PIL import Image
4
  from scipy import ndimage
5
  import matplotlib.pyplot as plt
6
+ from bulk_bulge_generation import definitions
7
 
8
+ def apply_vector_field_transform(image, func, radius, center=(0.5, 0.5), strength=1, edge_smoothness=0.1, center_smoothness=0.20):
9
+ # 0.106 strength = .50
10
+ # 0.106 strength = 1
 
 
 
 
 
 
 
 
 
11
  rows, cols = image.shape[:2]
12
  max_dim = max(rows, cols)
13
 
14
+ #Normalize the positions
15
+ # Y Needs to be flipped
16
+ center_y = int(center[1] * rows)
17
+ center_x = int(center[0] * cols)
18
 
 
19
  pixel_radius = int(max_dim * radius)
20
 
21
  y, x = np.ogrid[:rows, :cols]
 
31
  # Calculate gradients
32
  gy, gx = np.gradient(z)
33
 
34
+ # Creating a sigmoid function to apply to masks
35
+ def sigmoid(x, center, steepness):
36
+ return 1 / (1 + np.exp(-steepness * (x - center)))
37
+
38
+ # Masking
39
+ edge_mask = np.clip((radius - dist_from_center) / (radius * edge_smoothness), 0, 1)
40
+
41
+ center_mask = np.clip((dist_from_center - radius * center_smoothness) / (radius * center_smoothness), 0, 1)
42
+
43
+ mask = edge_mask * center_mask
44
 
45
  # Apply mask to gradients
46
  gx = gx * mask
 
123
 
124
  return vector_field
125
 
126
+ def transform_image(image, func_choice, randomization_check, radius, center_x, center_y, strength, edge_smoothness, center_smoothness, reverse_gradient=True, spiral_frequency=1):
127
  I = np.asarray(Image.open(image))
128
 
129
  def pinch(x, y):
 
133
  return np.arctan2(y, x)
134
 
135
  def bulge(x, y):
136
+ # Where does this make an array???:w
137
+
138
+ print(x.shape)
139
+ print(y.shape)
140
+ # Mess with this number
141
+ # num = 10
142
+
143
+ # if x < num or y < num:
144
+ # # This might not be correct
145
+ # return 1
146
+ # else:
147
+ # r = -np.sqrt(x**2 + y**2)
148
+ r = -np.sqrt(x**2 + y**2)
149
+ print(r.shape)
150
+ print(type(r))
151
  # return -1 / (r + 1)
152
+ return r
153
 
154
  def spiral(x, y, frequency=1):
155
  r = np.sqrt(x**2 + y**2)
 
162
  func = shift
163
  elif func_choice == "Bulge":
164
  func = bulge
165
+ elif func_choice == "Shift Up":
166
  func = lambda x, y: spiral(x, y, frequency=spiral_frequency)
167
 
168
+ if randomization_check == True:
169
+ rng = np.random.default_rng()
170
+ radius, location, strength, edge_smoothness= definitions(rng)
171
+ center_x = location[0]
172
+ center_y = location[1]
173
+
174
+
175
+ transformed, (gx, gy) = apply_vector_field_transform(I, func, radius, (center_x, center_y), strength, edge_smoothness, center_smoothness)
176
  vector_field = create_gradient_vector_field(gx, gy, I.shape[:2], reverse=reverse_gradient)
177
 
178
  return transformed, vector_field
 
181
  fn=transform_image,
182
  inputs=[
183
  gr.Image(type="filepath"),
184
+ gr.Dropdown(["Pinch", "Spiral", "Shift Up", "Bulge"], value="Bulge", label="Function"),
185
+ gr.Checkbox(label="Randomize inputs?"),
186
  gr.Slider(0, 0.5, value=0.25, label="Radius (as fraction of image size)"),
187
  gr.Slider(0, 1, value=0.5, label="Center X"),
188
  gr.Slider(0, 1, value=0.5, label="Center Y"),
189
  gr.Slider(0, 1, value=0.5, label="Strength"),
190
+ gr.Slider(0, 1, value=0.5, label="Edge Smoothness"),
191
+ gr.Slider(0, 0.5, value=0.1, label="Center Smoothness")
192
  # gr.Checkbox(label="Reverse Gradient Direction"),
193
  ],
194
  outputs=[
bulk_bulge_generation.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from PIL import Image
3
+ from transformation import apply_vector_field_transform
4
+ import os
5
+ from os import path
6
+
7
+ def definitions(generator):
8
+
9
+ # The image bulge should be entirly contained within the image. For instance, if we have a radius of 0.5 (the max), the image should be force to be at 0.5 (x and y) locations.
10
+ radius = generator.random() * 0.5
11
+ # radius = generator.normal(loc=0.5, scale=(0.5/4))
12
+ # print(f"Radius is {radius}")
13
+ # strengtH = Generator.random()
14
+ strength = generator.normal(loc=1, scale=(1/6))
15
+ # print(f"Strength is {strength}")
16
+
17
+ smoothness = generator.normal(loc=1, scale=(1/6))
18
+ # print(f"Smoothness is {smoothness}")
19
+
20
+ # size = 3
21
+
22
+ vmin = min([1-radius, radius])
23
+ vmax = max([1-radius, radius])
24
+ print()
25
+ print("Radius is {radius}")
26
+ print("Max and Min positions to calculate mean + std")
27
+ print(vmin)
28
+ print(vmax)
29
+ print()
30
+
31
+ print("Mean and Std Dev")
32
+ mean = (vmax+vmin) / 2
33
+ std = (vmax-vmin) / 4
34
+ print(mean)
35
+ print(std)
36
+ print()
37
+ # x = generator.normal(loc=mean, scale=std, size=(2))
38
+ x = np.random.uniform(low=vmin, high=vmax, size=(2))
39
+ # print(x)
40
+
41
+ # print(f"({np.random.uniform(vmin, vmax)}, {np.random.uniform(vmin, vmax)})")
42
+ # print(f"({x[0]}, {x[1]})")
43
+ return radius, x, strength, smoothness
44
+
45
+ def bulge(x, y):
46
+ return -np.sqrt(x**2 + y**2)
47
+
48
+ if __name__ == "__main__":
49
+ # Sets the numpy generator
50
+ rng = np.random.default_rng()
51
+
52
+ os.chdir("data/")
53
+ os.makedirs("grid", exist_ok=True)
54
+ os.makedirs("output", exist_ok=True)
55
+ files = os.listdir("grid/")
56
+ os.chdir("grid/")
57
+
58
+ for _ in files:
59
+ rad, location, strth, smth = definitions(rng)
60
+ I = np.asarray(Image.open(_))
61
+ transformed, (gx, gy) = apply_vector_field_transform(I, bulge, rad, location, strth, smth)
62
+
63
+ os.chdir("../output/")
64
+ result = Image.fromarray(transformed)
65
+ result.save(f"{_.title()}.jpg")
66
+ os.chdir("../grid/")
67
+
68
+
transformation.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from PIL import Image
3
+ from scipy import ndimage
4
+ import matplotlib.pyplot as plt
5
+
6
+ def apply_vector_field_transform(image, func, radius, center=(0.5, 0.5), strength=1, edge_smoothness=0.1):
7
+ """
8
+ Apply a vector field transformation to an image based on a given multivariate function.
9
+
10
+ :param image: Input image as a numpy array (height, width, channels)
11
+ :param func: A function that takes x and y as inputs and returns a scalar
12
+ :param radius: Radius of the effect as a fraction of the image size
13
+ :param center: Tuple (y, x) for the center of the effect, normalized to [0, 1]
14
+ :param strength: Strength of the effect, scaled to image size
15
+ :param edge_smoothness: Width of the smooth transition at the edge, as a fraction of the radius
16
+ :return: Tuple of (transformed image as a numpy array, gradient vectors for vector field)
17
+ """
18
+ rows, cols = image.shape[:2]
19
+ max_dim = max(rows, cols)
20
+
21
+ # Convert normalized center to pixel coordinates
22
+ center_y = int(center[0] * rows)
23
+ center_x = int(center[1] * cols)
24
+
25
+ # Convert normalized radius to pixel radius
26
+ pixel_radius = int(max_dim * radius)
27
+
28
+ y, x = np.ogrid[:rows, :cols]
29
+ y = (y - center_y) / max_dim
30
+ x = (x - center_x) / max_dim
31
+
32
+ # Calculate distance from center
33
+ dist_from_center = np.sqrt(x**2 + y**2)
34
+
35
+ # Calculate function values
36
+ z = func(x, y)
37
+
38
+ # Calculate gradients
39
+ gy, gx = np.gradient(z)
40
+
41
+ # Create smooth transition mask
42
+ mask = np.clip((radius - dist_from_center) / (radius * edge_smoothness), 0, 1)
43
+
44
+ # Apply mask to gradients
45
+ gx = gx * mask
46
+ gy = gy * mask
47
+
48
+ # Normalize gradient vectors
49
+ magnitude = np.sqrt(gx**2 + gy**2)
50
+ magnitude[magnitude == 0] = 1 # Avoid division by zero
51
+ gx = gx / magnitude
52
+ gy = gy / magnitude
53
+
54
+ # Scale the effect (Play with the number 5)
55
+ scale_factor = strength * np.log(max_dim) / 100 # Adjust strength based on image size
56
+ gx = gx * scale_factor * mask
57
+ gy = gy * scale_factor * mask
58
+
59
+ # Create the mapping
60
+ x_new = x + gx
61
+ y_new = y + gy
62
+
63
+ # Convert back to pixel coordinates
64
+ x_new = x_new * max_dim + center_x
65
+ y_new = y_new * max_dim + center_y
66
+
67
+ # Ensure the new coordinates are within the image boundaries
68
+ x_new = np.clip(x_new, 0, cols - 1)
69
+ y_new = np.clip(y_new, 0, rows - 1)
70
+
71
+ # Apply the transformation to each channel
72
+ channels = [ndimage.map_coordinates(image[..., i], [y_new, x_new], order=1, mode='reflect')
73
+ for i in range(image.shape[2])]
74
+
75
+ transformed_image = np.dstack(channels).astype(image.dtype)
76
+
77
+ return transformed_image, (gx, gy)
78
+
79
+ def create_gradient_vector_field(gx, gy, image_shape, step=20, reverse=False):
80
+ """
81
+ Create a gradient vector field visualization with option to reverse direction.
82
+
83
+ :param gx: X-component of the gradient
84
+ :param gy: Y-component of the gradient
85
+ :param image_shape: Shape of the original image (height, width)
86
+ :param step: Spacing between arrows
87
+ :param reverse: If True, reverse the direction of the arrows
88
+ :return: Gradient vector field as a numpy array (RGB image)
89
+ """
90
+ rows, cols = image_shape
91
+ y, x = np.mgrid[step/2:rows:step, step/2:cols:step].reshape(2, -1).astype(int)
92
+
93
+ # Calculate the scale based on image size
94
+ max_dim = max(rows, cols)
95
+ scale = max_dim / 1000 # Adjusted for longer arrows
96
+
97
+ # Reverse direction if specified
98
+ direction = -1 if reverse else 1
99
+
100
+ fig, ax = plt.subplots(figsize=(cols/50, rows/50), dpi=100)
101
+ ax.quiver(x, y, direction * gx[y, x], direction * -gy[y, x],
102
+ scale=scale,
103
+ scale_units='width',
104
+ width=0.002 * max_dim / 500,
105
+ headwidth=8,
106
+ headlength=12,
107
+ headaxislength=0,
108
+ color='black',
109
+ minshaft=2,
110
+ minlength=0,
111
+ pivot='tail')
112
+ ax.set_xlim(0, cols)
113
+ ax.set_ylim(rows, 0)
114
+ ax.set_aspect('equal')
115
+ ax.axis('off')
116
+
117
+ fig.tight_layout(pad=0)
118
+ fig.canvas.draw()
119
+ vector_field = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
120
+ vector_field = vector_field.reshape(fig.canvas.get_width_height()[::-1] + (3,))
121
+ plt.close(fig)
122
+
123
+ return vector_field
124
+
125
+ def transform_image(image, func_choice, radius, center_x, center_y, strength, edge_smoothness, reverse_gradient=True, spiral_frequency=1):
126
+ I = np.asarray(Image.open(image))
127
+
128
+ def pinch(x, y):
129
+ return x**2 + y**2
130
+
131
+ def shift(x, y):
132
+ return np.arctan2(y, x)
133
+
134
+ def bulge(x, y):
135
+ r = np.sqrt(x**2 + y**2)
136
+ # return -1 / (r + 1)
137
+ return -r
138
+
139
+ def spiral(x, y, frequency=1):
140
+ r = np.sqrt(x**2 + y**2)
141
+ theta = np.arctan2(y, x)
142
+ return r * np.sin(theta - frequency * r)
143
+
144
+ if func_choice == "Pinch":
145
+ func = pinch
146
+ elif func_choice == "Spiral":
147
+ func = shift
148
+ elif func_choice == "Bulge":
149
+ func = bulge
150
+ elif func_choice == "Shift":
151
+ func = lambda x, y: spiral(x, y, frequency=spiral_frequency)
152
+
153
+ transformed, (gx, gy) = apply_vector_field_transform(I, func, radius, (center_y, center_x), strength, edge_smoothness)
154
+ vector_field = create_gradient_vector_field(gx, gy, I.shape[:2], reverse=reverse_gradient)
155
+
156
+ return transformed, vector_field
157
+