commit 89cd91d935a2e2e317f4199a6e5397515464de7f Author: uvos Date: Fri Jun 4 21:05:04 2021 +0200 Inital commit diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2a2fce8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.6) + +project(uvoscam) + +set(SRC_FILES uvoscam.cpp) + +find_package(PkgConfig REQUIRED) +find_package(OpenCV REQUIRED) +pkg_check_modules(GLIB REQUIRED glib-2.0) +pkg_check_modules(ARAVIS REQUIRED aravis-0.8) + +add_library(${PROJECT_NAME} SHARED ${SRC_FILES}) +target_link_libraries( ${PROJECT_NAME} ${GLIB_LDFLAGS} ${ARAVIS_LDFLAGS} ${OpenCV_LIBS} -pthread) +target_include_directories(${PROJECT_NAME} PRIVATE ${GLIB_INCLUDE_DIRS} ${ARAVIS_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS}) +add_definitions("-std=c++17 -Wall -O2") +install(TARGETS ${PROJECT_NAME} DESTINATION lib) +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s") + +link_directories(${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(${PROJECT_NAME}_cli main.cpp) +add_dependencies(${PROJECT_NAME}_cli ${PROJECT_NAME}) +target_include_directories(${PROJECT_NAME}_cli PRIVATE ${ARAVIS_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS}) +target_link_libraries( ${PROJECT_NAME}_cli -l${PROJECT_NAME} ${OpenCV_LIBS}) +target_link_libraries( ${PROJECT_NAME}_cli) +add_definitions("-std=c++17 -Wall -O2") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s") + + +set(CMAKE_INSTALL_PREFIX "/usr") +install(TARGETS ${PROJECT_NAME}_cli RUNTIME DESTINATION bin) +install(TARGETS ${PROJECT_NAME} DESTINATION lib) +install(FILES ./uvoscam.h DESTINATION include) diff --git a/log.h b/log.h new file mode 100644 index 0000000..5052a26 --- /dev/null +++ b/log.h @@ -0,0 +1,95 @@ +/** +* Lubricant Detecter +* Copyright (C) 2021 Carl Klemm +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 3 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#pragma once +#include +#include + +class Log +{ +public: + + enum Level + { + DEBUG, + INFO, + WARN, + ERROR + }; + +private: + bool opened = false; + Level msglevel = DEBUG; + + inline std::string getLabel(Level level) + { + std::string label; + switch(level) + { + case DEBUG: + label = "DEBUG"; + break; + case INFO: + label = "INFO "; + break; + case WARN: + label = "WARN "; + break; + case ERROR: + label = "ERROR"; + break; + } + return label; + } + +public: + + + inline static bool headers = false; + inline static Level level = WARN; + + Log() {} + Log(Level type) + { + msglevel = type; + if(headers) + { + operator << ("["+getLabel(type)+"]"); + } + } + + ~Log() + { + if(opened) + { + std::cout<<'\n'; + } + opened = false; + } + + template Log &operator<<(const T &msg) + { + if(msglevel >= level) + { + std::cout< +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "uvoscam.h" + +#define VERSION "0.1" + +using namespace cam; + +static std::string filename; + +void listDevices() +{ + std::vector cameras = Camera::getAvailableCameras(); + size_t i = 0; + for(auto camera : cameras) + std::cout< tokenize(const std::string& in, char delim = ' ') +{ + std::vector vect; + std::stringstream ss(in); + + for(std::string token; std::getline(ss, token, delim);) + vect.push_back(token); + return vect; +} + +int main(int argc, char* argv[]) +{ + Camera camera(callback); + + Log::level = Log::DEBUG; + + Log(Log::INFO)<<"UVOS Camera capture program "< "; + std::string line; + std::getline(std::cin, line); + std::vector tokens = tokenize(line); + + int ret = -1; + + if(!tokens.empty()) + { + if(tokens[0].find("help") != std::string::npos) + help(); + else if(tokens[0].find("exit") != std::string::npos || tokens[0].find("quit") != std::string::npos) + break; + else if(tokens[0].find("list") != std::string::npos) + listDevices(); + else if(tokens[0].find("open") != std::string::npos) + { + if(tokens.size() > 1) + { + ret = -1; + size_t index = std::stoul(tokens[1]); + if(index < Camera::getAvailableCameras().size()) + { + camera.openCamera(index); + if(camera.isOpen()) + { + ret = 0; + camera.setAcquisitionMode(Camera::MODE_SINGLE); + camera.setBayerMode(Camera::BAYER_GREEN); + camera.setTriggerMode(Camera::TRIGGER_SOFTWARE); + } + } + else + { + std::cout<<"Index out of range\n"; + } + } + } + else if(tokens[0].find("capture") != std::string::npos) + { + if(tokens.size() > 1) + { + ret = -1; + if(camera.isOpen()) + { + filename = tokens[1] + ".png"; + camera.startAcquisition(); + camera.trigger(); + } + } + } + } + + std::cout<<(ret == 0 ? "SUCESS\n" : "FAILURE\n"); + } + return 0; +} + diff --git a/uvoscam.cpp b/uvoscam.cpp new file mode 100644 index 0000000..a928785 --- /dev/null +++ b/uvoscam.cpp @@ -0,0 +1,453 @@ +/** +* uvoscam +* Copyright (C) 2021 Carl Klemm +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 3 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#include "uvoscam.h" + +#include +#include +#include +#include +#include + +#include"log.h" + +using namespace cam; + +cv::Mat Camera::debayer(ArvBuffer *buffer) +{ + size_t bufferSize; + const void* data = arv_buffer_get_data(buffer, &bufferSize); + const cv::Mat cvBuffer(static_cast(height_), static_cast(width_), CV_8U, const_cast(data), bufferSize/width_); + cv::Mat cvResult; + if(bayerMode == BAYER_DEBAYER) + { + cv::cvtColor(cvBuffer, cvResult, cv::COLOR_BayerBG2BGR); + } + else if(bayerMode == BAYER_RED || bayerMode == BAYER_BLUE) + { + cvBuffer.copyTo(cvResult); + for(int y = 0; y < cvResult.rows; y+=2) + { + uint8_t* colH = cvResult.ptr(y); + uint8_t* colL = cvResult.ptr(y+1); + for(int x = 0; x < cvResult.cols; x+=2) + { + if(bayerMode == BAYER_BLUE) + { + colH[x+1] = colH[x]; + colL[x] = colH[x]; + colL[x+1] = colH[x]; + } else + { + colH[x] = colL[x+1]; + colH[x+1] = colL[x+1]; + colL[x] = colL[x+1]; + } + } + } + } + else if(bayerMode == BAYER_GREEN) + { + cvBuffer.copyTo(cvResult); + for(int y = 0; y < cvResult.rows; ++y) + { + uint8_t* col = cvResult.ptr(y); + for(int x = 0; x < cvResult.cols; x+=2) + col[x+(y & 1)] = col[x+!(y & 1)]; + } + } + return cvResult; +} + +cv::Mat Camera::pack8(ArvBuffer *buffer, bool color) +{ + size_t bufferSize; + const void* data = arv_buffer_get_data(buffer, &bufferSize); + Log(Log::DEBUG)<<"bufferSize "<(height_), static_cast(width_), color ? CV_8UC3 : CV_8U, + const_cast(data)); + cv::Mat cvResult; + cvBuffer.copyTo(cvResult); + return cvResult; +} + +cv::Mat Camera::pack16(ArvBuffer *buffer) +{ + /*size_t bufferSize; + void* data = arv_buffer_get_data(buffer, &bufferSize); + cv::Mat cvBuffer(static_cast(height_), static_cast(width_), color ? CV_8SC3 : CV_8SC1, data, bufferSize/width_);*/ + cv::Mat cvResult; + //cvBuffer.copyTo(cvResult); + return cvResult; +} + +void Camera::decoderThreadFunc() +{ + while(true) + { + std::unique_lock bufferConditionMutexLock(bufferConditionMutex); + bufferCondition.wait(bufferConditionMutexLock); + if(join) + return; + decodeBufferMutex.lock(); + if(decodeBuffer != nullptr) + { + Log(Log::DEBUG)<<"decoderThreadFunc"; + if(format == ARV_PIXEL_FORMAT_BAYER_BG_8) + { + if(bayerMode != BAYER_PASSTHOUGH) + callback_(debayer(decodeBuffer)); + else + callback_(pack8(decodeBuffer, false)); + } + else if(format == ARV_PIXEL_FORMAT_MONO_8) + { + callback_(pack8(decodeBuffer, false)); + } + arv_stream_push_buffer(aStream_, decodeBuffer); + } + decodeBufferMutex.unlock(); + } +} + +void Camera::aCallback(void* instance, ArvStreamCallbackType type, ArvBuffer *buffer) +{ + Camera* instance_ = reinterpret_cast(instance); + if(type == ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE) + { + if(instance_->run) + { + gint inBuffers; + gint outBuffers; + arv_stream_get_n_buffers(instance_->aStream_, &inBuffers, &outBuffers); + Log(Log::DEBUG)<<"ARV_STREAM_CALLBACK_TYPE_BUFFER_DONE in "< decodeBufferMutexLock(instance_->decodeBufferMutex); + instance_->decodeBuffer = arv_stream_pop_buffer(instance_->aStream_); + instance_->bufferCondition.notify_all(); + if(instance_->aqmode == MODE_SINGLE) + instance_->run = false; + } + else + { + arv_stream_push_buffer(instance_->aStream_, arv_stream_pop_buffer(instance_->aStream_)); + } + } +} + +void Camera::getSize(unsigned int* width, unsigned int* height) +{ + *width = width_; + *height = height_; +} + +bool Camera::chooseFormat() +{ + guint count = 0; + GError* error = nullptr; + gint64* formats = arv_camera_dup_available_pixel_formats(aCamera_, &count, &error); + if(error) + return false; + for(guint i = 0; i < count && format < 0; ++i) + { + switch(formats[i]) + { + case ARV_PIXEL_FORMAT_MONO_8: + case ARV_PIXEL_FORMAT_MONO_10: + case ARV_PIXEL_FORMAT_MONO_12: + case ARV_PIXEL_FORMAT_RGB_8_PLANAR: + case ARV_PIXEL_FORMAT_BAYER_BG_8: + format = formats[i]; + default: + break; + } + } + + arv_camera_set_pixel_format(aCamera_, format, &error); + if(error) + return false; + + return format >= 0; +} + +bool Camera::setupCamera() +{ + if(!chooseFormat()) + { + Log(Log::ERROR)<<"Camera provides no supported pixel format"; + return false; + } + + GError* error = nullptr; + gint ymin, ymax; + arv_camera_get_height_bounds(aCamera_, &ymin, &ymax, &error); + if(error) + { + Log(Log::ERROR)<<"Can not get hight bounds"; + return false; + } + + Log(Log::DEBUG)<<"Height bounds min: "< Camera::getAvailableCameras(bool update) +{ + if(!scanned_ || update) + { + arv_update_device_list(); + scanned_ = true; + } + std::vector cameras; + for(size_t i = 0; i < arv_get_n_devices(); ++i) + { + Description camera; + camera.vendor = arv_get_device_vendor(i); + camera.serial = arv_get_device_serial_nbr(i); + camera.id = arv_get_device_id(i); + camera.model = arv_get_device_model(i); + cameras.push_back(camera); + } + return cameras; +} + +Camera::Camera(std::function callback): join(false), run(false), callback_(callback) +{ + arv_enable_interface("Fake"); +} + +bool Camera::openCamera(const std::string& name) +{ + aCamera_ = arv_camera_new(name.c_str(), nullptr); + if(ARV_IS_CAMERA(aCamera_)) + setupCamera(); + else + Log(Log::ERROR)<<"Can not create camera from name "<join(); + } + if(ARV_IS_STREAM(aStream_)) + { + stopAcquisition(); + g_object_unref(aStream_); + } + if(ARV_IS_CAMERA(aCamera_)) + g_object_unref(aCamera_); +} + +bool Camera::isOpen() +{ + return ARV_IS_STREAM(aStream_) && ARV_IS_CAMERA(aCamera_); +} + +bool Camera::setAcquisitionMode(AquisitionMode mode) +{ + GError* error = nullptr; + aqmode = mode; + arv_camera_set_acquisition_mode(aCamera_, mode == MODE_FREE ? + ARV_ACQUISITION_MODE_CONTINUOUS : ARV_ACQUISITION_MODE_SINGLE_FRAME, &error); + return !error; +} + +bool Camera::setExposureTime(uint64_t us) +{ + GError* error = nullptr; + arv_camera_set_exposure_time(aCamera_, static_cast(us), &error); + return !error; +} + +uint64_t Camera::getExposureTime() +{ + GError* error = nullptr; + double ret = arv_camera_get_exposure_time(aCamera_, &error); + return !error ? static_cast(ret) : 0; +} + +double Camera::getFrameRate() +{ + GError* error = nullptr; + double ret = arv_camera_get_frame_rate(aCamera_, &error); + return !error ? ret : -1; +} + +bool Camera::setFrameRate(double rate) +{ + GError* error = nullptr; + arv_camera_set_frame_rate(aCamera_, rate, &error); + return !error; +} + +int64_t Camera::getPixelFormat() +{ + return static_cast(format); +} + +bool Camera::setExposureAuto(bool enable) +{ + if(!arv_camera_is_exposure_auto_available(aCamera_, nullptr)) + return false; + + GError* error = nullptr; + arv_camera_set_exposure_time(aCamera_, enable ? ARV_AUTO_CONTINUOUS : ARV_AUTO_OFF, &error); + return !error; +} + +bool Camera::startAcquisition() +{ + GError* error = nullptr; + arv_camera_start_acquisition(aCamera_, &error); + if(!error) + run = true; + return !error; +} + +bool Camera::stopAcquisition() +{ + GError* error = nullptr; + arv_camera_stop_acquisition(aCamera_, &error); + if(!error) + run = false; + return !error; +} + +void Camera::setBayerMode(Camera::BayerMode mode) +{ + bayerMode = mode; +} + +Camera::BayerMode Camera::getBayerMode() +{ + return bayerMode; +} + +void Camera::setTriggerMode(Camera::TriggerMode mode) +{ + switch(mode) + { + case TRIGGER_FREE: + arv_camera_clear_triggers(aCamera_, nullptr); + break; + case TRIGGER_SOFTWARE: + arv_camera_set_trigger(aCamera_, "Software", nullptr); + break; + case TRIGGER_HARDWARE: + arv_camera_set_trigger(aCamera_, "Line1", nullptr); + break; + } +} + +void Camera::trigger() +{ + arv_camera_software_trigger(aCamera_, nullptr); +} diff --git a/uvoscam.h b/uvoscam.h new file mode 100644 index 0000000..53eed37 --- /dev/null +++ b/uvoscam.h @@ -0,0 +1,129 @@ +/** +* uvoscam +* Copyright (C) 2021 Carl Klemm +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* version 3 as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +*/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cam +{ + +class Camera +{ +public: + + struct Description + { + std::string vendor; + std::string serial; + std::string id; + std::string model; + }; + + static constexpr size_t BUFFER_DEPTH = 8; + + typedef int TriggerMode; + static constexpr TriggerMode TRIGGER_FREE = 0; + static constexpr TriggerMode TRIGGER_SOFTWARE = 1; + static constexpr TriggerMode TRIGGER_HARDWARE = 2; + + typedef int BayerMode; + static constexpr BayerMode BAYER_RED = 0; + static constexpr BayerMode BAYER_GREEN = 1; + static constexpr BayerMode BAYER_BLUE = 2; + static constexpr BayerMode BAYER_PASSTHOUGH = 3; + static constexpr BayerMode BAYER_DEBAYER = 4; + + + typedef int AquisitionMode; + static constexpr AquisitionMode MODE_FREE = 0; + static constexpr AquisitionMode MODE_SINGLE = 1; + +private: + ArvStream* aStream_ = nullptr; + ArvCamera* aCamera_ = nullptr; + ArvInterface interface; + + unsigned int width_; + unsigned int height_; + int64_t format = -1; + inline static bool scanned_ = false; + + ArvBuffer* imageBuffers[BUFFER_DEPTH] = {0}; + + BayerMode bayerMode = BAYER_DEBAYER; + + ArvBuffer* decodeBuffer = nullptr; + std::mutex decodeBufferMutex; + std::condition_variable bufferCondition; + std::mutex bufferConditionMutex; + std::thread* decoderThread = nullptr; + std::atomic_bool join; + + std::atomic_bool run; + + AquisitionMode aqmode = MODE_FREE; + + static void aCallback(void* instance, ArvStreamCallbackType type, ArvBuffer *buffer); + + void decoderThreadFunc(); + + std::function callback_; + + bool setupCamera(); + + bool chooseFormat(); + + cv::Mat debayer(ArvBuffer *buffer); + cv::Mat pack8(ArvBuffer *buffer, bool color); + cv::Mat pack16(ArvBuffer *buffer); + +public: + + static std::vector getAvailableCameras(bool update = false); + + Camera(std::function callback); + bool openCamera(const std::string& name); + bool openCamera(const Camera::Description& camera); + bool openCamera(const size_t index); + ~Camera(); + bool isOpen(); + bool setAcquisitionMode(AquisitionMode mode); + bool setExposureTime(uint64_t us); + uint64_t getExposureTime(); + double getFrameRate(); + bool setFrameRate(double rate); + int64_t getPixelFormat(); + bool setExposureAuto(bool enable); + bool startAcquisition(); + bool stopAcquisition(); + void getSize(unsigned int* x, unsigned int* y); + void setBayerMode(Camera::BayerMode mode); + Camera::BayerMode getBayerMode(); + void setTriggerMode(Camera::TriggerMode mode); + void trigger(); +}; + +}