2014-05-07 22:42:30 +00:00
#import numpy.core.multiarray # important this comes before cv!
import cv2
2013-02-19 00:11:43 +00:00
from flvlib import tags as flv_tags
2013-07-10 20:25:57 +00:00
import HydrusConstants as HC
2015-06-17 20:01:41 +00:00
import HydrusData
2014-05-28 21:03:24 +00:00
import HydrusExceptions
import HydrusImageHandling
import HydrusThreading
2014-04-30 21:31:40 +00:00
import matroska
2014-06-18 21:53:48 +00:00
import numpy
2013-07-10 20:25:57 +00:00
import os
2014-06-18 21:53:48 +00:00
import re
import subprocess
2015-09-23 21:21:02 +00:00
import sys
import tempfile
2013-02-19 00:11:43 +00:00
import traceback
2014-05-28 21:03:24 +00:00
import threading
import time
2013-02-19 00:11:43 +00:00
2015-11-04 22:30:28 +00:00
if HC . PLATFORM_LINUX : FFMPEG_PATH = os . path . join ( HC . BIN_DIR , ' ffmpeg ' )
elif HC . PLATFORM_OSX : FFMPEG_PATH = os . path . join ( HC . BIN_DIR , ' ffmpeg ' )
elif HC . PLATFORM_WINDOWS : FFMPEG_PATH = os . path . join ( HC . BIN_DIR , ' ffmpeg.exe ' )
2014-06-18 21:53:48 +00:00
2015-10-28 21:29:05 +00:00
if cv2 . __version__ . startswith ( ' 2 ' ) :
CAP_PROP_FRAME_COUNT = cv2 . cv . CV_CAP_PROP_FRAME_COUNT
CAP_PROP_FPS = cv2 . cv . CV_CAP_PROP_FPS
CAP_PROP_FRAME_WIDTH = cv2 . cv . CV_CAP_PROP_FRAME_WIDTH
CAP_PROP_FRAME_HEIGHT = cv2 . cv . CV_CAP_PROP_FRAME_HEIGHT
CAP_PROP_CONVERT_RGB = cv2 . cv . CV_CAP_PROP_CONVERT_RGB
CAP_PROP_POS_FRAMES = cv2 . cv . CV_CAP_PROP_POS_FRAMES
else :
CAP_PROP_FRAME_COUNT = cv2 . CAP_PROP_FRAME_COUNT
CAP_PROP_FPS = cv2 . CAP_PROP_FPS
CAP_PROP_FRAME_WIDTH = cv2 . CAP_PROP_FRAME_WIDTH
CAP_PROP_FRAME_HEIGHT = cv2 . CAP_PROP_FRAME_HEIGHT
CAP_PROP_CONVERT_RGB = cv2 . CAP_PROP_CONVERT_RGB
CAP_PROP_POS_FRAMES = cv2 . CAP_PROP_POS_FRAMES
2014-05-28 21:03:24 +00:00
def GetCVVideoProperties ( path ) :
capture = cv2 . VideoCapture ( path )
2015-10-28 21:29:05 +00:00
num_frames = int ( capture . get ( CAP_PROP_FRAME_COUNT ) )
2014-05-28 21:03:24 +00:00
2015-10-28 21:29:05 +00:00
fps = capture . get ( CAP_PROP_FPS )
2014-05-28 21:03:24 +00:00
length_in_seconds = num_frames / fps
length_in_ms = int ( length_in_seconds * 1000 )
duration = length_in_ms
2015-10-28 21:29:05 +00:00
width = int ( capture . get ( CAP_PROP_FRAME_WIDTH ) )
2014-05-28 21:03:24 +00:00
2015-10-28 21:29:05 +00:00
height = int ( capture . get ( CAP_PROP_FRAME_HEIGHT ) )
2014-05-28 21:03:24 +00:00
return ( ( width , height ) , duration , num_frames )
2014-06-25 20:37:06 +00:00
def GetFFMPEGVideoProperties ( path ) :
info = Hydrusffmpeg_parse_infos ( path )
( w , h ) = info [ ' video_size ' ]
duration_in_s = info [ ' duration ' ]
duration = int ( duration_in_s * 1000 )
num_frames = info [ ' video_nframes ' ]
return ( ( w , h ) , duration , num_frames )
2013-08-07 22:25:18 +00:00
def GetFLVProperties ( path ) :
2013-02-19 00:11:43 +00:00
2013-08-14 20:21:49 +00:00
with open ( path , ' rb ' ) as f :
2013-02-19 00:11:43 +00:00
2013-08-07 22:25:18 +00:00
flv = flv_tags . FLV ( f )
script_tag = None
for tag in flv . iter_tags ( ) :
2013-02-19 00:11:43 +00:00
2013-08-07 22:25:18 +00:00
if isinstance ( tag , flv_tags . ScriptTag ) :
script_tag = tag
break
2013-02-19 00:11:43 +00:00
2013-08-07 22:25:18 +00:00
width = 853
height = 480
duration = 0
num_frames = 0
2013-02-19 00:11:43 +00:00
2013-08-07 22:25:18 +00:00
if script_tag is not None :
tag_dict = script_tag . variable
# tag_dict can sometimes be a float?
# it is on the broken one I tried!
if ' width ' in tag_dict : width = tag_dict [ ' width ' ]
if ' height ' in tag_dict : height = tag_dict [ ' height ' ]
if ' duration ' in tag_dict : duration = int ( tag_dict [ ' duration ' ] * 1000 )
if ' framerate ' in tag_dict : num_frames = int ( ( duration / 1000.0 ) * tag_dict [ ' framerate ' ] )
2013-02-19 00:11:43 +00:00
2013-08-07 22:25:18 +00:00
return ( ( width , height ) , duration , num_frames )
2013-02-19 00:11:43 +00:00
2013-07-10 20:25:57 +00:00
2014-05-28 21:03:24 +00:00
def GetVideoFrameDuration ( path ) :
cv_video = cv2 . VideoCapture ( path )
2013-08-14 20:21:49 +00:00
2015-10-28 21:29:05 +00:00
fps = cv_video . get ( CAP_PROP_FPS )
2013-08-07 22:25:18 +00:00
2015-04-29 19:20:35 +00:00
if fps == 0 : raise HydrusExceptions . CantRenderWithCVException ( )
2015-04-25 22:31:50 +00:00
2014-05-28 21:03:24 +00:00
return 1000.0 / fps
2014-04-30 21:31:40 +00:00
def GetMatroskaOrWebm ( path ) :
tags = matroska . parse ( path )
ebml = tags [ ' EBML ' ] [ 0 ]
if ebml [ ' DocType ' ] [ 0 ] == ' matroska ' : return HC . VIDEO_MKV
elif ebml [ ' DocType ' ] [ 0 ] == ' webm ' : return HC . VIDEO_WEBM
raise Exception ( )
def GetMatroskaOrWebMProperties ( path ) :
tags = matroska . parse ( path )
segment = tags [ ' Segment ' ] [ 0 ]
info = segment [ ' Info ' ] [ 0 ]
duration = int ( ( info [ ' Duration ' ] [ 0 ] * info [ ' TimecodeScale ' ] [ 0 ] / 1e9 ) * 1000 )
tracks = segment [ ' Tracks ' ] [ 0 ]
trackentries = tracks [ ' TrackEntry ' ]
for trackentry in trackentries :
if ' Video ' in trackentry :
video = trackentry [ ' Video ' ] [ 0 ]
width = video [ ' PixelWidth ' ] [ 0 ]
height = video [ ' PixelHeight ' ] [ 0 ]
break
num_frames = 0
return ( ( width , height ) , duration , num_frames )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
# this is cribbed from moviepy
2014-06-18 21:53:48 +00:00
def Hydrusffmpeg_parse_infos ( filename , print_infos = False ) :
""" Get file infos using ffmpeg.
Returns a dictionnary with the fields :
" video_found " , " video_fps " , " duration " , " video_nframes " ,
" video_duration "
" audio_found " , " audio_fps "
" video_duration " is slightly smaller than " duration " to avoid
fetching the uncomplete frames at the end , which raises an error .
"""
# open the file in a pipe, provoke an error, read output
2015-09-23 21:21:02 +00:00
cmd = [ FFMPEG_PATH , " -i " , filename ]
2014-06-18 21:53:48 +00:00
is_GIF = filename . endswith ( ' .gif ' )
if is_GIF :
if HC . PLATFORM_WINDOWS : cmd + = [ " -f " , " null " , " NUL " ]
else : cmd + = [ " -f " , " null " , " /dev/null " ]
2015-06-17 20:01:41 +00:00
proc = subprocess . Popen ( cmd , bufsize = 10 * * 5 , stdout = subprocess . PIPE , stderr = subprocess . PIPE , startupinfo = HydrusData . GetSubprocessStartupInfo ( ) )
2014-06-18 21:53:48 +00:00
infos = proc . stderr . read ( ) . decode ( ' utf8 ' )
proc . terminate ( )
del proc
if print_infos :
# print the whole info text returned by FFMPEG
print ( infos )
lines = infos . splitlines ( )
if " No such file or directory " in lines [ - 1 ] :
raise IOError ( " %s not found ! Wrong path ? " % filename )
result = dict ( )
# get duration (in seconds)
2014-06-25 20:37:06 +00:00
# Duration: 00:00:02.46, start: 0.033000, bitrate: 1069 kb/s
2014-06-18 21:53:48 +00:00
try :
keyword = ( ' frame= ' if is_GIF else ' Duration: ' )
line = [ l for l in lines if keyword in l ] [ 0 ]
2014-06-25 20:37:06 +00:00
if ' start: ' in line :
m = re . search ( ' (start \\ : ) ' + ' [0-9] \\ .[0-9]* ' , line )
start_offset = float ( line [ m . start ( ) + 7 : m . end ( ) ] )
else : start_offset = 0
2014-06-18 21:53:48 +00:00
match = re . search ( " [0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9] " , line )
hms = map ( float , line [ match . start ( ) + 1 : match . end ( ) ] . split ( ' : ' ) )
if len ( hms ) == 1 :
result [ ' duration ' ] = hms [ 0 ]
elif len ( hms ) == 2 :
result [ ' duration ' ] = 60 * hms [ 0 ] + hms [ 1 ]
elif len ( hms ) == 3 :
result [ ' duration ' ] = 3600 * hms [ 0 ] + 60 * hms [ 1 ] + hms [ 2 ]
2014-06-25 20:37:06 +00:00
result [ ' duration ' ] - = start_offset
2014-06-18 21:53:48 +00:00
except :
raise IOError ( " Error reading duration in file %s , " % ( filename ) +
" Text parsed: %s " % infos )
# get the output line that speaks about video
lines_video = [ l for l in lines if ' Video: ' in l ]
result [ ' video_found ' ] = ( lines_video != [ ] )
if result [ ' video_found ' ] :
line = lines_video [ 0 ]
# get the size, of the form 460x320 (w x h)
match = re . search ( " [0-9]*x[0-9]*(,| ) " , line )
s = list ( map ( int , line [ match . start ( ) : match . end ( ) - 1 ] . split ( ' x ' ) ) )
result [ ' video_size ' ] = s
# get the frame rate
try :
match = re . search ( " ( [0-9]*.| )[0-9]* tbr " , line )
result [ ' video_fps ' ] = float ( line [ match . start ( ) : match . end ( ) ] . split ( ' ' ) [ 1 ] )
except :
match = re . search ( " ( [0-9]*.| )[0-9]* fps " , line )
result [ ' video_fps ' ] = float ( line [ match . start ( ) : match . end ( ) ] . split ( ' ' ) [ 1 ] )
2014-06-25 20:37:06 +00:00
num_frames = result [ ' duration ' ] * result [ ' video_fps ' ]
if num_frames != int ( num_frames ) : num_frames + = 1 # rounding up
result [ ' video_nframes ' ] = int ( num_frames )
2014-06-18 21:53:48 +00:00
result [ ' video_duration ' ] = result [ ' duration ' ]
# We could have also recomputed the duration from the number
# of frames, as follows:
# >>> result['video_duration'] = result['video_nframes'] / result['video_fps']
lines_audio = [ l for l in lines if ' Audio: ' in l ]
result [ ' audio_found ' ] = lines_audio != [ ]
if result [ ' audio_found ' ] :
line = lines_audio [ 0 ]
try :
match = re . search ( " [0-9]* Hz " , line )
result [ ' audio_fps ' ] = int ( line [ match . start ( ) + 1 : match . end ( ) ] )
except :
result [ ' audio_fps ' ] = ' unknown '
return result
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
# This was built from moviepy's FFMPEG_VideoReader
class VideoRendererFFMPEG ( object ) :
def __init__ ( self , path , mime , duration , num_frames , target_resolution , pix_fmt = " rgb24 " ) :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . _path = path
self . _mime = mime
self . _duration = float ( duration ) / 1000.0
self . _num_frames = num_frames
2014-05-28 21:03:24 +00:00
self . _target_resolution = target_resolution
2015-03-04 22:44:32 +00:00
self . lastread = None
2014-06-25 20:37:06 +00:00
self . fps = float ( self . _num_frames ) / self . _duration
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
if self . fps == 0 : self . fps = 24
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . pix_fmt = pix_fmt
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
if pix_fmt == ' rgba ' : self . depth = 4
else : self . depth = 3
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
( x , y ) = self . _target_resolution
bufsize = self . depth * x * y
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . process = None
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . bufsize = bufsize
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . initialize ( )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def __del__ ( self ) :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . close ( )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def close ( self ) :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
if self . process is not None :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . process . terminate ( )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . process . stdout . close ( )
self . process . stderr . close ( )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . process = None
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def initialize ( self , start_index = 0 ) :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . close ( )
if self . _mime == HC . IMAGE_GIF :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
ss = 0
self . pos = 0
skip_frames = start_index
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
else :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
ss = float ( start_index ) / self . fps
self . pos = start_index
skip_frames = 0
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
( w , h ) = self . _target_resolution
2014-05-28 21:03:24 +00:00
2015-06-17 20:01:41 +00:00
cmd = [ FFMPEG_PATH ,
2014-06-25 20:37:06 +00:00
' -ss ' , " %.03f " % ss ,
2015-06-17 20:01:41 +00:00
' -i ' , self . _path ,
2014-06-25 20:37:06 +00:00
' -loglevel ' , ' quiet ' ,
' -f ' , ' image2pipe ' ,
" -pix_fmt " , self . pix_fmt ,
" -s " , str ( w ) + ' x ' + str ( h ) ,
2015-06-17 20:01:41 +00:00
' -vcodec ' , ' rawvideo ' , ' - ' ]
2014-05-28 21:03:24 +00:00
2015-06-17 20:01:41 +00:00
self . process = subprocess . Popen ( cmd , bufsize = self . bufsize , stdout = subprocess . PIPE , stderr = subprocess . PIPE , startupinfo = HydrusData . GetSubprocessStartupInfo ( ) )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . skip_frames ( skip_frames )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def skip_frames ( self , n ) :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
( w , h ) = self . _target_resolution
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
for i in range ( n ) :
2014-05-28 21:03:24 +00:00
2015-09-09 22:04:39 +00:00
if self . process is not None :
self . process . stdout . read ( self . depth * w * h )
self . process . stdout . flush ( )
2014-06-25 20:37:06 +00:00
self . pos + = 1
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def read_frame ( self ) :
if self . pos == self . _num_frames : self . initialize ( )
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
if self . process is None : result = self . lastread
else :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
( w , h ) = self . _target_resolution
nbytes = self . depth * w * h
s = self . process . stdout . read ( nbytes )
if len ( s ) != nbytes :
result = self . lastread
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . close ( )
2014-05-28 21:03:24 +00:00
else :
2014-06-25 20:37:06 +00:00
result = numpy . fromstring ( s , dtype = ' uint8 ' ) . reshape ( ( h , w , len ( s ) / / ( w * h ) ) )
self . lastread = result
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
self . pos + = 1
return result
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
def set_position ( self , pos ) :
2014-05-28 21:03:24 +00:00
2014-06-25 20:37:06 +00:00
rewind = pos < self . pos
jump_a_long_way_ahead = pos > self . pos + 60
if rewind or jump_a_long_way_ahead : self . initialize ( pos )
else : self . skip_frames ( pos - self . pos )
2014-05-28 21:03:24 +00:00
2014-06-18 21:53:48 +00:00
2015-08-05 18:42:35 +00:00
# the cv code was initially written by @fluffy_cub
2014-07-30 21:18:17 +00:00
class GIFRenderer ( object ) :
2014-06-18 21:53:48 +00:00
2014-07-23 21:21:37 +00:00
def __init__ ( self , path , num_frames , target_resolution ) :
2014-06-18 21:53:48 +00:00
2014-07-23 21:21:37 +00:00
self . _path = path
self . _num_frames = num_frames
2014-06-18 21:53:48 +00:00
self . _target_resolution = target_resolution
2015-11-04 22:30:28 +00:00
if cv2 . __version__ . startswith ( ' 2 ' ) :
2015-06-17 20:01:41 +00:00
self . _InitialisePIL ( )
else :
self . _InitialiseCV ( )
2014-06-18 21:53:48 +00:00
def _GetCurrentFrame ( self ) :
2014-07-30 21:18:17 +00:00
if self . _cv_mode :
2014-08-06 20:29:17 +00:00
( retval , numpy_image ) = self . _cv_video . read ( )
2014-07-30 21:18:17 +00:00
if not retval :
self . _next_render_index = ( self . _next_render_index + 1 ) % self . _num_frames
2015-11-04 22:30:28 +00:00
raise HydrusExceptions . CantRenderWithCVException ( ' CV could not render frame ' + str ( self . _next_render_index - 1 ) + ' . ' )
2014-07-30 21:18:17 +00:00
else :
if self . _pil_image . mode == ' P ' and ' transparency ' in self . _pil_image . info :
2015-07-29 19:11:35 +00:00
# The gif problems seem to be here.
# I think that while some transparent animated gifs expect their frames to be pasted over each other, the others expect them to be fresh every time.
# Determining which is which doesn't seem to be available in PIL, and PIL's internal calculations seem to not be 100% correct.
# Just letting PIL try to do it on its own with P rather than converting to RGBA sometimes produces artifacts
2014-07-30 21:18:17 +00:00
current_frame = self . _pil_image . convert ( ' RGBA ' )
if self . _pil_canvas is None : self . _pil_canvas = current_frame
else : self . _pil_canvas . paste ( current_frame , None , current_frame ) # use the rgba image as its own mask
else : self . _pil_canvas = self . _pil_image
2014-08-06 20:29:17 +00:00
numpy_image = HydrusImageHandling . GenerateNumPyImageFromPILImage ( self . _pil_canvas )
2014-07-30 21:18:17 +00:00
2014-06-18 21:53:48 +00:00
2014-07-23 21:21:37 +00:00
self . _next_render_index = ( self . _next_render_index + 1 ) % self . _num_frames
2014-06-18 21:53:48 +00:00
2014-07-23 21:21:37 +00:00
if self . _next_render_index == 0 :
2014-06-25 20:37:06 +00:00
2014-07-23 21:21:37 +00:00
self . _RewindGIF ( )
2014-07-30 21:18:17 +00:00
else :
2014-06-25 20:37:06 +00:00
2014-07-30 21:18:17 +00:00
if not self . _cv_mode :
self . _pil_image . seek ( self . _next_render_index )
2015-10-21 21:53:10 +00:00
if self . _pil_global_palette is not None and self . _pil_image . palette == self . _pil_global_palette : # for some reason, when pil falls back from local palette to global palette, a bunch of important variables reset!
2014-07-30 21:18:17 +00:00
2015-02-25 19:34:30 +00:00
self . _pil_image . palette . dirty = self . _pil_dirty
self . _pil_image . palette . mode = self . _pil_mode
self . _pil_image . palette . rawmode = self . _pil_rawmode
2014-07-30 21:18:17 +00:00
2014-06-25 20:37:06 +00:00
2014-08-06 20:29:17 +00:00
return numpy_image
2014-06-18 21:53:48 +00:00
2014-07-30 21:18:17 +00:00
def _InitialiseCV ( self ) :
2015-06-17 20:01:41 +00:00
self . _cv_mode = True
2014-07-30 21:18:17 +00:00
self . _cv_video = cv2 . VideoCapture ( self . _path )
2015-10-28 21:29:05 +00:00
self . _cv_video . set ( CAP_PROP_CONVERT_RGB , True )
2014-07-30 21:18:17 +00:00
self . _next_render_index = 0
self . _last_frame = None
def _InitialisePIL ( self ) :
2015-06-17 20:01:41 +00:00
self . _cv_mode = False
2014-07-30 21:18:17 +00:00
self . _pil_image = HydrusImageHandling . GeneratePILImage ( self . _path )
self . _pil_canvas = None
self . _pil_global_palette = self . _pil_image . palette
2015-10-21 21:53:10 +00:00
if self . _pil_global_palette is not None :
self . _pil_dirty = self . _pil_image . palette . dirty
self . _pil_mode = self . _pil_image . palette . mode
self . _pil_rawmode = self . _pil_image . palette . rawmode
2014-07-30 21:18:17 +00:00
self . _next_render_index = 0
self . _last_frame = None
# believe it or not, doing this actually fixed a couple of gifs!
self . _pil_image . seek ( 1 )
self . _pil_image . seek ( 0 )
2014-06-18 21:53:48 +00:00
def _RenderCurrentFrame ( self ) :
2014-07-30 21:18:17 +00:00
if self . _cv_mode :
try :
2014-08-06 20:29:17 +00:00
numpy_image = self . _GetCurrentFrame ( )
2014-07-30 21:18:17 +00:00
2014-08-06 20:29:17 +00:00
numpy_image = HydrusImageHandling . EfficientlyResizeNumpyImage ( numpy_image , self . _target_resolution )
2014-07-30 21:18:17 +00:00
2014-08-06 20:29:17 +00:00
numpy_image = cv2 . cvtColor ( numpy_image , cv2 . COLOR_BGR2RGB )
2014-07-30 21:18:17 +00:00
except HydrusExceptions . CantRenderWithCVException :
if self . _last_frame is None :
self . _InitialisePIL ( )
2015-04-29 19:20:35 +00:00
numpy_image = self . _RenderCurrentFrame ( )
2014-07-30 21:18:17 +00:00
2014-08-06 20:29:17 +00:00
else : numpy_image = self . _last_frame
2014-07-30 21:18:17 +00:00
else :
2014-06-18 21:53:48 +00:00
2014-08-06 20:29:17 +00:00
numpy_image = self . _GetCurrentFrame ( )
2014-06-18 21:53:48 +00:00
2014-08-06 20:29:17 +00:00
numpy_image = HydrusImageHandling . EfficientlyResizeNumpyImage ( numpy_image , self . _target_resolution )
2014-06-18 21:53:48 +00:00
2015-04-29 19:20:35 +00:00
self . _last_frame = numpy_image
2014-06-18 21:53:48 +00:00
2014-08-06 20:29:17 +00:00
return numpy_image
2014-06-18 21:53:48 +00:00
2014-06-25 20:37:06 +00:00
def _RewindGIF ( self ) :
2014-06-18 21:53:48 +00:00
2014-07-30 21:18:17 +00:00
if self . _cv_mode :
self . _cv_video . release ( )
self . _cv_video . open ( self . _path )
2015-10-28 21:29:05 +00:00
#self._cv_video.set( CAP_PROP_POS_FRAMES, 0.0 )
2014-07-30 21:18:17 +00:00
else :
self . _pil_image . seek ( 0 )
2014-06-18 21:53:48 +00:00
2015-04-29 19:20:35 +00:00
self . _next_render_index = 0
2014-06-18 21:53:48 +00:00
2014-07-23 21:21:37 +00:00
def read_frame ( self ) : return self . _RenderCurrentFrame ( )
2014-06-18 21:53:48 +00:00
2014-07-23 21:21:37 +00:00
def set_position ( self , index ) :
2014-06-18 21:53:48 +00:00
2014-07-23 21:21:37 +00:00
if index == self . _next_render_index : return
elif index < self . _next_render_index : self . _RewindGIF ( )
2014-06-18 21:53:48 +00:00
2014-07-23 21:21:37 +00:00
while self . _next_render_index < index : self . _GetCurrentFrame ( )
2014-06-18 21:53:48 +00:00
2015-10-28 21:29:05 +00:00
#self._cv_video.set( CV_CAP_PROP_POS_FRAMES, index )
2014-06-18 21:53:48 +00:00
2013-07-10 20:25:57 +00:00