huge changes

This commit is contained in:
2025-06-19 15:31:45 -04:00
parent 2bcf6a3325
commit bb253155fb
18 changed files with 4243 additions and 129 deletions

View File

@ -19,6 +19,57 @@ import imageio.v3 as iio
from scipy.integrate import quad
from scipy.signal.windows import gaussian # For creating Gaussian kernel
import rawpy
import math
def get_grain_parameters(width, height, iso):
"""
Calculates mu_r and sigma for the film grain script based on image
dimensions (width, height) and target ISO.
Args:
width (int): The width of the source image in pixels.
height (int): The height of the source image in pixels.
iso (float): The target film ISO to simulate (e.g., 100, 400, 3200).
Returns:
tuple: A tuple containing the calculated (mu_r, sigma) for the script.
"""
# --- Baseline Parameters (calibrated for a 24MP image @ ISO 400) ---
# A 24MP image (e.g., 6000x4000) has 24,000,000 pixels.
PIXELS_BASE = 24_000_000.0
ISO_BASE = 400.0
MU_R_BASE = 0.15
SIGMA_BASE = 0.5
# --- Scaling Exponents (Artistically chosen for a natural feel) ---
# The exponent for mu_r is larger than for sigma to ensure that
# grain intensity (related to mu_r²/sigma²) increases with ISO.
ISO_EXPONENT_MU = 0.4
ISO_EXPONENT_SIGMA = 0.3
# Clamp ISO to a reasonable range to avoid extreme/invalid values
iso = max(64.0, min(iso, 8000.0))
# 1. Calculate the total number of pixels in the actual image
pixels_actual = float(width * height)
# 2. Calculate the resolution scaler
# This scales parameters based on the image's linear dimensions (sqrt of area)
# relative to the 24MP baseline.
resolution_scaler = math.sqrt(pixels_actual / PIXELS_BASE)
print(f"Resolution scaler: {resolution_scaler:.4f} (for {width}x{height} image)")
# 3. Calculate the ISO scaler
iso_ratio = iso / ISO_BASE
iso_scaler_mu = iso_ratio ** ISO_EXPONENT_MU
iso_scaler_sigma = iso_ratio ** ISO_EXPONENT_SIGMA
print(f"ISO scaler: μ = {iso_scaler_mu:.4f}, σ = {iso_scaler_sigma:.4f} (for ISO {iso})")
# 4. Calculate the final parameters by applying both scalers
final_mu_r = MU_R_BASE * resolution_scaler * iso_scaler_mu
final_sigma = SIGMA_BASE * resolution_scaler * iso_scaler_sigma
return (final_mu_r, final_sigma)
wp.init()
@ -239,7 +290,7 @@ def create_gaussian_kernel_2d(sigma, radius):
return kernel_2d.flatten().astype(np.float32)
def render_film_grain(image_path, mu_r, sigma_filter, output_path, seed=42, mono=False):
def render_film_grain(image_path, iso, output_path, seed=42, mono=False):
try:
if image_path.lower().endswith('.arw') or image_path.lower().endswith('.dng'):
# Use rawpy for TIFF images to handle metadata correctly
@ -276,6 +327,7 @@ def render_film_grain(image_path, mu_r, sigma_filter, output_path, seed=42, mono
img_np_float = img_np.astype(np.float32)
height, width, channels = img_np_float.shape
mu_r, sigma_filter = get_grain_parameters(width, height, iso)
print(f"Input image: {width}x{height}x{channels}")
print(f"Parameters: μr = {mu_r}, σ_filter = {sigma_filter}")
@ -399,13 +451,10 @@ if __name__ == "__main__":
parser.add_argument("input_image", help="Path to the input image (TIFF, PNG, JPG, or RAW (ARW/DNG) format)")
parser.add_argument("output_image", help="Path to save the output image (TIFF (16-bit), PNG, JPG format)")
parser.add_argument(
"--mu_r", type=float, default=0.1, help="Mean grain radius (relative to pixel size)"
)
parser.add_argument(
"--sigma",
type=float,
default=0.8,
help="Standard deviation of the Gaussian Filter for noise blurring (sigma_filter).",
"--iso",
type=int,
default=400,
help="Target film ISO to simulate (e.g., 100, 400, 1600).",
)
parser.add_argument(
"--seed", type=int, default=42, help="Random seed for noise generation"
@ -416,17 +465,7 @@ if __name__ == "__main__":
args = parser.parse_args()
if args.mu_r <= 0:
print("Warning: mu_r should be positive. Using default 0.1")
args.mu_r = 0.1
if args.sigma <= 0:
print("Warning: sigma_filter should be positive. Using default 0.8")
args.sigma = 0.8
if args.sigma < 3 * args.mu_r:
print(
f"Warning: sigma_filter ({args.sigma}) is less than 3*mu_r ({3 * args.mu_r:.2f}). Approximations in the model might be less accurate."
)
render_film_grain(
args.input_image, args.mu_r, args.sigma, args.output_image, args.seed, args.mono
args.input_image, args.iso, args.output_image, args.seed, args.mono
)