Skip to content

Commit 6ea3ba9

Browse files
committed
markdown source builds
Auto-generated via `{sandpaper}` Source : 27a02b6 Branch : main Author : Kimberly Meechan <24316371+K-Meech@users.noreply.github.com> Time : 2026-02-03 09:04:42 +0000 Message : Merge pull request datacarpentry#357 from datacarpentry/update/workflows Update Workflows to Version 0.18.5
1 parent ac75c7a commit 6ea3ba9

11 files changed

Lines changed: 211 additions & 49 deletions

File tree

01-introduction.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,11 @@ the type of objects in the image.
4646

4747
Some examples of image processing methods applied in research include:
4848

49-
- [imaging a Black Hole](https://iopscience.iop.org/article/10.3847/2041-8213/ab0e85)
50-
- [estimating the population of Emperor Penguins](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3325796/)
51-
- [the global-scale analysis of marine plankton diversity](https://www.cell.com/cell/fulltext/S0092-8674\(19\)31124-9)
52-
- [segmentation of liver and vessels from CT images](https://doi.org/10.1016/j.cmpb.2017.12.008)
49+
- [Imaging a black hole](https://iopscience.iop.org/article/10.3847/2041-8213/ab0e85)
50+
- [Segmentation of liver and vessels from CT images](https://doi.org/10.1016/j.cmpb.2017.12.008)
51+
- [Monitoring wading birds in the Everglades using drones](https://dx.doi.org/10.1002/rse2.421) ([Blog article summarizing the paper](https://jabberwocky.weecology.org/2024/07/29/monitoring-wading-birds-in-near-real-time-using-drones-and-computer-vision/))
52+
- [Estimating the population of emperor penguins](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3325796/)
53+
- [Global-scale analysis of marine plankton diversity](https://www.cell.com/cell/fulltext/S0092-8674\(19\)31124-9)
5354

5455
With this lesson,
5556
we aim to provide a thorough grounding in the fundamental concepts and skills

02-image-basics.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,12 @@ metadata
10851085
'resolution': (1.0, 1.0, 'NONE')}
10861086
```
10871087

1088+
Many popular image editing programs have built-in metadata viewing
1089+
capabilities. A platform-independent open-source tool that allows
1090+
users to read, write, and edit metadata is
1091+
[ExifTool](https://exiftool.org/). It can handle a wide range of file
1092+
types and metadata formats but requires some technical knowledge to be
1093+
used effectively.
10881094
Other software exists that can help you handle metadata,
10891095
e.g., [Fiji](https://imagej.net/Fiji)
10901096
and [ImageMagick](https://imagemagick.org/index.php).

06-blurring.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,7 @@ in [the scikit-image documentation](https://scikit-image.org/docs/dev/user_guide
247247

248248
::::::::::::::::::::::::::::::::::::::::::::::::::
249249

250-
This animation shows how the blur kernel moves along in the original image in
251-
order to calculate the colour channel values for the blurred image.
250+
Let's consider a very simple image to see blurring in action. The animation below shows how the blur kernel (large red square) moves along the image on the left in order to calculate the corresponding values for the blurred image (yellow central square) on the right. In this simple case, the original image is single-channel, but blurring would work likewise on a multi-channel image.
252251

253252
![](fig/blur-demo.gif){alt='Blur demo animation'}
254253

07-thresholding.md

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -478,9 +478,7 @@ def measure_root_mass(filename, sigma=1.0):
478478

479479
# determine root mass ratio
480480
root_pixels = np.count_nonzero(binary_mask)
481-
w = binary_mask.shape[1]
482-
h = binary_mask.shape[0]
483-
density = root_pixels / (w * h)
481+
density = root_pixels / binary_mask.size
484482

485483
return density
486484
```
@@ -499,11 +497,8 @@ Recall that in the `binary_mask`, every pixel has either a value of
499497
zero (black/background) or one (white/foreground).
500498
We want to count the number of white pixels,
501499
which can be accomplished with a call to the NumPy function `np.count_nonzero`.
502-
Then we determine the width and height of the image by using
503-
the elements of `binary_mask.shape`
504-
(that is, the dimensions of the NumPy array that stores the image).
505500
Finally, the density ratio is calculated by dividing the number of white pixels
506-
by the total number of pixels `w*h` in the image.
501+
by the total number of pixels `binary_mask.size` in the image.
507502
The function returns then root density of the image.
508503

509504
We can call this function with any filename and
@@ -650,9 +645,7 @@ def enhanced_root_mass(filename, sigma):
650645

651646
# determine root mass ratio
652647
root_pixels = np.count_nonzero(binary_mask)
653-
w = binary_mask.shape[1]
654-
h = binary_mask.shape[0]
655-
density = root_pixels / (w * h)
648+
density = root_pixels / binary_mask.size
656649

657650
return density
658651

LICENSE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@ SOFTWARE.
6969
## Trademark
7070

7171
"The Carpentries", "Software Carpentry", "Data Carpentry", and "Library
72-
Carpentry" and their respective logos are registered trademarks of [Community
73-
Initiatives][ci].
72+
Carpentry" and their respective logos are registered trademarks of
73+
[The Carpentries, Inc.][carpentries].
7474

7575
[cc-by-human]: https://creativecommons.org/licenses/by/4.0/
7676
[cc-by-legal]: https://creativecommons.org/licenses/by/4.0/legalcode
7777
[mit-license]: https://opensource.org/licenses/mit-license.html
78-
[ci]: https://communityin.org/
78+
[carpentries]: https://carpentries.org
7979
[osi]: https://opensource.org

data/letterA.tif

1.11 KB
Binary file not shown.

fig/blur-demo.gif

-30.6 MB
Loading
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
### METADATA
2+
# author: Marco Dalla Vecchia @marcodallavecchia
3+
# description: Simple blurring animation of simple image
4+
# data-source: letterA.tif was created using ImageJ (https://imagej.net/ij/)
5+
###
6+
7+
### INFO
8+
# This script creates the animated illustration of blurring in episode 6
9+
###
10+
11+
### USAGE
12+
# The script was written in Python 3.12 and required the following Python packages:
13+
# - numpy==2.2.3
14+
# - scipy==1.15.2
15+
# - matplotlib==3.10.1
16+
# - tqdm==4.67.1
17+
#
18+
# The script can be executed with
19+
# $ python create_blur_animation.py
20+
# The output animation will be saved directly in the fig folder where the markdown lesson file will pick it up
21+
###
22+
23+
### POTENTIAL IMPROVEMENTS
24+
# - Change colors for rectangular patches in animation
25+
# - Ask for image input instead of hard-coding it
26+
# - Ask for FPS as input
27+
# - Ask for animation format output
28+
29+
# Import packages
30+
import numpy as np
31+
from scipy.ndimage import convolve
32+
from matplotlib import pyplot as plt
33+
from matplotlib import patches as p
34+
from matplotlib.animation import FuncAnimation
35+
from tqdm import tqdm
36+
37+
# Path to input and output images
38+
data_path = "../../../data/"
39+
fig_path = "../../../fig/"
40+
input_file = data_path + "letterA.tif"
41+
output_file = fig_path + "blur-demo.gif"
42+
43+
# Change here colors to improve accessibility
44+
kernel_color = "tab:red"
45+
center_color = "tab:olive"
46+
kernel_size = 3
47+
48+
### ANIMATION FUNCTIONS
49+
def init():
50+
"""
51+
Initialization function
52+
- Set image array data
53+
- Autoscale image display
54+
- Set XY coordinates of rectangular patches
55+
"""
56+
im.set_array(img_convolved)
57+
im.autoscale()
58+
k_rect.set_xy((-0.5, -0.5))
59+
c_rect1.set_xy((kernel_size / 2 - 1, kernel_size / 2 - 1))
60+
return [im, k_rect, c_rect1]
61+
62+
def update(frame):
63+
"""
64+
Animation update function. For every frame do the following:
65+
- Update X and Y coordinates of rectangular patch for kernel
66+
- Update X and Y coordinates of rectangular patch for central pixel
67+
- Update blurred image frame
68+
"""
69+
pbar.update(1)
70+
row = (frame % total_frames) // (img_pad.shape[0] - kernel_size + 1)
71+
col = (frame % total_frames) % (img_pad.shape[1] - kernel_size + 1)
72+
73+
k_rect.set_x(col - 0.5)
74+
c_rect1.set_x(col + (kernel_size/2 - 1))
75+
k_rect.set_y(row - 0.5)
76+
c_rect1.set_y(row + (kernel_size/2 - 1))
77+
78+
im.set_array(all_frames[frame])
79+
im.autoscale()
80+
81+
return [im, k_rect, c_rect1]
82+
83+
# MAIN PROGRAM
84+
if __name__ == "__main__":
85+
86+
print(f"Creating blurring animation with kernel size: {kernel_size}")
87+
88+
# Load image
89+
img = plt.imread(input_file)
90+
91+
### HERE WE USE THE CONVOLVE FUNCTION TO GET THE FINAL BLURRED IMAGE
92+
# I chose a simple mean filter (equal kernel weights)
93+
kernel = np.ones(shape=(kernel_size, kernel_size)) / kernel_size ** 2 # create kernel
94+
# convolve the image, i.e., apply mean filter
95+
img_convolved = convolve(img, kernel, mode='constant', cval=0) # pad borders with zero like below for consistency
96+
97+
98+
### HERE WE CONVOLVE MANUALLY STEP-BY-STEP TO CREATE ANIMATION
99+
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
100+
new_img = np.zeros(img.shape, dtype=np.uint16) # this will be the blurred final image
101+
102+
# add first frame with complete blurred image for print version of GIF
103+
all_frames = [img_convolved]
104+
105+
# precompute animation frames and append to the list
106+
total_frames = (img_pad.shape[0] - kernel_size + 1) * (img_pad.shape[1] - kernel_size + 1) # total frames if by chance image is not squared
107+
for frame in range(total_frames):
108+
row = (frame % total_frames) // (img_pad.shape[0] - kernel_size + 1) # row index
109+
col = (frame % total_frames) % (img_pad.shape[1] - kernel_size + 1) # col index
110+
img_chunk = img_pad[row:row + kernel_size, col:col + kernel_size] # get current image chunk inside the kernel
111+
new_img[row, col] = np.mean(img_chunk).astype(np.uint16) # calculate its mean -> mean filter
112+
all_frames.append(new_img.copy()) # append to animation frames list
113+
114+
# We now have an extra frame
115+
total_frames += 1
116+
117+
### FROM HERE WE START CREATING THE ANIMATION
118+
# Initialize canvas
119+
f, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 5))
120+
121+
# Display the padded image -> this one won't change during the animation
122+
ax1.imshow(img_pad, cmap="gray")
123+
# Initialize the blurred image -> this is the first frame with already the final result
124+
im = ax2.imshow(img_convolved, animated=True, cmap="gray")
125+
126+
# Define rectangular patches to identify moving kernel
127+
k_rect = p.Rectangle((-0.5, -0.5), kernel_size, kernel_size, linewidth=2, edgecolor=kernel_color, facecolor="none", alpha=0.8) # kernel rectangle
128+
c_rect1 = p.Rectangle(((kernel_size/2 - 1), (kernel_size/2 - 1)), 1, 1, linewidth=2, edgecolor=center_color, facecolor="none") # central pixel rectangle
129+
# Add them to the figure
130+
ax1.add_patch(k_rect)
131+
ax1.add_patch(c_rect1)
132+
133+
# Fix limits of the image on the right (without padding) so that it is the same size as the image on the left (with padding)
134+
ax2.set(
135+
ylim=((img_pad.shape[0] - kernel_size / 2), -kernel_size / 2),
136+
xlim=(-kernel_size / 2, (img_pad.shape[1] - kernel_size / 2))
137+
)
138+
139+
# We don't need to see the ticks
140+
ax1.axis("off")
141+
ax2.axis("off")
142+
143+
# Create progress bar to visualize animation progress
144+
pbar = tqdm(total=total_frames)
145+
146+
### HERE WE CREATE THE ANIMATION
147+
# Use FuncAnimation to create the animation
148+
ani = FuncAnimation(
149+
f, update,
150+
frames=range(total_frames),
151+
interval=50, # we could change the animation speed
152+
init_func=init,
153+
blit=True
154+
)
155+
156+
# Export animation
157+
plt.tight_layout()
158+
ani.save(output_file)
159+
pbar.close()
160+
print("Animation exported")

files/environment.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: dc-image
2+
channels:
3+
- conda-forge
4+
dependencies:
5+
- python>=3.11
6+
- jupyterlab
7+
- numpy
8+
- matplotlib
9+
- scikit-image
10+
- ipympl
11+
- imageio

md5sum.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
"file" "checksum" "built" "date"
22
"CODE_OF_CONDUCT.md" "c93c83c630db2fe2462240bf72552548" "site/built/CODE_OF_CONDUCT.md" "2023-04-25"
3-
"LICENSE.md" "b24ebbb41b14ca25cf6b8216dda83e5f" "site/built/LICENSE.md" "2023-04-25"
3+
"LICENSE.md" "e94126b93e27bae014999c2e84ee93f8" "site/built/LICENSE.md" "2026-02-10"
44
"config.yaml" "101b3ac4b679126bb1f437306eb1b836" "site/built/config.yaml" "2023-04-25"
55
"index.md" "6e80c662708984307918adfad711e15f" "site/built/index.md" "2024-11-24"
6-
"episodes/01-introduction.md" "9755639c515fdbf752422e2e59128f63" "site/built/01-introduction.md" "2024-11-24"
7-
"episodes/02-image-basics.md" "f1c4a800b8213f11b6e2efbe40f501ab" "site/built/02-image-basics.md" "2024-11-24"
6+
"episodes/01-introduction.md" "4fe9db38f75f93b3ffbdb3115d36b802" "site/built/01-introduction.md" "2026-02-10"
7+
"episodes/02-image-basics.md" "e96045fe38777bd24cf11b76464f2eca" "site/built/02-image-basics.md" "2026-02-10"
88
"episodes/03-skimage-images.md" "d7890de460222e8cdf461c76cba37692" "site/built/03-skimage-images.md" "2024-11-24"
99
"episodes/04-drawing.md" "48b42ee384b5b907d9f9b93ad0e98ce8" "site/built/04-drawing.md" "2024-11-24"
1010
"episodes/05-creating-histograms.md" "4abbd123ba97d63c795398be6f6b7621" "site/built/05-creating-histograms.md" "2024-11-24"
11-
"episodes/06-blurring.md" "894ff33379584a8ee777f779564d5b01" "site/built/06-blurring.md" "2024-11-24"
12-
"episodes/07-thresholding.md" "4d34b89c8cd33cb6bb54103f805a39b9" "site/built/07-thresholding.md" "2024-11-24"
11+
"episodes/06-blurring.md" "5e65bcd38a39e786b6a6b37775cd5ed9" "site/built/06-blurring.md" "2026-02-10"
12+
"episodes/07-thresholding.md" "512a1806b8061dded3a68871e7bcdfe9" "site/built/07-thresholding.md" "2026-02-10"
1313
"episodes/08-connected-components.md" "f599af69b770c7234a4e7d4a06a7f6dd" "site/built/08-connected-components.md" "2024-11-24"
1414
"episodes/09-challenges.md" "3e95485b1bae2c37538830225ea26598" "site/built/09-challenges.md" "2024-11-24"
1515
"instructors/instructor-notes.md" "e8e378a5dfaec7b2873d788be85003ce" "site/built/instructor-notes.md" "2024-11-24"
1616
"learners/discuss.md" "ad762c335f99400dc2cd1a8aad36bdbd" "site/built/discuss.md" "2024-11-24"
1717
"learners/edge-detection.md" "fdbcee7436e104e6587e1cb40cd37d18" "site/built/edge-detection.md" "2024-11-24"
1818
"learners/prereqs.md" "7ca883d3d01d18c98ce7524ed297e56c" "site/built/prereqs.md" "2024-11-24"
1919
"learners/reference.md" "7ae9deea84007c6ed1339955ac23f4b7" "site/built/reference.md" "2023-04-26"
20-
"learners/setup.md" "7edbbef5b2cb020d40d88c9cb0dcd98f" "site/built/setup.md" "2024-11-24"
20+
"learners/setup.md" "36ceef28a0c5cbd8e7f924dc0d151893" "site/built/setup.md" "2026-02-10"
2121
"profiles/learner-profiles.md" "60b93493cf1da06dfd63255d73854461" "site/built/learner-profiles.md" "2023-04-25"

0 commit comments

Comments
 (0)