1
0
mirror of https://github.com/ciromattia/kcc synced 2025-12-13 09:46:25 +00:00

Comic2Panel: Refactored multiprocessing support

This commit is contained in:
Paweł Jastrzębski
2013-10-18 10:37:35 +02:00
parent 5e8bd52433
commit eedf537902

View File

@@ -27,7 +27,7 @@ import os
import sys import sys
from shutil import rmtree, copytree, move from shutil import rmtree, copytree, move
from optparse import OptionParser, OptionGroup from optparse import OptionParser, OptionGroup
from multiprocessing import Pool, Queue, freeze_support from multiprocessing import Pool, freeze_support
try: try:
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
from PIL import Image, ImageStat from PIL import Image, ImageStat
@@ -83,113 +83,120 @@ def sanitizePanelSize(panel, opt):
return newPanels return newPanels
def splitImage_init(queue, opt): def splitImage_tick(output):
splitImage.queue = queue if output:
splitImage.options = opt splitWorkerOutput.append(output)
splitWorkerPool.terminate()
if GUI:
GUI.emit(QtCore.SIGNAL("progressBarTick"))
if not GUI.conversionAlive:
splitWorkerPool.terminate()
# noinspection PyUnresolvedReferences
def splitImage(work): def splitImage(work):
path = work[0] #noinspection PyBroadException
name = work[1]
opt = splitImage.options
# Harcoded opttions
threshold = 1.0
delta = 15
print ".",
splitImage.queue.put(".")
fileExpanded = os.path.splitext(name)
filePath = os.path.join(path, name)
# Detect corrupted files
try:
Image.open(filePath)
except IOError:
raise RuntimeError('Cannot read image file %s' % filePath)
try: try:
path = work[0]
name = work[1]
opt = work[2]
# Harcoded opttions
threshold = 1.0
delta = 15
print ".",
fileExpanded = os.path.splitext(name)
filePath = os.path.join(path, name)
# Detect corrupted files
try:
Image.open(filePath)
except IOError:
raise RuntimeError('Cannot read image file %s' % filePath)
try:
image = Image.open(filePath)
image.verify()
except:
raise RuntimeError('Image file %s is corrupted' % filePath)
try:
image = Image.open(filePath)
image.load()
except:
raise RuntimeError('Image file %s is corrupted' % filePath)
image = Image.open(filePath) image = Image.open(filePath)
image.verify() image = image.convert('RGB')
except: widthImg, heightImg = image.size
raise RuntimeError('Image file %s is corrupted' % filePath) if heightImg > opt.height:
try:
image = Image.open(filePath)
image.load()
except:
raise RuntimeError('Image file %s is corrupted' % filePath)
image = Image.open(filePath)
image = image.convert('RGB')
widthImg, heightImg = image.size
if heightImg > opt.height:
if opt.debug:
from PIL import ImageDraw
debugImage = Image.open(filePath)
draw = ImageDraw.Draw(debugImage)
# Find panels
y1 = 0
y2 = 15
panels = []
while y2 < heightImg:
while ImageStat.Stat(image.crop([0, y1, widthImg, y2])).var[0] < threshold and y2 < heightImg:
y2 += delta
y2 -= delta
y1Temp = y2
y1 = y2 + delta
y2 = y1 + delta
while ImageStat.Stat(image.crop([0, y1, widthImg, y2])).var[0] >= threshold and y2 < heightImg:
y1 += delta
y2 += delta
if y1 + delta >= heightImg:
y1 = heightImg - 1
y2Temp = y1
if opt.debug: if opt.debug:
draw.line([(0, y1Temp), (widthImg, y1Temp)], fill=(0, 255, 0)) from PIL import ImageDraw
draw.line([(0, y2Temp), (widthImg, y2Temp)], fill=(255, 0, 0)) debugImage = Image.open(filePath)
panelHeight = y2Temp - y1Temp draw = ImageDraw.Draw(debugImage)
if panelHeight > delta:
# Panels that can't be cut nicely will be forcefully splitted
panelsCleaned = sanitizePanelSize([y1Temp, y2Temp, panelHeight], opt)
for panel in panelsCleaned:
panels.append(panel)
if opt.debug:
# noinspection PyUnboundLocalVariable
debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG')
# Create virtual pages # Find panels
pages = [] y1 = 0
currentPage = [] y2 = 15
pageLeft = opt.height panels = []
panelNumber = 0 while y2 < heightImg:
for panel in panels: while ImageStat.Stat(image.crop([0, y1, widthImg, y2])).var[0] < threshold and y2 < heightImg:
if pageLeft - panel[2] > 0: y2 += delta
pageLeft -= panel[2] y2 -= delta
currentPage.append(panelNumber) y1Temp = y2
panelNumber += 1 y1 = y2 + delta
else: y2 = y1 + delta
if len(currentPage) > 0: while ImageStat.Stat(image.crop([0, y1, widthImg, y2])).var[0] >= threshold and y2 < heightImg:
pages.append(currentPage) y1 += delta
pageLeft = opt.height - panel[2] y2 += delta
currentPage = [panelNumber] if y1 + delta >= heightImg:
panelNumber += 1 y1 = heightImg - 1
if len(currentPage) > 0: y2Temp = y1
pages.append(currentPage) if opt.debug:
draw.line([(0, y1Temp), (widthImg, y1Temp)], fill=(0, 255, 0))
draw.line([(0, y2Temp), (widthImg, y2Temp)], fill=(255, 0, 0))
panelHeight = y2Temp - y1Temp
if panelHeight > delta:
# Panels that can't be cut nicely will be forcefully splitted
panelsCleaned = sanitizePanelSize([y1Temp, y2Temp, panelHeight], opt)
for panel in panelsCleaned:
panels.append(panel)
if opt.debug:
# noinspection PyUnboundLocalVariable
debugImage.save(os.path.join(path, fileExpanded[0] + '-debug.png'), 'PNG')
# Create pages # Create virtual pages
pageNumber = 1 pages = []
for page in pages: currentPage = []
pageHeight = 0 pageLeft = opt.height
targetHeight = 0 panelNumber = 0
for panel in page: for panel in panels:
pageHeight += panels[panel][2] if pageLeft - panel[2] > 0:
if pageHeight > delta: pageLeft -= panel[2]
newPage = Image.new('RGB', (widthImg, pageHeight)) currentPage.append(panelNumber)
panelNumber += 1
else:
if len(currentPage) > 0:
pages.append(currentPage)
pageLeft = opt.height - panel[2]
currentPage = [panelNumber]
panelNumber += 1
if len(currentPage) > 0:
pages.append(currentPage)
# Create pages
pageNumber = 1
for page in pages:
pageHeight = 0
targetHeight = 0
for panel in page: for panel in page:
panelImg = image.crop([0, panels[panel][0], widthImg, panels[panel][1]]) pageHeight += panels[panel][2]
newPage.paste(panelImg, (0, targetHeight)) if pageHeight > delta:
targetHeight += panels[panel][2] newPage = Image.new('RGB', (widthImg, pageHeight))
newPage.save(os.path.join(path, fileExpanded[0] + '-' + for panel in page:
str(pageNumber) + '.png'), 'PNG') panelImg = image.crop([0, panels[panel][0], widthImg, panels[panel][1]])
pageNumber += 1 newPage.paste(panelImg, (0, targetHeight))
os.remove(filePath) targetHeight += panels[panel][2]
newPage.save(os.path.join(path, fileExpanded[0] + '-' +
str(pageNumber) + '.png'), 'PNG')
pageNumber += 1
os.remove(filePath)
except:
return str(sys.exc_info()[1])
def Copyright(): def Copyright():
@@ -199,7 +206,7 @@ def Copyright():
# noinspection PyBroadException # noinspection PyBroadException
def main(argv=None, qtGUI=None): def main(argv=None, qtGUI=None):
global options global options, GUI, splitWorkerPool, splitWorkerOutput
parser = OptionParser(usage="Usage: %prog [options] comic_folder", add_help_option=False) parser = OptionParser(usage="Usage: %prog [options] comic_folder", add_help_option=False)
mainOptions = OptionGroup(parser, "MANDATORY") mainOptions = OptionGroup(parser, "MANDATORY")
otherOptions = OptionGroup(parser, "OTHER") otherOptions = OptionGroup(parser, "OTHER")
@@ -230,37 +237,28 @@ def main(argv=None, qtGUI=None):
copytree(options.sourceDir, options.targetDir) copytree(options.sourceDir, options.targetDir)
work = [] work = []
pagenumber = 0 pagenumber = 0
queue = Queue() splitWorkerOutput = []
pool = Pool(None, splitImage_init, [queue, options]) splitWorkerPool = Pool()
for root, dirs, files in os.walk(options.targetDir, False): for root, dirs, files in os.walk(options.targetDir, False):
for name in files: for name in files:
if getImageFileName(name) is not None: if getImageFileName(name) is not None:
pagenumber += 1 pagenumber += 1
work.append([root, name]) work.append([root, name, options])
else: else:
os.remove(os.path.join(root, name)) os.remove(os.path.join(root, name))
if GUI: if GUI:
GUI.emit(QtCore.SIGNAL("progressBarTick"), pagenumber) GUI.emit(QtCore.SIGNAL("progressBarTick"), pagenumber)
if len(work) > 0: if len(work) > 0:
workers = pool.map_async(func=splitImage, iterable=work) for i in work:
pool.close() splitWorkerPool.apply_async(func=splitImage, args=(i, ), callback=splitImage_tick)
if GUI: splitWorkerPool.close()
while not workers.ready(): splitWorkerPool.join()
# noinspection PyBroadException if GUI and not GUI.conversionAlive:
try:
queue.get(True, 5)
except:
pass
GUI.emit(QtCore.SIGNAL("progressBarTick"))
pool.join()
queue.close()
try:
workers.get()
except:
rmtree(options.targetDir, True) rmtree(options.targetDir, True)
raise RuntimeError("One of workers crashed. Cause: " + str(sys.exc_info()[1])) raise UserWarning("Conversion interrupted.")
if GUI: if len(splitWorkerOutput) > 0:
GUI.emit(QtCore.SIGNAL("progressBarTick"), 1) rmtree(options.targetDir, True)
raise RuntimeError("One of workers crashed. Cause: " + splitWorkerOutput[0])
if options.inPlace: if options.inPlace:
rmtree(options.sourceDir) rmtree(options.sourceDir)
move(options.targetDir, options.sourceDir) move(options.targetDir, options.sourceDir)