Automatically crop black borders from covers

This commit is contained in:
Timmy Welch 2022-09-19 20:24:42 -07:00
parent 20e7de5b5f
commit 7df2e3fdc0
No known key found for this signature in database
3 changed files with 80 additions and 3 deletions

View File

@ -42,6 +42,13 @@ def internal(parser: settngs.Manager) -> None:
def identifier(parser: settngs.Manager) -> None:
# identifier settings
parser.add_setting("--series-match-identify-thresh", default=91, type=int, help="")
parser.add_setting(
"-b",
"--border-crop-percent",
default=10,
type=int,
help="ComicTagger will automatically add an additional cover that has any black borders cropped. If the difference in height is less than %(default)s%% the cover will not be cropped.",
)
parser.add_setting(
"--publisher-filter",
default=["Panini Comics", "Abril", "Planeta DeAgostini", "Editorial Televisa", "Dino Comics"],

View File

@ -35,7 +35,7 @@ from comictalker.talkerbase import ComicTalker, TalkerError
logger = logging.getLogger(__name__)
try:
from PIL import Image
from PIL import Image, ImageChops
pil_available = True
except ImportError:
@ -166,6 +166,48 @@ class IssueIdentifier:
return cropped_image_data
# Adapted from https://stackoverflow.com/a/10616717/20629671
def crop_border(self, image_data: bytes, ratio: int) -> bytes | None:
im = Image.open(io.BytesIO(image_data))
# RGBA doesn't work????
tmp = im.convert("RGB")
bg = Image.new("RGB", tmp.size, "black")
diff = ImageChops.difference(tmp, bg)
diff = ImageChops.add(diff, diff, 2.0, -100)
bbox = diff.getbbox()
width_percent = 0
height_percent = 0
# If bbox is None that should mean it's solid black
if bbox:
width = bbox[2] - bbox[0]
height = bbox[3] - bbox[1]
# Convert to percent
width_percent = 100 - ((width / im.width) * 100)
height_percent = 100 - ((height / im.height) * 100)
logger.debug(
"Width: %s Height: %s, ratio: %s %s ratio met: %s",
im.width,
im.height,
width_percent,
height_percent,
width_percent > ratio or height_percent > ratio,
)
# If there is a difference return the image otherwise return None
if width_percent > ratio or height_percent > ratio:
output = io.BytesIO()
im.crop(bbox).save(output, format="PNG")
cropped_image_data = output.getvalue()
output.close()
return cropped_image_data
return None
def set_progress_callback(self, cb_func: Callable[[int, int], None]) -> None:
self.callback = cb_func
@ -308,8 +350,7 @@ class IssueIdentifier:
self.log_msg(score, False)
if score <= self.strong_score_thresh:
# such a good score, we can quit now, since for sure we
# have a winner
# such a good score, we can quit now, since for sure we have a winner
done = True
break
if done:
@ -464,6 +505,11 @@ class IssueIdentifier:
if narrow_cover_hash is not None:
hash_list.append(narrow_cover_hash)
cropped_border = self.crop_border(cover_image_data, self.options.identifier_border_crop_percent)
if cropped_border is not None:
hash_list.append(self.calculate_hash(cropped_border))
logger.info("Adding cropped cover to the hashlist")
try:
image_url = issue.image_url
alt_urls = issue.alt_image_urls

View File

@ -1,6 +1,9 @@
from __future__ import annotations
import io
import pytest
from PIL import Image
import comicapi.comicarchive
import comicapi.issuestring
@ -73,3 +76,24 @@ def test_search(cbz, options, comicvine_api):
for r, e in zip(results, [cv_expected]):
del r["url_image_hash"]
assert r == e
def test_crop_border(cbz, options, comicvine_api):
settings, definitions = options
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, settings, comicvine_api)
# This creates a white square centered on a black background
bg = Image.new("RGBA", (100, 100), (0, 0, 0, 255))
fg = Image.new("RGBA", (50, 50), (255, 255, 255, 255))
bg.paste(fg, (bg.width // 2 - (fg.width // 2), bg.height // 2 - (fg.height // 2)))
output = io.BytesIO()
bg.save(output, format="PNG")
image_data = output.getvalue()
output.close()
cropped = ii.crop_border(image_data, 49)
im = Image.open(io.BytesIO(cropped))
assert im.width == fg.width
assert im.height == fg.height
assert list(im.getdata()) == list(fg.getdata())