哪些帧在闪烁或有噪点
轻微闪烁静帧看不出,却会在大屏和连续播放中非常明显。
有人这样说「这组镜头在大屏放的时候,噪点忽明忽暗,得赶紧查清楚是哪几帧出问题。」
能替你选的都选好了。不想动,直接点下面「交给 AI 办」,点一下就开始做;想改,就点一下要的那个。
已替你选好想改就点一下
更多选项(一般不用动)
只有你知道的不填就先空着,成品里留【待填】
还想怎么改可以不选
开工前请确认
高级选项一般不用动 · 这件事通常 1–3 分钟做好
示例成品全文按上面这组示例选项真跑出来的
先说结论:当前对话里我拿不到 seq_045.exr 的实际像素数据,所以不能直接给出“第几帧有问题”的真实结果。下面是直接可跑的脚本,跑完会生成你要的 3 个交付物:
- Excel 明细表:
seq_045_noise_audit.xlsx - 异常帧号 CSV:
seq_045_abnormal_frames.csv - 给 VFX 主管看的报告:
seq_045_noise_report.md
如果 seq_045.exr 不是序列而只是单个文件,脚本只能做单帧噪点评估,判断不了“忽明忽暗”。序列目录里应是一组命名类似 seq_045.0001.exr、seq_045.0002.exr 的文件。
直接可跑的脚本
把下面脚本保存为 audit_seq045.py,改好 SEQ_DIR 和 SEQ_PATTERN,运行即可。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
seq_045 噪点帧审计脚本
输出:
- seq_045_noise_audit.xlsx 全帧明细 Excel
- seq_045_abnormal_frames.csv 异常帧号 CSV
- seq_045_noise_report.md 给 VFX 主管看的报告
用法:
1) 安装依赖:pip install numpy pandas openpyxl opencv-python imageio
2) 修改 SEQ_DIR 和 SEQ_PATTERN 为实际序列路径/命名
3) 运行:python audit_seq045.py
"""
import os
import re
import glob
import numpy as np
import pandas as pd
import cv2
# ====== 按实际路径改这里 ======
SEQ_DIR = r"./seq_045"
SEQ_PATTERN = "seq_045*.exr"
# ===============================
EXCEL_PATH = "seq_045_noise_audit.xlsx"
CSV_PATH = "seq_045_abnormal_frames.csv"
REPORT_PATH = "seq_045_noise_report.md"
def natural_key(name):
nums = re.findall(r"\d+", name)
return [int(n) for n in nums]
def read_linear_rgb(path):
"""读取 EXR 为线性 RGB float32。优先 OpenCV,失败则 imageio/OpenEXR。"""
# 方式 1:OpenCV
try:
arr = cv2.imread(path, cv2.IMREAD_ANYDEPTH | cv2.IMREAD_COLOR)
if arr is not None:
if arr.dtype != np.float32:
arr = arr.astype(np.float32) / 255.0
return cv2.cvtColor(arr, cv2.COLOR_BGR2RGB).astype(np.float32)
except Exception:
pass
# 方式 2:imageio
try:
import imageio.v3 as iio
arr = iio.imread(path)
if arr.ndim == 2:
arr = np.repeat(arr[..., None], 3, axis=2)
elif arr.shape[-1] > 3:
arr = arr[..., :3]
return arr.astype(np.float32)
except Exception:
pass
# 方式 3:OpenEXR
try:
import OpenEXR
import Imath
exr = OpenEXR.InputFile(path)
hdr = exr.header()
dw = hdr["dataWindow"]
w = dw.max.x - dw.min.x + 1
h = dw.max.y - dw.min.y + 1
pt = Imath.PixelType(Imath.PixelType.FLOAT)
r = np.frombuffer(exr.channel("R", pt), dtype=np.float32).reshape(h, w)
g = np.frombuffer(exr.channel("G", pt), dtype=np.float32).reshape(h, w)
b = np.frombuffer(exr.channel("B", pt), dtype=np.float32).reshape(h, w)
return np.stack([r, g, b], axis=-1).astype(np.float32)
except Exception:
pass
raise IOError(f"无法读取 EXR: {path}")
def brightness(rgb):
return 0.2126 * rgb[..., 0] + 0.7152 * rgb[..., 1] + 0.0722 * rgb[..., 2]
def robust_noise_sigma(residual):
med = np.median(residual)
mad = np.median(np.abs(residual - med))
return float(1.4826 * mad)
def analyze_frame(path, prev_res):
rgb = read_linear_rgb(path)
y = brightness(rgb).astype(np.float32)
# 高频残差:去掉低频结构,剩下的主要就是噪点/颗粒
y_blur = cv2.GaussianBlur(y, (0, 0), sigmaX=1.0)
res = y - y_blur
noise_sigma = robust_noise_sigma(res)
# 暗部:取最暗 5% 像素,评估暗部颗粒
dark_cut = np.quantile(y, 0.05)
dark_mask = y < dark_cut
if dark_mask.sum() < 200:
dark_noise = np.nan
else:
dark_res = res[dark_mask]
dark_noise = 1.4826 * np.median(np.abs(dark_res - np.median(dark_res)))
# 帧间残差差异:用于抓“忽明忽暗”
if prev_res is not None and prev_res.shape == res.shape:
diff = res - prev_res
flicker_diff = flo