Skip to content

Commit e8ce40b

Browse files
committed
Allow creating a task panel without being able to load segmentation.
1 parent b89483d commit e8ce40b

13 files changed

Lines changed: 245 additions & 250 deletions

src/python/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ set(python_files
6767
director/lcmgl.py
6868
director/lcmobjectcollection.py
6969
director/lcmoctomap.py
70-
director/lcmcollections.py
70+
director/lcmcollections.py
7171
director/lcmspy.py
7272
director/lcmUtils.py
7373
director/mainwindowapp.py
@@ -155,6 +155,8 @@ set(python_files
155155
director/macros/segmentation_view/perspective.py
156156

157157
director/tasks/__init__.py
158+
director/tasks/basictasks.py
159+
director/tasks/imagebasedaffordancefit.py
158160
director/tasks/robottasks.py
159161
director/tasks/taskmanagerwidget.py
160162
director/tasks/taskuserpanel.py

src/python/director/doordemo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from director import planplayback
2929
from director.footstepsdriver import FootstepRequestGenerator
3030
from director.tasks.taskuserpanel import TaskUserPanel
31-
from director.tasks.taskuserpanel import ImageBasedAffordanceFit
31+
from director.tasks.imagebasedaffordancefit import ImageBasedAffordanceFit
3232

3333

3434
import director.tasks.robottasks as rt

src/python/director/drilldemo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from director import vtkNumpy as vnp
3333

3434
from director.tasks.taskuserpanel import TaskUserPanel
35-
from director.tasks.taskuserpanel import ImageBasedAffordanceFit
35+
from director.tasks.imagebasedaffordancefit import ImageBasedAffordanceFit
3636

3737
import director.tasks.robottasks as rt
3838
import director.tasks.taskmanagerwidget as tmw

src/python/director/polarisplatformplanner.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333

3434

3535
from director.tasks.taskuserpanel import TaskUserPanel
36-
from director.tasks.taskuserpanel import ImageBasedAffordanceFit
3736

3837
import director.tasks.robottasks as rt
3938
import director.tasks.taskmanagerwidget as tmw

src/python/director/surprisetask.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
from director import switchplanner
3232

3333
from director.tasks.taskuserpanel import TaskUserPanel
34-
from director.tasks.taskuserpanel import ImageBasedAffordanceFit
34+
from director.tasks.imagebasedaffordancefit import ImageBasedAffordanceFit
3535

3636
import director.tasks.robottasks as rt
3737
import director.tasks.taskmanagerwidget as tmw
@@ -278,7 +278,3 @@ def fitSwitchBox(self):
278278
self.fitter.imagePicker.numberOfPoints = 2
279279
self.fitter.pointCloudSource = 'lidar'
280280
self.fitter.fitFunc = self.fitter.fitSwitchBox
281-
282-
283-
284-

src/python/director/switchplanner.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
from director import vtkNumpy as vnp
3131

3232
from director.tasks.taskuserpanel import TaskUserPanel
33-
from director.tasks.taskuserpanel import ImageBasedAffordanceFit
3433

3534
import director.tasks.robottasks as rt
3635
import director.tasks.taskmanagerwidget as tmw
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import copy
2+
import inspect
3+
import re
4+
5+
from director import asynctaskqueue as atq
6+
from director import propertyset
7+
from director.simpletimer import SimpleTimer
8+
9+
from PythonQt import QtCore
10+
from PythonQt import QtGui
11+
12+
def _splitCamelCase(name):
13+
name = re.sub('(.)([A-Z][a-z]+)', r'\1 \2', name)
14+
return re.sub('([a-z0-9])([A-Z])', r'\1 \2', name)
15+
16+
17+
class AsyncTask(object):
18+
'''
19+
AsyncTask documentation.
20+
'''
21+
22+
def __init__(self, **kwargs):
23+
self.statusMessage = ''
24+
self.failReason = ''
25+
self.properties = propertyset.PropertySet()
26+
self.properties.addProperty('Name', _splitCamelCase(self.__class__.__name__).lower())
27+
28+
for cls in reversed(inspect.getmro(self.__class__)):
29+
if hasattr(cls, 'getDefaultProperties'):
30+
cls.getDefaultProperties(self.properties)
31+
32+
for name, value in kwargs.iteritems():
33+
self.properties.setProperty(_splitCamelCase(name).capitalize(), value)
34+
35+
36+
def __call__(self):
37+
return self.run()
38+
39+
def stop(self):
40+
pass
41+
42+
def run(self):
43+
pass
44+
45+
def fail(self, reason):
46+
self.failReason = reason
47+
raise atq.AsyncTaskQueue.FailException(reason)
48+
49+
def copy(self):
50+
return copy.deepcopy(self)
51+
52+
53+
class PrintTask(AsyncTask):
54+
'''
55+
Name: Print Task
56+
Short Description: prints a string
57+
Description:
58+
59+
This task prints a message string.
60+
'''
61+
62+
printFunction = None
63+
64+
@staticmethod
65+
def getDefaultProperties(properties):
66+
properties.addProperty('Message', '<empty message>')
67+
68+
def run(self):
69+
if self.printFunction:
70+
self.printFunction(self.properties.message)
71+
else:
72+
print self.properties.message
73+
74+
75+
class CallbackTask(AsyncTask):
76+
77+
def __init__(self, callback=None, **kwargs):
78+
AsyncTask.__init__(self, **kwargs)
79+
self.callback = callback
80+
81+
def run(self):
82+
if self.callback:
83+
yield self.callback()
84+
85+
86+
class ExceptionTask(AsyncTask):
87+
88+
def run(self):
89+
raise Exception('Task exception')
90+
91+
92+
class UserPromptTask(AsyncTask):
93+
94+
promptsEnabled = True
95+
promptFunction = None
96+
97+
98+
@staticmethod
99+
def getDefaultProperties(properties):
100+
properties.addProperty('Message', 'continue?')
101+
properties.addProperty('Always', False)
102+
103+
def showUserPrompt(self):
104+
if self.promptFunction:
105+
self.promptFunction(self, self.properties.message)
106+
else:
107+
self.showDialog()
108+
109+
def showDialog(self):
110+
111+
self.d = QtGui.QDialog()
112+
113+
buttons = QtGui.QDialogButtonBox()
114+
buttons.addButton('Yes', QtGui.QDialogButtonBox.AcceptRole)
115+
buttons.addButton('No', QtGui.QDialogButtonBox.RejectRole)
116+
buttons.connect('accepted()', self.d.accept)
117+
buttons.connect('rejected()', self.d.reject)
118+
119+
l = QtGui.QVBoxLayout(self.d)
120+
l.addWidget(QtGui.QLabel(self.properties.message))
121+
l.addWidget(buttons)
122+
123+
self.d.setAttribute(QtCore.Qt.WA_QuitOnClose, False)
124+
self.d.show()
125+
self.d.raise_()
126+
self.d.connect('accepted()', self.accept)
127+
self.d.connect('rejected()', self.reject)
128+
129+
def accept(self):
130+
self.result = True
131+
132+
def reject(self):
133+
self.result = False
134+
135+
def run(self):
136+
137+
if not self.promptsEnabled and not self.properties.getProperty('Always'):
138+
return
139+
140+
self.result = None
141+
142+
self.showUserPrompt()
143+
144+
while self.result is None:
145+
yield
146+
147+
if not self.result:
148+
raise atq.AsyncTaskQueue.PauseException()
149+
150+
151+
class DelayTask(AsyncTask):
152+
153+
@staticmethod
154+
def getDefaultProperties(properties):
155+
properties.addProperty('Delay time', 1.0, attributes=propertyset.PropertyAttributes(minimum=0.0, maximum=1e4, singleStep=0.1))
156+
157+
def run(self):
158+
159+
delayTime = self.properties.getProperty('Delay time')
160+
t = SimpleTimer()
161+
162+
while True:
163+
164+
elapsed = t.elapsed()
165+
if elapsed >= delayTime:
166+
break
167+
168+
self.statusMessage = 'Waiting %.1f seconds' % (delayTime - elapsed)
169+
yield
170+
171+
172+
class PauseTask(AsyncTask):
173+
174+
def run(self):
175+
raise atq.AsyncTaskQueue.PauseException()
176+
177+
178+
class QuitTask(AsyncTask):
179+
180+
def run(self):
181+
QtCore.QCoreApplication.instance().quit()
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from director import cameraview
2+
from director.pointpicker import ImagePointPicker
3+
from director import segmentation
4+
5+
class ImageBasedAffordanceFit(object):
6+
7+
def __init__(self, imageView=None, numberOfPoints=1):
8+
9+
self.imageView = imageView or cameraview.CameraImageView(cameraview.imageManager, self.getImageChannel(), 'image view')
10+
self.imagePicker = ImagePointPicker(self.imageView, numberOfPoints=numberOfPoints)
11+
self.imagePicker.connectDoubleClickEvent(self.onImageViewDoubleClick)
12+
self.imagePicker.annotationFunc = self.onImageAnnotation
13+
self.imagePicker.start()
14+
15+
self.pointCloudSource = 'lidar'
16+
self.pickLineRadius = 0.05
17+
self.pickNearestToCamera = True
18+
19+
def getImageChannel(self):
20+
return 'CAMERA_LEFT'
21+
22+
def getPointCloud(self):
23+
assert self.pointCloudSource in ('lidar', 'stereo')
24+
if self.pointCloudSource == 'stereo':
25+
return segmentation.getDisparityPointCloud(decimation=1, removeOutliers=False)
26+
else:
27+
return segmentation.getCurrentRevolutionData()
28+
29+
def onImageAnnotation(self, *points):
30+
polyData = self.getPointCloud()
31+
points = [self.getPointCloudLocationFromImage(p, self.imageView, polyData) for p in points]
32+
self.fit(polyData, points)
33+
34+
def getPointCloudLocationFromImage(self, imagePixel, imageView, polyData):
35+
cameraPos, ray = imageView.getWorldPositionAndRay(imagePixel)
36+
return segmentation.extractPointsAlongClickRay(cameraPos, ray, polyData, distanceToLineThreshold=self.pickLineRadius, nearestToCamera=self.pickNearestToCamera)
37+
38+
def onImageViewDoubleClick(self, displayPoint, modifiers, imageView):
39+
pass
40+
41+
def fit(self, pointData, points):
42+
pass

0 commit comments

Comments
 (0)