Skip to content

Commit ef610fd

Browse files
committed
Merge pull request #199 from cburbridge/master
Add TPCC QSR.
2 parents c313a68 + 17f84a5 commit ef610fd

6 files changed

Lines changed: 212 additions & 4 deletions

File tree

qsr_lib/scripts/example_extended.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ def pretty_print_world_qsr_trace(which_qsr, qsrlib_response_message):
6969
world.add_object_state_series(o2)
7070
world.add_object_state_series(o3)
7171

72+
elif which_qsr == "tpcc":
73+
# Then we need three objects to do a test...
74+
# Don't bother with more than one timestamp.
75+
o1 = [Object_State(name="o1", timestamp=0, x=0., y=0., xsize=5., ysize=8.)]
76+
o2 = [Object_State(name="o2", timestamp=0, x=5., y=0., xsize=5., ysize=8.)]
77+
o3 = [Object_State(name="o3", timestamp=0, x=5., y=0., xsize=5., ysize=8.)]
78+
79+
world.add_object_state_series(o1)
80+
world.add_object_state_series(o2)
81+
world.add_object_state_series(o3)
82+
7283
elif which_qsr == "rcc8" or which_qsr == "rcc5":
7384
# dynamic_args = {which_qsr: {"quantisation_factor": args.quantisation_factor}}
7485
o1 = [Object_State(name="o1", timestamp=0, x=1., y=1., xsize=5., ysize=8.),
@@ -315,6 +326,9 @@ def pretty_print_world_qsr_trace(which_qsr, qsrlib_response_message):
315326
# print(dynamic_args["for_all_qsrs"]["qsrs_for"])
316327
# # DBG: eof
317328

329+
# dynamic_args[which_qsr]["qsrs_for"] = [("o1", "o2", "o3"), ("o1", "o3")]
330+
dynamic_args = {"tpcc": {"qsrs_for": [("o1", "o2", "o3"), ("o1", "o3")]}}
331+
318332
qsrlib_request_message = QSRlib_Request_Message(which_qsr=which_qsr, input_data=world, dynamic_args=dynamic_args)
319333

320334
if args.ros:

qsr_lib/scripts/mwe.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,20 @@ def pretty_print_world_qsr_trace(which_qsr, qsrlib_response_message):
4343
Object_State(name="o1", timestamp=2, x=3., y=1., xsize=5., ysize=8.),
4444
Object_State(name="o1", timestamp=3, x=6., y=1., xsize=5., ysize=8.),
4545
Object_State(name="o1", timestamp=4, x=6., y=1., xsize=5., ysize=8.)]
46-
4746
o2 = [Object_State(name="o2", timestamp=0, x=1., y=1., xsize=5., ysize=8.),
4847
Object_State(name="o2", timestamp=1, x=2., y=1., xsize=5., ysize=8.),
4948
Object_State(name="o2", timestamp=2, x=1., y=1., xsize=5., ysize=8.),
5049
Object_State(name="o2", timestamp=3, x=1., y=1., xsize=5., ysize=8.),
5150
Object_State(name="o2", timestamp=4, x=1., y=2., xsize=5., ysize=8.)]
52-
#Object_State(name="o2", timestamp=5, x=1., y=2., xsize=5., ysize=8.)]
51+
o3 = [Object_State(name="o3", timestamp=0, x=5., y=2., xsize=5.2, ysize=8.5),
52+
Object_State(name="o3", timestamp=1, x=6., y=4., xsize=5.2, ysize=8.5),
53+
Object_State(name="o3", timestamp=2, x=2., y=4., xsize=5.2, ysize=8.5),
54+
Object_State(name="o3", timestamp=3, x=1., y=4., xsize=5.2, ysize=8.5),
55+
Object_State(name="o3", timestamp=4, x=0., y=4., xsize=5.2, ysize=8.5)]
56+
5357
world.add_object_state_series(o1)
5458
world.add_object_state_series(o2)
59+
world.add_object_state_series(o3)
5560

5661
# ****************************************************************************************************
5762
dynammic_args = {"qsr_relations_and_values" : {"Touch": 0.5, "Near": 1, "Far": 3}}

qsr_lib/src/qsrlib_qsrs/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from qsr_moving_or_stationary import QSR_Moving_or_Stationary
1313
from qsr_new_mwe import QSR_MWE
1414
from qsr_ra import QSR_RA
15-
15+
from qsr_tpcc import QSR_TPCC
1616
# register new qsrs by class name below
1717
qsrs_registry = (QSR_RCC2,
1818
QSR_RCC3_Rectangle_Bounding_Boxes_2D,
@@ -27,4 +27,5 @@
2727
QSR_Arg_Prob_Relations_Distance,
2828
QSR_Moving_or_Stationary,
2929
QSR_MWE,
30-
QSR_RA)
30+
QSR_RA,
31+
QSR_TPCC)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import print_function, division
3+
from qsrlib_qsrs.qsr_triadic_abstractclass import QSR_Triadic_1t_Abstractclass
4+
import math
5+
6+
class QSR_TPCC(QSR_Triadic_1t_Abstractclass):
7+
""" TPCC QSRs.
8+
.. seealso:: For further details about TPCC, see http://www.sfbtr8.spatial-cognition.de/project/r3/QualitativeCalculi/TPCC/index.html.
9+
"""
10+
_unique_id = "tpcc"
11+
_all_possible_relations = ('dlf', 'dfl', 'dsl', 'dbl', 'dlb', 'dsb', 'drb', 'dbr',
12+
'dsr', 'dfr', 'drf', 'dsf', 'clf', 'cfl', 'csl', 'cbl',
13+
'clb', 'csb', 'crb', 'cbr', 'csr', 'cfr', 'crf', 'csf',
14+
'sam')
15+
_dtype = "points"
16+
__partition_names = ['lb','bl','fl','lf','rf','fr','br','rb']
17+
__partition_size = 2 * math.pi / len(__partition_names)
18+
19+
def __init__(self):
20+
"""Constructor."""
21+
super(QSR_TPCC, self).__init__()
22+
23+
def _compute_qsr(self, origin, relatum, objct, qsr_params, **kwargs):
24+
base_distance = math.sqrt((origin.x-relatum.x)**2 + (origin.y-relatum.y)**2)
25+
object_distance = math.sqrt((objct.x-relatum.x)**2 + (objct.y-relatum.y)**2)
26+
if object_distance == 0:
27+
return "sam"
28+
29+
relation = "d" if object_distance > base_distance else "c" # is it far or close: first letter
30+
31+
angle = self._relative_angle(origin, relatum, objct)
32+
partition = int(angle / self.__partition_size)
33+
relation += self.__partition_names[partition]
34+
35+
sin_angle = math.fabs(math.sin(angle))
36+
if sin_angle < 0.00001 or sin_angle > 0.99999:
37+
relation = relation[0]+'s'+relation[2]
38+
39+
return relation
40+
41+
@staticmethod
42+
def _relative_angle(a, b, c):
43+
"""Compute relative angle used to select the (left/right/straight/front/back/straight)
44+
relationship"""
45+
angle_BA = math.atan2((b.y - a.y),(b.x - a.x))
46+
47+
if angle_BA < 0:
48+
angle_BA += 2 * math.pi
49+
50+
angle_CB = math.atan2((c.y - b.y), (c.x - b.x))
51+
if angle_CB < 0:
52+
angle_CB += 2 * math.pi
53+
54+
angle_rel = angle_CB - angle_BA
55+
if angle_rel < 0:
56+
angle_rel += 2 * math.pi
57+
58+
return angle_rel
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import print_function, division
3+
from abc import ABCMeta, abstractmethod
4+
from qsrlib_qsrs.qsr_abstractclass import QSR_Abstractclass
5+
from qsrlib_utils.combinations_and_permutations import *
6+
from qsrlib_io.world_qsr_trace import *
7+
8+
class QSR_Triadic_Abstractclass(QSR_Abstractclass):
9+
"""Abstract class of triadic QSRs, i.e. QSRs that are computed over three objects."""
10+
11+
__metaclass__ = ABCMeta
12+
13+
def __init__(self):
14+
"""Constructor."""
15+
super(QSR_Triadic_Abstractclass, self).__init__()
16+
17+
def _init_qsrs_for_default(self, objects_names_of_world_state):
18+
"""Default list of entities for which QSRs are to be computed for.
19+
20+
:param objects_names_of_world_state: Objects names at a world state.
21+
:type objects_names_of_world_state: list of str
22+
:return: The permutations, i.e. all possible pairs including mirrors, of the list of names passed in the
23+
arguments. E.g. for `objects_names_of_world_state = ['a', 'b']` return `[('a', 'b'), ('b', 'a')]`.
24+
:rtype: list of tuples of str
25+
"""
26+
return possible_triplets(objects_names_of_world_state)
27+
28+
def _validate_qsrs_for(self, qsrs_for):
29+
"""Validate `qsrs_for` which must be a list of tuples of three objects names.
30+
31+
:param qsrs_for: The original `qsrs_for` that needs validation.
32+
:type qsrs_for: list
33+
:return: List of string objects names to make QSRs, which might be the same as the argument `qsrs_for` or a
34+
subset of it with elements that passed the validation test, i.e. the elements of the list must be tuples of
35+
three strings.
36+
:rtype: list
37+
"""
38+
return [p for p in qsrs_for if isinstance(p, (list, tuple)) and (len(p) == 3)]
39+
40+
def _return_points(self, data1, data2, data3):
41+
"""Return the arguments as they are in their point form.
42+
43+
:param data1: First object data.
44+
:type data1: :class:`Object_State <qsrlib_io.world_trace.Object_State>`
45+
:param data2: Second object data.
46+
:type data2: :class:`Object_State <qsrlib_io.world_trace.Object_State>`
47+
:param data3: Third object data.
48+
:type data3: :class:`Object_State <qsrlib_io.world_trace.Object_State>`
49+
:return: `data1`, `data2`, `data3`
50+
:rtype: qsrlib_io.world_trace.Object_State, qsrlib_io.world_trace.Object_State
51+
"""
52+
return data1, data2, data3
53+
54+
def _return_bounding_boxes_2d(self, data1, data2, data3):
55+
"""Return the 2D bounding boxes of the arguments.
56+
57+
:param data1: First object data.
58+
:type data1: :class:`Object_State <qsrlib_io.world_trace.Object_State>`
59+
:param data2: Second object data.
60+
:type data2: :class:`Object_State <qsrlib_io.world_trace.Object_State>`
61+
:param data3: Third object data.
62+
:type data3: :class:`Object_State <qsrlib_io.world_trace.Object_State>`
63+
:return: `bbox1`, `bbox2`, `bbox3`
64+
:rtype: list of floats, list of floats
65+
"""
66+
return data1.return_bounding_box_2d(), data2.return_bounding_box_2d(), data3.return_bounding_box_2d()
67+
68+
69+
class QSR_Triadic_1t_Abstractclass(QSR_Triadic_Abstractclass):
70+
"""Special case abstract class of triadic QSRs. Works with triadic QSRs that require data over one timestamp."""
71+
72+
__metaclass__ = ABCMeta
73+
74+
def __init__(self):
75+
"""Constructor."""
76+
super(QSR_Triadic_1t_Abstractclass, self).__init__()
77+
78+
@abstractmethod
79+
def _compute_qsr(self, data1, data2, data3, qsr_params, **kwargs):
80+
"""Compute QSR value.
81+
82+
:param data1: First object data.
83+
:type data1: :class:`Object_State <qsrlib_io.world_trace.Object_State>`
84+
:param data2: Second object data.
85+
:type data2: :class:`Object_State <qsrlib_io.world_trace.Object_State>`
86+
:param data3: Third object data.
87+
:type data3: :class:`Object_State <qsrlib_io.world_trace.Object_State>`
88+
:param qsr_params: QSR specific parameters passed in `dynamic_args`.
89+
:type qsr_params: dict
90+
:param kwargs: kwargs arguments.
91+
:return: Computed QSR value.
92+
:rtype: str
93+
"""
94+
return
95+
96+
def make_world_qsr_trace(self, world_trace, timestamps, qsr_params, req_params, **kwargs):
97+
"""Compute the world QSR trace from the arguments.
98+
99+
:param world_trace: Input data.
100+
:type world_trace: :class:`World_Trace <qsrlib_io.world_trace.World_Trace>`
101+
:param timestamps: List of sorted timestamps of `world_trace`.
102+
:type timestamps: list
103+
:param qsr_params: QSR specific parameters passed in `dynamic_args`.
104+
:type qsr_params: dict
105+
:param req_params: Request parameters.
106+
:type req_params: dict
107+
:param kwargs: kwargs arguments.
108+
:return: Computed world QSR trace.
109+
:rtype: :class:`World_QSR_Trace <qsrlib_io.world_qsr_trace.World_QSR_Trace>`
110+
"""
111+
ret = World_QSR_Trace(qsr_type=self._unique_id)
112+
for t in timestamps:
113+
world_state = world_trace.trace[t]
114+
qsrs_for = self._process_qsrs_for(world_state.objects.keys(), req_params["dynamic_args"])
115+
for p in qsrs_for:
116+
between = ",".join(p)
117+
try:
118+
data1, data2, data3 = self._dtype_map[self._dtype](world_state.objects[p[0]], world_state.objects[p[1]], world_state.objects[p[2]])
119+
except KeyError:
120+
raise KeyError("%s is not a valid value, should be one of %s" % (self._dtype, self._dtype_map.keys()))
121+
ret.add_qsr(QSR(timestamp=t, between=between,
122+
qsr=self._format_qsr(self._compute_qsr(data1, data2, data3, qsr_params, **kwargs))),
123+
t)
124+
return ret

qsr_lib/src/qsrlib_utils/combinations_and_permutations.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,9 @@ def possible_pairs_between_two_lists(s1, s2, mirrors=True):
3939
"""
4040
s1, s2 = set(s1), set(s2)
4141
return list(itertools.product(s1, s2)) + list(itertools.product(s2, s1)) if mirrors else list(itertools.product(s1, s2))
42+
43+
def possible_triplets(s, mirrors=True):
44+
"""
45+
Return the possible triplets from the list s.
46+
"""
47+
return list(itertools.permutations(set(s), 3)) if mirrors else list(itertools.combinations(set(s), 3))

0 commit comments

Comments
 (0)