Merged Notebooks
Merged Notebooks
1
# Create BLUE plane (keep blue, set others to 0)
blue_plane = img_array.copy()
blue_plane[:, :, 0] = 0 # Set red channel to 0
blue_plane[:, :, 1] = 0 # Set green channel to 0
plt.subplot(2, 3, 3)
plt.imshow(green_plane)
plt.title('Green Plane')
plt.axis('off')
plt.subplot(2, 3, 4)
plt.imshow(blue_plane)
plt.title('Blue Plane')
plt.axis('off')
plt.subplot(2, 3, 6)
plt.imshow(img_binary, cmap='binary')
plt.title('Binary Image')
plt.axis('off')
plt.tight_layout()
plt.show()
2
img_gray.save('image_gray.png')
Image.fromarray(img_binary).save('image_binary.png')
plt.subplot(2, 3, 1)
plt.imshow(img_half)
plt.title('Half Size')
plt.axis('off')
plt.subplot(2, 3, 2)
plt.imshow(img_quarter)
plt.title('Quarter Size')
plt.axis('off')
plt.subplot(2, 3, 3)
plt.imshow(img_45)
plt.title('Rotated 45°')
plt.axis('off')
plt.subplot(2, 3, 4)
plt.imshow(img_90)
plt.title('Rotated 90°')
plt.axis('off')
plt.subplot(2, 3, 5)
plt.imshow(img_180)
plt.title('Rotated 180°')
plt.axis('off')
plt.tight_layout()
plt.show()
3
print("All operations completed successfully!")
4
All operations completed successfully!
[ ]:
5
img_array[:, i:i+stripe_width] = 255
return Image.fromarray(img_array)
plt.subplot(1, 2, 2)
plt.imshow(B, cmap='gray')
plt.title('Image B - Vertical Stripes')
plt.axis('off')
plt.tight_layout()
plt.show()
# b. Subtraction of A and B
subtraction = np.clip(A_array - B_array, 0, 255).astype(np.uint8)
subtraction_img = Image.fromarray(subtraction)
6
plt.subplot(1, 3, 2)
plt.imshow(subtraction_img, cmap='gray')
plt.title('A - B')
plt.axis('off')
plt.subplot(1, 3, 3)
plt.imshow(multiplication_img, cmap='gray')
plt.title('A * B')
plt.axis('off')
plt.tight_layout()
plt.show()
plt.figure(figsize=(12, 4))
plt.imshow(sinusoidal_img, cmap='gray')
plt.title('Sinusoidal Intensity Image')
plt.axis('off')
plt.show()
7
# Draw black box
draw.rectangle([x1, y1, x2, y2], fill=0)
return img
plt.figure(figsize=(6, 6))
plt.imshow(box_img, cmap='gray')
plt.title('White Image with Black Box (58x58) at Center')
plt.axis('off')
plt.show()
8
All operations completed successfully!
[ ]:
9
[1]: import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
# a. Image negative
def image_negative(img_array):
# Calculate negative: s = L-1-r where L is max intensity (255 for 8-bit)
return 255 - img_array
negative = image_negative(img_array)
plt.figure(figsize=(5, 5))
plt.imshow(negative, cmap='gray')
plt.title('Image Negative')
plt.axis('off')
plt.show()
for i, c in enumerate(c_values):
# Apply log transform
log_img = log_transform(img_array, c)
10
log_img = 255 * (log_img - np.min(log_img)) / (np.max(log_img) - np.
↪min(log_img))
log_img = log_img.astype(np.uint8)
inverse_log_img = inverse_log_img.astype(np.uint8)
plt.tight_layout()
plt.show()
plt.tight_layout()
plt.show()
11
# d. Contrast stretching
def contrast_stretching(img_array, r1, s1, r2, s2):
# Create an empty array with the same shape as input
result = np.zeros_like(img_array, dtype=np.float64)
plt.figure(figsize=(15, 5))
plt.tight_layout()
plt.show()
12
# e. Gray level slicing
def gray_level_slicing(img_array, lower, upper, background=False):
result = np.copy(img_array).astype(np.float64)
if background:
# Highlight range, preserve the rest
result[mask] = 255
else:
# Highlight range, set the rest to 0
result[~mask] = 0
result[mask] = 255
return result.astype(np.uint8)
plt.figure(figsize=(15, 5))
plt.tight_layout()
plt.show()
13
14
15
All intensity transformations completed!
[ ]:
16
[ ]: import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
# a. Averaging Filter
def averaging_filter(image, kernel_size):
# Create padded image
pad_size = kernel_size // 2
padded = pad_image(image, pad_size)
# Apply filter
for i in range(height):
for j in range(width):
# Extract neighborhood
neighborhood = padded[i:i+kernel_size, j:j+kernel_size]
# Calculate average
output[i, j] = np.mean(neighborhood)
return output.astype(np.uint8)
17
plt.subplot(2, 3, 1)
plt.imshow(img_array, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(2, 3, i+2)
plt.imshow(filtered_img, cmap='gray')
plt.title(f'Averaging Filter {size}x{size}')
plt.axis('off')
plt.tight_layout()
plt.show()
# Apply filter
for i in range(height):
for j in range(width):
# Extract neighborhood
neighborhood = padded[i:i+kernel_size, j:j+kernel_size]
# Apply weighted average
output[i, j] = np.sum(neighborhood * kernel)
18
return output.astype(np.uint8)
plt.subplot(2, 3, i+2)
plt.imshow(filtered_img, cmap='gray')
plt.title(f'Weighted Averaging Filter {size}x{size}')
plt.axis('off')
plt.tight_layout()
plt.show()
# c. Median Filtering
def median_filter(image, kernel_size):
# Create padded image
pad_size = kernel_size // 2
padded = pad_image(image, pad_size)
# Apply filter
for i in range(height):
for j in range(width):
# Extract neighborhood
neighborhood = padded[i:i+kernel_size, j:j+kernel_size]
# Calculate median
output[i, j] = np.median(neighborhood)
return output.astype(np.uint8)
19
plt.imshow(img_array, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(2, 3, i+2)
plt.imshow(filtered_img, cmap='gray')
plt.title(f'Median Filter {size}x{size}')
plt.axis('off')
plt.tight_layout()
plt.show()
# d. Max Filtering
def max_filter(image, kernel_size):
# Create padded image
pad_size = kernel_size // 2
padded = pad_image(image, pad_size)
# Apply filter
for i in range(height):
for j in range(width):
# Extract neighborhood
neighborhood = padded[i:i+kernel_size, j:j+kernel_size]
# Calculate maximum
output[i, j] = np.max(neighborhood)
return output.astype(np.uint8)
# e. Min Filtering
def min_filter(image, kernel_size):
# Create padded image
pad_size = kernel_size // 2
padded = pad_image(image, pad_size)
# Apply filter
for i in range(height):
20
for j in range(width):
# Extract neighborhood
neighborhood = padded[i:i+kernel_size, j:j+kernel_size]
# Calculate minimum
output[i, j] = np.min(neighborhood)
return output.astype(np.uint8)
plt.figure(figsize=(15, 5))
plt.subplot(1, 3, 1)
plt.imshow(img_array, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(1, 3, 2)
plt.imshow(max_filtered, cmap='gray')
plt.title(f'Max Filter {filter_size}x{filter_size}')
plt.axis('off')
plt.subplot(1, 3, 3)
plt.imshow(min_filtered, cmap='gray')
plt.title(f'Min Filter {filter_size}x{filter_size}')
plt.axis('off')
plt.tight_layout()
plt.show()
plt.figure(figsize=(15, 10))
plt.subplot(2, 3, 1)
plt.imshow(img_array, cmap='gray')
plt.title('Original Image')
plt.axis('off')
21
plt.subplot(2, 3, 2)
plt.imshow(avg_img, cmap='gray')
plt.title(f'Average Filter {comparison_size}x{comparison_size}')
plt.axis('off')
plt.subplot(2, 3, 3)
plt.imshow(weighted_img, cmap='gray')
plt.title(f'Weighted Average {comparison_size}x{comparison_size}')
plt.axis('off')
plt.subplot(2, 3, 4)
plt.imshow(median_img, cmap='gray')
plt.title(f'Median Filter {comparison_size}x{comparison_size}')
plt.axis('off')
plt.subplot(2, 3, 5)
plt.imshow(max_img, cmap='gray')
plt.title(f'Max Filter {comparison_size}x{comparison_size}')
plt.axis('off')
plt.subplot(2, 3, 6)
plt.imshow(min_img, cmap='gray')
plt.title(f'Min Filter {comparison_size}x{comparison_size}')
plt.axis('off')
plt.tight_layout()
plt.show()
22
print(" - Expands light objects (dilation-like effect)")
print(" - Useful for finding light objects on dark backgrounds")
print()
print("5. Min Filter:")
print(" - Enhances dark regions")
print(" - Expands dark objects (erosion-like effect)")
print(" - Useful for finding dark objects on light backgrounds")
23
24
25
26
Observations on filtering effects:
1. Averaging Filter:
- Blur increases with window size
- Reduces noise but also reduces edge sharpness
- May create artifacts at strong edges
3. Median Filter:
- Excellent at removing salt-and-pepper noise
- Preserves edges better than averaging
- Larger windows may remove fine details
4. Max Filter:
- Enhances bright regions
- Expands light objects (dilation-like effect)
- Useful for finding light objects on dark backgrounds
27
5. Min Filter:
- Enhances dark regions
- Expands dark objects (erosion-like effect)
- Useful for finding dark objects on light backgrounds
[ ]:
noisy_image[pepper_coords[0], pepper_coords[1]] = 0
return noisy_image
plt.subplot(1, 2, 2)
plt.imshow(noisy_img, cmap='gray')
28
plt.title('Image with Salt & Pepper Noise')
plt.axis('off')
plt.tight_layout()
plt.show()
# Apply filter
for i in range(height):
for j in range(width):
# Extract neighborhood
neighborhood = padded[i:i+kernel_size, j:j+kernel_size]
# Calculate average
output[i, j] = np.mean(neighborhood)
return output.astype(np.uint8)
29
for j in range(kernel_size):
distance = np.sqrt((i - center)**2 + (j - center)**2)
kernel[i, j] = np.exp(-0.5 * (distance**2))
# Apply filter
for i in range(height):
for j in range(width):
# Extract neighborhood
neighborhood = padded[i:i+kernel_size, j:j+kernel_size]
# Apply weighted average
output[i, j] = np.sum(neighborhood * kernel)
return output.astype(np.uint8)
# c. Median Filtering
def median_filter(image, kernel_size):
# Create padded image
pad_size = kernel_size // 2
padded = pad_image(image, pad_size)
# Apply filter
for i in range(height):
for j in range(width):
# Extract neighborhood
neighborhood = padded[i:i+kernel_size, j:j+kernel_size]
# Calculate median
output[i, j] = np.median(neighborhood)
return output.astype(np.uint8)
# d. Max Filtering
def max_filter(image, kernel_size):
# Create padded image
pad_size = kernel_size // 2
padded = pad_image(image, pad_size)
30
# Apply filter
for i in range(height):
for j in range(width):
# Extract neighborhood
neighborhood = padded[i:i+kernel_size, j:j+kernel_size]
# Calculate maximum
output[i, j] = np.max(neighborhood)
return output.astype(np.uint8)
# e. Min Filtering
def min_filter(image, kernel_size):
# Create padded image
pad_size = kernel_size // 2
padded = pad_image(image, pad_size)
# Apply filter
for i in range(height):
for j in range(width):
# Extract neighborhood
neighborhood = padded[i:i+kernel_size, j:j+kernel_size]
# Calculate minimum
output[i, j] = np.min(neighborhood)
return output.astype(np.uint8)
# Create a dictionary to store results for each filter type and kernel size
results = {}
results[f'avg_{size}'] = avg_img
results[f'weighted_{size}'] = weighted_img
results[f'median_{size}'] = median_img
results[f'max_{size}'] = max_img
31
results[f'min_{size}'] = min_img
if mse == 0:
return float('inf')
max_pixel = 255.0
psnr = 20 * np.log10(max_pixel / np.sqrt(mse))
return psnr
plt.subplot(2, 3, 1)
plt.imshow(img_array, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(2, 3, 2)
plt.imshow(noisy_img, cmap='gray')
plt.title('Noisy Image')
plt.axis('off')
plt.subplot(2, 3, 3)
plt.imshow(results['avg_3'], cmap='gray')
plt.title(f'Average Filter 3x3\nPSNR: {psnr_values["avg_3"]:.2f} dB')
plt.axis('off')
plt.subplot(2, 3, 4)
plt.imshow(results['weighted_3'], cmap='gray')
plt.title(f'Weighted Average 3x3\nPSNR: {psnr_values["weighted_3"]:.2f} dB')
plt.axis('off')
plt.subplot(2, 3, 5)
plt.imshow(results['median_3'], cmap='gray')
plt.title(f'Median Filter 3x3\nPSNR: {psnr_values["median_3"]:.2f} dB')
plt.axis('off')
plt.tight_layout()
plt.show()
32
# Compare averaging vs median filtering with different kernel sizes
# FIX: Create a 2 x 3 figure instead of 2 x 3 with indices that go out of bounds
plt.figure(figsize=(15, 10))
plt.axis('off')
plt.axis('off')
plt.tight_layout()
plt.show()
plt.subplot(2, 3, 1)
plt.imshow(img_array, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(2, 3, 2)
plt.imshow(noisy_img, cmap='gray')
plt.title('Noisy Image')
plt.axis('off')
plt.subplot(2, 3, 3)
plt.imshow(results['avg_5'], cmap='gray')
plt.title(f'Average Filter 5x5\nPSNR: {psnr_values["avg_5"]:.2f} dB')
plt.axis('off')
33
plt.subplot(2, 3, 4)
plt.imshow(results['weighted_5'], cmap='gray')
plt.title(f'Weighted Average 5x5\nPSNR: {psnr_values["weighted_5"]:.2f} dB')
plt.axis('off')
plt.subplot(2, 3, 5)
plt.imshow(results['median_5'], cmap='gray')
plt.title(f'Median Filter 5x5\nPSNR: {psnr_values["median_5"]:.2f} dB')
plt.axis('off')
plt.tight_layout()
plt.show()
plt.subplot(1, 3, 1)
plt.imshow(noisy_img, cmap='gray')
plt.title('Noisy Image')
plt.axis('off')
plt.subplot(1, 3, 2)
plt.imshow(results['max_3'], cmap='gray')
plt.title(f'Max Filter 3x3\nPSNR: {psnr_values["max_3"]:.2f} dB')
plt.axis('off')
plt.subplot(1, 3, 3)
plt.imshow(results['min_3'], cmap='gray')
plt.title(f'Min Filter 3x3\nPSNR: {psnr_values["min_3"]:.2f} dB')
plt.axis('off')
plt.tight_layout()
plt.show()
34
print("\n3. Median Filter:")
print(" - Excellent at removing salt and pepper noise")
print(" - Preserves edges much better than averaging filters")
print(" - Typically achieves highest PSNR values for salt and pepper noise")
print(" - Larger window sizes may remove small details")
35
36
37
Comparative Analysis of Filtering Techniques for Salt and Pepper Noise:
===================================================================
1. Linear Smoothing (Average Filtering):
- Moderately effective at removing salt and pepper noise
- Tends to blur edges and fine details
- Performance increases with kernel size but at cost of more blurring
38
2. Weighted Averaging Filter:
- Slightly better than uniform averaging at preserving edges
- Still ineffective at completely removing impulse noise
- Creates a more natural blur than uniform averaging
3. Median Filter:
- Excellent at removing salt and pepper noise
- Preserves edges much better than averaging filters
- Typically achieves highest PSNR values for salt and pepper noise
- Larger window sizes may remove small details
4. Max Filter:
- Good at removing 'pepper' noise (black pixels)
- Enlarges 'salt' noise (white pixels)
- Brightens the overall image
5. Min Filter:
- Good at removing 'salt' noise (white pixels)
- Enlarges 'pepper' noise (black pixels)
- Darkens the overall image
[ ]:
39
def display_images(images, titles, figsize=(15, 10)):
n = len(images)
fig, axes = plt.subplots(1, n, figsize=figsize)
if n == 1:
axes = [axes]
plt.tight_layout()
plt.show()
# a. Laplacian filter
def laplacian_filter(image):
# Define the Laplacian filter kernel
laplacian_kernel = np.array([
[0, 1, 0],
[1, -4, 1],
[0, 1, 0]
])
return sharpened
40
def composite_mask_filter(image):
# Define the composite kernel (combined Laplacian with directional␣
↪component)
composite_kernel = np.array([
[-1, -1, -1],
[-1, 9, -1],
[-1, -1, -1]
])
return filtered
# c. Unsharp masking
def unsharp_masking(image, sigma=1.0, amount=1.0):
# Create a blurred version of the image
blurred = ndimage.gaussian_filter(image, sigma=sigma)
return sharpened
41
# Note: Since laplacian represents edges, we subtract it (it's already␣
↪negative for edges)
high_boost = image - k * laplacian
return high_boost
sobel_y = np.array([
[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]
])
def prewitt_filter(image):
# Define Prewitt kernels
prewitt_x = np.array([
[-1, 0, 1],
[-1, 0, 1],
[-1, 0, 1]
])
42
prewitt_y = np.array([
[-1, -1, -1],
[0, 0, 0],
[1, 1, 1]
])
43
display_images(
[original_img, laplacian_filtered, composite_filtered, unsharp_filtered,␣
↪high_boost_filtered, sobel_sharpened, prewitt_sharpened],
figsize=(20, 10)
)
44
[ ]:
Note: you may need to restart the kernel to use updated packages.
45
scikit-image) (2.2.1)
Requirement already satisfied: scipy>=1.11.4 in
c:\users\dkg19\appdata\local\programs\python\python313\lib\site-packages (from
scikit-image) (1.15.1)
Requirement already satisfied: networkx>=3.0 in
c:\users\dkg19\appdata\local\programs\python\python313\lib\site-packages (from
scikit-image) (3.4.2)
Requirement already satisfied: pillow>=10.1 in
c:\users\dkg19\appdata\local\programs\python\python313\lib\site-packages (from
scikit-image) (11.1.0)
Collecting imageio!=2.35.0,>=2.33 (from scikit-image)
Downloading imageio-2.37.0-py3-none-any.whl.metadata (5.2 kB)
Collecting tifffile>=2022.8.12 (from scikit-image)
Downloading tifffile-2025.3.30-py3-none-any.whl.metadata (32 kB)
Requirement already satisfied: packaging>=21 in
c:\users\dkg19\appdata\roaming\python\python313\site-packages (from scikit-
image) (24.2)
Collecting lazy-loader>=0.4 (from scikit-image)
Downloading lazy_loader-0.4-py3-none-any.whl.metadata (7.6 kB)
Downloading scikit_image-0.25.2-cp313-cp313-win_amd64.whl (12.9 MB)
---------------------------------------- 0.0/12.9 MB ? eta -:--:--
--------------------------------------- 0.3/12.9 MB ? eta -:--:--
--------------------------------------- 0.3/12.9 MB ? eta -:--:--
- -------------------------------------- 0.5/12.9 MB 866.1 kB/s eta 0:00:15
- -------------------------------------- 0.5/12.9 MB 866.1 kB/s eta 0:00:15
-- ------------------------------------- 0.8/12.9 MB 687.3 kB/s eta 0:00:18
-- ------------------------------------- 0.8/12.9 MB 687.3 kB/s eta 0:00:18
--- ------------------------------------ 1.0/12.9 MB 705.4 kB/s eta 0:00:17
---- ----------------------------------- 1.3/12.9 MB 741.1 kB/s eta 0:00:16
---- ----------------------------------- 1.3/12.9 MB 741.1 kB/s eta 0:00:16
---- ----------------------------------- 1.3/12.9 MB 741.1 kB/s eta 0:00:16
---- ----------------------------------- 1.6/12.9 MB 633.3 kB/s eta 0:00:18
---- ----------------------------------- 1.6/12.9 MB 633.3 kB/s eta 0:00:18
----- ---------------------------------- 1.8/12.9 MB 627.7 kB/s eta 0:00:18
----- ---------------------------------- 1.8/12.9 MB 627.7 kB/s eta 0:00:18
------ --------------------------------- 2.1/12.9 MB 622.9 kB/s eta 0:00:18
------ --------------------------------- 2.1/12.9 MB 622.9 kB/s eta 0:00:18
------- -------------------------------- 2.4/12.9 MB 647.3 kB/s eta 0:00:17
-------- ------------------------------- 2.6/12.9 MB 670.9 kB/s eta 0:00:16
-------- ------------------------------- 2.6/12.9 MB 670.9 kB/s eta 0:00:16
-------- ------------------------------- 2.9/12.9 MB 684.6 kB/s eta 0:00:15
--------- ------------------------------ 3.1/12.9 MB 712.8 kB/s eta 0:00:14
--------- ------------------------------ 3.1/12.9 MB 712.8 kB/s eta 0:00:14
--------- ------------------------------ 3.1/12.9 MB 712.8 kB/s eta 0:00:14
---------- ----------------------------- 3.4/12.9 MB 673.0 kB/s eta 0:00:15
---------- ----------------------------- 3.4/12.9 MB 673.0 kB/s eta 0:00:15
----------- ---------------------------- 3.7/12.9 MB 675.6 kB/s eta 0:00:14
----------- ---------------------------- 3.7/12.9 MB 675.6 kB/s eta 0:00:14
46
----------- ---------------------------- 3.7/12.9 MB 675.6 kB/s eta 0:00:14
------------ --------------------------- 3.9/12.9 MB 637.4 kB/s eta 0:00:15
------------ --------------------------- 3.9/12.9 MB 637.4 kB/s eta 0:00:15
------------ --------------------------- 3.9/12.9 MB 637.4 kB/s eta 0:00:15
------------- -------------------------- 4.2/12.9 MB 619.0 kB/s eta 0:00:15
------------- -------------------------- 4.2/12.9 MB 619.0 kB/s eta 0:00:15
------------- -------------------------- 4.2/12.9 MB 619.0 kB/s eta 0:00:15
------------- -------------------------- 4.5/12.9 MB 606.1 kB/s eta 0:00:14
------------- -------------------------- 4.5/12.9 MB 606.1 kB/s eta 0:00:14
-------------- ------------------------- 4.7/12.9 MB 595.4 kB/s eta 0:00:14
-------------- ------------------------- 4.7/12.9 MB 595.4 kB/s eta 0:00:14
--------------- ------------------------ 5.0/12.9 MB 606.4 kB/s eta 0:00:14
---------------- ----------------------- 5.2/12.9 MB 623.8 kB/s eta 0:00:13
----------------- ---------------------- 5.8/12.9 MB 661.9 kB/s eta 0:00:11
------------------ --------------------- 6.0/12.9 MB 681.7 kB/s eta 0:00:11
-------------------- ------------------- 6.6/12.9 MB 722.1 kB/s eta 0:00:09
--------------------- ------------------ 7.1/12.9 MB 760.4 kB/s eta 0:00:08
---------------------- ----------------- 7.3/12.9 MB 776.4 kB/s eta 0:00:08
----------------------- ---------------- 7.6/12.9 MB 792.5 kB/s eta 0:00:07
------------------------ --------------- 7.9/12.9 MB 806.6 kB/s eta 0:00:07
------------------------- -------------- 8.1/12.9 MB 815.8 kB/s eta 0:00:06
------------------------- -------------- 8.1/12.9 MB 815.8 kB/s eta 0:00:06
-------------------------- ------------- 8.4/12.9 MB 809.4 kB/s eta 0:00:06
-------------------------- ------------- 8.7/12.9 MB 807.9 kB/s eta 0:00:06
-------------------------- ------------- 8.7/12.9 MB 807.9 kB/s eta 0:00:06
--------------------------- ------------ 8.9/12.9 MB 799.8 kB/s eta 0:00:05
---------------------------- ----------- 9.2/12.9 MB 805.2 kB/s eta 0:00:05
---------------------------- ----------- 9.2/12.9 MB 805.2 kB/s eta 0:00:05
------------------------------ --------- 9.7/12.9 MB 823.8 kB/s eta 0:00:04
------------------------------ --------- 10.0/12.9 MB 836.0 kB/s eta 0:00:04
------------------------------- -------- 10.2/12.9 MB 844.4 kB/s eta 0:00:04
------------------------------- -------- 10.2/12.9 MB 844.4 kB/s eta 0:00:04
-------------------------------- ------- 10.5/12.9 MB 843.9 kB/s eta 0:00:03
--------------------------------- ------ 10.7/12.9 MB 839.0 kB/s eta 0:00:03
--------------------------------- ------ 10.7/12.9 MB 839.0 kB/s eta 0:00:03
---------------------------------- ----- 11.0/12.9 MB 840.9 kB/s eta 0:00:03
---------------------------------- ----- 11.3/12.9 MB 847.0 kB/s eta 0:00:02
----------------------------------- ---- 11.5/12.9 MB 856.1 kB/s eta 0:00:02
------------------------------------ --- 11.8/12.9 MB 863.9 kB/s eta 0:00:02
-------------------------------------- - 12.3/12.9 MB 880.0 kB/s eta 0:00:01
--------------------------------------- 12.6/12.9 MB 889.8 kB/s eta 0:00:01
--------------------------------------- 12.6/12.9 MB 889.8 kB/s eta 0:00:01
---------------------------------------- 12.9/12.9 MB 881.4 kB/s eta 0:00:00
Downloading imageio-2.37.0-py3-none-any.whl (315 kB)
Downloading lazy_loader-0.4-py3-none-any.whl (12 kB)
Downloading tifffile-2025.3.30-py3-none-any.whl (226 kB)
Installing collected packages: tifffile, lazy-loader, imageio, scikit-image
Successfully installed imageio-2.37.0 lazy-loader-0.4 scikit-image-0.25.2
47
tifffile-2025.3.30
Parameters:
- input_image: numpy array of the input image
- no_of_bins: number of bins to use for histogram calculation
Returns:
- enhanced_image: numpy array of the enhanced image with the same shape as␣
↪input
"""
# Check if the image is grayscale or color
if len(input_image.shape) == 2:
# Grayscale image
return _equalize_grayscale(input_image, no_of_bins)
elif len(input_image.shape) == 3 and input_image.shape[2] == 3:
# Color image
return _equalize_color(input_image, no_of_bins)
else:
raise ValueError("Unsupported image format")
48
# Create a mapping function (LUT)
mapping_function = np.interp(np.arange(256), bin_centers, cdf_normalized)
return result.astype(np.uint8)
# Convert to HSV
hsv_img = color.rgb2hsv(img_norm)
if is_grayscale:
if img.mode != 'L':
49
img = img.convert('L')
original = np.array(img)
else:
if img.mode != 'RGB':
img = img.convert('RGB')
original = np.array(img)
return results
"""
Analyze a single image with different bin counts for histogram equalization.
Parameters:
- image_path: path to the input image
- bin_values: list of bin counts to test
- is_grayscale: whether to process as grayscale
"""
# Process the image
results = process_image(image_path, bin_values, is_grayscale)
# Create figure
fig, axes = plt.subplots(2, len(bin_values)+1,␣
↪figsize=(3*(len(bin_values)+1), 6))
50
col = i + 1 # Skip column 0 (original image)
key = f'bins_{bins}'
if is_grayscale:
axes[0, col].imshow(results[key], cmap='gray')
else:
axes[0, col].imshow(results[key])
plt.tight_layout()
image_type = "Grayscale" if is_grayscale else "Color"
plt.suptitle(f'{image_type} Image - Histogram Equalization with Different␣
↪Bin Counts', fontsize=16, y=1.02)
plt.subplots_adjust(top=0.85)
plt.show()
return results
# Calculate histogram
hist, bin_edges = np.histogram(image_for_hist.flatten(), bins=no_of_bins,␣
↪range=(0, 256))
# Calculate CDF
cdf = hist.cumsum()
if cdf[-1] > 0:
cdf_normalized = (cdf * 255) / cdf[-1]
else:
cdf_normalized = np.zeros_like(cdf)
51
# Create mapping function
mapping_function = np.interp(np.arange(256), bin_centers, cdf_normalized)
# Plot
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
ax2 = ax1.twinx()
ax2.plot(bin_centers, cdf / cdf.max() if cdf.max() > 0 else cdf, 'r-',␣
↪linewidth=2)
plt.tight_layout()
plt.show()
return mapping_function
# Create figure
fig, axes = plt.subplots(1, len(bin_values) + 1, figsize=(5 *␣
↪(len(bin_values) + 1), 5))
52
if len(image.shape) == 2:
axes[0].imshow(image, cmap='gray')
else:
axes[0].imshow(image)
axes[0].set_title('Original Image')
axes[0].axis('off')
plt.tight_layout()
plt.show()
# Compare histograms
fig, axes = plt.subplots(1, len(bin_values) + 1, figsize=(5 *␣
↪(len(bin_values) + 1), 4))
plt.tight_layout()
plt.show()
# Main execution
# Your specific image paths
image_path1 = r"C:\Users\dkg19\OneDrive\Desktop\DIP LAB\image_png.png"
image_path2 = r"C:\Users\dkg19\OneDrive\Desktop\DIP LAB\image_gray.png"
53
# Define bin values to test
bin_values = [8, 32, 64, 128, 256]
# Summary of findings
print("\nSummary of Findings:")
print("1. The effect of bin count on histogram equalization:")
print(" - Lower bin counts (8-32) tend to produce more dramatic contrast␣
↪changes")
print(" - Higher bin counts (128-256) create more subtle and natural-looking␣
↪enhancements")
print(" - The optimal bin count depends on the image's initial contrast␣
↪distribution")
54
Processing grayscale image (image_gray.png)…
55
Transformation function for 32 bins:
56
Comparing detailed results for grayscale image…
57
Transformation function for 128 bins:
Summary of Findings:
1. The effect of bin count on histogram equalization:
- Lower bin counts (8-32) tend to produce more dramatic contrast changes
- Higher bin counts (128-256) create more subtle and natural-looking
enhancements
- The optimal bin count depends on the image's initial contrast distribution
[ ]:
[ ]: import numpy as np
import matplotlib.pyplot as plt
from skimage import io, exposure
58
input_Image = np.mean(input_Image, axis=2).astype(np.uint8) # Convert␣
↪to grayscale if needed
if specified_Image.ndim > 2:
specified_Image = np.mean(specified_Image, axis=2).astype(np.uint8) #␣
↪Convert to grayscale if needed
cdf_input = hist_input.cumsum()
cdf_input = 255 * cdf_input / cdf_input[-1] # Normalize to 0-255
cdf_ref = hist_ref.cumsum()
cdf_ref = 255 * cdf_ref / cdf_ref[-1] # Normalize to 0-255
# For each intensity level j in the input image, find the intensity level k␣
↪in the
# reference image such that cdf_input[j] � cdf_ref[k]
for i in range(256):
# Find bin containing intensity i
j = min(no_of_bins - 1, int(i / 256 * no_of_bins))
# Find the intensity in the reference image with the closest CDF value
diff = np.abs(cdf_input[j] - cdf_ref)
index = np.argmin(diff)
lookup_table[i] = int(index * 256 / no_of_bins)
return matched_image
59
x = np.linspace(0, 1, 100)
y = np.linspace(0, 1, 100)
X, Y = np.meshgrid(x, y)
low_contrast = np.uint8((X + Y) * 50 + 50) # Values between 50 and 150
# Display images
axes[0, 0].imshow(low_contrast, cmap='gray')
axes[0, 0].set_title('Low Contrast Image (A)')
axes[0, 0].axis('off')
# Display histograms
axes[1, 0].hist(low_contrast.flatten(), bins=256, range=(0, 256),␣
↪color='black')
plt.tight_layout()
plt.show()
60
if __name__ == "__main__":
demonstrate_histogram_specification()
[ ]:
Parameters:
-----------
image : 2D numpy array
Input grayscale image
filter_function : 2D numpy array
The filter to be applied in the frequency domain
61
Returns:
--------
filtered_image : 2D numpy array
The filtered image
"""
# Convert to float for better precision
if image.dtype != np.float64:
image = img_as_float(image)
# Inverse shift
filtered_f_transform_unshifted = fftpack.ifftshift(filtered_f_transform)
return filtered_image
Parameters:
-----------
shape : tuple
The shape of the filter (should match the image shape)
cutoff_frequency : float
The cutoff frequency of the filter
Returns:
--------
h : 2D numpy array
The ideal low-pass filter
"""
rows, cols = shape
62
crow, ccol = rows // 2, cols // 2
# Convert to float
h = np.zeros((rows, cols))
h[mask] = 1.0
return h
Parameters:
-----------
shape : tuple
The shape of the filter (should match the image shape)
cutoff_frequency : float
The cutoff frequency of the filter
order : int, optional
The order of the Butterworth filter (default is 2)
Returns:
--------
h : 2D numpy array
The Butterworth low-pass filter
"""
rows, cols = shape
crow, ccol = rows // 2, cols // 2
return h
63
"""
Create a Gaussian low-pass filter with the specified cutoff frequency
Parameters:
-----------
shape : tuple
The shape of the filter (should match the image shape)
cutoff_frequency : float
The cutoff frequency of the filter (standard deviation)
Returns:
--------
h : 2D numpy array
The Gaussian low-pass filter
"""
rows, cols = shape
crow, ccol = rows // 2, cols // 2
return h
fig = plt.figure(figsize=figsize)
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, filter_function, cmap='viridis')
ax.set_title(title)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Filter Value')
plt.tight_layout()
64
return fig
"""
# Create filters
ideal_filter = create_ideal_lowpass_filter(image.shape, cutoff_frequency)
butterworth_filter = create_butterworth_lowpass_filter(image.shape,␣
↪cutoff_frequency, order=2)
gaussian_filter = create_gaussian_lowpass_filter(image.shape,␣
↪cutoff_frequency)
# Apply filters
ideal_filtered = apply_frequency_filter(image, ideal_filter)
butterworth_filtered = apply_frequency_filter(image, butterworth_filter)
gaussian_filtered = apply_frequency_filter(image, gaussian_filter)
# Create figure
fig = plt.figure(figsize=(15, 12))
gs = GridSpec(3, 3, figure=fig)
# Original image
ax1 = fig.add_subplot(gs[0, 0])
ax1.imshow(image, cmap='gray')
ax1.set_title('Original Image')
ax1.axis('off')
# Filters
ax2 = fig.add_subplot(gs[0, 1])
ax2.imshow(ideal_filter, cmap='gray')
ax2.set_title(f'Ideal Filter (r={cutoff_frequency})')
ax2.axis('off')
# Filtered images
ax5 = fig.add_subplot(gs[0, 2])
65
ax5.imshow(ideal_filtered, cmap='gray')
ax5.set_title('Ideal Filtered')
ax5.axis('off')
# Filter cross-sections
ax8 = fig.add_subplot(gs[1:, 0])
center_row = image.shape[0] // 2
ax8.plot(ideal_filter[center_row, :], label='Ideal')
ax8.plot(butterworth_filter[center_row, :], label='Butterworth')
ax8.plot(gaussian_filter[center_row, :], label='Gaussian')
ax8.set_title('Filter Cross-section')
ax8.set_xlabel('Spatial Frequency')
ax8.set_ylabel('Filter Response')
ax8.legend()
ax8.grid(True)
plt.tight_layout()
plt.suptitle(f'Comparison of Low-Pass Filters (Cutoff =␣
↪{cutoff_frequency})', fontsize=16)
plt.subplots_adjust(top=0.93)
return fig, {
'ideal': ideal_filtered,
'butterworth': butterworth_filtered,
'gaussian': gaussian_filtered
}
def demonstrate_ringing_effect(image):
"""
Demonstrate the ringing effect of Ideal low-pass filter
"""
# Apply Ideal filters with different cutoff frequencies
cutoffs = [10, 30, 60]
filtered_images = []
66
filtered = apply_frequency_filter(image, ideal_filter)
filtered_images.append(filtered)
# Original image
axes[0, 0].imshow(image, cmap='gray')
axes[0, 0].set_title('Original Image')
axes[0, 0].axis('off')
plt.tight_layout()
plt.suptitle('Demonstration of Ringing Effect in Ideal Low-Pass Filter',␣
↪fontsize=16)
plt.subplots_adjust(top=0.93)
return fig
"""
Compare the effect of different cutoff frequencies for the specified filter␣
↪type
67
Parameters:
-----------
image : 2D numpy array
Input grayscale image
filter_type : str
'butterworth' or 'gaussian'
cutoffs : list
List of cutoff frequencies to compare
order : int
Order for Butterworth filter (ignored for Gaussian)
"""
filtered_images = []
filters = []
# Create figure
rows = 2
cols = len(cutoffs) + 1
fig = plt.figure(figsize=(3*cols, 3*rows))
# Original image
ax = plt.subplot(rows, cols, 1)
ax.imshow(image, cmap='gray')
ax.set_title('Original Image')
ax.axis('off')
# Filtered images
for i, (cutoff, filtered) in enumerate(zip(cutoffs, filtered_images)):
ax = plt.subplot(rows, cols, i+2)
ax.imshow(filtered, cmap='gray')
ax.set_title(f'{title} (r={cutoff})')
ax.axis('off')
# Filters
68
ax = plt.subplot(rows, cols, cols+1)
center_row = image.shape[0] // 2
for i, (cutoff, filter_func) in enumerate(zip(cutoffs, filters)):
ax.plot(filter_func[center_row, :], label=f'r={cutoff}')
ax.set_title('Filter Cross-sections')
ax.legend()
ax.grid(True)
# Filter visualizations
for i, (cutoff, filter_func) in enumerate(zip(cutoffs, filters)):
ax = plt.subplot(rows, cols, cols+i+2)
ax.imshow(filter_func, cmap='viridis')
ax.set_title(f'Filter (r={cutoff})')
ax.axis('off')
plt.tight_layout()
plt.suptitle(f'Comparison of {title} Low-Pass Filter with Different Cutoff␣
↪Frequencies', fontsize=16)
plt.subplots_adjust(top=0.93)
return fig
magnitude_spectrum = np.abs(f_transform_shifted)
if log_transform:
# Use log transform to enhance visualization
magnitude_spectrum = np.log1p(magnitude_spectrum)
69
def main():
# Load image - using the specific path you provided
try:
image_path = r'C:\Users\dkg19\OneDrive\Desktop\DIP LAB\image_gray.png'
image = io.imread(image_path, as_gray=True)
print(f"Successfully loaded image: {image_path}")
print(f"Image shape: {image.shape}")
except Exception as e:
print(f"Error loading image: {e}")
print("Creating a sample image instead.")
size = 512
x = np.linspace(-10, 10, size)
y = np.linspace(-10, 10, size)
X, Y = np.meshgrid(x, y)
image = np.zeros((size, size))
# Add a square
square_size = size // 4
center = size // 2
image[center-square_size:center+square_size, center-square_size:
↪center+square_size] += 0.5
# Normalize to [0, 1]
image = (image - image.min()) / (image.max() - image.min())
fig_spectrum.savefig('output/frequency_spectrum.png', dpi=300,␣
↪bbox_inches='tight')
# Part a: Compare Ideal, Butterworth, and Gaussian filters with the same␣
↪cutoff
cutoff = 30
70
print(f"Comparing filter types with cutoff radius = {cutoff}")
fig_a, filtered_images = compare_filter_types(image,␣
↪cutoff_frequency=cutoff)
fig_3d_ideal.savefig('output/ideal_filter_3d.png', dpi=300,␣
↪bbox_inches='tight')
fig_3d_butterworth.savefig('output/butterworth_filter_3d.png', dpi=300,␣
↪bbox_inches='tight')
fig_3d_gaussian.savefig('output/gaussian_filter_3d.png', dpi=300,␣
↪bbox_inches='tight')
71
# Save filtered images separately
io.imsave('output/ideal_filtered.png',␣
↪img_as_float(filtered_images['ideal']))
io.imsave('output/butterworth_filtered.png',␣
↪img_as_float(filtered_images['butterworth']))
io.imsave('output/gaussian_filtered.png',␣
↪img_as_float(filtered_images['gaussian']))
if __name__ == '__main__':
main()
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
File c:
↪\Users\dkg19\AppData\Local\Programs\Python\Python313\Lib\site-packages\PIL\PngImagePlugin.
↪py:1363, in _save(im, fp, filename, chunk, save_all)
1362 try:
-> 1363 rawmode, bit_depth, color_type = _OUTMODES[outmode]
1364 except KeyError as e:
KeyError: 'F'
The above exception was the direct cause of the following exception:
72
462 # Save filtered images separately
--> 463␣
↪io.imsave('output/ideal_filtered.png', img_as_float(filtered_images['ideal']))
464 io.imsave('output/butterworth_filtered.png',␣
↪img_as_float(filtered_images['butterworth']))
465 io.imsave('output/gaussian_filtered.png',␣
↪img_as_float(filtered_images['gaussian']))
File c:
↪\Users\dkg19\AppData\Local\Programs\Python\Python313\Lib\site-packages\skimage\_shared\util
↪py:328, in deprecate_parameter.__call__.<locals>.fixed_func(*args, **kwargs)
File c:
↪\Users\dkg19\AppData\Local\Programs\Python\Python313\Lib\site-packages\skimage\io\_io.
↪py:206, in imsave(fname, arr, plugin, check_contrast, **plugin_args)
File c:
↪\Users\dkg19\AppData\Local\Programs\Python\Python313\Lib\site-packages\skimage\_shared\util
↪py:538, in deprecate_func.__call__.<locals>.wrapped(*args, **kwargs)
File c:
↪\Users\dkg19\AppData\Local\Programs\Python\Python313\Lib\site-packages\skimage\io\manage_pl
↪py:254, in call_plugin(kind, *args, **kwargs)
File c:
↪\Users\dkg19\AppData\Local\Programs\Python\Python313\Lib\site-packages\imageio\v3.
↪py:139, in imwrite(uri, image, plugin, extension, format_hint, **kwargs)
(…)
73
136
137 """
--> 139 with imopen(
140 uri,
141 "w",
142 legacy_mode=False,
143 plugin=plugin,
144 format_hint=format_hint,
145 extension=extension,
146 ) as img_file:
147 encoded = img_file.write(image, **kwargs)
149 return encoded
File c:
↪\Users\dkg19\AppData\Local\Programs\Python\Python313\Lib\site-packages\imageio\core\v3_plug
↪py:367, in PluginV3.__exit__(self, type, value, traceback)
File c:
↪\Users\dkg19\AppData\Local\Programs\Python\Python313\Lib\site-packages\imageio\plugins\pill
↪py:144, in PillowPlugin.close(self)
File c:
↪\Users\dkg19\AppData\Local\Programs\Python\Python313\Lib\site-packages\imageio\plugins\pill
↪py:485, in PillowPlugin._flush_writer(self)
File c:
↪\Users\dkg19\AppData\Local\Programs\Python\Python313\Lib\site-packages\PIL\Image.
↪py:2596, in Image.save(self, fp, format, **params)
File c:
↪\Users\dkg19\AppData\Local\Programs\Python\Python313\Lib\site-packages\PIL\PngImagePlugin.
↪py:1366, in _save(im, fp, filename, chunk, save_all)
74
1365 msg = f"cannot write mode {mode} as PNG"
-> 1366 raise OSError(msg) from e
1368 #
1369 # write minimal PNG file
1371 fp.write(_MAGIC)
75
76
77
78
79
[ ]:
[1]: # -----------------------------------------
# Import necessary libraries
# -----------------------------------------
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from scipy.fft import fft2, ifft2, fftshift, ifftshift
# -----------------------------------------
# Load the grayscale image
# -----------------------------------------
80
image_path = r'C:\Users\dkg19\OneDrive\Desktop\DIP LAB\image_gray.png'
img = Image.open(image_path).convert('L') # Ensure it's grayscale
img_array = np.array(img)
# -----------------------------------------
# Define filter functions
# -----------------------------------------
def ideal_high_pass_filter(shape, cutoff):
P, Q = shape
u = np.arange(0, P)
v = np.arange(0, Q)
U, V = np.meshgrid(u, v, indexing='ij')
D = np.sqrt((U - P//2)**2 + (V - Q//2)**2)
H = np.zeros((P, Q))
H[D > cutoff] = 1
return H
return H
# -----------------------------------------
81
# Define function to apply filter in frequency domain
# -----------------------------------------
def apply_filter(img_array, filter_mask):
F = fft2(img_array)
F_shifted = fftshift(F)
G = F_shifted * filter_mask
G_ishift = ifftshift(G)
img_filtered = np.abs(ifft2(G_ishift))
return img_filtered
# -----------------------------------------
# (a) Apply Ideal, Butterworth, Gaussian HPFs with same cutoff
# -----------------------------------------
cutoff = 30 # cutoff frequency
# Show results
plt.figure(figsize=(18,6))
plt.subplot(1,3,1)
plt.imshow(img_ideal, cmap='gray')
plt.title('Ideal High Pass Filter')
plt.axis('off')
plt.subplot(1,3,2)
plt.imshow(img_butterworth, cmap='gray')
plt.title('Butterworth High Pass Filter (n=2)')
plt.axis('off')
plt.subplot(1,3,3)
plt.imshow(img_gaussian, cmap='gray')
plt.title('Gaussian High Pass Filter')
plt.axis('off')
plt.show()
# -----------------------------------------
# (b) Ringing effect demonstration with Ideal HPF
# -----------------------------------------
82
plt.figure(figsize=(6,6))
plt.imshow(img_ideal, cmap='gray')
plt.title('Ringing Effect - Ideal High Pass Filter')
plt.axis('off')
plt.show()
# -----------------------------------------
# (c) Compare Butterworth HPF for different cut-off frequencies
# -----------------------------------------
cutoffs = [5, 15, 30, 90, 120]
plt.figure(figsize=(20,10))
plt.subplot(2, 3, i)
plt.imshow(img_filtered, cmap='gray')
plt.title(f'Butterworth HPF\nCutoff={cutoff}')
plt.axis('off')
plt.tight_layout()
plt.show()
# -----------------------------------------
# (d) Compare Gaussian HPF for different cut-off frequencies
# -----------------------------------------
plt.figure(figsize=(20,10))
plt.subplot(2, 3, i)
plt.imshow(img_filtered, cmap='gray')
plt.title(f'Gaussian HPF\nCutoff={cutoff}')
plt.axis('off')
plt.tight_layout()
plt.show()
83
84
85
[ ]:
86