Skip to content

Commit

Permalink
Merge branch '48361-add-farshow-integration' into 'main'
Browse files Browse the repository at this point in the history
[#48361] Added a farshow integration example

See merge request repositories/edge-ai-demos-grabthecam!19
  • Loading branch information
glatosinski committed Sep 11, 2023
2 parents 79cb58a + c3a1f71 commit 84b967f
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 6 deletions.
9 changes: 9 additions & 0 deletions .ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ before_script:
libv4l-dev
make
rapidjson-dev
libglfw3-dev
libglew-dev
libgl1-mesa-dev
- export CC=/usr/bin/gcc-12 CXX=/usr/bin/g++-12
- git clone --recursive https://github.com/antmicro/farshow
- cd farshow
- cmake -S . -B ./build
- cmake --build ./build
- cmake --install ./build

stages:
- lint
Expand Down
43 changes: 37 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ project(grabthecam VERSION 0.1)
find_package(OpenCV REQUIRED)
find_package(RapidJSON REQUIRED)

option(ADD_GRABTHECAM_FARSHOW_DEMO "Adds a target for grabthecam-farshow integration demo" OFF)

set(INCLUDE_DIRECTORIES
${OpenCV_INCLUDE_DIRS}
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
Expand Down Expand Up @@ -50,16 +52,45 @@ target_link_libraries(${PROJECT_NAME}-demo PUBLIC
${OpenCV_LIBS}
)

configure_file(
${PROJECT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in
${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY
)

if(ADD_GRABTHECAM_FARSHOW_DEMO)
find_package(farshow QUIET)
if(NOT farshow_FOUND)
cmake_policy(SET CMP0002 OLD)
message("farshow not found - building it")
include(FetchContent)
FetchContent_Declare(farshow
GIT_REPOSITORY https://github.com/antmicro/farshow
GIT_TAG main
)
FetchContent_MakeAvailable(farshow)
endif()

add_executable(${PROJECT_NAME}-farshow-streamer
src/farshow-streamer-example.cpp
)
target_include_directories(${PROJECT_NAME}-farshow-streamer PUBLIC ${INCLUDE_DIRECTORIES})

target_link_libraries(${PROJECT_NAME}-farshow-streamer PUBLIC
${PROJECT_NAME}
v4l2
farshow-connection
${OpenCV_LIBS}
)
install(TARGETS ${PROJECT_NAME}-farshow-streamer)
endif()

install(TARGETS ${PROJECT_NAME} ${PROJECT_NAME}-demo
EXPORT ${PROJECT_NAME}-targets
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)

configure_file(
${PROJECT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in
${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY
)

export(
TARGETS ${PROJECT_NAME} FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake
)
Expand All @@ -75,7 +106,7 @@ install(
)

add_custom_target(format
COMMAND bash -c "find ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/include -iname \"*.cpp\" -o -iname \"*.hpp\" |xargs clang-tidy -format-style=file -p ${CMAKE_BINARY_DIR} -fix"
COMMAND bash -c "find ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/include -iname \"*.cpp\" -o -iname \"*.hpp\"| xargs clang-format --style=file -i"
COMMAND bash -c "clang-tidy -format-style=file \$(find ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/include | egrep '(*hpp|*cpp)' | tr '\\n' ' ') -p ${CMAKE_BINARY_DIR} -fix"
COMMAND bash -c "clang-format --style=file -i \$(find ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/include | egrep '(*hpp|*cpp)' | tr '\\n' ' ')"
VERBATIM
)
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ To build the project, go to its root directory and execute:
cmake -S . -B build
```

In order to add the option to build a `grabthecam` - `farshow` integration demo, execute the following command instead:
```
cmake -S . -B build -DADD_GRABTHECAM_FARSHOW_DEMO=ON
```

Next, go to `build` and execute either
```
make
```
for the standalone demonstration, or
```
make grabthecam-farshow-streamer
```
for the integration demo.

## Running the demo

After a successful build, you can run the demo, e.g.:
Expand All @@ -43,10 +58,22 @@ The captured frame will be saved as `frame.png`.
The configuration will be saved as `.my_configuration`. If you use `-s` it is saved as `.pyvidctrl_<driver_name>` (for compatibility with [pyvidctrl camera management TUI tool](https://github.com/antmicro/pyvidctrl)).
Please note that `--save` and `--load` can only have values assigned through the `--param=value` syntax.

Farshow-Grabthecam integration demo follows simmilar syntax, with an addition of `-a <address>` and `-p <port>` for configuring the frame destination for the sender
```
cd build
./grabthecam-farshow-streamer --type YUYV --dims 960,720 -a 0.0.0.0 -p 18881
```

To start the receiver, please follow the README instructions on installation from the [farshow](https://github.com/antmicro/farshow) repository and run
```(bash)
farshow -i 0.0.0.0 -p 18881
```

You can find more information about available arguments in command-line help:

```
./grabthecam-demo --help
./grabthecam-farshow-streamer --help
```

## Installation
Expand Down
15 changes: 15 additions & 0 deletions include/grabthecam/pixelformatsinfo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@
namespace grabthecam
{

/**
* Parse command line options
*
* @param name Four character name of the V4L2 format
*/
uint32_t convertToV4l2Fourcc(std::string name)
{
uint32_t fourcc = 0;
for (int i = 0; i < 4; i++)
{
fourcc |= (name[0] << 8 * i);
}
return fourcc;
}

/// Information about converters and input formats assigned to pixel formats
static std::unordered_map<unsigned int, std::function<std::shared_ptr<FrameConverter>()>> formats_info = {
{V4L2_PIX_FMT_YYUV, [] { return std::make_shared<Yuv2BGRConverter>(cv::COLOR_YUV2BGR_YUY2); }},
Expand Down
202 changes: 202 additions & 0 deletions src/farshow-streamer-example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
#include "cxxopts/cxxopts.hpp"
#include "farshow/framesender.hpp"
#include "farshow/streamexception.hpp"
#include "grabthecam/cameracapture.hpp"
#include "grabthecam/cameracapturetemplates.hpp"
#include "grabthecam/frameconverters/anyformat2bgrconverter.hpp"
#include "grabthecam/frameconverters/bayer2bgrconverter.hpp"
#include "grabthecam/frameconverters/yuv2bgrconverter.hpp"
#include "grabthecam/pixelformatsinfo.hpp"
#include <opencv2/imgproc.hpp>
#include <thread>

#include "grabthecam/utils.hpp"

/**
* User's preferred configuration
*/
typedef struct Config
{
std::string camera_filename; ///< Path to the camera file
std::string type = ""; ///< Raw frame type
std::vector<int> dims = {}; ///< Frame width and height
unsigned int pix_format = 0; ///< Raw frame type – v4l2 code
bool use_trigger = false; ///< Whether to set up and use an external trigger
std::string ip; ///< IP Address for the farshow streamer
uint16_t port; ///< Port for the farshow streamer
std::string stream_name; ///< Name of the farshow stream
std::optional<std::string> saveConfig; ///< Where to save the configuration
std::optional<std::string> loadConfig; ///< Where to load the configuration from
} Config;

/**
* Parse command line options
*
* @param argc Arguments counter
* @param argv Arguments values
*/
Config parseOptions(int argc, char const *argv[])
{
Config config;
cxxopts::ParseResult result;

// Set available options
cxxopts::Options options(argv[0], "A simple camera frame streaming application with farshow.");

// clang-format off
options.add_options()
("c, camera", "Filename of a camera device",
cxxopts::value(config.camera_filename)->default_value("/dev/video0"))
("t, type", "Frame type in V4L2 FourCC format (eg. RG12, RGGB) or compressed format type (supported: JPG, PNG)",
cxxopts::value(config.type))
("d, dims", "Frame width and height (eg. `960,720`)",
cxxopts::value(config.dims))
("a, address", "IP address for the farshow streamer",
cxxopts::value(config.ip))
("p, port", "Port for the farshow streamer",
cxxopts::value(config.port))
("n, name", "Stream name",
cxxopts::value(config.stream_name)->default_value("grabthecam_demo stream"))
// The only working syntax for save and load is --save=value and --load=value,
// it is a known limitation of cxxopts described in
// https://github.com/jarro2783/cxxopts/issues/210 where implicit values have to
// be assigned through the `=` sign or will otherwise be ignored
("s, save", "Save configuration to the file. You can provide the filename or the "
".pyvidctrl_<driver_name> file will be used",
cxxopts::value(config.saveConfig)->implicit_value(""))
("l, load", "Load the configuration from file. You can provide the filename or the"
" .pyvidctrl_<driver_name> file will be used",
cxxopts::value(config.loadConfig)->implicit_value(""))
("use_trigger", "Use any trigger source provided in the configuration file")
("h, help", "Print usage");
// clang-format on

// Get command line parameters and parse them
try
{
result = options.parse(argc, argv);
}
catch (cxxopts::OptionException e)
{
std::cerr << std::endl
<< "\033[31mError while parsing command line arguments: " << e.what() << "\033[0m" << std::endl
<< std::endl;
std::cout << options.help() << std::endl;
exit(1);
}
catch (grabthecam::CameraException e)
{
std::cerr << "\033[31m" << e.what() << "\033[0m" << std::endl << std::endl;
std::cout << options.help() << std::endl;
exit(1);
}

if (result.count("type"))
{
try
{
if (config.type.length() == 4)
{
config.pix_format = grabthecam::convertToV4l2Fourcc(config.type);
}
else if (config.type == "JPG")
{
config.pix_format = V4L2_PIX_FMT_MJPEG;
}
else
{
throw(std::out_of_range("Wrong format"));
}
}
catch (std::out_of_range e)
{
std::cerr << std::endl
<< "\033[31mError while parsing command line arguments: Wrong value '" << config.type
<< "' for parameter 'type'\033[0m" << std::endl
<< std::endl;
std::cout << options.help() << std::endl;
exit(1);
}
}

if (result.count("help"))
{
std::cout << options.help() << std::endl;
exit(0);
}

if (result.count("use_trigger"))
{
config.use_trigger = true;
}

return config;
}

int main(int argc, char const *argv[])
{
// SET UP THE CAMERA
Config conf = parseOptions(argc, argv); ///< user's configuration
grabthecam::CameraCapture camera(conf.camera_filename); ///< cameracapture object
farshow::FrameSender streamer(conf.ip, conf.port);

camera.printControls();

// adjust camera settings
if (!conf.dims.empty())
{
camera.setFormat(conf.dims[0], conf.dims[1], conf.pix_format); // set frame format
}
else
{
camera.setFormat(0, 0, conf.pix_format);
}
auto format = camera.getFormat(); ///< Actually set frame format
double time_perframe;

///////////////////////////////////////////////////////////////////////////////////////////////

std::cout << "Format set to " << conf.type << " " << std::get<0>(format) << " x " << std::get<1>(format)
<< std::endl;

if (conf.loadConfig.has_value())
{
std::string filename = camera.loadConfig(*conf.loadConfig);
std::cout << "Configuration loaded from " << filename << std::endl;
}

if (conf.saveConfig.has_value())
{
std::string filename = camera.saveConfig(*conf.saveConfig);
std::cout << "Configuration saved to " << filename << std::endl;
}

// detect if trigger information was set
if (camera.getTriggerInfo().has_value() && conf.use_trigger)
{
std::cout << "\nThe camera is now waiting for an external trigger."
"\nIn order to save the frame, please set it off via "
"an external tool\nor provide a triggering implementation "
"to CameraCapture::triggerFrame and call it.\n";
camera.enableTrigger();
}

auto f = [&streamer, conf](cv::Mat foo) { streamer.sendFrame(foo, conf.stream_name); };
// CAPTURE FRAMES
if (camera.hasConverter())
{
cv::Mat processed_frame = camera.capture();
while (1)
{
std::thread sender(f, processed_frame);
processed_frame = camera.capture();
sender.join();
}
}
else
{
throw grabthecam::CameraException(std::to_string(conf.pix_format) +
" is not convertable from RAW via CV2. Please specify another pixel format.");
}
return 0;
}

0 comments on commit 84b967f

Please sign in to comment.