
    W|h(C                         S r SSKJr  SSKrSSKJrJr  \\" S/5      SS j5       5       r " S S5      r	 " S	 S
\
5      r\ SS j5       rg)z?Contains everything that can help automate the cuts in MoviePy.    )defaultdictN)convert_parameter_to_secondsuse_clip_fps_by_default
start_timec           	      
  ^  U 4S jn[         R                  " UT R                  SU-  5      SS nU" S5      nU Vs/ s H"  n[         R                  " XS" U5      5      S   PM$     nnU[         R                  " U5         $ s  snf )a  Find the period of a video based on frames correlation.

Parameters
----------

clip : moviepy.Clip.Clip
  Clip for which the video period will be computed.

fps : int, optional
  Number of frames per second used computing the period. Higher values will
  produce more accurate periods, but the execution time will be longer.

start_time : float, optional
  First timeframe used to calculate the period of the clip.

Examples
--------

.. code:: python

    from moviepy import *
    from moviepy.video.tools.cuts import find_video_period

    clip = VideoFileClip("media/chaplin.mp4").subclipped(0, 1).loop(2)
    round(videotools.find_video_period(clip, fps=80), 6)
    1
c                 B   > TR                  U 5      R                  5       $ N)	get_frameflatten)tclips    R/home/james-whalen/.local/lib/python3.13/site-packages/moviepy/video/tools/cuts.pyframe find_video_period.<locals>.frame)   s    ~~a ((**       Nr   )r   r   )nparangedurationcorrcoefargmax)r   fpsr   r   timingsrefr   corrss   `       r   find_video_periodr   
   sv    >+ ii
DMM1s7;AB?G
(C7>?w!R[[eAh'-wE?299U#$$ @s   )B c                   6    \ rS rSrSrS rS rS rS rS r	Sr
g	)
FramesMatch2   a+  Frames match inside a set of frames.

Parameters
----------

start_time : float
  Starting time.

end_time : float
  End time.

min_distance : float
  Lower bound on the distance between the first and last frames

max_distance : float
  Upper bound on the distance between the first and last frames
c                 F    Xl         X l        X0l        X@l        X!-
  U l        g r	   )r   end_timemin_distancemax_distance	time_span)selfr   r!   r"   r#   s        r   __init__FramesMatch.__init__E   s"    $ ((!.r   c                 d    SU R                   U R                  U R                  U R                  4-  $ )Nz(%.04f, %.04f, %.04f, %.04f)r   r!   r"   r#   r%   s    r   __str__FramesMatch.__str__L   s4    -OOMM	1
 
 	
r   c                 "    U R                  5       $ r	   )r+   r*   s    r   __repr__FramesMatch.__repr__T   s    ||~r   c                 p    [        U R                  U R                  U R                  U R                  45      $ r	   )iterr   r!   r"   r#   r*   s    r   __iter__FramesMatch.__iter__W   s/    __dmmT->->@Q@QR
 	
r   c                     UR                   U R                   :H  =(       aY    UR                  U R                  :H  =(       a9    UR                  U R                  :H  =(       a    UR                  U R                  :H  $ r	   r)   )r%   others     r   __eq__FramesMatch.__eq__\   sb    / 8$--/8""d&7&778 ""d&7&77		
r   )r!   r#   r"   r   r$   N)__name__
__module____qualname____firstlineno____doc__r&   r+   r.   r2   r6   __static_attributes__ r   r   r   r   2   s     $/



r   r   c                   j    \ rS rSrSrS rSS jrS rS r\	S 5       r
\	SS	 j5       r SS
 jrS rSrg)FramesMatchese   zFrames matches inside a set of frames.

You can instantiate it passing a list of FramesMatch objects or
using the class methods ``load`` and ``from_clip``.

Parameters
----------

lst : list
  Iterable of FramesMatch objects.
c                 B    [         R                  U [        US S95        g )Nc                     U R                   $ r	   )r#   )es    r   <lambda>(FramesMatches.__init__.<locals>.<lambda>s   s    annr   key)listr&   sorted)r%   lsts     r   r&   FramesMatches.__init__r   s    dF3,DEFr   Nc                 n    Ub  [        U 5      U-  S-  nUS:X  a  U S   $ [        U S[        U5       5      $ )aO  Returns a new instance of FramesMatches object or a FramesMatch
from the current class instance given different conditions.

By default returns the first FramesMatch that the current instance
stores.

Parameters
----------

n : int, optional
  Number of matches to retrieve from the current FramesMatches object.
  Only has effect when ``percent=None``.

percent : float, optional
  Percent of the current match to retrieve.

Returns
-------

FramesMatch or FramesMatches : If the number of matches to retrieve is
  greater than 1 returns a FramesMatches object, otherwise a
  FramesMatch.

Nd   r   r   )lenr@   int)r%   npercents      r   bestFramesMatches.bestu   sA    2 D	G#c)Aq&tAwCmD3q6N&CCr   c                 *    [        [        X5      5      $ )a  Return a FramesMatches object obtained by filtering out the
FramesMatch which do not satistify a condition.

Parameters
----------

condition : func
  Function which takes a FrameMatch object as parameter and returns a
  bool.

Examples
--------
.. code:: python

    # Only keep the matches corresponding to (> 1 second) sequences.
    new_matches = matches.filter(lambda match: match.time_span > 1)
)r@   filter)r%   	conditions     r   rV   FramesMatches.filter   s    $ VI455r   c                     [         R                  " U[         R                  " U  Vs/ s H"  n[         R                  " [        U5      5      PM$     sn5      SSS9  gs  snf )zSave a FramesMatches object to a file.

Parameters
----------

filename : str
  Path to the file in which will be dumped the FramesMatches object data.
z%.03f	)fmt	delimiterN)r   savetxtarrayrI   )r%   filenamerD   s      r   saveFramesMatches.save   sD     	

HH6AbhhtAw'67		
6s   )Ac                 ~    [         R                  " U 5      nU Vs/ s H  n[        U6 PM     nn[        U5      $ s  snf )zLoad a FramesMatches object from a file.

Parameters
----------

filename : str
  Path to the file to use loading a FramesMatches object.

Examples
--------
>>> matching_frames = FramesMatches.load("somefile")
)r   loadtxtr   r@   )r_   arrrD   mfss       r   loadFramesMatches.load   s:     jj"(+,1{A,S!! -s   :c           
      |  ^^^ U R                   U R                  -  S-  mU4S jm0 mUU4S jn/ nU R                  SUS9 GH  u  pxSUR                  5       -  n	T" X5      n
[        R
                  " U
5      n[        TR                  5       5       H[  nX|-
  U:  a  TR                  U5        M  [        TU   S   U-
  5      TU   S   U-   S.TU   U'   TU   U   S	   U:  TU   U   S
'   M]     [        TR                  5       5      nXUS.TU'   [        U5       H  u  pTU   U   S
   (       a  M  U" X|5      nU=TU   U   S	'   TU   U   S'   X:  TU   U   S
'   XS-   S  HZ  nTU   U   TU   U   nn[        US   UUS   -   5      US'   [        US	   UUS   -
  US	   U-
  5      US	'   US	   U:  d  MU  SUS
'   M\     M     UT Vs/ s H5  nUU:w  d  M  TU   U   S
   (       a  M  UUTU   U   S	   TU   U   S   4PM7     sn-  nGM     [        U Vs/ s H  n[        U6 PM     sn5      $ s  snf s  snf )a  Finds all the frames that look alike in a clip, for instance to make
a looping GIF.

Parameters
----------

clip : moviepy.video.VideoClip.VideoClip
  A MoviePy video clip.

distance_threshold : float
  Distance above which a match is rejected.

max_duration : float
  Maximal duration (in seconds) between two matching frames.

fps : int, optional
  Frames per second (default will be ``clip.fps``).

logger : str, optional
  Either ``"bar"`` for progress bar or ``None`` or any Proglog logger.

Returns
-------

FramesMatches
    All pairs of frames with ``end_time - start_time < max_duration``
    and whose distance is under ``distance_threshold``.

Examples
--------

We find all matching frames in a given video and turn the best match
with a duration of 1.5 seconds or more into a GIF:

.. code:: python

    from moviepy import VideoFileClip
    from moviepy.video.tools.cuts import FramesMatches

    clip = VideoFileClip("foo.mp4").resize(width=200)
    matches = FramesMatches.from_clip(
        clip, distance_threshold=10, max_duration=3,  # will take time
    )
    best = matches.filter(lambda m: m.time_span > 1.5).best()
    clip.subclipped(best.start_time, best.end_time).write_gif("foo.gif")
   c                 .   > X-  R                  5       T-  $ r	   )sum)F1F2N_pixelss     r   dot_product,FramesMatches.from_clip.<locals>.dot_product   s    G==?X--r   c                    > T" TU    S   TU   S   5      nTU    S   TU   S   pC[         R                  " X4-   SU-  -
  5      $ )Nr   |F|sq   )r   sqrt)t1t2uvuvro   
frame_dicts        r   distance)FramesMatches.from_clip.<locals>.distance   sS    Z^G4jnW6MNBb>'*JrN7,Cq77151r6>**r   T)
with_timeslogger      ?|F|)minmaxr   rejected)r   rr   r   r   r   N)whiter_framesr   r   rt   rI   keyspopabsrJ   	enumerater   r   r@   r   )r   distance_thresholdmax_durationr   r~   r{   matching_framesr   r   
flat_frame	F_norm_sqF_normrv   t_Fidistt3t3tt2t3ru   rD   rn   ro   rz   s                        @@@r   	from_clipFramesMatches.from_clip   s   ` 66DFF?Q&	. 
	+
 ((D(HHAu}}.J#J;IWWY'F:??,- Fl*NN2&  #:b>%#86#AB)"~e4v=)JrN1%
 #2q)%03EE rN1%j1 . *+C&0VTJqM"3 b>!$Z0FJJ
2q!%(:b>!+<U+C040J
2q!*-!eg,B !+2q 1:b>"3EC!$SZU1C!DCJ!$SZU1CT%[SWEW!XCJ5z$66*.J ' (, $ $B!G L%/^A%6z%B LQ
2q)%0*R.2CE2JK$  O[ If G!k1oGHH  Hs   
H4H4,H4H9c           	      0  ^ Uc  Un[        S 5      nU  H  u  nmpxXV   R                  TXx/5        M     [        UR                  5       S S9n	/ n
SnU	 GH  u  plXk:  a  M  U VVVs/ s H  u  poPM	     nnnnU VVVs/ s H  u  pnX:  d  M  XU4PM     nnnnU VVVs/ s H  u  pnX-
  U:  d  M  XU4PM     nnnnU(       d  Mu  U VVVs1 s H  u  pnXs:  d  M  UiM     nnnnU Vs1 s H  oU-
  S::  d  M  UiM     nnUR	                  U5      (       d  M  [        S U 5       5      m[        U4S jU 5       5      u  mpxU
R                  [        UTXx5      5        Xd-   nGM     [        U
5      $ s  snnnf s  snnnf s  snnnf s  snnnf s  snf )a^  Select the scenes at which a video clip can be reproduced as the
smoothest possible way, mainly oriented for the creation of GIF images.

Parameters
----------

match_threshold : float
  Maximum distance possible between frames. The smaller, the
  better-looping the GIFs are.

min_time_span : float
  Minimum duration for a scene. Only matches with a duration longer
  than the value passed to this parameters will be extracted.

nomatch_threshold : float, optional
  Minimum distance possible between frames. If is ``None``, then it is
  chosen equal to ``match_threshold``.

time_distance : float, optional
  Minimum time offset possible between matches.

Returns
-------

FramesMatches : New instance of the class with the selected scenes.

Examples
--------

.. code:: python

    from pprint import pprint
    from moviepy import *
    from moviepy.video.tools.cuts import FramesMatches

    ch_clip = VideoFileClip("media/chaplin.mp4").subclipped(1, 4)
    mirror_and_clip = [ch_clip.with_effects([vfx.TimeMirror()]), ch_clip]
    clip = concatenate_videoclips(mirror_and_clip)

    result = FramesMatches.from_clip(clip, 10, 3).select_scenes(
        1, 2, nomatch_threshold=0,
    )
    print(result)
    # [(1.0000, 4.0000, 0.0000, 0.0000),
    #  (1.1600, 3.8400, 0.0000, 0.0000),
    #  (1.2800, 3.7200, 0.0000, 0.0000),
    #  (1.4000, 3.6000, 0.0000, 0.0000)]
c                      / $ r	   r>   r>   r   r   rE   -FramesMatches.select_scenes.<locals>.<lambda>q  s    "r   c                     U S   $ Nr   r>   )ks    r   rE   r   u  s    !r   rG   r   g333333?c              3   *   #    U  H	  u  po1v   M     g 7fr	   r>   ).0endr"   r#   s       r   	<genexpr>.FramesMatches.select_scenes.<locals>.<genexpr>  s     VCU?s,cCUs   c              3   <   >#    U  H  oS    T:X  d  M  Uv   M     g7f)r   Nr>   )r   rD   r   s     r   r   r     s      3-a1-s   	)	r   appendrJ   itemsintersectionr   nextr   r@   )r%   match_thresholdmin_time_spannomatch_thresholdtime_distancedict_startsstartr"   r#   starts_endsresult	min_startends_distancesr   endsgreat_matchesgreat_long_matchespoor_matchesshort_matchess                `     r   select_scenesFramesMatches.select_scenes;  s   f $ /!*-6:2E3%%sL&GH 7; [..0nE	%0!E AOP=SCDP :H9G5S1 2L19G   :G"9F5SK=0 2L19F  " & :H9G5S3 9G  
 -1IDS5[S4HSDMI,,];;VCUVVC.2 3-3 /+C MM+eS,MN-II &1L V$$E Q"
 Js6   'E7
?E>
E>
!F
4F
F
F
-F=Fc                 ~    U  H7  u  pE  nSUSU-  SU-  4-  nUR                  XE5      R                  " U40 UD6  M9     g)an  Extract the matching frames represented by the instance from a clip
and write them as GIFs in a directory, one GIF for each matching frame.

Parameters
----------

clip : video.VideoClip.VideoClip
  A video clip whose frames scenes you want to obtain as GIF images.

gif_dir : str
  Directory in which the GIF images will be written.

kwargs
  Passed as ``clip.write_gif`` optional arguments.

Examples
--------

.. code:: python

    import os
    from pprint import pprint
    from moviepy import *
    from moviepy.video.tools.cuts import FramesMatches

    ch_clip = VideoFileClip("media/chaplin.mp4").subclipped(1, 4)
    clip = concatenate_videoclips([ch_clip.time_mirror(), ch_clip])

    result = FramesMatches.from_clip(clip, 10, 3).select_scenes(
        1, 2, nomatch_threshold=0,
    )

    os.mkdir("foo")
    result.write_gifs(clip, "foo")
    # MoviePy - Building file foo/00000100_00000400.gif with imageio.
    # MoviePy - Building file foo/00000115_00000384.gif with imageio.
    # MoviePy - Building file foo/00000128_00000372.gif with imageio.
    # MoviePy - Building file foo/00000140_00000360.gif with imageio.
z%s/%08d_%08d.gifrN   N)
subclipped	write_gif)r%   r   gifs_dirkwargsr   r   _names           r   
write_gifsFramesMatches.write_gifs  sK    P !%E1%3;c	(JJDOOE'11$A&A !%r   r>   )r   N)Nbarr   )r8   r9   r:   r;   r<   r&   rS   rV   r`   staticmethodrf   r   r   r   r=   r>   r   r   r@   r@   e   s^    
GD:6(
  " "" pI pIf UVd%L*Br   r@   c                 4   Uc.  U R                  USUS9 Vs/ s H  oUR                  5       PM     nn[        R                  " U[        S9nU b  U R
                  nO[        U5      SU-  -  n[        [        R                  " U5      5      nUR                  5       nS[        R                  " [        R                  " XrU-  :  5      5      S   -   n	S/[        SU-  U	-  5      -   U/-   n
[        XSS 5       VVs/ s H  u  pX4PM
     nnnX4$ s  snf s  snnf )a  Detects scenes of a clip based on luminosity changes.

Note that for large clip this may take some time.

Returns
-------

tuple : cuts, luminosities
  cuts is a series of cuts [(0,t1), (t1,t2),...(...,tf)]
  luminosities are the luminosities computed for each
  frame of the clip.

Parameters
----------

clip : video.VideoClip.VideoClip, optional
  A video clip. Can be None if a list of luminosities is
  provided instead. If provided, the luminosity of each
  frame of the clip will be computed. If the clip has no
  'fps' attribute, you must provide it.

luminosities : list, optional
  A list of luminosities, e.g. returned by detect_scenes
  in a previous run.

luminosity_threshold : float, optional
  Determines a threshold above which the 'luminosity jumps'
  will be considered as scene changes. A scene change is defined
  as a change between 2 consecutive frames that is larger than
  (avg * thr) where avg is the average of the absolute changes
  between consecutive frames.

logger : str, optional
  Either ``"bar"`` for progress bar or ``None`` or any Proglog logger.

fps : int, optional
  Frames per second value. Must be provided if you provide
  no clip or a clip without fps attribute.
Nuint32)r   dtyper~   )r   r   r   r   )r   rk   r   r^   floatr   rO   r   diffmeannonzerorI   zip)r   luminositiesluminosity_thresholdr~   r   fr   luminosity_diffsavgluminosity_jumpsr   ru   rv   cutss                 r   detect_scenesr     s   V !--#Xf-U
UEEGU 	 
 88L6Lmm,39-277<01



!C	BHHRZZ 0#3M MNOPQRR  cD#)'7788C5@G#&w#<=#<RH#<D=!
 >s   D:D)Ng333333?)NN
   r   N)r<   collectionsr   numpyr   moviepy.decoratorsr   r   r   r   rI   r@   r   r>   r   r   <module>r      sh    E #  T |n-#% . #%L0
 0
ffBD fBR MQ; ;r   