Skip to content

Commit c4a1ba4

Browse files
authored
Merge pull request #160 from AllenInstitute/develop
Develop -> master
2 parents 4bca171 + d9e2f26 commit c4a1ba4

File tree

12 files changed

+359
-26
lines changed

12 files changed

+359
-26
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 2.3.0
2+
current_version = 2.4.0
33
commit = True
44
tag = True
55

.travis.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
language: python
22
python:
33
- "2.7"
4-
- "3.5"
54
- "3.6"
6-
- "3.6-dev" # 3.6 development branch
75
- "3.7-dev"
86
services:
97
- docker
@@ -35,11 +33,14 @@ before_install:
3533
- mkdir -p /tmp/example_1 && cp -R $RENDER_WS_JAVA_CLIENT_EXAMPLE_DATA/example_1 /tmp/.
3634
- cp -R $RENDER_APP_EXAMPLE_DATA/* /tmp/.
3735
- export RENDER_EXAMPLE_DATA=/tmp
36+
- echo "$DOCKER_PASSWORD" | docker login --username "$DOCKER_USERNAME" --password-stdin
3837
- docker-compose up -d
3938
env:
4039
global:
4140
- RENDER_HOST=localhost RENDER_PORT=8080 RENDER_CLIENT_SCRIPTS=$TRAVIS_BUILD_DIR/render/render-ws-java-client/src/main/scripts
42-
- secure: "gIvBfWzWZizVxIW3eJfMX4V4/wrtn6zVoI9lkDbnXgn42+jIUdZYLEnHdZDtgOYtwUHUiY0UuXX/D9pl5XK+5IrZ+ocRKv8vIL1APJNxJGWJThkk8ifQFn+fuG2MvlA1b3z3903FNMSw3AcG0479YucZkPx7TmexU5DHM/zf/+/KvCV5qDPKjZu4hL3o/njuwNZGpYmT0+pUvugiAhF1c5YBmPBMhaplUYKdjuGMeLOlB/QxmBdkTPWCeVda8ohB3ngGc2yjhtDPAaOo9LKNrdLje6DCr2Zv6e4qo27YNFpXoICN0qUPOaoGOoVJ/HOq8HUZgJMi9KiYH2oqP27RqPOHaGABK7PF5aMg6vvUBcCYP4n93rnbm7g4k/+A/mUeew9lXyrh8+O9d8ul5eNfLdaQ1HGnR093Vx53Sr7Q9lyNU/PUteMGzRYQw2EQGuryGiAKJ5tWD9HIHtqQ7iNZup8ijHCKVdmtLHROTHMvpcagQ33Sl/+U05E50YoDZRzvTNSaWUoNhl01xRIyp35KYy55Ei2X8eMTJlirdzKj1hAeOED98SmKx8ROypxJDzcO/N8AGlPRc9/l+OplKTvpnlD3Nsd/bxErWEmrBDtAkY05rfR48e9xtDyqV4KuAXy0wBJn5eqFEFpNNxzMi78CHsF023UodVsWAzn4HvAojrQ="
41+
- secure: "AZCuswnHLEvDkfcmijSb4sy1E0vCg3nHn8gk9EvO7z9JeJG4FtHYBW3bLiN4WSjxszzaCK16lku0RY4OL2gCv0C87iHaXkDqAWwN0YIFGOkzDhZJRD5CENTsx1w/JFySqTBNiO82gIAnNP+J19mFm1ArtNhDIYAiUPo+CseG8S0cpbQqQe3tBd+XB/u32PUS1zA1mIg6qDXKupoDCUuWBxCZ01Jdv/YDUH3zxz6K9SQmbzJ4m5B1TCw8E6r/dj6mBSjJSh2XmZlOU0Ha2U9nEkkkr7nbNU1GYQYRt9uiqDH4juvFw6WePP+MVzFJCq0Hqdyb9lk/7jh4BX5ffN2o/vs14FuXZ57wblcHZkkEgD0ViIlGjqgPn0WuTJstkVlpvvwuweVfXXxJrYAZwhf14goKiWneDZMJ6g82Z7r9GVQFwEwl/mu5fdLP/qKZ8i3AAd9SgnGdn5eQ/xD5JVJj9JZFqfLUJs7JQreGYfW2r5URIL3Kd2laRucUD4IPwnzHT+VjgNHFtv07bmNxRGPdwC4pbWOMiyq2BvG3JFw/y8j3bNvLxK6XLhDgHtV13NinZx8uZcocb8yCNyBpez+Pfo3RzTcHKnF5foKoEEZIU5OuyGGgeHUup/vg2qsKGn3Wl1nH5fWSbdR2pPHLSD+mpZdLAA9JjqEfVrhZtoXLUpA="
42+
- secure: "tnw8YYrVoasmJ+3I3VjENfx6BIBG2qaJ4m0+rUR9dal7cmrGTrcyJ1d41Nm+K1XKRvLvBkEHKBzdEVlg2XaAOVBTam6u+zW/Dc16ZXgz7yK1qXtytwiXBe2P7c9HePXIDA/SCapKXmjF5/Zt0BdpXv4fPyK8kbdIjels9Xt8teDTfSxJ6AfXlfXXPiF6A1Lzgf+z9GRL9GXZrgic6itFdNfgErGThgefQSJKr3wKR/wRyx87QD5juzrZtcwtFzcvvO8X8n6D+oOdc9XfRsE1EgFO8RHla32c0BbcKmRP8vwwtfVUt1tqU3SQZP6+n9EUBiSUR9/B50hCOiX5O2RdtHIMncJT0n4ZQe2PmTmwpcY1bU1NJpUszxPXNZrEHRdGqbXfiQq8+sONAWZcrzbqLsP2cXpt+4FrHk+iepu0RBLpLq/UdcVLozELgkNxM1C9k3OF6OlAC//5jWi4hjeoc2L8LpR09Cxm7PCtBvqbCNO2mPhv+h4dsFzmlyN2JL34lQyfzD1v1Jqz4ifJHYupKKtIdA7UiU+RU40ylDzTKfvlI63MkUwxS2u4BY0Gc5WWy/NoJuF6F39c+peoLgcoHiKnXoIvTyWCNSfieXCnuiHrU7nLnijM9qzSZxM6a43KiSGRZAQInQUXzKX5a7cGHaQIFskd3TK71emnhdewM40="
43+
- secure: "UucVIVQCg9T2d9NiSdMirj37nlwWEyZi2mybzNNVJ8TMfC05Vz+E/b/1ByxOLpUYmXZTzU22Xx316ueIaIk05L+Q8q4Dy1S/spaKBNcJQ8kDxlcD7y0YBPX+QqyOpXCnB1Kt1oawy92g2qALFDMBoupr7xT8aTvCwDigrJYghMiVM1RVlQQAJBaGkB32qs934uQeknvLqSZV35BTFGHTaqJoWV8OyxNwOZFtf5Z8gdWxrAYHOQg9iW+5CP+zLhybROLrMuI5vhj/yvP2I07Scx+jMKOgGw7snPygHpwR0dNw/NvNdy+XIx3zqUlts5Bb1nqCeaNzNouXd1D5NHoqsw/0VLt/HcbCxUKXuaViJy5KfN/qi9U7lSJ0KC2qazwsQXviFYTrT2q1U2n6zlV/CC9e1sLjQmIF3GAysFD3Fcg88Qz/3aZKl6nkqP/T2Y1t/FOPjBSD5ytlpr8xbs6rpxZMU/k91agD5UeP4/lwbncfsJswPNajdTz5scWjHPACDLIxYTwEry64z808lHcc23c9MIeqrkXMI4nPYnLSPQyhXBOJncEugKernBh053Pu5+hDrg9bsoIjtsGmh+p9GnIjrtPAkc8g4tyU+v0PKMVUkqw+mvkVEgzRlKUzURuRTVy5lTHisu0mo6Xmtgw0qfVkTOa2Gnr9gsfmaTToyXY="
4344
# command to run tests
4445
script:
4546
- python setup.py test # or py.test for Python versions 3.5 and below
@@ -49,4 +50,4 @@ cache:
4950
- $HOME/.m2
5051
after_success:
5152
- codecov -t 3f12d985-af62-455d-a11d-9669c039640d
52-
- "BRANCHES_TO_MERGE_REGEX='develop' BRANCH_TO_MERGE_INTO=master GITHUB_REPO=fcollman/render-python .travis/merge_script.sh"
53+
- "BRANCHES_TO_MERGE_REGEX='develop' BRANCH_TO_MERGE_INTO=master GITHUB_REPO=AllenInstitute/render-python .travis/merge_script.sh"

README.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
.. image:: https://readthedocs.org/projects/render-python/badge/
22
:target: http://render-python.readthedocs.io/en/latest/
33
:alt: Documentation Status
4-
.. image:: https://travis-ci.org/fcollman/render-python.svg?branch=master
5-
:target: https://travis-ci.org/fcollman/render-python
4+
.. image:: https://travis-ci.com/AllenInstitute/render-python.svg?branch=master
5+
:target: https://travis-ci.com/AllenInstitute/render-python
66
:alt: Build Status
7-
.. image:: https://codecov.io/gh/fcollman/render-python/branch/master/graph/badge.svg
8-
:target: https://codecov.io/gh/fcollman/render-python
7+
.. image:: https://codecov.io/gh/AllenInstitute/render-python/branch/master/graph/badge.svg
8+
:target: https://codecov.io/gh/AllenInstitute/render-python
99

1010
render-python
1111
#############

integration_tests/test_client_integrated.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,6 @@ def teststack2(render, render_example_tilespec_and_transforms):
185185
renderapi.stack.delete_stack(stack, render=render)
186186

187187

188-
189188
def test_tile_pair_client(render, teststack, **kwargs):
190189
zvalues = np.array(renderapi.stack.get_z_values_for_stack(
191190
teststack, render=render))
@@ -196,6 +195,21 @@ def test_tile_pair_client(render, teststack, **kwargs):
196195
assert isinstance(tilepairjson, dict)
197196
assert len(tilepairjson['neighborPairs']) > 3
198197

198+
multitpjson_tmp, multitpfiles_tmp = renderapi.client.tilePairClient(
199+
teststack, np.min(zvalues), np.max(zvalues), outjson=None,
200+
render=render, maxPairsPerFile=2, return_jsonfiles=True, **kwargs)
201+
202+
with tempfile.NamedTemporaryFile(
203+
suffix=".json", mode='r', delete=False) as f:
204+
outjson_specified = f.name
205+
206+
multitpjson, multitpfiles = renderapi.client.tilePairClient(
207+
teststack, np.min(zvalues), np.max(zvalues), outjson=outjson_specified,
208+
render=render, maxPairsPerFile=2, return_jsonfiles=True, **kwargs)
209+
210+
assert len(multitpjson["neighborPairs"]) == len(tilepairjson["neighborPairs"]) == len(multitpjson_tmp["neighborPairs"])
211+
assert len(multitpfiles) == len(multitpfiles_tmp) == len(tilepairjson["neighborPairs"]) // 2
212+
199213

200214
@pytest.mark.parametrize("bounds,raises", [
201215
({}, True),

renderapi/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from .render import connect
1414
from .render import Render
1515

16-
__version__ = "2.3.0"
16+
__version__ = "2.4.0"
1717
__all__ = ['render', 'client', 'tilespec', 'errors',
1818
'stack', 'image', 'pointmatch', 'coordinate',
1919
'connect', 'transform', 'resolvedtiles', 'Render']

renderapi/client/client_calls.py

Lines changed: 85 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
import json
33
import os
4+
import re
45
import subprocess
56
import tempfile
67

@@ -135,6 +136,11 @@ def tilePairClient(stack, minz, maxz, outjson=None, delete_json=False,
135136
excludeSameSectionNeighbors=None,
136137
excludePairsInMatchCollection=None,
137138
minx=None, maxx=None, miny=None, maxy=None,
139+
useRowColPositions=None, existingMatchOwner=None,
140+
minExistingMatchCount=None, onlyIncludeTilesFromStack=None,
141+
onlyIncludeTilesNearTileIdsJson=None, maxPairsPerFile=None,
142+
return_jsondata=True,
143+
return_jsonfiles=False,
138144
subprocess_mode=None,
139145
host=None, port=None, owner=None, project=None,
140146
client_script=None, memGB=None,
@@ -191,11 +197,29 @@ def tilePairClient(stack, minz, maxz, outjson=None, delete_json=False,
191197
minimum y bound from which tile 'p' is selected
192198
maxy : float
193199
maximum y bound from wich tile 'p' is selected
200+
useRowColPositions : bool
201+
whether to use raster positions for neighbor analysis rather
202+
than radial cutoff
203+
existingMatchOwner : str
204+
owner for the excludePairsInMatchCollection collection
205+
minExistingMatchCount : int
206+
minimum match threshold to exclude pairs
207+
present in excludePairsInMatchCollection collection
208+
onlyIncludeTilesFromStack : str
209+
only include tiles which exist in this stack
210+
onlyIncludeTilesNearTileIdsJson : str
211+
path to json listing tileIds which should be paired
212+
maxPairsPerFile : int
213+
maximum neighborPairs per file. Generating more pairs than
214+
this number (default 100000) will generate additional json
215+
files with a p[0-9]+ suffix on the root.
194216
195217
Returns
196218
-------
197-
:obj:`list` of :obj:`dict`
219+
:obj:`list` of :obj:`dict`, optional
198220
list of tilepairs
221+
:obj:`list` of string, optional
222+
list of json files containing tilepairs
199223
"""
200224
if outjson is None:
201225
with tempfile.NamedTemporaryFile(
@@ -219,6 +243,18 @@ def tilePairClient(stack, minz, maxz, outjson=None, delete_json=False,
219243
'--excludeSameSectionNeighbors') +
220244
get_param(excludePairsInMatchCollection,
221245
'--excludePairsInMatchCollection') +
246+
get_param(useRowColPositions,
247+
'--useRowColPositions') +
248+
get_param(existingMatchOwner,
249+
'--existingMatchOwner') +
250+
get_param(minExistingMatchCount,
251+
'--minExistingMatchCount') +
252+
get_param(onlyIncludeTilesFromStack,
253+
"--onlyIncludeTilesFromStack") +
254+
get_param(onlyIncludeTilesNearTileIdsJson,
255+
'--onlyIncludeTilesNearTileIdsJson') +
256+
get_param(maxPairsPerFile,
257+
'--maxPairsPerFile') +
222258
['--toJson', outjson] +
223259
get_param(minx, '--minX') + get_param(maxx, '--maxX') +
224260
get_param(miny, '--minY') + get_param(maxy, '--maxY'))
@@ -228,12 +264,45 @@ def tilePairClient(stack, minz, maxz, outjson=None, delete_json=False,
228264
subprocess_mode=subprocess_mode,
229265
add_args=argvs, **kwargs)
230266

231-
with open(outjson, 'r') as f:
232-
jsondata = json.load(f)
267+
# We create the jsonfile, so if it is empty it could be multiple
268+
if not os.path.isfile(outjson) or os.stat(outjson).st_size == 0:
269+
if os.path.isfile(outjson):
270+
# outjson we created is empty, so remove it
271+
os.remove(outjson)
272+
273+
outbn_root, outbn_ext = os.path.splitext(os.path.basename(outjson))
274+
outbn_pattern = "{root}_p[0-9]+{ext}".format(
275+
root=outbn_root, ext=outbn_ext)
276+
jsonfiles = [
277+
os.path.join(os.path.dirname(outjson), bn)
278+
for bn in os.listdir(os.path.dirname(outjson))
279+
if re.match(outbn_pattern, bn)]
280+
else:
281+
jsonfiles = [outjson]
282+
283+
if return_jsondata:
284+
jsondata_list = []
285+
for jsonfile in jsonfiles:
286+
with open(jsonfile, 'r') as f:
287+
jsondata_list.append(json.load(f))
288+
if len({d["renderParametersUrlTemplate"] for d in jsondata_list}) != 1: # pragma: no cover
289+
raise ValueError(
290+
"Found tilepair files with disparate "
291+
"renderParametersUrlTemplate values. "
292+
"Maybe there are additional files "
293+
"matching the outjson pattern?")
294+
pairdata = [i for l in (d["neighborPairs"] for d in jsondata_list)
295+
for i in l]
296+
jsondata = dict(jsondata_list[0], **{"neighborPairs": pairdata})
233297

234298
if delete_json:
235-
os.remove(outjson)
236-
return jsondata
299+
for jsonfile in jsonfiles:
300+
os.remove(jsonfile)
301+
302+
return ((jsondata, jsonfiles) if return_jsonfiles and return_jsondata
303+
else jsondata if return_jsondata
304+
else jsonfiles if return_jsonfiles
305+
else None)
237306

238307

239308
@renderclientaccess
@@ -346,10 +415,11 @@ def renderSectionClient(stack, rootDirectory, zs, scale=None,
346415
maxIntensity=None, minIntensity=None, bounds=None,
347416
format=None, channel=None, customOutputFolder=None,
348417
customSubFolder=None, padFileNamesWithZeros=None,
349-
doFilter=None, fillWithNoise=None, imageType=None,
350-
subprocess_mode=None, host=None, port=None, owner=None,
351-
project=None, client_script=None, memGB=None,
352-
render=None, **kwargs):
418+
resolutionUnit=None, doFilter=None, fillWithNoise=None,
419+
imageType=None, subprocess_mode=None, host=None,
420+
port=None, owner=None, project=None,
421+
client_script=None, memGB=None, render=None,
422+
**kwargs):
353423
"""run RenderSectionClient.java
354424
355425
Parameters
@@ -379,6 +449,9 @@ def renderSectionClient(stack, rootDirectory, zs, scale=None,
379449
folder to save all images in under outputFolder (overrides default of none)
380450
padFileNamesWithZeros: bool
381451
whether to pad file names with zeros to make sortable
452+
resolutionUnit: str
453+
if format is tiff and unit is specified (e.g. as 'nm'), include resolution data
454+
in rendered tiff headers.
382455
imageType: int
383456
8,16,24 to specify what kind of image type to save
384457
doFilter : str
@@ -418,9 +491,11 @@ def renderSectionClient(stack, rootDirectory, zs, scale=None,
418491
get_param(customOutputFolder, '--customOutputFolder') +
419492
get_param(imageType, '--imageType') +
420493
get_param(channel, '--channels') +
421-
get_param(customSubFolder, '--customSubFolder') +
494+
get_param(customSubFolder, '--customSubFolder') +
422495
get_param(padFileNamesWithZeros, '--padFileNamesWithZeros') +
496+
get_param(resolutionUnit, '--resolutionUnit') +
423497
bound_param + zs)
498+
424499
call_run_ws_client('org.janelia.render.client.RenderSectionClient',
425500
memGB=memGB, client_script=client_script,
426501
subprocess_mode=subprocess_mode, add_args=argvs,

renderapi/pointmatch.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,83 @@
1111
logger.addHandler(NullHandler())
1212

1313

14+
def copy_match_explicit(match):
15+
"""create independent match dictionary equivalent to the input match
16+
significantly faster than e.g. copy.deepcopy or json serialization
17+
18+
Parameters
19+
----------
20+
match : dict
21+
match dictionary
22+
23+
Returns
24+
-------
25+
new_match : dict
26+
match dictionary equivalent to input match
27+
28+
"""
29+
# explicitly copy match dictionary since it contains lists
30+
new_matchd = {
31+
"p": [i[:] for i in match["matches"]["p"]],
32+
"q": [i[:] for i in match["matches"]["q"]],
33+
"w": match["matches"]["w"][:]
34+
}
35+
36+
new_match = {k: (match[k] if k != "matches" else new_matchd)
37+
for k in match.keys()}
38+
return new_match
39+
40+
41+
def copy_matches_explicit(matches):
42+
"""create independent match dictionaries equivalent to the input matches
43+
significantly faster than e.g. copy.deepcopy or json serialization
44+
45+
Parameters
46+
----------
47+
matches : list[dict]
48+
list of match dictionaries to copy
49+
50+
Returns
51+
-------
52+
new_matches : list[dict]
53+
list of match dictionaries equivalent to input matches
54+
55+
"""
56+
return [copy_match_explicit(match) for match in matches]
57+
58+
59+
def swap_matchpair(match, copy=True):
60+
"""
61+
62+
Parameters
63+
----------
64+
match : dict
65+
match dictionary to swap p->q,q->p
66+
copy : bool
67+
whether to return a copy which, when modified, does not change original
68+
69+
Returns
70+
-------
71+
new_match : dict
72+
match dictionary with "p" and "q" swapped
73+
"""
74+
updated_d = {
75+
"pId": match["qId"],
76+
"qId": match["pId"],
77+
"qGroupId": match["pGroupId"],
78+
"pGroupId": match["qGroupId"],
79+
"matches": {
80+
"p": match["matches"]["q"],
81+
"q": match["matches"]["p"],
82+
"w": match["matches"]["w"] # include weight because shallow swap
83+
}
84+
}
85+
86+
new_match = {k: updated_d.get(k, v) for k, v in match.items()}
87+
88+
return (copy_match_explicit(new_match) if copy else new_match)
89+
90+
1491
@renderaccess
1592
def get_matchcollection_owners(host=None, port=None,
1693
session=requests.session(),

renderapi/resolvedtiles.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,32 @@ def from_dict(self, d):
5353
""" # noqa: E501
5454

5555

56+
def combine_resolvedtiles(rts_l):
57+
"""combine multiple ResolvedTiles objects into a single ResolvedTiles
58+
59+
Like render, this has an implicit expectation that transformIds and tileIds
60+
are unique among all input objects
61+
62+
Parameters
63+
----------
64+
rts_l : list[renderapi.resolvedtiles.ResolvedTiles]
65+
list of ResolvedTiles objects to combine
66+
67+
Returns
68+
-------
69+
rts : renderapi.resolvedtiles.ResolvedTiles
70+
combined ResolvedTiles object
71+
"""
72+
tforms = list({tform.transformId: tform for l in (
73+
rts.transforms for rts in rts_l)
74+
for tform in l}.values())
75+
tspecs = list({ts.tileId: ts for l in (
76+
rts.tilespecs for rts in rts_l)
77+
for ts in l}.values())
78+
79+
return ResolvedTiles(transformList=tforms, tilespecs=tspecs)
80+
81+
5682
@renderaccess
5783
def put_tilespecs(stack, resolved_tiles=None, deriveData=True,
5884
tilespecs=None, shared_transforms=None,

renderapi/stack.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def __init__(self, cycleNumber=None, cycleStepNumber=None,
5050
self.stackResolutionZ = stackResolutionZ
5151
self.mipmapPathBuilder = mipmapPathBuilder
5252
self.materializedBoxRootPath = materializedBoxRootPath
53-
self.createTimestamp = (strftime('%Y-%M-%dT%H:%M:%S.00Z') if
53+
self.createTimestamp = (strftime('%Y-%m-%dT%H:%M:%S.00Z') if
5454
createTimestamp is None else createTimestamp)
5555
self.versionNotes = versionNotes
5656

0 commit comments

Comments
 (0)