[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
This commit is contained in:
parent
9cb3f009ea
commit
f5f4bd4115
@ -8,10 +8,10 @@ namespace score {
|
|||||||
class Image {
|
class Image {
|
||||||
public:
|
public:
|
||||||
Image();
|
Image();
|
||||||
explicit Image(cv::Mat&& image);
|
explicit Image(const cv::Mat& image);
|
||||||
~Image();
|
~Image();
|
||||||
|
|
||||||
cv::Mat get() const;
|
[[nodiscard]] cv::Mat get() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
cv::Mat data_;
|
cv::Mat data_;
|
||||||
|
|||||||
40
include/cloud_point/imageFactory.h
Normal file
40
include/cloud_point/imageFactory.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// Created by vptyp on 12.03.2026.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cloud_point/image.h>
|
||||||
|
#include <cloud_point_rpc/imageRpc.h>
|
||||||
|
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<unsigned char *>(image.data.data()));
|
||||||
|
|
||||||
|
return Image{imageMat};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace score
|
||||||
21
include/cloud_point_rpc/imageRpc.h
Normal file
21
include/cloud_point_rpc/imageRpc.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// Created by vptyp on 12.03.2026.
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace score {
|
||||||
|
|
||||||
|
struct ImageRPC {
|
||||||
|
int width{0};
|
||||||
|
int height{0};
|
||||||
|
enum class Type {
|
||||||
|
UNKNOWN,
|
||||||
|
BGR,
|
||||||
|
RGBA,
|
||||||
|
DEPTH,
|
||||||
|
} type{Type::UNKNOWN};
|
||||||
|
std::vector<unsigned char> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace score
|
||||||
@ -9,8 +9,8 @@ namespace score {
|
|||||||
//no work
|
//no work
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::Image(cv::Mat&& image) {
|
Image::Image(const cv::Mat& image) {
|
||||||
this->data_ = std::move(image);
|
this->data_ = image;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image::~Image() = default;
|
Image::~Image() = default;
|
||||||
|
|||||||
@ -6,9 +6,24 @@ test_sources = files(
|
|||||||
'test_c_api.cpp',
|
'test_c_api.cpp',
|
||||||
'test_base64.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_exe = executable('unit_tests',
|
||||||
test_sources,
|
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)
|
test('unit_tests', test_exe)
|
||||||
|
|||||||
91
tests/test_image.cpp
Normal file
91
tests/test_image.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
//
|
||||||
|
// Created by vptyp on 12.03.2026.
|
||||||
|
//
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <cloud_point/imageFactory.h>
|
||||||
|
|
||||||
|
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<cv::Vec3b>(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<cv::Vec4b>(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<double *>(rpc.data.data());
|
||||||
|
for (int i = 0; i < 50; ++i)
|
||||||
|
dataPtr[i] = static_cast<double>(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<double>(0, 0), 0.0);
|
||||||
|
EXPECT_DOUBLE_EQ(mat.at<double>(4, 9), 49.0);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user