Use threading to make thumbs in parallel.

Known bug: interrupt makes it go crazy.
patches
Joachim Tingvold 2014-03-16 15:17:13 +01:00
parent c9ce27eb87
commit dee0ab294f
1 changed files with 54 additions and 29 deletions

View File

@ -5,8 +5,16 @@ import os
import os.path import os.path
from PIL import Image from PIL import Image
from PIL.ExifTags import TAGS from PIL.ExifTags import TAGS
from multiprocessing import Pool
import gc import gc
def make_thumbs(self, original_path, thumb_path, size):
# The pool methods use a queue.Queue to pass tasks to the worker processes.
# Everything that goes through the queue.Queue must be pickable, and since
# self._thumbnail is not defined at the top level, it's not pickable.
# This is why we have this "dummy" function, so that it's pickable.
self._thumbnail(original_path, thumb_path, size[0], size[1])
class Album(object): class Album(object):
def __init__(self, path): def __init__(self, path):
self._path = trim_base(path) self._path = trim_base(path)
@ -120,7 +128,7 @@ class Photo(object):
return return
self._attributes = {} self._attributes = {}
self._attributes["dateTimeFile"] = mtime self._attributes["dateTimeFile"] = mtime
try: try:
image = Image.open(path) image = Image.open(path)
except KeyboardInterrupt: except KeyboardInterrupt:
@ -128,9 +136,9 @@ class Photo(object):
except: except:
self.is_valid = False self.is_valid = False
return return
self._metadata(image) self._metadata(path)
self._thumbnails(image, thumb_path, path) self._thumbnails(path, thumb_path)
def _metadata(self, image): def _metadata(self, path):
self._attributes["size"] = image.size self._attributes["size"] = image.size
self._orientation = 1 self._orientation = 1
try: try:
@ -225,6 +233,39 @@ class Photo(object):
_metadata.subject_distance_range_list = ["Unknown", "Macro", "Close view", "Distant view"] _metadata.subject_distance_range_list = ["Unknown", "Macro", "Close view", "Distant view"]
def _thumbnail(self, image, thumb_path, original_path, size, square=False): def _thumbnail(self, image, thumb_path, original_path, size, square=False):
def _thumbnail(self, original_path, thumb_path, size, square=False):
try:
image = Image.open(original_path)
except KeyboardInterrupt:
raise
except:
self.is_valid = False
return
mirror = image
if self._orientation == 2:
# Vertical Mirror
mirror = image.transpose(Image.FLIP_LEFT_RIGHT)
elif self._orientation == 3:
# Rotation 180
mirror = image.transpose(Image.ROTATE_180)
elif self._orientation == 4:
# Horizontal Mirror
mirror = image.transpose(Image.FLIP_TOP_BOTTOM)
elif self._orientation == 5:
# Horizontal Mirror + Rotation 270
mirror = image.transpose(Image.FLIP_TOP_BOTTOM).transpose(Image.ROTATE_270)
elif self._orientation == 6:
# Rotation 270
mirror = image.transpose(Image.ROTATE_270)
elif self._orientation == 7:
# Vertical Mirror + Rotation 270
mirror = image.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270)
elif self._orientation == 8:
# Rotation 90
mirror = image.transpose(Image.ROTATE_90)
image = mirror
thumb_path = os.path.join(thumb_path, image_cache(self._path, size, square)) thumb_path = os.path.join(thumb_path, image_cache(self._path, size, square))
info_string = "%s -> %spx" % (os.path.basename(original_path), str(size)) info_string = "%s -> %spx" % (os.path.basename(original_path), str(size))
if square: if square:
@ -268,31 +309,15 @@ class Photo(object):
message("save failure", os.path.basename(thumb_path)) message("save failure", os.path.basename(thumb_path))
os.unlink(thumb_path) os.unlink(thumb_path)
def _thumbnails(self, image, thumb_path, original_path): def _thumbnails(self, original_path, thumb_path):
mirror = image # get number of cores on the system, and use all minus one
if self._orientation == 2: num_of_cores = os.sysconf('SC_NPROCESSORS_ONLN') - 1
# Vertical Mirror pool = Pool(processes=num_of_cores)
mirror = image.transpose(Image.FLIP_LEFT_RIGHT) for size in Photo.thumb_sizes:
elif self._orientation == 3: pool.apply_async(make_thumbs, args = (self, original_path, thumb_path, size))
# Rotation 180 pool.close()
mirror = image.transpose(Image.ROTATE_180) pool.join()
elif self._orientation == 4:
# Horizontal Mirror
mirror = image.transpose(Image.FLIP_TOP_BOTTOM)
elif self._orientation == 5:
# Horizontal Mirror + Rotation 270
mirror = image.transpose(Image.FLIP_TOP_BOTTOM).transpose(Image.ROTATE_270)
elif self._orientation == 6:
# Rotation 270
mirror = image.transpose(Image.ROTATE_270)
elif self._orientation == 7:
# Vertical Mirror + Rotation 270
mirror = image.transpose(Image.FLIP_LEFT_RIGHT).transpose(Image.ROTATE_270)
elif self._orientation == 8:
# Rotation 90
mirror = image.transpose(Image.ROTATE_90)
for size in Photo.thumb_sizes:
self._thumbnail(mirror, thumb_path, original_path, size[0], size[1])
@property @property
def name(self): def name(self):
return os.path.basename(self._path) return os.path.basename(self._path)