-
-
Notifications
You must be signed in to change notification settings - Fork 123
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0c9362e
commit ba145f8
Showing
5 changed files
with
182 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Simple blur animation of image | ||
## Installation | ||
Use the provided `env.yml` environment file to install dependencies: | ||
```bash | ||
$ conda env install -f env.yml | ||
``` | ||
|
||
## Usage | ||
1. Navigate to script directory: | ||
```bash | ||
$ cd ./image-processing/episodes/files/source-code/blur-animation | ||
``` | ||
|
||
2. Run the script using the installed virtual environment: | ||
```bash | ||
$ conda activate anim-env | ||
$ python create_blur_animation.py | ||
``` | ||
|
||
3. Follow prompt (choose kernel size) and press Enter. | ||
|
||
|
||
## Author | ||
@marcodallavecchia |
147 changes: 147 additions & 0 deletions
147
files/source-code/blur-animation/create_blur_animation.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
### METADATA | ||
# author: Marco Dalla Vecchia | ||
# description: Simple blurring animation of simple image | ||
# data-source: A.tif was created using ImageJ (https://imagej.net/ij/) | ||
### | ||
|
||
### POTENTIAL IMPROVEMENTS | ||
# - Change colors for rectangular patches in animation | ||
# - Ask for image input instead of hard-coding it | ||
# - Ask for FPS as input | ||
# - Ask for animation format output | ||
|
||
|
||
# Import packages | ||
# Use associated requirements file to make sure you have all dependencies installed | ||
from matplotlib import pyplot as plt | ||
from matplotlib import patches as p | ||
from matplotlib.animation import FuncAnimation | ||
import numpy as np | ||
from scipy.ndimage import convolve | ||
from tqdm import tqdm | ||
|
||
# Fix image path depending from where you run this script -> this should run as is, from the repo structure | ||
img_path = "../../../data/A.tif" | ||
# Change here colors to improve accessibility | ||
kernel_color = "tab:red" | ||
center_color = "tab:olive" | ||
|
||
### ANIMATION FUNCTIONS | ||
def init(): | ||
""" | ||
Initialization function | ||
- Set image array data | ||
- Autoscale image display | ||
- Set XY coordinates of rectangular patches | ||
""" | ||
im.set_array(img_convolved) | ||
im.autoscale() | ||
k_rect.set_xy((-0.5, -0.5)) | ||
c_rect1.set_xy((kernel_size / 2 - 1, kernel_size / 2 - 1)) | ||
return [im, k_rect, c_rect1] | ||
|
||
def update(frame): | ||
""" | ||
Animation update function. For every frame do the following: | ||
- Update X and Y coordinates of rectangular patch for kernel | ||
- Update X and Y coordinates of rectangular patch for central pixel | ||
- Update blurred image frame | ||
""" | ||
pbar.update(1) | ||
row = (frame % total_frames) // (img_pad.shape[0] - kernel_size + 1) | ||
col = (frame % total_frames) % (img_pad.shape[1] - kernel_size + 1) | ||
|
||
k_rect.set_x(col - 0.5) | ||
c_rect1.set_x(col + (kernel_size/2 - 1)) | ||
k_rect.set_y(row - 0.5) | ||
c_rect1.set_y(row + (kernel_size/2 - 1)) | ||
|
||
im.set_array(all_frames[frame]) | ||
im.autoscale() | ||
|
||
return [im, k_rect, c_rect1] | ||
|
||
# MAIN PROGRAM | ||
if __name__ == "__main__": | ||
# simple input to ask for kernel size | ||
print("Please provide kernel size for mean filter blur animation") | ||
kernel_size = int(input("> ")) | ||
|
||
while kernel_size % 2 == 0: | ||
print("Please use an odd kernel size") | ||
kernel_size = int(input("> ")) | ||
|
||
print("Creating blurred animation with kernel size:", kernel_size) | ||
|
||
# Load image | ||
img = plt.imread(img_path) | ||
|
||
### HERE WE USE THE CONVOLVE FUNCTION TO GET THE FINAL BLURRED IMAGE | ||
# I chose a simple mean filter (equal kernel weights) | ||
kernel = np.ones(shape=(kernel_size, kernel_size)) / kernel_size ** 2 # create kernel | ||
# convolve the image i.e. apply mean filter | ||
img_convolved = convolve(img, kernel, mode='constant', cval=0) # pad borders with zero like below for consistency | ||
|
||
|
||
### HERE WE CONVOLVE MANUALLY STEP-BY-STEP TO CREATE ANIMATION | ||
img_pad = np.pad(img, (int(np.ceil(kernel_size/2) - 1), int(np.ceil(kernel_size/2) - 1))) # Pad image to deal with borders | ||
new_img = np.zeros(img.shape, dtype=np.uint16) # this will be the blurred final image | ||
|
||
# add first frame with complete blurred image for print version of GIF | ||
all_frames = [img_convolved] | ||
|
||
# precompute animation frames and append to the list | ||
total_frames = (img_pad.shape[0] - kernel_size + 1) * (img_pad.shape[1] - kernel_size + 1) # total frames if by change image is not squared | ||
for frame in range(total_frames): | ||
row = (frame % total_frames) // (img_pad.shape[0] - kernel_size + 1) # row index | ||
col = (frame % total_frames) % (img_pad.shape[1] - kernel_size + 1) # col index | ||
img_chunk = img_pad[row : row + kernel_size, col : col + kernel_size] # get current image chunk inside the kernel | ||
new_img[row, col] = np.mean(img_chunk).astype(np.uint16) # calculate its mean -> mean filter | ||
all_frames.append(new_img.copy()) # append to animation frames list | ||
|
||
# We now have an extra frame | ||
total_frames += 1 | ||
|
||
### FROM HERE WE START CREATING THE ANIMATION | ||
# Initialize canvas | ||
f, (ax1, ax2) = plt.subplots(1,2, figsize=(10,5)) | ||
|
||
# Display the padded image -> this one won't change during the animation | ||
ax1.imshow(img_pad, cmap='gray') | ||
# Initialize the blurred image -> this is the first frame with already the final result | ||
im = ax2.imshow(img_convolved, animated=True, cmap='gray') | ||
|
||
# Define rectangular patches to identify moving kernel | ||
k_rect = p.Rectangle((-0.5,-0.5), kernel_size, kernel_size, linewidth=2, edgecolor=kernel_color, facecolor='none', alpha=0.8) # kernel rectangle | ||
c_rect1 = p.Rectangle(((kernel_size/2 - 1), (kernel_size/2 - 1)), 1, 1, linewidth=2, edgecolor=center_color, facecolor='none') # central pixel rectangle | ||
# Add them to the figure | ||
ax1.add_patch(k_rect) | ||
ax1.add_patch(c_rect1) | ||
|
||
# Fix limits to the right image (without padding) is the same size as the left image (with padding) | ||
ax2.set( | ||
ylim=((img_pad.shape[0] - kernel_size / 2), -kernel_size / 2), | ||
xlim=(-kernel_size / 2, (img_pad.shape[1] - kernel_size / 2)) | ||
) | ||
|
||
# We don't need to see the ticks | ||
ax1.axis("off") | ||
ax2.axis("off") | ||
|
||
# Create progress bar to visualize animation progress | ||
pbar = tqdm(total=total_frames) | ||
|
||
### HERE WE CREATE THE ANIMATION | ||
# Use FuncAnimation to create the animation | ||
ani = FuncAnimation( | ||
f, update, | ||
frames=range(total_frames), | ||
interval=50, # we could change the animation speed | ||
init_func=init, | ||
blit=True | ||
) | ||
|
||
# Export animation | ||
plt.tight_layout() | ||
ani.save('../../../fig/blur-demo.gif') | ||
print("Animation exported") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
name: anim-env | ||
channels: | ||
- defaults | ||
dependencies: | ||
- python=3.11 | ||
- pandas | ||
- scikit-image | ||
- seaborn | ||
- pooch | ||
- jupyterlab | ||
- tqdm |