162 lines
5.3 KiB
C++
162 lines
5.3 KiB
C++
#include "fgc/VimbaCameraSource.h"
|
|
|
|
#include "fgc/Logger.h"
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <stdexcept>
|
|
#include <thread>
|
|
|
|
#include <VmbCPP/VmbCPP.h>
|
|
|
|
using namespace VmbCPP;
|
|
|
|
namespace fgc {
|
|
|
|
namespace {
|
|
|
|
long long nowMs() {
|
|
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
std::chrono::system_clock::now().time_since_epoch())
|
|
.count();
|
|
}
|
|
|
|
// Frame observer: dumps the first few frames after each trigger (sensor
|
|
// settling), then delivers completed frames to the source callback.
|
|
class FrameObserver : public IFrameObserver {
|
|
public:
|
|
FrameObserver(CameraPtr camera, int cam_id, ICameraSource::FrameCallback cb)
|
|
: IFrameObserver(camera), cam_id_(cam_id), cb_(std::move(cb)) {}
|
|
|
|
void resetSettle() { settle_.store(3); }
|
|
|
|
void FrameReceived(const FramePtr pFrame) override {
|
|
if (settle_.fetch_sub(1) > 0) { // still settling -> discard
|
|
m_pCamera->QueueFrame(pFrame);
|
|
return;
|
|
}
|
|
VmbFrameStatusType status;
|
|
if (pFrame->GetReceiveStatus(status) == VmbErrorSuccess &&
|
|
status == VmbFrameStatusComplete) {
|
|
VmbUint32_t w = 0, h = 0, sz = 0;
|
|
const VmbUchar_t* buf = nullptr;
|
|
if (pFrame->GetWidth(w) == VmbErrorSuccess &&
|
|
pFrame->GetHeight(h) == VmbErrorSuccess &&
|
|
pFrame->GetBufferSize(sz) == VmbErrorSuccess &&
|
|
pFrame->GetBuffer(buf) == VmbErrorSuccess && buf && w && h) {
|
|
Frame f;
|
|
f.width = w;
|
|
f.height = h;
|
|
f.channels = static_cast<int>(sz / (static_cast<size_t>(w) * h));
|
|
f.cam_id = cam_id_;
|
|
f.timestamp_ms = nowMs();
|
|
f.data.assign(buf, buf + sz);
|
|
if (cb_) cb_(f);
|
|
}
|
|
}
|
|
m_pCamera->QueueFrame(pFrame);
|
|
}
|
|
|
|
private:
|
|
int cam_id_;
|
|
ICameraSource::FrameCallback cb_;
|
|
std::atomic<int> settle_{3};
|
|
};
|
|
|
|
} // namespace
|
|
|
|
struct VimbaCameraSource::Impl {
|
|
explicit Impl(std::vector<std::string> ids)
|
|
: camera_ids(std::move(ids)), sys(VmbSystem::GetInstance()) {}
|
|
|
|
std::vector<std::string> camera_ids;
|
|
VmbSystem& sys;
|
|
std::vector<CameraPtr> cameras;
|
|
std::vector<IFrameObserverPtr> observers;
|
|
ICameraSource::FrameCallback callback;
|
|
bool started = false;
|
|
};
|
|
|
|
VimbaCameraSource::VimbaCameraSource(std::vector<std::string> camera_ids)
|
|
: impl_(std::make_unique<Impl>(std::move(camera_ids))) {}
|
|
|
|
VimbaCameraSource::~VimbaCameraSource() {
|
|
try {
|
|
stop();
|
|
close();
|
|
} catch (const std::exception& e) {
|
|
LOG_WARN << "VimbaCameraSource shutdown: " << e.what();
|
|
}
|
|
}
|
|
|
|
void VimbaCameraSource::open() {
|
|
if (impl_->sys.Startup() != VmbErrorSuccess)
|
|
throw std::runtime_error("Could not start Vimba X API");
|
|
for (const auto& id : impl_->camera_ids) {
|
|
CameraPtr cam;
|
|
if (impl_->sys.GetCameraByID(id.c_str(), cam) != VmbErrorSuccess)
|
|
throw std::runtime_error("Camera not found: " + id);
|
|
if (cam->Open(VmbAccessModeFull) != VmbErrorSuccess)
|
|
throw std::runtime_error("Could not open camera: " + id);
|
|
impl_->cameras.push_back(cam);
|
|
LOG_INFO << "Opened camera " << id;
|
|
}
|
|
}
|
|
|
|
void VimbaCameraSource::close() {
|
|
for (auto& cam : impl_->cameras) cam->Close();
|
|
impl_->cameras.clear();
|
|
impl_->sys.Shutdown();
|
|
}
|
|
|
|
void VimbaCameraSource::start() {
|
|
int cam_id = 0;
|
|
for (auto& cam : impl_->cameras) {
|
|
IFrameObserverPtr observer(new FrameObserver(cam, cam_id, impl_->callback));
|
|
if (cam->StartContinuousImageAcquisition(5, observer) != VmbErrorSuccess)
|
|
throw std::runtime_error("Could not start acquisition");
|
|
impl_->observers.push_back(observer);
|
|
++cam_id;
|
|
}
|
|
impl_->started = true;
|
|
}
|
|
|
|
void VimbaCameraSource::stop() {
|
|
for (auto& cam : impl_->cameras) cam->StopContinuousImageAcquisition();
|
|
impl_->observers.clear();
|
|
impl_->started = false;
|
|
}
|
|
|
|
bool VimbaCameraSource::trigger() {
|
|
if (impl_->cameras.empty()) return false;
|
|
for (auto& obs : impl_->observers)
|
|
SP_DYN_CAST<FrameObserver>(obs)->resetSettle();
|
|
|
|
VmbErrorType result = VmbErrorSuccess;
|
|
for (int i = 0; i < 4; ++i) { // 3 settle frames + 1 real
|
|
FeaturePtr feature;
|
|
if (SP_ACCESS(impl_->cameras[0])->GetFeatureByName("TriggerSoftware", feature) ==
|
|
VmbErrorSuccess)
|
|
result = feature->RunCommand();
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(400));
|
|
}
|
|
return result == VmbErrorSuccess;
|
|
}
|
|
|
|
bool VimbaCameraSource::setFrameRate(double fps) {
|
|
VmbErrorType result = VmbErrorSuccess;
|
|
for (auto& cam : impl_->cameras) {
|
|
FeaturePtr feature;
|
|
if (SP_ACCESS(cam)->GetFeatureByName("AcquisitionFrameRate", feature) == VmbErrorSuccess)
|
|
result = feature->SetValue(fps);
|
|
}
|
|
if (result == VmbErrorSuccess) LOG_INFO << "camera fps set to " << fps;
|
|
return result == VmbErrorSuccess;
|
|
}
|
|
|
|
void VimbaCameraSource::setFrameCallback(FrameCallback cb) { impl_->callback = std::move(cb); }
|
|
|
|
int VimbaCameraSource::cameraCount() const { return static_cast<int>(impl_->cameras.size()); }
|
|
|
|
} // namespace fgc
|