upload files

This commit is contained in:
wangziao 2025-04-19 23:56:29 -07:00
commit 34c677e747
7 changed files with 127 additions and 0 deletions

BIN
color_rotate.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

BIN
color_rotate.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
grid.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

BIN
grid.mp4 Normal file

Binary file not shown.

99
map2video.py Normal file
View File

@ -0,0 +1,99 @@
from PIL import Image
import imageio
# Function to map (x, y) to (u, v)
def map_function(x, y, scale_s, scale_t, mapparam):
# You can define your mapping function here.
# For example, this function scales and shifts the coordinates:
u = mapparam * (scale_s*scale_t) * x / (x*x+y*y)
v = mapparam * (scale_s*scale_t) * y / (x*x+y*y)
return u, v
def create_frames(stepsize,N_pics,w,h,wt,ht,mapparam):
default_color = (0, 0, 0)
output_images = []
scale_s = min(w, h)
scale_t = min(wt, ht)
for iter in range(N_pics):
print("%d\r"%iter,end="")
offset = -iter*stepsize
def wrap(int_u):
return (int_u+offset) % (wt - 1)
output_image = Image.new("RGB", (w, h))
for x in range(w):
for y in range(h):
realx, realy = x-w//2, y-h//2
if realx==0 and realy==0:
output_image.putpixel((x,y), default_color)
continue
realu, realv = map_function(realx, realy, scale_s, scale_t, mapparam)
u, v = realu + wt//2, realv + ht//2
# Interpolation (you can use different methods, such as bilinear)
u_floor, v_floor = int(u), int(v)
u_frac, v_frac = u - u_floor, v - v_floor
# Ensure u and v are within the bounds of the texture image
if u_floor < 0 or u_floor > wt - 2 or v_floor < 0 or v_floor > ht - 2:
output_image.putpixel((x,y), default_color)
continue
color_top_left = texture_image.getpixel((wrap(u_floor), v_floor))
color_top_right = texture_image.getpixel((wrap(u_floor + 1), v_floor))
color_bottom_left = texture_image.getpixel((wrap(u_floor), v_floor + 1))
color_bottom_right = texture_image.getpixel((wrap(u_floor + 1), v_floor + 1))
# Interpolate colors
color = (
int((1 - u_frac) * (1 - v_frac) * color_top_left[0] +
u_frac * (1 - v_frac) * color_top_right[0] +
(1 - u_frac) * v_frac * color_bottom_left[0] +
u_frac * v_frac * color_bottom_right[0]),
int((1 - u_frac) * (1 - v_frac) * color_top_left[1] +
u_frac * (1 - v_frac) * color_top_right[1] +
(1 - u_frac) * v_frac * color_bottom_left[1] +
u_frac * v_frac * color_bottom_right[1]),
int((1 - u_frac) * (1 - v_frac) * color_top_left[2] +
u_frac * (1 - v_frac) * color_top_right[2] +
(1 - u_frac) * v_frac * color_bottom_left[2] +
u_frac * v_frac * color_bottom_right[2])
)
output_image.putpixel((x, y), color)
output_images.append(output_image)
return output_images
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser(description='Turn an image into a stylized gif where every frame of the gif is\
a conformal mapping of the original image shifted and wrapped around in the width direction')
parser.add_argument('input_file')
parser.add_argument('output_file')
parser.add_argument('--output_width', type=int, default=400)
parser.add_argument('--output_height', type=int, default=400)
parser.add_argument('--num_frames', type=int, default=60)
parser.add_argument('--x_shift_per_frame', type=int, nargs="?")
parser.add_argument('--mapping_param', type=float, default=0.05)
args = parser.parse_args()
# Load your texture image
texture_image = Image.open(args.input_file)
# Set the width and height of the output image
w, h = args.output_width, args.output_height
# Set the width and height of the texture image
wt, ht = texture_image.width, texture_image.height
mapparam = args.mapping_param
N_pics = args.num_frames
if args.x_shift_per_frame==None:
stepsize = wt / N_pics # one whole period
else:
stepsize = args.x_shift_per_frame
output_images = create_frames(stepsize,N_pics,w,h,wt,ht,mapparam)
imageio.mimsave(args.output_file, output_images)

25
readme.md Normal file
View File

@ -0,0 +1,25 @@
# Conformal Mapping and Video Generation
## What is the mapping?
```
u = mapparam * (scale_s*scale_t) * x / (x*x+y*y)
v = mapparam * (scale_s*scale_t) * y / (x*x+y*y)
```
is equivalent to the normalized version:
```
u/scale_t = mapparam * (x/scale_s) / ((x/scale_s)**2 + (y/scale_s)**2)
v/scale_t = mapparam * (y/scale_s) / ((x/scale_s)**2 + (y/scale_s)**2)
```
This explains why there is a "hole" in the output image: small normalized x,y maps to big normalized u,v. If the normalized u,v has an absolute value greater than 0.5, there is no corresponding pixel in the texture input file, we output the default color black.
## How to get the example results
Environment: Python 3.11. Do `pip install -r requirements.txt` to get necessary packages.
`python map2video.py color_rotate.png color_rotate.gif --num_frames 40`
`python map2video.py grid.jpg grid.mp4 --num_frames 40`

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
pillow==9.3.0
imageio==2.31.5
imageio-ffmpeg==0.4.9