Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
c82ac3a
added base64 encoding/decoding
Jun 15, 2018
9e9345b
starting framework for ThinPlateSplineTransform
Jun 15, 2018
7375c8f
updated
Jun 15, 2018
f99a43c
changin encode/decode to deal only with numpy arrays
Jun 15, 2018
a561415
adding from_dict method to thin_plate_spline
Jun 15, 2018
f0890a8
unflattening matrix
Jun 15, 2018
a21273c
fixing np -> numpy
Jun 15, 2018
1f2dc8b
adding to_dict to thin plate spline
Jun 15, 2018
735121d
apply flake8
Jun 15, 2018
1340747
commenting out thinplate split to_dict
Jun 15, 2018
664a753
adding whitespace to start of datastring
Jun 15, 2018
84ef98d
adding textto start dataString
Jun 15, 2018
c2a423a
commenting out _process dataString
Jun 19, 2018
65585a7
modifying _process dataString
Jun 19, 2018
39db053
modifying _process dataString
Jun 19, 2018
84f7ae4
modifying _process dataString
Jun 19, 2018
a929127
fixing up ThinPlateSpline class
Jun 19, 2018
d06636d
fixing
Jun 19, 2018
0befa95
flake8
Jun 19, 2018
2259205
fixed test/transform/thinplatespline
Jun 19, 2018
01f6b96
removing trailing L's in hex from encode/decode
Jun 19, 2018
32f307b
increasing coverage on affine and b vectors
Jun 19, 2018
c4e5406
removing trailing L in hex from encode/decode
Jun 19, 2018
0307c75
one more hex change
Jun 19, 2018
f13f173
adding cast to byte for python 3 for zlib decompress output
Jun 19, 2018
7ef4073
adding cast to byte for python 3 for zlib compress input
Jun 20, 2018
e560005
casting utf-8
Jun 20, 2018
f98c03f
fixing...
Jun 20, 2018
725eef5
think python2/3 zlib stuff fixed
Jun 20, 2018
fe4bb02
one last python 3 bit
Jun 20, 2018
1ec1250
trivial change to retrigger failed build
Jun 20, 2018
6e8c054
another trivial change to retrigger failed build
Jun 20, 2018
2cbd0b3
another trivial change to retrigger failed build
Jun 20, 2018
9361346
adding decode support for '@'
Jun 21, 2018
1a9cec0
Merge pull request #1 from djkapner/tweaking_base64
djkapner Jun 21, 2018
bb0733b
major simplification from Russel on encode/decode
Jun 22, 2018
d760bbd
getting rid of leading b'' in encode for python 3
Jun 22, 2018
323adbe
catching and testing for '@' in decode
Jun 22, 2018
8df94d4
removing bitstring requirement
Jun 22, 2018
4c240d1
removing n argument to decodeBase64
Jun 22, 2018
82984ef
fixing multiline string
Jun 22, 2018
a63620f
removed documentation referring to n, retrigger failed build
Jun 22, 2018
a4cca94
accidental deletion
Jun 22, 2018
7ad78ee
first try with pytest.raises() for code coverage
Jun 22, 2018
904ce25
changing IndexErrors to ValueErrors
Jun 22, 2018
483c5b5
last flake8 indent fix
Jun 22, 2018
a498117
trivial change to retrigger failed build
Jun 23, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 80 additions & 1 deletion renderapi/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -1626,6 +1626,85 @@ def dataString(self):
shapestring, betastring, meanstring, varstring, dimstring)


class ThinPlateSplineTransform(Transform):
"""
render-python class that can hold a dataString for
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)
self.labels = labels
self.transformId = transformId
self.className = (
'mpicbg.trakem2.transform.ThinPlateSplineTransform')

def _process_dataString(self, dataString):
fields = dataString.split(" ")

self.ndims = int(fields[1])
self.nLm = int(fields[2])

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:]
except ValueError:
raise RenderError(
"inconsistent sizes and array lengths, \
in ThinPlateSplineTransform dataString")
else:
self.aMtx = None
self.bVec = None

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 ValueError:
raise RenderError(
"inconsistent sizes and array lengths, \
in ThinPlateSplineTransform 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)


class NonLinearTransform(NonLinearCoordinateTransform):
className = 'mpicbg.trakem2.transform.nonLinearTransform'

Expand Down
43 changes: 43 additions & 0 deletions renderapi/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import inspect
import copy
import json
import base64
import zlib

import numpy
import requests
Expand Down Expand Up @@ -358,3 +360,44 @@ 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 : 1D numpy array
floating point values to be encoded

Returns
-------
encoded: string
"""
return base64.b64encode(
zlib.compress(
src.byteswap().tobytes())
).decode('utf-8')


def decodeBase64(src):
"""decode a string
encoded in base64 binary-to-text encoding
same as in trakem2...ThinPlateSplineTransform.java

Parameters
----------
src : string
encoded string

Returns
-------
arr: length n numpy array of double-precision floats
"""
if src[0] == '@':
b = base64.b64decode(src[1:])
else:
b = zlib.decompress(base64.b64decode(src))
return numpy.frombuffer(b).byteswap()
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ numpy
pillow
sphinxcontrib-napoleon
decorator
six
six
3 changes: 3 additions & 0 deletions test/rendersettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down
6 changes: 6 additions & 0 deletions test/test_files/thin_plate_spline.json

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions test/test_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -600,3 +600,40 @@ 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():
j = json.load(open(rendersettings.TEST_THINPLATESPLINE_FILE, 'r'))
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)
# 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():
# case for Stephan's '@' character
s = '@QAkh+fAbhm6/8AAAAAAAAA=='
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)
assert(np.all(x == y))