
    +jJ                     8   S r SSKrSSKJr  SSKrSSKJs  Jr  SSK	J
r
  SSKrS"S jrS#S jrS rS rS	 rS
 rS rS rS rS rS r " S S5      r " S S5      rSSSSSS.S\R4                  S\S\S\S\S\S\\R:                     S \R4                  4S! jjrg)$u)  
Optical-flow-warped noise for VOID Pass 2 refinement.

Adapted from RyannDaGreat/CommonSource (MIT License, Ryan Burgert):
  https://github.com/RyannDaGreat/CommonSource
  - noise_warp.py  (NoiseWarper / warp_xyωc / regaussianize / get_noise_from_video)
  - raft.py        (RaftOpticalFlow)

Only the code paths that ``comfy_extras/nodes_void.py::VOIDWarpedNoise`` actually
uses (torch THWC uint8 input, no background removal, no visualization, no disk
I/O, default warp/noise params) have been inlined.  External ``rp`` utilities
have been replaced with equivalents from torch.nn.functional / einops.  The
RAFT optical-flow model itself is loaded offline via ``OpticalFlowLoader`` in
``nodes_void.py`` and passed into ``get_noise_from_video`` by the caller; this
module never downloads weights at runtime.
    N)Optional)	rearrangec                    U R                   S:w  a!  [        S[        U R                  5       35      eU R                  u  pEn[	        U[
        [        45      (       aD  [	        U[        5      (       d/  [        S[        XQ-  5      5      n[        S[        Xa-  5      5      nOUu  pxXx4XV4:X  a  U(       a  U R                  5       $ U $ 0 n	US;   a  SU	S'   [        R                  " U S   4Xx4US.U	D6S	   n
U
$ )
a  Resize a CHW tensor.

``size`` is either a scalar factor or a (h, w) tuple.  ``interp`` is one
of ``"bilinear"``, ``"nearest"``, ``"area"``.  When ``copy`` is False and
the requested size matches the input, returns the input tensor as is
(faster but callers must not mutate the result).
   z5_torch_resize_chw expects a 3D CHW tensor, got shape    )bilinearbicubicFalign_cornersNsizemoder   )ndim
ValueErrortupleshape
isinstanceintfloatboolmaxcloneFinterpolate)imager   interpcopy_in_hin_wnew_hnew_wkwargsouts              </home/wildlama/comfy/ComfyUI/comfy_extras/void_noise_warp.py_torch_resize_chwr%       s     zzQCE%++DVCWX
 	
 KKMAT$e%%jt.D.DAs4;'(As4;'(~$% $u{{}/%/F(("'
--d
P5.v
P
PQR
SCJ    c                    U R                   S:w  a!  [        S[        U R                  5       35      eUR                  UR                  :w  a7  [        S[        UR                  5       S[        UR                  5       35      eU R                  u  pEnU[        R
                  " XaR                  UR                  S9-   nU[        R
                  " XRR                  UR                  S9SS2S4   -   nXvS-
  -  S-  S-
  n	XS-
  -  S-  S-
  n
[        R                  " X/S	S
9S   R                  U R                  5      n[        R                  " U S   XSSS9S   nU$ )zRelative remap of a CHW image via ``F.grid_sample``.

Equivalent to ``rp.torch_remap_image(image, dx, dy, relative=True, interp=interp)``
for ``interp`` in {"bilinear", "nearest"}.  Out-of-bounds samples are 0.
r   z9_torch_remap_relative expects a 3D CHW tensor, got shape z1_torch_remap_relative: dx and dy must match, got  vs devicedtypeNr      dimTzeros)r   r
   padding_moder   )r   r   r   r   torcharanger*   r+   stacktor   grid_sample)r   dxdyr   r   hwx_absy_absx_normy_normgridr#   s                r$   _torch_remap_relativer@   =   sD    zzQGekkHZG[\
 	
 
xx288?bhh?PPTUZ[][c[cUdTef
 	
 kkGA!a		BBEa		B1d7KKE1uo"Q&F1uo"Q&F;;'R0699%++FD
--dTd	C Jr&   c                 t   U R                   S:w  a!  [        S[        U R                  5       35      eU R                  u  p4nUR                  XE4:w  d  UR                  XE4:w  a=  [        SU SU S[        UR                  5       S[        UR                  5       35      eUR	                  5       [
        R                  " XQR                  [
        R                  S9-   nUR	                  5       [
        R                  " XBR                  [
        R                  S9SS2S4   -   nUS	:  Xt:  -  US	:  -  Xe:  -  R                  S
5      nXu-  U-   R                  S
5      U   n	[        U S5      U   n
[
        R                  " XE-  U4U R                  U R                  S9nUR                  S	X5        [        USXES9$ )zScatter-add a CHW image using relative floor-rounded (dx, dy) offsets.

Equivalent to ``rp.torch_scatter_add_image(image, dx, dy, relative=True,
interp='floor')``.  Out-of-bounds targets are dropped.
r   z?_torch_scatter_add_relative expects a 3D CHW tensor, got shape z,_torch_scatter_add_relative: dx/dy must be (, z
), got dx=z dy=r)   Nr   r-   c h w -> (h w) cr+   r*   (h w) c -> c h wr9   r:   )r   r   r   r   longr2   r3   r*   reshaper   r0   r+   
index_add_)r   r7   r8   in_cr   r   xyvalidindices
flat_imager#   s               r$   _torch_scatter_add_relativerP   Z   s    zzQMeTYT_T_N`Mab
 	
 {{D	xxD<288|#;:4&4& IBHHo&d5?*;=
 	

 		ELLiiuzzJJA
	ELLiiuzzJ1d7SSA1f"a1f-:CCBGEx!|$$R(/G5"45e<J
++t{D)U\\
RCNN1g*S,==r&   c                     U R                   u  pn[        U S5      n[        R                  " USSSSS9u  pVn[        USX#S9nXWU4$ )zFind unique pixel values in a CHW tensor.

Returns ``(unique_colors [U, C], counts [U], index_matrix [H, W])`` where
``index_matrix[i, j]`` is the index of the unique color at that pixel.
rC   r   TF)r/   return_inversereturn_countssortedz(h w) -> h wrF   )r   r   r2   unique)	r   r   r9   r:   flatunique_colorsinverse_indicescountsindex_matrixs	            r$   unique_pixelsr[   {   sW     kkGA!U./D-2\\!DU.*MF _nGL,..r&   c                 D   U R                   u  p#n[        UR                  5       R                  5       5      S-   n[	        U S5      n[
        R                  " XVR                   S   4UR                  UR                  S9nUR                  SUR                  S5      U5        U$ )z>For each unique index, sum the CHW image values at its pixels.r   rC   rD   r   r-   )r   r   r   itemr   r2   r0   r+   r*   rI   view)r   rZ   r   r9   r:   urV   r#   s           r$   sum_indexed_valuesr`      s    kkGA!L##%&*AU./D
++q**Q-(

4;;
OCNN1l''+T2Jr&   c                 Z    U R                   u  p#XR                  S5         n[        USX#S9$ )z@Build a CHW image from an index matrix and a (U, C) color table.r-   rE   rF   )r   r^   r   )rZ   rW   r9   r:   rV   s        r$   indexed_to_imagerb      s2    DA**2./DT-88r&   c                    U R                   u  pn[        U SS 5      u  pn[        R                  " U 5      n[	        Xe5      n[        XW[        US5      -  5      nXh-
  n	[        U[        US5      5      n
X
S-  -  U	-   nX4$ )a7  Variance-preserving re-sampling of a CHW noise tensor.

Wherever the noise contains groups of identical pixel values (e.g. after
a nearest-neighbor warp that duplicated source pixels), adds zero-mean
foreign noise within each group and scales by ``1/sqrt(count)`` so the
output is unit-variance gaussian again.
Nr   zu -> u 1      ?)r   r[   r2   
randn_liker`   rb   r   )noiser   hswsrY   rZ   foreign_noisesummedmeanedzeroed_foreigncounts_imageoutputs               r$   regaussianizero      s     IA2+E"1I6A|$$U+M<FlYvz5R,RSF"+N#L)FJ2OPLS((>9Fr&   c           	         U R                   u  pn[        R                  " [        R                  " X R                  U R
                  S9[        R                  " X0R                  U R
                  S9SS9u  pE[        R                  " XT/5      $ )zIReturn a (2, H, W) tensor of (x, y) pixel coordinates matching ``image``.r)   ij)indexing)r   r2   meshgridr3   r*   r+   r4   )r   r   r9   r:   rL   rK   s         r$   xy_meshgrid_like_imagert      sa    kkGA!>>Q||5;;?Q||5;;?DA
 ;;vr&   c                     [         R                  " U SS 5      n[         R                  " U SS 5      n[         R                  " XX /5      $ )uT   Pack a (C, H, W) noise tensor into a state tensor (3+C, H, W) = [dx, dy, ω, noise].Nr   )r2   
zeros_like	ones_likecat)rf   r0   oness      r$   noise_to_staterz      s@    U2AY'E??5!9%D99eD011r&   c                     U SS $ )z.Unpack the noise channels from a state tensor.r   N )states    r$   state_to_noiser~      s    9r&   c           
      p  ^ UR                   U R                   :w  a%  [        SUR                    SU R                    35      eU R                  S:w  a!  [        S[        U R                  5       35      eU R                  u  p#nUR                  SX44:w  a'  [        SU SU S[        UR                  5       35      eU R                   nS	u  pgSnSn	Sn
X)-
  nX(-
  nUS
::  a  [        SU S35      eX
   S
:  R                  5       (       d  [        S5      e[        U 5      n[        R                  " U 5      nS
USU& SX'   S
X* S& [        R                  " U 5      n[        U SU US
   * US   * S5      USU& [        X* S US
   * US   * S5      X* S& SX   X   S
:H  '   U R                  5       nUSU=== U-  sss& UUSU -   R                  5       nUU   S
:  UU   U:  -  UU   S
:  -  UU   U:  -  n[        R                  " US   ) UU5      nUSU R                  5       mUSU=== T-  sss& S
USU& U4S jnU" [        R                  " SX4U R                  US95      S
:  n[        R                  " UX5      n[        R                  " UU/SS9n[!        UU* S 5      u  UU* S& nUU
   US
   -  UU
'   UU
   R#                  5       UU
'   [        R$                  " USSS9u  nn[        R                  " U5      nU" UU
   S   5      S
   UU
'   U" USU UU
   S   -  5      UU
   S   -  USU& U" UU* S UU
   S   -  5      U" UU
   S   S-  5      R'                  5       -  UU* S& [        R                  " UUU5      nUU
   UU
   R)                  5       -  UU
'   UU
==   S-  ss'   UU
==   S-  ss'   U$ )u   Warp a noise-warper state tensor along the given optical flow.

``state`` has shape ``(3+c, h, w)`` (= dx, dy, ω, c noise channels).
``flow`` has shape ``(2, h, w)`` (= dx, dy).
z@warp_state: flow and state must be on the same device, got flow=z state=r   z4warp_state: state must be 3D (3+C, H, W), got shape r,   z%warp_state: flow must have shape (2, rB   z), got )r   r   r   zDwarp_state: state has no noise channels (expected 3+C with C>0, got z
 channels)z/warp_state: all weights in state[2] must be > 0Nr   nearestc                 *   > [        U TS   TS   5      $ )Nr   r   )rP   )tensorscat_xys    r$   scatwarp_state.<locals>.scat  s    *671:wqzJJr&   rD   r.   )chunksr/   gh㈵>gH.?)r*   r   r   r   r   allrt   r2   
empty_liker@   r   roundwherery   r+   rx   ro   
nan_to_numchunksqrtmean)r}   flowxyocr9   r:   r*   x_chy_chxyxyww_chcocr?   init
pre_expand
pre_shrinkpos	in_boundsr   shrink_maskconcatrm   expandshrinkrn   r   s                             @r$   
warp_stater      sJ    {{ell"}GELL>;
 	
 zzQB5CUBVW
 	
 JDQzzaY3A3b75CTBUV
 	
 \\FJD	
B
CD
A	BAvRSWRXXbc
 	
 K!O  ""JKK!%(DE"DD"IDJDI !!%(J+E#2Ja47(IVJsO,U34[47(T!WHiXJst./JZ%*+ JsOtO*Sb/!
(
(
*CTaCIM2c$i1nETUVWIio-tZ@J"o##%GsOwOJsOK
 uzz!QVLMPQQK [$;J YY
J/Q7F -faRSk :FA23K$<,q/1F4L$<**,F4LVA1=Jj)F
4(./2F4Lz#2D)9$)??@6$<PTCUUF3BKz1"#D)9$)??@44!#D
dfFA23K [[ff5F$<&,"3"3"55F4L
4LDL
4LVLMr&   c                   N    \ rS rSrSr\R                  4S jr\S 5       r	S r
Srg)NoiseWarperi$  a  Maintain a warpable noise state and emit gaussian noise per frame.

Simplified from RyannDaGreat/CommonSource/noise_warp.py::NoiseWarper:
``scale_factor``, ``post_noise_alpha``, ``progressive_noise_alpha``, and
``warp_kwargs`` are all dropped since VOIDWarpedNoise always uses defaults.
c                     US::  d  US::  d  US::  a  [        SU SU SU 35      eXl        X l        X0l        X@l        XPl        [        R                  " XX5US9n[        U5      U l	        g )Nr   z/NoiseWarper: c/h/w must all be positive, got c=z h=z w=rD   )
r   r   r9   r:   r*   r+   r2   randnrz   _state)selfr   r9   r:   r*   r+   rf   s          r$   __init__NoiseWarper.__init__,  sq    6Q!VqAvA!Cs#aSQ  
A!@$U+r&   c                 z    [        U R                  5      nU R                  SS nX-  US-  R                  5       -  $ )Nr,   r   )r~   r   r   )r   nweightss      r$   rf   NoiseWarper.noise:  s<    
 4;;'++a"{gl00222r&   c                    UR                   UR                   :w  a7  [        S[        UR                   5       S[        UR                   5       35      e[        R                  " X/5      R                  U R                  U R                  5      nUR                   u  pEn[        X0R                  U R                  4SSS9nUR                   SS  u  pxUS==   Xu-  -  ss'   US==   X-  -  ss'   [        U R                  U5      U l        U $ )	Nz'NoiseWarper: dx and dy must match, got r(   r   Tr   r   r   )r   r   r   r2   r4   r5   r*   r+   r%   r9   r:   r   r   )	r   r7   r8   r   r   oflowhoflowwflowhflowws	            r$   __call__NoiseWarper.__call__C  s    88rxx9%/9J$uUWU]U]N_`  {{B8$''TZZ@ JJ6 '7$Ozz"#
 	Q5>!Q5>! d3r&   )r   r   r*   r+   r9   r:   N)__name__
__module____qualname____firstlineno____doc__r2   float32r   propertyrf   r   __static_attributes__r|   r&   r$   r   r   $  s.     /4mm , 3 3r&   r   c                   .    \ rS rSrSrSS jrS rS rSrg)	RaftOpticalFlowi\  aU  RAFT-large wrapper around a pre-loaded torchvision model.

``model`` must be the ``torchvision.models.optical_flow.raft_large`` module
with its weights already populated; this class is load-agnostic so the
caller owns downloading/offload concerns (see ``OpticalFlowLoader`` in
``nodes_void.py``).  ``__call__`` returns a ``(2, H, W)`` flow.
Nc                    Uc  [         R                  R                  5       n[        U[        R
                  5      (       d  [        R
                  " U5      OUnUR                  U5      nUR                  5         X l        Xl        g N)	comfymodel_managementget_torch_devicer   r2   r*   r5   evalmodel)r   r   r*   s      r$   r   RaftOpticalFlow.__init__e  s]    >++<<>F-7-M-Mf%SY 


r&   c                     UR                  U R                  [        R                  5      nUR                  u  p4nUS-  S-  nUS-  S-  n[        X&U4SSS9nUS-  S-
  nUS    $ )N   r   Fr   r,   r   )r5   r*   r2   r   r   r%   )r   	image_chwr   r   r9   r:   r    r!   s           r$   _preprocessRaftOpticalFlow._preprocesso  sj    T[[%--8++aa1a1!%%P	AT{r&   c                    UR                   UR                   :w  a7  [        S[        UR                   5       S[        UR                   5       35      eUR                   u  p4n[        R                  " 5          U R                  U5      nU R                  U5      nU R                  Xg5      nUS   S   n	U	R                   SS XE4:w  a  [        XU4SSS	9n	SSS5        U	$ ! , (       d  f       W	$ = f)
z:``from_image``, ``to_image``: CHW float tensors in [0, 1].z9RaftOpticalFlow: from_image and to_image must match, got r(   r-   r   r   Nr   Fr   )r   r   r   r2   no_gradr   r   r%   )
r   
from_imageto_imager   r9   r:   img1img2list_of_flowsr   s
             r$   r   RaftOpticalFlow.__call__x  s    x~~-Z--./tE(..4I3JL  ""a]]_##J/D##H-D JJt2M $Q'Dzz"#1&((1vzN   _ s   6AC
C,)r*   r   r   )	r   r   r   r   r   r   r   r   r   r|   r&   r$   r   r   \  s    r&   r      rd   r       )noise_channelsresize_framesresize_flowdownscale_factorr*   video_framesraftr   r   r   r   r*   returnc                v  ^ [        U[        5      (       a  US:  a  [        SU< 35      eU R                  S:w  d  U R                  S   S:w  a!  [        S[        U R                  5       35      eU R                  [        R                  :w  a  [        SU R                   35      eUc  [        R                  R                  5       n[        U[        R                  5      (       d  [        R                  " U5      OUnUR                  S	:X  a  [        R                   " S
5        U R                  S   nU R#                  U5      R%                  SSSS5      R#                  [        R&                  5      S-  nUS:w  aa  [)        S[        UR                  S   U-  5      5      n	[)        S[        UR                  S   U-  5      5      n
[*        R,                  " XU
4SS9nUR                  u    pnXL-  nXM-  nUT-  (       d
  UT-  (       a  [        R                   " SXT5        [        R.                  " 5          [1        X.XS9nUR2                  T-  nUR4                  T-  n[        R6                  " UUUU4[        R&                  US9nU4S jnU" UR8                  5      R%                  SSS5      US'   US   n[;        SU5       HW  nUU   nU" UU5      R#                  U5      nU" US   US   5        U" UR8                  5      R%                  SSS5      UU'   UnMY     SSS5        U$ ! , (       d  f       W$ = f)a  Produce optical-flow-warped gaussian noise from a video.

Args:
    video_frames: ``(T, H, W, 3)`` uint8 torch tensor.
    raft: Pre-loaded RAFT optical-flow wrapper (see ``RaftOpticalFlow``).
    noise_channels: Channels in the output noise.
    resize_frames: Pre-RAFT frame scale factor.
    resize_flow: Post-flow up-scale factor applied to the optical flow;
        the internal noise state is allocated at
        ``(resize_flow * resize_frames * H, resize_flow * resize_frames * W)``.
    downscale_factor: Area-pool factor applied to the noise before return;
        should evenly divide the internal noise resolution.
    device: Target device.  Defaults to ``comfy.model_management.get_torch_device()``.

Returns:
    ``(T, H', W', noise_channels)`` float32 noise tensor on ``device``.
r   z>get_noise_from_video: resize_flow must be a positive int, got    r-   r   zEget_noise_from_video: video_frames must have shape (T, H, W, 3), got zHget_noise_from_video: video_frames must be uint8 in [0, 255], got dtype NcpuzVOIDWarpedNoise: running get_noise_from_video on CPU; this will be slow (minutes for ~45 frames).  Use CUDA for interactive use.r   r,   g     o@      ?arear   ztVOIDWarpedNoise: internal noise size %dx%d is not divisible by downscale_factor %d; output noise may have artifacts.)r   r9   r:   r*   rD   c                 ,   > [        U ST-  SSS9nUT-  $ )Nr   r   Fr   )r%   )	noise_chwdownr   s     r$   	downscale'get_noise_from_video.<locals>.downscale  s(     %Y6F0FUZ[D***r&   )r   r   r   r   r   r   r+   r2   uint8	TypeErrorr   r   r   r*   typeloggingwarningr5   permuter   r   r   r   r   r   r9   r:   emptyrf   range)r   r   r   r   r   r   r*   Tframesr    r!   r   HW
internal_h
internal_wwarperdown_hdown_wrn   r   previcurrr   s        `                   r$   get_noise_from_videor     s   6 k3'';?L[O\
 	
 A!3!3B!71!<++,-/
 	
 U[[(%++,.
 	

 ~''88:)3FELL)I)IU\\&!vF{{eL	

 	1A__V$,,Q1a8;;EMMJURFAs6<<?]:;<As6<<?]:;<vENHJAqQJJ$$
5E(ED$4	
 
j
 ----/u}}V
	+ fll+33Aq!<q	ayq!A!9Dd#&&v.D47DG$!&,,/771a@F1ID ) 
6 M7 
6 Ms   6C)L))
L8)T)r   )r   r   typingr   r2   torch.nn.functionalnn
functionalr   einopsr   comfy.model_managementr   r%   r@   rP   r[   r`   rb   ro   rt   rz   r~   r   r   r   Tensorr   r   r*   r   r|   r&   r$   <module>r     s   "       ::>B/9 ,2
Yx1 1p+ +l %)`,,`
` 	`
 ` ` ` U\\"` \\`r&   