From a5c3ddd85e9a2ac7b03be7c632ed42be43956cf5 Mon Sep 17 00:00:00 2001 From: Angelo DelliSanti Date: Wed, 17 Apr 2024 17:36:44 +0200 Subject: [PATCH] Deprecate cpp examples Change-Id: I45f1651ba61af5a61aa119197288ee568f95caed --- .github/workflows/hello-world-cpp.yml | 33 -- .github/workflows/object-detector-cpp.yml | 40 --- .../workflows/opencv-image-capture-cpp.yml | 33 -- .github/workflows/parameter-api-cpp.yml | 33 -- README.md | 10 +- hello-world-cpp/Dockerfile | 65 ---- hello-world-cpp/README.md | 103 ------- hello-world-cpp/app/Makefile | 15 - hello-world-cpp/app/src/hello_world.cpp | 7 - hello-world-cpp/docker-compose.yml | 15 - object-detector-cpp/Dockerfile | 75 ----- object-detector-cpp/Dockerfile.model | 31 -- object-detector-cpp/README.md | 184 ------------ object-detector-cpp/app/Makefile | 36 --- object-detector-cpp/app/src/object_detect.cpp | 240 --------------- .../app/src/serving_client.hpp | 180 ----------- .../config/env.aarch64.artpec8 | 3 - object-detector-cpp/config/env.aarch64.cpu | 3 - object-detector-cpp/config/env.armv7hf.cpu | 3 - object-detector-cpp/config/env.armv7hf.tpu | 3 - object-detector-cpp/docker-compose.yml | 47 --- opencv-image-capture-cpp/Dockerfile | 68 ----- opencv-image-capture-cpp/README.md | 284 ------------------ opencv-image-capture-cpp/app/Makefile | 26 -- opencv-image-capture-cpp/app/src/capture.cpp | 207 ------------- opencv-image-capture-cpp/docker-compose.yml | 17 -- parameter-api-cpp/.gitignore | 1 - parameter-api-cpp/Dockerfile | 75 ----- parameter-api-cpp/README.md | 158 ---------- parameter-api-cpp/app/Makefile | 53 ---- .../app/apis/keyvaluestore.proto | 35 --- parameter-api-cpp/app/src/parameter.cpp | 134 --------- parameter-api-cpp/docker-compose.yml | 8 - 33 files changed, 1 insertion(+), 2224 deletions(-) delete mode 100644 .github/workflows/hello-world-cpp.yml delete mode 100644 .github/workflows/object-detector-cpp.yml delete mode 100644 .github/workflows/opencv-image-capture-cpp.yml delete mode 100644 .github/workflows/parameter-api-cpp.yml delete mode 100644 hello-world-cpp/Dockerfile delete mode 100755 hello-world-cpp/README.md delete mode 100644 hello-world-cpp/app/Makefile delete mode 100644 hello-world-cpp/app/src/hello_world.cpp delete mode 100644 hello-world-cpp/docker-compose.yml delete mode 100644 object-detector-cpp/Dockerfile delete mode 100644 object-detector-cpp/Dockerfile.model delete mode 100644 object-detector-cpp/README.md delete mode 100644 object-detector-cpp/app/Makefile delete mode 100644 object-detector-cpp/app/src/object_detect.cpp delete mode 100644 object-detector-cpp/app/src/serving_client.hpp delete mode 100644 object-detector-cpp/config/env.aarch64.artpec8 delete mode 100644 object-detector-cpp/config/env.aarch64.cpu delete mode 100644 object-detector-cpp/config/env.armv7hf.cpu delete mode 100644 object-detector-cpp/config/env.armv7hf.tpu delete mode 100644 object-detector-cpp/docker-compose.yml delete mode 100644 opencv-image-capture-cpp/Dockerfile delete mode 100644 opencv-image-capture-cpp/README.md delete mode 100644 opencv-image-capture-cpp/app/Makefile delete mode 100644 opencv-image-capture-cpp/app/src/capture.cpp delete mode 100644 opencv-image-capture-cpp/docker-compose.yml delete mode 100644 parameter-api-cpp/.gitignore delete mode 100644 parameter-api-cpp/Dockerfile delete mode 100644 parameter-api-cpp/README.md delete mode 100644 parameter-api-cpp/app/Makefile delete mode 100644 parameter-api-cpp/app/apis/keyvaluestore.proto delete mode 100644 parameter-api-cpp/app/src/parameter.cpp delete mode 100644 parameter-api-cpp/docker-compose.yml diff --git a/.github/workflows/hello-world-cpp.yml b/.github/workflows/hello-world-cpp.yml deleted file mode 100644 index 61e6d47..0000000 --- a/.github/workflows/hello-world-cpp.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Build hello-world-cpp application - -on: - push: - paths: - - 'hello-world-cpp/**' - - '!hello-world-cpp/README.md' - - '.github/workflows/hello-world-cpp.yml' - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - env: - EXREPO: acap-computer-vision-examples - EXNAME: hello-world-cpp - strategy: - matrix: - include: - - arch: armv7hf - - arch: aarch64 - steps: - - uses: actions/checkout@v2 - - uses: docker/setup-buildx-action@v2 - with: - install: true - - name: Build ${{ env.example }} application - env: - example: ${{ env.EXNAME }} - imagetag: ${{ env.EXREPO }}_${{ env.EXNAME }}:1.0 - run: | - cd $EXNAME - docker build --no-cache --build-arg ARCH=${{ matrix.arch }} --tag $imagetag . diff --git a/.github/workflows/object-detector-cpp.yml b/.github/workflows/object-detector-cpp.yml deleted file mode 100644 index 8c1eede..0000000 --- a/.github/workflows/object-detector-cpp.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Build object-detector-cpp application - -on: - push: - paths: - - 'object-detector-cpp/**' - - '!object-detector-cpp/README.md' - - '.github/workflows/object-detector-cpp.yml' - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - include: - - arch: armv7hf - chip: cpu - - arch: armv7hf - chip: edgetpu - - arch: aarch64 - chip: artpec8 - env: - EXREPO: acap-computer-vision-examples - EXNAME: object-detector-cpp - steps: - - uses: actions/checkout@v2 - - uses: docker/setup-buildx-action@v2 - with: - install: true - - name: Build ${{ env.example }} application - env: - example: ${{ env.EXNAME }}-${{ matrix.chip }} - imagetag: ${{ env.EXREPO }}_${{ env.EXNAME }}-${{ matrix.chip }}:1.0 - modeltag: ${{ env.EXREPO }}_${{ env.EXNAME }}-${{ matrix.chip }}-model:1.0 - run: | - cd $EXNAME - docker run --rm --privileged multiarch/qemu-user-static --credential yes --persistent yes - docker build --no-cache --build-arg CHIP=${{ matrix.chip }} --build-arg ARCH=${{ matrix.arch }} --tag $imagetag . - docker build --file Dockerfile.model --tag $modeltag --build-arg ARCH=${{ matrix.arch }} . diff --git a/.github/workflows/opencv-image-capture-cpp.yml b/.github/workflows/opencv-image-capture-cpp.yml deleted file mode 100644 index a0bcb16..0000000 --- a/.github/workflows/opencv-image-capture-cpp.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Build opencv-image-capture-cpp application - -on: - push: - paths: - - 'opencv-image-capture-cpp/**' - - '!opencv-image-capture-cpp/README.md' - - '.github/workflows/opencv-image-capture-cpp.yml' - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - env: - EXREPO: acap-computer-vision-examples - EXNAME: opencv-image-capture-cpp - strategy: - matrix: - include: - - arch: armv7hf - - arch: aarch64 - steps: - - uses: actions/checkout@v2 - - uses: docker/setup-buildx-action@v2 - with: - install: true - - name: Build ${{ env.example }} application - env: - example: ${{ env.EXNAME }} - imagetag: ${{ env.EXREPO }}_${{ env.EXNAME }}:1.0 - run: | - cd $EXNAME - docker build --no-cache --build-arg ARCH=${{ matrix.arch }} --tag $imagetag . diff --git a/.github/workflows/parameter-api-cpp.yml b/.github/workflows/parameter-api-cpp.yml deleted file mode 100644 index e476f91..0000000 --- a/.github/workflows/parameter-api-cpp.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: Build parameter-api-cpp application - -on: - push: - paths: - - 'parameter-api-cpp/**' - - '!parameter-api-cpp/README.md' - - '.github/workflows/parameter-api-cpp.yml' - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - env: - EXREPO: acap-computer-vision-examples - EXNAME: parameter-api-cpp - strategy: - matrix: - include: - - arch: armv7hf - - arch: aarch64 - steps: - - uses: actions/checkout@v2 - - uses: docker/setup-buildx-action@v2 - with: - install: true - - name: Build ${{ env.example }} application - env: - example: ${{ env.EXNAME }} - imagetag: ${{ env.EXREPO }}_${{ env.EXNAME }}:1.0 - run: | - cd $EXNAME - docker build --no-cache --build-arg ARCH=${{ matrix.arch }} --tag $imagetag . diff --git a/README.md b/README.md index 0d0f4b8..2862560 100644 --- a/README.md +++ b/README.md @@ -45,23 +45,15 @@ The examples support the following architectures: Below is a list of examples available in the repository: -* [hello-world-cpp](./hello-world-cpp/) - * A C++ example which cross compiles a simple OpenCV hello-world application. * [hello-world-python](./hello-world-python/) * A Python example which builds a simple hello-world application. * [minimal-ml-inference](./minimal-ml-inference/) * A Python example to build a minimal machine learning inference application. -* [object-detector-cpp](./object-detector-cpp/) - * A C++ example which runs object detection on the camera. * [object-detector-python](./object-detector-python/) * A Python example which implements object detection on a video stream from the camera. -* [opencv-image-capture-cpp](./opencv-image-capture-cpp/) - * A C++ example which captures camera frames and properties such as time stamps, zoom, focus etc., through OpenCV. * [opencv-qr-decoder-python](./opencv-qr-decoder-python/) * A Python example which detects and decodes QR codes in the video stream using OpenCV. -* [parameter-api-cpp](./parameter-api-cpp/) - * A C++ example which reads camera parameters using the beta version of the Parameter-API. * [parameter-api-python](./parameter-api-python/) * A Python example which reads camera parameters using the beta version of the Parameter-API. * [pose-estimator-with-flask](./pose-estimator-with-flask/) @@ -72,7 +64,7 @@ Below is a list of examples available in the repository: ### Docker Hub images The examples are based on the [ACAP Computer Vision SDK](https://github.com/AxisCommunications/acap-computer-vision-sdk). -This SDK is an image which contains APIs and tooling to build computer vision apps for running on camera, with support for C/C++ and Python. +This SDK is an image which contains APIs and tooling to build computer vision apps for running on camera, with support for Python. Additionally, there is the [ACAP Native SDK](https://github.com/AxisCommunications/acap-native-sdk), which is more geared towards building ACAPs that uses AXIS-developed APIs directly, and primarily does so using C/C++. ## How to work with Github repository diff --git a/hello-world-cpp/Dockerfile b/hello-world-cpp/Dockerfile deleted file mode 100644 index 1a5b7cd..0000000 --- a/hello-world-cpp/Dockerfile +++ /dev/null @@ -1,65 +0,0 @@ -# syntax=docker/dockerfile:1 - -# Specify the architecture at build time: mipsis32r2el/armv7hf/aarch64 -ARG ARCH=armv7hf -ARG REPO=axisecp -ARG SDK_VERSION=1.12 -ARG UBUNTU_VERSION=22.04 - -FROM arm32v7/ubuntu:${UBUNTU_VERSION} as runtime-image-armv7hf -FROM arm64v8/ubuntu:${UBUNTU_VERSION} as runtime-image-aarch64 - -FROM ${REPO}/acap-computer-vision-sdk:${SDK_VERSION}-${ARCH}-runtime AS cv-sdk-runtime -FROM ${REPO}/acap-computer-vision-sdk:${SDK_VERSION}-${ARCH}-devel AS cv-sdk-devel - -# Setup proxy configuration -ARG HTTP_PROXY -ARG ARCH -ENV http_proxy=${HTTP_PROXY} -ENV https_proxy=${HTTP_PROXY} - -ENV DEBIAN_FRONTEND=noninteractive - -## Install dependencies -RUN < -DOCKER_PORT=2376 - -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT system prune --all --force -``` - -If you encounter any TLS related issues, please see the TLS setup chapter regarding the `DOCKER_CERT_PATH` environment variable in the [Docker ACAP repository](https://github.com/AxisCommunications/docker-acap#securing-the-docker-acap-using-tls). - -### Install the image - -Next, the built image needs to be uploaded to the device. This can be done through a registry or directly. In this case, the direct transfer is used by piping the compressed application directly to the device's Docker client: - -```sh -docker save $APP_NAME | docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT load -``` - -### Run the container - -With the application image on the device, it can be started. As the example uses OpenCV, the OpenCV requirements will be included in `docker-compose.yml`, which is used to run the application: - -```sh -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT compose up - -# Cleanup -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT compose down --volumes -``` - -The expected output from the application is (depending on the OpenCV pulled from the ACAP Computer Vision SDK): - -```text -... -Hello World from OpenCV 4.5.1 -``` - -## License - -**[Apache License 2.0](../LICENSE)** diff --git a/hello-world-cpp/app/Makefile b/hello-world-cpp/app/Makefile deleted file mode 100644 index 5708fc1..0000000 --- a/hello-world-cpp/app/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -TARGET = hello_world -OBJ = src/$(TARGET).o - -CXXFLAGS += -I/usr/include -I/axis/opencv/usr/include -CPPFLAGS = -Os -pipe -std=c++17 - -.PHONY: all clean - -all: $(TARGET) - -$(TARGET): $(OBJ) - $(CXX) $< $(CPPFLAGS) -o $@ && $(STRIP) --strip-unneeded $@ - -clean: - $(RM) *.o $(TARGET) diff --git a/hello-world-cpp/app/src/hello_world.cpp b/hello-world-cpp/app/src/hello_world.cpp deleted file mode 100644 index a48cc1d..0000000 --- a/hello-world-cpp/app/src/hello_world.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include - -int main(void) -{ - std::cout << "Hello World from OpenCV " << CV_VERSION << "\n"; -} diff --git a/hello-world-cpp/docker-compose.yml b/hello-world-cpp/docker-compose.yml deleted file mode 100644 index 5711242..0000000 --- a/hello-world-cpp/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: '3.3' - -services: - hello-world-cpp-app: - image: ${APP_NAME} - ipc: "host" - environment: - - DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket - - DEBUG=y - volumes: - - /usr/lib/libvdostream.so.1:/usr/lib/libvdostream.so.1 - - /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket:rw - - /var/run/statuscache:/var/run/statuscache:rw - devices: - - /dev/datacache0:/dev/datacache0:rw diff --git a/object-detector-cpp/Dockerfile b/object-detector-cpp/Dockerfile deleted file mode 100644 index a558209..0000000 --- a/object-detector-cpp/Dockerfile +++ /dev/null @@ -1,75 +0,0 @@ -# syntax=docker/dockerfile:1 - -ARG ARCH=armv7hf -ARG REPO=axisecp -ARG SDK_VERSION=1.12 -ARG UBUNTU_VERSION=22.04 - -FROM arm32v7/ubuntu:${UBUNTU_VERSION} as runtime-image-armv7hf -FROM arm64v8/ubuntu:${UBUNTU_VERSION} as runtime-image-aarch64 - -FROM ${REPO}/acap-computer-vision-sdk:${SDK_VERSION}-${ARCH} as cv-sdk-runtime -FROM ${REPO}/acap-computer-vision-sdk:${SDK_VERSION}-${ARCH}-devel as cv-sdk-devel - -# Setup proxy configuration -ARG HTTP_PROXY -ENV http_proxy=${HTTP_PROXY} -ENV https_proxy=${HTTP_PROXY} - -ENV DEBIAN_FRONTEND=noninteractive -RUN < -DOCKER_PORT=2376 - -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT system prune --all --force -``` - -If you encounter any TLS related issues, please see the TLS setup chapter regarding the `DOCKER_CERT_PATH` environment variable in the [Docker ACAP repository](https://github.com/AxisCommunications/docker-acap#securing-the-docker-acap-using-tls). - -### Install the images - -Next, the built images needs to be uploaded to the device. This can be done through a registry or directly. In this case, the direct transfer is used by piping the compressed application directly to the device's Docker client: - -```sh -docker save $APP_NAME | docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT load - -docker save $MODEL_NAME | docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT load -``` - -> [!NOTE] -> If the *inference-server* ([containerized ACAP Runtime](https://github.com/AxisCommunications/acap-runtime#containerized-version)) is not already present on the device, it will be pulled from Docker Hub -> when running `docker compose up`. -> If the pull action fails due to network connectivity, pull the image to your host system and load it to -> the device instead. - -### Run the containers - -With the images on the device, they can be started using `docker-compose.yml`: - -```sh -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT compose --env-file ./config/env.$ARCH.$CHIP up - -# Terminate with Ctrl-C and cleanup -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT compose --env-file ./config/env.$ARCH.$CHIP down --volumes -``` - -### The expected output - -```text -object-detector_1 | Caught frame 2 480x320 -object-detector_1 | Connecting to: inference-server:8501 -object-detector_1 | Waiting for response Z -object-detector_1 | Call predict OK -object-detector_1 | Object: 51 banana, Confidence: 0.91, Box: [-0.00, -0.00, 0.98, 0.93] -object-detector_1 | Capture: 90 ms -object-detector_1 | Inference grpc call: 35 ms -object-detector_1 | Postprocess: 0 ms -``` - -## Proxy settings - -Depending on the network, you might need proxy settings in the following file: `~/.docker/config.json`. - -For reference please see: https://docs.docker.com/network/proxy/. - -## Zero copy - -This example uses the inference server for video inference processing by using gRPC API. In case this client and the inference server is located on the same camera, it is possible to speed up inference by using shared memory to pass the video image to the inference server by activating following define statement in file `app/src/serving_client.hpp`: - -```c++ -#define ZEROCOPY -``` - -## Server authentication - -This example uses the inference server for video inference processing. The API uses SSL/TLS server authentication and encryption as the default behavior, but can use an insecure gRPC communication channel if no server certificate is provided. For the secure communication, we need: - -1. To provide a server certificate `server.pem` as the first parameter to object detector. -2. To start the inference server by specifying the server certificate `server.pem` and the private key `server.key`. - -Both of these are present in the `docker-compose.yml` file. - -## Model over gRPC - -This example uses the inference server for video inference processing by using gRPC API. The inference server supports multiple clients at the same time. Models are normally loaded when the inference server is starting up, but models can also be loaded by specifying the model file path over gRPC. Please note the model path specified must be accessible by the inference server. - -### Hardware acceleration - -The `./config` folder contains configuration files with the parameters to run the inference on different camera models, also giving the possibility to use the hardware accelerator. To achieve the best performance we recommend using the TPU (Tensor Processing Unit) equipped with ARTPEC-7 cameras (e.g. [Axis-Q1615 Mk III](https://www.axis.com/products/axis-q1615-mk-iii)) or the DLPU (Deep Learning Processing Unit) equipped in ARTPEC-8 cameras (e.g. [Axis-Q1656](https://www.axis.com/products/axis-q1656)). - -## License - -**[Apache License 2.0](../LICENSE)** diff --git a/object-detector-cpp/app/Makefile b/object-detector-cpp/app/Makefile deleted file mode 100644 index 3fad4bd..0000000 --- a/object-detector-cpp/app/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -# Application Name -TARGET := objdetector - -# Function to recursively find files in directory tree -rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) - -# Find all .o files compiled from protbuf files -PROTO_O := $(call rwildcard, /axis/tfproto, *.o) - -# Determine the base path -BASE := $(abspath $(patsubst %/,%,$(dir $(firstword $(MAKEFILE_LIST))))) - -# Find cpp files -OBJECTS := $(patsubst %.cpp, %.o, $(wildcard $(BASE)/src/*.cpp)) - -CXX = $(TARGET_TOOLCHAIN)-g++ -CXXFLAGS += -I/usr/include -I/usr/include/grpcpp/security -I/axis/tfproto -I/axis/openblas/usr/include -I/axis/opencv/usr/include -I/build-root/usr/include -CPPFLAGS = --sysroot=/build-root $(ARCH_CFLAGS) -Os -pipe -std=c++17 -STRIP=$(TARGET_TOOLCHAIN)-strip - -LDLIBS += -L $(BASE)/lib \ - -L /axis/opencv/usr/lib \ - -L /axis/openblas/usr/lib -LDLIBS += -lm -lopencv_core -lopencv_imgproc -lopencv_imgcodecs -lopencv_videoio -lopenblas -lgfortran -LDLIBS += -lprotobuf -lz -lgrpc -lgpr -lgrpc++ -lssl -lcrypto -lcares -lprofiler -lrt -LDLIBS += -lvdostream -lfido -lcapaxis -lstatuscache - -.PHONY: all clean - -all: $(TARGET) - -$(TARGET): $(OBJECTS) - $(CXX) $< $(CPPFLAGS) $(LDLIBS) $(PROTO_O) -o $@ && $(STRIP) --strip-unneeded $@ - -clean: - $(RM) *.o $(TARGET) diff --git a/object-detector-cpp/app/src/object_detect.cpp b/object-detector-cpp/app/src/object_detect.cpp deleted file mode 100644 index 1427ca6..0000000 --- a/object-detector-cpp/app/src/object_detect.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Copyright (C) 2021 Axis Communications AB, Lund, Sweden - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "serving_client.hpp" - -using namespace std; -using namespace cv; -using namespace chrono; -using namespace grpc; - -const int IMG_WIDTH = 480; -const int IMG_HEIGHT = 320; -constexpr size_t IMG_NBR_BYTES = IMG_WIDTH * IMG_HEIGHT; -const float CONFIDENCE_CUTOFF = 0.3; - -vector get_classes_from_file() { - string str; - vector classes; - ifstream file(getenv("OBJECT_LIST_PATH")); - - if (!file) { - cerr << "Cannot open classes file" << endl; - exit(EXIT_FAILURE); - } - - int expect = 0; - char unknown[40]; - while (getline(file, str)) { - int pos = str.find(" "); - if (pos > 0) { - int id = atoi(str.substr(0, pos).c_str()); - while (expect < id) { - snprintf(unknown, sizeof(unknown), "%d unknown", expect++); - classes.push_back(unknown); - } - classes.push_back(str); - expect++; - } - } - - return classes; -} - -VideoCapture setup_capture(int nbr_buffers) { - // Start a stream from 'CAP_PROP_CHANNEL: 1' - VideoCapture cap(1); - cap.set(CAP_PROP_FPS, 5); - cap.set(CAP_PROP_FRAME_WIDTH, IMG_WIDTH); - cap.set(CAP_PROP_FRAME_HEIGHT, IMG_HEIGHT); - cap.set(CAP_PROP_FOURCC, VideoWriter::fourcc('R', 'G', 'B', '3')); - - // Request one extra in-flight buffer to reach target FPS - cap.set(CAP_PROP_UNIMATRIX_MAX_BUFFERS, nbr_buffers + 1); - - // Validate all properties by trying to grab one frame - try { - cap.grab(); - } catch (const std::exception &e) { - cerr << "Failed to open stream: " << e.what() << endl; - exit(EXIT_FAILURE); - } - - if (nbr_buffers > cap.get(CAP_PROP_UNIMATRIX_MAX_BUFFERS)) { - cerr << "Insufficient UniMatrix Buffers!" << endl; - exit(EXIT_FAILURE); - } - - return cap; -} - -void postprocess(PredictResponse &response, const vector &classes) { - OutMap &outputs = *response.mutable_outputs(); - - const float *boxes = - (const float *)outputs["TFLite_Detection_PostProcess"].tensor_content().data(); - const float *objects = - (const float *)outputs["TFLite_Detection_PostProcess:1"].tensor_content().data(); - const float *confidences = - (const float *)outputs["TFLite_Detection_PostProcess:2"].tensor_content().data(); - const int nelm = 50; - - cout << fixed << setprecision(2); - - for (int i = 0; i < nelm; i++) { - const string object = classes[round(objects[i])]; - const float &confidence = confidences[i]; - const float *pbox = boxes + 4 * i; - - // Stop when confidence is too low - if (confidence <= CONFIDENCE_CUTOFF || confidence > 1.0) { - break; - } - - cout << "Object: " << object; - cout << ", Confidence: " << confidence; - cout << ", Box: [" << pbox[0] << ", " << pbox[1] << ", " << pbox[2] << ", " - << pbox[3] << "]" << endl; - } -} - -inline int64_t ms_time(steady_clock::time_point start, - steady_clock::time_point end) { - return duration_cast(end - start).count(); -} - -void output_timing_info(steady_clock::time_point start, - steady_clock::time_point framecapture_end, - steady_clock::time_point grpc_end, - steady_clock::time_point postprocess_end) { - - cout << "Capture: " << ms_time(start, framecapture_end) << " ms" - << endl; - cout << "Inference grpc call: " << ms_time(framecapture_end, grpc_end) << " ms" - << endl; - cout << "Postprocess: " << ms_time(grpc_end, postprocess_end) << " ms" - << endl; -} - -string read_text(const char* path) -{ - FILE* fptr = fopen(path, "r"); - if (fptr == nullptr) { - throw std::runtime_error(strerror(errno) + string(": ") + path); - } - - fseek(fptr, 0, SEEK_END); - size_t len = ftell(fptr); - fseek(fptr, 0, SEEK_SET); - string content(len + 1, '\0'); - size_t size = fread(&content[0], 1, len, fptr); - fclose(fptr); - return content; -} - -int main(int argc, char *argv[]) { - try { - // Number of outstanding zero-copy buffers (This is a very precious resource) - constexpr size_t BUFFERS = 2; - Mat frame[BUFFERS]; - - cout << "Start:"; - for (int i=0; i < argc; i++) { - cout << " " << argv[i]; - } - cout << endl; - - vector classes = get_classes_from_file(); - VideoCapture cap = setup_capture(BUFFERS); - string connect_string = string(getenv("INFERENCE_HOST")); - - shared_ptr channel; - if (argc < 2) { - shared_ptr creds = InsecureChannelCredentials(); - channel = grpc::CreateChannel(connect_string, creds); - } - else - { - string root_cert = read_text(argv[1]); - SslCredentialsOptions ssl_opts = {root_cert.c_str(), "", ""}; - shared_ptr creds = grpc::SslCredentials(ssl_opts); - grpc::ChannelArguments args; - args.SetSslTargetNameOverride("localhost"); - channel = grpc::CreateCustomChannel(connect_string, creds, args); - } - ServingClient client(channel); - - string model(getenv("MODEL_PATH")); - - int frame_idx = 0; - for (;;) { - Mat &mat = frame[frame_idx++ % BUFFERS]; - - auto time_start = steady_clock::now(); - - // Blocking read - cap.read(mat); - - auto time_framecapture_end = steady_clock::now(); - - // Make sure capture succeeded - if (mat.empty()) - throw std::runtime_error("Failed to fetch frame"); - - uint32_t seqnum = cap.get(CAP_PROP_POS_FRAMES); - // Write info about frame - cout << "Caught frame " << setw(2) << seqnum << " " << mat.cols << "x" - << mat.rows << endl; - - cout << "Connecting to: " << connect_string << endl; - - string output; - // Call the inference server - auto maybe_response = client.callPredict(model, mat, output); - - auto time_grpc_end = steady_clock::now(); - - cout << output << endl; - - // Run postprocessing if we got a valid result - if (maybe_response) { - auto response = maybe_response.value(); - postprocess(response, classes); - } - - auto time_postprocess_end = steady_clock::now(); - - output_timing_info(time_start, time_framecapture_end, - time_grpc_end, time_postprocess_end); - - cout << endl; - } - - return 0; - } catch (const exception &e) { - cerr << "Exception: " << e.what() << endl; - } -} diff --git a/object-detector-cpp/app/src/serving_client.hpp b/object-detector-cpp/app/src/serving_client.hpp deleted file mode 100644 index 1ed2137..0000000 --- a/object-detector-cpp/app/src/serving_client.hpp +++ /dev/null @@ -1,180 +0,0 @@ -/** - * Copyright (C) 2020 Axis Communications AB, Lund, Sweden - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "google/protobuf/map.h" -#include "grpcpp/create_channel.h" -#include "grpcpp/security/credentials.h" - -#include "tensorflow/core/framework/tensor.grpc.pb.h" -#include "tensorflow/core/framework/tensor_shape.grpc.pb.h" -#include "tensorflow/core/framework/types.grpc.pb.h" -#include "tensorflow_serving/apis/predict.grpc.pb.h" -#include "tensorflow_serving/apis/prediction_service.grpc.pb.h" - -#define ZEROCOPY - -using grpc::Channel; -using grpc::ClientContext; -using grpc::Status; - -using tensorflow::serving::PredictionService; -using tensorflow::serving::PredictRequest; -using tensorflow::serving::PredictResponse; - -using namespace std; - -typedef google::protobuf::Map OutMap; - -/** - * Inference serving client. Serving of inference over gRPC. - */ -class ServingClient { -public: - /** - * Class constructor. - * - * @param channel gRPC channel for requests - */ - ServingClient(std::shared_ptr channel) - : stub_(PredictionService::NewStub(channel)) { -#ifdef ZEROCOPY - // Create memory mapped file - cerr << "Create memory mapped file" << endl; - fd_ = shm_open(sharedFile_, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); - if (fd_ < 0) { - cerr << "Can not create memory mapped file" << endl; - } -#endif - } - - ~ServingClient() { - if (fd_>= 0) { - close(fd_); - } - shm_unlink(sharedFile_); - } - - /** - * Prediction request to inference server. Assembles the client's payload, - * sends it and presents the response back from the server. - * - * @param model_name name of prediction model. - * @param image to perform prediction on. - * @return status message - */ - optional callPredict(const std::string &model_name, - const cv::Mat &image, string &output); - -private: - std::unique_ptr stub_; - int fd_ = -1; - const char* sharedFile_ = "/capture.bmp"; -}; - -optional -ServingClient::callPredict(const std::string &model_name, const cv::Mat &image, - string &output) { - // Data we are sending to the server. - PredictRequest request; - request.mutable_model_spec()->set_name(model_name); - - google::protobuf::Map &inputs = - *request.mutable_inputs(); - - tensorflow::TensorProto proto; - - proto.mutable_tensor_shape()->add_dim()->set_size(1); - proto.mutable_tensor_shape()->add_dim()->set_size(image.rows); - proto.mutable_tensor_shape()->add_dim()->set_size(image.cols); - proto.mutable_tensor_shape()->add_dim()->set_size(image.channels()); - - size_t size = image.rows * image.cols * image.channels(); - - if (fd_ < 0) { - proto.set_tensor_content(image.data, size); - - proto.set_dtype(tensorflow::DataType::DT_UINT8); - - inputs["data"] = proto; - - std::cout << "Waiting for response" << std::endl; - - PredictResponse response; - ClientContext context; - // The actual RPC. - Status status = stub_->Predict(&context, request, &response); - - // Act upon its status. - if (status.ok()) { - output = "Call predict OK"; - return response; - } else { - output = "gRPC call return code: " + status.error_code() + string(": ") + - status.error_message(); - return {}; - } - } - else - { - // Zero copy image buffer - if (ftruncate(fd_, size) != 0) { - output = "Can not set size of memory mapped file"; - return {}; - } - - // Get an address to fd's memory for this process's memory space - void* data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); - if (data == MAP_FAILED) { - output = "Can not create memory map"; - return {}; - } - - memcpy(data, image.data, size); - proto.add_string_val(sharedFile_); - proto.set_dtype(tensorflow::DataType::DT_STRING); - inputs["data"] = proto; - std::cout << "Waiting for response Z" << std::endl; - - PredictResponse response; - ClientContext context; - // The actual RPC. - Status status = stub_->Predict(&context, request, &response); - - // Cleanup - munmap(data, size); - - // Act upon its status. - if (status.ok()) { - output = "Call predict OK"; - return response; - } else { - output = "gRPC call return code: " + status.error_message(); - return {}; - } - } -} diff --git a/object-detector-cpp/config/env.aarch64.artpec8 b/object-detector-cpp/config/env.aarch64.artpec8 deleted file mode 100644 index ffa0871..0000000 --- a/object-detector-cpp/config/env.aarch64.artpec8 +++ /dev/null @@ -1,3 +0,0 @@ -MODEL_PATH=/models/ssd_mobilenet_v2_coco_quant_postprocess.tflite -INFERENCE_SERVER_IMAGE=axisecp/acap-runtime:1.3.1-aarch64-containerized -INFERENCE_CHIP=12 diff --git a/object-detector-cpp/config/env.aarch64.cpu b/object-detector-cpp/config/env.aarch64.cpu deleted file mode 100644 index 6773cf7..0000000 --- a/object-detector-cpp/config/env.aarch64.cpu +++ /dev/null @@ -1,3 +0,0 @@ -MODEL_PATH=/models/ssd_mobilenet_v2_coco_quant_postprocess.tflite -INFERENCE_SERVER_IMAGE=axisecp/acap-runtime:1.3.1-aarch64-containerized -INFERENCE_CHIP=2 diff --git a/object-detector-cpp/config/env.armv7hf.cpu b/object-detector-cpp/config/env.armv7hf.cpu deleted file mode 100644 index e7b7613..0000000 --- a/object-detector-cpp/config/env.armv7hf.cpu +++ /dev/null @@ -1,3 +0,0 @@ -MODEL_PATH=/models/ssd_mobilenet_v2_coco_quant_postprocess.tflite -INFERENCE_SERVER_IMAGE=axisecp/acap-runtime:1.3.1-armv7hf-containerized -INFERENCE_CHIP=2 diff --git a/object-detector-cpp/config/env.armv7hf.tpu b/object-detector-cpp/config/env.armv7hf.tpu deleted file mode 100644 index 0d1e834..0000000 --- a/object-detector-cpp/config/env.armv7hf.tpu +++ /dev/null @@ -1,3 +0,0 @@ -MODEL_PATH=/models/ssd_mobilenet_v2_coco_quant_postprocess_edgetpu.tflite -INFERENCE_SERVER_IMAGE=axisecp/acap-runtime:1.3.1-armv7hf-containerized -INFERENCE_CHIP=4 diff --git a/object-detector-cpp/docker-compose.yml b/object-detector-cpp/docker-compose.yml deleted file mode 100644 index dc41ae7..0000000 --- a/object-detector-cpp/docker-compose.yml +++ /dev/null @@ -1,47 +0,0 @@ -version: '3.3' -services: - object-detector: - image: ${APP_NAME} - command: /usr/bin/objdetector /models/server.pem - depends_on: - - acap_dl-models - - inference-server - ipc: host - environment: - - INFERENCE_HOST=unix:///tmp/acap-runtime.sock - - INFERENCE_PORT=0 - - MODEL_PATH=${MODEL_PATH} - - OBJECT_LIST_PATH=/models/coco_labels.txt - volumes: - - /usr/lib/libvdostream.so.1:/usr/lib/libvdostream.so.1 - - acap_dl-models:/models:ro - - /var/run/dbus:/var/run/dbus:rw - - inference-server:/tmp - - inference-server: - image: ${INFERENCE_SERVER_IMAGE} - logging: - driver: "json-file" - options: - max-file: "5" - max-size: "100k" - entrypoint: ["/opt/app/acap_runtime/acapruntime", "-m", "${MODEL_PATH}", "-j", "${INFERENCE_CHIP}", "-c", "/models/server.pem", "-k", "/models/server.key"] - depends_on: - - acap_dl-models - ipc: host - volumes: - - acap_dl-models:/models:ro - - /run/dbus/system_bus_socket:/run/dbus/system_bus_socket - - /run/parhand/parhandsocket:/run/parhand/parhandsocket - - /usr/lib:/usr/lib - - /usr/bin:/usr/bin - - inference-server:/tmp - - acap_dl-models: - image: ${MODEL_NAME} - volumes: - - acap_dl-models:/models - -volumes: - acap_dl-models: {} - inference-server: {} diff --git a/opencv-image-capture-cpp/Dockerfile b/opencv-image-capture-cpp/Dockerfile deleted file mode 100644 index e0db182..0000000 --- a/opencv-image-capture-cpp/Dockerfile +++ /dev/null @@ -1,68 +0,0 @@ -# syntax=docker/dockerfile:1 - -# Specify the architecture at build time: mipsis32r2el/armv7hf/aarch64 -# Should be used for getting API image -# Currently, only armv7hf is supported -ARG ARCH=armv7hf -ARG REPO=axisecp -ARG SDK_VERSION=1.12 -ARG UBUNTU_VERSION=22.04 - -FROM arm32v7/ubuntu:${UBUNTU_VERSION} as runtime-image-armv7hf -FROM arm64v8/ubuntu:${UBUNTU_VERSION} as runtime-image-aarch64 - -FROM ${REPO}/acap-computer-vision-sdk:${SDK_VERSION}-${ARCH}-runtime AS cv-sdk-runtime -FROM ${REPO}/acap-computer-vision-sdk:${SDK_VERSION}-${ARCH}-devel AS cv-sdk-devel - -# Setup proxy configuration -ARG HTTP_PROXY -ENV http_proxy=${HTTP_PROXY} -ENV https_proxy=${HTTP_PROXY} - -ENV DEBIAN_FRONTEND=noninteractive - -## Install dependencies -ARG ARCH -RUN < -DOCKER_PORT=2376 - -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT system prune --all --force -``` - -If you encounter any TLS related issues, please see the TLS setup chapter regarding the `DOCKER_CERT_PATH` environment variable in the [Docker ACAP repository](https://github.com/AxisCommunications/docker-acap#securing-the-docker-acap-using-tls). - -### Install the image - -Next, the built image needs to be uploaded to the device. This can be done through a registry or directly. In this case, the direct transfer is used by piping the compressed application directly to the device's Docker client: - -```sh -docker save $APP_NAME | docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT load -``` - -### Run the container - -With the application image on the device, it can be started using `docker-compose.yml`: - -```sh -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT compose up - -# Terminate with Ctrl-C and cleanup -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT compose down --volumes -``` - -#### The expected output - -```text -Setting Rotation 270: Done -Setting Rotation 180: Done -Setting Rotation 90: Done -Setting Rotation 0: Done -Configured image parameters in 72667µs -FRAME[ 0] 640x360 fd[7] offs[0xc1000] size[0x56400] -FRAME[ 1] 640x360 fd[11] offs[0xc3000] size[0x56400] -FRAME[ 2] 640x360 fd[7] offs[0xc5000] size[0x56400] -FRAME[ 3] 640x360 fd[11] offs[0xc1000] size[0x56400] -FRAME[ 4] 640x360 fd[7] offs[0xc3000] size[0x56400] -FRAME[ 5] 640x360 fd[11] offs[0xc5000] size[0x56400] -FRAME[ 6] 640x360 fd[7] offs[0xc1000] size[0x56400] -FRAME[ 7] 640x360 fd[11] offs[0xc3000] size[0x56400] -FRAME[ 8] 640x360 fd[7] offs[0xc5000] size[0x56400] -FRAME[ 9] 640x360 fd[11] offs[0xc1000] size[0x56400] -FRAME[10] 640x360 fd[7] offs[0xc3000] size[0x56400] -FRAME[11] 640x360 fd[11] offs[0xc5000] size[0x56400] -FRAME[12] 640x360 fd[7] offs[0xc1000] size[0x56400] -FRAME[13] 640x360 fd[11] offs[0xc3000] size[0x56400] -FRAME[14] 640x360 fd[7] offs[0xc5000] size[0x56400] -FRAME[15] 640x360 fd[11] offs[0xc1000] size[0x56400] -FRAME[16] 640x360 fd[7] offs[0xc3000] size[0x56400] -FRAME[17] 640x360 fd[11] offs[0xc5000] size[0x56400] -FRAME[18] 640x360 fd[7] offs[0xc1000] size[0x56400] -FRAME[19] 640x360 fd[11] offs[0xc3000] size[0x56400] -FRAME[20] 640x360 fd[7] offs[0xc5000] size[0x56400] -FRAME[21] 640x360 fd[11] offs[0xc1000] size[0x56400] -FRAME[22] 640x360 fd[7] offs[0xc3000] size[0x56400] -FRAME[23] 640x360 fd[11] offs[0xc5000] size[0x56400] -FRAME[24] 640x360 fd[7] offs[0xc1000] size[0x56400] -FRAME[25] 640x360 fd[11] offs[0xc3000] size[0x56400] -FRAME[26] 640x360 fd[7] offs[0xc5000] size[0x56400] -FRAME[27] 640x360 fd[11] offs[0xc1000] size[0x56400] -FRAME[28] 640x360 fd[7] offs[0xc3000] size[0x56400] -FRAME[29] 640x360 fd[11] offs[0xc5000] size[0x56400] -Captured 30 frames in 973831µs -Optics: iCS -iteration[0] gain: 191.000000 expo: 503.000000 zoom: 1.862427 focus: 0.902723 f-number: 1.718091 -iteration[1] gain: 191.000000 expo: 503.000000 zoom: 1.862427 focus: 0.902723 f-number: 1.718091 -iteration[2] gain: 191.000000 expo: 503.000000 zoom: 1.862427 focus: 0.902723 f-number: 1.718091 -Queried settings 3 times in 15344µs -``` - -#### OpenCV-VideoIO UniMatrix extensions - -These extensions facilitate inter-process zero-copy by allowing buffers -to be forwarded across unix domain sockets. - -Mandatory zero-copy extensions: - -* CAP_PROP_UNIMATRIX_FD - Buffer file descriptor -* CAP_PROP_UNIMATRIX_FD_OFFSET - Buffer offset -* CAP_PROP_UNIMATRIX_FD_CAPACITY - Buffer capacity -* CAP_PROP_UNIMATRIX_MAX_BUFFERS - Maximum buffers in-flight - -UniMatrix conforming backends must support at least *one* of these fourcc formats: - -* NV12 -* NV21 -* RGB3 - -The most efficient color format should always be default. - -UniMatrix conforming backends must support monochrome fourcc: - -* Y800 - -Optional properties: - -* CAP_PROP_UNIMATRIX_FNUMBER - f-number - -#### OpenCV-VideoIO Axis implementation - -Axis supports all UniMatrix extensions: - -* CAP_PROP_UNIMATRIX_FD -* CAP_PROP_UNIMATRIX_FD_OFFSET -* CAP_PROP_UNIMATRIX_FD_CAPACITY -* CAP_PROP_UNIMATRIX_MAX_BUFFERS -* CAP_PROP_UNIMATRIX_FNUMBER - -These UniMatrix stream formats are not supported: - -* NV21 - -Below stream formats are fully hardware-accelerated: - -* Y800 -* NV12 - This is the default native format of the camera. - -Below stream formats are converted from the native format (i.e. it has a performance penalty): - -* RGB3 - -These *stream* properties can be changed before capturing the first frame, -not when the stream is running: - -* CAP_PROP_FPS -* CAP_PROP_FOURCC -* CAP_PROP_CHANNEL -* CAP_PROP_FRAME_WIDTH -* CAP_PROP_FRAME_HEIGHT -* CAP_PROP_UNIMATRIX_ROTATION - Possible rotations include [0,90,180,270]. - Not every camera is required to support every rotation. - -These *stream* properties are read-only: - -* CAP_PROP_POS_MSEC -* CAP_PROP_POS_FRAMES - -These *image* properties are read-only: - -* CAP_PROP_ZOOM - Zoom factor (1.0-) -* CAP_PROP_FOCUS - Focus dioptre -* CAP_PROP_GAIN - Gain in milli-dB -* CAP_PROP_EXPOSURE - Exposure in µs -* CAP_PROP_UNIMATRIX_FNUMBER - f-number -* CAP_PROP_UNIMATRIX_OPTICS_TYPE - * CAP_UNIMATRIX_OPTICS_TYPE_MANUAL - Manual zoom/focus/iris - * CAP_UNIMATRIX_OPTICS_TYPE_DC - Manual zoom/focus with DirectControl-iris - * CAP_UNIMATRIX_OPTICS_TYPE_P - Manual zoom/focus with Precise-iris - * CAP_UNIMATRIX_OPTICS_TYPE_iCS - Intelligent CS-mount - * CAP_UNIMATRIX_OPTICS_TYPE_CAMBLOCK - Camblock - -These *image* properties are write-only: - -* CAP_PROP_UNIMATRIX_EXPOSURE_MODE - * CAP_UNIMATRIX_EXPOSURE_MODE_AUTO - Automatic exposure - * CAP_UNIMATRIX_EXPOSURE_MODE_HOLD - Hold current exposure - * CAP_PROP_UNIMATRIX_MAX_EXPOSURE_us - Limit max automatic exposure time (unit: µs) - -These *image* properties are read-write: - -* CAP_PROP_UNIMATRIX_TONEMAPPING - ToneMapping [0-100] -* CAP_PROP_UNIMATRIX_TEMPORAL_FILTER - Temporal Noise-Filter [0-100] - -## Proxy settings - -Depending on the network, you might need proxy settings in the following file: `~/.docker/config.json`. - -For reference please see: https://docs.docker.com/network/proxy/. - -## License - -**[Apache License 2.0](../LICENSE)** - -## References - -* https://docs.opencv.org/ diff --git a/opencv-image-capture-cpp/app/Makefile b/opencv-image-capture-cpp/app/Makefile deleted file mode 100644 index 9fd19ed..0000000 --- a/opencv-image-capture-cpp/app/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -# Application Name -TARGET := capture_app - -# Determine the base path -BASE := $(abspath $(patsubst %/,%,$(dir $(firstword $(MAKEFILE_LIST))))) - -# Find cpp files -OBJECTS := $(patsubst %.cpp, %.o, $(wildcard $(BASE)/src/*.cpp)) - -CXXFLAGS += -I/usr/include -I/axis/opencv/usr/include -CPPFLAGS = -Os -pipe -std=c++17 - -LDLIBS += -L /axis/opencv/usr/lib -L /axis/openblas/usr/lib -LDLIBS += -lm -lstdc++ -lopencv_core -lopencv_imgproc -lopencv_imgcodecs -lopencv_videoio -LDLIBS += -lvdostream -lfido -lcapaxis -lstatuscache -lopenblas -LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --libs $(PKGS)) - -.PHONY: all clean - -all: $(TARGET) - -$(TARGET): $(OBJECTS) - $(CXX) $< $(CPPFLAGS) $(LDLIBS) -o $@ && $(STRIP) --strip-unneeded $@ - -clean: - $(RM) *.o $(TARGET) diff --git a/opencv-image-capture-cpp/app/src/capture.cpp b/opencv-image-capture-cpp/app/src/capture.cpp deleted file mode 100644 index e558e9d..0000000 --- a/opencv-image-capture-cpp/app/src/capture.cpp +++ /dev/null @@ -1,207 +0,0 @@ -/** - * Copyright (C) 2021 Axis Communications AB, Lund, Sweden - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -// UniMatrix zero-copy example -#include - -#include -#include - -#include -#include - -using namespace cv; -using namespace std; - -static inline uint64_t -monotonic_now() -{ - struct timespec ts = {0}; - - if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0) - throw std::runtime_error("Clock Monotonic failed"); - - return uint64_t(ts.tv_sec) * 1000000u + (ts.tv_nsec / 1000u); -} - -int main(void) try -{ - // Number of outstanding zero-copy buffers (This is a very precious resource) - constexpr size_t BUFFERS = 2; - constexpr size_t NFRAMES = 30; - Mat frame[BUFFERS]; - - // Start a stream from 'CAP_PROP_CHANNEL: 1' - VideoCapture cap(1); - cap.set(CAP_PROP_FPS, 30); - cap.set(CAP_PROP_FRAME_WIDTH, 640); - cap.set(CAP_PROP_FRAME_HEIGHT, 360); - - // The most efficient color format is always default. - // Only the default format is guaranteed to have zero-copy. - // It's mandatory to support at least one of the formats below. - cap.set(CAP_PROP_FOURCC, VideoWriter::fourcc('N','V','1','2')); - cap.set(CAP_PROP_FOURCC, VideoWriter::fourcc('N','V','2','1')); - cap.set(CAP_PROP_FOURCC, VideoWriter::fourcc('R','G','B','3')); - - // Monochrome is mandatory - cap.set(CAP_PROP_FOURCC, VideoWriter::fourcc('Y','8','0','0')); - - // Request one extra in-flight buffer to reach target FPS - cap.set(CAP_PROP_UNIMATRIX_MAX_BUFFERS, BUFFERS + 1); - - // Choose Rotation before grabbing the first frame. - // This is a potentially slow operation any active streams will be closed! - try - { - cout << "Setting Rotation 270: " << flush; - cap.set(CAP_PROP_UNIMATRIX_ROTATION, 270); - cout << "Done" << endl; - - cout << "Setting Rotation 180: " << flush; - cap.set(CAP_PROP_UNIMATRIX_ROTATION, 180); - cout << "Done" << endl; - - cout << "Setting Rotation 90: " << flush; - cap.set(CAP_PROP_UNIMATRIX_ROTATION, 90); - cout << "Done" << endl; - - cout << "Setting Rotation 0: " << flush; - cap.set(CAP_PROP_UNIMATRIX_ROTATION, 0); - cout << "Done" << endl; - } - catch(const std::exception& e) - { - cerr << "Failed setting Rotation: " << e.what() << endl; - } - - // Validate all properties by trying to grab one frame - try { cap.grab(); } - catch(const std::exception& e) - { - cerr << "Failed to open stream: " << e.what() << endl; - exit(EXIT_FAILURE); - } - - if (BUFFERS > cap.get(CAP_PROP_UNIMATRIX_MAX_BUFFERS)) { - cerr << "Insufficient UniMatrix Buffers!" << endl; - exit(EXIT_FAILURE); - } - - uint64_t start = monotonic_now(); - uint64_t delta = 0; - - cap.set(CAP_PROP_UNIMATRIX_TONEMAPPING, 50); - cap.set(CAP_PROP_UNIMATRIX_TEMPORAL_FILTER, 50); - cap.set(CAP_PROP_UNIMATRIX_EXPOSURE_MODE, CAP_UNIMATRIX_EXPOSURE_MODE_HOLD); - cap.set(CAP_PROP_UNIMATRIX_EXPOSURE_MODE, CAP_UNIMATRIX_EXPOSURE_MODE_AUTO); - cap.set(CAP_PROP_UNIMATRIX_MAX_EXPOSURE_us, 10000); // 10000µs = 1/100s - - delta = monotonic_now() - start; - cout << "Configured image parameters in " << delta << "µs" << endl; - - // Fetch frames - start = monotonic_now(); - for (size_t i = 0; i < NFRAMES; ++i) - { - Mat& mat = frame[i%BUFFERS]; - - // Blocking read - cap.read(mat); - - // Make sure capture succeeded - if (mat.empty()) - throw std::runtime_error("Failed to fetch frame"); - - // Fetch UniMatrix zero-copy properties - int fd = cap.get(CAP_PROP_UNIMATRIX_FD); - off_t offs = cap.get(CAP_PROP_UNIMATRIX_FD_OFFSET); - size_t size = cap.get(CAP_PROP_UNIMATRIX_FD_CAPACITY); - - uint32_t seqnum = cap.get(CAP_PROP_POS_FRAMES); - - // Success! - cout << "FRAME[" << std::setw(2) << seqnum << "] " - << mat.cols << "x" << mat.rows << "\t" - << "fd[" << fd << "]\t" - << std::hex - << "offs[0x" << offs << "]\t" - << "size[0x" << size << "]\t" - << std::dec - << endl; - } - - delta = monotonic_now() - start; - cout << std::fixed; - cout << "Captured " << NFRAMES << " frames in " << delta << "µs" << endl; - - start = monotonic_now(); - - switch (lround(cap.get(CAP_PROP_UNIMATRIX_OPTICS_TYPE))) - { - case CAP_UNIMATRIX_OPTICS_TYPE_MANUAL: - cout << "Optics: Manual zoom/focus/iris" << endl; - break; - case CAP_UNIMATRIX_OPTICS_TYPE_DC: - cout << "Optics: DirectControl-iris" << endl; - break; - case CAP_UNIMATRIX_OPTICS_TYPE_P: - cout << "Optics: Precise-iris" << endl; - break; - case CAP_UNIMATRIX_OPTICS_TYPE_iCS: - cout << "Optics: iCS" << endl; - break; - case CAP_UNIMATRIX_OPTICS_TYPE_CAMBLOCK: - cout << "Optics: Camblock" << endl; - break; - default: - cout << "Optics: Unknown" << endl; - break; - } - - // Read misc camera properties - constexpr size_t ITER = 3u; - for (size_t i = 0; i < ITER; ++i) - { - double gain = cap.get(CAP_PROP_GAIN); - double expo = cap.get(CAP_PROP_EXPOSURE); - double zoom = cap.get(CAP_PROP_ZOOM); - double focus = cap.get(CAP_PROP_FOCUS); - double fnumber = cap.get(CAP_PROP_UNIMATRIX_FNUMBER); - double tonemapping = cap.get(CAP_PROP_UNIMATRIX_TONEMAPPING); - double temporal_filter = cap.get(CAP_PROP_UNIMATRIX_TEMPORAL_FILTER); - - // Success! - cout << "iteration[" << std::setw(1) << i << "] " - << "gain: " << gain << " " - << "expo: " << expo << " " - << "zoom: " << zoom << " " - << "focus: " << focus << " " - << "f-number: " << fnumber << " " - << "tonemapping: " << tonemapping << " " - << "temporal-filter: " << temporal_filter << endl; - } - - delta = monotonic_now() - start; - cout << "Queried settings " << ITER << " times in " << delta << "µs" << endl; - - return 0; -} -catch(const std::exception& e) -{ - cerr << e.what() << endl; -} diff --git a/opencv-image-capture-cpp/docker-compose.yml b/opencv-image-capture-cpp/docker-compose.yml deleted file mode 100644 index a362374..0000000 --- a/opencv-image-capture-cpp/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -version: '3.3' - -services: - capture-example-app: - image: ${APP_NAME} - ipc: "host" - privileged: true - environment: - - DBUS_SYSTEM_BUS_ADDRESS=unix:path=/var/run/dbus/system_bus_socket - - DEBUG=y - volumes: - - /usr/lib/libvdostream.so.1:/usr/lib/libvdostream.so.1 - - /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket:rw - - /var/run/statuscache:/var/run/statuscache:rw - devices: - - /dev/datacache0:/dev/datacache0:rw - diff --git a/parameter-api-cpp/.gitignore b/parameter-api-cpp/.gitignore deleted file mode 100644 index f24d8a7..0000000 --- a/parameter-api-cpp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/certificates diff --git a/parameter-api-cpp/Dockerfile b/parameter-api-cpp/Dockerfile deleted file mode 100644 index 0e87152..0000000 --- a/parameter-api-cpp/Dockerfile +++ /dev/null @@ -1,75 +0,0 @@ -# syntax=docker/dockerfile:1 - -ARG ARCH=armv7hf -ARG REPO=axisecp -ARG SDK_VERSION=1.12 -ARG UBUNTU_VERSION=22.04 - -FROM arm32v7/ubuntu:${UBUNTU_VERSION} as runtime-image-armv7hf -FROM arm64v8/ubuntu:${UBUNTU_VERSION} as runtime-image-aarch64 - -FROM ${REPO}/acap-computer-vision-sdk:${SDK_VERSION}-${ARCH} as cv-sdk-runtime -FROM ${REPO}/acap-computer-vision-sdk:${SDK_VERSION}-${ARCH}-devel as cv-sdk-devel - -ENV DEBIAN_FRONTEND=noninteractive -# TARGET_TOOLCHAIN is defined as the platform arch string -# i.e., arm-linux-gnueabihf for the ARTPEC-7 platform - -# Setup proxy configuration -ARG HTTP_PROXY -ENV http_proxy=${HTTP_PROXY} -ENV https_proxy=${HTTP_PROXY} - -RUN </axis-cgi/param.cgi?action=list` where `` is the IP address of your device. - -## Example structure - -Below is the structure of the example with a brief description of scripts. - -```text -parameter-api-cpp -├── app -│ ├── apis -│ │ └── keyvaluestore.proto -│ ├── src -│ │ └── parameter.cpp -│ └── Makefile -├── docker-compose.yml -├── Dockerfile -└── README.md -``` - -* **app/apis/keyvaluestore.proto** - Protobuf file to define Parameter-API messages and services -* **app/src/parameter.cpp** - App to retrieve Axis device parameters -* **app/Makefile** - Used by the make tool to build the program -* **docker-compose.yml** - Specifies the images used to run the application, and their interdependencies -* **Dockerfile** - Specifies how the application is built -* **README.md** - Instructions on how to build and run the application - -## Requirements - -Meet the following requirements to ensure compatibility with the example: - -* Axis device - * Chip: ARTPEC-{7-8} DLPU devices (e.g., Q1615 MkIII) - * Firmware: 10.9 or higher - * [Docker ACAP](https://github.com/AxisCommunications/docker-acap#installing) installed and started, using TLS and SD card as storage - * [ACAP Runtime](https://github.com/AxisCommunications/acap-runtime#native-acap-application) installed and started -* Computer - * Either [Docker Desktop](https://docs.docker.com/desktop/) version 4.11.1 or higher, - * or [Docker Engine](https://docs.docker.com/engine/) version 20.10.17 or higher with BuildKit enabled using Docker Compose version 1.29.2 or higher - -## Proxy settings - -Depending on the network, you might need proxy settings in the following file: `~/.docker/config.json`. - -For reference please see: https://docs.docker.com/network/proxy/. - -## How to run the code - -### Export the environment variable for the architecture - -Export the `ARCH` variable depending on the architecture of your camera: - -```sh -# For arm32 -export ARCH=armv7hf - -# For arm64 -export ARCH=aarch64 -``` - -### Build the Docker image - -With the architecture defined, the `parameter-api` image can be built. The environment variables are supplied as build arguments such that they are made available to Docker during the build process: - -```sh -# Define app name -export APP_NAME=parameter-api - -# Build app -docker build --tag $APP_NAME --build-arg ARCH . -``` - -### Set your device IP address and clear Docker memory - -```sh -DEVICE_IP= -DOCKER_PORT=2376 - -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT system prune --all --force -``` - -If you encounter any TLS related issues, please see the TLS setup chapter regarding the `DOCKER_CERT_PATH` environment variable in the [Docker ACAP repository](https://github.com/AxisCommunications/docker-acap#securing-the-docker-acap-using-tls). - -### Install the image and required server certificates - -To use SSL/TLS server authentication and encryption, a server certificate is provided as a parameter to the application in the `docker-compose.yml` file: - -```yaml - entrypoint: /usr/bin/parameter /certificates/server.pem -``` - -Certificate files for TLS are created in the build process of this example and must be copied to ACAP Runtime folder on the device: - -```sh -docker cp $(docker create $APP_NAME):/certificates . -scp certificates/* root@$DEVICE_IP:/usr/local/packages/acapruntime -``` - -Use SSH to change the ownership of the files on the device: - -```sh -ssh root@$DEVICE_IP 'chown sdk /usr/local/packages/acapruntime/server.*' -``` - -After copying the server certificates onto the device, we have to make sure to enable TLS and then restart the ACAP Runtime. - -```sh -AXIS_TARGET_PASSWORD='' - -# Enable TLS -curl --anyauth -u "root:$AXIS_TARGET_PASSWORD" "$DEVICE_IP/axis-cgi/param.cgi?action=update&acapruntime.UseTLS=yes" - -# Restart ACAP -curl --anyauth -u "root:$AXIS_TARGET_PASSWORD" "$DEVICE_IP/axis-cgi/applications/control.cgi?package=acapruntime&action=restart" -``` - -where `` is the password to the `root` user. - -Finally install the application image to the device. This can be done through a registry or directly. In this case, the direct transfer is used by piping the compressed application directly to the device's Docker client: - -```sh -docker save $APP_NAME | docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT load -``` - -### Run the container - -With the application image on the device, it can be started using `docker-compose.yml`: - -```sh -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT compose up - -# Cleanup -docker --tlsverify --host tcp://$DEVICE_IP:$DOCKER_PORT compose down --volumes -``` - -### The expected output - -```text -parameter-api_1 | root.Brand.Brand : AXIS -parameter-api_1 | root.Brand.WebURL : http://www.axis.com -parameter-api_1 | root.Image.I0.Enabled : yes -parameter-api_1 | root.Brand.ProdFullName : AXIS Q1615 Mk III Network Camera -parameter-api_1 | root.Brand.ProdNbr : Q1615 Mk III -parameter-api_1 | root.invalid : -``` - -## License - -**[Apache License 2.0](../LICENSE)** diff --git a/parameter-api-cpp/app/Makefile b/parameter-api-cpp/app/Makefile deleted file mode 100644 index ecdca6f..0000000 --- a/parameter-api-cpp/app/Makefile +++ /dev/null @@ -1,53 +0,0 @@ -# Application Name -TARGET := parameter - -# Paths -OUT_PATH ?= $(CURDIR) -API_PATH := $(CURDIR)/apis -SRC_PATH := $(CURDIR)/src -GRPC_CPP_PLUGIN := grpc_cpp_plugin -GRPC_CPP_PLUGIN_PATH ?= $(shell which $(GRPC_CPP_PLUGIN)) - -# Function to recursively find files in directory tree -rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) - -# Find cpp files -OBJECTS := $(patsubst %.cpp, %.o, $(wildcard $(SRC_PATH)/*.cpp)) - -# Build files -PROTOBUF_FILES := $(call rwildcard, $(API_PATH),*.proto) -PROTOBUF_H := $(patsubst %.proto,%.pb.h,$(patsubst $(API_PATH)%,$(SRC_PATH)%,$(PROTOBUF_FILES))) -PROTOBUF_O := $(patsubst %.pb.h,%.pb.o,$(PROTOBUF_H)) -PROTOBUF_GRPC_O := $(patsubst %.pb.h,%.grpc.pb.o,$(PROTOBUF_H)) - -CXX = $(TARGET_TOOLCHAIN)-g++ -CXXFLAGS += -I/usr/include/grpcpp/security -I/build-root/usr/include -CPPFLAGS = --sysroot=/build-root $(ARCH_CFLAGS) -Os -pipe -std=c++17 -STRIP=$(TARGET_TOOLCHAIN)-strip - -# Compiler flags -LDLIBS += -lprotobuf -lgrpc -lgpr -lgrpc++ -.PHONY: all clean - -all: $(TARGET) - -$(TARGET): $(PROTOBUF_H) $(PROTOBUF_O) $(PROTOBUF_GRPC_O) $(OBJECTS) - $(CXX) $< $(CPPFLAGS) $(PROTOBUF_O) $(PROTOBUF_GRPC_O) $(OBJECTS) $(LDLIBS) -o $@ && $(STRIP) --strip-unneeded $@ - -# Protobuf object files -%.pb.o: %.pb.cc - $(CXX) -c $(CXXFLAGS) -I$(OUT_PATH) $^ -o $@ - -# Generate protobuf gRPC source files -$(SRC_PATH)/%.grpc.pb.cc $(SRC_PATH)/%grpc.pb.h: $(API_PATH)/%.proto | - protoc $(PKG_CONFIG_CFLAGS_I) \ - -I$(API_PATH) \ - --grpc_out=$(SRC_PATH) \ - --plugin=protoc-gen-grpc=$(GRPC_CPP_PLUGIN_PATH) $< - -# Generate protobuf source files -$(SRC_PATH)/%.pb.cc $(SRC_PATH)/%.pb.h: $(API_PATH)/%.proto | - protoc $(PKG_CONFIG_CFLAGS_I) -I$(API_PATH) --cpp_out=$(SRC_PATH) $< - -clean: - $(RM) *.o $(TARGET) diff --git a/parameter-api-cpp/app/apis/keyvaluestore.proto b/parameter-api-cpp/app/apis/keyvaluestore.proto deleted file mode 100644 index 4a4f869..0000000 --- a/parameter-api-cpp/app/apis/keyvaluestore.proto +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (C) 2022 Axis Communications AB, Lund, Sweden - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto3"; - -package keyvaluestore; - -// A simple key-value storage service -service KeyValueStore { - // Provides a value for each key request - rpc GetValues (stream Request) returns (stream Response) {} -} - -// The request message containing the key -message Request { - string key = 1; -} - -// The response message containing the value associated with the key -message Response { - string value = 1; -} diff --git a/parameter-api-cpp/app/src/parameter.cpp b/parameter-api-cpp/app/src/parameter.cpp deleted file mode 100644 index 7e38f13..0000000 --- a/parameter-api-cpp/app/src/parameter.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright (C) 2022 Axis Communications AB, Lund, Sweden - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "keyvaluestore.grpc.pb.h" -#include "grpcpp/create_channel.h" -#include "grpcpp/security/credentials.h" -#include "google/protobuf/map.h" -#include -#include - -using namespace std; -using namespace keyvaluestore; -using namespace grpc; - -using grpc::Channel; -using grpc::ClientContext; -using grpc::Status; - -const char* target = "unix:///tmp/acap-runtime.sock"; - -class Parameter { - public: - Parameter(std::shared_ptr channel) - : stub_(KeyValueStore::NewStub(channel)) {} - - // Requests each key in the vector and return the key - // and its corresponding value as a pair - vector> GetValues( - const vector& keys) { - - vector> values; - ClientContext context; - Request request; - Response response; - - auto stream = stub_->GetValues(&context); - for (const auto& key : keys) { - // Key we are sending to the server. - request.set_key(key); - stream->Write(request); - - // Get the value for the sent key - if (stream->Read(&response)) { - values.push_back(make_pair(key, response.value())); - } - } - - stream->WritesDone(); - Status status = stream->Finish(); - - // Act upon its status. - if (!status.ok()) { - throw std::runtime_error("Can not access gRPC channel: " + status.error_message()); - } - return values; - } - private: - std::unique_ptr stub_; -}; - -string read_text(const char* path) -{ - FILE* fptr = fopen(path, "r"); - if (fptr == nullptr) { - throw std::runtime_error(strerror(errno) + string(": ") + path); - } - - fseek(fptr, 0, SEEK_END); - size_t len = ftell(fptr); - fseek(fptr, 0, SEEK_SET); - string content(len + 1, '\0'); - size_t size = fread(&content[0], 1, len, fptr); - fclose(fptr); - return content; -} - -// main function -int main(int argc, char* argv[]) -{ - // camera keys - vector keys = { - "root.Brand.Brand", - "root.Brand.WebURL", - "root.Image.I0.Enabled", - "root.Brand.ProdFullName", - "root.Brand.ProdNbr", - "root.invalid" - }; - - try { - // Create channel - shared_ptr channel; - if (argc > 1) { - // gRPC secure connection - string root_cert = read_text(argv[1]); - SslCredentialsOptions ssl_opts = {root_cert.c_str(), "", ""}; - shared_ptr creds = grpc::SslCredentials(ssl_opts); - grpc::ChannelArguments args; - args.SetSslTargetNameOverride("localhost"); - channel = grpc::CreateCustomChannel(target, creds, args); - } - else { - // gRPC insecure connection - channel = grpc::CreateChannel( - target, grpc::InsecureChannelCredentials()); - } - - // Get parameters - Parameter param(channel); - vector> values = param.GetValues(keys); - // Display camera key and value pair - for (pair value: values) { - cout << value.first << " : " << value.second << endl; - } - return 0; - } - catch(const std::exception& e) - { - cerr << "Error has occurred: " << e.what() << endl; - } -} diff --git a/parameter-api-cpp/docker-compose.yml b/parameter-api-cpp/docker-compose.yml deleted file mode 100644 index 10d9729..0000000 --- a/parameter-api-cpp/docker-compose.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: '3.3' - -services: - parameter-api: - image: ${APP_NAME} - entrypoint: /usr/bin/parameter /certificates/server.pem - volumes: - - /tmp:/tmp