diff --git a/hyppopy/tests/data/functionsimulator/axis.cfg b/hyppopy/tests/data/functionsimulator/axis.cfg new file mode 100644 index 0000000..46936d8 --- /dev/null +++ b/hyppopy/tests/data/functionsimulator/axis.cfg @@ -0,0 +1,29 @@ +[axis_00] +min_x:0 +max_x:1 +min_y:0 +max_y:5 + +[axis_01] +min_x:-10 +max_x:10 +min_y:-10 +max_y:10 + +[axis_02] +min_x:0 +max_x:20 +min_y:0 +max_y:20 + +[axis_03] +min_x:-30 +max_x:5 +min_y:-10 +max_y:-2 + +[axis_04] +min_x:5 +max_x:10 +min_y:2 +max_y:4 \ No newline at end of file diff --git a/hyppopy/tests/data/functionsimulator/plot_01.png b/hyppopy/tests/data/functionsimulator/plot_01.png new file mode 100644 index 0000000..1d53765 Binary files /dev/null and b/hyppopy/tests/data/functionsimulator/plot_01.png differ diff --git a/hyppopy/tests/data/functionsimulator/plot_02.png b/hyppopy/tests/data/functionsimulator/plot_02.png new file mode 100644 index 0000000..d534919 Binary files /dev/null and b/hyppopy/tests/data/functionsimulator/plot_02.png differ diff --git a/hyppopy/tests/data/functionsimulator/plot_03.png b/hyppopy/tests/data/functionsimulator/plot_03.png new file mode 100644 index 0000000..f3fc6e8 Binary files /dev/null and b/hyppopy/tests/data/functionsimulator/plot_03.png differ diff --git a/hyppopy/tests/data/functionsimulator/plot_04.png b/hyppopy/tests/data/functionsimulator/plot_04.png new file mode 100644 index 0000000..dace67e Binary files /dev/null and b/hyppopy/tests/data/functionsimulator/plot_04.png differ diff --git a/hyppopy/tests/data/functionsimulator/plot_05.png b/hyppopy/tests/data/functionsimulator/plot_05.png new file mode 100644 index 0000000..cf2508c Binary files /dev/null and b/hyppopy/tests/data/functionsimulator/plot_05.png differ diff --git a/hyppopy/tests/test_virtualfunction.py b/hyppopy/tests/test_virtualfunction.py new file mode 100644 index 0000000..ece1d39 --- /dev/null +++ b/hyppopy/tests/test_virtualfunction.py @@ -0,0 +1,117 @@ +# DKFZ +# +# +# Copyright (c) German Cancer Research Center, +# Division of Medical and Biological Informatics. +# All rights reserved. +# +# This software is distributed WITHOUT ANY WARRANTY; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. +# +# See LICENSE.txt or http://www.mitk.org for details. +# +# Author: Sven Wanner (s.wanner@dkfz.de) + +import os +import unittest +import numpy as np + +from hyppopy.virtualfunction import VirtualFunction +from hyppopy.globals import TESTDATA_DIR + + +class VirtualFunctionTestSuite(unittest.TestCase): + + def setUp(self): + pass + + def test_imagereading(self): + vfunc = VirtualFunction() + vfunc.load_images(os.path.join(TESTDATA_DIR, 'functionsimulator')) + self.assertTrue(isinstance(vfunc.data, np.ndarray)) + self.assertEqual(vfunc.data.shape[0], 5) + self.assertEqual(vfunc.data.shape[1], 512) + gt = [0.83984375*5, 0.44140625*20-10, 0.25390625*20, 0.81640625*8-10, 0.67578125*2+2] + for i in range(5): + self.assertAlmostEqual(vfunc.data[i][0], gt[i]) + gt = [[0, 1], [-10, 10], [0, 20], [-30, 5], [5, 10]] + for i in range(5): + self.assertEqual(vfunc.axis[i][0], gt[i][0]) + self.assertEqual(vfunc.axis[i][1], gt[i][1]) + + def test_data_adding(self): + gt = [[-10, 10], [-30, 5]] + vfunc = VirtualFunction() + dim0 = np.arange(0, 1.1, 0.1) + dim1 = np.arange(1.0, -0.1, -0.1) + vfunc.add_dimension(dim0, gt[0]) + self.assertEqual(len(vfunc.data.shape), 2) + self.assertEqual(vfunc.data.shape[0], 1) + self.assertEqual(vfunc.data.shape[1], 11) + vfunc.add_dimension(dim1, gt[1]) + self.assertEqual(vfunc.data.shape[0], 2) + self.assertEqual(vfunc.data.shape[1], 11) + for n in range(11): + self.assertAlmostEqual(dim0[n], vfunc.data[0, n]) + self.assertAlmostEqual(dim1[n], vfunc.data[1, n]) + for i in range(2): + self.assertEqual(vfunc.axis[i][0], gt[i][0]) + self.assertEqual(vfunc.axis[i][1], gt[i][1]) + + def test_sampling(self): + vfunc = VirtualFunction() + vfunc.load_images(os.path.join(TESTDATA_DIR, 'functionsimulator')) + ranges = [[0, 1], [-10, 10], [0, 20], [-30, 5], [5, 10]] + x_ranges = [] + for r in ranges: + dr = (r[1]-r[0])/512.0 + x_ranges.append(np.arange(r[0], r[1], dr)) + data = [[], [], [], [], []] + for n in range(x_ranges[0].shape[0]): + x = [x_ranges[0][n], x_ranges[1][n], x_ranges[2][n], x_ranges[3][n], x_ranges[4][n]] + f = vfunc(*x) + for i in range(5): + data[i].append(f[i]) + + sum = 0 + for i in range(512): + for n in range(5): + sum += vfunc.data[n][i]-data[n][i] + self.assertTrue(sum < 18) + + def test_minima(self): + vfunc = VirtualFunction() + vfunc.load_images(os.path.join(TESTDATA_DIR, 'functionsimulator')) + minima = vfunc.minima() + + gt = [[[0.7265625], 0.48828125], [[-4.0234375], -7.890625], [[2.265625], 0.859375], [ + [-17.421875, -17.353515625, -17.28515625, -17.216796875, -17.1484375, -17.080078125, -17.01171875, + -16.943359375, -16.875, -16.806640625, -16.73828125, -16.669921875, -16.6015625, -16.533203125, + -16.46484375, -16.396484375, -16.328125, -16.259765625, -16.19140625, -16.123046875, -16.0546875, + -15.986328125, -15.91796875, -15.849609375, -15.78125, -15.712890625, -15.64453125, -15.576171875, + -15.5078125, -15.439453125, -15.37109375, -15.302734375, -15.234375, -15.166015625, -15.09765625, + -15.029296875, -14.9609375, -14.892578125, -14.82421875, -14.755859375, -14.6875, -14.619140625, + -14.55078125, -14.482421875, -14.4140625, -14.345703125, -14.27734375, -14.208984375, -14.140625, + -14.072265625, -14.00390625, -13.935546875, -13.8671875, -13.798828125, -13.73046875, -13.662109375, + -13.59375, -13.525390625, -13.45703125, -13.388671875, -13.3203125, -13.251953125, -13.18359375, + -13.115234375, -13.046875, -12.978515625, -12.91015625, -12.841796875, -12.7734375, -12.705078125, + -12.63671875, -12.568359375, -12.5, -12.431640625, -12.36328125, -12.294921875, -12.2265625, -12.158203125, + -12.08984375, -12.021484375, -11.953125, -11.884765625, -11.81640625, -11.748046875, -11.6796875, + -11.611328125, -11.54296875, -11.474609375, -11.40625, -11.337890625, -11.26953125, -11.201171875, + -11.1328125, -11.064453125, -10.99609375, -10.927734375, -10.859375, -10.791015625, -10.72265625, + -10.654296875, -10.5859375, -10.517578125, -10.44921875, -10.380859375, -10.3125, -10.244140625, + -10.17578125, -10.107421875, -10.0390625, -9.970703125, -9.90234375, -9.833984375, -9.765625, -9.697265625, + -9.62890625, -9.560546875, -9.4921875, -9.423828125, -9.35546875, -9.287109375, -9.21875, -9.150390625, + -9.08203125, -9.013671875, -8.9453125, -8.876953125, -8.80859375, -8.740234375, -8.671875, -8.603515625, + -8.53515625, -8.466796875, -8.3984375, -8.330078125, -8.26171875, -8.193359375, -8.125, -8.056640625, + -7.98828125, -7.919921875, -7.8515625, -7.783203125, -7.71484375, -7.646484375, -7.578125, -7.509765625, + -7.44140625, -7.373046875, -7.3046875, -7.236328125, -7.16796875, -7.099609375, -7.03125], -9.125], + [[5.44921875, 5.458984375, 5.46875, 5.478515625, 5.48828125, 5.498046875, 5.5078125, 5.517578125, 5.52734375], + 2.09375]] + + self.assertAlmostEqual(minima, gt) + + +if __name__ == '__main__': + unittest.main() diff --git a/hyppopy/virtualfunction.py b/hyppopy/virtualfunction.py new file mode 100644 index 0000000..0842503 --- /dev/null +++ b/hyppopy/virtualfunction.py @@ -0,0 +1,175 @@ +import os +import sys +import numpy as np +import configparser +from glob import glob +import matplotlib.pyplot as plt +import matplotlib.image as mpimg + + +class VirtualFunction(object): + + def __init__(self): + self.config = None + self.data = None + self.axis = [] + + def __call__(self, *args): + assert len(args) == self.dims(), "wrong number of arguments!" + for i in range(len(args)): + assert self.axis[i][0] <= args[i] <= self.axis[i][1], "out of range access on axis {}!".format(i) + lpos, rpos, fracs = self.pos_to_indices(args) + fl = self.data[(list(range(self.dims())), lpos)] + fr = self.data[(list(range(self.dims())), rpos)] + return fl*np.array(fracs) + fr*(1-np.array(fracs)) + + def clear(self): + self.axis.clear() + self.data = None + self.config = None + + def dims(self): + return self.data.shape[0] + + def size(self): + return self.data.shape[1] + + def minima(self): + glob_mins = [] + for dim in range(self.dims()): + x = [] + fmin = np.min(self.data[dim, :]) + for _x in range(self.size()): + if self.data[dim, _x] <= fmin: + x.append(_x/self.size()*(self.axis[dim][1]-self.axis[dim][0])+self.axis[dim][0]) + glob_mins.append([x, fmin]) + return glob_mins + + def pos_to_indices(self, positions): + lpos = [] + rpos = [] + pfracs = [] + for n in range(self.dims()): + pos = positions[n] + pos -= self.axis[n][0] + pos /= np.abs(self.axis[n][1]-self.axis[n][0]) + pos *= self.data.shape[1]-1 + lp = int(np.floor(pos)) + if lp < 0: + lp = 0 + rp = int(np.ceil(pos)) + if rp > self.data.shape[1]-1: + rp = self.data.shape[1]-1 + pfracs.append(1.0-(pos-np.floor(pos))) + lpos.append(lp) + rpos.append(rp) + return lpos, rpos, pfracs + + def plot(self, dim=None, title=""): + if dim is None: + dim = list(range(self.dims())) + else: + dim = [dim] + fig = plt.figure(figsize=(10, 8)) + for i in range(len(dim)): + width = np.abs(self.axis[dim[i]][1]-self.axis[dim[i]][0]) + ax = np.arange(self.axis[dim[i]][0], self.axis[dim[i]][1], width/self.size()) + plt.plot(ax, self.data[dim[i], :], '.', label='axis_{}'.format(str(dim[i]).zfill(2))) + plt.legend() + plt.grid() + plt.title(title) + plt.show() + + def add_dimension(self, data, x_range): + if self.data is None: + self.data = data + if len(self.data.shape) == 1: + self.data = self.data.reshape((1, self.data.shape[0])) + else: + if len(data.shape) == 1: + data = data.reshape((1, data.shape[0])) + assert self.data.shape[1] == data.shape[1], "shape mismatch while adding dimension!" + dims = self.data.shape[0] + size = self.data.shape[1] + tmp = np.append(self.data, data) + self.data = tmp.reshape((dims+1, size)) + self.axis.append(x_range) + + def load_images(self, path): + self.config = None + self.data = None + self.axis.clear() + img_fnames = [] + for f in glob(path + os.sep + "*"): + if f.endswith(".png"): + img_fnames.append(f) + elif f.endswith(".cfg"): + self.config = self.read_config(f) + else: + print("WARNING: files of type {} not supported, the file {} is ignored!".format(f.split(".")[-1], + os.path.basename(f))) + + if self.config is None: + print("Aborted, failed to read configfile!") + sys.exit() + sections = self.config.sections() + if len(sections) != len(img_fnames): + print("Aborted, inconsistent number of image tmplates and axis specifications!") + sys.exit() + img_fnames.sort() + size_x = None + size_y = None + for n, fname in enumerate(img_fnames): + img = mpimg.imread(fname) + if len(img.shape) > 2: + img = img[:, :, 0] + if size_x is None: + size_x = img.shape[1] + if size_y is None: + size_y = img.shape[0] + self.data = np.zeros((len(img_fnames), size_x), dtype=np.float32) + assert img.shape[0] == size_y, "Shape mismatch in dimension y {} is not {}".format(img.shape[0], size_y) + assert img.shape[1] == size_x, "Shape mismatch in dimension x {} is not {}".format(img.shape[1], size_x) + + self.sample_image(img, n) + + def sample_image(self, img, dim): + sec_name = "axis_{}".format(str(dim).zfill(2)) + assert sec_name in self.config.sections(), "config section {} not found!".format(sec_name) + settings = self.get_axis_settings(sec_name) + self.axis.append([float(settings['min_x']), float(settings['max_x'])]) + y_range = [float(settings['min_y']), float(settings['max_y'])] + + for x in range(img.shape[1]): + candidates = np.where(img[:, x] > 0) + assert len(candidates[0]) > 0, "non function value in image detected, ensure each column has at least one value > 0!" + + y_pos = candidates[0][0]/img.shape[0] + self.data[dim, x] = 1-y_pos + + self.data[dim, :] *= np.abs(y_range[1] - y_range[0]) + self.data[dim, :] += y_range[0] + + def read_config(self, fname): + try: + config = configparser.ConfigParser() + config.read(fname) + return config + except Exception as e: + print(e) + return None + + def get_axis_settings(self, section): + dict1 = {} + options = self.config.options(section) + for option in options: + try: + dict1[option] = self.config.get(section, option) + if dict1[option] == -1: + print("skip: %s" % option) + except: + print("exception on %s!" % option) + dict1[option] = None + return dict1 + + diff --git a/hyppopy/virtualparameterspace/axis.cfg b/hyppopy/virtualparameterspace/axis.cfg new file mode 100644 index 0000000..c997e14 --- /dev/null +++ b/hyppopy/virtualparameterspace/axis.cfg @@ -0,0 +1,47 @@ +[axis_00] +min_x:0 +max_x:1 +min_y:-1 +max_y:1 + +[axis_01] +min_x:0 +max_x:8000 +min_y:-310 +max_y:20 + +[axis_02] +min_x:-20 +max_x:20 +min_y:0 +max_y:20 + +[axis_03] +min_x:0 +max_x:5 +min_y:0 +max_y:2 + +[axis_04] +min_x:0 +max_x:10 +min_y:2 +max_y:4 + +[axis_05] +min_x:0 +max_x:10000 +min_y:0 +max_y:1 + +[axis_06] +min_x:-20000 +max_x:0 +min_y:0 +max_y:9000 + +[axis_07] +min_x:-1 +max_x:1 +min_y:0 +max_y:1 \ No newline at end of file diff --git a/hyppopy/virtualparameterspace/axis_00.png b/hyppopy/virtualparameterspace/axis_00.png new file mode 100644 index 0000000..b1b2674 Binary files /dev/null and b/hyppopy/virtualparameterspace/axis_00.png differ diff --git a/hyppopy/virtualparameterspace/axis_01.png b/hyppopy/virtualparameterspace/axis_01.png new file mode 100644 index 0000000..86de66b Binary files /dev/null and b/hyppopy/virtualparameterspace/axis_01.png differ diff --git a/hyppopy/virtualparameterspace/axis_02.png b/hyppopy/virtualparameterspace/axis_02.png new file mode 100644 index 0000000..a6c8551 Binary files /dev/null and b/hyppopy/virtualparameterspace/axis_02.png differ diff --git a/hyppopy/virtualparameterspace/axis_03.png b/hyppopy/virtualparameterspace/axis_03.png new file mode 100644 index 0000000..25e5474 Binary files /dev/null and b/hyppopy/virtualparameterspace/axis_03.png differ diff --git a/hyppopy/virtualparameterspace/axis_04.png b/hyppopy/virtualparameterspace/axis_04.png new file mode 100644 index 0000000..9c71285 Binary files /dev/null and b/hyppopy/virtualparameterspace/axis_04.png differ diff --git a/hyppopy/virtualparameterspace/axis_05.png b/hyppopy/virtualparameterspace/axis_05.png new file mode 100644 index 0000000..fc7b934 Binary files /dev/null and b/hyppopy/virtualparameterspace/axis_05.png differ diff --git a/hyppopy/virtualparameterspace/axis_06.png b/hyppopy/virtualparameterspace/axis_06.png new file mode 100644 index 0000000..4910a3c Binary files /dev/null and b/hyppopy/virtualparameterspace/axis_06.png differ diff --git a/hyppopy/virtualparameterspace/axis_07.png b/hyppopy/virtualparameterspace/axis_07.png new file mode 100644 index 0000000..ba8a376 Binary files /dev/null and b/hyppopy/virtualparameterspace/axis_07.png differ