mirror of
https://github.com/vincentbernat/i3wm-configuration.git
synced 2025-07-10 18:24:22 +02:00
wallpaper: more tests
The scoring system is a bit fragile...
This commit is contained in:
parent
f5e47c6727
commit
01d3fb95bc
1 changed files with 88 additions and 17 deletions
105
bin/wallpaper
105
bin/wallpaper
|
@ -22,6 +22,7 @@ logger = logging.getLogger("wallpaper")
|
||||||
Rectangle = collections.namedtuple("Rectangle", ["x", "y", "width", "height"])
|
Rectangle = collections.namedtuple("Rectangle", ["x", "y", "width", "height"])
|
||||||
WallpaperPart = collections.namedtuple("WallpaperPart", ["rectangle", "image"])
|
WallpaperPart = collections.namedtuple("WallpaperPart", ["rectangle", "image"])
|
||||||
|
|
||||||
|
|
||||||
def get_outputs():
|
def get_outputs():
|
||||||
"""Get physical outputs."""
|
"""Get physical outputs."""
|
||||||
# Get display size
|
# Get display size
|
||||||
|
@ -38,7 +39,9 @@ def get_outputs():
|
||||||
if output_info.crtc == 0:
|
if output_info.crtc == 0:
|
||||||
continue
|
continue
|
||||||
crtc_info = randr.get_crtc_info(window, output_info.crtc, output_info.timestamp)
|
crtc_info = randr.get_crtc_info(window, output_info.crtc, output_info.timestamp)
|
||||||
outputs.append(Rectangle(crtc_info.x, crtc_info.y, crtc_info.width, crtc_info.height))
|
outputs.append(
|
||||||
|
Rectangle(crtc_info.x, crtc_info.y, crtc_info.width, crtc_info.height)
|
||||||
|
)
|
||||||
|
|
||||||
for o in outputs:
|
for o in outputs:
|
||||||
logger.debug("output: %s", o)
|
logger.debug("output: %s", o)
|
||||||
|
@ -49,12 +52,24 @@ def get_covering_rectangles(outputs):
|
||||||
"""Compute all possible groups of covering boxes for the provided
|
"""Compute all possible groups of covering boxes for the provided
|
||||||
outputs. For each group, an output is included in exactly one box.
|
outputs. For each group, an output is included in exactly one box.
|
||||||
|
|
||||||
>>> get_covering_rectangles([Rectangle(0, 0, 100, 100)])
|
>>> gcr = get_covering_rectangles
|
||||||
|
>>> gcr([Rectangle(0, 0, 100, 100)])
|
||||||
{(Rectangle(x=0, y=0, width=100, height=100),)}
|
{(Rectangle(x=0, y=0, width=100, height=100),)}
|
||||||
>>> get_covering_rectangles([Rectangle(0, 0, 100, 100), Rectangle(100, 0, 100, 100)])
|
>>> gcr([Rectangle(0, 0, 100, 100),
|
||||||
{(Rectangle(x=0, y=0, width=100, height=100), Rectangle(x=100, y=0, width=100, height=100)), (Rectangle(x=0, y=0, width=200, height=100),)}
|
... Rectangle(100, 0, 100, 100)]) # doctest: +NORMALIZE_WHITESPACE
|
||||||
>>> get_covering_rectangles([Rectangle(0, 0, 100, 100), Rectangle(100, 0, 100, 100), Rectangle(0, 100, 100, 100)])
|
{(Rectangle(x=0, y=0, width=100, height=100),
|
||||||
{(Rectangle(x=100, y=0, width=100, height=100), Rectangle(x=0, y=100, width=100, height=100), Rectangle(x=0, y=0, width=100, height=100)), (Rectangle(x=100, y=0, width=100, height=100), Rectangle(x=0, y=0, width=100, height=200)), (Rectangle(x=0, y=0, width=200, height=100), Rectangle(x=0, y=100, width=100, height=100))}
|
Rectangle(x=100, y=0, width=100, height=100)),
|
||||||
|
(Rectangle(x=0, y=0, width=200, height=100),)}
|
||||||
|
>>> gcr([Rectangle(0, 0, 100, 100),
|
||||||
|
... Rectangle(100, 0, 100, 100),
|
||||||
|
... Rectangle(0, 100, 100, 100)]) # doctest: +NORMALIZE_WHITESPACE
|
||||||
|
{(Rectangle(x=100, y=0, width=100, height=100),
|
||||||
|
Rectangle(x=0, y=100, width=100, height=100),
|
||||||
|
Rectangle(x=0, y=0, width=100, height=100)),
|
||||||
|
(Rectangle(x=100, y=0, width=100, height=100),
|
||||||
|
Rectangle(x=0, y=0, width=100, height=200)),
|
||||||
|
(Rectangle(x=0, y=0, width=200, height=100),
|
||||||
|
Rectangle(x=0, y=100, width=100, height=100))}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
candidates = set()
|
candidates = set()
|
||||||
|
@ -81,7 +96,10 @@ def get_covering_rectangles(outputs):
|
||||||
for output in outputs:
|
for output in outputs:
|
||||||
nb = 0
|
nb = 0
|
||||||
for c in candidate:
|
for c in candidate:
|
||||||
if c.x <= output.x < c.x + c.width and c.y <= output.y < c.y + c.height:
|
if (
|
||||||
|
c.x <= output.x < c.x + c.width
|
||||||
|
and c.y <= output.y < c.y + c.height
|
||||||
|
):
|
||||||
nb += 1
|
nb += 1
|
||||||
if nb != 1: # output not contained in a single rectangle
|
if nb != 1: # output not contained in a single rectangle
|
||||||
break
|
break
|
||||||
|
@ -92,6 +110,7 @@ def get_covering_rectangles(outputs):
|
||||||
logger.debug("group: %s", g)
|
logger.debug("group: %s", g)
|
||||||
return groups
|
return groups
|
||||||
|
|
||||||
|
|
||||||
def get_random_images(directory, number):
|
def get_random_images(directory, number):
|
||||||
"""Get random images from a directory."""
|
"""Get random images from a directory."""
|
||||||
images = []
|
images = []
|
||||||
|
@ -103,11 +122,55 @@ def get_random_images(directory, number):
|
||||||
images = [Image.open(image) for image in images]
|
images = [Image.open(image) for image in images]
|
||||||
|
|
||||||
for image in images:
|
for image in images:
|
||||||
logger.debug("image: %s %s×%s", image.filename[(len(directory) + 1) :], *image.size)
|
directory_len = len(directory) + 1
|
||||||
|
logger.debug("image: %s %s×%s", image.filename[directory_len:], *image.size)
|
||||||
return images
|
return images
|
||||||
|
|
||||||
|
|
||||||
def get_best_parts(groups, images):
|
def get_best_parts(groups, images):
|
||||||
"""Find optimal association for images for the groups of covering rectangles."""
|
"""Find optimal association for images for the groups of covering rectangles.
|
||||||
|
|
||||||
|
>>> gbp = get_best_parts
|
||||||
|
>>> gbp([[Rectangle(0, 0, 100, 100)]],
|
||||||
|
... [Image.new("RGB", (100, 100))]) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
|
||||||
|
[WallpaperPart(rectangle=Rectangle(x=0, y=0, width=100, height=100),
|
||||||
|
image=<PIL.Image.Image image mode=RGB size=100x100 at ...>)]
|
||||||
|
>>> gbp([[Rectangle(0, 0, 100, 100)]],
|
||||||
|
... [Image.new("RGB", (100, 100)),
|
||||||
|
... Image.new("RGB", (100, 200))]) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
|
||||||
|
[WallpaperPart(rectangle=Rectangle(x=0, y=0, width=100, height=100),
|
||||||
|
image=<PIL.Image.Image image mode=RGB size=100x100 at ...>)]
|
||||||
|
>>> gbp([[Rectangle(0, 0, 100, 100)]],
|
||||||
|
... [Image.new("RGB", (50, 50)),
|
||||||
|
... Image.new("RGB", (100, 200))]) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
|
||||||
|
[WallpaperPart(rectangle=Rectangle(x=0, y=0, width=100, height=100),
|
||||||
|
image=<PIL.Image.Image image mode=RGB size=50x50 at ...>)]
|
||||||
|
>>> gbp([[Rectangle(0, 0, 100, 100)]],
|
||||||
|
... [Image.new("RGB", (10, 10)),
|
||||||
|
... Image.new("RGB", (100, 200))]) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
|
||||||
|
[WallpaperPart(rectangle=Rectangle(x=0, y=0, width=100, height=100),
|
||||||
|
image=<PIL.Image.Image image mode=RGB size=100x200 at ...>)]
|
||||||
|
>>> gbp([[Rectangle(0, 0, 100, 100), Rectangle(0, 100, 100, 100)],
|
||||||
|
... [Rectangle(0, 0, 200, 100)]],
|
||||||
|
... [Image.new("RGB", (100, 100)),
|
||||||
|
... Image.new("RGB", (200, 100))]) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
|
||||||
|
[WallpaperPart(rectangle=Rectangle(x=0, y=0, width=200, height=100),
|
||||||
|
image=<PIL.Image.Image image mode=RGB size=200x100 at ...>)]
|
||||||
|
>>> gbp([[Rectangle(0, 0, 100, 100), Rectangle(100, 0, 100, 100)],
|
||||||
|
... [Rectangle(0, 0, 200, 100)]],
|
||||||
|
... [Image.new("RGB", (100, 100)),
|
||||||
|
... Image.new("RGB", (100, 100))]) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
|
||||||
|
[WallpaperPart(rectangle=Rectangle(x=0, y=0, width=100, height=100),
|
||||||
|
image=<PIL.Image.Image image mode=RGB size=100x100 at ...>),
|
||||||
|
WallpaperPart(rectangle=Rectangle(x=100, y=0, width=100, height=100),
|
||||||
|
image=<PIL.Image.Image image mode=RGB size=100x100 at ...>)]
|
||||||
|
>>> gbp([[Rectangle(0, 0, 1920, 1080), Rectangle(1920, 0, 1920, 1080)],
|
||||||
|
... [Rectangle(0, 0, 3840, 1080)]],
|
||||||
|
... [Image.new("RGB", (2560, 1440)),
|
||||||
|
... Image.new("RGB", (3840, 1440))]) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
|
||||||
|
[WallpaperPart(rectangle=Rectangle(x=0, y=0, width=3840, height=1080),
|
||||||
|
image=<PIL.Image.Image image mode=RGB size=3840x1440 at ...>)]
|
||||||
|
"""
|
||||||
best_association = None
|
best_association = None
|
||||||
best_score = 0
|
best_score = 0
|
||||||
for group in groups:
|
for group in groups:
|
||||||
|
@ -118,7 +181,10 @@ def get_best_parts(groups, images):
|
||||||
continue
|
continue
|
||||||
seen.append(association)
|
seen.append(association)
|
||||||
score = 0
|
score = 0
|
||||||
association = [WallpaperPart(rectangle=assoc[0], image=assoc[1]) for assoc in association]
|
association = [
|
||||||
|
WallpaperPart(rectangle=assoc[0], image=assoc[1])
|
||||||
|
for assoc in association
|
||||||
|
]
|
||||||
for assoc in association:
|
for assoc in association:
|
||||||
# Similar ratio
|
# Similar ratio
|
||||||
oratio = assoc.rectangle.width * 100 // assoc.rectangle.height
|
oratio = assoc.rectangle.width * 100 // assoc.rectangle.height
|
||||||
|
@ -133,8 +199,8 @@ def get_best_parts(groups, images):
|
||||||
r = ipixels / opixels
|
r = ipixels / opixels
|
||||||
if r >= 1:
|
if r >= 1:
|
||||||
r = 1
|
r = 1
|
||||||
score += r * 50
|
score += r * 60
|
||||||
score /= len(group)
|
score /= len(group) * len(group)
|
||||||
logger.debug("association: %s, score %.2f", association, score)
|
logger.debug("association: %s, score %.2f", association, score)
|
||||||
if score > best_score or best_association is None:
|
if score > best_score or best_association is None:
|
||||||
best_association = association
|
best_association = association
|
||||||
|
@ -173,6 +239,7 @@ def save(wallpaper, target, compression):
|
||||||
background.save(tmp, "png", compress_level=compression)
|
background.save(tmp, "png", compress_level=compression)
|
||||||
os.rename(tmp.name, target)
|
os.rename(tmp.name, target)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Parse
|
# Parse
|
||||||
description = sys.modules[__name__].__doc__
|
description = sys.modules[__name__].__doc__
|
||||||
|
@ -220,13 +287,17 @@ if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
outputs, background = get_outputs()
|
outputs, background = get_outputs()
|
||||||
candidates = get_covering_rectangles(outputs)
|
candidates = get_covering_rectangles(outputs)
|
||||||
images = get_random_images(options.directory, len(outputs) + options.extra_images)
|
images = get_random_images(
|
||||||
|
options.directory, len(outputs) + options.extra_images
|
||||||
|
)
|
||||||
wallpaper_parts = get_best_parts(candidates, images)
|
wallpaper_parts = get_best_parts(candidates, images)
|
||||||
for part in wallpaper_parts:
|
for part in wallpaper_parts:
|
||||||
logger.info("wallpaper: {} ({}×{})".format(
|
logger.info(
|
||||||
part.image.filename[(len(options.directory) + 1) :],
|
"wallpaper: {} ({}×{})".format(
|
||||||
*part.image.size
|
part.image.filename[(len(options.directory) + 1) :],
|
||||||
))
|
*part.image.size
|
||||||
|
)
|
||||||
|
)
|
||||||
build(background, wallpaper_parts)
|
build(background, wallpaper_parts)
|
||||||
save(background, options.target, options.compression)
|
save(background, options.target, options.compression)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue