From 6e7645e319e06c1bf88e122738e1ba05af53dc35 Mon Sep 17 00:00:00 2001 From: Matthias Bisping Date: Fri, 8 Apr 2022 14:04:48 +0200 Subject: [PATCH] topological sorting of definitions by caller hierarchy --- image_prediction/stitching/grouping.py | 10 +- image_prediction/stitching/merging.py | 172 ++++++++++++------------- image_prediction/stitching/utils.py | 18 +-- 3 files changed, 100 insertions(+), 100 deletions(-) diff --git a/image_prediction/stitching/grouping.py b/image_prediction/stitching/grouping.py index 8c56325..c6b0d18 100644 --- a/image_prediction/stitching/grouping.py +++ b/image_prediction/stitching/grouping.py @@ -5,11 +5,6 @@ from funcy import compose, second from image_prediction.stitching.utils import make_coord_getter -def group_by_coordinate(pairs, coord_getter): - pairs = sorted(pairs, key=coord_getter) - return map(compose(list, second), groupby(pairs, coord_getter)) - - class CoordGrouper: def __init__(self, axis): self.c1_getter = make_coord_getter(f"{other_axis(axis)}1") @@ -24,3 +19,8 @@ class CoordGrouper: def other_axis(axis): return "y" if axis == "x" else "x" + + +def group_by_coordinate(pairs, coord_getter): + pairs = sorted(pairs, key=coord_getter) + return map(compose(list, second), groupby(pairs, coord_getter)) diff --git a/image_prediction/stitching/merging.py b/image_prediction/stitching/merging.py index 7cdd61e..60fbb74 100644 --- a/image_prediction/stitching/merging.py +++ b/image_prediction/stitching/merging.py @@ -13,71 +13,51 @@ from image_prediction.utils.generic import until from test.utils.stitching import HorizontalKeyMapper, VerticalKeyMapper -def make_pair_merger(axis): - return {"y": merge_pair_vertically, "x": merge_pair_horizontally}[axis] +def no_new_merges(pairs1, pairs2): + return len(pairs1) == len(pairs2) + + +def merge_along_both_axes(pairs: Iterable[ImageMetadataPair]): + pairs = merge_along_axis(pairs, "x") + pairs = list(merge_along_axis(pairs, "y")) + + return pairs + + +def merge_along_axis(pairs: Iterable[ImageMetadataPair], axis): + def group_pairs_within_groups_by_greater_coordinate(groups): + return map(CoordGrouper(axis).group_pairs_by_greater_coordinate, groups) + + def merge_groups_along_orthogonal_axis(groups): + return map(make_group_merger(axis), groups) + + def group_pairs_by_lesser_coordinate(pairs): + return CoordGrouper(axis).group_pairs_by_lesser_coordinate(pairs) + + return rcompose( + group_pairs_by_lesser_coordinate, # pairs -> groups of pairs aligned on one edge + group_pairs_within_groups_by_greater_coordinate, # -> groups of pairs fully aligned on orthogonal axis + flatten_groups_once, # groups of groups of pairs -> groups of pairs + merge_groups_along_orthogonal_axis, + flatten_groups_once, # groups of pairs -> pairs + )(pairs) def make_group_merger(axis): return {"y": merge_group_vertically, "x": merge_group_horizontally}[axis] -def merge_metadata_horizontally(m1: dict, m2: dict): - m1, m2 = map(HorizontalKeyMapper, [m1, m2]) - return merge_metadata(m1, m2) +def merge_group_vertically(group: Iterable[ImageMetadataPair]): + return merge_group(group, "y") -def merge_metadata_vertically(m1: dict, m2: dict): - m1, m2 = map(VerticalKeyMapper, [m1, m2]) - return merge_metadata(m1, m2) +def merge_group_horizontally(group: Iterable[ImageMetadataPair]): + return merge_group(group, "x") -def merge_metadata(m1: dict, m2: dict): - - c1 = min(m1.c1, m2.c1) - c2 = max(m1.c2, m2.c2) - dim = m1.dim + m2.dim - - merged = deepcopy(m1) - merged.dim = dim - merged.c1 = c1 - merged.c2 = c2 - - return merged.wrapped - - -def merge_pair_horizontally(p1: ImageMetadataPair, p2: ImageMetadataPair): - metadata_merged = merge_metadata_horizontally(p1.metadata, p2.metadata) - image_concatenated = concat_images_horizontally(p1.image, p2.image, metadata_merged) - return ImageMetadataPair(image_concatenated, metadata_merged) - - -def merge_pair_vertically(p1: ImageMetadataPair, p2: ImageMetadataPair): - metadata_merged = merge_metadata_vertically(p1.metadata, p2.metadata) - image_concatenated = concat_images_vertically(p1.image, p2.image, metadata_merged) - return ImageMetadataPair(image_concatenated, metadata_merged) - - -def concat_images_horizontally(im1: Image, im2: Image, metadata: dict): - return concat_images(im1, im2, metadata, 0) - - -def concat_images_vertically(im1: Image, im2: Image, metadata: dict): - return concat_images(im1, im2, metadata, 1) - - -def concat_images(im1: Image, im2: Image, metadata: dict, axis): - - im_aggr = Image.new(im1.mode, (metadata[Info.WIDTH], metadata[Info.HEIGHT])) - - images = [im1, im2] - - offsets = [0, *[im.size[axis] for im in images]] - - for im, offset in zip(images, offsets): - box = (offset, 0) if not axis else (0, offset) - im_aggr.paste(im, box=box) - - return im_aggr +def merge_group(group: Iterable[ImageMetadataPair], direction): + reduce_group = make_merger_aggregator(direction) + return until(no_new_merges, reduce_group, group) def make_merger_aggregator(direction) -> Callable[[Iterable[ImageMetadataPair]], Iterable[ImageMetadataPair]]: @@ -108,44 +88,64 @@ def make_merger_aggregator(direction) -> Callable[[Iterable[ImageMetadataPair]], return merger_aggregator -def merge_group(group: Iterable[ImageMetadataPair], direction): - reduce_group = make_merger_aggregator(direction) - return until(no_new_merges, reduce_group, group) +def make_pair_merger(axis): + return {"y": merge_pair_vertically, "x": merge_pair_horizontally}[axis] -def merge_group_horizontally(group: Iterable[ImageMetadataPair]): - return merge_group(group, "x") +def merge_pair_vertically(p1: ImageMetadataPair, p2: ImageMetadataPair): + metadata_merged = merge_metadata_vertically(p1.metadata, p2.metadata) + image_concatenated = concat_images_vertically(p1.image, p2.image, metadata_merged) + return ImageMetadataPair(image_concatenated, metadata_merged) -def merge_group_vertically(group: Iterable[ImageMetadataPair]): - return merge_group(group, "y") +def merge_pair_horizontally(p1: ImageMetadataPair, p2: ImageMetadataPair): + metadata_merged = merge_metadata_horizontally(p1.metadata, p2.metadata) + image_concatenated = concat_images_horizontally(p1.image, p2.image, metadata_merged) + return ImageMetadataPair(image_concatenated, metadata_merged) -def merge_along_axis(pairs: Iterable[ImageMetadataPair], axis): - def group_pairs_within_groups_by_greater_coordinate(groups): - return map(CoordGrouper(axis).group_pairs_by_greater_coordinate, groups) - - def merge_groups_along_orthogonal_axis(groups): - return map(make_group_merger(axis), groups) - - def group_pairs_by_lesser_coordinate(pairs): - return CoordGrouper(axis).group_pairs_by_lesser_coordinate(pairs) - - return rcompose( - group_pairs_by_lesser_coordinate, # pairs -> groups of pairs aligned on one edge - group_pairs_within_groups_by_greater_coordinate, # -> groups of pairs fully aligned on orthogonal axis - flatten_groups_once, # groups of groups of pairs -> groups of pairs - merge_groups_along_orthogonal_axis, - flatten_groups_once, # groups of pairs -> pairs - )(pairs) +def merge_metadata_vertically(m1: dict, m2: dict): + m1, m2 = map(VerticalKeyMapper, [m1, m2]) + return merge_metadata(m1, m2) -def merge_along_both_axes(pairs: Iterable[ImageMetadataPair]): - pairs = merge_along_axis(pairs, "x") - pairs = list(merge_along_axis(pairs, "y")) - - return pairs +def merge_metadata_horizontally(m1: dict, m2: dict): + m1, m2 = map(HorizontalKeyMapper, [m1, m2]) + return merge_metadata(m1, m2) -def no_new_merges(pairs1, pairs2): - return len(pairs1) == len(pairs2) +def merge_metadata(m1: dict, m2: dict): + + c1 = min(m1.c1, m2.c1) + c2 = max(m1.c2, m2.c2) + dim = m1.dim + m2.dim + + merged = deepcopy(m1) + merged.dim = dim + merged.c1 = c1 + merged.c2 = c2 + + return merged.wrapped + + +def concat_images_vertically(im1: Image, im2: Image, metadata: dict): + return concat_images(im1, im2, metadata, 1) + + +def concat_images_horizontally(im1: Image, im2: Image, metadata: dict): + return concat_images(im1, im2, metadata, 0) + + +def concat_images(im1: Image, im2: Image, metadata: dict, axis): + + im_aggr = Image.new(im1.mode, (metadata[Info.WIDTH], metadata[Info.HEIGHT])) + + images = [im1, im2] + + offsets = [0, *[im.size[axis] for im in images]] + + for im, offset in zip(images, offsets): + box = (offset, 0) if not axis else (0, offset) + im_aggr.paste(im, box=box) + + return im_aggr diff --git a/image_prediction/stitching/utils.py b/image_prediction/stitching/utils.py index 422c160..6e9c4b7 100644 --- a/image_prediction/stitching/utils.py +++ b/image_prediction/stitching/utils.py @@ -3,11 +3,8 @@ from itertools import chain from image_prediction.info import Info -def make_getter(key): - def getter(pair): - return pair.metadata[key] - - return getter +def flatten_groups_once(groups): + return chain.from_iterable(groups) def make_coord_getter(c): @@ -19,12 +16,15 @@ def make_coord_getter(c): }[c] +def make_getter(key): + def getter(pair): + return pair.metadata[key] + + return getter + + def make_length_getter(dim): return { "width": make_getter(Info.WIDTH), "height": make_getter(Info.HEIGHT), }[dim] - - -def flatten_groups_once(groups): - return chain.from_iterable(groups) \ No newline at end of file