@@ -129,12 +129,6 @@ def __init__(self):
129129 self .cached_denoise_full = None
130130 self .cached_denoise_proxy = None
131131 self .last_denoise_key = None
132- # Fast tier: proxy-resolution denoise (~3s) shown while the full-res
133- # pass (~40s, abortable, idle-scheduled) hasn't landed yet.
134- self .last_denoise_proxy_key = None
135- # View tier: per-ROI denoise results (zoomed views before the full
136- # pass lands). Keyed by (path, roi_rect, shape); tiny LRU.
137- self ._denoise_view_cache : dict = {}
138132
139133 self ._should_stop = False
140134 self ._gpu_uint8 = None # Pre-allocated pooled uint8 output buffer
@@ -1045,130 +1039,70 @@ def _trim_executor_caches(self, budget_bytes: Optional[int] = None):
10451039 full .trim (remaining )
10461040
10471041 def _executor_denoise (self , src : np .ndarray ) -> np .ndarray :
1042+ """降噪 = 解码链的一环(去马赛克后):原生分辨率只算一次,
1043+ 内存+磁盘双缓存;所有视图(代理/任意缩放 ROI/导出)从结果派生,
1044+ 缩放拖动永不触发重算。计算期间仅当切换到其他照片时让路。"""
10481045 path = self ._executor_path
10491046 if path is None :
10501047 return src
10511048
10521049 denoise_cache_key = (path , 'denoise' )
1053-
1054- # Proxy-mode preview. With the full result cached, serve its
1055- # downscale; otherwise denoise the proxy itself (~3s) as the fast
1056- # tier — the full pass runs later (idle refine / export) and its
1057- # arrival flips the op tier, re-rendering from the exact result.
1058- if self ._executor_using_proxy :
1059- if (
1060- denoise_cache_key == self .last_denoise_key
1061- and self .cached_denoise_full is not None
1062- ):
1063- if self .cached_denoise_proxy is None :
1064- self .cached_denoise_proxy = self ._make_proxy (self .cached_denoise_full )
1065- if self .cached_denoise_proxy is not None :
1066- return np .ascontiguousarray (self .cached_denoise_proxy )
1067- proxy_key = (path , 'denoise-proxy' )
1068- if (
1069- proxy_key == self .last_denoise_proxy_key
1070- and self .cached_denoise_proxy is not None
1071- ):
1072- return np .ascontiguousarray (self .cached_denoise_proxy )
1073- try :
1074- self .denoise_started .emit ()
1075- logger .info ("[Worker] SCUNet proxy-tier denoise (fast preview)..." )
1076-
1077- def progress_cb (cur , total ):
1078- self .denoise_progress .emit (cur , total )
1079-
1080- denoised = denoise_rgb_linear (src , progress_callback = progress_cb )
1081- self .cached_denoise_proxy = denoised
1082- self .last_denoise_proxy_key = proxy_key
1083- self .denoise_finished .emit ()
1084- return np .ascontiguousarray (denoised )
1085- except Exception as e :
1086- logger .error (f"[Worker] Proxy denoise failed: { e } " )
1087- self .denoise_finished .emit ()
1088- return src
1089- finally :
1090- denoise_clear_session ()
1091-
1092- if self ._executor_params .get ('_denoise_tier' ) == 'view' :
1093- rect = self ._executor_params .get ('_preview_roi' )
1094- vkey = (path , tuple (rect ) if rect else None , src .shape )
1095- cached = self ._denoise_view_cache .get (vkey )
1050+ missing = (denoise_cache_key != self .last_denoise_key
1051+ or self .cached_denoise_full is None )
1052+ if missing :
1053+ from raw_alchemy .pipeline import denoise_disk_cache
1054+ from raw_alchemy .onnx .rgb_denoiser import MODEL_FILE as _DN_TAG
1055+ cached = denoise_disk_cache .load (path , _DN_TAG )
10961056 if cached is not None :
1097- return np .ascontiguousarray (cached )
1098- try :
1099- self .denoise_started .emit ()
1100- logger .info ("[Worker] SCUNet view-tier denoise (ROI)..." )
1101- abortable = bool (self ._executor_params .get ('_denoise_abortable' ))
1102-
1103- def progress_cb (cur , total ):
1104- self .denoise_progress .emit (cur , total )
1105- if abortable :
1106- with self .lock :
1107- superseded = self .pending_request is not None
1108- if superseded :
1109- raise PipelineAborted (
1110- f"view denoise superseded at tile { cur } /{ total } "
1111- )
1112-
1113- denoised = denoise_rgb_linear (src , progress_callback = progress_cb )
1114- if len (self ._denoise_view_cache ) >= 4 :
1115- self ._denoise_view_cache .pop (next (iter (self ._denoise_view_cache )))
1116- self ._denoise_view_cache [vkey ] = denoised
1117- self .denoise_finished .emit ()
1118- return np .ascontiguousarray (denoised )
1119- except PipelineAborted :
1120- self .denoise_finished .emit ()
1121- raise
1122- except Exception as e :
1123- logger .error (f"[Worker] View denoise failed: { e } " )
1124- self .denoise_finished .emit ()
1125- return src
1126- finally :
1127- denoise_clear_session ()
1057+ self .cached_denoise_original = self .cpu_linear
1058+ self .cached_denoise_full = cached
1059+ self .cached_denoise_proxy = None
1060+ self .last_denoise_key = denoise_cache_key
1061+ missing = False
11281062
1129- if denoise_cache_key != self .last_denoise_key or self .cached_denoise_full is None :
1063+ if missing :
1064+ # 无论当前视图是代理还是 ROI,都对原生全分辨率源计算
1065+ source = self .cpu_linear if self .cpu_linear is not None else src
11301066 try :
11311067 self .denoise_started .emit ()
1132- logger .info ("[Worker] SCUNet RGB denoise (post-demosaic)..." )
1133-
1134- abortable = bool (self ._executor_params .get ('_denoise_abortable' ))
1068+ logger .info ("[Worker] SCUNet RGB denoise (native, once)..." )
11351069
11361070 def progress_cb (cur , total ):
11371071 self .denoise_progress .emit (cur , total )
1138- if abortable :
1139- with self .lock :
1140- superseded = self .pending_request is not None
1141- if superseded :
1142- raise PipelineAborted (
1143- f"full denoise superseded at tile { cur } /{ total } "
1144- )
1145-
1146- denoised = denoise_rgb_linear (
1147- src ,
1148- progress_callback = progress_cb ,
1149- )
1072+ with self .lock :
1073+ pr = self .pending_request
1074+ if pr is not None and pr .path != path :
1075+ raise PipelineAborted (
1076+ f"denoise superseded by another photo at { cur } /{ total } " )
11501077
1078+ denoised = denoise_rgb_linear (source , progress_callback = progress_cb )
11511079 self .cached_denoise_original = self .cpu_linear
11521080 self .cached_denoise_full = denoised
11531081 self .cached_denoise_proxy = None
11541082 self .last_denoise_key = denoise_cache_key
1083+ denoise_disk_cache .save (path , _DN_TAG , denoised )
11551084 self .denoise_finished .emit ()
11561085 except PipelineAborted :
11571086 self .denoise_finished .emit ()
11581087 raise
11591088 except Exception as e :
1160- logger .error (f"[Worker] Denoising failed: { e } " )
1089+ logger .error (f"[Worker] Denoising failed: { type ( e ). __name__ } : { e } " )
11611090 self .denoise_finished .emit ()
11621091 self .cached_denoise_original = None
11631092 self .cached_denoise_full = None
11641093 self .cached_denoise_proxy = None
11651094 self .last_denoise_key = None
11661095 return src
11671096 finally :
1168- # Always drop the ONNX session: on DirectML it pins hundreds
1169- # of MB of VRAM that the interactive preview renderer needs.
11701097 denoise_clear_session ()
11711098
1099+ if self ._executor_using_proxy :
1100+ if self .cached_denoise_proxy is None :
1101+ self .cached_denoise_proxy = self ._make_proxy (self .cached_denoise_full )
1102+ if self .cached_denoise_proxy is not None :
1103+ return np .ascontiguousarray (self .cached_denoise_proxy )
1104+ return np .ascontiguousarray (self .cached_denoise_full )
1105+
11721106 denoised = np .ascontiguousarray (self .cached_denoise_full )
11731107 if not self ._executor_params .get ('lens_correct' , False ):
11741108 self .cpu_corrected = denoised
@@ -1296,16 +1230,11 @@ def _executor_auto_gain(self, _metering_img: np.ndarray, metering_mode: str) ->
12961230 def _prepare_executor_source_state (self , params : ProcessorParams ):
12971231 self ._executor_corrected_source = None
12981232 denoise_enabled = params .get ('denoise_enabled' , False )
1299- if not denoise_enabled and (
1300- self .last_denoise_key is not None
1301- or self .last_denoise_proxy_key is not None
1302- ):
1233+ if not denoise_enabled and self .last_denoise_key is not None :
13031234 self .cached_denoise_original = None
13041235 self .cached_denoise_full = None
13051236 self .cached_denoise_proxy = None
13061237 self .last_denoise_key = None
1307- self .last_denoise_proxy_key = None
1308- self ._denoise_view_cache .clear ()
13091238
13101239 if not params .get ('lens_correct' , False ):
13111240 if denoise_enabled and self .cached_denoise_full is not None :
@@ -1467,25 +1396,6 @@ def _do_process(self, request: ProcessRequest):
14671396 # zoom>fit ROI rendering (T7.5): resolve the visible-region crop
14681397 # before the output key so the view key covers the ROI rect.
14691398 roi_info = None if use_proxy else self ._compute_preview_roi (params )
1470- if params .get ('denoise_enabled' , False ):
1471- full_ready = (
1472- (request .path , 'denoise' ) == self .last_denoise_key
1473- and self .cached_denoise_full is not None
1474- )
1475- # tier(与 DPI 挡位同一哲学:屏幕需要多少就处理多少):
1476- # proxy = fit 视图对 3MP 代理降噪(~3s)
1477- # view = 放大视图只对 DPI 挡位后的 ROI 降噪(~6-19s,带缓存)
1478- # full = 精确结果(空闲精化产出/导出)
1479- if full_ready :
1480- params ['_denoise_tier' ] = 'full'
1481- elif use_proxy :
1482- params ['_denoise_tier' ] = 'proxy'
1483- elif roi_info is not None :
1484- params ['_denoise_tier' ] = 'view'
1485- else :
1486- params ['_denoise_tier' ] = 'full'
1487- # 预览请求中的降噪按 tile 让路给新交互(空闲精化重试)
1488- params ['_denoise_abortable' ] = True
14891399 if roi_info is not None :
14901400 params ['_preview_roi' ] = roi_info [0 ]
14911401 params ['_roi_full_size' ] = roi_info [1 ]
@@ -1536,13 +1446,6 @@ def _do_process(self, request: ProcessRequest):
15361446 _tw , _th = self ._make_roi_target_size (_rw , _rh , _fw , _fh , params )
15371447 _roi_out = (_tw , _th ) if (_tw < _rw * 0.95 or _th < _rh * 0.95 ) else None
15381448 ops = self ._insert_roi_op (ops , roi_info [0 ], _roi_out )
1539- if params .get ('_denoise_tier' ) == 'view' :
1540- di = next ((k for k , o in enumerate (ops ) if o .name == 'denoise' ), None )
1541- ri = next ((k for k , o in enumerate (ops ) if o .name == 'roi' ), None )
1542- if di is not None and ri is not None and di < ri :
1543- d_op = ops .pop (di )
1544- ri = next (k for k , o in enumerate (ops ) if o .name == 'roi' )
1545- ops .insert (ri + 1 , d_op )
15461449 executor_source = self ._select_executor_source (use_proxy )
15471450 executor = self ._get_preview_executor ()
15481451 # Cooperative cancellation (T7.3): a newer user request aborts
0 commit comments