FAU Erlangen-Nürnberg
Institute of Micro- and Nanostructure Research



=; Python infers the type automatically.pixel_size_nm beats x.def normalize(image):
"""Min-max normalize an image array to [0, 1].
Note: a constant image (max==min) yields NaN; guard with
`if hi==lo: return np.zeros_like(...)` in real pipelines.
"""
img_min = image.min()
img_max = image.max()
return (image - img_min) / (img_max - img_min)
# Call it
normalized = normalize(my_stem_image)def defines a function; the docstring ("""...""") documents what it does.shape is a tuple: (rows, cols) for 2-D; (x, y, kx, ky) for a 4D-STEM cube.dtype governs storage: float32 (4 bytes/element) vs float64 (8 bytes/element).# Zeros and ones (useful initialisers)
zeros = np.zeros((512, 512)) # blank image canvas
ones = np.ones((3, 3))
# Range and linspace
x = np.arange(0, 10, 0.5) # 0, 0.5, 1.0, …, 9.5
q = np.linspace(0, 1, 128) # 128 evenly spaced values in [0, 1]
# Random noise (Gaussian approximation — Week 2 shows why Poisson is the correct low-dose model)
noise = np.random.randn(512, 512) # Gaussian, mean=0, std=1# Load (simulate) a 512×512 HAADF image
stem_image = np.random.poisson(lam=200, size=(512, 512)).astype(np.float32)
print(stem_image.shape) # (512, 512)
print(stem_image.dtype) # float32
# Crop a 64×64 ROI from the centre
cx, cy = 256, 256
roi = stem_image[cy-32:cy+32, cx-32:cx+32]
print(roi.shape) # (64, 64)
# Single pixel value
print(stem_image[0, 0]) # top-left pixelarray[start:stop] — stop is exclusive.stem_image[-1, :] is the last row.image = np.random.poisson(200, (512, 512)).astype(float) # (512, 512)
dark = np.array([5.0]) # scalar
# Broadcasting: scalar applies to every element
corrected = image - dark # same shape as image
# Row-wise mean subtraction
row_mean = image.mean(axis=1, keepdims=True) # shape (512, 1)
row_corrected = image - row_mean # shape (512, 512)(512, 512) - (512, 1) → the column of means is subtracted from every column.import matplotlib.pyplot as plt
import numpy as np
# Synthetic HAADF-like image: bright atoms on dark background
stem = np.random.poisson(lam=50, size=(256, 256)).astype(float)
# Add a few "atoms" (bright spots)
for (r, c) in [(64, 64), (128, 128), (192, 64), (64, 192)]:
stem[r-4:r+4, c-4:c+4] += 500
fig, ax = plt.subplots(figsize=(5, 5))
im = ax.imshow(stem, cmap='gray', origin='upper')
plt.colorbar(im, ax=ax, label='Counts')
ax.set_title('Synthetic HAADF-STEM image')
ax.set_xlabel('x (pixels)')
ax.set_ylabel('y (pixels)')
plt.tight_layout()
plt.show()# Extract and plot a horizontal line profile (common in HAADF)
row = 128
profile = stem[row, :] # 1-D array of length 256
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].imshow(stem, cmap='gray')
axes[0].axhline(row, color='red', lw=1)
axes[0].set_title('STEM image')
axes[1].plot(profile)
axes[1].set_xlabel('Column (px)')
axes[1].set_ylabel('Intensity (counts)')
axes[1].set_title(f'Line profile at row {row}')
plt.tight_layout()
plt.show().ipynb notebook file.import numpy as np
import matplotlib.pyplot as plt
# Simulate loading a HAADF-STEM image (replace with dm3/tif in practice)
# Shape: (height, width) — here 512×512 pixels at 0.05 nm/px
np.random.seed(42)
base = np.zeros((512, 512))
# Crystalline columns on a 20 px lattice
for r in range(10, 512, 20):
for c in range(10, 512, 20):
base[r-2:r+3, c-2:c+3] = 1.0
# Add Poisson noise (realistic detector statistics)
stem_image = np.random.poisson(lam=(base * 800 + 50)).astype(np.float32)
print("Shape :", stem_image.shape) # (512, 512)
print("Dtype :", stem_image.dtype) # float32
print("Min :", stem_image.min())
print("Max :", stem_image.max())tifffile.imread('image.tif') or hyperspy.load('image.dm3') return the same ndarray.jupyter nbconvert --execute notebook.ipynb — it must produce all figures without manual steps.notebooks/week01_python_numpy.ipynb — “Python & NumPy in 90 minutes.”

©Philipp Pelz - FAU Erlangen-Nürnberg - Data Science for Electron Microscopy