Source code for node_arrange_images

# This file is part of Sympathy for Data.
# Copyright (c) 2017, Combine Control Systems AB
#
# SYMPATHY FOR DATA COMMERCIAL LICENSE
# You should have received a link to the License with Sympathy for Data.

import numpy as np

from sympathy.api import node
from sympathy.api.nodeconfig import Port, Ports, Tag, Tags

from sylib.imageprocessing.image import ImagePort


class ArrangeImagesAbstract:
    author = 'Mathias Broxvall'
    icon = 'image_arrange.svg'
    description = ('Arranges a sequence of images '
                   'horizontally, vertically, or in a grid ')
    tags = Tags(Tag.ImageProcessing.Layers)

    parameters = node.parameters()
    parameters.set_integer(
        'horizontally',
        value=0,
        description=('Number of images horizontally. Use 0 for all horizontal '
                     'or -1 for grid layout'),
        label='Horizontally')
    parameters.set_integer(
        'hspacing',
        value=0,
        description='Spacing in pixels between images horizontally',
        label='Horizontal spacing')
    parameters.set_integer(
        'vspacing',
        value=0,
        description='Spacing in pixels between images vertically',
        label='Vertical spacing')
    parameters.set_boolean(
        'add_alpha',
        value=True,
        description=('If true then output will have one more output channel '
                     'than the inputs'),
        label='Add alpha')
    parameters.set_boolean(
        'use_alpha',
        value=True,
        description=('If true then last output channel will be set to 0 '
                     'in-between images, 1 otherwise'),
        label='Use alpha')
    parameters.set_boolean(
        'center',
        value=True,
        description=('Centers the images inside each grid cell where they are'
                     ' placed'),
        label='Center')
    parameters.set_string(
        'background',
        value="0.0",
        description='Background colour as float values separated by comma',
        label='Background')

    def calc_size(self, horizontally, hspacing, vspacing, images):
        im_width = np.max([im.shape[1] for im in images])
        im_height = np.max([im.shape[0] for im in images])
        im_channels = np.min(
            [im.shape[2] if len(im.shape) > 2 else 1 for im in images])
        width = (horizontally - 1) * hspacing + horizontally * im_width
        rows = int(np.ceil(len(images) / float(horizontally)))
        height = (rows - 1) * vspacing + rows * im_height
        return im_height, im_width, height, width, im_channels, rows

    def execute(self, node_context):
        inputs = self.get_input_images(node_context)
        output = node_context.output['result']

        images = [input_.get_image() for input_ in inputs]

        hspacing = node_context.parameters['hspacing'].value
        vspacing = node_context.parameters['vspacing'].value
        add_alpha = node_context.parameters['add_alpha'].value
        use_alpha = node_context.parameters['use_alpha'].value
        horizontally = node_context.parameters['horizontally'].value
        center = node_context.parameters['center'].value
        background = node_context.parameters['background'].value

        background = [float(v) for v in background.split(',')]

        if horizontally == -1:
            horizontally = int(np.ceil(np.sqrt(len(images))))
        if horizontally == 0:
            horizontally = len(images)

        im_height, im_width, height, width, channels, rows = (
            self.calc_size(horizontally, hspacing, vspacing, images))
        if add_alpha:
            channels += 1

        if len(background) < channels:
            background += background[-1:] * (channels - len(background))
        if use_alpha:
            background[-1] = 0

        result = np.full((height, width, channels), background[:channels])
        for row in range(rows):
            posy = row * vspacing + row * im_height
            for col in range(horizontally):
                if col + row * horizontally >= len(images):
                    break
                im = images[col + row * horizontally]
                posx = col * hspacing + col * im_width
                h, w = im.shape[:2]
                if center:
                    dx = int((im_width - w) / 2)
                    dy = int((im_height - h) / 2)
                else:
                    dx, dy = 0, 0

                for ch in range(channels - add_alpha * 1):
                    if len(im.shape) == 2:
                        result[posy+dy:posy+dy+h, posx+dx:posx+dx+w, ch] = (
                            im[:, :])
                    else:
                        result[posy+dy:posy+dy+h, posx+dx:posx+dx+w, ch] = (
                            im[:, :, ch])
                if use_alpha:
                    result[posy+dy:posy+dy+h, posx+dx:posx+dx+w, -1] = (
                        np.ones(im.shape[:2]))
        output.set_image(result)


[docs] class ArrangeImages(ArrangeImagesAbstract, node.Node): """ Arranges all inputs images in a horizontal, vertical, or grid layout with optional spacing between each image. The number of output channels are limited by the image with the least number of channels. Outputs optional alpha channel with 1's or 0's on pixels covered by an image or by the spacing. """ name = 'Arrange Images' nodeid = 'com.sympathyfordata.imageanalysis.arrange_images' inputs = Ports([ Port.Custom('image', 'Input images', name='images', n=(1, 6, 2)) ]) outputs = Ports([ ImagePort('result after filtering', name='result'), ]) def get_input_images(self, node_context): return node_context.input.group('images')
[docs] class ArrangeImageList(ArrangeImagesAbstract, node.Node): """ Arranges all inputs images in a horizontal, vertical, or grid layout with optional spacing between each image. The number of output channels are limited by the image with the least number of channels. Outputs optional alpha channel with 1's or 0's on pixels covered by an image or by the spacing. """ name = 'Arrange Image List' nodeid = 'com.sympathyfordata.imageanalysis.arrange_image_list' inputs = Ports([ Port.Custom('[image]', 'Input images', name='images') ]) outputs = Ports([ ImagePort('result after filtering', name='result'), ]) def get_input_images(self, node_context): return node_context.input['images']