From f5f4bd41154c9dec871a3ab2d156236f1558c43e Mon Sep 17 00:00:00 2001 From: Artur Mukhamadiev Date: Thu, 12 Mar 2026 22:04:26 +0300 Subject: [PATCH] [image] connection block between opencv and rpc Details: imageRPC for now assumed to have 4 fields: width,height of type int (4 signed bytes) type (enum BGR,RGBA,DEPTH) and raw vector of data tests written by opencode + gemini flash TG-7 --- include/cloud_point/image.h | 4 +- include/cloud_point/imageFactory.h | 40 +++++++++++++ include/cloud_point_rpc/imageRpc.h | 21 +++++++ src/cloud_point/image.cpp | 4 +- tests/meson.build | 17 +++++- tests/test_image.cpp | 91 ++++++++++++++++++++++++++++++ 6 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 include/cloud_point/imageFactory.h create mode 100644 include/cloud_point_rpc/imageRpc.h create mode 100644 tests/test_image.cpp diff --git a/include/cloud_point/image.h b/include/cloud_point/image.h index c12a194..c54812e 100644 --- a/include/cloud_point/image.h +++ b/include/cloud_point/image.h @@ -8,10 +8,10 @@ namespace score { class Image { public: Image(); - explicit Image(cv::Mat&& image); + explicit Image(const cv::Mat& image); ~Image(); - cv::Mat get() const; + [[nodiscard]] cv::Mat get() const; protected: cv::Mat data_; diff --git a/include/cloud_point/imageFactory.h b/include/cloud_point/imageFactory.h new file mode 100644 index 0000000..7de2bb8 --- /dev/null +++ b/include/cloud_point/imageFactory.h @@ -0,0 +1,40 @@ +// +// Created by vptyp on 12.03.2026. +// + +#pragma once +#include +#include +namespace score { + +class ImageFactory { + public: + /** + * @brief tries to decode available RPC image type to opencv compliant + * @return opencv compliant image type + * @throw runtime_error if type is unknown + */ + static int pixelType(const ImageRPC::Type &type) { + switch (type) { + case ImageRPC::Type::BGR: + return CV_8UC3; + case ImageRPC::Type::RGBA: + return CV_8UC4; + case ImageRPC::Type::DEPTH: + return CV_64FC1; + default: + throw std::runtime_error("Unknown image type"); + } + } + /** + * @brief tries to create Image object from ImageRPC + * @throw runtime_error if type is unknown + */ + static Image create(const ImageRPC &image) { + cv::Mat imageMat(image.width, image.height, pixelType(image.type), + const_cast(image.data.data())); + + return Image{imageMat}; + } +}; +} // namespace score \ No newline at end of file diff --git a/include/cloud_point_rpc/imageRpc.h b/include/cloud_point_rpc/imageRpc.h new file mode 100644 index 0000000..ff4b69f --- /dev/null +++ b/include/cloud_point_rpc/imageRpc.h @@ -0,0 +1,21 @@ +// +// Created by vptyp on 12.03.2026. +// +#pragma once +#include + +namespace score { + +struct ImageRPC { + int width{0}; + int height{0}; + enum class Type { + UNKNOWN, + BGR, + RGBA, + DEPTH, + } type{Type::UNKNOWN}; + std::vector data; +}; + +} // namespace score \ No newline at end of file diff --git a/src/cloud_point/image.cpp b/src/cloud_point/image.cpp index cc265f2..7a36c44 100644 --- a/src/cloud_point/image.cpp +++ b/src/cloud_point/image.cpp @@ -9,8 +9,8 @@ namespace score { //no work } - Image::Image(cv::Mat&& image) { - this->data_ = std::move(image); + Image::Image(const cv::Mat& image) { + this->data_ = image; } Image::~Image() = default; diff --git a/tests/meson.build b/tests/meson.build index 2e02e8a..b04ede3 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -6,9 +6,24 @@ test_sources = files( 'test_c_api.cpp', 'test_base64.cpp' ) +test_deps = [cloud_point_rpc_dep, cloud_point_rpc_cli_dep, + cloud_point_rpc_test_dep, json_dep, gtest_dep, + gtest_main_dep, gmock_dep] + +if opencv_dep.found() + message('found cloud_point_compute dependency') + test_sources += files( + 'test_image.cpp' + ) + test_deps += [cloud_point_compute_dep] +else + message('cpc_dep was not found') +endif + + test_exe = executable('unit_tests', test_sources, - dependencies : [cloud_point_rpc_dep, cloud_point_rpc_cli_dep, cloud_point_rpc_test_dep, json_dep, gtest_dep, gtest_main_dep, gmock_dep]) + dependencies : test_deps) test('unit_tests', test_exe) diff --git a/tests/test_image.cpp b/tests/test_image.cpp new file mode 100644 index 0000000..aa3f4e7 --- /dev/null +++ b/tests/test_image.cpp @@ -0,0 +1,91 @@ +// +// Created by vptyp on 12.03.2026. +// +#include +#include + +#include + +class ImageTest : public ::testing::Test { + protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(ImageTest, DefaultConstructor) { + score::Image image; + cv::Mat mat = image.get(); + EXPECT_TRUE(mat.empty()); +} + +TEST_F(ImageTest, ConstructorWithMat) { + cv::Mat input = cv::Mat::zeros(5, 5, CV_8UC1); + score::Image image(input); + cv::Mat output = image.get(); + EXPECT_EQ(output.rows, 5); + EXPECT_EQ(output.cols, 5); + EXPECT_EQ(output.type(), CV_8UC1); +} + +TEST_F(ImageTest, PixelTypeMapping) { + EXPECT_EQ(score::ImageFactory::pixelType(score::ImageRPC::Type::BGR), + CV_8UC3); + EXPECT_EQ(score::ImageFactory::pixelType(score::ImageRPC::Type::RGBA), + CV_8UC4); + EXPECT_EQ(score::ImageFactory::pixelType(score::ImageRPC::Type::DEPTH), + CV_64FC1); + EXPECT_THROW(score::ImageFactory::pixelType(score::ImageRPC::Type::UNKNOWN), + std::runtime_error); +} + +TEST_F(ImageTest, CreateBGR) { + score::ImageRPC rpc; + rpc.width = 10; + rpc.height = 20; + rpc.type = score::ImageRPC::Type::BGR; + rpc.data.resize(rpc.width * rpc.height * 3, 128); + + score::Image image = score::ImageFactory::create(rpc); + cv::Mat mat = image.get(); + + EXPECT_EQ(mat.rows, 10); + EXPECT_EQ(mat.cols, 20); + EXPECT_EQ(mat.type(), CV_8UC3); + EXPECT_EQ(mat.at(0, 0)[0], 128); +} + +TEST_F(ImageTest, CreateRGBA) { + score::ImageRPC rpc; + rpc.width = 15; + rpc.height = 25; + rpc.type = score::ImageRPC::Type::RGBA; + rpc.data.resize(rpc.width * rpc.height * 4, 255); + + score::Image image = score::ImageFactory::create(rpc); + cv::Mat mat = image.get(); + + EXPECT_EQ(mat.rows, 15); + EXPECT_EQ(mat.cols, 25); + EXPECT_EQ(mat.type(), CV_8UC4); + EXPECT_EQ(mat.at(0, 0)[0], 255); +} + +TEST_F(ImageTest, CreateDepth) { + score::ImageRPC rpc; + rpc.width = 5; + rpc.height = 10; + rpc.type = score::ImageRPC::Type::DEPTH; + rpc.data.resize(rpc.width * rpc.height * sizeof(double)); + auto *dataPtr = reinterpret_cast(rpc.data.data()); + for (int i = 0; i < 50; ++i) + dataPtr[i] = static_cast(i); + + score::Image image = score::ImageFactory::create(rpc); + cv::Mat mat = image.get(); + + EXPECT_EQ(mat.rows, 5); + EXPECT_EQ(mat.cols, 10); + EXPECT_EQ(mat.type(), CV_64FC1); + EXPECT_DOUBLE_EQ(mat.at(0, 0), 0.0); + EXPECT_DOUBLE_EQ(mat.at(4, 9), 49.0); +}