hydrus/include/HydrusImageHandling.py

267 lines
6.0 KiB
Python
Executable File

import cStringIO
import HydrusConstants as HC
import HydrusExceptions
import HydrusThreading
import os
from PIL import _imaging
from PIL import Image as PILImage
import shutil
import struct
import threading
import time
import traceback
import HydrusData
import HydrusGlobals as HG
import HydrusPaths
import warnings
warnings.simplefilter( 'ignore', PILImage.DecompressionBombWarning )
def ConvertToPngIfBmp( path ):
with open( path, 'rb' ) as f:
header = f.read( 2 )
if header == 'BM':
( os_file_handle, temp_path ) = HydrusPaths.GetTempPath()
try:
with open( path, 'rb' ) as f_source:
with open( temp_path, 'wb' ) as f_dest:
HydrusPaths.CopyFileLikeToFileLike( f_source, f_dest )
pil_image = GeneratePILImage( temp_path )
pil_image.save( path, 'PNG' )
finally:
HydrusPaths.CleanUpTempPath( os_file_handle, temp_path )
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
def EfficientlyResizePILImage( pil_image, ( target_x, target_y ) ):
( im_x, im_y ) = pil_image.size
if target_x >= im_x and target_y >= im_y: return pil_image
#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 )
#
return pil_image.resize( ( target_x, target_y ), PILImage.ANTIALIAS )
def EfficientlyThumbnailPILImage( pil_image, ( target_x, target_y ) ):
( im_x, im_y ) = pil_image.size
#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 )
#
if im_x > target_x or im_y > target_y:
pil_image.thumbnail( ( target_x, target_y ), PILImage.ANTIALIAS )
def GeneratePILImage( path ):
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
if pil_image is None:
raise Exception( 'The file at ' + path + ' could not be rendered!' )
return pil_image
def GeneratePILImageFromNumpyImage( numpy_image ):
( h, w, depth ) = numpy_image.shape
if depth == 3:
format = 'RGB'
elif depth == 4:
format = 'RGBA'
pil_image = PILImage.frombytes( format, ( w, h ), numpy_image.data )
return pil_image
def GetGIFFrameDurations( path ):
pil_image = GeneratePILImage( path )
frame_durations = []
i = 0
while True:
try: pil_image.seek( i )
except: break
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.
else:
duration = pil_image.info[ 'duration' ]
# 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
frame_durations.append( duration )
i += 1
return frame_durations
def GetImageProperties( path ):
( ( width, height ), num_frames ) = GetResolutionAndNumFrames( path )
if num_frames > 1:
durations = GetGIFFrameDurations( path )
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
try:
pil_image.seek( 1 )
pil_image.seek( 0 )
num_frames = 1
while True:
try:
pil_image.seek( pil_image.tell() + 1 )
num_frames += 1
except: break
except:
num_frames = 1
return ( ( x, y ), num_frames )
def GetThumbnailResolution( ( im_x, im_y ), ( target_x, target_y ) ):
if target_x >= im_x and target_y >= im_y:
return ( im_x, im_y )
im_x = float( im_x )
im_y = float( im_y )
target_x = float( target_x )
target_y = float( target_y )
x_ratio = im_x / target_x
y_ratio = im_y / target_y
if x_ratio > y_ratio:
target_y = im_y / x_ratio
elif y_ratio > x_ratio:
target_x = im_x / y_ratio
target_x = int( target_x )
target_y = int( target_y )
return ( target_x, target_y )
def IsDecompressionBomb( path ):
warnings.simplefilter( 'error', PILImage.DecompressionBombWarning )
try:
GeneratePILImage( path )
except PILImage.DecompressionBombWarning:
return True
finally:
warnings.simplefilter( 'ignore', PILImage.DecompressionBombWarning )
return False