Camera Stubs for OpenCV's camera class cv::VideoCapture
.
This library provides several classes which can replace the VideoCapture class from OpenCV and provides alternative methods in providing images. You can, for example, send an image over TCP/HTTP with the HttpCamera
class which will provide this image as a next "grabbed" image. All cameras can be replaced with another camera or the original cv::VideoCapture
at runtime. It is similar to the cv::CAP_IMAGES
backend, but provides more flexibility.
The intention of this library is to make automatic testing possible without relying on a hardware camera but also to be as close as the final system as possible.
-
AnyCamera
: can hold anycv::VideoCapture
interface compatible camera. -
MemoryCamera
: grabs images from a defined vector of images wich are already loaded in memory. Returns an empty image and error when the sequence was reached the end, or, when thecircular
option is enabled, starts from the start of the sequence again. -
FileCamera
: grabs images from a provided sequence of paths. Returns an empty image and error when the sequence was reached the end, or, when thecircular
option is enabled, starts from the start of the sequence again. -
DirectoryCamera
: grabs images from a provided directory. Returns an empty image anderror when the all images in the directory were shown once. When thecircular
option is enabled, the images in the directory are shown endlessly. -
DirectoryTriggerCamera
: grabs images from a provided directory which were newly added to the directory. Every add/move of an image file into the directory is like the camera was triggered. It queues those images. -
HttpCamera
: opens a HTTP server where you can connect and can images (.png/.jpg) via "POST /images" <body containing the encoded image data>. Queues those images. "DELETE /images" empties the queue.
FairyCam provides a new class FairyCam::AnyCamera
which is an interface of cv::VideoCapture
without relying on inheritance. It
can take any Camera which fulfills the cv::VideoCapture
/FairyCam::IsAnyCamera
-Concept.
An example using dynamic polymorphism where you can change the camera at runtime:
import FairyCam;
// or, if you don't use modules
// #include <FairyCam/AnyCamera.hpp>
// #include <FairyCam/FileCamera.hpp>
int startSystem(FairyCam::AnyCamera cam)
{
bool keepRunning = cam.open(0,0,{});
while(keepRunning && cam.isOpened())
{
cv::Mat input;
if (cam.read(input))
{
keepRunning = process(input);
}
}
return 0;
}
int main()
{
///... load config etc.
using namespace FairyCam;
if (config::isTestingEnabled())
{
return startSystem(FileCamera({.files={"myFile1.png", "differentFile.jpg"}));
}
return startSystem(cv::VideoCapture());
}
You can also use the IsAnyCamera
concept for compile time polymorphism. This method generates more code, but will not use any virtual calls or pointer indirections.
import FairyCam;
// or, if you don't use modules
// #include <FairyCam/IsAnyCamera.hpp>
// #include <FairyCam/FileCamera.hpp>
// Template with concept instead of runtime polymorphism.
// It eliminates the virtual calls and indirection of AnyCamera
// and adds extra code size due to duplicating this function.
int startSystem(FairyCamera::IsAnyCamera auto cam)
{
bool keepRunning = cam.open(0,0,{});
while(keepRunning && cam.isOpened())
{
cv::Mat input;
if (cam.read(input))
{
keepRunning = process(input);
}
}
return 0;
}
int main()
{
///... load config etc.
if (config::isTestingEnabled())
{
return startSystem(FileCamera({.files={"myFile1.png", "differentFile.jpg"}));
}
return startSystem(cv::VideoCapture());
}
For more examples and usage, see in the tests directory. Remark: Tests uses C++20 modules with CMake. Please have a look at this comment for CMake Module Support.
It is possible to control the error with a given camera. For this purpose, ChaosCamera
exists. It takes a camera and a error controlling sequence, for example RanomSequence
which has a change of throwing an exception based on randomness.
Here is an example:
AnyCamera camera = ...;
ChaosCamera chaos_cam(std::move(camera),
BernoulliSequence({.isOpen = 0.05}));
// isOpen will fail 5% of the time.
// It will throw the exception "NotOpenException".
You can also add custom exceptions and weight them
AnyCamera camera = ...;
ChaosCamera chaos_cam(std::move(camera),
BernoulliSequence({.isOpen = BernoulliSequence::Fail(0.5).
with<MyException>(5).
with<MySecondException>(0.5) }));
// "isOpen will fail 50% of the time.
// The ratio of MyException:MySecondException will be 10:1"
// Weights are optional.
// Default is a uniform distribution
ChaosCamera
supports setExceptionMode
to enable/disable exceptions. It is on by default, so disable it if you don't want any. In this case, the corresponding functions will return false
.
All standard exception which will be thrown when no custom exceptions where defined, are derived from std::exception
.
Similar to dynamic_cast
, it should be possible to cast from AnyCamera
to the original camera type.
For this, the function AnyCamera::dynamicCast<T>
is provided. If the underlying type matches given T
, a reference of T is returned. Otherwise, the optional is std::nullopt
Example:
using namespace FairyCam;
AnyCamera cam = AnyCamera::create<FileCamera>();
if (auto file_cam_ref = cam.dynamicCast<FileCamera>)
{
FileCamera& file_cam = *file_cam;
//.. do something specific
}
MIT License © Matthias Möller. Made with ♥ in Germany.