Inital commit

This commit is contained in:
2021-06-04 21:05:04 +02:00
commit 89cd91d935
5 changed files with 850 additions and 0 deletions

33
CMakeLists.txt Normal file
View File

@ -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)

95
log.h Normal file
View File

@ -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 <iostream>
#include <string>
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<class T> Log &operator<<(const T &msg)
{
if(msglevel >= level)
{
std::cout<<msg;
opened = true;
}
return *this;
}
};

140
main.cpp Normal file
View File

@ -0,0 +1,140 @@
/**
* Cam
* 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 <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <vector>
#include <string>
#include <sstream>
#include <opencv2/imgcodecs.hpp>
#include "log.h"
#include "uvoscam.h"
#define VERSION "0.1"
using namespace cam;
static std::string filename;
void listDevices()
{
std::vector<Camera::Description> cameras = Camera::getAvailableCameras();
size_t i = 0;
for(auto camera : cameras)
std::cout<<i++<<": "<<camera.id<<' '<<camera.model<<' '<<camera.vendor<<'\n';
}
void help()
{
std::cout<<"Avaiable commands:\n"
<<"help Displays this message\n"
<<"list Lists cameras found\n"
<<"open [name] Open camera with string name\n"
<<"close Close camera \n"
<<"capture [file] Take an image and save it to file\n"
<<"exit Quit program\n";
}
void callback(cv::Mat data)
{
Log(Log::INFO)<<"Saveing image";
cv::imwrite("./testimg.png", data);
}
std::vector<std::string> tokenize(const std::string& in, char delim = ' ')
{
std::vector<std::string> 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 "<<VERSION;
while(true)
{
std::cout<<"> ";
std::string line;
std::getline(std::cin, line);
std::vector<std::string> 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;
}

453
uvoscam.cpp Normal file
View File

@ -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 <iostream>
#include <thread>
#include <atomic>
#include <opencv2/imgproc.hpp>
#include <string.h>
#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<int>(height_), static_cast<int>(width_), CV_8U, const_cast<void*>(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<uint8_t>(y);
uint8_t* colL = cvResult.ptr<uint8_t>(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<uint8_t>(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 "<<bufferSize;
const cv::Mat cvBuffer(static_cast<int>(height_), static_cast<int>(width_), color ? CV_8UC3 : CV_8U,
const_cast<void*>(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<int>(height_), static_cast<int>(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<std::mutex> 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<Camera*>(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 "<<inBuffers<<" out "<<outBuffers;
std::unique_lock<std::mutex> 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: "<<ymin<<" max: "<<ymax;
gint xmin, xmax;
arv_camera_get_width_bounds(aCamera_, &xmin, &xmax, &error);
if(error)
{
Log(Log::ERROR)<<"Can not get width bounds";
return false;
}
Log(Log::DEBUG)<<"Width bounds min: "<<xmin<<" max: "<<xmax;
arv_camera_set_region(aCamera_, 0, 0, xmax, ymax, nullptr);
gint x, y, w, h;
arv_camera_get_region(aCamera_, &x, &y, &w, &h, &error);
if(error)
{
Log(Log::ERROR)<<"Can not get camera roi";
return false;
}
width_ = w;
height_= h;
Log(Log::DEBUG)<<"Wdith "<<width_<<" Heigt "<<height_;
bool binning = arv_camera_is_binning_available(aCamera_, &error);
if(error)
{
Log(Log::ERROR)<<"Can not get camera binning";
return false;
}
if(binning)
{
arv_camera_set_binning(aCamera_, 1, 1, &error);
if(error)
{
Log(Log::ERROR)<<"failed to set binning";
return false;
}
}
Log(Log::DEBUG)<<"Binning: "<<(binning ? "yes" : "no");
decoderThread = new std::thread([this](){decoderThreadFunc();});
Log(Log::DEBUG)<<"Using "<<arv_camera_get_pixel_format_as_string(aCamera_, nullptr)<<" as pixel format";
aStream_ = arv_camera_create_stream(aCamera_, aCallback, this, nullptr);
if(!ARV_IS_STREAM(aStream_))
{
Log(Log::ERROR)<<"Can not create stream";
return false;
}
size_t frameSize = arv_camera_get_payload(aCamera_, nullptr);
for(size_t i = 0; i < BUFFER_DEPTH; ++i)
{
imageBuffers[i] = arv_buffer_new(frameSize, nullptr);
arv_stream_push_buffer(aStream_, imageBuffers[i]);
}
setExposureAuto(false);
setTriggerMode(TRIGGER_FREE);
setAcquisitionMode(ARV_ACQUISITION_MODE_CONTINUOUS);
setFrameRate(10);
setExposureTime(100000);
arv_camera_stop_acquisition(aCamera_, nullptr);
return true;
}
std::vector<Camera::Description> Camera::getAvailableCameras(bool update)
{
if(!scanned_ || update)
{
arv_update_device_list();
scanned_ = true;
}
std::vector<Description> 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<void(cv::Mat)> 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 "<<name;
return true;
}
bool Camera::openCamera(const Camera::Description& camera)
{
aCamera_ = arv_camera_new(camera.id.c_str(), nullptr);
if(ARV_IS_CAMERA(aCamera_))
setupCamera();
else
Log(Log::ERROR)<<"Can not create camera";
return true;
}
bool Camera::openCamera(const size_t index)
{
Description camera = getAvailableCameras().at(index);
aCamera_ = arv_camera_new(camera.id.c_str(), nullptr);
if(ARV_IS_CAMERA(aCamera_))
setupCamera();
else
Log(Log::ERROR)<<"Can not create camera from index "<<index<<" name "<<camera.model;
return true;
}
Camera::~Camera()
{
join = true;
if(decoderThread)
{
bufferCondition.notify_all();
decoderThread->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<double>(us), &error);
return !error;
}
uint64_t Camera::getExposureTime()
{
GError* error = nullptr;
double ret = arv_camera_get_exposure_time(aCamera_, &error);
return !error ? static_cast<uint64_t>(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<ArvPixelFormat>(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);
}

129
uvoscam.h Normal file
View File

@ -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 <string>
#include <functional>
#include <condition_variable>
#include <stdint.h>
#include <thread>
#include <atomic>
#include <opencv2/core/mat.hpp>
#include <arv.h>
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<void(cv::Mat)> 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<Camera::Description> getAvailableCameras(bool update = false);
Camera(std::function<void(cv::Mat)> 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();
};
}