Release Python GIL when possible
This commit is contained in:
parent
5965c3bdbe
commit
d56c17a166
|
@ -0,0 +1,92 @@
|
|||
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||
// de Barcelona (UAB).
|
||||
//
|
||||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "carla/NonCopyable.h"
|
||||
|
||||
#ifdef LIBCARLA_WITH_PYTHON_SUPPORT
|
||||
# include <boost/python.hpp>
|
||||
#endif // LIBCARLA_WITH_PYTHON_SUPPORT
|
||||
|
||||
namespace carla {
|
||||
|
||||
class PythonUtil {
|
||||
public:
|
||||
|
||||
static bool ThisThreadHasTheGIL() {
|
||||
#ifdef LIBCARLA_WITH_PYTHON_SUPPORT
|
||||
# ifdef PYTHON3X
|
||||
return PyGILState_Check();
|
||||
# else
|
||||
PyThreadState *tstate = _PyThreadState_Current;
|
||||
return (tstate != nullptr) && (tstate == PyGILState_GetThisThreadState());
|
||||
# endif // PYTHON3
|
||||
#else
|
||||
return false;
|
||||
#endif // LIBCARLA_WITH_PYTHON_SUPPORT
|
||||
}
|
||||
|
||||
#ifdef LIBCARLA_WITH_PYTHON_SUPPORT
|
||||
|
||||
/// Acquires a lock on the Python's Global Interpreter Lock, necessary for
|
||||
/// calling Python code from a different thread.
|
||||
class AcquireGIL : private NonCopyable {
|
||||
public:
|
||||
|
||||
AcquireGIL() : _state(PyGILState_Ensure()) {}
|
||||
|
||||
~AcquireGIL() {
|
||||
PyGILState_Release(_state);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
PyGILState_STATE _state;
|
||||
};
|
||||
|
||||
/// Releases the lock on the Python's Global Interpreter Lock, use it when doing
|
||||
/// blocking I/O operations.
|
||||
class ReleaseGIL : private NonCopyable {
|
||||
public:
|
||||
|
||||
ReleaseGIL() : _state(PyEval_SaveThread()) {}
|
||||
|
||||
~ReleaseGIL() {
|
||||
PyEval_RestoreThread(_state);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
PyThreadState *_state;
|
||||
};
|
||||
|
||||
#else // LIBCARLA_WITH_PYTHON_SUPPORT
|
||||
|
||||
class AcquireGIL : private NonCopyable {};
|
||||
class ReleaseGIL : private NonCopyable {};
|
||||
|
||||
#endif // LIBCARLA_WITH_PYTHON_SUPPORT
|
||||
|
||||
/// A deleter that can be passed to a shared_ptr to release the GIL before
|
||||
/// destroying the object.
|
||||
class ReleaseGILDeleter {
|
||||
public:
|
||||
|
||||
template <typename T>
|
||||
void operator()(T *ptr) const {
|
||||
#ifdef LIBCARLA_WITH_PYTHON_SUPPORT
|
||||
if (ptr != nullptr && PythonUtil::ThisThreadHasTheGIL()) {
|
||||
ReleaseGIL lock;
|
||||
delete ptr;
|
||||
} else
|
||||
#endif // LIBCARLA_WITH_PYTHON_SUPPORT
|
||||
delete ptr;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace carla
|
|
@ -12,11 +12,11 @@
|
|||
namespace carla {
|
||||
namespace client {
|
||||
|
||||
geom::Location Actor::GetLocation() {
|
||||
geom::Location Actor::GetLocation() const {
|
||||
return GetClientImplementation()->GetActorLocation(*this);
|
||||
}
|
||||
|
||||
geom::Transform Actor::GetTransform() {
|
||||
geom::Transform Actor::GetTransform() const {
|
||||
return GetClientImplementation()->GetActorTransform(*this);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,9 +28,9 @@ namespace client {
|
|||
using Super::GetDisplayId;
|
||||
using Super::GetWorld;
|
||||
|
||||
geom::Location GetLocation();
|
||||
geom::Location GetLocation() const;
|
||||
|
||||
geom::Transform GetTransform();
|
||||
geom::Transform GetTransform() const;
|
||||
|
||||
void SetLocation(const geom::Location &location);
|
||||
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||
// de Barcelona (UAB).
|
||||
//
|
||||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#include "carla/client/Client.h"
|
||||
|
||||
namespace carla {
|
||||
namespace client {
|
||||
|
||||
Client::Client(const std::string &host, uint16_t port, size_t worker_threads)
|
||||
: _client_state(MakeShared<detail::Client>(host, port, worker_threads)) {}
|
||||
|
||||
} // namespace client
|
||||
} // namespace carla
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "carla/PythonUtil.h"
|
||||
#include "carla/client/detail/Client.h"
|
||||
|
||||
namespace carla {
|
||||
|
@ -50,5 +51,13 @@ namespace client {
|
|||
SharedPtr<detail::Client> _client_state;
|
||||
};
|
||||
|
||||
inline Client::Client(
|
||||
const std::string &host,
|
||||
uint16_t port,
|
||||
size_t worker_threads)
|
||||
: _client_state(
|
||||
new detail::Client(host, port, worker_threads),
|
||||
PythonUtil::ReleaseGILDeleter()) {}
|
||||
|
||||
} // namespace client
|
||||
} // namespace carla
|
||||
|
|
|
@ -130,11 +130,11 @@ namespace detail {
|
|||
});
|
||||
}
|
||||
|
||||
geom::Location Client::GetActorLocation(Actor &actor) {
|
||||
geom::Location Client::GetActorLocation(const Actor &actor) {
|
||||
return _pimpl->CallAndWait<geom::Location>("get_actor_location", actor.Serialize());
|
||||
}
|
||||
|
||||
geom::Transform Client::GetActorTransform(Actor &actor) {
|
||||
geom::Transform Client::GetActorTransform(const Actor &actor) {
|
||||
return _pimpl->CallAndWait<geom::Transform>("get_actor_transform", actor.Serialize());
|
||||
}
|
||||
|
||||
|
|
|
@ -76,9 +76,9 @@ namespace detail {
|
|||
const streaming::Token &token,
|
||||
std::function<void(SharedPtr<sensor::SensorData>)> callback);
|
||||
|
||||
geom::Location GetActorLocation(Actor &actor);
|
||||
geom::Location GetActorLocation(const Actor &actor);
|
||||
|
||||
geom::Transform GetActorTransform(Actor &actor);
|
||||
geom::Transform GetActorTransform(const Actor &actor);
|
||||
|
||||
void SetActorLocation(Actor &actor, const geom::Location &location);
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@ def get_libcarla_extensions():
|
|||
os.path.join(pwd, 'dependencies/lib/libboost_filesystem.a'),
|
||||
os.path.join(pwd, 'dependencies/lib', pylib)]
|
||||
extra_compile_args = [
|
||||
'-fPIC', '-std=c++14', '-DBOOST_ERROR_CODE_HEADER_ONLY', '-Wno-missing-braces'
|
||||
'-fPIC', '-std=c++14', '-Wno-missing-braces',
|
||||
'-DBOOST_ERROR_CODE_HEADER_ONLY', '-DLIBCARLA_WITH_PYTHON_SUPPORT'
|
||||
]
|
||||
if 'TRAVIS' in os.environ and os.environ['TRAVIS'] == 'true':
|
||||
print('Travis CI build detected: disabling PNG support.')
|
||||
|
@ -44,16 +45,19 @@ def get_libcarla_extensions():
|
|||
raise NotImplementedError
|
||||
elif os.name == "nt":
|
||||
pwd = os.path.dirname(os.path.realpath(__file__))
|
||||
pylib = "libboost_python%d%d-vc141-mt-x64-1_67.lib" % (sys.version_info.major, sys.version_info.minor)
|
||||
|
||||
pylib = "libboost_python%d%d-vc141-mt-x64-1_67.lib" % (
|
||||
sys.version_info.major,
|
||||
sys.version_info.minor)
|
||||
extra_link_args = [
|
||||
'shlwapi.lib',
|
||||
os.path.join(pwd, 'dependencies/lib/rpc.lib'),
|
||||
os.path.join(pwd, 'dependencies/lib', pylib)]
|
||||
|
||||
# https://docs.microsoft.com/es-es/cpp/porting/modifying-winver-and-win32-winnt
|
||||
extra_compile_args = ['/DPYTHON3X', '/DBOOST_ALL_NO_LIB', '/DBOOST_PYTHON_STATIC_LIB',
|
||||
'/DBOOST_ERROR_CODE_HEADER_ONLY', '/D_WIN32_WINNT=0x0501' ]
|
||||
extra_compile_args = [
|
||||
'/DPYTHON3X', '/DBOOST_ALL_NO_LIB', '/DBOOST_PYTHON_STATIC_LIB',
|
||||
'/DBOOST_ERROR_CODE_HEADER_ONLY', '/D_WIN32_WINNT=0x0501',
|
||||
'/DLIBCARLA_WITH_PYTHON_SUPPORT']
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#include <carla/client/Actor.h>
|
||||
#include <carla/client/Vehicle.h>
|
||||
|
||||
#include <boost/python.hpp>
|
||||
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
|
||||
|
@ -30,23 +28,19 @@ void export_actor() {
|
|||
|
||||
class_<cc::Actor, boost::noncopyable, boost::shared_ptr<cc::Actor>>("Actor", no_init)
|
||||
.add_property("id", &cc::Actor::GetId)
|
||||
.add_property("type_id", +[](const cc::Actor &self) -> std::string {
|
||||
return self.GetTypeId();
|
||||
})
|
||||
.add_property("type_id", +[](const cc::Actor &self) -> std::string { return self.GetTypeId(); })
|
||||
.add_property("is_alive", &cc::Actor::IsAlive)
|
||||
.def("get_world", &cc::Actor::GetWorld)
|
||||
.def("get_location", &cc::Actor::GetLocation)
|
||||
.def("get_transform", &cc::Actor::GetTransform)
|
||||
.def("get_location", CONST_CALL_WITHOUT_GIL(cc::Actor, GetLocation))
|
||||
.def("get_transform", CONST_CALL_WITHOUT_GIL(cc::Actor, GetTransform))
|
||||
.def("set_location", &cc::Actor::SetLocation, (arg("location")))
|
||||
.def("set_transform", &cc::Actor::SetTransform, (arg("transform")))
|
||||
.def("destroy", &cc::Actor::Destroy)
|
||||
.def("destroy", CALL_WITHOUT_GIL(cc::Actor, Destroy))
|
||||
.def(self_ns::str(self_ns::self))
|
||||
;
|
||||
|
||||
class_<cc::Vehicle, bases<cc::Actor>, boost::noncopyable, boost::shared_ptr<cc::Vehicle>>("Vehicle", no_init)
|
||||
.add_property("control", +[](const cc::Vehicle &self) -> cr::VehicleControl {
|
||||
return self.GetControl();
|
||||
})
|
||||
.add_property("control", CALL_RETURNING_COPY(cc::Vehicle, GetControl))
|
||||
.def("apply_control", &cc::Vehicle::ApplyControl, (arg("control")))
|
||||
.def("set_autopilot", &cc::Vehicle::SetAutopilot, (arg("enabled")=true))
|
||||
.def(self_ns::str(self_ns::self))
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <carla/client/BlueprintLibrary.h>
|
||||
#include <carla/client/ActorBlueprint.h>
|
||||
|
||||
#include <boost/python.hpp>
|
||||
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
|
||||
|
||||
#include <ostream>
|
||||
|
@ -124,13 +123,9 @@ void export_blueprint() {
|
|||
;
|
||||
|
||||
class_<cc::ActorAttribute>("ActorAttribute", no_init)
|
||||
.add_property("id", +[](const cc::ActorAttribute &self) -> std::string {
|
||||
return self.GetId();
|
||||
})
|
||||
.add_property("id", CALL_RETURNING_COPY(cc::ActorAttribute, GetId))
|
||||
.add_property("type", &cc::ActorAttribute::GetType)
|
||||
.add_property("recommended_values", +[](const cc::ActorAttribute &self) -> std::vector<std::string> {
|
||||
return self.GetRecommendedValues();
|
||||
})
|
||||
.add_property("recommended_values", CALL_RETURNING_COPY(cc::ActorAttribute, GetRecommendedValues))
|
||||
.add_property("is_modifiable", &cc::ActorAttribute::IsModifiable)
|
||||
.def("as_bool", &cc::ActorAttribute::As<bool>)
|
||||
.def("as_int", &cc::ActorAttribute::As<int>)
|
||||
|
@ -158,16 +153,12 @@ void export_blueprint() {
|
|||
;
|
||||
|
||||
class_<cc::ActorBlueprint>("ActorBlueprint", no_init)
|
||||
.add_property("id", +[](const cc::ActorBlueprint &self) -> std::string {
|
||||
return self.GetId();
|
||||
})
|
||||
.add_property("id", CALL_RETURNING_COPY(cc::ActorBlueprint, GetId))
|
||||
.add_property("tags", &cc::ActorBlueprint::GetTags)
|
||||
.def("contains_tag", &cc::ActorBlueprint::ContainsTag)
|
||||
.def("match_tags", &cc::ActorBlueprint::MatchTags)
|
||||
.def("contains_attribute", &cc::ActorBlueprint::ContainsAttribute)
|
||||
.def("get_attribute", +[](const cc::ActorBlueprint &self, const std::string &id) -> cc::ActorAttribute {
|
||||
return self.GetAttribute(id);
|
||||
})
|
||||
.def("get_attribute", CALL_RETURNING_COPY_1(cc::ActorBlueprint, GetAttribute, const std::string &))
|
||||
.def("set_attribute", &cc::ActorBlueprint::SetAttribute)
|
||||
.def("__len__", &cc::ActorBlueprint::size)
|
||||
.def("__iter__", range(&cc::ActorBlueprint::begin, &cc::ActorBlueprint::end))
|
||||
|
|
|
@ -4,11 +4,10 @@
|
|||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#include <carla/PythonUtil.h>
|
||||
#include <carla/client/Client.h>
|
||||
#include <carla/client/World.h>
|
||||
|
||||
#include <boost/python.hpp>
|
||||
|
||||
static void SetTimeout(carla::client::Client &client, double seconds) {
|
||||
size_t ms = static_cast<size_t>(1e3 * seconds);
|
||||
client.SetTimeout(carla::time_duration::milliseconds(ms));
|
||||
|
@ -22,8 +21,8 @@ void export_client() {
|
|||
init<std::string, uint16_t, size_t>((arg("host"), arg("port"), arg("worker_threads")=0u)))
|
||||
.def("set_timeout", &::SetTimeout, (arg("seconds")))
|
||||
.def("get_client_version", &cc::Client::GetClientVersion)
|
||||
.def("get_server_version", &cc::Client::GetServerVersion)
|
||||
.def("ping", &cc::Client::Ping)
|
||||
.def("get_server_version", CONST_CALL_WITHOUT_GIL(cc::Client, GetServerVersion))
|
||||
.def("ping", CONST_CALL_WITHOUT_GIL(cc::Client, Ping))
|
||||
.def("get_world", &cc::Client::GetWorld)
|
||||
;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include <carla/rpc/VehicleControl.h>
|
||||
|
||||
#include <boost/python.hpp>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace carla {
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#include <rpc/config.h>
|
||||
#include <rpc/rpc_error.h>
|
||||
|
||||
#include <boost/python.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
void translator(const rpc::rpc_error &e) {
|
||||
|
|
|
@ -4,204 +4,34 @@
|
|||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#include <carla/PythonUtil.h>
|
||||
#include <carla/client/Sensor.h>
|
||||
#include <carla/image/ImageConverter.h>
|
||||
#include <carla/image/ImageIO.h>
|
||||
#include <carla/image/ImageView.h>
|
||||
#include <carla/pointcloud/PointCloudIO.h>
|
||||
#include <carla/sensor/SensorData.h>
|
||||
#include <carla/sensor/data/Image.h>
|
||||
#include <carla/sensor/data/LidarMeasurement.h>
|
||||
|
||||
#include <boost/python.hpp>
|
||||
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
|
||||
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
|
||||
/// Aquires a lock on the Python's Global Interpreter Lock, necessary for
|
||||
/// calling Python code from a different thread.
|
||||
///
|
||||
/// https://wiki.python.org/moin/GlobalInterpreterLock
|
||||
class GILLockGuard {
|
||||
public:
|
||||
|
||||
GILLockGuard()
|
||||
: _state(PyGILState_Ensure()) {}
|
||||
|
||||
~GILLockGuard() {
|
||||
PyGILState_Release(_state);
|
||||
static void SubscribeToStream(carla::client::Sensor &self, boost::python::object callback) {
|
||||
// Make sure the callback is actually callable.
|
||||
if (!PyCallable_Check(callback.ptr())) {
|
||||
PyErr_SetString(PyExc_TypeError, "callback argument must be callable!");
|
||||
boost::python::throw_error_already_set();
|
||||
return;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
PyGILState_STATE _state;
|
||||
};
|
||||
|
||||
namespace carla {
|
||||
namespace client {
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Sensor &sensor) {
|
||||
out << "Sensor(id=" << sensor.GetId() << ", type=" << sensor.GetTypeId() << ')';
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
|
||||
namespace sensor {
|
||||
namespace data {
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Image &image) {
|
||||
out << "Image(frame=" << image.GetFrameNumber()
|
||||
<< ", size=" << image.GetWidth() << 'x' << image.GetHeight()
|
||||
<< ')';
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const LidarMeasurement &meas) {
|
||||
out << "LidarMeasurement(frame=" << meas.GetFrameNumber()
|
||||
<< ", number_of_points=" << meas.size()
|
||||
<< ')';
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace data
|
||||
} // namespace sensor
|
||||
} // namespace carla
|
||||
|
||||
enum class EColorConverter {
|
||||
None,
|
||||
Depth,
|
||||
LogarithmicDepth,
|
||||
CityScapesPalette
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static auto GetRawDataAsBuffer(T &self) {
|
||||
auto *data = reinterpret_cast<unsigned char *>(self.data());
|
||||
auto size = sizeof(typename T::value_type) * self.size();
|
||||
#if PYTHON3X // NOTE(Andrei): python 3
|
||||
auto *ptr = PyMemoryView_FromMemory(reinterpret_cast<char *>(data), size, PyBUF_READ);
|
||||
#else // NOTE(Andrei): python 2
|
||||
auto *ptr = PyBuffer_FromMemory(data, size);
|
||||
#endif
|
||||
return boost::python::object(boost::python::handle<>(ptr));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void ConvertImage(T &self, EColorConverter cc) {
|
||||
using namespace carla::image;
|
||||
auto view = ImageView::MakeView(self);
|
||||
switch (cc) {
|
||||
case EColorConverter::Depth:
|
||||
ImageConverter::ConvertInPlace(view, ColorConverter::Depth());
|
||||
break;
|
||||
case EColorConverter::LogarithmicDepth:
|
||||
ImageConverter::ConvertInPlace(view, ColorConverter::LogarithmicDepth());
|
||||
break;
|
||||
case EColorConverter::CityScapesPalette:
|
||||
ImageConverter::ConvertInPlace(view, ColorConverter::CityScapesPalette());
|
||||
break;
|
||||
case EColorConverter::None:
|
||||
break; // ignore.
|
||||
default:
|
||||
throw std::invalid_argument("invalid color converter!");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::string SaveImageToDisk(T &self, std::string path, EColorConverter cc) {
|
||||
using namespace carla::image;
|
||||
auto view = ImageView::MakeView(self);
|
||||
switch (cc) {
|
||||
case EColorConverter::None:
|
||||
return ImageIO::WriteView(
|
||||
std::move(path),
|
||||
view);
|
||||
case EColorConverter::Depth:
|
||||
return ImageIO::WriteView(
|
||||
std::move(path),
|
||||
ImageView::MakeColorConvertedView(view, ColorConverter::Depth()));
|
||||
case EColorConverter::LogarithmicDepth:
|
||||
return ImageIO::WriteView(
|
||||
std::move(path),
|
||||
ImageView::MakeColorConvertedView(view, ColorConverter::LogarithmicDepth()));
|
||||
case EColorConverter::CityScapesPalette:
|
||||
return ImageIO::WriteView(
|
||||
std::move(path),
|
||||
ImageView::MakeColorConvertedView(view, ColorConverter::CityScapesPalette()));
|
||||
default:
|
||||
throw std::invalid_argument("invalid color converter!");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::string SavePointCloudToDisk(T &self, std::string path) {
|
||||
return carla::pointcloud::PointCloudIO::SaveToDisk(std::move(path), self.begin(), self.end());
|
||||
// Subscribe to the sensor.
|
||||
self.Listen([callback](auto message) {
|
||||
carla::PythonUtil::AcquireGIL lock;
|
||||
try {
|
||||
boost::python::call<void>(callback.ptr(), boost::python::object(message));
|
||||
} catch (const boost::python::error_already_set &e) {
|
||||
PyErr_Print();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void export_sensor() {
|
||||
using namespace boost::python;
|
||||
namespace cc = carla::client;
|
||||
namespace cs = carla::sensor;
|
||||
namespace csd = carla::sensor::data;
|
||||
|
||||
class_<cs::SensorData, boost::noncopyable, boost::shared_ptr<cs::SensorData>>("SensorData", no_init)
|
||||
.add_property("frame_number", &cs::SensorData::GetFrameNumber)
|
||||
.add_property("transform", +[](const cs::SensorData &self) -> carla::rpc::Transform {
|
||||
return self.GetSensorTransform();
|
||||
})
|
||||
;
|
||||
|
||||
enum_<EColorConverter>("ColorConverter")
|
||||
.value("None", EColorConverter::None)
|
||||
.value("Depth", EColorConverter::Depth)
|
||||
.value("LogarithmicDepth", EColorConverter::LogarithmicDepth)
|
||||
.value("CityScapesPalette", EColorConverter::CityScapesPalette)
|
||||
;
|
||||
|
||||
class_<csd::Image, bases<cs::SensorData>, boost::noncopyable, boost::shared_ptr<csd::Image>>("Image", no_init)
|
||||
.add_property("width", &csd::Image::GetWidth)
|
||||
.add_property("height", &csd::Image::GetHeight)
|
||||
.add_property("fov", &csd::Image::GetFOVAngle)
|
||||
.add_property("raw_data", &GetRawDataAsBuffer<csd::Image>)
|
||||
.def("convert", &ConvertImage<csd::Image>, (arg("color_converter")))
|
||||
.def("save_to_disk", &SaveImageToDisk<csd::Image>, (arg("path"), arg("color_converter")=EColorConverter::None))
|
||||
.def("__len__", &csd::Image::size)
|
||||
.def("__iter__", iterator<csd::Image>())
|
||||
.def(self_ns::str(self_ns::self))
|
||||
;
|
||||
|
||||
class_<csd::LidarMeasurement, bases<cs::SensorData>, boost::noncopyable, boost::shared_ptr<csd::LidarMeasurement>>("LidarMeasurement", no_init)
|
||||
.add_property("horizontal_angle", &csd::LidarMeasurement::GetHorizontalAngle)
|
||||
.add_property("channels", &csd::LidarMeasurement::GetChannelCount)
|
||||
.add_property("raw_data", &GetRawDataAsBuffer<csd::LidarMeasurement>)
|
||||
.def("get_point_count", &csd::LidarMeasurement::GetPointCount, (arg("channel")))
|
||||
.def("save_to_disk", &SavePointCloudToDisk<csd::LidarMeasurement>, (arg("path")))
|
||||
.def("__len__", &csd::LidarMeasurement::size)
|
||||
.def("__iter__", iterator<csd::LidarMeasurement>())
|
||||
.def(self_ns::str(self_ns::self))
|
||||
;
|
||||
|
||||
class_<cc::Sensor, bases<cc::Actor>, boost::noncopyable, boost::shared_ptr<cc::Sensor>>("Sensor", no_init)
|
||||
.def("listen", +[](cc::Sensor &self, boost::python::object callback) {
|
||||
// Make sure the callback is actually callable.
|
||||
if (!PyCallable_Check(callback.ptr())) {
|
||||
PyErr_SetString(PyExc_TypeError, "callback argument must be callable!");
|
||||
throw_error_already_set();
|
||||
return;
|
||||
}
|
||||
|
||||
// Subscribe to the sensor.
|
||||
self.Listen([callback](auto message) { // Here we retrieve the deserialized object.
|
||||
GILLockGuard gil_lock;
|
||||
try {
|
||||
boost::python::call<void>(callback.ptr(), boost::python::object(message));
|
||||
} catch (const boost::python::error_already_set &e) {
|
||||
PyErr_Print();
|
||||
}
|
||||
});
|
||||
}, (arg("callback")))
|
||||
.def("listen", &SubscribeToStream, (arg("callback")))
|
||||
.def(self_ns::str(self_ns::self))
|
||||
;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
// Copyright (c) 2017 Computer Vision Center (CVC) at the Universitat Autonoma
|
||||
// de Barcelona (UAB).
|
||||
//
|
||||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#include <carla/PythonUtil.h>
|
||||
#include <carla/image/ImageConverter.h>
|
||||
#include <carla/image/ImageIO.h>
|
||||
#include <carla/image/ImageView.h>
|
||||
#include <carla/pointcloud/PointCloudIO.h>
|
||||
#include <carla/sensor/SensorData.h>
|
||||
#include <carla/sensor/data/Image.h>
|
||||
#include <carla/sensor/data/LidarMeasurement.h>
|
||||
|
||||
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
|
||||
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
|
||||
namespace carla {
|
||||
namespace sensor {
|
||||
namespace data {
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Image &image) {
|
||||
out << "Image(frame=" << image.GetFrameNumber()
|
||||
<< ", size=" << image.GetWidth() << 'x' << image.GetHeight()
|
||||
<< ')';
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const LidarMeasurement &meas) {
|
||||
out << "LidarMeasurement(frame=" << meas.GetFrameNumber()
|
||||
<< ", number_of_points=" << meas.size()
|
||||
<< ')';
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace data
|
||||
} // namespace sensor
|
||||
} // namespace carla
|
||||
|
||||
enum class EColorConverter {
|
||||
None,
|
||||
Depth,
|
||||
LogarithmicDepth,
|
||||
CityScapesPalette
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static auto GetRawDataAsBuffer(T &self) {
|
||||
auto *data = reinterpret_cast<unsigned char *>(self.data());
|
||||
auto size = sizeof(typename T::value_type) * self.size();
|
||||
#if PYTHON3X // NOTE(Andrei): python 3
|
||||
auto *ptr = PyMemoryView_FromMemory(reinterpret_cast<char *>(data), size, PyBUF_READ);
|
||||
#else // NOTE(Andrei): python 2
|
||||
auto *ptr = PyBuffer_FromMemory(data, size);
|
||||
#endif
|
||||
return boost::python::object(boost::python::handle<>(ptr));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void ConvertImage(T &self, EColorConverter cc) {
|
||||
carla::PythonUtil::ReleaseGIL unlock;
|
||||
using namespace carla::image;
|
||||
auto view = ImageView::MakeView(self);
|
||||
switch (cc) {
|
||||
case EColorConverter::Depth:
|
||||
ImageConverter::ConvertInPlace(view, ColorConverter::Depth());
|
||||
break;
|
||||
case EColorConverter::LogarithmicDepth:
|
||||
ImageConverter::ConvertInPlace(view, ColorConverter::LogarithmicDepth());
|
||||
break;
|
||||
case EColorConverter::CityScapesPalette:
|
||||
ImageConverter::ConvertInPlace(view, ColorConverter::CityScapesPalette());
|
||||
break;
|
||||
case EColorConverter::None:
|
||||
break; // ignore.
|
||||
default:
|
||||
throw std::invalid_argument("invalid color converter!");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::string SaveImageToDisk(T &self, std::string path, EColorConverter cc) {
|
||||
carla::PythonUtil::ReleaseGIL unlock;
|
||||
using namespace carla::image;
|
||||
auto view = ImageView::MakeView(self);
|
||||
switch (cc) {
|
||||
case EColorConverter::None:
|
||||
return ImageIO::WriteView(
|
||||
std::move(path),
|
||||
view);
|
||||
case EColorConverter::Depth:
|
||||
return ImageIO::WriteView(
|
||||
std::move(path),
|
||||
ImageView::MakeColorConvertedView(view, ColorConverter::Depth()));
|
||||
case EColorConverter::LogarithmicDepth:
|
||||
return ImageIO::WriteView(
|
||||
std::move(path),
|
||||
ImageView::MakeColorConvertedView(view, ColorConverter::LogarithmicDepth()));
|
||||
case EColorConverter::CityScapesPalette:
|
||||
return ImageIO::WriteView(
|
||||
std::move(path),
|
||||
ImageView::MakeColorConvertedView(view, ColorConverter::CityScapesPalette()));
|
||||
default:
|
||||
throw std::invalid_argument("invalid color converter!");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::string SavePointCloudToDisk(T &self, std::string path) {
|
||||
carla::PythonUtil::ReleaseGIL unlock;
|
||||
return carla::pointcloud::PointCloudIO::SaveToDisk(std::move(path), self.begin(), self.end());
|
||||
}
|
||||
|
||||
void export_sensor_data() {
|
||||
using namespace boost::python;
|
||||
namespace cc = carla::client;
|
||||
namespace cs = carla::sensor;
|
||||
namespace csd = carla::sensor::data;
|
||||
|
||||
class_<cs::SensorData, boost::noncopyable, boost::shared_ptr<cs::SensorData>>("SensorData", no_init)
|
||||
.add_property("frame_number", &cs::SensorData::GetFrameNumber)
|
||||
.add_property("transform", CALL_RETURNING_COPY(cs::SensorData, GetSensorTransform))
|
||||
;
|
||||
|
||||
enum_<EColorConverter>("ColorConverter")
|
||||
.value("None", EColorConverter::None)
|
||||
.value("Depth", EColorConverter::Depth)
|
||||
.value("LogarithmicDepth", EColorConverter::LogarithmicDepth)
|
||||
.value("CityScapesPalette", EColorConverter::CityScapesPalette)
|
||||
;
|
||||
|
||||
class_<csd::Image, bases<cs::SensorData>, boost::noncopyable, boost::shared_ptr<csd::Image>>("Image", no_init)
|
||||
.add_property("width", &csd::Image::GetWidth)
|
||||
.add_property("height", &csd::Image::GetHeight)
|
||||
.add_property("fov", &csd::Image::GetFOVAngle)
|
||||
.add_property("raw_data", &GetRawDataAsBuffer<csd::Image>)
|
||||
.def("convert", &ConvertImage<csd::Image>, (arg("color_converter")))
|
||||
.def("save_to_disk", &SaveImageToDisk<csd::Image>, (arg("path"), arg("color_converter")=EColorConverter::None))
|
||||
.def("__len__", &csd::Image::size)
|
||||
.def("__iter__", iterator<csd::Image>())
|
||||
.def(self_ns::str(self_ns::self))
|
||||
;
|
||||
|
||||
class_<csd::LidarMeasurement, bases<cs::SensorData>, boost::noncopyable, boost::shared_ptr<csd::LidarMeasurement>>("LidarMeasurement", no_init)
|
||||
.add_property("horizontal_angle", &csd::LidarMeasurement::GetHorizontalAngle)
|
||||
.add_property("channels", &csd::LidarMeasurement::GetChannelCount)
|
||||
.add_property("raw_data", &GetRawDataAsBuffer<csd::LidarMeasurement>)
|
||||
.def("get_point_count", &csd::LidarMeasurement::GetPointCount, (arg("channel")))
|
||||
.def("save_to_disk", &SavePointCloudToDisk<csd::LidarMeasurement>, (arg("path")))
|
||||
.def("__len__", &csd::LidarMeasurement::size)
|
||||
.def("__iter__", iterator<csd::LidarMeasurement>())
|
||||
.def(self_ns::str(self_ns::self))
|
||||
;
|
||||
}
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
#include <carla/geom/Transform.h>
|
||||
|
||||
#include <boost/python.hpp>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace carla {
|
||||
|
|
|
@ -4,21 +4,31 @@
|
|||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#include <carla/PythonUtil.h>
|
||||
#include <carla/client/Actor.h>
|
||||
#include <carla/client/World.h>
|
||||
|
||||
#include <boost/python.hpp>
|
||||
|
||||
void export_world() {
|
||||
using namespace boost::python;
|
||||
namespace cc = carla::client;
|
||||
namespace cg = carla::geom;
|
||||
|
||||
#define SPAWN_ACTOR_WITHOUT_GIL(fn) +[]( \
|
||||
cc::World &self, \
|
||||
const cc::ActorBlueprint &blueprint, \
|
||||
const cg::Transform &transform, \
|
||||
cc::Actor *parent) { \
|
||||
carla::PythonUtil::ReleaseGIL unlock; \
|
||||
return self.fn(blueprint, transform, parent); \
|
||||
}, \
|
||||
(arg("blueprint"), arg("transform"), arg("attach_to")=carla::SharedPtr<cc::Actor>())
|
||||
|
||||
class_<cc::World>("World", no_init)
|
||||
.def("get_blueprint_library", &cc::World::GetBlueprintLibrary)
|
||||
.def("get_spectator", &cc::World::GetSpectator)
|
||||
.def("try_spawn_actor", &cc::World::TrySpawnActor,
|
||||
(arg("blueprint"), arg("transform"), arg("attach_to")=carla::SharedPtr<cc::Actor>()))
|
||||
.def("spawn_actor", &cc::World::SpawnActor,
|
||||
(arg("blueprint"), arg("transform"), arg("attach_to")=carla::SharedPtr<cc::Actor>()))
|
||||
.def("get_blueprint_library", CONST_CALL_WITHOUT_GIL(cc::World, GetBlueprintLibrary))
|
||||
.def("get_spectator", CONST_CALL_WITHOUT_GIL(cc::World, GetSpectator))
|
||||
.def("try_spawn_actor", SPAWN_ACTOR_WITHOUT_GIL(TrySpawnActor))
|
||||
.def("spawn_actor", SPAWN_ACTOR_WITHOUT_GIL(SpawnActor))
|
||||
;
|
||||
|
||||
#undef SPAWN_ACTOR_WITHOUT_GIL
|
||||
}
|
||||
|
|
|
@ -4,7 +4,30 @@
|
|||
// This work is licensed under the terms of the MIT license.
|
||||
// For a copy, see <https://opensource.org/licenses/MIT>.
|
||||
|
||||
#include <boost/python.hpp>
|
||||
#include <carla/PythonUtil.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
// Convenient for requests without arguments.
|
||||
#define CALL_WITHOUT_GIL(cls, fn) +[](cls &self) { \
|
||||
carla::PythonUtil::ReleaseGIL unlock; \
|
||||
return self.fn(); \
|
||||
}
|
||||
|
||||
// Convenient for const requests without arguments.
|
||||
#define CONST_CALL_WITHOUT_GIL(cls, fn) CALL_WITHOUT_GIL(const cls, fn)
|
||||
|
||||
// Convenient for const requests that need to make a copy of the returned value.
|
||||
#define CALL_RETURNING_COPY(cls, fn) +[](const cls &self) \
|
||||
-> std::decay_t<std::result_of_t<decltype(&cls::fn)(cls*)>> { \
|
||||
return self.fn(); \
|
||||
}
|
||||
|
||||
// Convenient for const requests that need to make a copy of the returned value.
|
||||
#define CALL_RETURNING_COPY_1(cls, fn, _T1) +[](const cls &self, _T1 t1) \
|
||||
-> std::decay_t<std::result_of_t<decltype(&cls::fn)(cls*, _T1)>> { \
|
||||
return self.fn(std::forward<_T1>(t1)); \
|
||||
}
|
||||
|
||||
#include "Actor.cpp"
|
||||
#include "Blueprint.cpp"
|
||||
|
@ -12,6 +35,7 @@
|
|||
#include "Control.cpp"
|
||||
#include "Exception.cpp"
|
||||
#include "Sensor.cpp"
|
||||
#include "SensorData.cpp"
|
||||
#include "Transform.cpp"
|
||||
#include "World.cpp"
|
||||
|
||||
|
@ -24,6 +48,7 @@ BOOST_PYTHON_MODULE(libcarla) {
|
|||
export_blueprint();
|
||||
export_actor();
|
||||
export_sensor();
|
||||
export_sensor_data();
|
||||
export_world();
|
||||
export_client();
|
||||
export_exception();
|
||||
|
|
Loading…
Reference in New Issue