Source code for node_perspective

# This file is part of Sympathy for Data.
# Copyright (c) 2018, 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.exceptions import SyDataError, SyConfigurationError
from sympathy.api.nodeconfig import Port, Ports, Tag, Tags, adjust

from sylib.imageprocessing.image import ImagePort
from sympathy.utils.pip_util import import_optional


def _cv2():
    return import_optional("cv2", group="opencv")


[docs] class PerspectiveTransform(node.Node): """ Performs a perspective transform in an image by mapping four points (atleast 3 non-colinear) in the input image to specific corresponding points in the output image. The input table should have exactly four rows and four columns. Two of the columns give the XY coordinates of an input point and the other two columns the corresponding point in the output image. The result is an image after applying the perspective transform implied by the given points. """ name = 'Perspective Transform' author = 'Mathias Broxvall' nodeid = 'com.sympathyfordata.imageanalysis.perspective_transform' icon = 'image_perspective_transform.svg' description = ( 'Performs a perspective transform in an image by mapping four points ' '(atleast 3 non-colinear) in the input image to specific corresponding' ' points in the output image' ) tags = Tags(Tag.ImageProcessing.ImageManipulation) inputs = Ports([ ImagePort('image to transform', name='image'), Port.Table('Table mapping four coordinates', name='coords') ]) outputs = Ports([ ImagePort('Result', name='result'), ]) parameters = node.parameters() parameters.set_string( 'xbefore', label='X column before', description='Column containing X coordinates before transform', value='', editor=node.editors.combo_editor(options=[], edit=True)) parameters.set_string( 'ybefore', label='Y column before', description='Column containing Y coordinates before transform', value='', editor=node.editors.combo_editor(options=[], edit=True)) parameters.set_string( 'xafter', label='X column after', description='Column containing X coordinates after transform', value='', editor=node.editors.combo_editor(options=[], edit=True)) parameters.set_string( 'yafter', label='Y column after', description='Column containing Y coordinates after transform', value='', editor=node.editors.combo_editor(options=[], edit=True)) def adjust_parameters(self, node_context): names = node_context.input['coords'].column_names() try: if node_context.parameters['xbefore'].value == '': node_context.parameters['xbefore'].value = names[0] if node_context.parameters['ybefore'].value == '': node_context.parameters['ybefore'].value = names[1] if node_context.parameters['xafter'].value == '': node_context.parameters['xafter'].value = names[2] if node_context.parameters['yafter'].value == '': node_context.parameters['yafter'].value = names[3] except Exception: pass adjust(node_context.parameters['xbefore'], node_context.input['coords']) adjust(node_context.parameters['ybefore'], node_context.input['coords']) adjust(node_context.parameters['xafter'], node_context.input['coords']) adjust(node_context.parameters['yafter'], node_context.input['coords']) def execute(self, node_context): params = node_context.parameters image = node_context.input['image'].get_image() coords_table = node_context.input['coords'] xbefore = params['xbefore'].value ybefore = params['ybefore'].value xafter = params['xafter'].value yafter = params['yafter'].value for name in [xbefore, ybefore, xafter, yafter]: if name not in coords_table.column_names(): raise SyConfigurationError( 'Invalid column name "{}" in configuration, column does ' 'not exist in input table'.format(name)) xbefore = coords_table[xbefore] ybefore = coords_table[ybefore] xafter = coords_table[xafter] yafter = coords_table[yafter] before = np.float32(list(zip(xbefore, ybefore))) after = np.float32(list(zip(xafter, yafter))) try: matrix = _cv2().getPerspectiveTransform(before, after) except _cv2().error as e: raise SyDataError( 'Error creating perspective transformation from input points, ' 'make sure you have 4 points of which 3 are non-colinear.\n' '{}'.format(e) ) from e im = _cv2().warpPerspective(image, matrix, (image.shape[1], image.shape[0])) node_context.output['result'].set_image(im)