45 Commits
v1.0 ... v1.2

Author SHA1 Message Date
6109ff388a update README.md 2024-09-19 22:47:25 +08:00
98acd6efbd misc 2024-09-19 15:50:57 +08:00
92b09a51c7 misc 2024-09-19 11:28:20 +08:00
7f5f665af2 misc 2024-09-19 11:20:33 +08:00
44cf2a0526 fix integral 2024-09-19 10:32:26 +08:00
df8267b60d simd integral image 2024-09-18 22:31:51 +08:00
01a20a18f1 static check 2024-09-18 19:53:27 +08:00
087ab9bc17 misc 2024-09-18 18:10:35 +08:00
d7bf2ad384 no copy 2024-09-18 15:45:03 +08:00
2c20dc2006 add openmp support 2024-09-18 11:21:33 +08:00
57ae2c8704 update doc 2024-09-13 20:54:55 +08:00
8e7227f74b demo: match start top level 2024-09-13 20:39:50 +08:00
c72b709178 add LICENSE 2024-09-13 20:28:04 +08:00
b5523f8491 replace call cv::Mat::ptr 2024-09-13 17:55:58 +08:00
f43f78f45f add readme 2024-09-12 21:07:31 +08:00
15372e0b60 add sample image 2024-09-12 21:06:50 +08:00
3d315445ab remove unused 2024-09-12 19:58:50 +08:00
d03818a63c special optim for arm 2024-09-12 08:36:55 +08:00
ed14c0e6f4 product hand write 2024-09-11 22:15:47 +08:00
fa20b52b85 serialize to file 2024-09-11 17:53:31 +08:00
7a88dad291 misc 2024-09-11 17:52:38 +08:00
dd5de6f6f1 misc 2024-09-11 14:30:31 +08:00
8050b8612a 🎈 perf(match): less reduce sum 2024-09-11 10:21:56 +08:00
3a689e900f revert 2024-09-06 21:19:58 +08:00
b8918b123d static check 2024-09-06 14:35:08 +08:00
1c67371cc9 fix type 2024-09-05 13:57:41 +08:00
422c12d691 impl shift 2024-09-05 11:21:55 +08:00
bece4f138f misc 2024-09-04 18:14:09 +08:00
75098c811c remove unused 2024-09-04 14:57:09 +08:00
a52deadc30 misc 2024-09-04 09:15:30 +08:00
ef1556c538 build config 2024-09-04 09:12:58 +08:00
92160ee1d9 misc 2024-09-04 09:06:42 +08:00
29fb7b001c static check 2024-09-03 22:53:25 +08:00
d85d4f7443 misc 2024-09-03 18:05:13 +08:00
13cbf06f46 aligned model at training 2024-09-03 17:03:42 +08:00
a5c8049af1 aligned model at deserialization 2024-09-03 15:27:48 +08:00
371b926fb0 cleanr name 2024-09-02 17:57:10 +08:00
3409395778 misc 2024-09-01 17:54:02 +08:00
36392b3e34 format check 2024-08-31 18:21:00 +08:00
726a55725a fix doc 2024-08-30 22:00:56 +08:00
338c1dcf96 static check 2024-08-30 21:53:42 +08:00
6dd78fbe21 support rect roi 2024-08-30 20:57:12 +08:00
665559d8ca export api 2024-08-29 15:20:48 +08:00
8758d7801d misc 2024-08-27 10:36:22 +08:00
fc4f04b9be add loongarch simd(lsx) 2024-08-25 20:59:55 +08:00
16 changed files with 949 additions and 160 deletions

View File

@ -2,16 +2,56 @@ cmake_minimum_required(VERSION 3.12)
project(match)
find_package(OpenCV REQUIRED)
add_executable(${PROJECT_NAME}
main.cpp
option(ENABLE_OPENMP "enable openmp" OFF)
if(ENABLE_OPENMP)
# find OpenMP
find_package(OpenMP REQUIRED)
if(OPENMP_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif(OPENMP_FOUND)
endif(ENABLE_OPENMP)
#==============================================================
#library
#==============================================================
add_library(algo SHARED
grayMatch.h
grayMatch.cpp
serialize.cpp
privateType.h
apiExport.h
)
target_include_directories(algo PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(algo ${OpenCV_LIBRARIES} $<$<BOOL:${OPENMP_FOUND}>:OpenMP::OpenMP_CXX>)
target_compile_options(algo PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX /external:W0>
$<$<STREQUAL:${CMAKE_SYSTEM_NAME},Linux>: -fPIC -fvisibility=hidden -Wl,--exclude-libs,ALL -Wall -Wextra -Wpedantic -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion -Werror>
$<$<AND:$<CXX_COMPILER_ID:Clang>,$<STREQUAL:${CMAKE_SYSTEM_NAME},Windows>>:/W4 /WX /external:W0>
$<$<STREQUAL:${CMAKE_SYSTEM_PROCESSOR},loongarch64>:-mlsx>
)
target_compile_definitions(algo PUBLIC API_EXPORTS
$<$<STREQUAL:${CMAKE_SYSTEM_PROCESSOR},loongarch64>:CV_LSX>
)
message("Arch:${CMAKE_SYSTEM_PROCESSOR}")
#==============================================================
#exe
#==============================================================
add_executable(${PROJECT_NAME}
main.cpp
)
target_include_directories(${PROJECT_NAME} PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBRARIES})
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBRARIES} algo)
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX /external:W0>
$<$<STREQUAL:${CMAKE_SYSTEM_NAME},Linux>:-fPIC -fvisibility=hidden -Wall -Wextra -Wpedantic -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion -Werror>
$<$<STREQUAL:${CMAKE_SYSTEM_NAME},Linux>: -fPIC -fvisibility=hidden -Wall -Wextra -Wpedantic -Wmisleading-indentation -Wunused -Wuninitialized -Wshadow -Wconversion -Werror>
$<$<AND:$<CXX_COMPILER_ID:Clang>,$<STREQUAL:${CMAKE_SYSTEM_NAME},Windows>>:/W4 /WX /external:W0>
)
target_compile_definitions(${PROJECT_NAME} PRIVATE IMG_DIR="${CMAKE_CURRENT_SOURCE_DIR}/img")

24
LICENSE Normal file
View File

@ -0,0 +1,24 @@
BSD 2-Clause License
Copyright (c) 2024, SurfaceMan
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

17
README.md Normal file
View File

@ -0,0 +1,17 @@
# Template match with gray model(ncc)
## highlights:
1. original code based [Fastest_Image_Pattern_Matching](https://github.com/DennisLiu1993/Fastest_Image_Pattern_Matching), you can checkout tag [v1.0](https://github.com/SurfaceMan/gray_match/releases/tag/v1.0) for more details.
2. refactor simd match process with opencv [Universal intrinsics](https://docs.opencv.org/4.x/df/d91/group__core__hal__intrin.html), have be tested on x86_64(sse),arm(neon),LoongArch(lsx).
3. support model save/load as binary file
4. provide pure c interface
5. support openmp
## usage:
all you need can be found in [main.cpp](main.cpp)
## gallery:
![sample](img/result.png)

31
apiExport.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#if defined(_WIN32) || defined(_WIN64) || defined(__WINDOWS__)
#define API_EXPORT __declspec(dllexport)
#define API_IMPORT __declspec(dllimport)
#define API_LOCAL
#elif defined(linux) || defined(__linux) || defined(__linux__)
#define API_EXPORT __attribute__((visibility("default")))
#define API_IMPORT __attribute__((visibility("default")))
#define API_LOCAL __attribute__((visibility("hidden")))
#elif defined(__APPLE__)
#define API_EXPORT __attribute__((visibility("default")))
#define API_IMPORT __attribute__((visibility("default")))
#define API_LOCAL __attribute__((visibility("hidden")))
#else
#define API_EXPORT
#define API_IMPORT
#define API_LOCAL
#endif
#ifdef __cplusplus
#define API_DEMANGLED extern "C"
#else
#define API_DEMANGLED
#endif
#ifdef API_EXPORTS
#define API_PUBLIC API_DEMANGLED API_EXPORT
#else
#define API_PUBLIC API_DEMANGLED API_IMPORT
#endif

View File

@ -1,6 +1,8 @@
#include "grayMatch.h"
#include "privateType.h"
#include <opencv2/core/hal/intrin.hpp>
#include <opencv2/opencv.hpp>
#include <utility>
constexpr int MIN_AREA = 256;
@ -8,37 +10,6 @@ constexpr double TOLERANCE = 0.0000001;
constexpr int CANDIDATE = 5;
constexpr double INVALID = -1.;
struct Model {
std::vector<cv::Mat> pyramids;
std::vector<cv::Scalar> mean;
std::vector<double> normal;
std::vector<double> invArea;
std::vector<bool> equal1;
uchar borderColor = 0;
void clear() {
pyramids.clear();
normal.clear();
invArea.clear();
mean.clear();
equal1.clear();
}
void resize(const std::size_t size) {
normal.resize(size);
invArea.resize(size);
mean.resize(size);
equal1.resize(size);
}
void reserve(const std::size_t size) {
normal.reserve(size);
invArea.reserve(size);
mean.reserve(size);
equal1.reserve(size);
}
};
struct BlockMax {
struct Block {
cv::Rect rect;
@ -170,6 +141,15 @@ int computeLayers(const int width, const int height, const int minArea) {
return layer;
}
inline cv::Point2d transform(const cv::Point2d &point, const cv::Mat &rotate) {
const auto ptr = rotate.ptr<double>();
auto x = point.x * ptr[ 0 ] + point.y * ptr[ 1 ] + ptr[ 2 ];
auto y = point.x * ptr[ 3 ] + point.y * ptr[ 4 ] + ptr[ 5 ];
return {x, y};
}
cv::Size computeRotationSize(const cv::Size &dstSize, const cv::Size &templateSize, double angle,
const cv::Mat &rotate) {
if (angle > 360) {
@ -186,34 +166,20 @@ cv::Size computeRotationSize(const cv::Size &dstSize, const cv::Size &templateSi
return dstSize;
}
const std::vector<cv::Point2d> points{
{0, 0},
{static_cast<double>(dstSize.width) - 1, 0},
{0, static_cast<double>(dstSize.height) - 1},
{static_cast<double>(dstSize.width) - 1, static_cast<double>(dstSize.height) - 1}};
std::vector<cv::Point2d> trans;
cv::transform(points, trans, rotate);
const std::vector<cv::Point2d> pt{
transform({0, 0}, rotate), transform({static_cast<double>(dstSize.width) - 1, 0}, rotate),
transform({0, static_cast<double>(dstSize.height) - 1}, rotate),
transform({static_cast<double>(dstSize.width) - 1, static_cast<double>(dstSize.height) - 1},
rotate)};
cv::Point2d min = trans[ 0 ];
cv::Point2d max = trans[ 0 ];
for (const auto &point : trans) {
if (point.x < min.x) {
min.x = point.x;
}
if (point.y < min.y) {
min.y = point.y;
}
if (point.x > max.x) {
max.x = point.x;
}
if (point.y > max.y) {
max.y = point.y;
}
}
cv::Point2d min;
cv::Point2d max;
min.x = std::min(std::min(std::min(pt[ 0 ].x, pt[ 1 ].x), pt[ 2 ].x), pt[ 3 ].x);
min.y = std::min(std::min(std::min(pt[ 0 ].y, pt[ 1 ].y), pt[ 2 ].y), pt[ 3 ].y);
max.x = std::max(std::max(std::max(pt[ 0 ].x, pt[ 1 ].x), pt[ 2 ].x), pt[ 3 ].x);
max.y = std::max(std::max(std::max(pt[ 0 ].y, pt[ 1 ].y), pt[ 2 ].y), pt[ 3 ].y);
if (angle > 0 && angle < 90) {
;
} else if (angle > 90 && angle < 180) {
angle -= 90;
} else if (angle > 180 && angle < 270) {
@ -237,15 +203,217 @@ cv::Size computeRotationSize(const cv::Size &dstSize, const cv::Size &templateSi
(templateSize.width > size.width && templateSize.height < size.height) ||
templateSize.area() > size.area();
if (wrongSize) {
size = {static_cast<int>(max.x - min.x + 0.5), static_cast<int>(max.y - min.y + 0.5)};
size = {static_cast<int>(lround(max.x - min.x + 0.5)),
static_cast<int>(lround(max.y - min.y + 0.5))};
}
return size;
}
void coeffDenominator(const cv::Mat &src, const cv::Size &templateSize, cv::Mat &result,
const double mean, const double normal, const double invArea,
const bool equal1) {
#ifdef CV_SIMD
void matchTemplateSimd(const cv::Mat &src, const cv::Mat &templateImg, cv::Mat &result) {
result = cv::Mat(src.size() - templateImg.size() + cv::Size(1, 1), CV_32FC1);
auto *resultStart = result.ptr<float>();
const auto resultStep = result.step[ 0 ] / result.step[ 1 ];
const auto *srcStart = src.ptr<uchar>();
const auto srcStep = src.step[ 0 ];
const auto *temStart = templateImg.ptr<uchar>();
const auto temStep = templateImg.step[ 0 ];
for (int y = 0; y < result.rows; y++) {
auto *resultPtr = resultStart + resultStep * y;
for (int x = 0; x < result.cols; x++) {
auto vSum = cv::vx_setall_u32(0);
for (int templateRow = 0; templateRow < templateImg.rows; templateRow++) {
auto *srcPtr = srcStart + srcStep * (y + templateRow) + x;
auto *temPtr = temStart + temStep * templateRow;
for (int i = 0; i < templateImg.cols; i += cv::v_uint8::nlanes) {
auto vTem = cv::v_load_aligned(temPtr + i);
auto vSrc = cv::v_load(srcPtr + i);
#ifdef __aarch64__
cv::v_uint16 vDot1;
cv::v_uint16 vDot2;
cv::v_uint32 v1;
cv::v_uint32 v2;
cv::v_uint32 v3;
cv::v_uint32 v4;
cv::v_mul_expand(vTem, vSrc, vDot1, vDot2);
cv::v_expand(vDot1, v1, v2);
cv::v_expand(vDot2, v3, v4);
vSum += v1 + v2 + v3 + v4;
#else
vSum += cv::v_dotprod_expand(vSrc, vTem);
#endif
}
}
auto sum = cv::v_reduce_sum(vSum);
resultPtr[ x ] = static_cast<float>(sum);
}
}
}
inline void integralSum(const cv::v_uint16 &src, double *dst, double *prevDst, cv::v_uint32 &pre) {
auto sum = src + cv::v_rotate_left<1>(src);
sum += cv::v_rotate_left<2>(sum);
sum += cv::v_rotate_left<4>(sum);
cv::v_uint32 v1;
cv::v_uint32 v2;
cv::v_expand(sum, v1, v2);
v1 += pre;
v2 += pre;
pre = cv::v_setall_u32(cv::v_extract_n<cv::v_uint32::nlanes - 1>(v2));
cv::v_uint64 v3;
cv::v_uint64 v4;
cv::v_expand(v1, v3, v4);
cv::v_store(dst, cv::v_cvt_f64(cv::v_reinterpret_as_s64(v3)) + cv::v_load(prevDst));
cv::v_store(dst + cv::v_float64::nlanes, cv::v_cvt_f64(cv::v_reinterpret_as_s64(v4)) +
cv::v_load(prevDst + cv::v_float64::nlanes));
cv::v_expand(v2, v3, v4);
cv::v_store(dst + cv::v_float64::nlanes * 2,
cv::v_cvt_f64(cv::v_reinterpret_as_s64(v3)) +
cv::v_load(prevDst + cv::v_float64::nlanes * 2));
cv::v_store(dst + cv::v_float64::nlanes * 3,
cv::v_cvt_f64(cv::v_reinterpret_as_s64(v4)) +
cv::v_load(prevDst + cv::v_float64::nlanes * 3));
}
inline void integralSqSum(cv::v_uint16 &src, double *dst, double *prevDst, cv::v_uint32 &pre) {
cv::v_uint32 v1;
cv::v_uint32 v2;
cv::v_expand(src, v1, v2);
{
auto shift1 = cv::v_rotate_left<1>(src);
cv::v_uint32 v3;
cv::v_uint32 v4;
cv::v_expand(shift1, v3, v4);
v1 += v3;
v2 += v4;
v4 = cv::v_extract<2>(v1, v2);
v2 += v4;
v3 = cv::v_rotate_left<2>(v1);
v1 += v3;
v1 += pre;
v2 += v1;
pre = cv::v_setall_u32(cv::v_extract_n<cv::v_uint32::nlanes - 1>(v2));
}
cv::v_uint64 v3;
cv::v_uint64 v4;
cv::v_expand(v1, v3, v4);
cv::v_store(dst, cv::v_cvt_f64(cv::v_reinterpret_as_s64(v3)) + cv::v_load(prevDst));
cv::v_store(dst + cv::v_float64::nlanes, cv::v_cvt_f64(cv::v_reinterpret_as_s64(v4)) +
cv::v_load(prevDst + cv::v_float64::nlanes));
cv::v_expand(v2, v3, v4);
cv::v_store(dst + cv::v_float64::nlanes * 2,
cv::v_cvt_f64(cv::v_reinterpret_as_s64(v3)) +
cv::v_load(prevDst + cv::v_float64::nlanes * 2));
cv::v_store(dst + cv::v_float64::nlanes * 3,
cv::v_cvt_f64(cv::v_reinterpret_as_s64(v4)) +
cv::v_load(prevDst + cv::v_float64::nlanes * 3));
}
/*
inline void integralSqSum(cv::v_uint32 &src, double *dst, double *prevDst, cv::v_uint32 &pre) {
src += cv::v_rotate_left<1>(src);
src += cv::v_rotate_left<2>(src);
src += pre;
pre = cv::v_setall_u32(cv::v_extract_n<cv::v_uint32::nlanes - 1>(src));
cv::v_uint64 v1;
cv::v_uint64 v2;
cv::v_expand(src, v1, v2);
cv::v_store(dst, cv::v_cvt_f64(cv::v_reinterpret_as_s64(v1)) + cv::v_load(prevDst));
cv::v_store(dst + cv::v_float64::nlanes, cv::v_cvt_f64(cv::v_reinterpret_as_s64(v2)) +
cv::v_load(prevDst + cv::v_float64::nlanes));
}
inline void integralSqSum(cv::v_uint16 &src, double *dst, double *prevDst, cv::v_uint32 &pre) {
cv::v_uint32 v1;
cv::v_uint32 v2;
cv::v_expand(src, v1, v2);
integralSqSum(v1, dst, prevDst, pre);
integralSqSum(v2, dst + cv::v_uint32::nlanes, prevDst + cv::v_uint32::nlanes, pre);
}
*/
inline void integralSum(cv::v_uint16 &v1, cv::v_uint16 &v2, double *dst, double *prevDst,
cv::v_uint32 &pre) {
integralSum(v1, dst, prevDst, pre);
integralSum(v2, dst + cv::v_uint16::nlanes, prevDst + cv::v_uint16::nlanes, pre);
}
inline void integralSqSum(cv::v_uint16 &v1, cv::v_uint16 &v2, double *dst, double *prevDst,
cv::v_uint32 &pre) {
v1 = cv::v_mul_wrap(v1, v1);
v2 = cv::v_mul_wrap(v2, v2);
integralSqSum(v1, dst, prevDst, pre);
integralSqSum(v2, dst + cv::v_uint16::nlanes, prevDst + cv::v_uint16::nlanes, pre);
}
void integralSimd(const cv::Mat &src, cv::Mat &sum, cv::Mat &sqSum) {
const auto size = src.size() + cv::Size(1, 1);
sum.create(size, CV_64FC1);
sqSum.create(size, CV_64FC1);
memset(sum.data, 0, sum.rows * sum.step[ 0 ]);
memset(sqSum.data, 0, sqSum.rows * sqSum.step[ 0 ]);
const auto *srcStart = src.ptr<uchar>();
const auto srcStep = src.step[ 0 ];
auto *sumStart = sum.ptr<double>(1) + 1;
const auto sumStep = sum.step[ 0 ] / sum.step[ 1 ];
auto *sqSumStart = sqSum.ptr<double>(1) + 1;
const auto sqSumStep = sqSum.step[ 0 ] / sqSum.step[ 1 ];
for (int y = 0; y < src.rows; y++) {
auto *srcPtr = srcStart + srcStep * y;
auto *sumPtr = sumStart + sumStep * y;
auto *sqSumPtr = sqSumStart + sqSumStep * y;
auto *preSumPtr = sumStart + sumStep * (y - 1);
auto *preSqSumPtr = sqSumStart + sqSumStep * (y - 1);
cv::v_uint32 prevSum = cv::vx_setzero_u32();
cv::v_uint32 prevSqSum = cv::vx_setzero_u32();
int x = 0;
for (; x < src.cols - cv::v_uint8::nlanes; x += cv::v_uint8::nlanes) {
cv::v_uint16 v1;
cv::v_uint16 v2;
cv::v_expand(cv::v_load(srcPtr + x), v1, v2);
integralSum(v1, v2, sumPtr + x, preSumPtr + x, prevSum);
integralSqSum(v1, v2, sqSumPtr + x, preSqSumPtr + x, prevSqSum);
}
auto prevSum2 = cv::v_extract_n<cv::v_uint32::nlanes - 1>(prevSum);
auto prevSqSum2 = cv::v_extract_n<cv::v_uint32::nlanes - 1>(prevSqSum);
for (; x < src.cols; x++) {
auto val = srcPtr[ x ];
auto sqVal = val * val;
prevSum2 += val;
sumPtr[ x ] = prevSum2 + preSumPtr[ x ];
prevSqSum2 += sqVal;
sqSumPtr[ x ] = prevSqSum2 + preSqSumPtr[ x ];
}
}
}
#endif
void ccoeffDenominator(const cv::Mat &src, const cv::Size &templateSize, cv::Mat &result,
const double mean, const double normal, const double invArea,
const bool equal1) {
if (equal1) {
result = cv::Scalar::all(1);
return;
@ -253,7 +421,11 @@ void coeffDenominator(const cv::Mat &src, const cv::Size &templateSize, cv::Mat
cv::Mat sum;
cv::Mat sqSum;
#ifdef CV_SIMD
integralSimd(src, sum, sqSum);
#else
cv::integral(src, sum, sqSum, CV_64F);
#endif
const auto *q0 = sqSum.ptr<double>(0);
const auto *q1 = q0 + templateSize.width;
@ -273,23 +445,19 @@ void coeffDenominator(const cv::Mat &src, const cv::Size &templateSize, cv::Mat
for (int x = 0; x < result.cols; x++, idx++) {
auto &score = scorePtr[ x ];
const auto partSum = p0[ idx ] - p1[ idx ] - p2[ idx ] + p3[ idx ];
auto partMean = partSum * partSum;
const auto num = score - partSum * mean;
partMean *= invArea;
const auto numerator = score - partSum * mean;
auto partSum2 = q0[ idx ] - q1[ idx ] - q2[ idx ] + q3[ idx ];
auto partSqSum = q0[ idx ] - q1[ idx ] - q2[ idx ] + q3[ idx ];
auto partSqNormal = partSqSum - partSum * partSum * invArea;
const auto diff = std::max(partSum2 - partMean, 0.);
if (diff <= std::min(0.5, 10 * FLT_EPSILON * partSum2)) {
partSum2 = 0;
} else {
partSum2 = sqrt(diff) * normal;
}
const auto diff = std::max(partSqNormal, 0.);
double denominator =
(diff <= std::min(0.5, 10 * FLT_EPSILON * partSqSum)) ? 0 : sqrt(diff) * normal;
if (abs(num) < partSum2) {
score = static_cast<float>(num / partSum2);
} else if (abs(num) < partSum2 * 1.125) {
score = num > 0.f ? 1.f : -1.f;
if (abs(numerator) < denominator) {
score = static_cast<float>(numerator / denominator);
} else if (abs(numerator) < denominator * 1.125) {
score = numerator > 0.f ? 1.f : -1.f;
} else {
score = 0;
}
@ -297,48 +465,14 @@ void coeffDenominator(const cv::Mat &src, const cv::Size &templateSize, cv::Mat
}
}
#ifdef CV_SIMD
float convSimd(const uchar *kernel, const uchar *src, const int kernelWidth) {
const auto blockSize = cv::VTraits<cv::v_uint8>::vlanes();
auto vSum = cv::vx_setall_u32(0);
int i = 0;
for (; i < kernelWidth - blockSize; i += blockSize) {
vSum += cv::v_dotprod_expand(cv::v_load(kernel + i), cv::v_load(src + i));
}
auto sum = cv::v_reduce_sum(vSum);
for (; i < kernelWidth; i++) {
sum += kernel[ i ] * src[ i ];
}
return static_cast<float>(sum);
}
void matchTemplateSimd(const cv::Mat &src, const cv::Mat &templateImg, cv::Mat &result) {
result = cv::Mat::zeros(src.size() - templateImg.size() + cv::Size(1, 1), CV_32FC1);
for (int y = 0; y < result.rows; y++) {
auto *resultPtr = result.ptr<float>(y);
for (int x = 0; x < result.cols; x++) {
auto &score = resultPtr[ x ];
for (int templateRow = 0; templateRow < templateImg.rows; templateRow++) {
auto *srcPtr = src.ptr<uchar>(y + templateRow) + x;
auto *temPtr = templateImg.ptr<uchar>(templateRow);
score += convSimd(temPtr, srcPtr, templateImg.cols);
}
}
}
}
#endif
void matchTemplate(cv::Mat &src, cv::Mat &result, const Model *model, int level) {
#ifdef CV_SIMD
matchTemplateSimd(src, model->pyramids[ level ], result);
#else
cv::matchTemplate(src, model->pyramids[ level ], result, cv::TM_CCORR);
#endif
coeffDenominator(src, model->pyramids[ level ].size(), result, model->mean[ level ][ 0 ],
model->normal[ level ], model->invArea[ level ], model->equal1[ level ]);
ccoeffDenominator(src, model->pyramids[ level ].size(), result, model->mean[ level ][ 0 ],
model->normal[ level ], model->invArea[ level ], model->equal1[ level ] == 1);
}
void nextMaxLoc(const cv::Point &pos, const cv::Size templateSize, const double maxOverlap,
@ -372,17 +506,25 @@ void nextMaxLoc(cv::Mat &score, const cv::Point &pos, const cv::Size templateSiz
cv::minMaxLoc(score, nullptr, &maxScore, nullptr, &maxPos);
}
inline cv::Point2d transform(const cv::Point2d &point, const cv::Mat &rotate) {
const auto ptr = rotate.ptr<double>();
inline cv::Mat getRotationMatrix2D(const cv::Point2f &center, double angle) {
angle *= CV_PI / 180;
double alpha = std::cos(angle);
double beta = std::sin(angle);
auto x = point.x * ptr[ 0 ] + point.y * ptr[ 1 ] + ptr[ 2 ];
auto y = point.x * ptr[ 3 ] + point.y * ptr[ 4 ] + ptr[ 5 ];
cv::Mat rotate(2, 3, CV_64FC1);
auto ptr = rotate.ptr<double>();
ptr[ 0 ] = alpha;
ptr[ 1 ] = beta;
ptr[ 2 ] = (1 - alpha) * center.x - beta * center.y;
ptr[ 3 ] = -beta;
ptr[ 4 ] = alpha;
ptr[ 5 ] = beta * center.x + (1 - alpha) * center.y;
return {x, y};
return rotate;
}
inline cv::Point2d transform(const cv::Point2d &point, const cv::Point &center, double angle) {
const auto rotate = cv::getRotationMatrix2D(center, angle, 1.);
const auto rotate = getRotationMatrix2D(center, angle);
return transform(point, rotate);
}
@ -392,14 +534,13 @@ inline cv::Point2d sizeCenter(const cv::Size &size) {
}
void cropRotatedRoi(const cv::Mat &src, const cv::Size &templateSize, const cv::Point2d topLeft,
const cv::Mat &rotate, cv::Mat &roi) {
cv::Mat &rotate, cv::Mat &roi) {
const auto point = transform(topLeft, rotate);
const cv::Size paddingSize(templateSize.width + 6, templateSize.height + 6);
auto rt = rotate;
rt.at<double>(0, 2) -= point.x - 3;
rt.at<double>(1, 2) -= point.y - 3;
rotate.at<double>(0, 2) -= point.x - 3;
rotate.at<double>(1, 2) -= point.y - 3;
cv::warpAffine(src, roi, rt, paddingSize);
cv::warpAffine(src, roi, rotate, paddingSize);
}
void filterOverlap(std::vector<Candidate> &candidates, const std::vector<cv::RotatedRect> &rects,
@ -436,8 +577,9 @@ void filterOverlap(std::vector<Candidate> &candidates, const std::vector<cv::Rot
continue;
}
const auto area = cv::contourArea(points);
const auto overlap = area / rect.size.area();
const auto area = cv::contourArea(points);
const auto overlap =
area / static_cast<double>(rect.size.width * rect.size.height);
if (overlap > maxOverlap) {
(candidate.score > refCandidate.score ? refCandidate.score
: candidate.score) = INVALID;
@ -454,8 +596,8 @@ Model *trainModel(const cv::Mat &src, int level) {
return nullptr;
}
if (level < 0) {
// level must greater than 1
if (level <= 0) {
// level must greater than 0
level = computeLayers(src.size().width, src.size().height, MIN_AREA);
}
@ -468,11 +610,24 @@ Model *trainModel(const cv::Mat &src, int level) {
auto *result = new Model;
Model &model = *result;
cv::buildPyramid(src, model.pyramids, level);
model.borderColor = cv::mean(src).val[ 0 ] < 128 ? 255 : 0;
model.reserve(model.pyramids.size());
for (const auto &pyramid : model.pyramids) {
std::vector<cv::Mat> pyramids;
cv::buildPyramid(src, pyramids, level);
model.borderColor = cv::mean(src).val[ 0 ] < 128 ? 255 : 0;
model.reserve(pyramids.size());
for (const auto &pyramid : pyramids) {
int alignedWidth = static_cast<int>(cv::alignSize(pyramid.cols, cv::v_uint8::nlanes));
auto img = cv::Mat::zeros(pyramid.rows, alignedWidth, CV_8UC1);
cv::Mat sub = img(cv::Rect(0, 0, pyramid.cols, pyramid.rows));
for (int y = 0; y < pyramid.rows; y++) {
auto *dstPtr = sub.ptr<uchar>(y);
auto *srcPtr = pyramid.ptr<uchar>(y);
memcpy(dstPtr, srcPtr, pyramid.cols);
}
model.pyramids.push_back(sub);
auto invArea = 1. / pyramid.size().area();
cv::Scalar mean;
@ -497,6 +652,9 @@ inline double sizeAngleStep(const cv::Size &size) {
return atan(2. / std::max(size.width, size.height)) * 180. / CV_PI;
}
#pragma omp declare reduction(combine : std::vector<Candidate> : omp_out.insert( \
omp_out.end(), omp_in.begin(), omp_in.end()))
std::vector<Candidate> matchTopLevel(const cv::Mat &dstTop, double startAngle, double spanAngle,
double maxOverlap, double minScore, int maxCount,
const Model *model, int level) {
@ -509,9 +667,10 @@ std::vector<Candidate> matchTopLevel(const cv::Mat &dstTop, double startAngle, d
bool calMaxByBlock = (dstTop.size().area() / templateTop.size().area() > 500) && maxCount > 10;
const auto count = static_cast<int>(spanAngle / angleStep) + 1;
#pragma omp parallel for reduction(combine : candidates)
for (int i = 0; i < count; i++) {
const auto angle = startAngle + angleStep * i;
auto rotate = cv::getRotationMatrix2D(center, angle, 1.);
auto rotate = getRotationMatrix2D(center, angle);
auto size = computeRotationSize(dstTop.size(), templateTop.size(), angle, rotate);
auto tx = (size.width - 1) / 2. - center.x;
@ -573,9 +732,11 @@ std::vector<Candidate> matchDownLevel(const std::vector<cv::Mat> &pyramids,
const std::vector<Candidate> &candidates, double minScore,
int subpixel, const Model *model, int level) {
std::vector<Candidate> levelMatched;
auto count = static_cast<int>(candidates.size());
for (const auto &candidate : candidates) {
auto pose = candidate;
#pragma omp parallel for reduction(combine : levelMatched)
for (int index = 0; index < count; index++) {
auto pose = candidates[ index ];
bool matched = true;
for (int currentLevel = level - 1; currentLevel >= 0; currentLevel--) {
const auto &currentTemplateImg = model->pyramids[ currentLevel ];
@ -597,7 +758,7 @@ std::vector<Candidate> matchDownLevel(const std::vector<cv::Mat> &pyramids,
cv::Mat newScoreRect;
for (int i = -1; i <= 1; i++) {
auto angle = pose.angle + i * currentAngleStep;
auto rotate = cv::getRotationMatrix2D(center, angle, 1.);
auto rotate = getRotationMatrix2D(center, angle);
cv::Mat roi;
cropRotatedRoi(currentDstImg, tmpSize, topLeft, rotate, roi);
@ -700,7 +861,7 @@ std::vector<Pose> matchModel(const cv::Mat &dst, const Model *model, int level,
for (const auto &candidate : levelMatched) {
std::vector<cv::Point2f> points{topRight + cv::Point2f(candidate.pos),
bottomRight + cv::Point2f(candidate.pos)};
auto rotate = cv::getRotationMatrix2D(candidate.pos, -candidate.angle, 1.);
auto rotate = getRotationMatrix2D(candidate.pos, -candidate.angle);
std::vector<cv::Point2f> rotatedPoints;
cv::transform(points, rotatedPoints, rotate);
@ -731,3 +892,126 @@ std::vector<Pose> matchModel(const cv::Mat &dst, const Model *model, int level,
return result;
}
Model_t trainModel(const unsigned char *data, int width, int height, int channels, int bytesPerLine,
int roiLeft, int roiTop, int roiWidth, int roiHeight, int levelNum) {
if ((1 != channels && 3 != channels && 4 != channels) || nullptr == data) {
return nullptr;
}
auto type = channels == 1 ? CV_8UC1 : (channels == 3 ? CV_8UC3 : CV_8UC4);
cv::Mat img(cv::Size(width, height), type, const_cast<unsigned char *>(data), bytesPerLine);
cv::Mat src;
if (1 == channels) {
src = img;
} else {
cv::cvtColor(img, src, channels == 3 ? cv::COLOR_RGB2GRAY : cv::COLOR_RGBA2GRAY);
}
cv::Rect rect(roiLeft, roiTop, roiWidth, roiHeight);
cv::Rect imageRect(0, 0, width, height);
auto roi = rect & imageRect;
if (roi.empty()) {
return nullptr;
}
return trainModel(src(roi), levelNum);
}
void matchModel(const unsigned char *data, int width, int height, int channels, int bytesPerLine,
int roiLeft, int roiTop, int roiWidth, int roiHeight, const Model_t model,
int *count, Pose *poses, int level, double startAngle, double spanAngle,
double maxOverlap, double minScore, int subpixel) {
if (nullptr == count) {
return;
}
if (nullptr == poses || nullptr == data) {
*count = 0;
return;
}
if (1 != channels && 3 != channels && 4 != channels) {
*count = 0;
return;
}
auto type = channels == 1 ? CV_8UC1 : (channels == 3 ? CV_8UC3 : CV_8UC4);
cv::Mat img(cv::Size(width, height), type, const_cast<unsigned char *>(data), bytesPerLine);
cv::Mat dst;
if (1 == channels) {
dst = img;
} else {
cv::cvtColor(img, dst, channels == 3 ? cv::COLOR_RGB2GRAY : cv::COLOR_RGBA2GRAY);
}
cv::Rect rect(roiLeft, roiTop, roiWidth, roiHeight);
cv::Rect imageRect(0, 0, width, height);
auto roi = rect & imageRect;
if (roi.empty()) {
*count = 0;
return;
}
auto result = matchModel(dst(roi), model, level, startAngle, spanAngle, maxOverlap, minScore,
*count, subpixel);
auto size = std::min(*count, static_cast<int>(result.size()));
for (int i = 0; i < size; i++) {
const auto &pose = result[ i ];
poses[ i ] = {pose.x + static_cast<float>(roi.x), pose.y + static_cast<float>(roi.y),
pose.angle, pose.score};
}
*count = size;
}
void freeModel(Model_t *model) {
if (nullptr == model || nullptr == *model) {
return;
}
delete *model;
*model = nullptr;
}
int modelLevel(const Model *model) {
if (nullptr == model) {
return 0;
}
return static_cast<int>(model->pyramids.size());
}
void modelImage(const Model *model, int level, unsigned char *data, int length, int *width,
int *height, int *channels) {
if (nullptr == model) {
return;
}
if (level < 0 || level > static_cast<int>(model->pyramids.size() - 1)) {
return;
}
const auto &img = model->pyramids[ level ];
if (nullptr != width) {
*width = img.cols;
}
if (nullptr != height) {
*height = img.rows;
}
if (nullptr != channels) {
*channels = img.channels();
}
if (nullptr == data || length < img.cols * img.rows * img.channels()) {
return;
}
for (int y = 0; y < img.rows; y++) {
const auto *ptr = img.ptr<unsigned char>(y);
memcpy(data + y * img.cols, ptr, img.cols);
}
}

View File

@ -1,10 +1,12 @@
#ifndef GRAY_MATCH_H
#define GRAY_MATCH_H
#include <opencv2/opencv.hpp>
#include "apiExport.h"
struct Model;
using Model_t = Model *;
struct Pose {
float x;
float y;
@ -12,14 +14,94 @@ struct Pose {
float score;
};
Model *trainModel(const cv::Mat &src, int level);
/**
* @brief train match model
* @param data image data
* @param width image width
* @param height image height
* @param channels image channels 1(gray)/3(rgb)/4(rgba)
* @param bytesPerLine bytes per line
* @param roiLeft rectangle roi left
* @param roiTop rectangle roi top
* @param roiWidth rectangle roi width
* @param roiHeight rectangle roi height
* @param levelNum pyramid levels (> 0:user setting,-1:auto)
* @return
*/
API_PUBLIC Model_t trainModel(const unsigned char *data, int width, int height, int channels,
int bytesPerLine, int roiLeft, int roiTop, int roiWidth,
int roiHeight, int levelNum);
/**
* @brief match model
* @param data image data
* @param width image width
* @param height image height
* @param channels image channels 1(gray)/3(rgb)/4(rgba)
* @param bytesPerLine bytes per line
* @param roiLeft rectangle roi left
* @param roiTop rectangle roi top
* @param roiWidth rectangle roi width
* @param roiHeight rectangle roi height
* @param model trained model
* @param count in(max detect count)/out(found count)
* @param poses pose array inited with size not less than count
* @param level match start at which level (level>=0 && level<modelLevel-1,-1:auto)
* @param startAngle rotation start angle
* @param spanAngle rotation angle range
* @param maxOverlap overlap threshold
* @param minScore minimum matched score
* @param subpixel compute subpixel result
* @return
*/
API_PUBLIC void matchModel(const unsigned char *data, int width, int height, int channels,
int bytesPerLine, int roiLeft, int roiTop, int roiWidth, int roiHeight,
const Model_t model, int *count, Pose *poses, int level,
double startAngle, double spanAngle, double maxOverlap, double minScore,
int subpixel);
std::vector<Pose> matchModel(const cv::Mat &dst, const Model *model, int level, double startAngle,
double spanAngle, double maxOverlap, double minScore, int maxCount,
int subpixel);
/**
* @brief get trained model levels
* @param model
* @return pyramid level
*/
API_PUBLIC int modelLevel(const Model_t model);
void serialize(Model *model, int &size, uint8_t *buffer);
/**
* @brief get trained model image
* @param model
* @param level pyramid level index(level>=0 && level<modelLevel-1)
* @param data image data buffer(need allocated), can input nullptr to query width/height/channels
* @param length buffer length not less than width*height*channels
* @param width image width, can input nullptr
* @param height image height, can input nullptr
* @param channels image channels, can input nullptr
* @return
*/
API_PUBLIC void modelImage(const Model_t model, int level, unsigned char *data, int length,
int *width, int *height, int *channels);
Model *deserialize(int size, uint8_t *buffer);
/**
* @brief free model
* @param model
* @return
*/
API_PUBLIC void freeModel(Model_t *model);
#endif // GRAY_MATCH_H
/**
* @brief serialize model to buffer
* @param model
* @param buffer need allocated, can input nullptr to query size
* @param size in(buffer size)/out(written size)
* @return true(success)false(failed)
*/
API_PUBLIC bool serialize(const Model_t model, unsigned char *buffer, int *size);
/**
* @brief deserialize model
* @param buffer
* @param size buffer size
* @return model
*/
API_PUBLIC Model_t deserialize(unsigned char *buffer, int size);
#endif // GRAY_MATCH_H

BIN
img/3.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
img/h.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 KiB

BIN
img/i.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 KiB

BIN
img/j.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 KiB

BIN
img/k.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 KiB

BIN
img/l.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 932 KiB

BIN
img/result.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

View File

@ -1,30 +1,78 @@
#include "grayMatch.h"
#include <fstream>
#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
auto src =
cv::imread("C:/Users/qiuyong/Desktop/test/template/model3.bmp", cv::IMREAD_GRAYSCALE);
auto dst =
cv::imread("C:/Users/qiuyong/Desktop/test/template/model3_src1.bmp", cv::IMREAD_GRAYSCALE);
auto src = cv::imread(std::string(IMG_DIR) + "/3.bmp", cv::IMREAD_GRAYSCALE);
auto dst = cv::imread(std::string(IMG_DIR) + "/h.bmp", cv::IMREAD_GRAYSCALE);
auto t0 = cv::getTickCount();
auto model = trainModel(src, -1);
auto t1 = cv::getTickCount();
auto poses = matchModel(dst, model, -1, 0, 360, 0, 0.5, 70, 1);
auto t2 = cv::getTickCount();
const std::string modelName("model.bin");
{
auto t0 = cv::getTickCount();
auto model = trainModel(src.data, src.cols, src.rows, src.channels(), int(src.step), 0, 0,
src.cols, src.rows, -1);
auto t1 = cv::getTickCount();
auto trainCost = double(t1 - t0) / cv::getTickFrequency();
auto matchCost = double(t2 - t1) / cv::getTickFrequency();
std::cout << "train(s):" << trainCost << " match(s):" << matchCost << std::endl;
// get size
int size;
serialize(model, nullptr, &size);
// serialize to buffer
std::vector<uchar> buffer(size);
serialize(model, buffer.data(), &size);
// write to file
std::ofstream ofs(modelName, std::ios::binary | std::ios::out);
if (!ofs.is_open()) {
return -1;
}
ofs.write((const char *)buffer.data(), size);
freeModel(&model);
auto trainCost = double(t1 - t0) / cv::getTickFrequency();
std::cout << "train(s):" << trainCost << std::endl;
}
int num = 70;
std::vector<Pose> poses(num);
{
// open file
std::ifstream ifs(modelName, std::ios::binary | std::ios::in);
if (!ifs.is_open()) {
return -2;
}
// get size
ifs.seekg(0, std::ios::end);
auto size = ifs.tellg();
ifs.seekg(0, std::ios::beg);
// read to buffer
std::vector<uchar> buffer(size);
ifs.read((char *)buffer.data(), size);
// deserialize from buffer
auto model = deserialize(buffer.data(), int(buffer.size()));
auto t2 = cv::getTickCount();
matchModel(dst.data, dst.cols, dst.rows, dst.channels(), int(dst.step), 0, 0, dst.cols,
dst.rows, model, &num, poses.data(), -1, 0, 360, 0, 0.5, 1);
auto t3 = cv::getTickCount();
auto matchCost = double(t3 - t2) / cv::getTickFrequency();
std::cout << "match(s):" << matchCost << std::endl;
}
cv::Mat color;
cv::cvtColor(dst, color, cv::COLOR_GRAY2RGB);
for (auto &pose : poses) {
for (int i = 0; i < num; i++) {
auto &pose = poses[ i ];
cv::RotatedRect rect(cv::Point2f(pose.x, pose.y), src.size(), -pose.angle);
std::vector<cv::Point2f> pts;
cv::Point2f pts[ 4 ];
rect.points(pts);
cv::line(color, pts[ 0 ], pts[ 1 ], cv::Scalar(255, 0, 0), 1, cv::LINE_AA);

35
privateType.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include <opencv2/core.hpp>
struct Model {
std::vector<cv::Mat> pyramids;
std::vector<cv::Scalar> mean;
std::vector<double> normal;
std::vector<double> invArea;
std::vector<uchar> equal1;
uchar borderColor = 0;
void clear() {
pyramids.clear();
normal.clear();
invArea.clear();
mean.clear();
equal1.clear();
}
void resize(const std::size_t size) {
normal.resize(size);
invArea.resize(size);
mean.resize(size);
equal1.resize(size);
}
void reserve(const std::size_t size) {
pyramids.reserve(size);
normal.reserve(size);
invArea.reserve(size);
mean.reserve(size);
equal1.reserve(size);
}
};

228
serialize.cpp Normal file
View File

@ -0,0 +1,228 @@
#include "grayMatch.h"
#include "privateType.h"
#include <opencv2/core/hal/intrin.hpp>
class Buffer {
public:
Buffer(int size_, unsigned char *data_)
: m_size(size_)
, m_data(data_) {}
virtual ~Buffer() = default;
virtual void operator&(uchar &val) = 0;
virtual void operator&(std::vector<cv::Mat> &val) = 0;
virtual void operator&(std::vector<cv::Scalar> &val) = 0;
virtual void operator&(std::vector<double> &val) = 0;
virtual void operator&(std::vector<uchar> &val) = 0;
void operator&(Model &val) {
this->operator&(val.pyramids);
this->operator&(val.mean);
this->operator&(val.normal);
this->operator&(val.invArea);
this->operator&(val.equal1);
this->operator&(val.borderColor);
}
[[nodiscard]] int count() const {
return m_size;
}
protected:
int m_size = 0;
unsigned char *m_data = nullptr;
};
void binWrite(void *dst, void *src, int size) {
memcpy(dst, src, size);
}
void fakeWrite(void *dst, void *src, int size) {
(void)(dst);
(void)(src);
(void)(size);
}
using Write = void (*)(void *, void *, int);
template <Write write> class OutBuffer : public Buffer {
public:
explicit OutBuffer(unsigned char *data_)
: Buffer(0, data_) {}
void operator&(uchar &val) final {
write(m_data + m_size, &val, sizeof(val));
m_size += static_cast<int>(sizeof(val));
}
void operator&(std::vector<cv::Mat> &val) final {
int size = static_cast<int>(val.size());
write(m_data + m_size, &size, sizeof(size));
m_size += static_cast<int>(sizeof(size));
for (auto &element : val) {
writeElement(element);
}
}
void writeElement(cv::Mat &val) {
write(m_data + m_size, &val.cols, sizeof(int));
m_size += static_cast<int>(sizeof(int));
write(m_data + m_size, &val.rows, sizeof(int));
m_size += static_cast<int>(sizeof(int));
for (int i = 0; i < val.rows; i++) {
write(m_data + m_size, val.ptr<unsigned char>(i), val.cols);
m_size += val.cols;
}
}
void operator&(std::vector<cv::Scalar> &val) final {
int size = static_cast<int>(val.size());
write(m_data + m_size, &size, sizeof(size));
m_size += static_cast<int>(sizeof(size));
for (auto &element : val) {
writeElement(element);
}
}
void writeElement(cv::Scalar &val) {
write(m_data + m_size, val.val, sizeof(double) * 4);
m_size += static_cast<int>(sizeof(double)) * 4;
}
void operator&(std::vector<double> &val) final {
int size = static_cast<int>(val.size());
write(m_data + m_size, &size, sizeof(size));
m_size += static_cast<int>(sizeof(size));
write(m_data + m_size, val.data(), static_cast<int>(sizeof(double)) * size);
m_size += static_cast<int>(sizeof(double)) * size;
}
void operator&(std::vector<uchar> &val) final {
int size = static_cast<int>(val.size());
write(m_data + m_size, &size, sizeof(size));
m_size += static_cast<int>(sizeof(size));
write(m_data + m_size, val.data(), sizeof(uchar) * size);
m_size += static_cast<int>(sizeof(uchar)) * size;
}
};
using SizeCountBuffer = OutBuffer<fakeWrite>;
using WriteBuffer = OutBuffer<binWrite>;
class ReadBuffer : public Buffer {
public:
explicit ReadBuffer(unsigned char *data_)
: Buffer(0, data_) {}
void operator&(uchar &val) final {
memcpy(&val, m_data + m_size, sizeof(uchar));
m_size += static_cast<int>(sizeof(uchar));
}
void operator&(std::vector<cv::Mat> &val) final {
int count = 0;
memcpy(&count, m_data + m_size, sizeof(int));
val.resize(count);
m_size += static_cast<int>(sizeof(count));
for (auto &element : val) {
read(element);
}
}
void read(cv::Mat &val) {
int width = 0;
memcpy(&width, m_data + m_size, sizeof(int));
m_size += static_cast<int>(sizeof(int));
int height = 0;
memcpy(&height, m_data + m_size, sizeof(int));
m_size += static_cast<int>(sizeof(int));
int alignedWidth = static_cast<int>(cv::alignSize(width, cv::v_uint8::nlanes));
auto img = cv::Mat::zeros(height, alignedWidth, CV_8UC1);
val = img(cv::Rect(0, 0, width, height));
for (int y = 0; y < height; y++) {
auto *ptr = val.ptr<uchar>(y);
memcpy(ptr, m_data + m_size, width);
m_size += width;
}
}
void operator&(std::vector<cv::Scalar> &val) final {
int count = 0;
memcpy(&count, m_data + m_size, sizeof(int));
val.resize(count);
m_size += static_cast<int>(sizeof(count));
for (auto &element : val) {
read(element);
}
}
void read(cv::Scalar &val) {
memcpy(val.val, m_data + m_size, sizeof(double) * 4);
m_size += static_cast<int>(sizeof(double)) * 4;
}
void operator&(std::vector<double> &val) final {
int count = 0;
memcpy(&count, m_data + m_size, sizeof(int));
val.resize(count);
m_size += static_cast<int>(sizeof(count));
memcpy(val.data(), m_data + m_size, sizeof(double) * count);
m_size += static_cast<int>(sizeof(double)) * count;
}
void operator&(std::vector<uchar> &val) final {
int count = 0;
memcpy(&count, m_data + m_size, sizeof(int));
val.resize(count);
m_size += static_cast<int>(sizeof(count));
memcpy(val.data(), m_data + m_size, sizeof(bool) * count);
m_size += static_cast<int>(sizeof(uchar)) * count;
}
};
void operation(Buffer *buf, Model &model) {
(*buf) & (model);
}
bool serialize(const Model_t model, unsigned char *buffer, int *size) {
if (nullptr == size) {
return false;
}
if (nullptr == model) {
*size = 0;
return false;
}
SizeCountBuffer counter(buffer);
operation(&counter, *model);
*size = counter.count();
if (nullptr == buffer) {
return true;
}
if (counter.count() > *size) {
*size = 0;
return false;
}
WriteBuffer writer(buffer);
operation(&writer, *model);
return true;
}
Model_t deserialize(unsigned char *buffer, int size) {
if (size < 1 || nullptr == buffer) {
return nullptr;
}
ReadBuffer reader(buffer);
auto *model = new Model;
operation(&reader, *model);
return model;
}