Closed eberrigan closed 1 month ago
A new class named SkeletonEncoder
has been introduced in the sleap/skeleton.py
file, replacing the existing jsonpickle.encode
functionality with a custom encoder for converting Python objects into JSON strings. The SkeletonEncoder
includes methods for encoding Node
and EdgeType
objects, while the to_json
method in the Skeleton
class has been updated to utilize this new encoder. Additionally, tests for encoding and decoding Skeleton
objects have been added, and the attrs
package version constraints have been specified more clearly in the environment files.
File | Change Summary |
---|---|
sleap/skeleton.py | Added new class SkeletonEncoder for custom encoding; modified to_json method to use SkeletonEncoder . |
environment.yml | Updated attrs package version constraint to >=21.2.0 . |
environment_no_cuda.yml | Updated attrs package version constraint to >=21.2.0 . |
tests/test_skeleton.py | Added tests for encoding and decoding Skeleton objects using SkeletonEncoder . |
SkeletonDecoder
class in this PR is directly related to the SkeletonEncoder
class in the main PR, as both classes are involved in the (de)serialization of Node
and EdgeType
objects within the sleap/skeleton.py
file.In the code where rabbits play,
A new encoder hops today.
Skeletons now dance with glee,
Encoding joyfully, you see!
With every node, a match so bright,
In the world of bytes, all feels right! 🐇✨
Attention: Patch coverage is 98.41270%
with 1 line
in your changes missing coverage. Please review.
Project coverage is 75.50%. Comparing base (
7ed1229
) to head (83a2704
). Report is 52 commits behind head on develop.
Files with missing lines | Patch % | Lines |
---|---|---|
sleap/skeleton.py | 98.41% | 1 Missing :warning: |
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
Related:
Description
Looking at our
fly_skeleton_legs.json
test data: Our finaljson_str
has keysdirected
,graph
,links
,multigraph
,nodes
.directed
is a boolean. (UNCHANGED from input graph)graph
is a dict with keysname
andnum_edges_inserted
. (UNCHANGED from input graph)links
is a list of dicts with keysedge_insert_idx
,key
,source
,target
,type
, for each edge.multigraph
is a boolean = True. (UNCHANGED from input graph)nodes
is a list of dicts with keysid
for each node.Node(name='neck', weight=1.0)
"links":[{... source: { Node(name='neck', weight=1.0) }
{"py/object": "sleap.skeleton.Node", "py/state": {"py/tuple": ["neck", 1.0]}}
Node(name='head', weight=1.0)
"links":[{... target: { Node(name='head', weight=1.0) }
{"py/object": "sleap.skeleton.Node", "py/state": {"py/tuple": ["head", 1.0]}}
<EdgeType.BODY: 1>
"links":[{... type: { <EdgeType.BODY: 1> }
{"py/reduce": [{"py/type": "sleap.skeleton.EdgeType"}, {"py/tuple": [1]}, null, null, null]}
Node(name='head', weight=1.0)
"nodes": [{'id': Node(name='head', weight=1.0)}, ..., ]
{"id": {"py/id": 2}}
This is because
Skeleton._graph
is passed throughjson_graph.node_link_data
, which returns a dictionary with node-link formatted data (see documentation here.links
and notlink
.networkx
pinned. I am not sure why that hasn't been a problem.Then the node-link formatted data is passed to
jsonpickle.encode(data)
.Node
. EachNode
object, which has attributesname
andweight
, is encoded as a Python objectsleap.skeleton.Node
. TheNode
's internal state is represented as a Python tuple containing thename
(as a string) andweight
(as a numerical value):["name", weight]
.node_to_idx
mapping (https://github.com/talmolab/sleap/blob/3c7f5afa8dd952fdee8ef3c8ac94b0689dac9cdb/sleap/skeleton.py#L1002-L1006), i.e. when the skeleton is fromLabels
, the node is returned as an integer without the name and weight attributes.EdgeType
.EdgeType
inherits from Python'sEnum
class. The possible edge types areBODY = 1
andSYMMETRY = 2
. When serialized,EdgeType
is represented using Python'sreduce
function, storing the type assleap.skeleton.EdgeType
and the value as a Python tuple containing theEnum
value.If the object has been "seen" before, it will not be encoded as the full JSON string but referenced by its
py/id
, which starts at 1 and indexes the objects in the order they are seen so that the second time the first object is used, it will be referenced as{"py/id": 1}
.source
ortarget
in thelinks
list, it is encoded as a py/object with a py/state. Then it is referenced later on using the py/id.nodes
then uses only the py/id to reference theNode
.jsongraph.node_link_data()
tojsonpickle.encode()
(shown below) which hasnodes
first andlinks
after.source
,target
andtype
: in the graph belowtype
comes beforesource
andtarget
so unless explicitly ordered aftersource
andtarget
the py/id will not be consistent with the legacy data.SkeletonEncoder
called_encode_links
that is used within the classmethodencode
to encode thelinks
first, and within thelinks
list, encode thesource
,target
and then thetype
.EdgeType
would be decoded as aNode
and the edges would not be formed correctly in the decodedSkeleton
usingSkeleton.from_json
.A test has been added which loads a skeleton from a JSON file, get the graph with
json_graph.node_link_data
fromnetworkx
, encodes the graph with the newSkeletonEncoder.encode()
method, then usesSkeleton.from_json
to deserialize the JSON string (withjsonpickle.decode
, thenjson_graph.node_link_graph
). TheSkeleton.matches()
method is used to test the equivalence of the deserializedSkeletons
. [X] This test could be parametrized to do with various skeleton fixtures. [X] We should make sure to do this with a template fixture at least. [X] An additional test could be added to see if the serialized JSON strings are equivalent.attrs
is unconstrained (see github run) because with the version ofattrs
pulled in, the nodes arejson_pickle
fails to decode theNode
s which have been encoded as tuples by ourSkeletonEncoder.encode()
(to maintain backwards compatibility).Types of changes
Does this address any currently open issues?
1470 #1918
This pull request accompanies #1961 in handling JSON encoding and decoding internally instead of relying on
jsonpickle
.Outside contributors checklist
Thank you for contributing to SLEAP!
:heart:
Summary by CodeRabbit
Summary by CodeRabbit
New Features
SkeletonEncoder
class for enhanced encoding of skeleton representations.Node
andEdgeType
objects into JSON formats.Chores
attrs
package in environment configuration files.Tests
Skeleton
objects.