From c82ac3a838a89e1b930507d29ecfd33d525f79f5 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Thu, 14 Jun 2018 17:10:39 -0700 Subject: [PATCH 01/46] added base64 encoding/decoding --- renderapi/utils.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/renderapi/utils.py b/renderapi/utils.py index 756d49cf..ca33a85c 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -7,6 +7,9 @@ import inspect import copy import json +import base64 +import zlib +import bitstring import numpy import requests @@ -358,3 +361,75 @@ def fitargspec(f, oldargs, oldkwargs): logger.error('Cannot fit argspec for {}'.format(f)) logger.error(e) return oldargs, oldkwargs + + +def encodeBase64(src): + """encode an array or list of doubles + in Base64 binary-to-text encoding + same as in trakem2...ThinPlateSplineTransform.java + + Parameters + ---------- + src : array or list + floating point values to be encoded + + Returns + ------- + encoded: string + """ + s = '' + for ix in src: + bits = bitstring.BitArray(float=ix, length=64).uint + s += chr(bits >> 56) + s += chr(bits >> 48 & 0xffL) + s += chr(bits >> 40 & 0xffL) + s += chr(bits >> 32 & 0xffL) + s += chr(bits >> 24 & 0xffL) + s += chr(bits >> 16 & 0xffL) + s += chr(bits >> 8 & 0xffL) + s += chr(bits & 0xffL) + zs = zlib.compress(s) + encoded = base64.b64encode(zs) + return encoded + + +def decodeBase64(src, n): + """decode a string + encoded in base64 binary-to-text encoding + same as in trakem2...ThinPlateSplineTransform.java + + Parameters + ---------- + src : string + encoded string + n : int + number of values to decode + + Returns + ------- + arr: list of double-precision floats + """ + zipped = base64.b64decode(src) + bvalues = zlib.decompress(zipped) + arr = [] + j = 0 + for i in range(n): + bits = 0L + bits += (ord(bvalues[j]) & 0xffL) << 56 + j += 1 + bits += (ord(bvalues[j]) & 0xffL) << 48 + j += 1 + bits += (ord(bvalues[j]) & 0xffL) << 40 + j += 1 + bits += (ord(bvalues[j]) & 0xffL) << 32 + j += 1 + bits += (ord(bvalues[j]) & 0xffL) << 24 + j += 1 + bits += (ord(bvalues[j]) & 0xffL) << 16 + j += 1 + bits += (ord(bvalues[j]) & 0xffL) << 8 + j += 1 + bits += ord(bvalues[j]) & 0xffL + j += 1 + arr.append(bitstring.BitArray(uint=bits, length=64).float) + return arr From 9e9345bc8da179bbe8c0d6a1a4ab59af604f6fff Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Thu, 14 Jun 2018 17:42:27 -0700 Subject: [PATCH 02/46] starting framework for ThinPlateSplineTransform --- renderapi/transform.py | 62 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 002bd299..74747ed4 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -11,7 +11,7 @@ import numpy as np from .errors import ConversionError, EstimationError, RenderError -from .utils import NullHandler +from .utils import NullHandler, encodeBase64, decodeBase64 logger = logging.getLogger(__name__) logger.addHandler(NullHandler()) @@ -1626,6 +1626,66 @@ def dataString(self): shapestring, betastring, meanstring, varstring, dimstring) +class ThinPlateSplineTransform(Transform): + """ + render-python class that implements the + mpicbg.trakem2.transform.ThinPlateSplineTransform class + Parameters + ---------- + dataString: str or None + data string of transformation + labels : list of str + list of labels to give this transform + json: dict or None + json compatible dictionary representation of the transformation + Returns + ------- + :class:`ThinPlateSplineTransform` + a transform instance + """ + + className = 'mpicbg.trakem2.transform.ThinPlateSplineTransform' + + def __init__(self, dataString=None, json=None, transformId=None, + labels=None): + if json is not None: + self.from_dict(json) + else: + if dataString is not None: + self._process_dataString(dataString) + if labels is not None: + self.labels = labels + self.transformId = transformId + self.className = ( + 'mpicbg.trakem2.transform.ThinPlateSplineTransform') + + def _process_dataString(self, dataString): + + fields = dataString.split(" ") + + self.ndims = int(fields[0]) + self.nLm = int(fields[1]) + + values = decodeBase64(fields[2], self.ndims*self.ndims + self.ndims) + self.aMtx = values[0:self.ndims*self.ndims].reshape( + self.ndims, self.ndims) + self.bVec = values[self.ndims*self.ndims:] + + values = decodeBase64(fields[3], 2*self.ndims*self.nLm) + self.srcPts = values[0:self.ndims*self.nLm].reshape( + self.ndims, self.nLm) + self.dMtxDat = values[self.ndims*self.nLm:] + + @property + def dataString(self): + header = '{} {}'.format(self.ndims, self.nLm) + blk1 = np.concatenate((self.aMtx.flatten(), self.bVec)) + b64_1 = encodeBase64(blk1) + blk2 = np.concatenate((self.srcPts.flatten(), self.dMtxDat)) + b64_2 = encodeBase64(blk2) + return '{} {} {}'.format(header, b64_1, b64_2) + + class NonLinearTransform(NonLinearCoordinateTransform): className = 'mpicbg.trakem2.transform.nonLinearTransform' From 7375c8f741b5be7c60b8c5430b958a2b60705e84 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 15 Jun 2018 08:20:24 -0700 Subject: [PATCH 03/46] updated --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1ec45f7d..b6d9dae5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ numpy pillow sphinxcontrib-napoleon decorator -six \ No newline at end of file +six +bitstring From f99a43cc016591e660a0244416c1e7d3566d040c Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 15 Jun 2018 08:27:34 -0700 Subject: [PATCH 04/46] changin encode/decode to deal only with numpy arrays --- renderapi/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index ca33a85c..7882a9ce 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -370,7 +370,7 @@ def encodeBase64(src): Parameters ---------- - src : array or list + src : 1D numpy array floating point values to be encoded Returns @@ -407,7 +407,7 @@ def decodeBase64(src, n): Returns ------- - arr: list of double-precision floats + arr: length n numpy array of double-precision floats """ zipped = base64.b64decode(src) bvalues = zlib.decompress(zipped) @@ -432,4 +432,4 @@ def decodeBase64(src, n): bits += ord(bvalues[j]) & 0xffL j += 1 arr.append(bitstring.BitArray(uint=bits, length=64).float) - return arr + return np.array(arr) From a56141526d814eb9ba498fc089de77c75bf1858f Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 15 Jun 2018 09:22:01 -0700 Subject: [PATCH 05/46] adding from_dict method to thin_plate_spline --- renderapi/transform.py | 8 ++ test/test_files/thin_plate_spline.json | 192 +++++++++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 test/test_files/thin_plate_spline.json diff --git a/renderapi/transform.py b/renderapi/transform.py index 74747ed4..2a76c482 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1659,6 +1659,14 @@ def __init__(self, dataString=None, json=None, transformId=None, self.className = ( 'mpicbg.trakem2.transform.ThinPlateSplineTransform') + def from_dict(self,json): + self.ndims = json['ndims'] + self.nLm = json['nLm'] + self.aMtx = np.array(json['aMtx']) + self.bVec = np.array(json['bVec']) + self.srcPts = np.array(json['srcPts']) + self.dMtxDat = np.array(json['dMtxDat']) + def _process_dataString(self, dataString): fields = dataString.split(" ") diff --git a/test/test_files/thin_plate_spline.json b/test/test_files/thin_plate_spline.json new file mode 100644 index 00000000..f321c69c --- /dev/null +++ b/test/test_files/thin_plate_spline.json @@ -0,0 +1,192 @@ +{ + "ndims":2, + "nLm":225, + "aMtx":[[1.0,0.0],[0.0,1.0]], + "bVec":[1.0,1.0], + "srcPts":[ + [0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, + 0.0, 274.3, 548.6, 822.9, 1097.1, + 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, + 2742.9, 3017.1, 3291.4, 3565.7, 3840.0], + [ 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 274.3, 274.3, 274.3, 274.3, 274.3, + 274.3, 274.3, 274.3, 274.3, 274.3, + 274.3, 274.3, 274.3, 274.3, 274.3, + 548.6, 548.6, 548.6, 548.6, 548.6, + 548.6, 548.6, 548.6, 548.6, 548.6, + 548.6, 548.6, 548.6, 548.6, 548.6, + 822.9, 822.9, 822.9, 822.9, 822.9, + 822.9, 822.9, 822.9, 822.9, 822.9, + 822.9, 822.9, 822.9, 822.9, 822.9, + 1097.1, 1097.1, 1097.1, 1097.1, 1097.1, + 1097.1, 1097.1, 1097.1, 1097.1, 1097.1, + 1097.1, 1097.1, 1097.1, 1097.1, 1097.1, + 1371.4, 1371.4, 1371.4, 1371.4, 1371.4, + 1371.4, 1371.4, 1371.4, 1371.4, 1371.4, + 1371.4, 1371.4, 1371.4, 1371.4, 1371.4, + 1645.7, 1645.7, 1645.7, 1645.7, 1645.7, + 1645.7, 1645.7, 1645.7, 1645.7, 1645.7, + 1645.7, 1645.7, 1645.7, 1645.7, 1645.7, + 1920.0, 1920.0, 1920.0, 1920.0, 1920.0, + 1920.0, 1920.0, 1920.0, 1920.0, 1920.0, + 1920.0, 1920.0, 1920.0, 1920.0, 1920.0, + 2194.3, 2194.3, 2194.3, 2194.3, 2194.3, + 2194.3, 2194.3, 2194.3, 2194.3, 2194.3, + 2194.3, 2194.3, 2194.3, 2194.3, 2194.3, + 2468.6, 2468.6, 2468.6, 2468.6, 2468.6, + 2468.6, 2468.6, 2468.6, 2468.6, 2468.6, + 2468.6, 2468.6, 2468.6, 2468.6, 2468.6, + 2742.9, 2742.9, 2742.9, 2742.9, 2742.9, + 2742.9, 2742.9, 2742.9, 2742.9, 2742.9, + 2742.9, 2742.9, 2742.9, 2742.9, 2742.9, + 3017.1, 3017.1, 3017.1, 3017.1, 3017.1, + 3017.1, 3017.1, 3017.1, 3017.1, 3017.1, + 3017.1, 3017.1, 3017.1, 3017.1, 3017.1, + 3291.4, 3291.4, 3291.4, 3291.4, 3291.4, + 3291.4, 3291.4, 3291.4, 3291.4, 3291.4, + 3291.4, 3291.4, 3291.4, 3291.4, 3291.4, + 3565.7, 3565.7, 3565.7, 3565.7, 3565.7, + 3565.7, 3565.7, 3565.7, 3565.7, 3565.7, + 3565.7, 3565.7, 3565.7, 3565.7, 3565.7, + 3840.0, 3840.0, 3840.0, 3840.0, 3840.0, + 3840.0, 3840.0, 3840.0, 3840.0, 3840.0, + 3840.0, 3840.0, 3840.0, 3840.0, 3840.0]], + "dMtxDat":[ + [ 1.0, 1.3, 1.6, 1.9, 2.2, + 2.6, 2.9, 3.3, 3.7, 4.1, + 4.5, 4.9, 5.4, 5.8, 6.3, + 0.7, 1.0, 1.3, 1.6, 2.0, + 2.3, 2.7, 3.1, 3.5, 3.9, + 4.3, 4.7, 5.2, 5.7, 6.1, + 0.4, 0.7, 1.0, 1.4, 1.7, + 2.1, 2.4, 2.8, 3.2, 3.6, + 4.1, 4.5, 5.0, 5.5, 5.9, + 0.1, 0.4, 0.7, 1.1, 1.4, + 1.8, 2.2, 2.6, 3.0, 3.4, + 3.8, 4.3, 4.8, 5.2, 5.7, + -0.2, 0.1, 0.4, 0.8, 1.1, + 1.5, 1.9, 2.3, 2.7, 3.1, + 3.6, 4.0, 4.5, 5.0, 5.5, + -0.6, -0.2, 0.1, 0.4, 0.8, + 1.2, 1.6, 2.0, 2.4, 2.9, + 3.3, 3.8, 4.3, 4.8, 5.3, + -0.9, -0.6, -0.2, 0.1, 0.5, + 0.9, 1.3, 1.7, 2.1, 2.6, + 3.0, 3.5, 4.0, 4.5, 5.0, + -1.3, -1.0, -0.6, -0.2, 0.1, + 0.5, 0.9, 1.4, 1.8, 2.3, + 2.7, 3.2, 3.7, 4.2, 4.8, + -1.7, -1.3, -1.0, -0.6, -0.2, + 0.2, 0.6, 1.0, 1.5, 1.9, + 2.4, 2.9, 3.4, 3.9, 4.5, + -2.1, -1.7, -1.4, -1.0, -0.6, + -0.2, 0.2, 0.7, 1.1, 1.6, + 2.1, 2.6, 3.1, 3.6, 4.2, + -2.5, -2.1, -1.8, -1.4, -1.0, + -0.6, -0.1, 0.3, 0.8, 1.3, + 1.8, 2.3, 2.8, 3.3, 3.9, + -2.9, -2.6, -2.2, -1.8, -1.4, + -1.0, -0.5, -0.1, 0.4, 0.9, + 1.4, 1.9, 2.4, 3.0, 3.5, + -3.4, -3.0, -2.6, -2.2, -1.8, + -1.4, -0.9, -0.5, 0.0, 0.5, + 1.0, 1.5, 2.1, 2.6, 3.2, + -3.8, -3.5, -3.1, -2.7, -2.2, + -1.8, -1.3, -0.9, -0.4, 0.1, + 0.6, 1.2, 1.7, 2.3, 2.8, + -4.3, -3.9, -3.5, -3.1, -2.7, + -2.2, -1.8, -1.3, -0.8, -0.3, + 0.2, 0.8, 1.3, 1.9, 2.5], + [ 1.5, 1.0, 0.4, -0.1, -0.6, + -1.1, -1.5, -2.0, -2.4, -2.8, + -3.2, -3.6, -4.0, -4.4, -4.7, + 1.2, 0.7, 0.2, -0.3, -0.8, + -1.3, -1.7, -2.2, -2.6, -3.0, + -3.4, -3.8, -4.1, -4.5, -4.8, + 0.9, 0.4, -0.1, -0.6, -1.1, + -1.5, -2.0, -2.4, -2.8, -3.2, + -3.6, -3.9, -4.3, -4.6, -4.9, + 0.5, -0.0, -0.5, -1.0, -1.4, + -1.9, -2.3, -2.7, -3.1, -3.4, + -3.8, -4.2, -4.5, -4.8, -5.1, + 0.0, -0.4, -0.9, -1.4, -1.8, + -2.2, -2.6, -3.0, -3.4, -3.7, + -4.1, -4.4, -4.7, -5.0, -5.3, + -0.4, -0.9, -1.4, -1.8, -2.2, + -2.6, -3.0, -3.4, -3.7, -4.1, + -4.4, -4.7, -5.0, -5.3, -5.6, + -1.0, -1.4, -1.8, -2.3, -2.7, + -3.1, -3.4, -3.8, -4.1, -4.5, + -4.8, -5.1, -5.4, -5.6, -5.9, + -1.5, -2.0, -2.4, -2.8, -3.2, + -3.6, -3.9, -4.3, -4.6, -4.9, + -5.2, -5.5, -5.8, -6.0, -6.3, + -2.1, -2.6, -3.0, -3.4, -3.7, + -4.1, -4.4, -4.8, -5.1, -5.4, + -5.7, -5.9, -6.2, -6.4, -6.7, + -2.8, -3.2, -3.6, -4.0, -4.3, + -4.7, -5.0, -5.3, -5.6, -5.9, + -6.2, -6.4, -6.7, -6.9, -7.1, + -3.5, -3.9, -4.3, -4.6, -5.0, + -5.3, -5.6, -5.9, -6.2, -6.5, + -6.7, -7.0, -7.2, -7.4, -7.6, + -4.2, -4.6, -5.0, -5.3, -5.7, + -6.0, -6.3, -6.6, -6.8, -7.1, + -7.3, -7.6, -7.8, -8.0, -8.1, + -5.0, -5.4, -5.7, -6.1, -6.4, + -6.7, -7.0, -7.2, -7.5, -7.7, + -8.0, -8.2, -8.4, -8.6, -8.7, + -5.9, -6.2, -6.6, -6.9, -7.2, + -7.5, -7.7, -8.0, -8.2, -8.4, + -8.7, -8.9, -9.0, -9.2, -9.3, + -6.8, -7.1, -7.4, -7.7, -8.0, + -8.3, -8.5, -8.8, -9.0, -9.2, + -9.4, -9.6, -9.7, -9.9, -10.0]] +} + + + + From f0890a8e2ee6d872322e99c465bb770186653728 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 15 Jun 2018 09:25:25 -0700 Subject: [PATCH 06/46] unflattening matrix --- renderapi/transform.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 2a76c482..860dba44 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1682,14 +1682,15 @@ def _process_dataString(self, dataString): values = decodeBase64(fields[3], 2*self.ndims*self.nLm) self.srcPts = values[0:self.ndims*self.nLm].reshape( self.ndims, self.nLm) - self.dMtxDat = values[self.ndims*self.nLm:] + self.dMtxDat = values[self.ndims*self.nLm:].reshape( + self.ndims, self.nLm) @property def dataString(self): header = '{} {}'.format(self.ndims, self.nLm) blk1 = np.concatenate((self.aMtx.flatten(), self.bVec)) b64_1 = encodeBase64(blk1) - blk2 = np.concatenate((self.srcPts.flatten(), self.dMtxDat)) + blk2 = np.concatenate((self.srcPts.flatten(), self.dMtxDat.flatten())) b64_2 = encodeBase64(blk2) return '{} {} {}'.format(header, b64_1, b64_2) From a21273c2d3c4b7beb128cf226794d9790e5a9464 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 15 Jun 2018 09:27:29 -0700 Subject: [PATCH 07/46] fixing np -> numpy --- renderapi/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index 7882a9ce..d10e49f5 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -432,4 +432,4 @@ def decodeBase64(src, n): bits += ord(bvalues[j]) & 0xffL j += 1 arr.append(bitstring.BitArray(uint=bits, length=64).float) - return np.array(arr) + return numpy.array(arr) From 1f2dc8b0257aee3cb54e68e1e0ae0ee261deb96f Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 15 Jun 2018 09:33:52 -0700 Subject: [PATCH 08/46] adding to_dict to thin plate spline --- renderapi/transform.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/renderapi/transform.py b/renderapi/transform.py index 860dba44..91a672cc 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1667,6 +1667,16 @@ def from_dict(self,json): self.srcPts = np.array(json['srcPts']) self.dMtxDat = np.array(json['dMtxDat']) + def to_dict(self): + j={} + j['ndims'] = self.ndims + j['nLm'] = self.nLm + j['aMtx'] = self.aMtx.tolist() + j['bVec'] = self.bVec.tolist() + j['srcPts'] = self.srcPts.tolist() + j['dMtxDat'] = self.dMtxDat.tolist() + return j + def _process_dataString(self, dataString): fields = dataString.split(" ") From 735121d7156f46588848dee41de18ed4e23c4030 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 15 Jun 2018 09:57:58 -0700 Subject: [PATCH 09/46] apply flake8 --- renderapi/transform.py | 4 ++-- renderapi/utils.py | 2 +- test/rendersettings.py | 3 +++ test/test_transform.py | 10 ++++++++++ 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 91a672cc..56fb7a0d 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1659,7 +1659,7 @@ def __init__(self, dataString=None, json=None, transformId=None, self.className = ( 'mpicbg.trakem2.transform.ThinPlateSplineTransform') - def from_dict(self,json): + def from_dict(self, json): self.ndims = json['ndims'] self.nLm = json['nLm'] self.aMtx = np.array(json['aMtx']) @@ -1668,7 +1668,7 @@ def from_dict(self,json): self.dMtxDat = np.array(json['dMtxDat']) def to_dict(self): - j={} + j = {} j['ndims'] = self.ndims j['nLm'] = self.nLm j['aMtx'] = self.aMtx.tolist() diff --git a/renderapi/utils.py b/renderapi/utils.py index d10e49f5..27495485 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -370,7 +370,7 @@ def encodeBase64(src): Parameters ---------- - src : 1D numpy array + src : 1D numpy array floating point values to be encoded Returns diff --git a/test/rendersettings.py b/test/rendersettings.py index c96520f5..613104af 100644 --- a/test/rendersettings.py +++ b/test/rendersettings.py @@ -33,6 +33,9 @@ TEST_TILESPECS_FILE = os.path.join(TEST_FILES_DIR, 'tilespecs.json') +TEST_THINPLATESPLINE_FILE = os.path.join(TEST_FILES_DIR, + 'thin_plate_spline.json') + INTERPOLATED_TRANSFORM_TILESPEC = os.path.join( TEST_FILES_DIR, 'tilespec_interpolated.json') diff --git a/test/test_transform.py b/test/test_transform.py index 0b2cae58..b3152fce 100644 --- a/test/test_transform.py +++ b/test/test_transform.py @@ -600,3 +600,13 @@ def test_similarity_init(): assert(isinstance(tform, renderapi.transform.SimilarityModel)) assert(np.abs(tform.M[0, 0]-2) < EPSILON) assert(tform.M[0, 2] == 10) + + +def test_thinplatespline(): + j1 = json.load(open(rendersettings.TEST_THINPLATESPLINE_FILE, 'r')) + t1 = renderapi.transform.ThinPlateSplineTransform(json=j1) + t2 = renderapi.transform.ThinPlateSplineTransform(dataString=t1.dataString) + assert(t1 == t2) + j2 = t2.to_dict() + t3 = renderapi.transform.ThinPlateSplineTransform(json=j2) + assert(t1 == t3) From 1340747cc35ef8884b76a3a89811159ee456302c Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 15 Jun 2018 12:47:57 -0700 Subject: [PATCH 10/46] commenting out thinplate split to_dict --- renderapi/transform.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 56fb7a0d..6bf553ef 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1667,15 +1667,15 @@ def from_dict(self, json): self.srcPts = np.array(json['srcPts']) self.dMtxDat = np.array(json['dMtxDat']) - def to_dict(self): - j = {} - j['ndims'] = self.ndims - j['nLm'] = self.nLm - j['aMtx'] = self.aMtx.tolist() - j['bVec'] = self.bVec.tolist() - j['srcPts'] = self.srcPts.tolist() - j['dMtxDat'] = self.dMtxDat.tolist() - return j + #def to_dict(self): + # j = {} + # j['ndims'] = self.ndims + # j['nLm'] = self.nLm + # j['aMtx'] = self.aMtx.tolist() + # j['bVec'] = self.bVec.tolist() + # j['srcPts'] = self.srcPts.tolist() + # j['dMtxDat'] = self.dMtxDat.tolist() + # return j def _process_dataString(self, dataString): From 664a753122b7b9cb4861b32f938074ecb8cd0bde Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 15 Jun 2018 13:17:38 -0700 Subject: [PATCH 11/46] adding whitespace to start of datastring --- renderapi/transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 6bf553ef..44eae2c9 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1702,7 +1702,7 @@ def dataString(self): b64_1 = encodeBase64(blk1) blk2 = np.concatenate((self.srcPts.flatten(), self.dMtxDat.flatten())) b64_2 = encodeBase64(blk2) - return '{} {} {}'.format(header, b64_1, b64_2) + return ' {} {} {}'.format(header, b64_1, b64_2) class NonLinearTransform(NonLinearCoordinateTransform): From 84ef98d2eff4b3466d26e04ec52fdf1cdbdc5263 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 15 Jun 2018 13:31:51 -0700 Subject: [PATCH 12/46] adding textto start dataString --- renderapi/transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 44eae2c9..bfd45913 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1702,7 +1702,7 @@ def dataString(self): b64_1 = encodeBase64(blk1) blk2 = np.concatenate((self.srcPts.flatten(), self.dMtxDat.flatten())) b64_2 = encodeBase64(blk2) - return ' {} {} {}'.format(header, b64_1, b64_2) + return 'ThinPlateSplineR2LogR {} {} {}'.format(header, b64_1, b64_2) class NonLinearTransform(NonLinearCoordinateTransform): From c2a423a09aeade25742f9714ebfcc8b656f9850e Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 12:59:52 -0700 Subject: [PATCH 13/46] commenting out _process dataString --- renderapi/transform.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index bfd45913..1d29c1a1 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1677,23 +1677,23 @@ def from_dict(self, json): # j['dMtxDat'] = self.dMtxDat.tolist() # return j - def _process_dataString(self, dataString): + #def _process_dataString(self, dataString): - fields = dataString.split(" ") + # fields = dataString.split(" ") - self.ndims = int(fields[0]) - self.nLm = int(fields[1]) + # self.ndims = int(fields[0]) + # self.nLm = int(fields[1]) - values = decodeBase64(fields[2], self.ndims*self.ndims + self.ndims) - self.aMtx = values[0:self.ndims*self.ndims].reshape( - self.ndims, self.ndims) - self.bVec = values[self.ndims*self.ndims:] + # values = decodeBase64(fields[2], self.ndims*self.ndims + self.ndims) + # self.aMtx = values[0:self.ndims*self.ndims].reshape( + # self.ndims, self.ndims) + # self.bVec = values[self.ndims*self.ndims:] - values = decodeBase64(fields[3], 2*self.ndims*self.nLm) - self.srcPts = values[0:self.ndims*self.nLm].reshape( - self.ndims, self.nLm) - self.dMtxDat = values[self.ndims*self.nLm:].reshape( - self.ndims, self.nLm) + # values = decodeBase64(fields[3], 2*self.ndims*self.nLm) + # self.srcPts = values[0:self.ndims*self.nLm].reshape( + # self.ndims, self.nLm) + # self.dMtxDat = values[self.ndims*self.nLm:].reshape( + # self.ndims, self.nLm) @property def dataString(self): From 65585a7f7cb4a90d39b12c943d3b02bea2b52072 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 13:04:53 -0700 Subject: [PATCH 14/46] modifying _process dataString --- renderapi/transform.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 1d29c1a1..dd1d7265 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1677,7 +1677,8 @@ def from_dict(self, json): # j['dMtxDat'] = self.dMtxDat.tolist() # return j - #def _process_dataString(self, dataString): + def _process_dataString(self, dataString): + self.dataString = datastring # fields = dataString.split(" ") From 39db053e9842a397bfd344c704e7affa42fca775 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 13:07:22 -0700 Subject: [PATCH 15/46] modifying _process dataString --- renderapi/transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index dd1d7265..54c74ec9 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1678,7 +1678,7 @@ def from_dict(self, json): # return j def _process_dataString(self, dataString): - self.dataString = datastring + self.dataString = dataString # fields = dataString.split(" ") From 84f7ae4bee2e6934aed2cf572ec1a2b1740405e4 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 13:10:31 -0700 Subject: [PATCH 16/46] modifying _process dataString --- renderapi/transform.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 54c74ec9..c94de4d1 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1659,13 +1659,13 @@ def __init__(self, dataString=None, json=None, transformId=None, self.className = ( 'mpicbg.trakem2.transform.ThinPlateSplineTransform') - def from_dict(self, json): - self.ndims = json['ndims'] - self.nLm = json['nLm'] - self.aMtx = np.array(json['aMtx']) - self.bVec = np.array(json['bVec']) - self.srcPts = np.array(json['srcPts']) - self.dMtxDat = np.array(json['dMtxDat']) + #def from_dict(self, json): + # self.ndims = json['ndims'] + # self.nLm = json['nLm'] + # self.aMtx = np.array(json['aMtx']) + # self.bVec = np.array(json['bVec']) + # self.srcPts = np.array(json['srcPts']) + # self.dMtxDat = np.array(json['dMtxDat']) #def to_dict(self): # j = {} @@ -1696,14 +1696,14 @@ def _process_dataString(self, dataString): # self.dMtxDat = values[self.ndims*self.nLm:].reshape( # self.ndims, self.nLm) - @property - def dataString(self): - header = '{} {}'.format(self.ndims, self.nLm) - blk1 = np.concatenate((self.aMtx.flatten(), self.bVec)) - b64_1 = encodeBase64(blk1) - blk2 = np.concatenate((self.srcPts.flatten(), self.dMtxDat.flatten())) - b64_2 = encodeBase64(blk2) - return 'ThinPlateSplineR2LogR {} {} {}'.format(header, b64_1, b64_2) + #@property + #def dataString(self): + # header = '{} {}'.format(self.ndims, self.nLm) + # blk1 = np.concatenate((self.aMtx.flatten(), self.bVec)) + # b64_1 = encodeBase64(blk1) + # blk2 = np.concatenate((self.srcPts.flatten(), self.dMtxDat.flatten())) + # b64_2 = encodeBase64(blk2) + # return 'ThinPlateSplineR2LogR {} {} {}'.format(header, b64_1, b64_2) class NonLinearTransform(NonLinearCoordinateTransform): From a929127a2b459d57999d6a81bf5d14a88d55c1be Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 15:20:46 -0700 Subject: [PATCH 17/46] fixing up ThinPlateSpline class --- renderapi/transform.py | 84 +++++------ test/test_files/thin_plate_spline.json | 196 +------------------------ test/test_transform.py | 10 +- 3 files changed, 44 insertions(+), 246 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index c94de4d1..a2a43ebc 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1628,8 +1628,8 @@ def dataString(self): class ThinPlateSplineTransform(Transform): """ - render-python class that implements the - mpicbg.trakem2.transform.ThinPlateSplineTransform class + render-python class that can hold a dataString for + mpicbg.trakem2.transform.ThinPlateSplineTransform class. Parameters ---------- dataString: str or None @@ -1653,57 +1653,45 @@ def __init__(self, dataString=None, json=None, transformId=None, else: if dataString is not None: self._process_dataString(dataString) - if labels is not None: - self.labels = labels + self.labels = labels self.transformId = transformId self.className = ( 'mpicbg.trakem2.transform.ThinPlateSplineTransform') - #def from_dict(self, json): - # self.ndims = json['ndims'] - # self.nLm = json['nLm'] - # self.aMtx = np.array(json['aMtx']) - # self.bVec = np.array(json['bVec']) - # self.srcPts = np.array(json['srcPts']) - # self.dMtxDat = np.array(json['dMtxDat']) - - #def to_dict(self): - # j = {} - # j['ndims'] = self.ndims - # j['nLm'] = self.nLm - # j['aMtx'] = self.aMtx.tolist() - # j['bVec'] = self.bVec.tolist() - # j['srcPts'] = self.srcPts.tolist() - # j['dMtxDat'] = self.dMtxDat.tolist() - # return j - def _process_dataString(self, dataString): - self.dataString = dataString - - # fields = dataString.split(" ") - - # self.ndims = int(fields[0]) - # self.nLm = int(fields[1]) - - # values = decodeBase64(fields[2], self.ndims*self.ndims + self.ndims) - # self.aMtx = values[0:self.ndims*self.ndims].reshape( - # self.ndims, self.ndims) - # self.bVec = values[self.ndims*self.ndims:] - - # values = decodeBase64(fields[3], 2*self.ndims*self.nLm) - # self.srcPts = values[0:self.ndims*self.nLm].reshape( - # self.ndims, self.nLm) - # self.dMtxDat = values[self.ndims*self.nLm:].reshape( - # self.ndims, self.nLm) - - #@property - #def dataString(self): - # header = '{} {}'.format(self.ndims, self.nLm) - # blk1 = np.concatenate((self.aMtx.flatten(), self.bVec)) - # b64_1 = encodeBase64(blk1) - # blk2 = np.concatenate((self.srcPts.flatten(), self.dMtxDat.flatten())) - # b64_2 = encodeBase64(blk2) - # return 'ThinPlateSplineR2LogR {} {} {}'.format(header, b64_1, b64_2) + #self.dataString = dataString + + fields = dataString.split(" ") + + self.ndims = int(fields[1]) + self.nLm = int(fields[2]) + + if self.fields[3] != "null": + values = decodeBase64(fields[3], self.ndims*self.ndims + self.ndims) + self.aMtx = values[0:self.ndims*self.ndims].reshape( + self.ndims, self.ndims) + self.bVec = values[self.ndims*self.ndims:] + else: + self.aMtx = None + self.bVec = None + + values = decodeBase64(fields[4], 2*self.ndims*self.nLm) + self.srcPts = values[0:self.ndims*self.nLm].reshape( + self.ndims, self.nLm) + self.dMtxDat = values[self.ndims*self.nLm:].reshape( + self.ndims, self.nLm) + + @property + def dataString(self): + header = 'ThinPlateSplineR2LogR {} {}'.format(self.ndims, self.nLm) + if self.aMtx is not None: + blk1 = np.concatenate((self.aMtx.flatten(), self.bVec)) + b64_1 = encodeBase64(blk1) + else: + b64_1 = "null" + blk2 = np.concatenate((self.srcPts.flatten(), self.dMtxDat.flatten())) + b64_2 = encodeBase64(blk2) + return '{} {} {}'.format(header, b64_1, b64_2) class NonLinearTransform(NonLinearCoordinateTransform): diff --git a/test/test_files/thin_plate_spline.json b/test/test_files/thin_plate_spline.json index f321c69c..4718f574 100644 --- a/test/test_files/thin_plate_spline.json +++ b/test/test_files/thin_plate_spline.json @@ -1,192 +1,6 @@ { - "ndims":2, - "nLm":225, - "aMtx":[[1.0,0.0],[0.0,1.0]], - "bVec":[1.0,1.0], - "srcPts":[ - [0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0, - 0.0, 274.3, 548.6, 822.9, 1097.1, - 1371.4, 1645.7, 1920.0, 2194.3, 2468.6, - 2742.9, 3017.1, 3291.4, 3565.7, 3840.0], - [ 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, 0.0, 0.0, - 274.3, 274.3, 274.3, 274.3, 274.3, - 274.3, 274.3, 274.3, 274.3, 274.3, - 274.3, 274.3, 274.3, 274.3, 274.3, - 548.6, 548.6, 548.6, 548.6, 548.6, - 548.6, 548.6, 548.6, 548.6, 548.6, - 548.6, 548.6, 548.6, 548.6, 548.6, - 822.9, 822.9, 822.9, 822.9, 822.9, - 822.9, 822.9, 822.9, 822.9, 822.9, - 822.9, 822.9, 822.9, 822.9, 822.9, - 1097.1, 1097.1, 1097.1, 1097.1, 1097.1, - 1097.1, 1097.1, 1097.1, 1097.1, 1097.1, - 1097.1, 1097.1, 1097.1, 1097.1, 1097.1, - 1371.4, 1371.4, 1371.4, 1371.4, 1371.4, - 1371.4, 1371.4, 1371.4, 1371.4, 1371.4, - 1371.4, 1371.4, 1371.4, 1371.4, 1371.4, - 1645.7, 1645.7, 1645.7, 1645.7, 1645.7, - 1645.7, 1645.7, 1645.7, 1645.7, 1645.7, - 1645.7, 1645.7, 1645.7, 1645.7, 1645.7, - 1920.0, 1920.0, 1920.0, 1920.0, 1920.0, - 1920.0, 1920.0, 1920.0, 1920.0, 1920.0, - 1920.0, 1920.0, 1920.0, 1920.0, 1920.0, - 2194.3, 2194.3, 2194.3, 2194.3, 2194.3, - 2194.3, 2194.3, 2194.3, 2194.3, 2194.3, - 2194.3, 2194.3, 2194.3, 2194.3, 2194.3, - 2468.6, 2468.6, 2468.6, 2468.6, 2468.6, - 2468.6, 2468.6, 2468.6, 2468.6, 2468.6, - 2468.6, 2468.6, 2468.6, 2468.6, 2468.6, - 2742.9, 2742.9, 2742.9, 2742.9, 2742.9, - 2742.9, 2742.9, 2742.9, 2742.9, 2742.9, - 2742.9, 2742.9, 2742.9, 2742.9, 2742.9, - 3017.1, 3017.1, 3017.1, 3017.1, 3017.1, - 3017.1, 3017.1, 3017.1, 3017.1, 3017.1, - 3017.1, 3017.1, 3017.1, 3017.1, 3017.1, - 3291.4, 3291.4, 3291.4, 3291.4, 3291.4, - 3291.4, 3291.4, 3291.4, 3291.4, 3291.4, - 3291.4, 3291.4, 3291.4, 3291.4, 3291.4, - 3565.7, 3565.7, 3565.7, 3565.7, 3565.7, - 3565.7, 3565.7, 3565.7, 3565.7, 3565.7, - 3565.7, 3565.7, 3565.7, 3565.7, 3565.7, - 3840.0, 3840.0, 3840.0, 3840.0, 3840.0, - 3840.0, 3840.0, 3840.0, 3840.0, 3840.0, - 3840.0, 3840.0, 3840.0, 3840.0, 3840.0]], - "dMtxDat":[ - [ 1.0, 1.3, 1.6, 1.9, 2.2, - 2.6, 2.9, 3.3, 3.7, 4.1, - 4.5, 4.9, 5.4, 5.8, 6.3, - 0.7, 1.0, 1.3, 1.6, 2.0, - 2.3, 2.7, 3.1, 3.5, 3.9, - 4.3, 4.7, 5.2, 5.7, 6.1, - 0.4, 0.7, 1.0, 1.4, 1.7, - 2.1, 2.4, 2.8, 3.2, 3.6, - 4.1, 4.5, 5.0, 5.5, 5.9, - 0.1, 0.4, 0.7, 1.1, 1.4, - 1.8, 2.2, 2.6, 3.0, 3.4, - 3.8, 4.3, 4.8, 5.2, 5.7, - -0.2, 0.1, 0.4, 0.8, 1.1, - 1.5, 1.9, 2.3, 2.7, 3.1, - 3.6, 4.0, 4.5, 5.0, 5.5, - -0.6, -0.2, 0.1, 0.4, 0.8, - 1.2, 1.6, 2.0, 2.4, 2.9, - 3.3, 3.8, 4.3, 4.8, 5.3, - -0.9, -0.6, -0.2, 0.1, 0.5, - 0.9, 1.3, 1.7, 2.1, 2.6, - 3.0, 3.5, 4.0, 4.5, 5.0, - -1.3, -1.0, -0.6, -0.2, 0.1, - 0.5, 0.9, 1.4, 1.8, 2.3, - 2.7, 3.2, 3.7, 4.2, 4.8, - -1.7, -1.3, -1.0, -0.6, -0.2, - 0.2, 0.6, 1.0, 1.5, 1.9, - 2.4, 2.9, 3.4, 3.9, 4.5, - -2.1, -1.7, -1.4, -1.0, -0.6, - -0.2, 0.2, 0.7, 1.1, 1.6, - 2.1, 2.6, 3.1, 3.6, 4.2, - -2.5, -2.1, -1.8, -1.4, -1.0, - -0.6, -0.1, 0.3, 0.8, 1.3, - 1.8, 2.3, 2.8, 3.3, 3.9, - -2.9, -2.6, -2.2, -1.8, -1.4, - -1.0, -0.5, -0.1, 0.4, 0.9, - 1.4, 1.9, 2.4, 3.0, 3.5, - -3.4, -3.0, -2.6, -2.2, -1.8, - -1.4, -0.9, -0.5, 0.0, 0.5, - 1.0, 1.5, 2.1, 2.6, 3.2, - -3.8, -3.5, -3.1, -2.7, -2.2, - -1.8, -1.3, -0.9, -0.4, 0.1, - 0.6, 1.2, 1.7, 2.3, 2.8, - -4.3, -3.9, -3.5, -3.1, -2.7, - -2.2, -1.8, -1.3, -0.8, -0.3, - 0.2, 0.8, 1.3, 1.9, 2.5], - [ 1.5, 1.0, 0.4, -0.1, -0.6, - -1.1, -1.5, -2.0, -2.4, -2.8, - -3.2, -3.6, -4.0, -4.4, -4.7, - 1.2, 0.7, 0.2, -0.3, -0.8, - -1.3, -1.7, -2.2, -2.6, -3.0, - -3.4, -3.8, -4.1, -4.5, -4.8, - 0.9, 0.4, -0.1, -0.6, -1.1, - -1.5, -2.0, -2.4, -2.8, -3.2, - -3.6, -3.9, -4.3, -4.6, -4.9, - 0.5, -0.0, -0.5, -1.0, -1.4, - -1.9, -2.3, -2.7, -3.1, -3.4, - -3.8, -4.2, -4.5, -4.8, -5.1, - 0.0, -0.4, -0.9, -1.4, -1.8, - -2.2, -2.6, -3.0, -3.4, -3.7, - -4.1, -4.4, -4.7, -5.0, -5.3, - -0.4, -0.9, -1.4, -1.8, -2.2, - -2.6, -3.0, -3.4, -3.7, -4.1, - -4.4, -4.7, -5.0, -5.3, -5.6, - -1.0, -1.4, -1.8, -2.3, -2.7, - -3.1, -3.4, -3.8, -4.1, -4.5, - -4.8, -5.1, -5.4, -5.6, -5.9, - -1.5, -2.0, -2.4, -2.8, -3.2, - -3.6, -3.9, -4.3, -4.6, -4.9, - -5.2, -5.5, -5.8, -6.0, -6.3, - -2.1, -2.6, -3.0, -3.4, -3.7, - -4.1, -4.4, -4.8, -5.1, -5.4, - -5.7, -5.9, -6.2, -6.4, -6.7, - -2.8, -3.2, -3.6, -4.0, -4.3, - -4.7, -5.0, -5.3, -5.6, -5.9, - -6.2, -6.4, -6.7, -6.9, -7.1, - -3.5, -3.9, -4.3, -4.6, -5.0, - -5.3, -5.6, -5.9, -6.2, -6.5, - -6.7, -7.0, -7.2, -7.4, -7.6, - -4.2, -4.6, -5.0, -5.3, -5.7, - -6.0, -6.3, -6.6, -6.8, -7.1, - -7.3, -7.6, -7.8, -8.0, -8.1, - -5.0, -5.4, -5.7, -6.1, -6.4, - -6.7, -7.0, -7.2, -7.5, -7.7, - -8.0, -8.2, -8.4, -8.6, -8.7, - -5.9, -6.2, -6.6, -6.9, -7.2, - -7.5, -7.7, -8.0, -8.2, -8.4, - -8.7, -8.9, -9.0, -9.2, -9.3, - -6.8, -7.1, -7.4, -7.7, -8.0, - -8.3, -8.5, -8.8, -9.0, -9.2, - -9.4, -9.6, -9.7, -9.9, -10.0]] -} - - - - + "type":"leaf", + "id":"", + "classname":"mpicbg.trakem2.transform.ThinPlateSplineTransform", + "dataString":"ThinPlateSplineR2LogR 2 995 null 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" +} \ No newline at end of file diff --git a/test/test_transform.py b/test/test_transform.py index b3152fce..99d4f527 100644 --- a/test/test_transform.py +++ b/test/test_transform.py @@ -603,10 +603,6 @@ def test_similarity_init(): def test_thinplatespline(): - j1 = json.load(open(rendersettings.TEST_THINPLATESPLINE_FILE, 'r')) - t1 = renderapi.transform.ThinPlateSplineTransform(json=j1) - t2 = renderapi.transform.ThinPlateSplineTransform(dataString=t1.dataString) - assert(t1 == t2) - j2 = t2.to_dict() - t3 = renderapi.transform.ThinPlateSplineTransform(json=j2) - assert(t1 == t3) + j = json.load(open(rendersettings.TEST_THINPLATESPLINE_FILE, 'r')) + t = renderapi.transform.ThinPlateSplineTransform(dataString=j['dataString']) + assert (t['dataString']==t.dataString) From d06636d8cc08e62d52e9018daac06f525c271a04 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 15:23:40 -0700 Subject: [PATCH 18/46] fixing --- renderapi/transform.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index a2a43ebc..e69e38c8 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1659,14 +1659,12 @@ def __init__(self, dataString=None, json=None, transformId=None, 'mpicbg.trakem2.transform.ThinPlateSplineTransform') def _process_dataString(self, dataString): - #self.dataString = dataString - fields = dataString.split(" ") self.ndims = int(fields[1]) self.nLm = int(fields[2]) - if self.fields[3] != "null": + if fields[3] != "null": values = decodeBase64(fields[3], self.ndims*self.ndims + self.ndims) self.aMtx = values[0:self.ndims*self.ndims].reshape( self.ndims, self.ndims) From 0befa95ea757992c3a5a439f41d1c3822edcedd8 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 15:31:53 -0700 Subject: [PATCH 19/46] flake8 --- renderapi/transform.py | 3 ++- test/test_transform.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index e69e38c8..4a5ef3f6 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1665,7 +1665,8 @@ def _process_dataString(self, dataString): self.nLm = int(fields[2]) if fields[3] != "null": - values = decodeBase64(fields[3], self.ndims*self.ndims + self.ndims) + values = decodeBase64(fields[3], + self.ndims*self.ndims + self.ndims) self.aMtx = values[0:self.ndims*self.ndims].reshape( self.ndims, self.ndims) self.bVec = values[self.ndims*self.ndims:] diff --git a/test/test_transform.py b/test/test_transform.py index 99d4f527..2575c962 100644 --- a/test/test_transform.py +++ b/test/test_transform.py @@ -604,5 +604,6 @@ def test_similarity_init(): def test_thinplatespline(): j = json.load(open(rendersettings.TEST_THINPLATESPLINE_FILE, 'r')) - t = renderapi.transform.ThinPlateSplineTransform(dataString=j['dataString']) - assert (t['dataString']==t.dataString) + t = renderapi.transform.ThinPlateSplineTransform( + dataString=j['dataString']) + assert (t['dataString'] == t.dataString) From 225920516015373558c7057e0af222d212d0470f Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 15:56:09 -0700 Subject: [PATCH 20/46] fixed test/transform/thinplatespline --- test/test_transform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_transform.py b/test/test_transform.py index 2575c962..aa43029d 100644 --- a/test/test_transform.py +++ b/test/test_transform.py @@ -606,4 +606,4 @@ def test_thinplatespline(): j = json.load(open(rendersettings.TEST_THINPLATESPLINE_FILE, 'r')) t = renderapi.transform.ThinPlateSplineTransform( dataString=j['dataString']) - assert (t['dataString'] == t.dataString) + assert (j['dataString'] == t.dataString) From 01f6b965a9bcc4423da9df2ee881bc4351f607cd Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 16:12:29 -0700 Subject: [PATCH 21/46] removing trailing L's in hex from encode/decode --- renderapi/utils.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index 27495485..979f74f8 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -381,13 +381,13 @@ def encodeBase64(src): for ix in src: bits = bitstring.BitArray(float=ix, length=64).uint s += chr(bits >> 56) - s += chr(bits >> 48 & 0xffL) - s += chr(bits >> 40 & 0xffL) - s += chr(bits >> 32 & 0xffL) - s += chr(bits >> 24 & 0xffL) - s += chr(bits >> 16 & 0xffL) - s += chr(bits >> 8 & 0xffL) - s += chr(bits & 0xffL) + s += chr(bits >> 48 & 0xff) + s += chr(bits >> 40 & 0xff) + s += chr(bits >> 32 & 0xff) + s += chr(bits >> 24 & 0xff) + s += chr(bits >> 16 & 0xff) + s += chr(bits >> 8 & 0xff) + s += chr(bits & 0xff) zs = zlib.compress(s) encoded = base64.b64encode(zs) return encoded @@ -415,21 +415,21 @@ def decodeBase64(src, n): j = 0 for i in range(n): bits = 0L - bits += (ord(bvalues[j]) & 0xffL) << 56 + bits += (ord(bvalues[j]) & 0xff) << 56 j += 1 - bits += (ord(bvalues[j]) & 0xffL) << 48 + bits += (ord(bvalues[j]) & 0xff) << 48 j += 1 - bits += (ord(bvalues[j]) & 0xffL) << 40 + bits += (ord(bvalues[j]) & 0xff) << 40 j += 1 - bits += (ord(bvalues[j]) & 0xffL) << 32 + bits += (ord(bvalues[j]) & 0xff) << 32 j += 1 - bits += (ord(bvalues[j]) & 0xffL) << 24 + bits += (ord(bvalues[j]) & 0xff) << 24 j += 1 - bits += (ord(bvalues[j]) & 0xffL) << 16 + bits += (ord(bvalues[j]) & 0xff) << 16 j += 1 - bits += (ord(bvalues[j]) & 0xffL) << 8 + bits += (ord(bvalues[j]) & 0xff) << 8 j += 1 - bits += ord(bvalues[j]) & 0xffL + bits += ord(bvalues[j]) & 0xff j += 1 arr.append(bitstring.BitArray(uint=bits, length=64).float) return numpy.array(arr) From 32f307bc6020d85e68bf90ae4086e14b6f7cf1d0 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 16:20:19 -0700 Subject: [PATCH 22/46] increasing coverage on affine and b vectors --- test/test_transform.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/test_transform.py b/test/test_transform.py index aa43029d..35600ea3 100644 --- a/test/test_transform.py +++ b/test/test_transform.py @@ -607,3 +607,8 @@ def test_thinplatespline(): t = renderapi.transform.ThinPlateSplineTransform( dataString=j['dataString']) assert (j['dataString'] == t.dataString) + t.aMtx = np.zeros(4) + t.bVec = np.zeros(2) + t2 = renderapi.transform.ThinPlateSplineTransform( + dataString=t.dataString) + assert (t==t2) From c4e54069f1a5373afcb05c3af9574d5e74ced98b Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 16:21:49 -0700 Subject: [PATCH 23/46] removing trailing L in hex from encode/decode --- renderapi/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index 979f74f8..bb934c6d 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -414,7 +414,7 @@ def decodeBase64(src, n): arr = [] j = 0 for i in range(n): - bits = 0L + bits = 0 bits += (ord(bvalues[j]) & 0xff) << 56 j += 1 bits += (ord(bvalues[j]) & 0xff) << 48 From 0307c7567287587c7fe2f4fa6d99a31e13941fbc Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 16:32:52 -0700 Subject: [PATCH 24/46] one more hex change --- renderapi/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index bb934c6d..b6e1dacb 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -414,7 +414,7 @@ def decodeBase64(src, n): arr = [] j = 0 for i in range(n): - bits = 0 + bits = 0x00 bits += (ord(bvalues[j]) & 0xff) << 56 j += 1 bits += (ord(bvalues[j]) & 0xff) << 48 From f13f17320954328bde48180fd4741e1506fb18c4 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 16:58:44 -0700 Subject: [PATCH 25/46] adding cast to byte for python 3 for zlib decompress output --- renderapi/utils.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index b6e1dacb..f63cec4e 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -415,21 +415,21 @@ def decodeBase64(src, n): j = 0 for i in range(n): bits = 0x00 - bits += (ord(bvalues[j]) & 0xff) << 56 + bits += (ord(bytes([bvalues[j]])) & 0xff) << 56 j += 1 - bits += (ord(bvalues[j]) & 0xff) << 48 + bits += (ord(bytes([bvalues[j]])) & 0xff) << 48 j += 1 - bits += (ord(bvalues[j]) & 0xff) << 40 + bits += (ord(bytes([bvalues[j]])) & 0xff) << 40 j += 1 - bits += (ord(bvalues[j]) & 0xff) << 32 + bits += (ord(bytes([bvalues[j]])) & 0xff) << 32 j += 1 - bits += (ord(bvalues[j]) & 0xff) << 24 + bits += (ord(bytes([bvalues[j]])) & 0xff) << 24 j += 1 - bits += (ord(bvalues[j]) & 0xff) << 16 + bits += (ord(bytes([bvalues[j]])) & 0xff) << 16 j += 1 - bits += (ord(bvalues[j]) & 0xff) << 8 + bits += (ord(bytes([bvalues[j]])) & 0xff) << 8 j += 1 - bits += ord(bvalues[j]) & 0xff + bits += ord(bytes([bvalues[j]])) & 0xff j += 1 arr.append(bitstring.BitArray(uint=bits, length=64).float) return numpy.array(arr) From 7ef40735c45171520f4856d09c5b3e501a9b9499 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 17:12:52 -0700 Subject: [PATCH 26/46] adding cast to byte for python 3 for zlib compress input --- renderapi/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index f63cec4e..075005cd 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -388,7 +388,7 @@ def encodeBase64(src): s += chr(bits >> 16 & 0xff) s += chr(bits >> 8 & 0xff) s += chr(bits & 0xff) - zs = zlib.compress(s) + zs = zlib.compress(bytes(s)) encoded = base64.b64encode(zs) return encoded From e5600054416b0489234e47338c6ccb36098415fd Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 17:30:06 -0700 Subject: [PATCH 27/46] casting utf-8 --- renderapi/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index 075005cd..5af4f004 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -388,7 +388,7 @@ def encodeBase64(src): s += chr(bits >> 16 & 0xff) s += chr(bits >> 8 & 0xff) s += chr(bits & 0xff) - zs = zlib.compress(bytes(s)) + zs = zlib.compress(bytes(s,'utf-8')) encoded = base64.b64encode(zs) return encoded From f98c03fb07096e6699f4ba07dd52306cfadeed03 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Tue, 19 Jun 2018 18:04:15 -0700 Subject: [PATCH 28/46] fixing... --- renderapi/utils.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index 5af4f004..bd1f2e89 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -415,21 +415,21 @@ def decodeBase64(src, n): j = 0 for i in range(n): bits = 0x00 - bits += (ord(bytes([bvalues[j]])) & 0xff) << 56 + bits += (ord(bytes(bvalues[j])) & 0xff) << 56 j += 1 - bits += (ord(bytes([bvalues[j]])) & 0xff) << 48 + bits += (ord(bytes(bvalues[j])) & 0xff) << 48 j += 1 - bits += (ord(bytes([bvalues[j]])) & 0xff) << 40 + bits += (ord(bytes(bvalues[j])) & 0xff) << 40 j += 1 - bits += (ord(bytes([bvalues[j]])) & 0xff) << 32 + bits += (ord(bytes(bvalues[j])) & 0xff) << 32 j += 1 - bits += (ord(bytes([bvalues[j]])) & 0xff) << 24 + bits += (ord(bytes(bvalues[j])) & 0xff) << 24 j += 1 - bits += (ord(bytes([bvalues[j]])) & 0xff) << 16 + bits += (ord(bytes(bvalues[j])) & 0xff) << 16 j += 1 - bits += (ord(bytes([bvalues[j]])) & 0xff) << 8 + bits += (ord(bytes(bvalues[j])) & 0xff) << 8 j += 1 - bits += ord(bytes([bvalues[j]])) & 0xff + bits += ord(bytes(bvalues[j])) & 0xff j += 1 arr.append(bitstring.BitArray(uint=bits, length=64).float) return numpy.array(arr) From 725eef520a9dc8dc4684f295dc2226603080c2fe Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Wed, 20 Jun 2018 09:50:30 -0700 Subject: [PATCH 29/46] think python2/3 zlib stuff fixed --- renderapi/utils.py | 56 +++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index bd1f2e89..852c699b 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -377,18 +377,28 @@ def encodeBase64(src): ------- encoded: string """ - s = '' + s = [] for ix in src: bits = bitstring.BitArray(float=ix, length=64).uint - s += chr(bits >> 56) - s += chr(bits >> 48 & 0xff) - s += chr(bits >> 40 & 0xff) - s += chr(bits >> 32 & 0xff) - s += chr(bits >> 24 & 0xff) - s += chr(bits >> 16 & 0xff) - s += chr(bits >> 8 & 0xff) - s += chr(bits & 0xff) - zs = zlib.compress(bytes(s,'utf-8')) + s.append(bits >> 56) + s.append(bits >> 48 & 0xff) + s.append(bits >> 40 & 0xff) + s.append(bits >> 32 & 0xff) + s.append(bits >> 24 & 0xff) + s.append(bits >> 16 & 0xff) + s.append(bits >> 8 & 0xff) + s.append(bits & 0xff) + + try: + # python 3 case + zs = zlib.compress(bytearray(s)) + except TypeError: + # python 2 case + ns = "" + for i in s: + ns += chr(i) + zs = zlib.compress(ns) + encoded = base64.b64encode(zs) return encoded @@ -412,24 +422,34 @@ def decodeBase64(src, n): zipped = base64.b64decode(src) bvalues = zlib.decompress(zipped) arr = [] + if type(bvalues[0]) is str: + # python 2 + tmp = [] + for bv in bvalues: + tmp.append(ord(bv)) + bvalues = tmp + else: + # python 3 + pass + j = 0 for i in range(n): bits = 0x00 - bits += (ord(bytes(bvalues[j])) & 0xff) << 56 + bits += (bvalues[j] & 0xff) << 56 j += 1 - bits += (ord(bytes(bvalues[j])) & 0xff) << 48 + bits += (bvalues[j] & 0xff) << 48 j += 1 - bits += (ord(bytes(bvalues[j])) & 0xff) << 40 + bits += (bvalues[j] & 0xff) << 40 j += 1 - bits += (ord(bytes(bvalues[j])) & 0xff) << 32 + bits += (bvalues[j] & 0xff) << 32 j += 1 - bits += (ord(bytes(bvalues[j])) & 0xff) << 24 + bits += (bvalues[j] & 0xff) << 24 j += 1 - bits += (ord(bytes(bvalues[j])) & 0xff) << 16 + bits += (bvalues[j] & 0xff) << 16 j += 1 - bits += (ord(bytes(bvalues[j])) & 0xff) << 8 + bits += (bvalues[j] & 0xff) << 8 j += 1 - bits += ord(bytes(bvalues[j])) & 0xff + bits += bvalues[j] & 0xff j += 1 arr.append(bitstring.BitArray(uint=bits, length=64).float) return numpy.array(arr) From fe4bb0232a4abea4859baf1e712f11ea4fe89aba Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Wed, 20 Jun 2018 10:01:32 -0700 Subject: [PATCH 30/46] one last python 3 bit --- renderapi/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/renderapi/utils.py b/renderapi/utils.py index 852c699b..9098cc1c 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -400,6 +400,8 @@ def encodeBase64(src): zs = zlib.compress(ns) encoded = base64.b64encode(zs) + if type(encoded) is bytes: + encoded = encoded.decode() return encoded From 1ec12509b0548452c2f79ff51cdf196db68d2b58 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Wed, 20 Jun 2018 10:13:53 -0700 Subject: [PATCH 31/46] trivial change to retrigger failed build --- renderapi/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/renderapi/utils.py b/renderapi/utils.py index 9098cc1c..332a20bd 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -454,4 +454,5 @@ def decodeBase64(src, n): bits += bvalues[j] & 0xff j += 1 arr.append(bitstring.BitArray(uint=bits, length=64).float) + return numpy.array(arr) From 6e8c0544df75a5e830e821eb0725a5114e8f774d Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Wed, 20 Jun 2018 10:17:21 -0700 Subject: [PATCH 32/46] another trivial change to retrigger failed build --- renderapi/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/renderapi/utils.py b/renderapi/utils.py index 332a20bd..dfb39a6d 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -400,6 +400,7 @@ def encodeBase64(src): zs = zlib.compress(ns) encoded = base64.b64encode(zs) + if type(encoded) is bytes: encoded = encoded.decode() return encoded From 2cbd0b334a3335b79c66d997d246f6cbbd5e9ab8 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Wed, 20 Jun 2018 10:23:18 -0700 Subject: [PATCH 33/46] another trivial change to retrigger failed build --- renderapi/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/renderapi/utils.py b/renderapi/utils.py index dfb39a6d..ad2629ce 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -378,6 +378,7 @@ def encodeBase64(src): encoded: string """ s = [] + for ix in src: bits = bitstring.BitArray(float=ix, length=64).uint s.append(bits >> 56) @@ -425,6 +426,7 @@ def decodeBase64(src, n): zipped = base64.b64decode(src) bvalues = zlib.decompress(zipped) arr = [] + if type(bvalues[0]) is str: # python 2 tmp = [] From 93613461f566076e3d25cde7b23a98e90c017133 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Thu, 21 Jun 2018 11:56:56 -0700 Subject: [PATCH 34/46] adding decode support for '@' --- renderapi/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index ad2629ce..3d124596 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -423,8 +423,11 @@ def decodeBase64(src, n): ------- arr: length n numpy array of double-precision floats """ - zipped = base64.b64decode(src) - bvalues = zlib.decompress(zipped) + if src[0]=='@': + bvalues = base64.b64decode(src[1:]) + else: + zipped = base64.b64decode(src) + bvalues = zlib.decompress(zipped) arr = [] if type(bvalues[0]) is str: From bb0733b544872a1c5617805a9e24a00383cbff57 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 11:22:51 -0700 Subject: [PATCH 35/46] major simplification from Russel on encode/decode --- renderapi/utils.py | 70 ++-------------------------------------------- 1 file changed, 2 insertions(+), 68 deletions(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index 3d124596..e808680f 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -9,7 +9,6 @@ import json import base64 import zlib -import bitstring import numpy import requests @@ -377,34 +376,7 @@ def encodeBase64(src): ------- encoded: string """ - s = [] - - for ix in src: - bits = bitstring.BitArray(float=ix, length=64).uint - s.append(bits >> 56) - s.append(bits >> 48 & 0xff) - s.append(bits >> 40 & 0xff) - s.append(bits >> 32 & 0xff) - s.append(bits >> 24 & 0xff) - s.append(bits >> 16 & 0xff) - s.append(bits >> 8 & 0xff) - s.append(bits & 0xff) - - try: - # python 3 case - zs = zlib.compress(bytearray(s)) - except TypeError: - # python 2 case - ns = "" - for i in s: - ns += chr(i) - zs = zlib.compress(ns) - - encoded = base64.b64encode(zs) - - if type(encoded) is bytes: - encoded = encoded.decode() - return encoded + return base64.b64encode(zlib.compress(src.byteswap().tobytes())) def decodeBase64(src, n): @@ -423,42 +395,4 @@ def decodeBase64(src, n): ------- arr: length n numpy array of double-precision floats """ - if src[0]=='@': - bvalues = base64.b64decode(src[1:]) - else: - zipped = base64.b64decode(src) - bvalues = zlib.decompress(zipped) - arr = [] - - if type(bvalues[0]) is str: - # python 2 - tmp = [] - for bv in bvalues: - tmp.append(ord(bv)) - bvalues = tmp - else: - # python 3 - pass - - j = 0 - for i in range(n): - bits = 0x00 - bits += (bvalues[j] & 0xff) << 56 - j += 1 - bits += (bvalues[j] & 0xff) << 48 - j += 1 - bits += (bvalues[j] & 0xff) << 40 - j += 1 - bits += (bvalues[j] & 0xff) << 32 - j += 1 - bits += (bvalues[j] & 0xff) << 24 - j += 1 - bits += (bvalues[j] & 0xff) << 16 - j += 1 - bits += (bvalues[j] & 0xff) << 8 - j += 1 - bits += bvalues[j] & 0xff - j += 1 - arr.append(bitstring.BitArray(uint=bits, length=64).float) - - return numpy.array(arr) + return numpy.frombuffer(zlib.decompress(base64.b64decode(src))).byteswap() From d760bbde1571cd5100deff6309c8f8c54203f553 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 11:35:07 -0700 Subject: [PATCH 36/46] getting rid of leading b'' in encode for python 3 --- renderapi/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index e808680f..a8af3e4d 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -376,7 +376,7 @@ def encodeBase64(src): ------- encoded: string """ - return base64.b64encode(zlib.compress(src.byteswap().tobytes())) + return base64.b64encode(zlib.compress(src.byteswap().tobytes())).decode('utf-8') def decodeBase64(src, n): From 323adbee5d7eb138436284903be9b638968746f4 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 12:16:43 -0700 Subject: [PATCH 37/46] catching and testing for '@' in decode --- renderapi/utils.py | 11 +++++++++-- test/test_transform.py | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index a8af3e4d..3efa34c2 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -376,7 +376,10 @@ def encodeBase64(src): ------- encoded: string """ - return base64.b64encode(zlib.compress(src.byteswap().tobytes())).decode('utf-8') + return base64.b64encode( + zlib.compress( + src.byteswap().tobytes()) + ).decode('utf-8') def decodeBase64(src, n): @@ -395,4 +398,8 @@ def decodeBase64(src, n): ------- arr: length n numpy array of double-precision floats """ - return numpy.frombuffer(zlib.decompress(base64.b64decode(src))).byteswap() + if src[0] == '@': + b = base64.b64decode(src[1:]) + else: + b = zlib.decompress(base64.b64decode(src)) + return numpy.frombuffer(b).byteswap() diff --git a/test/test_transform.py b/test/test_transform.py index 35600ea3..31268077 100644 --- a/test/test_transform.py +++ b/test/test_transform.py @@ -611,4 +611,17 @@ def test_thinplatespline(): t.bVec = np.zeros(2) t2 = renderapi.transform.ThinPlateSplineTransform( dataString=t.dataString) - assert (t==t2) + assert (t == t2) + + +def test_encode64(): + # case for Stephan's '@' character + s = '@QAkh+fAbhm6/8AAAAAAAAA==' + x = renderapi.utils.decodeBase64(s, 2) + assert(x[0] == 3.14159) + assert(x[1] == -1.0) + # all other + x = np.array([1.0, 3.0, 3.14159, -4.0, 10.0]) + s = renderapi.utils.encodeBase64(x) + y = renderapi.utils.decodeBase64(s, x.size) + assert(np.all(x == y)) From 8df94d491e76899cdc7186d8016b4c943f3b9e1d Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 15:31:21 -0700 Subject: [PATCH 38/46] removing bitstring requirement --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b6d9dae5..3f22a4bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,3 @@ pillow sphinxcontrib-napoleon decorator six -bitstring From 4c240d1e2e6937c506466c5c2ccc2d347678576e Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 15:46:09 -0700 Subject: [PATCH 39/46] removing n argument to decodeBase64 --- renderapi/transform.py | 29 +++++++++++++++++++---------- renderapi/utils.py | 2 +- test/test_transform.py | 5 +++-- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 4a5ef3f6..70d36bd4 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1665,20 +1665,29 @@ def _process_dataString(self, dataString): self.nLm = int(fields[2]) if fields[3] != "null": - values = decodeBase64(fields[3], - self.ndims*self.ndims + self.ndims) - self.aMtx = values[0:self.ndims*self.ndims].reshape( - self.ndims, self.ndims) - self.bVec = values[self.ndims*self.ndims:] + try: + values = decodeBase64(fields[3]) + self.aMtx = values[0:self.ndims*self.ndims].reshape( + self.ndims, self.ndims) + self.bVec = values[self.ndims*self.ndims:] + except IndexError: + raise RenderError( + "inconsistent sizes and array lengths, + in ThinPlateSplineTransform dataString") else: self.aMtx = None self.bVec = None - values = decodeBase64(fields[4], 2*self.ndims*self.nLm) - self.srcPts = values[0:self.ndims*self.nLm].reshape( - self.ndims, self.nLm) - self.dMtxDat = values[self.ndims*self.nLm:].reshape( - self.ndims, self.nLm) + try: + values = decodeBase64(fields[4]) + self.srcPts = values[0:self.ndims*self.nLm].reshape( + self.ndims, self.nLm) + self.dMtxDat = values[self.ndims*self.nLm:].reshape( + self.ndims, self.nLm) + except IndexError: + raise RenderError( + "inconsistent sizes and array lengths, + in ThinPlateSplineTransform dataString") @property def dataString(self): diff --git a/renderapi/utils.py b/renderapi/utils.py index 3efa34c2..0c839f48 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -382,7 +382,7 @@ def encodeBase64(src): ).decode('utf-8') -def decodeBase64(src, n): +def decodeBase64(src): """decode a string encoded in base64 binary-to-text encoding same as in trakem2...ThinPlateSplineTransform.java diff --git a/test/test_transform.py b/test/test_transform.py index 31268077..c2127884 100644 --- a/test/test_transform.py +++ b/test/test_transform.py @@ -617,11 +617,12 @@ def test_thinplatespline(): def test_encode64(): # case for Stephan's '@' character s = '@QAkh+fAbhm6/8AAAAAAAAA==' - x = renderapi.utils.decodeBase64(s, 2) + x = renderapi.utils.decodeBase64(s) + assert(x.size == 2) assert(x[0] == 3.14159) assert(x[1] == -1.0) # all other x = np.array([1.0, 3.0, 3.14159, -4.0, 10.0]) s = renderapi.utils.encodeBase64(x) - y = renderapi.utils.decodeBase64(s, x.size) + y = renderapi.utils.decodeBase64(s) assert(np.all(x == y)) From 82984efc026eccd5a27f9ed1a2592570f5ac3841 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 15:56:20 -0700 Subject: [PATCH 40/46] fixing multiline string --- renderapi/transform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 70d36bd4..3cf6a71b 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1672,7 +1672,7 @@ def _process_dataString(self, dataString): self.bVec = values[self.ndims*self.ndims:] except IndexError: raise RenderError( - "inconsistent sizes and array lengths, + "inconsistent sizes and array lengths, \ in ThinPlateSplineTransform dataString") else: self.aMtx = None @@ -1686,7 +1686,7 @@ def _process_dataString(self, dataString): self.ndims, self.nLm) except IndexError: raise RenderError( - "inconsistent sizes and array lengths, + "inconsistent sizes and array lengths, \ in ThinPlateSplineTransform dataString") @property From a63620f803e48bef8b5dc28dc595aae1e81df7b6 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 16:05:34 -0700 Subject: [PATCH 41/46] removed documentation referring to n, retrigger failed build --- renderapi/utils.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index 0c839f48..1bd3bb8b 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +usr/bin/env python ''' utilities to make render/java/web/life interfacing easier ''' @@ -391,8 +391,6 @@ def decodeBase64(src): ---------- src : string encoded string - n : int - number of values to decode Returns ------- From a4cca94c9c7780d672f14f3367fe1175580660d5 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 16:13:21 -0700 Subject: [PATCH 42/46] accidental deletion --- renderapi/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/renderapi/utils.py b/renderapi/utils.py index 1bd3bb8b..8915d25a 100644 --- a/renderapi/utils.py +++ b/renderapi/utils.py @@ -1,4 +1,4 @@ -usr/bin/env python +#!/usr/bin/env python ''' utilities to make render/java/web/life interfacing easier ''' From 7ad78ee1a07600225d6f6853317d785c4a691cdd Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 16:37:19 -0700 Subject: [PATCH 43/46] first try with pytest.raises() for code coverage --- test/test_transform.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/test_transform.py b/test/test_transform.py index c2127884..4b3e620f 100644 --- a/test/test_transform.py +++ b/test/test_transform.py @@ -612,6 +612,17 @@ def test_thinplatespline(): t2 = renderapi.transform.ThinPlateSplineTransform( dataString=t.dataString) assert (t == t2) + # test incorrect lengths + with pytest.raises(renderapi.errors.RenderError): + s = t2.dataString.split(' ') + s[1] = str(int(s[1])+1) + t3 = renderapi.transform.ThinPlateSplineTransform( + dataString=" ".join(s)) + with pytest.raises(renderapi.errors.RenderError): + s = t2.dataString.split(' ') + s[2] = str(int(s[2])-4) + t3 = renderapi.transform.ThinPlateSplineTransform( + dataString=" ".join(s)) def test_encode64(): From 904ce251dd7d5e9d512d9bf6345dc1d42fe50f39 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 16:48:50 -0700 Subject: [PATCH 44/46] changing IndexErrors to ValueErrors --- renderapi/transform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 3cf6a71b..22dede74 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1670,7 +1670,7 @@ def _process_dataString(self, dataString): self.aMtx = values[0:self.ndims*self.ndims].reshape( self.ndims, self.ndims) self.bVec = values[self.ndims*self.ndims:] - except IndexError: + except ValueError: raise RenderError( "inconsistent sizes and array lengths, \ in ThinPlateSplineTransform dataString") @@ -1684,7 +1684,7 @@ def _process_dataString(self, dataString): self.ndims, self.nLm) self.dMtxDat = values[self.ndims*self.nLm:].reshape( self.ndims, self.nLm) - except IndexError: + except ValueError: raise RenderError( "inconsistent sizes and array lengths, \ in ThinPlateSplineTransform dataString") From 483c5b555a07ade486aeb421615e606a38dacb41 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 16:56:34 -0700 Subject: [PATCH 45/46] last flake8 indent fix --- renderapi/transform.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/renderapi/transform.py b/renderapi/transform.py index 22dede74..aa246b77 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1666,14 +1666,14 @@ def _process_dataString(self, dataString): if fields[3] != "null": try: - values = decodeBase64(fields[3]) - self.aMtx = values[0:self.ndims*self.ndims].reshape( - self.ndims, self.ndims) - self.bVec = values[self.ndims*self.ndims:] + values = decodeBase64(fields[3]) + self.aMtx = values[0:self.ndims*self.ndims].reshape( + self.ndims, self.ndims) + self.bVec = values[self.ndims*self.ndims:] except ValueError: raise RenderError( - "inconsistent sizes and array lengths, \ - in ThinPlateSplineTransform dataString") + "inconsistent sizes and array lengths, \ + in ThinPlateSplineTransform dataString") else: self.aMtx = None self.bVec = None @@ -1686,8 +1686,8 @@ def _process_dataString(self, dataString): self.ndims, self.nLm) except ValueError: raise RenderError( - "inconsistent sizes and array lengths, \ - in ThinPlateSplineTransform dataString") + "inconsistent sizes and array lengths, \ + in ThinPlateSplineTransform dataString") @property def dataString(self): From a498117e4639aaca8e40fdbd7299a2796bb431a3 Mon Sep 17 00:00:00 2001 From: Dan Kapner Date: Fri, 22 Jun 2018 17:00:58 -0700 Subject: [PATCH 46/46] trivial change to retrigger failed build --- renderapi/transform.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/renderapi/transform.py b/renderapi/transform.py index aa246b77..611e82fb 100644 --- a/renderapi/transform.py +++ b/renderapi/transform.py @@ -1692,13 +1692,16 @@ def _process_dataString(self, dataString): @property def dataString(self): header = 'ThinPlateSplineR2LogR {} {}'.format(self.ndims, self.nLm) + if self.aMtx is not None: blk1 = np.concatenate((self.aMtx.flatten(), self.bVec)) b64_1 = encodeBase64(blk1) else: b64_1 = "null" + blk2 = np.concatenate((self.srcPts.flatten(), self.dMtxDat.flatten())) b64_2 = encodeBase64(blk2) + return '{} {} {}'.format(header, b64_1, b64_2)