# 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.
from sympathy.api import node
from sympathy.api.nodeconfig import Port, Ports, Tag, Tags
from skimage import io
from sylib.imageprocessing.image import ImagePort
import numpy as np
import os
_image_conv_related_nodeids = [
'syip.convert_image_to_table',
'syip.convert_table_to_image',
'syip.convert_image_to_table_2d',
'syip.convert_table2d_to_image',
]
[docs]
class SaveImage(node.Node):
"""
Saves an image to a datasource in the given file format.
"""
name = 'Save Image'
author = 'Mathias Broxvall'
icon = 'image_save.svg'
description = 'Saves an image to a datasource in the given file format'
nodeid = 'syip.saveimage'
tags = Tags(Tag.ImageProcessing.IO)
file_formats = ['png', 'gif']
parameters = node.parameters()
parameters.set_string(
'File format', value='png',
description=('Desired file format of saved file '
'(.png or .gif currently available)'),
editor=node.editors.combo_editor(options=file_formats))
inputs = Ports([
ImagePort('Input image', name='image'),
Port.Datasource(
'File name before appending file type extension (eg: .png)',
name='dest')])
outputs = Ports([
Port.Datasource(
'File name after appending file type extension (eg: .png)',
name='out')])
def execute(self, node_context):
image = node_context.input['image'].get_image()
dest = node_context.input['dest']
if dest.decode_type() != 'FILE':
raise NotImplementedError(
'Image saving must be done to file names.')
if len(image.shape) > 2 and image.shape[2] not in [1, 3, 4]:
raise NotImplementedError(
'Only images with 1, 3, or 4 channels can be exported.')
if not isinstance(image.dtype, np.uint8):
image = (image*255.0).astype(np.uint8)
if len(image.shape) > 2 and image.shape[2] == 1:
image = image.reshape(image.shape[:2])
file_format = node_context.parameters['File format'].value
dest_path = dest.decode_path()
try:
# Creating destination path if it does not exist.
os.makedirs(os.path.dirname(dest_path))
except Exception:
pass
extensions = {'png': '.png', 'gif': '.gif'}
extension = extensions[file_format]
if dest_path.endswith(extension):
full_path = dest_path
else:
full_path = dest_path + extension
node_context.output['out'].encode_path(full_path)
io.imsave(full_path, image)
[docs]
class ImageToTable(node.Node):
"""
Converts an image into a table with a single column.
"""
name = 'Convert Image to Table'
author = 'Mathias Broxvall'
icon = 'image_to_table.svg'
description = (
'Converts an image into a table with a single column per channel or '
'for the whole image')
nodeid = 'syip.convert_image_to_table'
tags = Tags(Tag.ImageProcessing.IO)
related = _image_conv_related_nodeids
parameters = node.parameters()
parameters.set_string(
'Column name', value='image', label="Column name",
description=(
'Name of column which image data is converted into, '
'or prefix if multiple columns are generated'))
parameters.set_boolean(
'Multiple columns', value=False, label="Multiple columns",
description='Converts each channel of the image into separate column')
parameters.set_boolean(
'Coordinates', value=False, label="Coordinates",
description=(
'Adds a column for XY coordinates. Channel coordinate is '
'also added if multi-columns is false.'))
inputs = Ports([
ImagePort('Input image', name='image'),
])
outputs = Ports([
Port.Table('Table containing image data', name='data'),
Port.Table('Table containing image meta data', name='meta'),
])
def execute(self, node_context):
image = node_context.input['image'].get_image()
column_name = node_context.parameters['Column name'].value
multi_columns = node_context.parameters['Multiple columns'].value
gen_coordinates = node_context.parameters['Coordinates'].value
data_out = node_context.output['data']
meta_out = node_context.output['meta']
width = image.shape[1]
height = image.shape[0]
channels = 1 if len(image.shape) < 3 else image.shape[2]
meta_out.set_column_from_array('width', np.array([width]))
meta_out.set_column_from_array('height', np.array([height]))
meta_out.set_column_from_array('channels', np.array([channels]))
if gen_coordinates:
if multi_columns:
data_out.set_column_from_array(
'X',
np.meshgrid(np.arange(width),
np.arange(height))[0].flatten())
data_out.set_column_from_array(
'Y',
np.meshgrid(np.arange(width),
np.arange(height))[1].flatten())
else:
data_out.set_column_from_array(
'X',
np.meshgrid(np.arange(width),
np.arange(height),
np.arange(channels))[0].flatten())
data_out.set_column_from_array(
'Y',
np.meshgrid(np.arange(width),
np.arange(height),
np.arange(channels))[1].flatten())
data_out.set_column_from_array(
'Ch',
np.meshgrid(np.arange(width),
np.arange(height),
np.arange(channels))[2].flatten())
if multi_columns:
for ch in range(channels):
data_out.set_column_from_array(
'{}:{}'.format(column_name, ch),
image[:, :, ch].reshape(width * height))
else:
data_out.set_column_from_array(
column_name, image.reshape(width * height * channels))
[docs]
class ImageToTable2D(node.Node):
"""
Converts first channel of image into a 2D table with X along columns and
Y along rows.
"""
name = 'Convert Image to 2D Table'
author = 'Mathias Broxvall'
icon = 'image_to_table.svg'
description = (
'Converts first channel of image into a 2D table with X along columns '
'and Y along rows.')
nodeid = 'syip.convert_image_to_table_2d'
tags = Tags(Tag.ImageProcessing.IO)
related = _image_conv_related_nodeids
parameters = node.parameters()
inputs = Ports([
ImagePort('Input image', name='image'),
])
outputs = Ports([
Port.Table('Table containing image data', name='data'),
])
def execute(self, node_context):
image = node_context.input['image'].get_image()
data_out = node_context.output['data']
height = image.shape[1]
if len(image.shape) >= 3:
image = image[:, :, 0]
for col in range(height):
data_out.set_column_from_array(
'Y{}'.format(col), image[:, col])
[docs]
class TableToImage(node.Node):
"""
Converts a table into an image using a column each for: data, image width,
image height and image channels.
"""
name = 'Convert Table to Image'
author = 'Mathias Broxvall'
icon = 'image_from_table.svg'
description = ('Converts a table into an image, using columns for data, '
'image width, image height and image channels.')
nodeid = 'syip.convert_table_to_image'
tags = Tags(Tag.ImageProcessing.IO)
related = _image_conv_related_nodeids
parameters = node.parameters()
parameters.set_string(
'Data column', label='Data column name', value='image',
description='Name of image data column in input table')
parameters.set_string(
'Width column', label='Width column name', value='width',
description='Name of image width column in input table')
parameters.set_string(
'Height column', label='Height column name', value='height',
description='Name of image height column in input table')
parameters.set_string(
'Channels column', label='Channels column name',
value='channels',
description='Name of image channels column in input table')
inputs = Ports([
Port.Table('Table containing image data', name='data'),
Port.Table('Table containing image meta data', name='meta'),
])
outputs = Ports([
ImagePort('Input image', name='image'),
])
def execute(self, node_context):
data = node_context.input['data'].get_column_to_array(
node_context.parameters['Data column'].value)
width = node_context.input['meta'].get_column_to_array(
node_context.parameters['Width column'].value)[0]
height = node_context.input['meta'].get_column_to_array(
node_context.parameters['Height column'].value)[0]
channels = node_context.input['meta'].get_column_to_array(
node_context.parameters['Channels column'].value)[0]
im = data.reshape((height, width, channels))
node_context.output['image'].set_image(im)
[docs]
class Table2DToImage(node.Node):
"""
Converts a 2D table into a grayscale image, using columns as X and rows
as Y positions.
"""
name = 'Convert 2D Table to Image'
author = 'Mathias Broxvall'
icon = 'image_from_table.svg'
description = (
'Converts a 2D table into a grayscale image, using columns as X and '
'rows as Y positions.')
nodeid = 'syip.convert_table2d_to_image'
tags = Tags(Tag.ImageProcessing.IO)
related = _image_conv_related_nodeids
parameters = node.parameters()
inputs = Ports([
Port.Table('Table containing image data', name='data'),
])
outputs = Ports([
ImagePort('Input image', name='image'),
])
def execute(self, node_context):
tbl = node_context.input['data']
data = np.array(
[tbl.get_column_to_array(colname)
for colname in tbl.column_names()]).T
node_context.output['image'].set_image(data)