1
0
mirror of https://github.com/ciromattia/kcc synced 2025-12-13 17:56:30 +00:00

Improved cropping mechanism

This commit is contained in:
Paweł Jastrzębski
2016-11-19 17:57:16 +01:00
parent b545f7ad48
commit d1a07d7ffa
2 changed files with 41 additions and 95 deletions

View File

@@ -525,10 +525,10 @@ def imgFileProcessing(work):
workImg = image.ComicPageParser((dirpath, afile), opt) workImg = image.ComicPageParser((dirpath, afile), opt)
for i in workImg.payload: for i in workImg.payload:
img = image.ComicPage(i[0], i[1], i[2], i[3], i[4], opt) img = image.ComicPage(i[0], i[1], i[2], i[3], i[4], opt)
if opt.cropping > 0 and not opt.webtoon:
img.cropWhiteSpace(opt.croppingp)
if opt.cropping == 2 and not opt.webtoon: if opt.cropping == 2 and not opt.webtoon:
img.cutPageNumber(opt.croppingpn) img.cropPageNumber(opt.croppingp)
if opt.cropping > 0 and not opt.webtoon:
img.cropMargin(opt.croppingp)
img.autocontrastImage() img.autocontrastImage()
img.resizeImage() img.resizeImage()
if opt.forcepng and not opt.forcecolor: if opt.forcepng and not opt.forcecolor:
@@ -960,6 +960,10 @@ def makeParser():
help="Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]") help="Double page parsing mode. 0: Split 1: Rotate 2: Both [Default=0]")
processingOptions.add_option("-g", "--gamma", type="float", dest="gamma", default="0.0", processingOptions.add_option("-g", "--gamma", type="float", dest="gamma", default="0.0",
help="Apply gamma correction to linearize the image [Default=Auto]") help="Apply gamma correction to linearize the image [Default=Auto]")
processingOptions.add_option("-c", "--cropping", type="int", dest="cropping", default="2",
help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]")
processingOptions.add_option("--cp", "--croppingpower", type="float", dest="croppingp", default="1.0",
help="Set cropping power [Default=1.0]")
processingOptions.add_option("--hq", action="store_true", dest="hqmode", default=False, processingOptions.add_option("--hq", action="store_true", dest="hqmode", default=False,
help="Enable high quality Panel View") help="Enable high quality Panel View")
processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False, processingOptions.add_option("--blackborders", action="store_true", dest="black_borders", default=False,
@@ -970,12 +974,6 @@ def makeParser():
help="Don't convert images to grayscale") help="Don't convert images to grayscale")
processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False, processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
help="Create PNG files instead JPEG") help="Create PNG files instead JPEG")
processingOptions.add_option("--cropping", type="int", dest="cropping", default="2",
help="Set cropping mode. 0: Disabled 1: Margins 2: Margins + page numbers [Default=2]")
processingOptions.add_option("--croppingpower", type="float", dest="croppingp", default="0.1",
help="Set margin cropping threshold [Default=0.1]")
processingOptions.add_option("--croppingpowerpage", type="float", dest="croppingpn", default="5.0",
help="Set page number cropping threshold [Default=5.0]")
customProfileOptions.add_option("--customwidth", type="int", dest="customwidth", default=0, customProfileOptions.add_option("--customwidth", type="int", dest="customwidth", default=0,
help="Replace screen width provided by device profile") help="Replace screen width provided by device profile")

View File

@@ -1,5 +1,6 @@
# Copyright (C) 2010 Alex Yatskov # Copyright (C) 2010 Alex Yatskov
# Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com> # Copyright (C) 2011 Stanislav (proDOOMman) Kosolapov <prodoomman@gmail.com>
# Copyright (c) 2016 Alberto Planas <aplanas@gmail.com>
# Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com> # Copyright (c) 2012-2014 Ciro Mattia Gonano <ciromattia@gmail.com>
# Copyright (c) 2013-2016 Pawel Jastrzebski <pawelj@iosphe.re> # Copyright (c) 2013-2016 Pawel Jastrzebski <pawelj@iosphe.re>
# #
@@ -20,7 +21,7 @@ import os
from io import BytesIO from io import BytesIO
from urllib.request import Request, urlopen from urllib.request import Request, urlopen
from urllib.parse import quote from urllib.parse import quote
from PIL import Image, ImageOps, ImageStat, ImageChops from PIL import Image, ImageOps, ImageStat, ImageChops, ImageFilter
from .shared import md5Checksum from .shared import md5Checksum
from . import __version__ from . import __version__
@@ -318,92 +319,39 @@ class ComicPage:
if self.image.size[0] > size[0] or self.image.size[1] > size[1]: if self.image.size[0] > size[0] or self.image.size[1] > size[1]:
self.image.thumbnail(size, Image.LANCZOS) self.image.thumbnail(size, Image.LANCZOS)
def cutPageNumber(self, fixedThreshold): def getBoundingBox(self, tmpImg):
if ImageChops.invert(self.image).getbbox() is not None: min_margin = [int(0.005 * i + 0.5) for i in tmpImg.size]
widthImg, heightImg = self.image.size max_margin = [int(0.1 * i + 0.5) for i in tmpImg.size]
delta = 2 bbox = tmpImg.getbbox()
diff = delta bbox = (
if ImageStat.Stat(self.image).var[0] < 2 * fixedThreshold: max(0, min(max_margin[0], bbox[0] - min_margin[0])),
return self.image max(0, min(max_margin[1], bbox[1] - min_margin[1])),
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\ min(tmpImg.size[0],
and diff < heightImg: max(tmpImg.size[0] - max_margin[0], bbox[2] + min_margin[0])),
diff += delta min(tmpImg.size[1],
diff -= delta max(tmpImg.size[1] - max_margin[1], bbox[3] + min_margin[1])),
pageNumberCut1 = diff )
if diff < delta: return bbox
diff = delta
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0]
diff += delta
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] - oldStat > 0\
and diff < heightImg // 4:
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0]
diff += delta
diff -= delta
pageNumberCut2 = diff
diff += delta
oldStat = ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg,
heightImg - pageNumberCut2))).var[0]
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg - pageNumberCut2))).var[0]\
< fixedThreshold + oldStat and diff < heightImg // 4:
diff += delta
diff -= delta
pageNumberCut3 = diff
delta = 5
diff = delta
while ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut2, diff, heightImg))).var[0]\
< fixedThreshold and diff < widthImg:
diff += delta
diff -= delta
pageNumberX1 = diff
diff = delta
while ImageStat.Stat(self.image.crop((widthImg - diff, heightImg - pageNumberCut2,
widthImg, heightImg))).var[0] < fixedThreshold and diff < widthImg:
diff += delta
diff -= delta
pageNumberX2 = widthImg - diff
if pageNumberCut3 - pageNumberCut1 > 2 * delta\
and float(pageNumberX2 - pageNumberX1) / float(pageNumberCut2 - pageNumberCut1) <= 9.0\
and ImageStat.Stat(self.image.crop((0, heightImg - pageNumberCut3, widthImg, heightImg))).var[0]\
/ ImageStat.Stat(self.image).var[0] < 0.1\
and pageNumberCut3 < heightImg / 4 - delta:
diff = pageNumberCut3
else:
diff = pageNumberCut1
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
def cropWhiteSpace(self, fixedThreshold): def cropPageNumber(self, power):
if ImageChops.invert(self.image).getbbox() is not None: if self.fill != 'white':
widthImg, heightImg = self.image.size tmpImg = self.image.convert(mode='L')
delta = 10 else:
diff = delta tmpImg = ImageOps.invert(self.image.convert(mode='L'))
# top tmpImg = tmpImg.point(lambda x: x and 255)
while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < fixedThreshold and diff < heightImg: tmpImg = tmpImg.filter(ImageFilter.MinFilter(size=3))
diff += delta tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=5))
diff -= delta tmpImg = tmpImg.point(lambda x: (x >= 48 * power) and x)
self.image = self.image.crop((0, diff, widthImg, heightImg)) self.image = self.image.crop(tmpImg.getbbox()) if tmpImg.getbbox() else self.image
widthImg, heightImg = self.image.size
diff = delta def cropMargin(self, power):
# left if self.fill != 'white':
while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < fixedThreshold and diff < widthImg: tmpImg = self.image.convert(mode='L')
diff += delta else:
diff -= delta tmpImg = ImageOps.invert(self.image.convert(mode='L'))
self.image = self.image.crop((diff, 0, widthImg, heightImg)) tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=3))
widthImg, heightImg = self.image.size tmpImg = tmpImg.point(lambda x: (x >= 16 * power) and x)
diff = delta self.image = self.image.crop(self.getBoundingBox(tmpImg)) if tmpImg.getbbox() else self.image
# down
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\
and diff < heightImg:
diff += delta
diff -= delta
self.image = self.image.crop((0, 0, widthImg, heightImg - diff))
widthImg, heightImg = self.image.size
diff = delta
# right
while ImageStat.Stat(self.image.crop((widthImg - diff, 0, widthImg, heightImg))).var[0] < fixedThreshold\
and diff < widthImg:
diff += delta
diff -= delta
self.image = self.image.crop((0, 0, widthImg - diff, heightImg))
class Cover: class Cover: