huge changes
This commit is contained in:
77
filmgrain
77
filmgrain
@ -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
|
||||
)
|
Reference in New Issue
Block a user