2015-11-18 22:44:07 +00:00
import numpy . core . multiarray # important this comes before cv!
import cv2
2019-01-09 22:59:03 +00:00
from . import ClientImageHandling
from . import HydrusData
from . import HydrusExceptions
from . import HydrusGlobals as HG
from . import HydrusImageHandling
2015-11-18 22:44:07 +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
def GetCVVideoProperties ( path ) :
capture = cv2 . VideoCapture ( path )
num_frames = int ( capture . get ( CAP_PROP_FRAME_COUNT ) )
fps = capture . get ( CAP_PROP_FPS )
length_in_seconds = num_frames / fps
length_in_ms = int ( length_in_seconds * 1000 )
duration = length_in_ms
width = int ( capture . get ( CAP_PROP_FRAME_WIDTH ) )
height = int ( capture . get ( CAP_PROP_FRAME_HEIGHT ) )
return ( ( width , height ) , duration , num_frames )
def GetVideoFrameDuration ( path ) :
cv_video = cv2 . VideoCapture ( path )
fps = cv_video . get ( CAP_PROP_FPS )
2015-11-25 22:00:57 +00:00
if fps in ( 0 , 1000 ) : raise HydrusExceptions . CantRenderWithCVException ( )
2015-11-18 22:44:07 +00:00
return 1000.0 / fps
# the cv code was initially written by @fluffy_cub
class GIFRenderer ( object ) :
def __init__ ( self , path , num_frames , target_resolution ) :
2018-10-03 21:00:15 +00:00
if HG . media_load_report_mode :
HydrusData . ShowText ( ' Loading GIF: ' + path )
2015-11-18 22:44:07 +00:00
self . _path = path
self . _num_frames = num_frames
self . _target_resolution = target_resolution
2017-12-06 22:06:56 +00:00
new_options = HG . client_controller . new_options
2016-05-04 21:50:55 +00:00
if new_options . GetBoolean ( ' disable_cv_for_gifs ' ) or cv2 . __version__ . startswith ( ' 2 ' ) :
2015-11-18 22:44:07 +00:00
self . _InitialisePIL ( )
else :
self . _InitialiseCV ( )
def _GetCurrentFrame ( self ) :
if self . _cv_mode :
( retval , numpy_image ) = self . _cv_video . read ( )
if not retval :
self . _next_render_index = ( self . _next_render_index + 1 ) % self . _num_frames
raise HydrusExceptions . CantRenderWithCVException ( ' CV could not render frame ' + str ( self . _next_render_index - 1 ) + ' . ' )
else :
2016-09-28 18:48:01 +00:00
current_frame = HydrusImageHandling . Dequantize ( self . _pil_image )
2016-06-29 19:55:46 +00:00
if current_frame . mode == ' RGBA ' :
2015-11-18 22:44:07 +00:00
2016-06-29 19:55:46 +00:00
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
2015-11-18 22:44:07 +00:00
2016-06-29 19:55:46 +00:00
elif current_frame . mode == ' RGB ' :
2015-11-18 22:44:07 +00:00
2016-06-29 19:55:46 +00:00
self . _pil_canvas = current_frame
2015-11-18 22:44:07 +00:00
numpy_image = ClientImageHandling . GenerateNumPyImageFromPILImage ( self . _pil_canvas )
self . _next_render_index = ( self . _next_render_index + 1 ) % self . _num_frames
if self . _next_render_index == 0 :
self . _RewindGIF ( )
else :
if not self . _cv_mode :
self . _pil_image . seek ( self . _next_render_index )
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!
self . _pil_image . palette . dirty = self . _pil_dirty
self . _pil_image . palette . mode = self . _pil_mode
self . _pil_image . palette . rawmode = self . _pil_rawmode
return numpy_image
def _InitialiseCV ( self ) :
2018-10-03 21:00:15 +00:00
if HG . media_load_report_mode :
HydrusData . ShowText ( ' Loading GIF with OpenCV ' )
2015-11-18 22:44:07 +00:00
self . _cv_mode = True
self . _cv_video = cv2 . VideoCapture ( self . _path )
self . _cv_video . set ( CAP_PROP_CONVERT_RGB , True )
self . _next_render_index = 0
self . _last_frame = None
def _InitialisePIL ( self ) :
2018-10-03 21:00:15 +00:00
if HG . media_load_report_mode :
HydrusData . ShowText ( ' Loading GIF with PIL ' )
2015-11-18 22:44:07 +00:00
self . _cv_mode = False
self . _pil_image = HydrusImageHandling . GeneratePILImage ( self . _path )
self . _pil_canvas = None
self . _pil_global_palette = self . _pil_image . palette
2016-07-06 21:13:15 +00:00
if self . _pil_global_palette is not None and False :
2015-11-18 22:44:07 +00:00
self . _pil_dirty = self . _pil_image . palette . dirty
self . _pil_mode = self . _pil_image . palette . mode
self . _pil_rawmode = self . _pil_image . palette . rawmode
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 )
def _RenderCurrentFrame ( self ) :
if self . _cv_mode :
try :
numpy_image = self . _GetCurrentFrame ( )
2019-03-27 22:01:02 +00:00
numpy_image = ClientImageHandling . ResizeNumpyImage ( numpy_image , self . _target_resolution )
2015-11-18 22:44:07 +00:00
numpy_image = cv2 . cvtColor ( numpy_image , cv2 . COLOR_BGR2RGB )
except HydrusExceptions . CantRenderWithCVException :
if self . _last_frame is None :
2018-10-03 21:00:15 +00:00
if HG . media_load_report_mode :
HydrusData . ShowText ( ' OpenCV Failed to render a frame ' )
2015-11-18 22:44:07 +00:00
self . _InitialisePIL ( )
numpy_image = self . _RenderCurrentFrame ( )
2016-07-06 21:13:15 +00:00
else :
numpy_image = self . _last_frame
2015-11-18 22:44:07 +00:00
else :
numpy_image = self . _GetCurrentFrame ( )
2019-03-27 22:01:02 +00:00
numpy_image = ClientImageHandling . ResizeNumpyImage ( numpy_image , self . _target_resolution )
2015-11-18 22:44:07 +00:00
self . _last_frame = numpy_image
return numpy_image
def _RewindGIF ( self ) :
if self . _cv_mode :
self . _cv_video . release ( )
self . _cv_video . open ( self . _path )
#self._cv_video.set( CAP_PROP_POS_FRAMES, 0.0 )
else :
self . _pil_image . seek ( 0 )
self . _next_render_index = 0
2016-07-06 21:13:15 +00:00
def read_frame ( self ) :
return self . _RenderCurrentFrame ( )
2015-11-18 22:44:07 +00:00
def set_position ( self , index ) :
if index == self . _next_render_index : return
elif index < self . _next_render_index : self . _RewindGIF ( )
while self . _next_render_index < index : self . _GetCurrentFrame ( )
#self._cv_video.set( CV_CAP_PROP_POS_FRAMES, index )
2017-05-10 21:33:58 +00:00
2019-03-06 23:06:22 +00:00
def Stop ( self ) :
pass