Spaces:
Sleeping
Sleeping
import numpy as np | |
import cv2 | |
import matplotlib.pyplot as plt | |
from mpl_toolkits.mplot3d import Axes3D | |
import os | |
import csv | |
# 1. Function to apply Fast Fourier Transform to a colored image (separate for each channel) | |
def apply_fft(image): | |
"""Apply Fast Fourier Transform to a colored image (separate for each channel)""" | |
fft_channels = [] | |
for channel in cv2.split(image): # Split the image into its color channels (B, G, R) | |
fft = np.fft.fft2(channel) | |
fft_shifted = np.fft.fftshift(fft) # Shift the zero frequency to the center | |
fft_channels.append(fft_shifted) | |
return fft_channels | |
# 2. Function to display frequency components of each color channel in interactive 3D plots using Matplotlib | |
def show_frequency_components_3d(fft_channels, title='Frequency Components'): | |
"""Display the magnitude and phase of the FFT for each channel (R, G, B) in 3D with Matplotlib""" | |
channel_names = ['Blue Channel', 'Green Channel', 'Red Channel'] | |
# Create a figure for 3D plotting | |
fig = plt.figure(figsize=(18, 6)) | |
# Loop through each channel's FFT data | |
for i, fft_data in enumerate(fft_channels): | |
magnitude = np.abs(fft_data) # Magnitude spectrum | |
phase = np.angle(fft_data) # Phase spectrum | |
# Generate grid for the 3D plot (x, y grid of frequencies) | |
rows, cols = magnitude.shape | |
x = np.linspace(-cols // 2, cols // 2, cols) | |
y = np.linspace(-rows // 2, rows // 2, rows) | |
X, Y = np.meshgrid(x, y) | |
# Create a subplot for each channel's magnitude and phase | |
ax = fig.add_subplot(1, 6, 2 * i + 1, projection='3d') | |
ax.set_title(f'{channel_names[i]} - Magnitude') | |
ax.plot_surface(X, Y, magnitude, cmap='viridis', edgecolor='none') | |
ax = fig.add_subplot(1, 6, 2 * i + 2, projection='3d') | |
ax.set_title(f'{channel_names[i]} - Phase') | |
ax.plot_surface(X, Y, phase, cmap='inferno', edgecolor='none') | |
plt.suptitle(title, fontsize=16) | |
plt.tight_layout() | |
plt.show() | |
# 3. Function to apply percentage-based filtering to the FFT (for each channel) | |
def filter_fft_percentage(fft_channels, percentage): | |
"""Apply percentage-based filtering, keeping the highest-magnitude frequency components""" | |
filtered_fft = [] | |
for fft_data in fft_channels: | |
magnitude = np.abs(fft_data) | |
flat_mag = magnitude.flatten() | |
sorted_mag = np.sort(flat_mag)[::-1] # Sort magnitudes in descending order | |
# Determine the threshold for the given percentage | |
num_elements_to_keep = int(len(sorted_mag) * percentage / 100) | |
threshold = sorted_mag[num_elements_to_keep - 1] if num_elements_to_keep > 0 else 0 | |
# Create a mask to keep only the top frequencies | |
mask = magnitude >= threshold | |
filtered_fft.append(fft_data * mask) # Apply mask to the FFT data | |
return filtered_fft | |
# 4. Function to apply inverse Fourier transform to reconstruct the color image | |
def inverse_fft(filtered_fft): | |
"""Apply inverse Fourier transform to reconstruct the image from filtered FFT""" | |
reconstructed_channels = [] | |
for fft_data in filtered_fft: | |
fft_ishift = np.fft.ifftshift(fft_data) # Reverse FFT shift | |
img_reconstructed = np.fft.ifft2(fft_ishift) # Apply inverse FFT | |
img_reconstructed = np.abs(img_reconstructed) # Get the magnitude of the result | |
# Normalize and convert to uint8 for image format | |
img_normalized = cv2.normalize(img_reconstructed, None, 0, 255, cv2.NORM_MINMAX) | |
reconstructed_channels.append(img_normalized.astype(np.uint8)) | |
# Merge the channels back into a color image (BGR format) | |
return cv2.merge(reconstructed_channels) | |
# 5. Main function to process images in batches | |
def process_images(): | |
"""Main function to process images, apply FFT, and filter results""" | |
# Create directories if they don't exist | |
os.makedirs('Modified', exist_ok=True) | |
# Ask user for the percentage of top frequencies to keep | |
percentage = float(input("Enter the percentage of highest-magnitude frequencies to keep (0-100): ")) | |
print(f"Applying percentage-based filtering: Keeping top {percentage}% of frequencies.") | |
# Create CSV logging file | |
with open('fft_features.csv', 'w', newline='') as csvfile: | |
csv_writer = csv.writer(csvfile) | |
csv_writer.writerow(['Image', 'Max Magnitude', 'Mean Magnitude', 'Non-zero Count']) | |
# Process each image in the 'original' folder | |
image_filenames = [filename for filename in os.listdir('original') | |
if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff'))] | |
# Process all images | |
for i, filename in enumerate(image_filenames): | |
# Read image (colored) | |
img_path = os.path.join('original', filename) | |
img = cv2.imread(img_path) | |
# Apply FFT to each channel (RGB) | |
fft_channels = apply_fft(img) | |
# Apply percentage-based filtering | |
filtered_fft = filter_fft_percentage(fft_channels, percentage) | |
# Reconstruct the image using inverse FFT | |
reconstructed = inverse_fft(filtered_fft) | |
# Show frequency components (filtered FFT) as interactive 3D plots using Matplotlib | |
show_frequency_components_3d(filtered_fft, f'Filtered FFT - {filename}') | |
# Log FFT features for the first channel (R) | |
magnitude = np.abs(filtered_fft[0]) # Just checking the first channel's magnitude | |
non_zero = magnitude > 0 | |
csv_writer.writerow([ | |
filename, | |
np.max(magnitude), | |
np.mean(magnitude[non_zero]) if np.any(non_zero) else 0, | |
np.count_nonzero(non_zero) | |
]) | |
# Save reconstructed image | |
cv2.imwrite(os.path.join('Modified', filename), reconstructed) | |
print("Processing completed successfully!") | |
# Run the main function | |
if __name__ == "__main__": | |
process_images() | |