mirror of
https://github.com/ciromattia/kcc
synced 2025-12-13 17:56:30 +00:00
Improved cropping mechanism
This commit is contained in:
@@ -525,10 +525,10 @@ def imgFileProcessing(work):
|
||||
workImg = image.ComicPageParser((dirpath, afile), opt)
|
||||
for i in workImg.payload:
|
||||
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:
|
||||
img.cutPageNumber(opt.croppingpn)
|
||||
img.cropPageNumber(opt.croppingp)
|
||||
if opt.cropping > 0 and not opt.webtoon:
|
||||
img.cropMargin(opt.croppingp)
|
||||
img.autocontrastImage()
|
||||
img.resizeImage()
|
||||
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]")
|
||||
processingOptions.add_option("-g", "--gamma", type="float", dest="gamma", default="0.0",
|
||||
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,
|
||||
help="Enable high quality Panel View")
|
||||
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")
|
||||
processingOptions.add_option("--forcepng", action="store_true", dest="forcepng", default=False,
|
||||
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,
|
||||
help="Replace screen width provided by device profile")
|
||||
|
||||
120
kcc/image.py
120
kcc/image.py
@@ -1,5 +1,6 @@
|
||||
# Copyright (C) 2010 Alex Yatskov
|
||||
# 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) 2013-2016 Pawel Jastrzebski <pawelj@iosphe.re>
|
||||
#
|
||||
@@ -20,7 +21,7 @@ import os
|
||||
from io import BytesIO
|
||||
from urllib.request import Request, urlopen
|
||||
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 . import __version__
|
||||
|
||||
@@ -318,92 +319,39 @@ class ComicPage:
|
||||
if self.image.size[0] > size[0] or self.image.size[1] > size[1]:
|
||||
self.image.thumbnail(size, Image.LANCZOS)
|
||||
|
||||
def cutPageNumber(self, fixedThreshold):
|
||||
if ImageChops.invert(self.image).getbbox() is not None:
|
||||
widthImg, heightImg = self.image.size
|
||||
delta = 2
|
||||
diff = delta
|
||||
if ImageStat.Stat(self.image).var[0] < 2 * fixedThreshold:
|
||||
return self.image
|
||||
while ImageStat.Stat(self.image.crop((0, heightImg - diff, widthImg, heightImg))).var[0] < fixedThreshold\
|
||||
and diff < heightImg:
|
||||
diff += delta
|
||||
diff -= delta
|
||||
pageNumberCut1 = diff
|
||||
if diff < delta:
|
||||
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 getBoundingBox(self, tmpImg):
|
||||
min_margin = [int(0.005 * i + 0.5) for i in tmpImg.size]
|
||||
max_margin = [int(0.1 * i + 0.5) for i in tmpImg.size]
|
||||
bbox = tmpImg.getbbox()
|
||||
bbox = (
|
||||
max(0, min(max_margin[0], bbox[0] - min_margin[0])),
|
||||
max(0, min(max_margin[1], bbox[1] - min_margin[1])),
|
||||
min(tmpImg.size[0],
|
||||
max(tmpImg.size[0] - max_margin[0], bbox[2] + min_margin[0])),
|
||||
min(tmpImg.size[1],
|
||||
max(tmpImg.size[1] - max_margin[1], bbox[3] + min_margin[1])),
|
||||
)
|
||||
return bbox
|
||||
|
||||
def cropWhiteSpace(self, fixedThreshold):
|
||||
if ImageChops.invert(self.image).getbbox() is not None:
|
||||
widthImg, heightImg = self.image.size
|
||||
delta = 10
|
||||
diff = delta
|
||||
# top
|
||||
while ImageStat.Stat(self.image.crop((0, 0, widthImg, diff))).var[0] < fixedThreshold and diff < heightImg:
|
||||
diff += delta
|
||||
diff -= delta
|
||||
self.image = self.image.crop((0, diff, widthImg, heightImg))
|
||||
widthImg, heightImg = self.image.size
|
||||
diff = delta
|
||||
# left
|
||||
while ImageStat.Stat(self.image.crop((0, 0, diff, heightImg))).var[0] < fixedThreshold and diff < widthImg:
|
||||
diff += delta
|
||||
diff -= delta
|
||||
self.image = self.image.crop((diff, 0, widthImg, heightImg))
|
||||
widthImg, heightImg = self.image.size
|
||||
diff = delta
|
||||
# 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))
|
||||
def cropPageNumber(self, power):
|
||||
if self.fill != 'white':
|
||||
tmpImg = self.image.convert(mode='L')
|
||||
else:
|
||||
tmpImg = ImageOps.invert(self.image.convert(mode='L'))
|
||||
tmpImg = tmpImg.point(lambda x: x and 255)
|
||||
tmpImg = tmpImg.filter(ImageFilter.MinFilter(size=3))
|
||||
tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=5))
|
||||
tmpImg = tmpImg.point(lambda x: (x >= 48 * power) and x)
|
||||
self.image = self.image.crop(tmpImg.getbbox()) if tmpImg.getbbox() else self.image
|
||||
|
||||
def cropMargin(self, power):
|
||||
if self.fill != 'white':
|
||||
tmpImg = self.image.convert(mode='L')
|
||||
else:
|
||||
tmpImg = ImageOps.invert(self.image.convert(mode='L'))
|
||||
tmpImg = tmpImg.filter(ImageFilter.GaussianBlur(radius=3))
|
||||
tmpImg = tmpImg.point(lambda x: (x >= 16 * power) and x)
|
||||
self.image = self.image.crop(self.getBoundingBox(tmpImg)) if tmpImg.getbbox() else self.image
|
||||
|
||||
|
||||
class Cover:
|
||||
|
||||
Reference in New Issue
Block a user