image_edit / tools.py
mohamed12ahmed's picture
Upload 10 files
c3182bc verified
# 📁 File: tools/image_enhancer/tools.py
import os
import cv2
import numpy as np
from PIL import Image, ImageEnhance, ImageFilter
from utils import validate_path, ensure_path_from_img, temp_output
# === TOOL IMPLEMENTATIONS ===
def apply_color_correction(input_path: str,
brightness: float = 1.0,
contrast: float = 1.0,
saturation: float = 1.0,
output_path: str = None):
"""
Applies color correction to an image including brightness, contrast and saturation adjustments.
Args:
input_path (str): Path to the input image file
output_path (str): Path where the corrected image will be saved
brightness (float): Brightness factor (1.0 is original, <1.0 darker, >1.0 brighter)
contrast (float): Contrast factor (1.0 is original, <1.0 less contrast, >1.0 more contrast)
saturation (float): Saturation factor (1.0 is original, <1.0 less saturated, >1.0 more saturated)
Returns:
None: The function saves the output file to the specified path
Raises:
FileNotFoundError: If the input file does not exist
ValueError: If the input is not a valid image
OSError: If there's an error creating the output directory or writing the file
"""
try:
if output_path is None:
output_path = temp_output()
img = ensure_path_from_img(input_path)
# Open the image
img = Image.open(img)
# Apply brightness adjustment
if brightness != 1.0:
enhancer = ImageEnhance.Brightness(img)
img = enhancer.enhance(brightness)
# Apply contrast adjustment
if contrast != 1.0:
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(contrast)
# Apply saturation adjustment
if saturation != 1.0:
enhancer = ImageEnhance.Color(img)
img = enhancer.enhance(saturation)
# Ensure the output directory exists
os.makedirs(os.path.dirname(output_path), exist_ok=True)
# Save the result
img.save(output_path)
except Exception as e:
# Re-raise with more context
raise type(e)(f"Error in color correction: {str(e)}")
return str(output_path)
def apply_sharpening(input_path: str, strength: float = 1.0, output_path: str = None)-> str:
"""
Applies sharpening to an image to enhance edges and details.
Args:
input_path (str): Path to the input image file
output_path (str): Path where the sharpened image will be saved
strength (float): Sharpening strength (1.0 is standard, >1.0 is stronger)
Returns:
output_path (str): Path where the sharpened image will be saved
Raises:
FileNotFoundError: If the input file does not exist
ValueError: If the input is not a valid image
OSError: If there's an error creating the output directory or writing the file
"""
try:
if output_path is None:
output_path = temp_output()
# Validate input file exists
if not os.path.exists(input_path):
raise FileNotFoundError(f"Input file not found: {input_path}")
img = ensure_path_from_img(input_path)
# Open the image
img = Image.open(img)
# Apply sharpening
if strength <= 1.0:
# Use PIL's built-in SHARPEN filter for moderate sharpening
sharpened = img.filter(ImageFilter.SHARPEN)
else:
# For stronger sharpening, use UnsharpMask with adjusted parameters
radius = 2.0
percent = int(150 * strength)
threshold = 3
sharpened = img.filter(ImageFilter.UnsharpMask(radius, percent, threshold))
# Ensure the output directory exists
os.makedirs(os.path.dirname(output_path), exist_ok=True)
# Save the result
sharpened.save(output_path)
except Exception as e:
# Re-raise with more context
raise type(e)(f"Error in sharpening: {str(e)}")
return str(output_path)
# --- Constants for Denoising Parameters ---
# These values control the filtering strength.
# h: Luminance component filter strength. Larger h means more noise removal.
# hColor: Color component filter strength.
# templateWindowSize: Should be odd. Recommended: 7.
# searchWindowSize: Should be odd. Recommended: 21.
DENOISE_PARAMS = {
"low": {
"h": 5,
"hColor": 5,
"templateWindowSize": 7,
"searchWindowSize": 21,
},
"medium": {
"h": 10,
"hColor": 10,
"templateWindowSize": 7,
"searchWindowSize": 21,
},
"high": {
"h": 15,
"hColor": 15,
"templateWindowSize": 7,
"searchWindowSize": 21,
}
}
def reduce_noise(input_path: str, strength: str = "medium", output_path: str = None) -> str:
"""
Reduces noise in an image using cv2.fastNlMeansDenoisingColored.
Args:
input_path (str): Path to the input image file.
output_path (str): Path where the denoised image will be saved.
strength (str): Noise reduction strength ("low", "medium", or "high").
Returns:
output_path (str): Path where the sharpened image will be saved
Raises:
FileNotFoundError: If the input file does not exist.
ValueError: If the strength parameter is invalid or the image cannot be loaded.
OSError: If there's an error creating the output directory or writing the file.
"""
if output_path is None:
output_path = temp_output()
if not os.path.exists(input_path):
raise FileNotFoundError(f"Input file not found: {input_path}")
if strength not in DENOISE_PARAMS:
raise ValueError(f"Invalid strength value: {strength}. Must be one of {list(DENOISE_PARAMS.keys())}")
try:
img = cv2.imread(input_path)
if img is None:
# This can happen if the file exists but is corrupted or not a valid image format.
raise ValueError(f"Failed to load image from path: {input_path}. The file may be corrupted or in an unsupported format.")
# Get parameters for the chosen strength
params = DENOISE_PARAMS[strength]
# Apply noise reduction
denoised = cv2.fastNlMeansDenoisingColored(
img,
None,
params["h"],
params["hColor"],
params["templateWindowSize"],
params["searchWindowSize"]
)
# Ensure the output directory exists
output_dir = os.path.dirname(output_path)
if output_dir:
os.makedirs(output_dir, exist_ok=True)
# Save the result
success = cv2.imwrite(output_path, denoised)
if not success:
raise OSError(f"Failed to save the denoised image to {output_path}")
except (cv2.error, OSError) as e:
# Catch specific errors from OpenCV or file system and re-raise
# them to provide context without losing the original error.
raise OSError(f"An error occurred during image processing or file saving for {input_path}") from e
return str(output_path)
def upscale_resolution(input_path: str, scale_factor: int = 2, method: str = "bicubic", output_path: str = None) -> str:
"""
Increases the resolution of an image using various upscaling methods.
Args:
input_path (str): Path to the input image file
scale_factor (int): How much to increase resolution (2 = 2x, 3 = 3x, 4 = 4x)
method (str): Upscaling method ("nearest", "bilinear", "bicubic", or "lanczos")
output_path (str): Path where the upscaled image will be saved
Returns:
output_path (str): Path where the sharpened image will be saved
Raises:
FileNotFoundError: If the input file does not exist
ValueError: If the scale_factor or method parameters are invalid
OSError: If there's an error creating the output directory or writing the file
"""
try:
if output_path is None:
output_path = temp_output()
# Validate input file exists
if not os.path.exists(input_path):
raise FileNotFoundError(f"Input file not found: {input_path}")
# Validate scale factor
if scale_factor not in [2, 3, 4]:
raise ValueError(f"Invalid scale factor: {scale_factor}. Must be 2, 3, or 4")
# Map method names to PIL resampling filters
method_map = {
"nearest": Image.NEAREST,
"bilinear": Image.BILINEAR,
"bicubic": Image.BICUBIC,
"lanczos": Image.LANCZOS
}
# Validate method name
if method.lower() not in method_map:
raise ValueError(f"Invalid method: {method}. Must be one of {list(method_map.keys())}")
# Use bicubic as default if method is not recognized
resampling_filter = method_map.get(method.lower(), Image.BICUBIC)
# Open the image
img = Image.open(input_path)
# Get original dimensions
width, height = img.size
# Calculate new dimensions
new_width = width * scale_factor
new_height = height * scale_factor
# Resize the image
upscaled = img.resize((new_width, new_height), resampling_filter)
# Ensure the output directory exists
os.makedirs(os.path.dirname(output_path), exist_ok=True)
# Save the result
upscaled.save(output_path)
except Exception as e:
# Re-raise with more context
raise type(e)(f"Error in upscaling: {str(e)}")
return str(output_path)
def auto_enhance(input_path: str, output_path: str = None) -> str:
"""
Automatically enhances an image by applying balanced corrections to
brightness, contrast, color, and sharpness.
Args:
input_path (str): Path to the input image file
output_path (str): Path where the enhanced image will be saved
Returns:
output_path (str): Path where the sharpened image will be saved
Raises:
FileNotFoundError: If the input file does not exist
ValueError: If the input is not a valid image
OSError: If there's an error creating the output directory or writing the file
"""
try:
if output_path is None:
output_path = temp_output()
# Validate input file exists
if not os.path.exists(input_path):
raise FileNotFoundError(f"Input file not found: {input_path}")
# Open the image
img = Image.open(input_path)
# Auto-enhance: apply a series of balanced enhancements
# Step 1: Auto-contrast
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(1.2)
# Step 2: Moderate brightness adjustment
enhancer = ImageEnhance.Brightness(img)
img = enhancer.enhance(1.1)
# Step 3: Moderate color enhancement
enhancer = ImageEnhance.Color(img)
img = enhancer.enhance(1.1)
# Step 4: Moderate sharpening
enhancer = ImageEnhance.Sharpness(img)
img = enhancer.enhance(1.3)
# Ensure the output directory exists
os.makedirs(os.path.dirname(output_path), exist_ok=True)
# Save the result
img.save(output_path)
except Exception as e:
# Re-raise with more context
raise type(e)(f"Error in auto enhancement: {str(e)}")
return str(output_path)