nick-leland commited on
Commit
45f18d1
·
1 Parent(s): 063f4f5

Added .gitignore and rewrote the app

Browse files
Files changed (2) hide show
  1. .gitignore +3 -0
  2. app.py +173 -9
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ __pycache__
2
+ venv/
3
+ key.txt
app.py CHANGED
@@ -1,17 +1,181 @@
1
  import numpy as np
2
  import gradio as gr
3
- from DistortionML.effect import distort_generation
 
 
4
 
5
- def greet(name):
6
- return "Hello " + name + "!!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  demo = gr.Interface(
9
- fn=greet,
10
- inputs=["text", "slider"],
11
- outputs="text"
12
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- # Currently working through https://www.gradio.app/main/guies/the-interface-class
 
15
 
16
  demo.launch()
17
-
 
1
  import numpy as np
2
  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]
30
+ y = (y - center_y) / max_dim
31
+ x = (x - center_x) / max_dim
32
+
33
+ # Calculate distance from center
34
+ dist_from_center = np.sqrt(x**2 + y**2)
35
+
36
+ # Calculate function values
37
+ z = func(x, y)
38
+
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
47
+ gy = gy * mask
48
+
49
+ # Normalize gradient vectors
50
+ magnitude = np.sqrt(gx**2 + gy**2)
51
+ magnitude[magnitude == 0] = 1 # Avoid division by zero
52
+ gx = gx / magnitude
53
+ gy = gy / magnitude
54
+
55
+ # Scale the effect (Play with the number 5)
56
+ scale_factor = strength * np.log(max_dim) / 100 # Adjust strength based on image size
57
+ gx = gx * scale_factor * mask
58
+ gy = gy * scale_factor * mask
59
+
60
+ # Create the mapping
61
+ x_new = x + gx
62
+ y_new = y + gy
63
+
64
+ # Convert back to pixel coordinates
65
+ x_new = x_new * max_dim + center_x
66
+ y_new = y_new * max_dim + center_y
67
+
68
+ # Ensure the new coordinates are within the image boundaries
69
+ x_new = np.clip(x_new, 0, cols - 1)
70
+ y_new = np.clip(y_new, 0, rows - 1)
71
+
72
+ # Apply the transformation to each channel
73
+ channels = [ndimage.map_coordinates(image[..., i], [y_new, x_new], order=1, mode='reflect')
74
+ for i in range(image.shape[2])]
75
+
76
+ transformed_image = np.dstack(channels).astype(image.dtype)
77
+
78
+ return transformed_image, (gx, gy)
79
+
80
+ def create_gradient_vector_field(gx, gy, image_shape, step=20, reverse=False):
81
+ """
82
+ Create a gradient vector field visualization with option to reverse direction.
83
+
84
+ :param gx: X-component of the gradient
85
+ :param gy: Y-component of the gradient
86
+ :param image_shape: Shape of the original image (height, width)
87
+ :param step: Spacing between arrows
88
+ :param reverse: If True, reverse the direction of the arrows
89
+ :return: Gradient vector field as a numpy array (RGB image)
90
+ """
91
+ rows, cols = image_shape
92
+ y, x = np.mgrid[step/2:rows:step, step/2:cols:step].reshape(2, -1).astype(int)
93
+
94
+ # Calculate the scale based on image size
95
+ max_dim = max(rows, cols)
96
+ scale = max_dim / 1000 # Adjusted for longer arrows
97
+
98
+ # Reverse direction if specified
99
+ direction = -1 if reverse else 1
100
+
101
+ fig, ax = plt.subplots(figsize=(cols/50, rows/50), dpi=100)
102
+ ax.quiver(x, y, direction * gx[y, x], direction * -gy[y, x],
103
+ scale=scale,
104
+ scale_units='width',
105
+ width=0.002 * max_dim / 500,
106
+ headwidth=8,
107
+ headlength=12,
108
+ headaxislength=0,
109
+ color='black',
110
+ minshaft=2,
111
+ minlength=0,
112
+ pivot='tail')
113
+ ax.set_xlim(0, cols)
114
+ ax.set_ylim(rows, 0)
115
+ ax.set_aspect('equal')
116
+ ax.axis('off')
117
+
118
+ fig.tight_layout(pad=0)
119
+ fig.canvas.draw()
120
+ vector_field = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
121
+ vector_field = vector_field.reshape(fig.canvas.get_width_height()[::-1] + (3,))
122
+ plt.close(fig)
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 zoom(x, y):
130
+ return x**2 + y**2
131
+
132
+ def rotation(x, y):
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
+
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 == "Zoom":
145
+ func = zoom
146
+ elif func_choice == "Rotation":
147
+ func = rotation
148
+ elif func_choice == "Bulge":
149
+ func = bulge
150
+ elif func_choice == "Spiral":
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
 
158
  demo = gr.Interface(
159
+ fn=transform_image,
160
+ inputs=[
161
+ gr.Image(type="filepath"),
162
+ gr.Dropdown(["Bulge", "Spiral", "Zoom", "Rotation"], value="Bulge", label="Function"),
163
+ gr.Slider(0, 0.5, value=0.25, label="Radius (as fraction of image size)"),
164
+ gr.Slider(0, 1, value=0.5, label="Center X"),
165
+ gr.Slider(0, 1, value=0.5, label="Center Y"),
166
+ gr.Slider(0, 1, value=0.5, label="Strength"),
167
+ gr.Slider(0, 1, value=0.5, label="Edge Smoothness")
168
+ # gr.Checkbox(label="Reverse Gradient Direction"),
169
+ ],
170
+ outputs=[
171
+ gr.Image(label="Transformed Image"),
172
+ gr.Image(label="Gradient Vector Field")
173
+ ],
174
+ title="Image Transformation Demo!",
175
+ description="This is a demo of the tool that I will be using to generate images to train a machine learning model on! This tool allows you to play around with the simple bulge effect primarily, I will be attempting to impliment manual function input within the app next!"
176
+ )
177
 
178
+ def update_spiral_frequency(func_choice):
179
+ return gr.Slider(visible=(func_choice == "Spiral"))
180
 
181
  demo.launch()