You've already forked Openterface_assets
mirror of
https://github.com/TechxArtisanStudio/Openterface_assets.git
synced 2025-12-17 14:02:32 -08:00
image_resizer.py
This commit is contained in:
281
image_resizer.py
Executable file
281
image_resizer.py
Executable file
@@ -0,0 +1,281 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Interactive Image Resizer
|
||||
Allows you to select an image from the project, view its properties,
|
||||
and resize it proportionally.
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
import sys
|
||||
|
||||
|
||||
# Supported image formats
|
||||
SUPPORTED_FORMATS = {'.png', '.jpg', '.jpeg', '.webp', '.gif', '.bmp', '.tiff', '.tif'}
|
||||
|
||||
|
||||
def find_images(directory):
|
||||
"""Recursively find all image files in the directory."""
|
||||
images = []
|
||||
directory_path = Path(directory)
|
||||
|
||||
for ext in SUPPORTED_FORMATS:
|
||||
images.extend(directory_path.rglob(f'*{ext}'))
|
||||
images.extend(directory_path.rglob(f'*{ext.upper()}'))
|
||||
|
||||
return sorted(images)
|
||||
|
||||
|
||||
def format_file_size(size_bytes):
|
||||
"""Convert bytes to human-readable format."""
|
||||
for unit in ['B', 'KB', 'MB', 'GB']:
|
||||
if size_bytes < 1024.0:
|
||||
return f"{size_bytes:.2f} {unit}"
|
||||
size_bytes /= 1024.0
|
||||
return f"{size_bytes:.2f} TB"
|
||||
|
||||
|
||||
def display_image_info(image_path):
|
||||
"""Display image information."""
|
||||
try:
|
||||
with Image.open(image_path) as img:
|
||||
width, height = img.size
|
||||
file_size = os.path.getsize(image_path)
|
||||
|
||||
print("\n" + "="*60)
|
||||
print(f"Image: {image_path}")
|
||||
print(f"Dimensions: {width} x {height} pixels")
|
||||
print(f"File Size: {format_file_size(file_size)}")
|
||||
print(f"Format: {img.format}")
|
||||
print(f"Mode: {img.mode}")
|
||||
print("="*60 + "\n")
|
||||
|
||||
return width, height, file_size
|
||||
except Exception as e:
|
||||
print(f"Error reading image: {e}")
|
||||
return None, None, None
|
||||
|
||||
|
||||
def resize_image_proportionally(image_path, target_width=None, target_height=None, scale_factor=None):
|
||||
"""Resize image proportionally based on target width, height, or scale factor."""
|
||||
try:
|
||||
with Image.open(image_path) as img:
|
||||
original_width, original_height = img.size
|
||||
|
||||
if scale_factor:
|
||||
new_width = int(original_width * scale_factor)
|
||||
new_height = int(original_height * scale_factor)
|
||||
elif target_width:
|
||||
scale = target_width / original_width
|
||||
new_width = target_width
|
||||
new_height = int(original_height * scale)
|
||||
elif target_height:
|
||||
scale = target_height / original_height
|
||||
new_width = int(original_width * scale)
|
||||
new_height = target_height
|
||||
else:
|
||||
print("Error: No resize parameter provided")
|
||||
return False
|
||||
|
||||
print(f"\nResizing from {original_width}x{original_height} to {new_width}x{new_height}")
|
||||
|
||||
# Resize with high-quality resampling
|
||||
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
||||
|
||||
# Ask for output filename
|
||||
original_path = Path(image_path)
|
||||
default_name = f"{original_path.stem}_resized{original_path.suffix}"
|
||||
output_name = input(f"Enter output filename (default: {default_name}): ").strip()
|
||||
|
||||
if not output_name:
|
||||
output_name = default_name
|
||||
|
||||
output_path = original_path.parent / output_name
|
||||
|
||||
# Handle overwrite confirmation
|
||||
if output_path.exists():
|
||||
confirm = input(f"File {output_path} already exists. Overwrite? (y/n): ").strip().lower()
|
||||
if confirm != 'y':
|
||||
print("Operation cancelled.")
|
||||
return False
|
||||
|
||||
# Save the resized image
|
||||
resized_img.save(output_path, quality=95, optimize=True)
|
||||
|
||||
new_size = os.path.getsize(output_path)
|
||||
print(f"\n✓ Image saved to: {output_path}")
|
||||
print(f" New dimensions: {new_width} x {new_height} pixels")
|
||||
print(f" New file size: {format_file_size(new_size)}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error resizing image: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def handle_image_resize_menu(image_path):
|
||||
"""Handle the resize menu for a specific image."""
|
||||
width, height, file_size = display_image_info(image_path)
|
||||
|
||||
if width is None:
|
||||
return False
|
||||
|
||||
# Resize options
|
||||
while True:
|
||||
print("\nResize Options:")
|
||||
print("1. Resize by scale factor (e.g., 0.5 for 50%, 2.0 for 200%)")
|
||||
print("2. Resize by target width (maintains aspect ratio)")
|
||||
print("3. Resize by target height (maintains aspect ratio)")
|
||||
print("4. Exit")
|
||||
|
||||
resize_choice = input("\nChoose an option (1-4): ").strip()
|
||||
|
||||
if resize_choice == '1':
|
||||
try:
|
||||
scale = float(input("Enter scale factor (e.g., 0.5, 1.5, 2.0): ").strip())
|
||||
if scale <= 0:
|
||||
print("Scale factor must be positive.")
|
||||
continue
|
||||
resize_image_proportionally(image_path, scale_factor=scale)
|
||||
except ValueError:
|
||||
print("Invalid scale factor. Please enter a number.")
|
||||
|
||||
elif resize_choice == '2':
|
||||
try:
|
||||
target_w = int(input(f"Enter target width (current: {width}): ").strip())
|
||||
if target_w <= 0:
|
||||
print("Width must be positive.")
|
||||
continue
|
||||
resize_image_proportionally(image_path, target_width=target_w)
|
||||
except ValueError:
|
||||
print("Invalid width. Please enter a number.")
|
||||
|
||||
elif resize_choice == '3':
|
||||
try:
|
||||
target_h = int(input(f"Enter target height (current: {height}): ").strip())
|
||||
if target_h <= 0:
|
||||
print("Height must be positive.")
|
||||
continue
|
||||
resize_image_proportionally(image_path, target_height=target_h)
|
||||
except ValueError:
|
||||
print("Invalid height. Please enter a number.")
|
||||
|
||||
elif resize_choice == '4':
|
||||
return True
|
||||
|
||||
else:
|
||||
print("Invalid option. Please try again.")
|
||||
|
||||
|
||||
def main():
|
||||
"""Main interactive function."""
|
||||
# Get the project root directory (parent of script location)
|
||||
script_dir = Path(__file__).parent
|
||||
project_root = script_dir
|
||||
|
||||
# Check if image path is provided as command-line argument
|
||||
if len(sys.argv) > 1:
|
||||
image_path_arg = sys.argv[1]
|
||||
image_path = Path(image_path_arg)
|
||||
|
||||
# Handle both absolute and relative paths
|
||||
if not image_path.is_absolute():
|
||||
# Try relative to current directory first
|
||||
if not image_path.exists():
|
||||
# Try relative to project root
|
||||
image_path = project_root / image_path
|
||||
else:
|
||||
image_path = Path(image_path_arg).resolve()
|
||||
else:
|
||||
image_path = Path(image_path_arg)
|
||||
|
||||
# Validate the image file
|
||||
if not image_path.exists():
|
||||
print(f"Error: Image file not found: {image_path}")
|
||||
print(f"Please check the path and try again.")
|
||||
sys.exit(1)
|
||||
|
||||
if image_path.suffix.lower() not in SUPPORTED_FORMATS:
|
||||
print(f"Error: Unsupported image format: {image_path.suffix}")
|
||||
print(f"Supported formats: {', '.join(sorted(SUPPORTED_FORMATS))}")
|
||||
sys.exit(1)
|
||||
|
||||
# Direct mode: process the specified image
|
||||
print("="*60)
|
||||
print("Interactive Image Resizer")
|
||||
print("="*60)
|
||||
print(f"Processing image: {image_path}")
|
||||
|
||||
try:
|
||||
handle_image_resize_menu(image_path)
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nInterrupted by user. Goodbye!")
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
return
|
||||
|
||||
# Interactive mode: show menu to select image
|
||||
print("="*60)
|
||||
print("Interactive Image Resizer")
|
||||
print("="*60)
|
||||
print(f"Scanning images in: {project_root}")
|
||||
|
||||
# Find all images
|
||||
images = find_images(project_root)
|
||||
|
||||
if not images:
|
||||
print("No supported image files found in the project.")
|
||||
return
|
||||
|
||||
print(f"\nFound {len(images)} image(s)\n")
|
||||
|
||||
while True:
|
||||
# Display image list
|
||||
print("\n" + "-"*60)
|
||||
print("Available Images:")
|
||||
print("-"*60)
|
||||
for idx, img_path in enumerate(images, 1):
|
||||
rel_path = img_path.relative_to(project_root)
|
||||
print(f"{idx:3d}. {rel_path}")
|
||||
|
||||
print(f"\n{len(images) + 1:3d}. Exit")
|
||||
print("-"*60)
|
||||
|
||||
# Get user selection
|
||||
try:
|
||||
choice = input(f"\nSelect an image (1-{len(images) + 1}): ").strip()
|
||||
|
||||
if not choice:
|
||||
continue
|
||||
|
||||
choice_num = int(choice)
|
||||
|
||||
if choice_num == len(images) + 1:
|
||||
print("Goodbye!")
|
||||
break
|
||||
|
||||
if choice_num < 1 or choice_num > len(images):
|
||||
print("Invalid selection. Please try again.")
|
||||
continue
|
||||
|
||||
selected_image = images[choice_num - 1]
|
||||
|
||||
# Handle resize menu
|
||||
handle_image_resize_menu(selected_image)
|
||||
|
||||
except ValueError:
|
||||
print("Invalid input. Please enter a number.")
|
||||
except KeyboardInterrupt:
|
||||
print("\n\nInterrupted by user. Goodbye!")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
BIN
src/images/cover-Gibby-with-Christmas-hat_resized.png
Normal file
BIN
src/images/cover-Gibby-with-Christmas-hat_resized.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 245 KiB |
Reference in New Issue
Block a user