hydrus/include/HydrusImageHandling.py

192 lines
4.7 KiB
Python
Executable File

import cStringIO
import HydrusConstants as HC
import HydrusExceptions
import HydrusThreading
import lz4
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
import HydrusPaths
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 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 ):
pil_image = PILImage.open( path )
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 ) ):
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
ratio_to_use = max( x_ratio, y_ratio )
target_x = int( im_x / ratio_to_use )
target_y = int( im_y / ratio_to_use )
return ( target_x, target_y )