U
    a+d*                     @   sb   d Z ddlmZ ddlZddlmZ edddZG dd	 d	ZG d
d de	Z
edddZdS )zN This module contains everything that can help automatize
the cuts in MoviePy     )defaultdictN)use_clip_fps_by_default333333?c                    sR    fddt | jd| dd }dfdd|D }|t | S )	z9 Finds the period of a video based on frames correlation c                    s     |  S N)Z	get_frameflatten)t)clip </tmp/pip-unpacked-wheel-0yp4gafk/moviepy/video/tools/cuts.py<lambda>       z#find_video_period.<locals>.<lambda>      ?   Nr   c                    s    g | ]}t  |d  qS ))r   r   )npZcorrcoef).0r   )framerefr	   r
   
<listcomp>   s     z%find_video_period.<locals>.<listcomp>)r   ZarangedurationZargmax)r   fpsZtminttZcorrsr	   )r   r   r   r
   find_video_period   s
    r   c                   @   s0   e Zd ZdZdd Zdd Zdd Zdd	 Zd
S )FramesMatchz
    
    Parameters
    -----------

    t1
      Starting time

    t2
      End time

    d_min
      Lower bound on the distance between the first and last frames

    d_max
      Upper bound on the distance between the first and last frames

    c                 C   s&   || _ || _|| _|| _|| | _d S r   )t1t2d_mind_maxZ	time_span)selfr   r   r   r   r	   r	   r
   __init__*   s
    zFramesMatch.__init__c                 C   s   d| j | j| j| jf S Nz(%.04f, %.04f, %.04f, %.04f)r   r   r   r   r   r	   r	   r
   __str__1   s       zFramesMatch.__str__c                 C   s   d| j | j| j| jf S r   r    r!   r	   r	   r
   __repr__6   s       zFramesMatch.__repr__c                 C   s   t | j| j| j| jfS r   )iterr   r   r   r   r!   r	   r	   r
   __iter__:   s    zFramesMatch.__iter__N)__name__
__module____qualname____doc__r   r"   r#   r%   r	   r	   r	   r
   r      s
   r   c                   @   sZ   e Zd Zdd ZdddZdd Zd	d
 Zedd ZedddZ	dddZ
dd ZdS )FramesMatchesc                 C   s   t | t|dd d d S )Nc                 S   s   | j S r   )r   )er	   r	   r
   r   B   r   z(FramesMatches.__init__.<locals>.<lambda>key)listr   sorted)r   lstr	   r	   r
   r   @   s    zFramesMatches.__init__r   Nc                 C   s8   |d k	rt | | d }|dkr(| d S t| d | S )Nd   r   r   )lenr*   )r   npercentr	   r	   r
   bestD   s    zFramesMatches.bestc                 C   s   t t|| S )a  
        Returns a FramesMatches object obtained by filtering out the FramesMatch
        which do not satistify the condition ``cond``. ``cond`` is a function
        (FrameMatch -> bool).

        Examples
        ---------
        >>> # Only keep the matches corresponding to (> 1 second) sequences.
        >>> new_matches = matches.filter( lambda match: match.time_span > 1)
        )r*   filter)r   Zcondr	   r	   r
   r6   I   s    zFramesMatches.filterc                 C   s&   t j|t dd | D ddd d S )Nc                 S   s   g | ]}t t|qS r	   )r   arrayr.   r   r+   r	   r	   r
   r   W   s     z&FramesMatches.save.<locals>.<listcomp>z%.03f	)fmt	delimiter)r   Zsavetxtr7   )r   filenamer	   r	   r
   saveV   s     zFramesMatches.savec                 C   s    t | }dd |D }t|S )zp Loads a FramesMatches object from a file.
        >>> matching_frames = FramesMatches.load("somefile")
        c                 S   s   g | ]}t | qS r	   r   r8   r	   r	   r
   r   `   s     z&FramesMatches.load.<locals>.<listcomp>)r   Zloadtxtr*   )r<   ZarrZmfsr	   r	   r
   loadZ   s    
zFramesMatches.loadc                    s  | j | j d fddi   fdd}g }| jdddD ]\}d	|  }||}t|}	t  D ]j}
|
 |kr |
 qvt	 |
 d
 |	  |
 d
 |	 d |
 <  |
  d |k |
  d< qvt
  }|||	d < t|D ]\}}
 |
  d r$q||
}|  |
  d<  |
  d< ||k |
  d< ||d d D ]t} |   |
 |  }}t|d ||d  |d< t|d ||d  |d | |d< |d |krrd|d< qrq| fdd D 7 }q@tdd |D S )a   Finds all the frames tht look alike in a clip, for instance to make a
        looping gif.

        This teturns a  FramesMatches object of the all pairs of frames with
        (t2-t1 < max_d) and whose distance is under dist_thr.

        This is well optimized routine and quite fast.

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

        >>> from moviepy.editor import VideoFileClip
        >>> from moviepy.video.tools.cuts import find_matching_frames
        >>> clip = VideoFileClip("foo.mp4").resize(width=200)
        >>> matches = find_matching_frames(clip, 10, 3) # will take time
        >>> best = matches.filter(lambda m: m.time_span > 1.5).best()
        >>> clip.subclip(best.t1, best.t2).write_gif("foo.gif")

        Parameters
        -----------

        clip
          A MoviePy video clip, possibly transformed/resized
        
        dist_thr
          Distance above which a match is rejected
        
        max_d
          Maximal duration (in seconds) between two matching frames
        
        fps
          Frames per second (default will be clip.fps)
        
           c                    s   | |     S r   sum)ZF1ZF2)N_pixelsr	   r
   r      r   z)FramesMatches.from_clip.<locals>.<lambda>c                    sJ    |  d  | d } |  d  | d  }}t || d|  S )Nr   |F|sq   )r   sqrt)r   r   Zuvuv)Fdot_productr	   r
   distance   s    z)FramesMatches.from_clip.<locals>.distanceTbar)Z
with_timesloggerr   |F|)minmaxrO   rejected)r   rD   rN   rP   r   Nc                    sH   g | ]@}|kr |  d  s| |  d  |  d fqS )rQ   rO   rP   r	   )r   r   )rI   r   r	   r
   r      s     z+FramesMatches.from_clip.<locals>.<listcomp>c                 S   s   g | ]}t | qS r	   r>   r8   r	   r	   r
   r      s     )whiter_framesr   r   rF   r.   keyspopabsr/   	enumeraterO   rP   r*   )r   Zdist_thrZmax_dr   rK   Zmatching_framesr   Z
flat_frameZ	F_norm_sqZF_normr   Zt_Fidistt3Zt3tZt2t3r	   )rI   rC   rJ   r   r
   	from_clipe   sD    (

"
 
zFramesMatches.from_clipr   c                    s.  dkrt dd }| D ] \ }}|  ||g qt| dd d}g }	d}
|D ]\}|
k rpq^dd |D }fd	d|D }fd
d|D }|sq^fdd|D }fdd|D }||sq^tdd |D  t fdd|D \ }}|	t || | }
q^t|	S )a!  

        match_thr
          The smaller, the better-looping the gifs are.

        min_time_span
          Only GIFs with a duration longer than min_time_span (in seconds)
          will be extracted.

        nomatch_thr
          If None, then it is chosen equal to match_thr

        Nc                   S   s   g S r   r	   r	   r	   r	   r
   r      r   z-FramesMatches.select_scenes.<locals>.<lambda>c                 S   s   | d S )Nr   r	   )kr	   r	   r
   r      r   r,   r   c                 S   s   g | ]\}}}|qS r	   r	   r   endr   r   r	   r	   r
   r      s     z/FramesMatches.select_scenes.<locals>.<listcomp>c                    s$   g | ]\}}}| k r|||fqS r	   r	   r^   )	match_thrr	   r
   r      s   c                    s(   g | ] \}}}|  kr|||fqS r	   r	   r^   )min_time_spanstartr	   r
   r      s   c                    s   h | ]\}}}| kr|qS r	   r	   r^   )nomatch_thrr	   r
   	<setcomp>   s      z.FramesMatches.select_scenes.<locals>.<setcomp>c                    s   h | ]}|  d kr|qS )g333333?r	   )r   r_   )rb   r	   r
   rd      s      c                 s   s   | ]\}}}|V  qd S r   r	   r^   r	   r	   r
   	<genexpr>   s     z.FramesMatches.select_scenes.<locals>.<genexpr>c                 3   s   | ]}|d   kr|V  qdS )r   Nr	   r8   )r_   r	   r
   re      s      )	r   appendr/   itemsintersectionrP   nextr   r*   )r   r`   ra   rc   Ztime_distanceZdict_startsr   r   Zstarts_endsresultZ	min_startZends_distancesZendsZgreat_matchesZgreat_long_matchesZpoor_matchesZshort_matchesr	   )r_   r`   ra   rc   rb   r
   select_scenes   s:    


zFramesMatches.select_scenesc                 C   sB   | D ]8\}}}}d|d| d| f }| ||j|dd qd S )Nz%s/%08d_%08d.gifr1   F)verbose)ZsubclipZ	write_gif)r   r   Zgif_dirrb   r_   _namer	   r	   r
   
write_gifs  s    zFramesMatches.write_gifs)r   N)N)Nr   )r&   r'   r(   r   r5   r6   r=   staticmethodr?   r\   rk   ro   r	   r	   r	   r
   r*   >   s   


d  
<r*   
   rL   c                 C   s   |dkr"dd | j |d|dD }tj|td}| dk	r@| j}nt|d|  }tt|}| }dtt	||| kd	  }d	gt
d| |  |g }	d
d t|	|	dd D }
|
|fS )a   Detects scenes of a clip based on luminosity changes.
    
    Note that for large clip this may take some time
    
    Returns
    --------
    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
      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
      A list of luminosities, e.g. returned by detect_scenes
      in a previous run.
    
    thr
      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.
      
    progress_bar
      We all love progress bars ! Here is one for you, in option.
      
    fps
      Must be provided if you provide no clip or a clip without
      fps attribute.
    

    Nc                 S   s   g | ]}|  qS r	   rA   )r   fr	   r	   r
   r   :  s     z!detect_scenes.<locals>.<listcomp>Zuint32)r   dtyperM   )rs   r   r   r   c                 S   s   g | ]\}}||fqS r	   r	   )r   r   r   r	   r	   r
   r   G  s     )rT   r   r7   floatr   r2   rW   ZdiffZmeanZnonzeror.   zip)r   ZluminositiesZthrrM   r   r_   Z	lum_diffsavgZluminosity_jumpsr   Zcutsr	   r	   r
   detect_scenes  s     +
  
 rw   )Nr   )NNrq   rL   N)r)   collectionsr   Znumpyr   Zmoviepy.decoratorsr   r   r   r.   r*   rw   r	   r	   r	   r
   <module>   s   
( Q    