hydrus/include/HydrusImageHandling.py

279 lines
6.3 KiB
Python
Raw Normal View History

2013-02-19 00:11:43 +00:00
import cStringIO
import HydrusConstants as HC
2014-03-05 22:44:02 +00:00
import HydrusExceptions
2014-05-21 21:37:35 +00:00
import HydrusThreading
2013-08-07 22:25:18 +00:00
import os
2014-10-22 22:31:58 +00:00
from PIL import _imaging
2013-02-19 00:11:43 +00:00
from PIL import Image as PILImage
2013-08-07 22:25:18 +00:00
import shutil
2013-02-19 00:11:43 +00:00
import struct
import threading
import time
import traceback
2015-03-25 22:04:19 +00:00
import HydrusData
2017-05-10 21:33:58 +00:00
import HydrusGlobals as HG
2015-11-04 22:30:28 +00:00
import HydrusPaths
2017-10-04 17:51:58 +00:00
import warnings
2017-10-11 17:38:14 +00:00
if not hasattr( PILImage, 'DecompressionBombWarning' ):
# super old versions don't have this, so let's just make a stub, wew
class DBW_stub( Exception ):
pass
PILImage.DecompressionBombWarning = DBW_stub
2017-10-04 17:51:58 +00:00
warnings.simplefilter( 'ignore', PILImage.DecompressionBombWarning )
2013-02-19 00:11:43 +00:00
2013-08-07 22:25:18 +00:00
def ConvertToPngIfBmp( path ):
2013-02-19 00:11:43 +00:00
2017-07-19 21:21:41 +00:00
with open( path, 'rb' ) as f:
header = f.read( 2 )
2013-08-07 22:25:18 +00:00
if header == 'BM':
2013-02-19 00:11:43 +00:00
2015-11-04 22:30:28 +00:00
( os_file_handle, temp_path ) = HydrusPaths.GetTempPath()
2013-02-19 00:11:43 +00:00
2015-03-25 22:04:19 +00:00
try:
with open( path, 'rb' ) as f_source:
with open( temp_path, 'wb' ) as f_dest:
2015-11-04 22:30:28 +00:00
HydrusPaths.CopyFileLikeToFileLike( f_source, f_dest )
2015-03-25 22:04:19 +00:00
pil_image = GeneratePILImage( temp_path )
pil_image.save( path, 'PNG' )
finally:
2015-11-04 22:30:28 +00:00
HydrusPaths.CleanUpTempPath( os_file_handle, temp_path )
2015-03-25 22:04:19 +00:00
2013-02-19 00:11:43 +00:00
2016-06-29 19:55:46 +00:00
def Dequantize( pil_image ):
if pil_image.mode not in ( 'RGBA', 'RGB' ):
if pil_image.mode == 'LA' or ( pil_image.mode == 'P' and pil_image.info.has_key( 'transparency' ) ):
pil_image = pil_image.convert( 'RGBA' )
else:
pil_image = pil_image.convert( 'RGB' )
return pil_image
2014-05-21 21:37:35 +00:00
def EfficientlyResizePILImage( pil_image, ( target_x, target_y ) ):
2013-02-19 00:11:43 +00:00
( im_x, im_y ) = pil_image.size
2014-05-21 21:37:35 +00:00
if target_x >= im_x and target_y >= im_y: return pil_image
2013-02-19 00:11:43 +00:00
2014-05-21 21:37:35 +00:00
#if pil_image.mode == 'RGB': # low quality resize screws up alpha channel!
#
# if im_x > 2 * target_x and im_y > 2 * target_y: pil_image.thumbnail( ( 2 * target_x, 2 * target_y ), PILImage.NEAREST )
#
2013-02-19 00:11:43 +00:00
2014-05-21 21:37:35 +00:00
return pil_image.resize( ( target_x, target_y ), PILImage.ANTIALIAS )
2013-02-19 00:11:43 +00:00
2014-05-21 21:37:35 +00:00
def EfficientlyThumbnailPILImage( pil_image, ( target_x, target_y ) ):
2013-02-19 00:11:43 +00:00
2014-05-21 21:37:35 +00:00
( im_x, im_y ) = pil_image.size
2013-02-19 00:11:43 +00:00
2014-06-11 21:20:42 +00:00
#if pil_image.mode == 'RGB': # low quality resize screws up alpha channel!
#
# if im_x > 2 * target_x or im_y > 2 * target_y: pil_image.thumbnail( ( 2 * target_x, 2 * target_y ), PILImage.NEAREST )
#
2013-02-19 00:11:43 +00:00
2015-11-25 22:00:57 +00:00
if im_x > target_x or im_y > target_y:
pil_image.thumbnail( ( target_x, target_y ), PILImage.ANTIALIAS )
2013-02-19 00:11:43 +00:00
2015-10-21 21:53:10 +00:00
def GeneratePILImage( path ):
2016-02-03 22:12:53 +00:00
fp = open( path, 'rb' )
try:
pil_image = PILImage.open( fp )
except:
# pil doesn't clean up its open file on exception, jej
fp.close()
raise
2015-10-21 21:53:10 +00:00
if pil_image is None:
raise Exception( 'The file at ' + path + ' could not be rendered!' )
return pil_image
2014-06-25 20:37:06 +00:00
def GeneratePILImageFromNumpyImage( numpy_image ):
( h, w, depth ) = numpy_image.shape
2016-10-12 21:52:50 +00:00
if depth == 3:
format = 'RGB'
elif depth == 4:
format = 'RGBA'
2014-06-25 20:37:06 +00:00
2015-10-21 21:53:10 +00:00
pil_image = PILImage.frombytes( format, ( w, h ), numpy_image.data )
2014-06-25 20:37:06 +00:00
return pil_image
2014-05-28 21:03:24 +00:00
def GetGIFFrameDurations( path ):
2015-10-21 21:53:10 +00:00
pil_image = GeneratePILImage( path )
2014-05-07 22:42:30 +00:00
frame_durations = []
i = 0
while True:
2015-10-21 21:53:10 +00:00
try: pil_image.seek( i )
2014-05-07 22:42:30 +00:00
except: break
2015-10-21 21:53:10 +00:00
if 'duration' not in pil_image.info:
duration = 83 # Set a 12 fps default when duration is missing or too funky to extract. most stuff looks ok at this.
2014-05-07 22:42:30 +00:00
else:
2015-10-21 21:53:10 +00:00
duration = pil_image.info[ 'duration' ]
2014-05-07 22:42:30 +00:00
2015-10-21 21:53:10 +00:00
# In the gif frame header, 10 is stored as 1ms. This 1 is commonly as utterly wrong as 0.
if duration in ( 0, 10 ):
duration = 80
2014-05-07 22:42:30 +00:00
frame_durations.append( duration )
i += 1
return frame_durations
2014-05-14 20:46:38 +00:00
def GetImageProperties( path ):
( ( width, height ), num_frames ) = GetResolutionAndNumFrames( path )
if num_frames > 1:
2014-05-28 21:03:24 +00:00
durations = GetGIFFrameDurations( path )
2014-05-14 20:46:38 +00:00
duration = sum( durations )
else:
duration = None
num_frames = None
return ( ( width, height ), duration, num_frames )
def GetResolutionAndNumFrames( path ):
pil_image = GeneratePILImage( path )
( x, y ) = pil_image.size
2013-02-19 00:11:43 +00:00
try:
2014-05-14 20:46:38 +00:00
pil_image.seek( 1 )
pil_image.seek( 0 )
2013-02-19 00:11:43 +00:00
2014-05-14 20:46:38 +00:00
num_frames = 1
2013-02-19 00:11:43 +00:00
2014-05-14 20:46:38 +00:00
while True:
try:
pil_image.seek( pil_image.tell() + 1 )
num_frames += 1
except: break
2013-02-19 00:11:43 +00:00
2017-08-23 21:34:25 +00:00
except:
num_frames = 1
2014-05-14 20:46:38 +00:00
return ( ( x, y ), num_frames )
2014-05-21 21:37:35 +00:00
def GetThumbnailResolution( ( im_x, im_y ), ( target_x, target_y ) ):
2014-05-14 20:46:38 +00:00
2016-06-01 20:04:15 +00:00
if target_x >= im_x and target_y >= im_y:
return ( im_x, im_y )
2014-05-21 21:37:35 +00:00
im_x = float( im_x )
im_y = float( im_y )
2014-05-14 20:46:38 +00:00
2014-05-21 21:37:35 +00:00
target_x = float( target_x )
target_y = float( target_y )
x_ratio = im_x / target_x
y_ratio = im_y / target_y
2016-06-01 20:04:15 +00:00
if x_ratio > y_ratio:
target_y = im_y / x_ratio
elif y_ratio > x_ratio:
target_x = im_x / y_ratio
2013-02-19 00:11:43 +00:00
2016-06-01 20:04:15 +00:00
target_x = int( target_x )
target_y = int( target_y )
2014-05-14 20:46:38 +00:00
2014-05-21 21:37:35 +00:00
return ( target_x, target_y )
2017-01-25 22:56:55 +00:00
2017-10-04 17:51:58 +00:00
def IsDecompressionBomb( path ):
warnings.simplefilter( 'error', PILImage.DecompressionBombWarning )
try:
GeneratePILImage( path )
except PILImage.DecompressionBombWarning:
return True
finally:
warnings.simplefilter( 'ignore', PILImage.DecompressionBombWarning )
return False