import requests
from PIL import Image
from io import BytesIO
import numpy as np
import matplotlib.pyplot as plt
from skimage.color import rgb2gray
from skimage.filters import threshold_multiotsu
def posterize(img, classes=3):
gray = rgb2gray(img) if img.ndim == 3 else img
thresholds = threshold_multiotsu(gray, classes=classes)
levels = np.digitize(gray, bins=thresholds)
return (levels * (255 // (classes - 1))).astype(np.uint8)
def show(img, title, cmap=None, colorbar=False):
plt.figure(figsize=(8, 8))
im = plt.imshow(img, cmap=cmap)
plt.title(title); plt.axis('off')
if colorbar: plt.colorbar(im, fraction=0.03)
plt.show()
def show_posterize(imgs: list, titles=None, classes=3):
"""imgs: list of numpy arrays (RGB or gray)"""
n = len(imgs)
fig, axes = plt.subplots(n, 2, figsize=(8, 4 * n))
if n == 1:
axes = [axes] # normalize shape
for i, img in enumerate(imgs):
result = posterize(img, classes)
axes[i][0].imshow(img, cmap="gray" if img.ndim == 2 else None)
axes[i][0].set_title(titles[i] if titles else f"Original {i+1}")
axes[i][1].imshow(result, cmap="gray", vmin=0, vmax=255)
axes[i][1].set_title(f"Posterized ({classes} levels)")
for ax in axes[i]:
ax.axis("off")
plt.tight_layout()
plt.show()
davids_head_url = r"https://upload.wikimedia.org/wikipedia/commons/thumb/8/86/%27David%27_by_Michelangelo_Fir_JBU013.jpg/250px-%27David%27_by_Michelangelo_Fir_JBU013.jpg?utm_source=commons.wikimedia.org&utm_campaign=index&utm_content=thumbnail" # Using a more direct URL
# Define headers to mimic a browser request
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}
# Download the image with headers
response = requests.get(davids_head_url, headers=headers)
response.raise_for_status() # Check for HTTP errors
img_data = Image.open(BytesIO(response.content))
# Convert to numpy array
davids_head_img = np.array(img_data.convert('L')) # Convert to grayscale for posterize function
show_posterize([davids_head_img], titles=['David\'s Head'], classes=3)
# add code to show processed image here:
from skimage.filters.rank import mean_bilateral # or scipy
from skimage.exposure import equalize_adapthist
def posterize_photo(img, classes=3, highlight_thresh=0.93):
gray = rgb2gray(img) if img.ndim == 3 else img.astype(float) / 255
# 1. Edge-preserving smooth — kills noise, keeps contours sharp
# Optimized: Reduced sigma_spatial for faster computation
from skimage.restoration import denoise_bilateral
smooth = denoise_bilateral(gray, sigma_color=0.01, sigma_spatial=1) # Smaller sigma_spatial
# 2. CLAHE — lifts midtone separation locally
# Optimized: Added kernel_size for faster computation
enhanced = equalize_adapthist(smooth, clip_limit=0.02, kernel_size=(128, 128)) # Smaller kernel_size (default is (8,8) or larger depending on image size)
# 3. Isolate specular highlights BEFORE Otsu sees them
highlight_mask = enhanced > highlight_thresh
# 4. Run Otsu only on non-highlight pixels
non_highlight = enhanced[~highlight_mask]
thresholds = threshold_multiotsu(non_highlight, classes=classes - 1)
# 5. Assemble result
result = np.digitize(enhanced, bins=thresholds) # 0, 1, 2
result[highlight_mask] = classes - 1 # force highlights → white
return (result * (255 // (classes - 1))).astype(np.uint8)
_tweaked = posterize_photo(davids_head_img, classes=3, highlight_thresh=0.77)
show(_tweaked, 'David\'s Head - Tweaked', cmap="gray", colorbar=False)
from skimage.exposure import adjust_gamma, rescale_intensity
from skimage.filters import gaussian
def posterize_photo2(img, classes=3, gamma=0.8, blur=1.2):
gray = rgb2gray(img) if img.ndim == 3 else img
# stretch full tonal range first
stretched = rescale_intensity(gray)
# gamma < 1 lifts shadows → better midtone separation
adjusted = adjust_gamma(stretched, gamma=gamma)
# cheap noise removal, preserves edges better than bilateral at this sigma
smoothed = gaussian(adjusted, sigma=blur)
thresholds = threshold_multiotsu(smoothed, classes=classes)
result = np.digitize(smoothed, bins=thresholds)
return (result * (255 // (classes - 1))).astype(np.uint8)
_tweaked = posterize_photo2(davids_head_img, classes=3, gamma=1.2, blur=1.5)
show(_tweaked, 'David\'s Head - Tweaked', cmap="gray", colorbar=False)
_tweaked = posterize_photo2(davids_head_img, classes=3, gamma=0.6, blur=1.5)