tracee 0.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ from argparse import ArgumentParser as ap
4
+ import numpy as np
5
+ import tracee
6
+
7
+
8
+ if __name__ == '__main__':
9
+ parser = ap(description='dump k-NN graph')
10
+
11
+ parser.add_argument(
12
+ 'input', type=str, help='input file name')
13
+ parser.add_argument(
14
+ 'k', type=int, help='the number of neighbor')
15
+ parser.add_argument(
16
+ 'output', type=str, help='output file name')
17
+
18
+ args = parser.parse_args()
19
+
20
+ data = np.loadtxt(args.input)
21
+ edge = tracee.generate_edge(data, args.k, 200)
22
+
23
+ v0 = data[edge[:,0],:]
24
+ v1 = data[edge[:,1],:]
25
+ els = np.hstack((v0,v1))
26
+
27
+ print(els)
28
+ fmt = ('%.3f','%.3f','%d','%.3f','%.3f','%d')
29
+ np.savetxt(args.output, els, fmt=fmt)
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ from argparse import ArgumentParser as ap
4
+ from time import time
5
+ import numpy as np
6
+
7
+ header = '''Generated Mock Data
8
+
9
+ Parameters:
10
+ number of objects: {n_object:d}
11
+ number of frames: {n_frame:d}
12
+ number of distractors: {n_distractor:d}
13
+ object velocity: {velocity:.1f}
14
+ object drop rate: {drop:.1f}
15
+ field of view size: {size:d}
16
+ random seed: {seed:d}
17
+ Columns:
18
+ 1st column: x position
19
+ 2nd column: y position
20
+ 3rd column: timestamp
21
+ '''
22
+ savefmt = ('%12.6f','%12.6f','%4d')
23
+
24
+
25
+ if __name__ == '__main__':
26
+ parser = ap(description='generate mock data')
27
+
28
+ parser.add_argument(
29
+ 'n_object', type=int,
30
+ help='the number of moving objects')
31
+ parser.add_argument(
32
+ 'n_frame', type=int,
33
+ help='the number of frames')
34
+ parser.add_argument(
35
+ 'n_distractor', type=int,
36
+ help='the number of distractors')
37
+ parser.add_argument(
38
+ '-v', '--velocity', type=int, default=5.0,
39
+ help='the velocity of moving objects')
40
+ parser.add_argument(
41
+ '-s', '--scatter', type=float, default=0.2,
42
+ help='scatter in the positions')
43
+ parser.add_argument(
44
+ '--drop', type=float, default=0.0,
45
+ help='drop rate between [0,1]')
46
+ parser.add_argument(
47
+ '--seed', type=int, default=None,
48
+ help='the seed of random generator')
49
+ parser.add_argument(
50
+ '--size', type=int, default=512,
51
+ help='the size of the field of view')
52
+ parser.add_argument(
53
+ '--margin', type=int, default=64,
54
+ help='margins of the field of view')
55
+ parser.add_argument(
56
+ '-q', '--quiet', action='store_true',
57
+ help='disable plot')
58
+
59
+ args = parser.parse_args()
60
+ shape = (args.n_object,1)
61
+ if args.seed is None:
62
+ args.seed = round(time())%65536
63
+ np.random.seed(args.seed)
64
+
65
+ L = args.size-2*args.margin
66
+ x0 = L*np.random.uniform(size=shape)+args.margin
67
+ y0 = L*np.random.uniform(size=shape)+args.margin
68
+ vx = args.velocity*np.random.normal(size=shape)
69
+ vy = args.velocity*np.random.normal(size=shape)
70
+ t = np.atleast_2d(np.arange(args.n_frame))
71
+
72
+ x,y = (x0+vx*t).flatten(),(y0+vy*t).flatten()
73
+ tt = np.tile(t,args.n_object).flatten()
74
+ x += np.random.normal(0, args.scatter, size=x.shape)
75
+ y += np.random.normal(0, args.scatter, size=y.shape)
76
+
77
+ position = np.vstack((x,y,tt)).T
78
+ ## remove vertices outside of the field of view
79
+ flag = (x>0) & (x<args.size) & (y>0) & (y<args.size)
80
+ position = position[flag,:]
81
+ ## remove random objects
82
+ flag = np.random.uniform(size=position.shape[0])>args.drop
83
+ position = position[flag,:]
84
+
85
+ ## add distractors
86
+ if args.n_distractor>0:
87
+ dx = np.random.uniform(args.size, size=args.n_distractor)
88
+ dy = np.random.uniform(args.size, size=args.n_distractor)
89
+ dt = np.random.uniform(args.n_frame, size=args.n_distractor)
90
+ dist = np.vstack((dx,dy,dt)).T
91
+ position = np.concatenate((position,dist))
92
+
93
+ ## sort position list chronologically
94
+ idx = np.argsort(position[:,2])
95
+ position = position[idx,:]
96
+
97
+ filename = 'mockdata_N{:03d}F{:03d}D{:03d}d{:02d}_{:04x}.txt'.format(
98
+ args.n_object,args.n_frame,args.n_distractor,
99
+ round(args.drop*100),args.seed)
100
+ np.savetxt(filename, position, fmt=savefmt,
101
+ header=header.format(**args.__dict__))
102
+
103
+ if args.quiet is False:
104
+ import matplotlib.pyplot as plt
105
+ fig = plt.figure(figsize=(10,10))
106
+ ax = fig.add_subplot()
107
+ ax.scatter(position[:,0],position[:,1],marker='+')
108
+ fig.tight_layout()
109
+ plt.show()
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ from argparse import ArgumentParser as ap
4
+ import numpy as np
5
+ import tracee
6
+
7
+
8
+ if __name__ == '__main__':
9
+ parser = ap(description='dump tracklet')
10
+
11
+ parser.add_argument(
12
+ 'input', type=str, help='input file name')
13
+ parser.add_argument(
14
+ 'k', type=int, help='the number of neighbor')
15
+ parser.add_argument(
16
+ 'output', type=str, help='output file name')
17
+ parser.add_argument(
18
+ '-L', '--limit', type=int, default=10,
19
+ help='shortest tracklet to be extracted')
20
+
21
+ args = parser.parse_args()
22
+
23
+ data = np.loadtxt(args.input)
24
+ param = tracee.default_parameters()
25
+ param.limit = args.limit
26
+ tracklet = tracee.extract(data, n_neighbor=args.k, param=param)
27
+
28
+ v0 = np.array([t.tail for t in tracklet])
29
+ v1 = np.array([t.head for t in tracklet])
30
+ segment = np.hstack((v0,v1))
31
+
32
+ print(segment)
33
+ fmt = ('%.3f','%.3f','%d','%.3f','%.3f','%d')
34
+ np.savetxt(args.output, segment, fmt=fmt)
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ import numpy as np
4
+ import tracee
5
+
6
+ # Load the data table.
7
+ # The table should have three columns.
8
+ filename = 'sample/mockdata_N006F030D500d00_b7e8.txt'
9
+ data = np.loadtxt(filename)
10
+
11
+ # Generate a parameter set.
12
+ param = tracee.default_parameters()
13
+ param.limit = 10 # Set the minimum number of points to 10.
14
+
15
+ # Call tracee.extract to identify tracklets.
16
+ # The function returns a list of Tracklet instances.
17
+ # The Tracklet instance contains
18
+ # - tail: the starting point.
19
+ # - head: the terminal point.
20
+ # - members: a list of the associated points.
21
+ tracklet = tracee.extract(data, n_neighbor=3, param=param)
tracee/__init__.py ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ from fdlsgm import default_parameters
4
+ from .extract import extract, Tracklet
5
+ from .extract import generate_edge
6
+
7
+
8
+ __version__ = '0.0.4'
9
+
10
+ __all__ = [
11
+ '__version__',
12
+ 'extract',
13
+ 'Tracklet',
14
+ 'generate_edge',
15
+ 'default_parameters'
16
+ ]
tracee/extract.py ADDED
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ from dataclasses import dataclass
4
+ from typing import List
5
+ import minimalKNN as knn
6
+ import fdlsgm
7
+ import numpy as np
8
+
9
+
10
+ @dataclass
11
+ class Tracklet(object):
12
+ ''' Tracklet Container
13
+
14
+ Atrributes:
15
+ tail (ndarray):
16
+ The starting point of the tracklet.
17
+ The argument should be compatible with an numpy.ndarray, whose
18
+ number of elements should be three.
19
+ The array content is expectd to be (x, y, t).
20
+ head (ndarray):
21
+ The terminal point of the tracklet.
22
+ The argument should be compatible with an numpy.ndarray, whose
23
+ number of elements should be three.
24
+ The array content is expectd to be (x, y, t).
25
+ members (ndarray):
26
+ The associated members of the tracklet.
27
+ The argument should be a two-dimensional array compatible with an
28
+ numpy.ndarray. The shape of the array should be (N, 3), where N
29
+ is the number of the members. Each element should have the
30
+ (t, y, x)-coordinate of a vertex.
31
+ '''
32
+ tail: np.ndarray
33
+ head: np.ndarray
34
+ members: np.ndarray
35
+
36
+ def __post_init__(self):
37
+ ''' Validation of the argument is applied here. '''
38
+ try:
39
+ self.tail = np.array(self.tail)
40
+ assert self.tail.shape == (3, )
41
+ except Exception as e:
42
+ raise AssertionError(
43
+ 'the starting point should have three elements.') from e
44
+ try:
45
+ self.head = np.array(self.head)
46
+ assert self.head.shape == (3, )
47
+ except Exception as e:
48
+ raise AssertionError(
49
+ 'the terminal point should have three elements.') from e
50
+ try:
51
+ self.members = np.array(self.members)
52
+ assert self.members.ndim == 2
53
+ assert self.members.shape[1] == 3
54
+ except Exception as e:
55
+ raise AssertionError(
56
+ 'the shape of the members should be (N, 3).') from e
57
+
58
+
59
+ def generate_edge(vertex: np.ndarray,
60
+ n_neighbor: int,
61
+ max_velocity: float):
62
+ ''' generate edges using k-nearest neighbor graph
63
+
64
+ Parameters:
65
+ vertex (ndarray):
66
+ A list of extracted sources. The shape should be (N, 3),
67
+ where N is the number of the sources.
68
+ Each element should be an array of (x, y, t), where t is the
69
+ timestamp, y and x are the location of the source.
70
+ n_neighbor (int):
71
+ The number of neighbors (k).
72
+ max_velocity (float):
73
+ Remove the edges whose collesponding velocities are larger
74
+ than this velocity.
75
+
76
+ Return:
77
+ An ndarray that contains pairs of the vertex indices. The first index
78
+ indicates the starting vertex, while the second does the terminal.
79
+ '''
80
+ ## edges:
81
+ ## An ndarray with the shape of (N, 2), where N is the number of edges.
82
+ ## The first element is the index of the starting vertex, while the
83
+ ## second element is the index of the end vertex.
84
+ edge = np.array(knn.compressed_graph(vertex, n_neighbor))
85
+ ## return an empty list when no edge is found.
86
+ if len(edge) == 0:
87
+ return np.ndarray((0, 2), float)
88
+
89
+ ## drop edges with the same z-location.
90
+ z, y, x = vertex[:, 2], vertex[:, 1], vertex[:, 0]
91
+ edge = edge[z[edge[:, 0]] != z[edge[:, 1]]]
92
+ ## return an empty list when no edge remains.
93
+ if len(edge) == 0:
94
+ return np.ndarray((0, 2), float)
95
+
96
+ ## drop edges faster than max_velocity.
97
+ dx = x[edge[:, 0]] - x[edge[:, 1]]
98
+ dy = y[edge[:, 0]] - y[edge[:, 1]]
99
+ dz = z[edge[:, 0]] - z[edge[:, 1]]
100
+ sqv = (dx * dx + dy * dy) / (dz * dz)
101
+ edge = edge[sqv < max_velocity ** 2]
102
+ ## return an empty list when no edge remains.
103
+ if len(edge) == 0:
104
+ return np.ndarray((0, 2), float)
105
+
106
+ ## drop edges faster than max_velocity.
107
+ dx = x[edge[:, 0]] - x[edge[:, 1]]
108
+ dy = y[edge[:, 0]] - y[edge[:, 1]]
109
+ dz = z[edge[:, 0]] - z[edge[:, 1]]
110
+ sqv = (dx * dx + dy * dy) / (dz * dz)
111
+ edge = edge[sqv < max_velocity ** 2]
112
+ ## return an empty list when no edge remains.
113
+ if len(edge) == 0:
114
+ return np.ndarray((0, 2), float)
115
+
116
+ ## correct the direction of the edges.
117
+ inv = z[edge[:, 0]] > z[edge[:, 1]]
118
+ edge[inv, :] = edge[inv, ::-1]
119
+
120
+ return edge
121
+
122
+
123
+ def extract(vertex: np.ndarray, *,
124
+ n_neighbor: int=3,
125
+ max_velocity: float=200.0,
126
+ param: fdlsgm.solve_parameters=None):
127
+ ''' extract tracklets from a source list.
128
+
129
+ Parameters:
130
+ vertex (ndarray):
131
+ A list of extracted sources. The shape should be (N, 3),where N is
132
+ the number of the sources.
133
+ Each element should be an array of (x, y, t), where t is the
134
+ timestamp, y and x are the location of the source.
135
+ n_neighbor (int):
136
+ The number of neighbors (k).
137
+ max_velocity (float):
138
+ Remove the edges whose collesponding velocities are larger than this
139
+ velocity.
140
+ param (solve_parameters):
141
+ The parameters of the fdlsgm.solve function.
142
+
143
+ Return:
144
+ A list of Tracklet instances.
145
+ '''
146
+ ## edges:
147
+ ## An ndarray with the shape of (N, 2), where N is the number of edges.
148
+ ## The first element is the index of the starting vertex, while the
149
+ ## second element is the index of the end vertex.
150
+ edge = generate_edge(vertex, n_neighbor, max_velocity)
151
+ ## return an empty list when no edge is found.
152
+ if len(edge) == 0:
153
+ return list()
154
+
155
+ ## v0: the vertices of starting points.
156
+ ## v1: the vertices of terminal points.
157
+ v0, v1 = vertex[edge[:, 0], :], vertex[edge[:, 1], :]
158
+ ## els:
159
+ ## An array of elemental line segments with the shape of (N, 6), where N
160
+ ## is the number of ELSs. The element should be (t0, y0, x0, t1, y1, x1).
161
+ ## The first three elements are the vertex of the starting point, while
162
+ ## the second three elements are the vertex of the terminal point.
163
+ els = np.hstack((v0, v1))
164
+
165
+ if param is None:
166
+ param = fdlsgm.default_parameters()
167
+
168
+ ## baseline:
169
+ ## An array of baselines.
170
+ ## The baseline class has four attributes:
171
+ ## - vertex0 (starting point)
172
+ ## - vertex1 (terminal point)
173
+ ## - size (number of vertices)
174
+ ## - elements (indexes of associated vertex)
175
+ baseline = fdlsgm.solve(els, param)
176
+
177
+ return [
178
+ Tracklet(b.vertex0, b.vertex1, vertex[np.unique(edge[b.elements, :])])
179
+ for b in baseline]
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.4
2
+ Name: tracee
3
+ Version: 0.0.4
4
+ Author-email: Ryou Ohsawa <ryou.ohsawa@nao.ac.jp>
5
+ Maintainer-email: Ryou Ohsawa <ryou.ohsawa@nao.ac.jp>
6
+ License: MIT
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: minimalKNN>=0.5
22
+ Requires-Dist: fdlsgm>=0.5.5
23
+ Dynamic: license-file
24
+
25
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
26
+ [![CodeQL](https://github.com/astronasutarou/tracee/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/astronasutarou/tracee/actions/workflows/github-code-scanning/codeql)
27
+
28
+ # Tracklet Extraction Engine
29
+
30
+ This package provides a function to retrieve _tracklets_ from a list of _vertices_ in a three-dimensional space. The function find a set of linearly aligned vertices in the list and derive a line segment (tracklet) to approximate the vertex distribution.
31
+
32
+
33
+ ## Overview
34
+
35
+ The module provides a class `Tracklet` and a function `extract`. The `extract` function receives a list of vertices as `numpy.ndarray`. The array shape should be `(n,6)`, where _n_ is the number of vertices. Then, a list of `Tracklets` instances are returned. Some tutorials are available in [notebook][notebook].
36
+
37
+ [notebook]: https://bitbucket.org/ryou_ohsawa/tracee/src/master/notebook/
38
+
39
+
40
+ ## Requiremnts
41
+ The package `tracee` depends on the following packages. Both packages are available in [PyPi][pypi]. Installation on Ubuntu 18.04 was confirmed. If you have any troubles in installation, please contact to the developer.
42
+
43
+ - [fdlsgm>=0.5.5][fdlsgm]
44
+ - [minimalKNN>=0.5][minimalKNN]
45
+
46
+
47
+ [pypi]: https://pypi.org/
48
+ [fdlsgm]: https://pypi.org/project/fdlsgm/
49
+ [minimalKNN]: https://pypi.org/project/minimalKNN/
@@ -0,0 +1,11 @@
1
+ notebook/sample/generate_knn-graph.py,sha256=XqQ_onhr1NHtG7qCbyvXwEr_B2DPOyVo3hM2Ctt9M2M,703
2
+ notebook/sample/generate_mock_data.py,sha256=h9AoIgY2RpgAf42QsRmSTkJuJ8wH2ZW20S14CeAcNTc,3397
3
+ notebook/sample/generate_tracklet.py,sha256=AYFoVzv7EUZwvkotCn875HRgnfkYFSXAQYmVTZtyY6I,945
4
+ paper/sections/tutorial.py,sha256=EEy49NyVCRJwYtrI59WleR-s7DuJQmBmHT-ufD-nm4E,652
5
+ tracee/__init__.py,sha256=QDeurTvO10gMzNqcMY3ulKPOFZYpxD29qfZXCMGU75c,293
6
+ tracee/extract.py,sha256=pfCrcOq7ai0NeFVt88O0d7IchPQzQsmveYwFDT0B3pQ,6578
7
+ tracee-0.0.4.dist-info/licenses/LICENSE,sha256=o3iPJukEaPov0meAAzJg7PbMOVpTYf0dkEIhoQXbBjs,1051
8
+ tracee-0.0.4.dist-info/METADATA,sha256=Y_innNrmnzmjsPUq2uA9Ej5fxkTPNNexe0N5s6cgFg8,2282
9
+ tracee-0.0.4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
10
+ tracee-0.0.4.dist-info/top_level.txt,sha256=s9tx7iYNwLQ1B922dr6ewoBc2l1nhO71ehJjCR2jn5E,28
11
+ tracee-0.0.4.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,7 @@
1
+ Copyright 2021 Ryou Ohsawa
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,4 @@
1
+ build
2
+ notebook
3
+ paper
4
+ tracee