mirror of
https://github.com/opencv/opencv.git
synced 2025-06-07 17:44:04 +08:00
Merge branch 4.x
This commit is contained in:
commit
cea26341a5
1
3rdparty/openjpeg/CMakeLists.txt
vendored
1
3rdparty/openjpeg/CMakeLists.txt
vendored
@ -15,6 +15,7 @@ ocv_warnings_disable(CMAKE_C_FLAGS
|
|||||||
-Wimplicit-const-int-float-conversion # clang
|
-Wimplicit-const-int-float-conversion # clang
|
||||||
-Wunused-but-set-variable # clang15
|
-Wunused-but-set-variable # clang15
|
||||||
-Wmissing-prototypes # clang, function opj_t1_ht_decode_cblk
|
-Wmissing-prototypes # clang, function opj_t1_ht_decode_cblk
|
||||||
|
-Wmissing-declarations # gcc, function opj_t1_ht_decode_cblk
|
||||||
)
|
)
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
|
@ -241,6 +241,8 @@ OCV_OPTION(WITH_1394 "Include IEEE1394 support" ON
|
|||||||
OCV_OPTION(WITH_AVFOUNDATION "Use AVFoundation for Video I/O (iOS/Mac)" ON
|
OCV_OPTION(WITH_AVFOUNDATION "Use AVFoundation for Video I/O (iOS/Mac)" ON
|
||||||
VISIBLE_IF APPLE
|
VISIBLE_IF APPLE
|
||||||
VERIFY HAVE_AVFOUNDATION)
|
VERIFY HAVE_AVFOUNDATION)
|
||||||
|
OCV_OPTION(WITH_AVIF "Enable AVIF support" OFF
|
||||||
|
VERIFY HAVE_AVIF)
|
||||||
OCV_OPTION(WITH_CAP_IOS "Enable iOS video capture" ON
|
OCV_OPTION(WITH_CAP_IOS "Enable iOS video capture" ON
|
||||||
VISIBLE_IF IOS
|
VISIBLE_IF IOS
|
||||||
VERIFY HAVE_CAP_IOS)
|
VERIFY HAVE_CAP_IOS)
|
||||||
@ -1390,6 +1392,14 @@ if(WITH_WEBP OR HAVE_WEBP)
|
|||||||
status(" WEBP:" WEBP_FOUND THEN "${WEBP_LIBRARY} (ver ${WEBP_VERSION})" ELSE "build (ver ${WEBP_VERSION})")
|
status(" WEBP:" WEBP_FOUND THEN "${WEBP_LIBRARY} (ver ${WEBP_VERSION})" ELSE "build (ver ${WEBP_VERSION})")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(WITH_AVIF OR HAVE_AVIF)
|
||||||
|
if(AVIF_VERSION)
|
||||||
|
status(" AVIF:" AVIF_FOUND THEN "${AVIF_LIBRARY} (ver ${AVIF_VERSION})" ELSE "NO")
|
||||||
|
else()
|
||||||
|
status(" AVIF:" AVIF_FOUND THEN "${AVIF_LIBRARY}" ELSE "NO")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WITH_PNG OR HAVE_PNG OR WITH_SPNG)
|
if(WITH_PNG OR HAVE_PNG OR WITH_SPNG)
|
||||||
if(WITH_SPNG)
|
if(WITH_SPNG)
|
||||||
status(" PNG:" "build-${SPNG_LIBRARY} (ver ${SPNG_VERSION})")
|
status(" PNG:" "build-${SPNG_LIBRARY} (ver ${SPNG_VERSION})")
|
||||||
|
46
cmake/OpenCVFindAVIF.cmake
Normal file
46
cmake/OpenCVFindAVIF.cmake
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#=============================================================================
|
||||||
|
# Find AVIF library
|
||||||
|
#=============================================================================
|
||||||
|
# Find the native AVIF headers and libraries.
|
||||||
|
#
|
||||||
|
# AVIF_INCLUDE_DIRS - where to find avif/avif.h, etc.
|
||||||
|
# AVIF_LIBRARIES - List of libraries when using AVIF.
|
||||||
|
# AVIF_FOUND - True if AVIF is found.
|
||||||
|
#=============================================================================
|
||||||
|
|
||||||
|
# Look for the header file.
|
||||||
|
|
||||||
|
unset(AVIF_FOUND)
|
||||||
|
|
||||||
|
find_package(libavif QUIET)
|
||||||
|
|
||||||
|
if(TARGET avif)
|
||||||
|
MARK_AS_ADVANCED(AVIF_INCLUDE_DIR)
|
||||||
|
MARK_AS_ADVANCED(AVIF_LIBRARY)
|
||||||
|
|
||||||
|
SET(AVIF_FOUND TRUE)
|
||||||
|
GET_TARGET_PROPERTY(AVIF_LIBRARY avif LOCATION)
|
||||||
|
GET_TARGET_PROPERTY(AVIF_INCLUDE_DIR1 avif INCLUDE_DIRECTORIES)
|
||||||
|
GET_TARGET_PROPERTY(AVIF_INCLUDE_DIR2 avif INTERFACE_INCLUDE_DIRECTORIES)
|
||||||
|
set(AVIF_INCLUDE_DIR)
|
||||||
|
if(AVIF_INCLUDE_DIR1)
|
||||||
|
LIST(APPEND AVIF_INCLUDE_DIR ${AVIF_INCLUDE_DIR1})
|
||||||
|
endif()
|
||||||
|
if(AVIF_INCLUDE_DIR2)
|
||||||
|
LIST(APPEND AVIF_INCLUDE_DIR ${AVIF_INCLUDE_DIR2})
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
FIND_PATH(AVIF_INCLUDE_DIR NAMES avif/avif.h)
|
||||||
|
|
||||||
|
# Look for the library.
|
||||||
|
FIND_LIBRARY(AVIF_LIBRARY NAMES avif)
|
||||||
|
MARK_AS_ADVANCED(AVIF_LIBRARY)
|
||||||
|
|
||||||
|
# handle the QUIETLY and REQUIRED arguments and set AVIF_FOUND to TRUE if
|
||||||
|
# all listed variables are TRUE
|
||||||
|
INCLUDE(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(AVIF DEFAULT_MSG AVIF_LIBRARY AVIF_INCLUDE_DIR)
|
||||||
|
|
||||||
|
SET(AVIF_LIBRARIES ${AVIF_LIBRARY})
|
||||||
|
SET(AVIF_INCLUDE_DIRS ${AVIF_INCLUDE_DIR})
|
||||||
|
endif()
|
@ -37,6 +37,16 @@ if(NOT ZLIB_FOUND)
|
|||||||
ocv_parse_header2(ZLIB "${${ZLIB_LIBRARY}_SOURCE_DIR}/zlib.h" ZLIB_VERSION)
|
ocv_parse_header2(ZLIB "${${ZLIB_LIBRARY}_SOURCE_DIR}/zlib.h" ZLIB_VERSION)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# --- libavif (optional) ---
|
||||||
|
|
||||||
|
if(WITH_AVIF)
|
||||||
|
ocv_clear_internal_cache_vars(AVIF_LIBRARY AVIF_INCLUDE_DIR)
|
||||||
|
include(cmake/OpenCVFindAVIF.cmake)
|
||||||
|
if(AVIF_FOUND)
|
||||||
|
set(HAVE_AVIF 1)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# --- libjpeg (optional) ---
|
# --- libjpeg (optional) ---
|
||||||
if(WITH_JPEG)
|
if(WITH_JPEG)
|
||||||
if(BUILD_JPEG)
|
if(BUILD_JPEG)
|
||||||
|
@ -72,6 +72,9 @@
|
|||||||
#cmakedefine HAVE_OPENJPEG
|
#cmakedefine HAVE_OPENJPEG
|
||||||
#cmakedefine HAVE_JASPER
|
#cmakedefine HAVE_JASPER
|
||||||
|
|
||||||
|
/* AVIF codec */
|
||||||
|
#cmakedefine HAVE_AVIF
|
||||||
|
|
||||||
/* IJG JPEG codec */
|
/* IJG JPEG codec */
|
||||||
#cmakedefine HAVE_JPEG
|
#cmakedefine HAVE_JPEG
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ EOF
|
|||||||
# Parse options
|
# Parse options
|
||||||
|
|
||||||
COLOR_OUTPUT=0
|
COLOR_OUTPUT=0
|
||||||
while getopts “hc” OPTION
|
while getopts "hc" OPTION
|
||||||
do
|
do
|
||||||
case $OPTION in
|
case $OPTION in
|
||||||
h)
|
h)
|
||||||
|
@ -133,7 +133,7 @@ nonmaxSuppression:
|
|||||||
Additional Resources
|
Additional Resources
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
-# Edward Rosten and Tom Drummond, “Machine learning for high speed corner detection” in 9th
|
-# Edward Rosten and Tom Drummond, "Machine learning for high speed corner detection" in 9th
|
||||||
European Conference on Computer Vision, vol. 1, 2006, pp. 430–443.
|
European Conference on Computer Vision, vol. 1, 2006, pp. 430–443.
|
||||||
2. Edward Rosten, Reid Porter, and Tom Drummond, "Faster and better: a machine learning approach to
|
2. Edward Rosten, Reid Porter, and Tom Drummond, "Faster and better: a machine learning approach to
|
||||||
corner detection" in IEEE Trans. Pattern Analysis and Machine Intelligence, 2010, vol 32, pp.
|
corner detection" in IEEE Trans. Pattern Analysis and Machine Intelligence, 2010, vol 32, pp.
|
||||||
|
76
doc/tutorials/others/barcode_detect_and_decode.markdown
Normal file
76
doc/tutorials/others/barcode_detect_and_decode.markdown
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
Barcode Recognition {#tutorial_barcode_detect_and_decode}
|
||||||
|
===================
|
||||||
|
|
||||||
|
@tableofcontents
|
||||||
|
|
||||||
|
@prev_tutorial{tutorial_traincascade}
|
||||||
|
@next_tutorial{tutorial_introduction_to_svm}
|
||||||
|
|
||||||
|
| | |
|
||||||
|
| -: | :- |
|
||||||
|
| Compatibility | OpenCV >= 4.8 |
|
||||||
|
|
||||||
|
Goal
|
||||||
|
----
|
||||||
|
|
||||||
|
In this chapter we will familiarize with the barcode detection and decoding methods available in OpenCV.
|
||||||
|
|
||||||
|
Basics
|
||||||
|
----
|
||||||
|
|
||||||
|
Barcode is major technique to identify commodity in real life. A common barcode is a pattern of parallel lines arranged by black bars and white bars with vastly different reflectivity. Barcode recognition is to scan the barcode in the horizontal direction to get a string of binary codes composed of bars of different widths and colors, that is, the code information of the barcode. The content of barcode can be decoded by matching with various barcode encoding methods. Currently, we support EAN-8, EAN-13, UPC-A and UPC-E standards.
|
||||||
|
|
||||||
|
See https://en.wikipedia.org/wiki/Universal_Product_Code and https://en.wikipedia.org/wiki/International_Article_Number
|
||||||
|
|
||||||
|
Related papers: @cite Xiangmin2015research , @cite kass1987analyzing , @cite bazen2002systematic
|
||||||
|
|
||||||
|
Code example
|
||||||
|
------------
|
||||||
|
|
||||||
|
### Main class
|
||||||
|
Several algorithms were introduced for barcode recognition.
|
||||||
|
|
||||||
|
While coding, we firstly need to create a cv::barcode::BarcodeDetector object. It has mainly three member functions, which will be introduced in the following.
|
||||||
|
|
||||||
|
#### Initialization
|
||||||
|
|
||||||
|
Optionally user can construct barcode detector with super resolution model which should be downloaded from https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode (`sr.caffemodel`, `sr.prototxt`).
|
||||||
|
|
||||||
|
@snippet cpp/barcode.cpp initialize
|
||||||
|
|
||||||
|
We need to create variables to store the outputs.
|
||||||
|
|
||||||
|
@snippet cpp/barcode.cpp output
|
||||||
|
|
||||||
|
#### Detecting
|
||||||
|
|
||||||
|
cv::barcode::BarcodeDetector::detect method uses an algorithm based on directional coherence. First, we compute the average squared gradients of every pixel, @cite bazen2002systematic . Then we divide an image into square patches and compute the **gradient orientation coherence** and **mean gradient direction** of each patch. Then, we connect all patches that have **high gradient orientation coherence** and **similar gradient direction**. At this stage we use multiscale patches to capture the gradient distribution of multi-size barcodes, and apply non-maximum suppression to filter duplicate proposals. At last, we use cv::minAreaRect to bound the ROI, and output the corners of the rectangles.
|
||||||
|
|
||||||
|
Detect codes in the input image, and output the corners of detected rectangles:
|
||||||
|
|
||||||
|
@snippet cpp/barcode.cpp detect
|
||||||
|
|
||||||
|
#### Decoding
|
||||||
|
|
||||||
|
cv::barcode::BarcodeDetector::decode method first super-scales the image (_optionally_) if it is smaller than threshold, sharpens the image and then binaries it by OTSU or local binarization. Then it reads the contents of the barcode by matching the similarity of the specified barcode pattern.
|
||||||
|
|
||||||
|
#### Detecting and decoding
|
||||||
|
|
||||||
|
cv::barcode::BarcodeDetector::detectAndDecode combines `detect` and `decode` in a single call. A simple example below shows how to use this function:
|
||||||
|
|
||||||
|
@snippet cpp/barcode.cpp detectAndDecode
|
||||||
|
|
||||||
|
Visualize the results:
|
||||||
|
|
||||||
|
@snippet cpp/barcode.cpp visualize
|
||||||
|
|
||||||
|
Results
|
||||||
|
-------
|
||||||
|
|
||||||
|
Original image:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
After detection:
|
||||||
|
|
||||||
|

|
BIN
doc/tutorials/others/images/barcode_book.jpg
Normal file
BIN
doc/tutorials/others/images/barcode_book.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
doc/tutorials/others/images/barcode_book_res.jpg
Normal file
BIN
doc/tutorials/others/images/barcode_book_res.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
@ -3,7 +3,7 @@ Introduction to Support Vector Machines {#tutorial_introduction_to_svm}
|
|||||||
|
|
||||||
@tableofcontents
|
@tableofcontents
|
||||||
|
|
||||||
@prev_tutorial{tutorial_traincascade}
|
@prev_tutorial{tutorial_barcode_detect_and_decode}
|
||||||
@next_tutorial{tutorial_non_linear_svms}
|
@next_tutorial{tutorial_non_linear_svms}
|
||||||
|
|
||||||
| | |
|
| | |
|
||||||
|
@ -8,6 +8,7 @@ Other tutorials (ml, objdetect, photo, stitching, video) {#tutorial_table_of_con
|
|||||||
- video. @subpage tutorial_optical_flow
|
- video. @subpage tutorial_optical_flow
|
||||||
- objdetect. @subpage tutorial_cascade_classifier
|
- objdetect. @subpage tutorial_cascade_classifier
|
||||||
- objdetect. @subpage tutorial_traincascade
|
- objdetect. @subpage tutorial_traincascade
|
||||||
|
- objdetect. @subpage tutorial_barcode_detect_and_decode
|
||||||
- ml. @subpage tutorial_introduction_to_svm
|
- ml. @subpage tutorial_introduction_to_svm
|
||||||
- ml. @subpage tutorial_non_linear_svms
|
- ml. @subpage tutorial_non_linear_svms
|
||||||
- ml. @subpage tutorial_introduction_to_pca
|
- ml. @subpage tutorial_introduction_to_pca
|
||||||
|
@ -4,7 +4,7 @@ Cascade Classifier Training {#tutorial_traincascade}
|
|||||||
@tableofcontents
|
@tableofcontents
|
||||||
|
|
||||||
@prev_tutorial{tutorial_cascade_classifier}
|
@prev_tutorial{tutorial_cascade_classifier}
|
||||||
@next_tutorial{tutorial_introduction_to_svm}
|
@next_tutorial{tutorial_barcode_detect_and_decode}
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
|
@ -410,9 +410,9 @@ public:
|
|||||||
* 1. Check whether j-th data point is consistent with the
|
* 1. Check whether j-th data point is consistent with the
|
||||||
* model
|
* model
|
||||||
* 2. Compute the likelihood ratio λj eq. (1)
|
* 2. Compute the likelihood ratio λj eq. (1)
|
||||||
* 3. If λj > A, decide the model is ’bad’ (model ”re-jected”),
|
* 3. If λj > A, decide the model is ’bad’ (model "re-jected"),
|
||||||
* else increment j or continue testing
|
* else increment j or continue testing
|
||||||
* 4. If j = N the number of correspondences decide model ”accepted”
|
* 4. If j = N the number of correspondences decide model "accepted"
|
||||||
*
|
*
|
||||||
* Verifies model and returns model score.
|
* Verifies model and returns model score.
|
||||||
|
|
||||||
|
@ -420,7 +420,7 @@ void OdometryTest::prepareFrameCheck()
|
|||||||
odf.getPyramidAt(normi, OdometryFramePyramidType::PYR_NORM, i);
|
odf.getPyramidAt(normi, OdometryFramePyramidType::PYR_NORM, i);
|
||||||
ASSERT_FALSE(normi.empty());
|
ASSERT_FALSE(normi.empty());
|
||||||
double nnorm = cv::norm(normi, gtNormal, NORM_INF, normmaski);
|
double nnorm = cv::norm(normi, gtNormal, NORM_INF, normmaski);
|
||||||
EXPECT_LE(nnorm, 1.8e-7) << "Normals diff is too big at pyr level " << i;
|
EXPECT_LE(nnorm, 3.3e-7) << "Normals diff is too big at pyr level " << i;
|
||||||
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,7 @@ ocv_add_dispatched_file(arithm SSE2 SSE4_1 AVX2 VSX3)
|
|||||||
ocv_add_dispatched_file(convert SSE2 AVX2 VSX3)
|
ocv_add_dispatched_file(convert SSE2 AVX2 VSX3)
|
||||||
ocv_add_dispatched_file(convert_scale SSE2 AVX2)
|
ocv_add_dispatched_file(convert_scale SSE2 AVX2)
|
||||||
ocv_add_dispatched_file(count_non_zero SSE2 AVX2)
|
ocv_add_dispatched_file(count_non_zero SSE2 AVX2)
|
||||||
|
ocv_add_dispatched_file(has_non_zero SSE2 AVX2)
|
||||||
ocv_add_dispatched_file(matmul SSE2 SSE4_1 AVX2 AVX512_SKX NEON_DOTPROD)
|
ocv_add_dispatched_file(matmul SSE2 SSE4_1 AVX2 AVX512_SKX NEON_DOTPROD)
|
||||||
ocv_add_dispatched_file(mean SSE2 AVX2)
|
ocv_add_dispatched_file(mean SSE2 AVX2)
|
||||||
ocv_add_dispatched_file(merge SSE2 AVX2)
|
ocv_add_dispatched_file(merge SSE2 AVX2)
|
||||||
|
@ -572,6 +572,14 @@ independently for each channel.
|
|||||||
*/
|
*/
|
||||||
CV_EXPORTS_AS(sumElems) Scalar sum(InputArray src);
|
CV_EXPORTS_AS(sumElems) Scalar sum(InputArray src);
|
||||||
|
|
||||||
|
/** @brief Checks for the presence of at least one non-zero array element.
|
||||||
|
|
||||||
|
The function returns whether there are non-zero elements in src
|
||||||
|
@param src single-channel array.
|
||||||
|
@sa mean, meanStdDev, norm, minMaxLoc, calcCovarMatrix
|
||||||
|
*/
|
||||||
|
CV_EXPORTS_W bool hasNonZero( InputArray src );
|
||||||
|
|
||||||
/** @brief Counts non-zero array elements.
|
/** @brief Counts non-zero array elements.
|
||||||
|
|
||||||
The function returns the number of non-zero elements in src :
|
The function returns the number of non-zero elements in src :
|
||||||
|
@ -460,6 +460,30 @@ OCL_PERF_TEST_P(CountNonZeroFixture, CountNonZero,
|
|||||||
SANITY_CHECK(result);
|
SANITY_CHECK(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////// countNonZero ////////////////////////
|
||||||
|
|
||||||
|
typedef Size_MatType HasNonZeroFixture;
|
||||||
|
|
||||||
|
OCL_PERF_TEST_P(HasNonZeroFixture, HasNonZero,
|
||||||
|
::testing::Combine(OCL_TEST_SIZES,
|
||||||
|
OCL_PERF_ENUM(CV_8UC1, CV_32FC1)))
|
||||||
|
{
|
||||||
|
const Size_MatType_t params = GetParam();
|
||||||
|
const Size srcSize = get<0>(params);
|
||||||
|
const int type = get<1>(params);
|
||||||
|
|
||||||
|
checkDeviceMaxMemoryAllocSize(srcSize, type);
|
||||||
|
|
||||||
|
UMat src(srcSize, type);
|
||||||
|
/*bool result = false;*/
|
||||||
|
randu(src, 0, 10);
|
||||||
|
declare.in(src);
|
||||||
|
|
||||||
|
OCL_TEST_CYCLE() /*result =*/ cv::hasNonZero(src);
|
||||||
|
|
||||||
|
SANITY_CHECK_NOTHING();
|
||||||
|
}
|
||||||
|
|
||||||
///////////// Phase ////////////////////////
|
///////////// Phase ////////////////////////
|
||||||
|
|
||||||
typedef Size_MatType PhaseFixture;
|
typedef Size_MatType PhaseFixture;
|
||||||
|
@ -101,4 +101,20 @@ PERF_TEST_P(Size_MatType, countNonZero, testing::Combine( testing::Values( TYPIC
|
|||||||
SANITY_CHECK(cnt);
|
SANITY_CHECK(cnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PERF_TEST_P(Size_MatType, hasNonZero, testing::Combine( testing::Values( TYPICAL_MAT_SIZES ), testing::Values( CV_8UC1, CV_8SC1, CV_16UC1, CV_16SC1, CV_32SC1, CV_32FC1, CV_64FC1 ) ))
|
||||||
|
{
|
||||||
|
Size sz = get<0>(GetParam());
|
||||||
|
int matType = get<1>(GetParam());
|
||||||
|
|
||||||
|
Mat src(sz, matType);
|
||||||
|
/*bool hnz = false;*/
|
||||||
|
|
||||||
|
declare.in(src, WARMUP_RNG);
|
||||||
|
|
||||||
|
int runs = (sz.width <= 640) ? 8 : 1;
|
||||||
|
TEST_CYCLE_MULTIRUN(runs) /*hnz =*/ hasNonZero(src);
|
||||||
|
|
||||||
|
SANITY_CHECK_NOTHING();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
107
modules/core/src/has_non_zero.dispatch.cpp
Normal file
107
modules/core/src/has_non_zero.dispatch.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html
|
||||||
|
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
#include "opencl_kernels_core.hpp"
|
||||||
|
#include "stat.hpp"
|
||||||
|
|
||||||
|
#include "has_non_zero.simd.hpp"
|
||||||
|
#include "has_non_zero.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX2,...,BASELINE based on CMakeLists.txt content
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
|
||||||
|
static HasNonZeroFunc getHasNonZeroTab(int depth)
|
||||||
|
{
|
||||||
|
CV_INSTRUMENT_REGION();
|
||||||
|
CV_CPU_DISPATCH(getHasNonZeroTab, (depth),
|
||||||
|
CV_CPU_DISPATCH_MODES_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENCL
|
||||||
|
static bool ocl_hasNonZero( InputArray _src, bool & res )
|
||||||
|
{
|
||||||
|
int type = _src.type(), depth = CV_MAT_DEPTH(type), kercn = ocl::predictOptimalVectorWidth(_src);
|
||||||
|
bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0;
|
||||||
|
|
||||||
|
if (depth == CV_64F && !doubleSupport)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int dbsize = ocl::Device::getDefault().maxComputeUnits();
|
||||||
|
size_t wgs = ocl::Device::getDefault().maxWorkGroupSize();
|
||||||
|
|
||||||
|
int wgs2_aligned = 1;
|
||||||
|
while (wgs2_aligned < (int)wgs)
|
||||||
|
wgs2_aligned <<= 1;
|
||||||
|
wgs2_aligned >>= 1;
|
||||||
|
|
||||||
|
ocl::Kernel k("reduce", ocl::core::reduce_oclsrc,
|
||||||
|
format("-D srcT=%s -D srcT1=%s -D cn=1 -D OP_COUNT_NON_ZERO"
|
||||||
|
" -D WGS=%d -D kercn=%d -D WGS2_ALIGNED=%d%s%s",
|
||||||
|
ocl::typeToStr(CV_MAKE_TYPE(depth, kercn)),
|
||||||
|
ocl::typeToStr(depth), (int)wgs, kercn,
|
||||||
|
wgs2_aligned, doubleSupport ? " -D DOUBLE_SUPPORT" : "",
|
||||||
|
_src.isContinuous() ? " -D HAVE_SRC_CONT" : ""));
|
||||||
|
if (k.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
UMat src = _src.getUMat(), db(1, dbsize, CV_32SC1);
|
||||||
|
k.args(ocl::KernelArg::ReadOnlyNoSize(src), src.cols, (int)src.total(),
|
||||||
|
dbsize, ocl::KernelArg::PtrWriteOnly(db));
|
||||||
|
|
||||||
|
size_t globalsize = dbsize * wgs;
|
||||||
|
if (k.run(1, &globalsize, &wgs, true))
|
||||||
|
return res = (saturate_cast<int>(cv::sum(db.getMat(ACCESS_READ))[0])>0), true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool hasNonZero(InputArray _src)
|
||||||
|
{
|
||||||
|
CV_INSTRUMENT_REGION();
|
||||||
|
|
||||||
|
int type = _src.type(), cn = CV_MAT_CN(type);
|
||||||
|
CV_Assert( cn == 1 );
|
||||||
|
|
||||||
|
bool res = false;
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENCL
|
||||||
|
CV_OCL_RUN_(OCL_PERFORMANCE_CHECK(_src.isUMat()) && _src.dims() <= 2,
|
||||||
|
ocl_hasNonZero(_src, res),
|
||||||
|
res)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Mat src = _src.getMat();
|
||||||
|
|
||||||
|
HasNonZeroFunc func = getHasNonZeroTab(src.depth());
|
||||||
|
CV_Assert( func != 0 );
|
||||||
|
|
||||||
|
if (src.dims == 2)//fast path to avoid creating planes of single rows
|
||||||
|
{
|
||||||
|
if (src.isContinuous())
|
||||||
|
res |= func(src.ptr<uchar>(0), src.total());
|
||||||
|
else
|
||||||
|
for(int row = 0, rowsCount = src.rows ; !res && (row<rowsCount) ; ++row)
|
||||||
|
res |= func(src.ptr<uchar>(row), src.cols);
|
||||||
|
}
|
||||||
|
else//if (src.dims != 2)
|
||||||
|
{
|
||||||
|
const Mat* arrays[] = {&src, nullptr};
|
||||||
|
Mat planes[1];
|
||||||
|
NAryMatIterator itNAry(arrays, planes, 1);
|
||||||
|
for(size_t p = 0 ; !res && (p<itNAry.nplanes) ; ++p, ++itNAry)
|
||||||
|
{
|
||||||
|
const Mat& plane = itNAry.planes[0];
|
||||||
|
if (plane.isContinuous())
|
||||||
|
res |= func(plane.ptr<uchar>(0), plane.total());
|
||||||
|
else
|
||||||
|
for(int row = 0, rowsCount = plane.rows ; !res && (row<rowsCount) ; ++row)
|
||||||
|
res |= func(plane.ptr<uchar>(row), plane.cols);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
327
modules/core/src/has_non_zero.simd.hpp
Normal file
327
modules/core/src/has_non_zero.simd.hpp
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
|
||||||
|
typedef bool (*HasNonZeroFunc)(const uchar*, size_t);
|
||||||
|
|
||||||
|
|
||||||
|
CV_CPU_OPTIMIZATION_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
HasNonZeroFunc getHasNonZeroTab(int depth);
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline bool hasNonZero_(const T* src, size_t len )
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
size_t i=0;
|
||||||
|
#if CV_ENABLE_UNROLLED
|
||||||
|
for(; !res && (i+4 <= len); i += 4 )
|
||||||
|
res |= ((src[i] | src[i+1] | src[i+2] | src[i+3]) != 0);
|
||||||
|
#endif
|
||||||
|
for( ; !res && (i < len); i++ )
|
||||||
|
res |= (src[i] != 0);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline bool hasNonZero_(const float* src, size_t len )
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
size_t i=0;
|
||||||
|
if (sizeof(float) == sizeof(unsigned int))
|
||||||
|
{
|
||||||
|
#if CV_ENABLE_UNROLLED
|
||||||
|
typedef unsigned int float_as_uint_t;
|
||||||
|
const float_as_uint_t* src_as_ui = reinterpret_cast<const float_as_uint_t*>(src);
|
||||||
|
for(; !res && (i+4 <= len); i += 4 )
|
||||||
|
{
|
||||||
|
const float_as_uint_t gathered = (src_as_ui[i] | src_as_ui[i+1] | src_as_ui[i+2] | src_as_ui[i+3]);
|
||||||
|
res |= ((gathered<<1) != 0);//remove what would be the sign bit
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
for( ; !res && (i < len); i++ )
|
||||||
|
res |= (src[i] != 0);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline bool hasNonZero_(const double* src, size_t len )
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
size_t i=0;
|
||||||
|
if (sizeof(double) == sizeof(uint64_t))
|
||||||
|
{
|
||||||
|
#if CV_ENABLE_UNROLLED
|
||||||
|
typedef uint64_t double_as_uint_t;
|
||||||
|
const double_as_uint_t* src_as_ui = reinterpret_cast<const double_as_uint_t*>(src);
|
||||||
|
for(; !res && (i+4 <= len); i += 4 )
|
||||||
|
{
|
||||||
|
const double_as_uint_t gathered = (src_as_ui[i] | src_as_ui[i+1] | src_as_ui[i+2] | src_as_ui[i+3]);
|
||||||
|
res |= ((gathered<<1) != 0);//remove what would be the sign bit
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
for( ; !res && (i < len); i++ )
|
||||||
|
res |= (src[i] != 0);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasNonZero8u( const uchar* src, size_t len )
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
const uchar* srcEnd = src+len;
|
||||||
|
#if CV_SIMD
|
||||||
|
typedef v_uint8 v_type;
|
||||||
|
const v_type v_zero = vx_setzero_u8();
|
||||||
|
constexpr const int unrollCount = 2;
|
||||||
|
int step = v_type::nlanes * unrollCount;
|
||||||
|
int len0 = len & -step;
|
||||||
|
const uchar* srcSimdEnd = src+len0;
|
||||||
|
|
||||||
|
int countSIMD = static_cast<int>((srcSimdEnd-src)/step);
|
||||||
|
while(!res && countSIMD--)
|
||||||
|
{
|
||||||
|
v_type v0 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v1 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
res = v_check_any(((v0 | v1) != v_zero));
|
||||||
|
}
|
||||||
|
|
||||||
|
v_cleanup();
|
||||||
|
#endif
|
||||||
|
return res || hasNonZero_(src, srcEnd-src);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasNonZero16u( const ushort* src, size_t len )
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
const ushort* srcEnd = src+len;
|
||||||
|
#if CV_SIMD
|
||||||
|
typedef v_uint16 v_type;
|
||||||
|
const v_type v_zero = vx_setzero_u16();
|
||||||
|
constexpr const int unrollCount = 4;
|
||||||
|
int step = v_type::nlanes * unrollCount;
|
||||||
|
int len0 = len & -step;
|
||||||
|
const ushort* srcSimdEnd = src+len0;
|
||||||
|
|
||||||
|
int countSIMD = static_cast<int>((srcSimdEnd-src)/step);
|
||||||
|
while(!res && countSIMD--)
|
||||||
|
{
|
||||||
|
v_type v0 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v1 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v2 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v3 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v0 |= v1;
|
||||||
|
v2 |= v3;
|
||||||
|
res = v_check_any(((v0 | v2) != v_zero));
|
||||||
|
}
|
||||||
|
|
||||||
|
v_cleanup();
|
||||||
|
#endif
|
||||||
|
return res || hasNonZero_(src, srcEnd-src);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasNonZero32s( const int* src, size_t len )
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
const int* srcEnd = src+len;
|
||||||
|
#if CV_SIMD
|
||||||
|
typedef v_int32 v_type;
|
||||||
|
const v_type v_zero = vx_setzero_s32();
|
||||||
|
constexpr const int unrollCount = 8;
|
||||||
|
int step = v_type::nlanes * unrollCount;
|
||||||
|
int len0 = len & -step;
|
||||||
|
const int* srcSimdEnd = src+len0;
|
||||||
|
|
||||||
|
int countSIMD = static_cast<int>((srcSimdEnd-src)/step);
|
||||||
|
while(!res && countSIMD--)
|
||||||
|
{
|
||||||
|
v_type v0 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v1 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v2 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v3 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v4 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v5 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v6 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v7 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v0 |= v1;
|
||||||
|
v2 |= v3;
|
||||||
|
v4 |= v5;
|
||||||
|
v6 |= v7;
|
||||||
|
|
||||||
|
v0 |= v2;
|
||||||
|
v4 |= v6;
|
||||||
|
res = v_check_any(((v0 | v4) != v_zero));
|
||||||
|
}
|
||||||
|
|
||||||
|
v_cleanup();
|
||||||
|
#endif
|
||||||
|
return res || hasNonZero_(src, srcEnd-src);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasNonZero32f( const float* src, size_t len )
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
const float* srcEnd = src+len;
|
||||||
|
#if CV_SIMD
|
||||||
|
typedef v_float32 v_type;
|
||||||
|
const v_type v_zero = vx_setzero_f32();
|
||||||
|
constexpr const int unrollCount = 8;
|
||||||
|
int step = v_type::nlanes * unrollCount;
|
||||||
|
int len0 = len & -step;
|
||||||
|
const float* srcSimdEnd = src+len0;
|
||||||
|
|
||||||
|
int countSIMD = static_cast<int>((srcSimdEnd-src)/step);
|
||||||
|
while(!res && countSIMD--)
|
||||||
|
{
|
||||||
|
v_type v0 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v1 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v2 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v3 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v4 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v5 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v6 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v7 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v0 |= v1;
|
||||||
|
v2 |= v3;
|
||||||
|
v4 |= v5;
|
||||||
|
v6 |= v7;
|
||||||
|
|
||||||
|
v0 |= v2;
|
||||||
|
v4 |= v6;
|
||||||
|
//res = v_check_any(((v0 | v4) != v_zero));//beware : (NaN != 0) returns "false" since != is mapped to _CMP_NEQ_OQ and not _CMP_NEQ_UQ
|
||||||
|
res = !v_check_all(((v0 | v4) == v_zero));
|
||||||
|
}
|
||||||
|
|
||||||
|
v_cleanup();
|
||||||
|
#endif
|
||||||
|
return res || hasNonZero_(src, srcEnd-src);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool hasNonZero64f( const double* src, size_t len )
|
||||||
|
{
|
||||||
|
bool res = false;
|
||||||
|
const double* srcEnd = src+len;
|
||||||
|
#if CV_SIMD_64F
|
||||||
|
typedef v_float64 v_type;
|
||||||
|
const v_type v_zero = vx_setzero_f64();
|
||||||
|
constexpr const int unrollCount = 16;
|
||||||
|
int step = v_type::nlanes * unrollCount;
|
||||||
|
int len0 = len & -step;
|
||||||
|
const double* srcSimdEnd = src+len0;
|
||||||
|
|
||||||
|
int countSIMD = static_cast<int>((srcSimdEnd-src)/step);
|
||||||
|
while(!res && countSIMD--)
|
||||||
|
{
|
||||||
|
v_type v0 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v1 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v2 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v3 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v4 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v5 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v6 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v7 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v8 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v9 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v10 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v11 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v12 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v13 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v14 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v_type v15 = vx_load(src);
|
||||||
|
src += v_type::nlanes;
|
||||||
|
v0 |= v1;
|
||||||
|
v2 |= v3;
|
||||||
|
v4 |= v5;
|
||||||
|
v6 |= v7;
|
||||||
|
v8 |= v9;
|
||||||
|
v10 |= v11;
|
||||||
|
v12 |= v13;
|
||||||
|
v14 |= v15;
|
||||||
|
|
||||||
|
v0 |= v2;
|
||||||
|
v4 |= v6;
|
||||||
|
v8 |= v10;
|
||||||
|
v12 |= v14;
|
||||||
|
|
||||||
|
v0 |= v4;
|
||||||
|
v8 |= v12;
|
||||||
|
//res = v_check_any(((v0 | v8) != v_zero));//beware : (NaN != 0) returns "false" since != is mapped to _CMP_NEQ_OQ and not _CMP_NEQ_UQ
|
||||||
|
res = !v_check_all(((v0 | v8) == v_zero));
|
||||||
|
}
|
||||||
|
|
||||||
|
v_cleanup();
|
||||||
|
#endif
|
||||||
|
return res || hasNonZero_(src, srcEnd-src);
|
||||||
|
}
|
||||||
|
|
||||||
|
HasNonZeroFunc getHasNonZeroTab(int depth)
|
||||||
|
{
|
||||||
|
static HasNonZeroFunc hasNonZeroTab[] =
|
||||||
|
{
|
||||||
|
(HasNonZeroFunc)GET_OPTIMIZED(hasNonZero8u), (HasNonZeroFunc)GET_OPTIMIZED(hasNonZero8u),
|
||||||
|
(HasNonZeroFunc)GET_OPTIMIZED(hasNonZero16u), (HasNonZeroFunc)GET_OPTIMIZED(hasNonZero16u),
|
||||||
|
(HasNonZeroFunc)GET_OPTIMIZED(hasNonZero32s), (HasNonZeroFunc)GET_OPTIMIZED(hasNonZero32f),
|
||||||
|
(HasNonZeroFunc)GET_OPTIMIZED(hasNonZero64f), 0
|
||||||
|
};
|
||||||
|
|
||||||
|
return hasNonZeroTab[depth];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CV_CPU_OPTIMIZATION_NAMESPACE_END
|
||||||
|
} // namespace
|
201
modules/core/test/test_hasnonzero.cpp
Normal file
201
modules/core/test/test_hasnonzero.cpp
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||||
|
//
|
||||||
|
// By downloading, copying, installing or using the software you agree to this license.
|
||||||
|
// If you do not agree to this license, do not download, install,
|
||||||
|
// copy or use the software.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// License Agreement
|
||||||
|
// For Open Source Computer Vision Library
|
||||||
|
//
|
||||||
|
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||||
|
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
|
||||||
|
// Third party copyrights are property of their respective owners.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
// are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// * Redistribution's of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// * Redistribution's 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.
|
||||||
|
//
|
||||||
|
// * The name of the copyright holders may not be used to endorse or promote products
|
||||||
|
// derived from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// 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 Intel Corporation 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.
|
||||||
|
//
|
||||||
|
//M*/
|
||||||
|
|
||||||
|
#include "test_precomp.hpp"
|
||||||
|
|
||||||
|
namespace opencv_test { namespace {
|
||||||
|
|
||||||
|
typedef testing::TestWithParam<std::tuple<int, Size> > HasNonZeroAllZeros;
|
||||||
|
|
||||||
|
TEST_P(HasNonZeroAllZeros, hasNonZeroAllZeros)
|
||||||
|
{
|
||||||
|
const int type = std::get<0>(GetParam());
|
||||||
|
const Size size = std::get<1>(GetParam());
|
||||||
|
|
||||||
|
Mat m = Mat::zeros(size, type);
|
||||||
|
EXPECT_FALSE(hasNonZero(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core, HasNonZeroAllZeros,
|
||||||
|
testing::Combine(
|
||||||
|
testing::Values(CV_8UC1, CV_8SC1, CV_16UC1, CV_16SC1, CV_32SC1, CV_32FC1, CV_64FC1),
|
||||||
|
testing::Values(Size(1, 1), Size(320, 240), Size(127, 113), Size(1, 113))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef testing::TestWithParam<std::tuple<int, Size> > HasNonZeroNegZeros;
|
||||||
|
|
||||||
|
TEST_P(HasNonZeroNegZeros, hasNonZeroNegZeros)
|
||||||
|
{
|
||||||
|
const int type = std::get<0>(GetParam());
|
||||||
|
const Size size = std::get<1>(GetParam());
|
||||||
|
|
||||||
|
Mat m = Mat(size, type);
|
||||||
|
m.setTo(Scalar::all(-0.));
|
||||||
|
EXPECT_FALSE(hasNonZero(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core, HasNonZeroNegZeros,
|
||||||
|
testing::Combine(
|
||||||
|
testing::Values(CV_32FC1, CV_64FC1),
|
||||||
|
testing::Values(Size(1, 1), Size(320, 240), Size(127, 113), Size(1, 113))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef testing::TestWithParam<std::tuple<int, Size> > HasNonZeroLimitValues;
|
||||||
|
|
||||||
|
TEST_P(HasNonZeroLimitValues, hasNonZeroLimitValues)
|
||||||
|
{
|
||||||
|
const int type = std::get<0>(GetParam());
|
||||||
|
const Size size = std::get<1>(GetParam());
|
||||||
|
|
||||||
|
Mat m = Mat(size, type);
|
||||||
|
|
||||||
|
m.setTo(Scalar::all(std::numeric_limits<double>::infinity()));
|
||||||
|
EXPECT_TRUE(hasNonZero(m));
|
||||||
|
|
||||||
|
m.setTo(Scalar::all(-std::numeric_limits<double>::infinity()));
|
||||||
|
EXPECT_TRUE(hasNonZero(m));
|
||||||
|
|
||||||
|
m.setTo(Scalar::all(std::numeric_limits<double>::quiet_NaN()));
|
||||||
|
EXPECT_TRUE(hasNonZero(m));
|
||||||
|
|
||||||
|
m.setTo((CV_MAT_DEPTH(type) == CV_64F) ? Scalar::all(std::numeric_limits<double>::epsilon()) : Scalar::all(std::numeric_limits<float>::epsilon()));
|
||||||
|
EXPECT_TRUE(hasNonZero(m));
|
||||||
|
|
||||||
|
m.setTo((CV_MAT_DEPTH(type) == CV_64F) ? Scalar::all(std::numeric_limits<double>::min()) : Scalar::all(std::numeric_limits<float>::min()));
|
||||||
|
EXPECT_TRUE(hasNonZero(m));
|
||||||
|
|
||||||
|
m.setTo((CV_MAT_DEPTH(type) == CV_64F) ? Scalar::all(std::numeric_limits<double>::denorm_min()) : Scalar::all(std::numeric_limits<float>::denorm_min()));
|
||||||
|
EXPECT_TRUE(hasNonZero(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core, HasNonZeroLimitValues,
|
||||||
|
testing::Combine(
|
||||||
|
testing::Values(CV_32FC1, CV_64FC1),
|
||||||
|
testing::Values(Size(1, 1), Size(320, 240), Size(127, 113), Size(1, 113))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef testing::TestWithParam<std::tuple<int, Size> > HasNonZeroRandom;
|
||||||
|
|
||||||
|
TEST_P(HasNonZeroRandom, hasNonZeroRandom)
|
||||||
|
{
|
||||||
|
const int type = std::get<0>(GetParam());
|
||||||
|
const Size size = std::get<1>(GetParam());
|
||||||
|
|
||||||
|
RNG& rng = theRNG();
|
||||||
|
|
||||||
|
const size_t N = std::min(100, size.area());
|
||||||
|
for(size_t i = 0 ; i<N ; ++i)
|
||||||
|
{
|
||||||
|
const int nz_pos_x = rng.uniform(0, size.width);
|
||||||
|
const int nz_pos_y = rng.uniform(0, size.height);
|
||||||
|
Mat m = Mat::zeros(size, type);
|
||||||
|
Mat nzROI = Mat(m, Rect(nz_pos_x, nz_pos_y, 1, 1));
|
||||||
|
nzROI.setTo(Scalar::all(1));
|
||||||
|
EXPECT_TRUE(hasNonZero(m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core, HasNonZeroRandom,
|
||||||
|
testing::Combine(
|
||||||
|
testing::Values(CV_8UC1, CV_8SC1, CV_16UC1, CV_16SC1, CV_32SC1, CV_32FC1, CV_64FC1),
|
||||||
|
testing::Values(Size(1, 1), Size(320, 240), Size(127, 113), Size(1, 113))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef testing::TestWithParam<tuple<int, int, bool> > HasNonZeroNd;
|
||||||
|
|
||||||
|
TEST_P(HasNonZeroNd, hasNonZeroNd)
|
||||||
|
{
|
||||||
|
const int type = get<0>(GetParam());
|
||||||
|
const int ndims = get<1>(GetParam());
|
||||||
|
const bool continuous = get<2>(GetParam());
|
||||||
|
|
||||||
|
RNG& rng = theRNG();
|
||||||
|
|
||||||
|
const size_t N = 10;
|
||||||
|
for(size_t i = 0 ; i<N ; ++i)
|
||||||
|
{
|
||||||
|
std::vector<size_t> steps(ndims);
|
||||||
|
std::vector<int> sizes(ndims);
|
||||||
|
size_t totalBytes = 1;
|
||||||
|
for(int dim = 0 ; dim<ndims ; ++dim)
|
||||||
|
{
|
||||||
|
const bool isFirstDim = (dim == 0);
|
||||||
|
const bool isLastDim = (dim+1 == ndims);
|
||||||
|
const int length = rng.uniform(1, 64);
|
||||||
|
steps[dim] = (isLastDim ? 1 : static_cast<size_t>(length))*CV_ELEM_SIZE(type);
|
||||||
|
sizes[dim] = (isFirstDim || continuous) ? length : rng.uniform(1, length);
|
||||||
|
totalBytes *= steps[dim]*static_cast<size_t>(sizes[dim]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> buffer(totalBytes);
|
||||||
|
void* data = buffer.data();
|
||||||
|
|
||||||
|
Mat m = Mat(ndims, sizes.data(), type, data, steps.data());
|
||||||
|
|
||||||
|
std::vector<Range> nzRange(ndims);
|
||||||
|
for(int dim = 0 ; dim<ndims ; ++dim)
|
||||||
|
{
|
||||||
|
const int pos = rng.uniform(0, sizes[dim]);
|
||||||
|
nzRange[dim] = Range(pos, pos+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat nzROI = Mat(m, nzRange.data());
|
||||||
|
nzROI.setTo(Scalar::all(1));
|
||||||
|
|
||||||
|
const int nzCount = countNonZero(m);
|
||||||
|
EXPECT_EQ((nzCount>0), hasNonZero(m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(Core, HasNonZeroNd,
|
||||||
|
testing::Combine(
|
||||||
|
testing::Values(CV_8UC1),
|
||||||
|
testing::Values(2, 3),
|
||||||
|
testing::Values(true, false)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
}} // namespace
|
@ -446,5 +446,11 @@ class dnn_test(NewOpenCVTests):
|
|||||||
|
|
||||||
normAssert(self, real_output, gold_output, "", getDefaultThreshold(target))
|
normAssert(self, real_output, gold_output, "", getDefaultThreshold(target))
|
||||||
|
|
||||||
|
def test_scalefactor_assign(self):
|
||||||
|
params = cv.dnn.Image2BlobParams()
|
||||||
|
self.assertEqual(params.scalefactor, (1.0, 1.0, 1.0, 1.0))
|
||||||
|
params.scalefactor = 2.0
|
||||||
|
self.assertEqual(params.scalefactor, (2.0, 0.0, 0.0, 0.0))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
NewOpenCVTests.bootstrap()
|
NewOpenCVTests.bootstrap()
|
||||||
|
@ -97,11 +97,11 @@ class CaffeImporter
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CaffeImporter(const char *pototxt, const char *caffeModel)
|
CaffeImporter(const char *prototxt, const char *caffeModel)
|
||||||
{
|
{
|
||||||
CV_TRACE_FUNCTION();
|
CV_TRACE_FUNCTION();
|
||||||
|
|
||||||
ReadNetParamsFromTextFileOrDie(pototxt, &net);
|
ReadNetParamsFromTextFileOrDie(prototxt, &net);
|
||||||
|
|
||||||
if (caffeModel && caffeModel[0])
|
if (caffeModel && caffeModel[0])
|
||||||
ReadNetParamsFromBinaryFileOrDie(caffeModel, &netBinary);
|
ReadNetParamsFromBinaryFileOrDie(caffeModel, &netBinary);
|
||||||
@ -193,7 +193,6 @@ public:
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
CV_Error(Error::StsError, "Unknown type \"" + String(field->type_name()) + "\" in prototxt");
|
CV_Error(Error::StsError, "Unknown type \"" + String(field->type_name()) + "\" in prototxt");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -556,7 +555,6 @@ public:
|
|||||||
if (idx < 0)
|
if (idx < 0)
|
||||||
{
|
{
|
||||||
CV_Error(Error::StsObjectNotFound, "Can't find output blob \"" + name + "\"");
|
CV_Error(Error::StsObjectNotFound, "Can't find output blob \"" + name + "\"");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dstNet.connect(addedBlobs[idx].layerId, addedBlobs[idx].outNum, layerId, inNum);
|
dstNet.connect(addedBlobs[idx].layerId, addedBlobs[idx].outNum, layerId, inNum);
|
||||||
|
@ -51,6 +51,11 @@ void blobFromImages(InputArrayOfArrays images_, OutputArray blob_, double scalef
|
|||||||
Size size, const Scalar& mean_, bool swapRB, bool crop, int ddepth)
|
Size size, const Scalar& mean_, bool swapRB, bool crop, int ddepth)
|
||||||
{
|
{
|
||||||
CV_TRACE_FUNCTION();
|
CV_TRACE_FUNCTION();
|
||||||
|
if (images_.kind() != _InputArray::STD_VECTOR_MAT && images_.kind() != _InputArray::STD_ARRAY_MAT &&
|
||||||
|
images_.kind() != _InputArray::STD_VECTOR_VECTOR) {
|
||||||
|
String error_message = "The data is expected as vectors of vectors or vectors of matrices.";
|
||||||
|
CV_Error(Error::StsBadArg, error_message);
|
||||||
|
}
|
||||||
Image2BlobParams param(Scalar::all(scalefactor), size, mean_, swapRB, ddepth);
|
Image2BlobParams param(Scalar::all(scalefactor), size, mean_, swapRB, ddepth);
|
||||||
if (crop)
|
if (crop)
|
||||||
param.paddingmode = DNN_PMODE_CROP_CENTER;
|
param.paddingmode = DNN_PMODE_CROP_CENTER;
|
||||||
@ -83,9 +88,13 @@ Mat blobFromImagesWithParams(InputArrayOfArrays images, const Image2BlobParams&
|
|||||||
void blobFromImagesWithParams(InputArrayOfArrays images_, OutputArray blob_, const Image2BlobParams& param)
|
void blobFromImagesWithParams(InputArrayOfArrays images_, OutputArray blob_, const Image2BlobParams& param)
|
||||||
{
|
{
|
||||||
CV_TRACE_FUNCTION();
|
CV_TRACE_FUNCTION();
|
||||||
|
if (images_.kind() != _InputArray::STD_VECTOR_MAT && images_.kind() != _InputArray::STD_ARRAY_MAT &&
|
||||||
|
images_.kind() != _InputArray::STD_VECTOR_VECTOR) {
|
||||||
|
String error_message = "The data is expected as vectors of vectors or vectors of matrices.";
|
||||||
|
CV_Error(Error::StsBadArg, error_message);
|
||||||
|
}
|
||||||
CV_CheckType(param.ddepth, param.ddepth == CV_32F || param.ddepth == CV_8U,
|
CV_CheckType(param.ddepth, param.ddepth == CV_32F || param.ddepth == CV_8U,
|
||||||
"Blob depth should be CV_32F or CV_8U");
|
"Blob depth should be CV_32F or CV_8U");
|
||||||
|
|
||||||
Size size = param.size;
|
Size size = param.size;
|
||||||
std::vector<Mat> images;
|
std::vector<Mat> images;
|
||||||
images_.getMatVector(images);
|
images_.getMatVector(images);
|
||||||
|
@ -31,7 +31,6 @@ void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep,
|
|||||||
void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, float* bpptr, int bpstep, float* outptr, int outstep,
|
void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep, float* bpptr, int bpstep, float* outptr, int outstep,
|
||||||
float bias, float minval, float maxval, bool ifMinMaxAct);
|
float bias, float minval, float maxval, bool ifMinMaxAct);
|
||||||
|
|
||||||
|
|
||||||
int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr<FastConv>& conv,
|
int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _output, const Ptr<FastConv>& conv,
|
||||||
int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct)
|
int ntasks, float minval, float maxval, ActivationLayer* activ, bool ifMinMaxAct)
|
||||||
{
|
{
|
||||||
@ -51,6 +50,23 @@ int runWinograd63(InputArray _input, InputArray _fusedAddMat, OutputArray _outpu
|
|||||||
int pad_left = conv->pad_left;
|
int pad_left = conv->pad_left;
|
||||||
|
|
||||||
int ngroups = conv->ngroups, Cg = C/ngroups, Kg = K/ngroups;
|
int ngroups = conv->ngroups, Cg = C/ngroups, Kg = K/ngroups;
|
||||||
|
|
||||||
|
const int CONV_WINO_KBLOCK = 4;
|
||||||
|
#if (CV_NEON && CV_NEON_AARCH64)
|
||||||
|
const int CONV_WINO_IBLOCK = 6;
|
||||||
|
#elif CV_TRY_AVX || CV_TRY_AVX2
|
||||||
|
const int CONV_WINO_IBLOCK = (conv->useAVX || conv->useAVX2) ? 6 : 3;
|
||||||
|
#else
|
||||||
|
const int CONV_WINO_IBLOCK = 3;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CV_TRY_AVX || CV_TRY_AVX2
|
||||||
|
const int CONV_WINO_ATOM_F32 = (conv->useAVX || conv->useAVX2) ? 8 : 4;
|
||||||
|
#else
|
||||||
|
const int CONV_WINO_ATOM_F32 = 4;
|
||||||
|
#endif
|
||||||
|
const int CONV_WINO_NATOMS_F32 = CONV_WINO_AREA / CONV_WINO_ATOM_F32; // for AVX2, it is 8, otherwise, it's 16.
|
||||||
|
|
||||||
int Kg_nblocks = (Kg + CONV_WINO_KBLOCK - 1)/CONV_WINO_KBLOCK;
|
int Kg_nblocks = (Kg + CONV_WINO_KBLOCK - 1)/CONV_WINO_KBLOCK;
|
||||||
const size_t inp_planesize = (size_t)Hi*Wi;
|
const size_t inp_planesize = (size_t)Hi*Wi;
|
||||||
const size_t out_planesize = (size_t)H0*W0;
|
const size_t out_planesize = (size_t)H0*W0;
|
||||||
@ -398,7 +414,7 @@ void winofunc_accum_f32(const float* inwptr, const float* wptr, float* outbuf, i
|
|||||||
void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep,
|
void winofunc_BtXB_8x8_f32(const float* inptr, int inpstep,
|
||||||
float* outptr, int Cg, const int winoIblock, const int winoAtomF32)
|
float* outptr, int Cg, const int winoIblock, const int winoAtomF32)
|
||||||
{
|
{
|
||||||
CV_Assert(CONV_WINO_IBLOCK == 3 && CONV_WINO_KBLOCK == 4 && CONV_WINO_ATOM_F32 == 4);
|
CV_Assert(winoIblock == 3 && winoAtomF32 == 4);
|
||||||
v_float32x4 x00 = v_load(inptr), x01 = v_load(inptr + 4);
|
v_float32x4 x00 = v_load(inptr), x01 = v_load(inptr + 4);
|
||||||
v_float32x4 x10 = v_load(inptr + inpstep), x11 = v_load(inptr + inpstep + 4);
|
v_float32x4 x10 = v_load(inptr + inpstep), x11 = v_load(inptr + inpstep + 4);
|
||||||
v_float32x4 x20 = v_load(inptr + inpstep*2), x21 = v_load(inptr + inpstep*2 + 4);
|
v_float32x4 x20 = v_load(inptr + inpstep*2), x21 = v_load(inptr + inpstep*2 + 4);
|
||||||
@ -573,7 +589,6 @@ void winofunc_AtXA_8x8_f32(const float* inptr, int inpstep,
|
|||||||
float* bpptr, int bpstep, float* outptr, int outstep,
|
float* bpptr, int bpstep, float* outptr, int outstep,
|
||||||
float bias, float minval, float maxval, bool ifMinMaxAct)
|
float bias, float minval, float maxval, bool ifMinMaxAct)
|
||||||
{
|
{
|
||||||
CV_Assert(CONV_WINO_IBLOCK == 3 && CONV_WINO_KBLOCK == 4 && CONV_WINO_ATOM_F32 == 4);
|
|
||||||
v_float32x4 x00 = v_load(inptr), x01 = v_load(inptr + 4);
|
v_float32x4 x00 = v_load(inptr), x01 = v_load(inptr + 4);
|
||||||
v_float32x4 x10 = v_load(inptr + inpstep), x11 = v_load(inptr + inpstep + 4);
|
v_float32x4 x10 = v_load(inptr + inpstep), x11 = v_load(inptr + inpstep + 4);
|
||||||
v_float32x4 x20 = v_load(inptr + inpstep*2), x21 = v_load(inptr + inpstep*2 + 4);
|
v_float32x4 x20 = v_load(inptr + inpstep*2), x21 = v_load(inptr + inpstep*2 + 4);
|
||||||
|
@ -181,6 +181,21 @@ Ptr<FastConv> initFastConv(
|
|||||||
{0.0f, 0.0f, 1.0f}
|
{0.0f, 0.0f, 1.0f}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const int CONV_WINO_KBLOCK = 4;
|
||||||
|
|
||||||
|
#if CV_TRY_AVX || CV_TRY_AVX2
|
||||||
|
const int CONV_WINO_ATOM_F32 = (conv->useAVX || conv->useAVX2) ? 8 : 4;
|
||||||
|
#else
|
||||||
|
const int CONV_WINO_ATOM_F32 = 4;
|
||||||
|
#endif
|
||||||
|
const int CONV_WINO_NATOMS_F32 = CONV_WINO_AREA / CONV_WINO_ATOM_F32; // for AVX2, it is 8, otherwise, it's 16.
|
||||||
|
|
||||||
|
#ifdef CONV_ARM_FP16
|
||||||
|
// FP 16
|
||||||
|
const int CONV_WINO_ATOM_F16 = CONV_WINO_ATOM_F32 * 2;
|
||||||
|
const int CONV_WINO_NATOMS_F16 = CONV_WINO_AREA / CONV_WINO_ATOM_F16;
|
||||||
|
#endif
|
||||||
|
|
||||||
// the weights are packed as 6-dim tensor:
|
// the weights are packed as 6-dim tensor:
|
||||||
// ngroups * ceil((K/ngroups)/KBLOCK) * (W*W/ATOM_SIZE) * (C/ngroups) * KBLOCK * ATOM_SIZE,
|
// ngroups * ceil((K/ngroups)/KBLOCK) * (W*W/ATOM_SIZE) * (C/ngroups) * KBLOCK * ATOM_SIZE,
|
||||||
// where W is the size of Winograd-transformed kernel (8x8),
|
// where W is the size of Winograd-transformed kernel (8x8),
|
||||||
@ -1275,7 +1290,7 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr<FastConv>& co
|
|||||||
else
|
else
|
||||||
Kg_nblocks = 1;
|
Kg_nblocks = 1;
|
||||||
|
|
||||||
bool seperateIm2col = fast_1x1 || stripes_per_plane == 1;
|
bool separateIm2col = fast_1x1 || stripes_per_plane == 1;
|
||||||
|
|
||||||
int Kstripes = Kg_nblocks * stripes_per_plane;
|
int Kstripes = Kg_nblocks * stripes_per_plane;
|
||||||
int nsubtasks = N * ngroups * Kstripes;
|
int nsubtasks = N * ngroups * Kstripes;
|
||||||
@ -1285,12 +1300,12 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr<FastConv>& co
|
|||||||
|
|
||||||
size_t taskbufsize = cbufsize * sizeof(float );
|
size_t taskbufsize = cbufsize * sizeof(float );
|
||||||
|
|
||||||
if (!seperateIm2col)
|
if (!separateIm2col)
|
||||||
taskbufsize += MAX_STRIPES * stripesize * esz;
|
taskbufsize += MAX_STRIPES * stripesize * esz;
|
||||||
|
|
||||||
size_t totalbufsize_base = taskbufsize * ntasks;
|
size_t totalbufsize_base = taskbufsize * ntasks;
|
||||||
size_t totalbufsize = totalbufsize_base;
|
size_t totalbufsize = totalbufsize_base;
|
||||||
if (seperateIm2col)
|
if (separateIm2col)
|
||||||
totalbufsize += N * ngroups * stripes_per_plane0 * stripesize * esz;
|
totalbufsize += N * ngroups * stripes_per_plane0 * stripesize * esz;
|
||||||
|
|
||||||
AutoBuffer<char> inpbuf_all_;
|
AutoBuffer<char> inpbuf_all_;
|
||||||
@ -1308,7 +1323,7 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr<FastConv>& co
|
|||||||
// In general, im2row results in Hk*Wk-x unrolling factor
|
// In general, im2row results in Hk*Wk-x unrolling factor
|
||||||
// (e.g. 3*3=9x unrolling for 3x3 convolution), thus for 1x1 convolution
|
// (e.g. 3*3=9x unrolling for 3x3 convolution), thus for 1x1 convolution
|
||||||
// the reordered tensor will take as much space as the original tensor.
|
// the reordered tensor will take as much space as the original tensor.
|
||||||
if (seperateIm2col)
|
if (separateIm2col)
|
||||||
{
|
{
|
||||||
// the optional phase 1. im2row
|
// the optional phase 1. im2row
|
||||||
parallel_for_(Range(0, ntasks), [&](const Range& r0) {
|
parallel_for_(Range(0, ntasks), [&](const Range& r0) {
|
||||||
@ -1409,7 +1424,7 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr<FastConv>& co
|
|||||||
|
|
||||||
CV_Assert(nstripes <= MAX_STRIPES);
|
CV_Assert(nstripes <= MAX_STRIPES);
|
||||||
|
|
||||||
if (!seperateIm2col)
|
if (!separateIm2col)
|
||||||
{
|
{
|
||||||
packInputData(inpbuf_task, inp, ofstab, dhwTab, zyx0, zyx_block_limit, ksize, stride_d, stride_h,
|
packInputData(inpbuf_task, inp, ofstab, dhwTab, zyx0, zyx_block_limit, ksize, stride_d, stride_h,
|
||||||
stride_w, pad_front, pad_top, pad_left, Dk, Hk, Wk, dilation_d, dilation_h, dilation_w,
|
stride_w, pad_front, pad_top, pad_left, Dk, Hk, Wk, dilation_d, dilation_h, dilation_w,
|
||||||
@ -1442,8 +1457,8 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr<FastConv>& co
|
|||||||
int out_width = zyx_block_limit - zyx0;
|
int out_width = zyx_block_limit - zyx0;
|
||||||
float *outptr = out + outofs;
|
float *outptr = out + outofs;
|
||||||
const float biasVal = *(conv->biasBuf.data() + g);
|
const float biasVal = *(conv->biasBuf.data() + g);
|
||||||
const char *inptr_ = seperateIm2col ? inpbuf_all_0 + (ng*stripes_per_plane0 + zyx0/CONV_NR) * stripesize * esz:
|
const char *inptr_ = separateIm2col ? inpbuf_all_0 + (ng * stripes_per_plane0 + zyx0 / CONV_NR) * stripesize * esz :
|
||||||
inpbuf_task;
|
inpbuf_task;
|
||||||
|
|
||||||
for (int stripe = 0; stripe < nstripes; stripe++)
|
for (int stripe = 0; stripe < nstripes; stripe++)
|
||||||
{
|
{
|
||||||
@ -1496,8 +1511,8 @@ void runFastConv(InputArray _input, OutputArray _output, const Ptr<FastConv>& co
|
|||||||
for (int c0 = 0; c0 < DkHkWkCg; c0 += C_BLOCK_SIZE)
|
for (int c0 = 0; c0 < DkHkWkCg; c0 += C_BLOCK_SIZE)
|
||||||
{
|
{
|
||||||
int c1 = c0 + C_BLOCK_SIZE < DkHkWkCg ? c0 + C_BLOCK_SIZE : DkHkWkCg;
|
int c1 = c0 + C_BLOCK_SIZE < DkHkWkCg ? c0 + C_BLOCK_SIZE : DkHkWkCg;
|
||||||
const char *inptr = seperateIm2col ? inpbuf_all_0 + (ng*stripes_per_plane0 + zyx0/CONV_NR)*stripesize*esz:
|
const char *inptr = separateIm2col ? inpbuf_all_0 + (ng * stripes_per_plane0 + zyx0 / CONV_NR) * stripesize * esz :
|
||||||
inpbuf_task;
|
inpbuf_task;
|
||||||
inptr += (c0 * CONV_NR) * esz;
|
inptr += (c0 * CONV_NR) * esz;
|
||||||
for (int stripe = 0; stripe < nstripes; stripe++, inptr += stripesize * esz)
|
for (int stripe = 0; stripe < nstripes; stripe++, inptr += stripesize * esz)
|
||||||
{
|
{
|
||||||
|
@ -33,36 +33,17 @@ typedef __fp16 float16_t; // Fix conflict between float16_t in arm_neon.h and fl
|
|||||||
#define CONV_NR_FP32 24
|
#define CONV_NR_FP32 24
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Winograd Params
|
|
||||||
enum {
|
enum {
|
||||||
CONV_WINO_STEP=6,
|
CONV_WINO_STEP=6,
|
||||||
CONV_WINO_KSIZE=3,
|
CONV_WINO_KSIZE=3,
|
||||||
CONV_WINO_SIZE=CONV_WINO_STEP+CONV_WINO_KSIZE-1, // 8
|
CONV_WINO_SIZE=CONV_WINO_STEP+CONV_WINO_KSIZE - 1, // 8
|
||||||
CONV_WINO_AREA=CONV_WINO_SIZE*CONV_WINO_SIZE,
|
CONV_WINO_AREA=CONV_WINO_SIZE*CONV_WINO_SIZE,
|
||||||
|
|
||||||
CONV_WINO_KBLOCK = 4,
|
|
||||||
#if (CV_NEON && CV_NEON_AARCH64) || CV_TRY_AVX || CV_TRY_AVX2
|
|
||||||
CONV_WINO_IBLOCK = 6,
|
|
||||||
#else
|
|
||||||
CONV_WINO_IBLOCK = 3,
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if CV_TRY_AVX || CV_TRY_AVX2
|
|
||||||
CONV_WINO_ATOM_F32 = 8,
|
|
||||||
#else
|
|
||||||
CONV_WINO_ATOM_F32 = 4,
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CONV_WINO_NATOMS_F32 = CONV_WINO_AREA / CONV_WINO_ATOM_F32, // for AVX2, it is 8, otherwise, it's 16.
|
|
||||||
|
|
||||||
// FP 16
|
|
||||||
CONV_WINO_ATOM_F16 = CONV_WINO_ATOM_F32 * 2,
|
|
||||||
CONV_WINO_NATOMS_F16 = CONV_WINO_AREA / CONV_WINO_ATOM_F16,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOTE that: CONV_TYPE_DEPTHWISE is for 3x3 depthwise conv, and others depthwise will be set as CONV_TYPE_DEPTHWISE_REMAIN.
|
// NOTE that: CONV_TYPE_DEPTHWISE is for 3x3 depthwise conv, and others depthwise will be set as CONV_TYPE_DEPTHWISE_REMAIN.
|
||||||
enum { CONV_TYPE_GENERIC=0, CONV_TYPE_DEPTHWISE=1, CONV_TYPE_WINOGRAD3X3=2, CONV_TYPE_DEPTHWISE_REMAIN=3 };
|
enum { CONV_TYPE_GENERIC=0, CONV_TYPE_DEPTHWISE=1, CONV_TYPE_WINOGRAD3X3=2, CONV_TYPE_DEPTHWISE_REMAIN=3 };
|
||||||
enum { CONV_1D = 0, CONV_2D = 1, CONV_3D = 2 };
|
enum { CONV_1D = 0, CONV_2D = 1, CONV_3D = 2 };
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace cv {
|
namespace cv {
|
||||||
|
@ -1363,14 +1363,22 @@ void ONNXImporter::parseSplit(LayerParams& layerParams, const opencv_onnx::NodeP
|
|||||||
{
|
{
|
||||||
DictValue splits = layerParams.get("split");
|
DictValue splits = layerParams.get("split");
|
||||||
const int numSplits = splits.size();
|
const int numSplits = splits.size();
|
||||||
CV_Assert(numSplits > 1);
|
|
||||||
|
|
||||||
std::vector<int> slicePoints(numSplits - 1, splits.get<int>(0));
|
if (numSplits == 1)
|
||||||
for (int i = 1; i < splits.size() - 1; ++i)
|
|
||||||
{
|
{
|
||||||
slicePoints[i] = slicePoints[i - 1] + splits.get<int>(i);
|
layerParams.set("num_split", 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CV_Assert(numSplits >= 1);
|
||||||
|
|
||||||
|
std::vector<int> slicePoints(numSplits - 1, splits.get<int>(0));
|
||||||
|
for (int i = 1; i < splits.size() - 1; ++i)
|
||||||
|
{
|
||||||
|
slicePoints[i] = slicePoints[i - 1] + splits.get<int>(i);
|
||||||
|
}
|
||||||
|
layerParams.set("slice_point", DictValue::arrayInt(&slicePoints[0], slicePoints.size()));
|
||||||
}
|
}
|
||||||
layerParams.set("slice_point", DictValue::arrayInt(&slicePoints[0], slicePoints.size()));
|
|
||||||
}
|
}
|
||||||
else if (node_proto.input_size() == 2) // opset >= 13, the split will be stored at the second input, instead of the attribute.
|
else if (node_proto.input_size() == 2) // opset >= 13, the split will be stored at the second input, instead of the attribute.
|
||||||
{
|
{
|
||||||
@ -2771,13 +2779,15 @@ void ONNXImporter::parseResize(LayerParams& layerParams, const opencv_onnx::Node
|
|||||||
if (layerParams.has("coordinate_transformation_mode"))
|
if (layerParams.has("coordinate_transformation_mode"))
|
||||||
{
|
{
|
||||||
String interp_mode = layerParams.get<String>("coordinate_transformation_mode");
|
String interp_mode = layerParams.get<String>("coordinate_transformation_mode");
|
||||||
CV_Assert_N(interp_mode != "tf_crop_and_resize", interp_mode != "tf_half_pixel_for_nn");
|
CV_Assert(interp_mode != "tf_crop_and_resize");
|
||||||
|
|
||||||
|
bool halfPixel = interp_mode == "tf_half_pixel_for_nn" || interp_mode == "half_pixel" || interp_mode == "pytorch_half_pixel";
|
||||||
|
|
||||||
layerParams.set("align_corners", interp_mode == "align_corners");
|
layerParams.set("align_corners", interp_mode == "align_corners");
|
||||||
|
layerParams.set("half_pixel_centers", halfPixel);
|
||||||
if (layerParams.get<String>("mode") == "linear")
|
if (layerParams.get<String>("mode") == "linear")
|
||||||
{
|
{
|
||||||
layerParams.set("mode", interp_mode == "pytorch_half_pixel" || interp_mode == "half_pixel" ?
|
layerParams.set("mode", halfPixel ? "opencv_linear" : "bilinear");
|
||||||
"opencv_linear" : "bilinear");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (layerParams.get<String>("mode") == "linear" && framework_name == "pytorch")
|
if (layerParams.get<String>("mode") == "linear" && framework_name == "pytorch")
|
||||||
@ -2830,13 +2840,15 @@ void ONNXImporter::parseUpsample(LayerParams& layerParams, const opencv_onnx::No
|
|||||||
if (layerParams.has("coordinate_transformation_mode"))
|
if (layerParams.has("coordinate_transformation_mode"))
|
||||||
{
|
{
|
||||||
String interp_mode = layerParams.get<String>("coordinate_transformation_mode");
|
String interp_mode = layerParams.get<String>("coordinate_transformation_mode");
|
||||||
CV_Assert_N(interp_mode != "tf_crop_and_resize", interp_mode != "tf_half_pixel_for_nn");
|
CV_Assert(interp_mode != "tf_crop_and_resize");
|
||||||
|
|
||||||
|
bool halfPixel = interp_mode == "tf_half_pixel_for_nn" || interp_mode == "half_pixel" || interp_mode == "pytorch_half_pixel";
|
||||||
|
|
||||||
layerParams.set("align_corners", interp_mode == "align_corners");
|
layerParams.set("align_corners", interp_mode == "align_corners");
|
||||||
|
layerParams.set("half_pixel_centers", halfPixel);
|
||||||
if (layerParams.get<String>("mode") == "linear")
|
if (layerParams.get<String>("mode") == "linear")
|
||||||
{
|
{
|
||||||
layerParams.set("mode", interp_mode == "pytorch_half_pixel" ?
|
layerParams.set("mode", halfPixel ? "opencv_linear" : "bilinear");
|
||||||
"opencv_linear" : "bilinear");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (layerParams.get<String>("mode") == "linear" && framework_name == "pytorch")
|
if (layerParams.get<String>("mode") == "linear" && framework_name == "pytorch")
|
||||||
|
@ -1017,6 +1017,7 @@ TEST_P(Test_ONNX_layers, Padding)
|
|||||||
TEST_P(Test_ONNX_layers, Resize)
|
TEST_P(Test_ONNX_layers, Resize)
|
||||||
{
|
{
|
||||||
testONNXModels("resize_nearest");
|
testONNXModels("resize_nearest");
|
||||||
|
testONNXModels("tf_half_pixel_for_nn");
|
||||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NN_BUILDER_2019)
|
||||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
|
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
|
||||||
testONNXModels("resize_bilinear");
|
testONNXModels("resize_bilinear");
|
||||||
@ -1146,6 +1147,7 @@ TEST_P(Test_ONNX_layers, Split)
|
|||||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
|
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NN_BUILDER);
|
||||||
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
if (backend == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
applyTestTag(CV_TEST_TAG_DNN_SKIP_IE_NGRAPH);
|
||||||
|
testONNXModels("split_0");
|
||||||
testONNXModels("split_1");
|
testONNXModels("split_1");
|
||||||
testONNXModels("split_2");
|
testONNXModels("split_2");
|
||||||
testONNXModels("split_3");
|
testONNXModels("split_3");
|
||||||
|
@ -156,6 +156,10 @@ set(gapi_srcs
|
|||||||
src/backends/ie/giebackend.cpp
|
src/backends/ie/giebackend.cpp
|
||||||
src/backends/ie/giebackend/giewrapper.cpp
|
src/backends/ie/giebackend/giewrapper.cpp
|
||||||
|
|
||||||
|
# OV Backend. FIXME: should be included by CMake
|
||||||
|
# if and only if OV support is enabled
|
||||||
|
src/backends/ov/govbackend.cpp
|
||||||
|
|
||||||
# ONNX backend
|
# ONNX backend
|
||||||
src/backends/onnx/gonnxbackend.cpp
|
src/backends/onnx/gonnxbackend.cpp
|
||||||
|
|
||||||
@ -182,6 +186,7 @@ set(gapi_srcs
|
|||||||
# Python bridge
|
# Python bridge
|
||||||
src/backends/ie/bindings_ie.cpp
|
src/backends/ie/bindings_ie.cpp
|
||||||
src/backends/onnx/bindings_onnx.cpp
|
src/backends/onnx/bindings_onnx.cpp
|
||||||
|
src/backends/ov/bindings_ov.cpp
|
||||||
src/backends/python/gpythonbackend.cpp
|
src/backends/python/gpythonbackend.cpp
|
||||||
|
|
||||||
# OpenVPL Streaming source
|
# OpenVPL Streaming source
|
||||||
|
128
modules/gapi/include/opencv2/gapi/infer/bindings_ov.hpp
Normal file
128
modules/gapi/include/opencv2/gapi/infer/bindings_ov.hpp
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2023 Intel Corporation
|
||||||
|
|
||||||
|
#ifndef OPENCV_GAPI_INFER_BINDINGS_OV_HPP
|
||||||
|
#define OPENCV_GAPI_INFER_BINDINGS_OV_HPP
|
||||||
|
|
||||||
|
#include <opencv2/gapi/util/any.hpp>
|
||||||
|
#include "opencv2/gapi/own/exports.hpp" // GAPI_EXPORTS
|
||||||
|
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
|
||||||
|
#include <opencv2/gapi/infer/ov.hpp> // Params
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace gapi {
|
||||||
|
namespace ov {
|
||||||
|
|
||||||
|
// NB: Used by python wrapper
|
||||||
|
// This class can be marked as SIMPLE, because it's implemented as pimpl
|
||||||
|
class GAPI_EXPORTS_W_SIMPLE PyParams {
|
||||||
|
public:
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams() = default;
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams(const std::string &tag,
|
||||||
|
const std::string &model_path,
|
||||||
|
const std::string &bin_path,
|
||||||
|
const std::string &device);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams(const std::string &tag,
|
||||||
|
const std::string &blob_path,
|
||||||
|
const std::string &device);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgPluginConfig(
|
||||||
|
const std::map<std::string, std::string> &config);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgInputTensorLayout(std::string tensor_layout);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgInputTensorLayout(
|
||||||
|
std::map<std::string, std::string> layout_map);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgInputModelLayout(std::string tensor_layout);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgInputModelLayout(
|
||||||
|
std::map<std::string, std::string> layout_map);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgOutputTensorLayout(std::string tensor_layout);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgOutputTensorLayout(
|
||||||
|
std::map<std::string, std::string> layout_map);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgOutputModelLayout(std::string tensor_layout);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgOutputModelLayout(
|
||||||
|
std::map<std::string, std::string> layout_map);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgOutputTensorPrecision(int precision);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgOutputTensorPrecision(
|
||||||
|
std::map<std::string, int> precision_map);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgReshape(std::vector<size_t> new_shape);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgReshape(
|
||||||
|
std::map<std::string, std::vector<size_t>> new_shape_map);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgNumRequests(const size_t nireq);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgMean(std::vector<float> mean_values);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgMean(
|
||||||
|
std::map<std::string, std::vector<float>> mean_map);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgScale(std::vector<float> scale_values);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgScale(
|
||||||
|
std::map<std::string, std::vector<float>> scale_map);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgResize(int interpolation);
|
||||||
|
|
||||||
|
GAPI_WRAP
|
||||||
|
PyParams& cfgResize(std::map<std::string, int> interpolation);
|
||||||
|
|
||||||
|
GBackend backend() const;
|
||||||
|
std::string tag() const;
|
||||||
|
cv::util::any params() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Params<cv::gapi::Generic>> m_priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
GAPI_EXPORTS_W PyParams params(const std::string &tag,
|
||||||
|
const std::string &model_path,
|
||||||
|
const std::string &weights,
|
||||||
|
const std::string &device);
|
||||||
|
|
||||||
|
GAPI_EXPORTS_W PyParams params(const std::string &tag,
|
||||||
|
const std::string &bin_path,
|
||||||
|
const std::string &device);
|
||||||
|
} // namespace ov
|
||||||
|
} // namespace gapi
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif // OPENCV_GAPI_INFER_BINDINGS_OV_HPP
|
@ -2,7 +2,7 @@
|
|||||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
// of this distribution and at http://opencv.org/license.html.
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2019-2021 Intel Corporation
|
// Copyright (C) 2019-2023 Intel Corporation
|
||||||
|
|
||||||
#ifndef OPENCV_GAPI_INFER_IE_HPP
|
#ifndef OPENCV_GAPI_INFER_IE_HPP
|
||||||
#define OPENCV_GAPI_INFER_IE_HPP
|
#define OPENCV_GAPI_INFER_IE_HPP
|
||||||
@ -55,6 +55,21 @@ using IEConfig = std::map<std::string, std::string>;
|
|||||||
enum InferMode {Sync, Async};
|
enum InferMode {Sync, Async};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using AttrMap = std::map<std::string, T>;
|
||||||
|
// NB: This type is used to hold in/out layers
|
||||||
|
// attributes such as precision, layout, shape etc.
|
||||||
|
//
|
||||||
|
// User can provide attributes either:
|
||||||
|
// 1. cv::util::monostate - No value specified explicitly.
|
||||||
|
// 2. Attr - value specified explicitly that should be broadcasted to all layers.
|
||||||
|
// 3. AttrMap[str->T] - map specifies value for particular layer.
|
||||||
|
template <typename Attr>
|
||||||
|
using LayerVariantAttr = cv::util::variant< cv::util::monostate
|
||||||
|
, AttrMap<Attr>
|
||||||
|
, Attr>;
|
||||||
|
|
||||||
struct ParamDesc {
|
struct ParamDesc {
|
||||||
std::string model_path;
|
std::string model_path;
|
||||||
std::string weights_path;
|
std::string weights_path;
|
||||||
@ -103,7 +118,11 @@ struct ParamDesc {
|
|||||||
using PrecisionVariantT = cv::util::variant<cv::util::monostate,
|
using PrecisionVariantT = cv::util::variant<cv::util::monostate,
|
||||||
PrecisionT,
|
PrecisionT,
|
||||||
PrecisionMapT>;
|
PrecisionMapT>;
|
||||||
|
|
||||||
PrecisionVariantT output_precision;
|
PrecisionVariantT output_precision;
|
||||||
|
LayerVariantAttr<std::string> input_layout;
|
||||||
|
LayerVariantAttr<std::string> output_layout;
|
||||||
|
LayerVariantAttr<int> interpolation;
|
||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@ -150,6 +169,9 @@ public:
|
|||||||
, {}
|
, {}
|
||||||
, {}
|
, {}
|
||||||
, InferMode::Async
|
, InferMode::Async
|
||||||
|
, {}
|
||||||
|
, {}
|
||||||
|
, {}
|
||||||
, {} } {
|
, {} } {
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -176,6 +198,9 @@ public:
|
|||||||
, {}
|
, {}
|
||||||
, {}
|
, {}
|
||||||
, InferMode::Async
|
, InferMode::Async
|
||||||
|
, {}
|
||||||
|
, {}
|
||||||
|
, {}
|
||||||
, {} } {
|
, {} } {
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -412,6 +437,80 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies the input layout for model.
|
||||||
|
|
||||||
|
The function is used to set an input layout for model.
|
||||||
|
|
||||||
|
@param layout Layout in string representation ("NCHW", "NHWC", etc)
|
||||||
|
will be applied to all input layers.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgInputLayout(std::string layout) {
|
||||||
|
desc.input_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
|
||||||
|
@param layout_map Map of pairs: name of corresponding input layer
|
||||||
|
and its layout in string representation ("NCHW", "NHWC", etc)
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>&
|
||||||
|
cfgInputLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
desc.input_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies the output layout for model.
|
||||||
|
|
||||||
|
The function is used to set an output layout for model.
|
||||||
|
|
||||||
|
@param layout Layout in string representation ("NCHW", "NHWC", etc)
|
||||||
|
will be applied to all output layers.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgOutputLayout(std::string layout) {
|
||||||
|
desc.output_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
|
||||||
|
@param layout_map Map of pairs: name of corresponding output layer
|
||||||
|
and its layout in string representation ("NCHW", "NHWC", etc)
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>&
|
||||||
|
cfgOutputLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
desc.output_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies resize interpolation algorithm.
|
||||||
|
*
|
||||||
|
The function is used to configure resize preprocessing for input layer.
|
||||||
|
|
||||||
|
@param interpolation Resize interpolation algorithm.
|
||||||
|
Supported algorithms: #INTER_LINEAR, #INTER_AREA.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgResize(int interpolation) {
|
||||||
|
desc.interpolation = interpolation;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
|
||||||
|
@param interpolation Map of pairs: name of corresponding input layer
|
||||||
|
and its resize algorithm.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgResize(detail::AttrMap<int> interpolation) {
|
||||||
|
desc.interpolation = std::move(interpolation);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
// BEGIN(G-API's network parametrization API)
|
// BEGIN(G-API's network parametrization API)
|
||||||
GBackend backend() const { return cv::gapi::ie::backend(); }
|
GBackend backend() const { return cv::gapi::ie::backend(); }
|
||||||
std::string tag() const { return Net::tag(); }
|
std::string tag() const { return Net::tag(); }
|
||||||
@ -446,7 +545,7 @@ public:
|
|||||||
const std::string &device)
|
const std::string &device)
|
||||||
: desc{ model, weights, device, {}, {}, {}, 0u, 0u,
|
: desc{ model, weights, device, {}, {}, {}, 0u, 0u,
|
||||||
detail::ParamDesc::Kind::Load, true, {}, {}, {}, 1u,
|
detail::ParamDesc::Kind::Load, true, {}, {}, {}, 1u,
|
||||||
{}, {}, {}, {}, InferMode::Async, {} },
|
{}, {}, {}, {}, InferMode::Async, {}, {}, {}, {} },
|
||||||
m_tag(tag) {
|
m_tag(tag) {
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -464,7 +563,7 @@ public:
|
|||||||
const std::string &device)
|
const std::string &device)
|
||||||
: desc{ model, {}, device, {}, {}, {}, 0u, 0u,
|
: desc{ model, {}, device, {}, {}, {}, 0u, 0u,
|
||||||
detail::ParamDesc::Kind::Import, true, {}, {}, {}, 1u,
|
detail::ParamDesc::Kind::Import, true, {}, {}, {}, 1u,
|
||||||
{}, {}, {}, {}, InferMode::Async, {} },
|
{}, {}, {}, {}, InferMode::Async, {}, {}, {}, {} },
|
||||||
m_tag(tag) {
|
m_tag(tag) {
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -556,6 +655,44 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @see ie::Params::cfgInputLayout */
|
||||||
|
Params& cfgInputLayout(std::string layout) {
|
||||||
|
desc.input_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params&
|
||||||
|
cfgInputLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
desc.input_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ie::Params::cfgOutputLayout */
|
||||||
|
Params& cfgOutputLayout(std::string layout) {
|
||||||
|
desc.output_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params&
|
||||||
|
cfgOutputLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
desc.output_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ie::Params::cfgResize */
|
||||||
|
Params& cfgResize(int interpolation) {
|
||||||
|
desc.interpolation = interpolation;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params& cfgResize(detail::AttrMap<int> interpolation) {
|
||||||
|
desc.interpolation = std::move(interpolation);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
// BEGIN(G-API's network parametrization API)
|
// BEGIN(G-API's network parametrization API)
|
||||||
GBackend backend() const { return cv::gapi::ie::backend(); }
|
GBackend backend() const { return cv::gapi::ie::backend(); }
|
||||||
std::string tag() const { return m_tag; }
|
std::string tag() const { return m_tag; }
|
||||||
|
685
modules/gapi/include/opencv2/gapi/infer/ov.hpp
Normal file
685
modules/gapi/include/opencv2/gapi/infer/ov.hpp
Normal file
@ -0,0 +1,685 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2023 Intel Corporation
|
||||||
|
|
||||||
|
#ifndef OPENCV_GAPI_INFER_OV_HPP
|
||||||
|
#define OPENCV_GAPI_INFER_OV_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <opencv2/gapi/util/any.hpp>
|
||||||
|
#include <opencv2/gapi/own/exports.hpp> // GAPI_EXPORTS
|
||||||
|
#include <opencv2/gapi/gkernel.hpp> // GKernelType[M], GBackend
|
||||||
|
#include <opencv2/gapi/infer.hpp> // Generic
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace gapi {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This namespace contains G-API OpenVINO 2.0 backend functions,
|
||||||
|
* structures, and symbols.
|
||||||
|
*/
|
||||||
|
namespace ov {
|
||||||
|
|
||||||
|
GAPI_EXPORTS cv::gapi::GBackend backend();
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using AttrMap = std::map<std::string, T>;
|
||||||
|
// NB: This type is supposed to be used to hold in/out layers
|
||||||
|
// attributes such as precision, layout, shape etc.
|
||||||
|
//
|
||||||
|
// User can provide attributes either:
|
||||||
|
// 1. cv::util::monostate - No value specified explicitly.
|
||||||
|
// 2. Attr - value specified explicitly that should be broadcasted to all layers.
|
||||||
|
// 3. AttrMap[str->T] - map specifies value for particular layer.
|
||||||
|
template <typename Attr>
|
||||||
|
using LayerVariantAttr = cv::util::variant< cv::util::monostate
|
||||||
|
, AttrMap<Attr>
|
||||||
|
, Attr>;
|
||||||
|
|
||||||
|
struct ParamDesc {
|
||||||
|
struct Model {
|
||||||
|
|
||||||
|
Model(const std::string &model_path_,
|
||||||
|
const std::string &bin_path_)
|
||||||
|
: model_path(model_path_), bin_path(bin_path_) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string model_path;
|
||||||
|
std::string bin_path;
|
||||||
|
|
||||||
|
LayerVariantAttr<std::string> input_tensor_layout;
|
||||||
|
LayerVariantAttr<std::string> input_model_layout;
|
||||||
|
LayerVariantAttr<std::string> output_tensor_layout;
|
||||||
|
LayerVariantAttr<std::string> output_model_layout;
|
||||||
|
LayerVariantAttr<int> output_tensor_precision;
|
||||||
|
|
||||||
|
LayerVariantAttr<std::vector<size_t>> new_shapes;
|
||||||
|
|
||||||
|
LayerVariantAttr<std::vector<float>> mean_values;
|
||||||
|
LayerVariantAttr<std::vector<float>> scale_values;
|
||||||
|
|
||||||
|
LayerVariantAttr<int> interpolation;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CompiledModel {
|
||||||
|
std::string blob_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
using Kind = cv::util::variant<Model, CompiledModel>;
|
||||||
|
|
||||||
|
ParamDesc(Kind &&kind_,
|
||||||
|
const std::string &device_,
|
||||||
|
const bool is_generic_,
|
||||||
|
const size_t num_in_,
|
||||||
|
const size_t num_out_)
|
||||||
|
: kind(std::move(kind_)), device(device_),
|
||||||
|
is_generic(is_generic_),
|
||||||
|
num_in(num_in_), num_out(num_out_) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Kind kind;
|
||||||
|
|
||||||
|
std::string device;
|
||||||
|
bool is_generic;
|
||||||
|
|
||||||
|
std::size_t num_in;
|
||||||
|
std::size_t num_out;
|
||||||
|
|
||||||
|
std::vector<std::string> input_names;
|
||||||
|
std::vector<std::string> output_names;
|
||||||
|
|
||||||
|
using PluginConfigT = std::map<std::string, std::string>;
|
||||||
|
PluginConfigT config;
|
||||||
|
|
||||||
|
size_t nireq = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// NB: Just helper to avoid code duplication.
|
||||||
|
static detail::ParamDesc::Model&
|
||||||
|
getModelToSetAttrOrThrow(detail::ParamDesc::Kind &kind,
|
||||||
|
const std::string &attr_name) {
|
||||||
|
if (cv::util::holds_alternative<detail::ParamDesc::CompiledModel>(kind)) {
|
||||||
|
cv::util::throw_error(
|
||||||
|
std::logic_error("Specifying " + attr_name + " isn't"
|
||||||
|
" possible for compiled model."));
|
||||||
|
}
|
||||||
|
GAPI_Assert(cv::util::holds_alternative<detail::ParamDesc::Model>(kind));
|
||||||
|
return cv::util::get<detail::ParamDesc::Model>(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This structure provides functions
|
||||||
|
* that fill inference parameters for "OpenVINO Toolkit" model.
|
||||||
|
*/
|
||||||
|
template<typename Net> struct Params {
|
||||||
|
public:
|
||||||
|
/** @brief Class constructor.
|
||||||
|
|
||||||
|
Constructs Params based on model information and specifies default values for other
|
||||||
|
inference description parameters. Model is loaded and compiled using "OpenVINO Toolkit".
|
||||||
|
|
||||||
|
@param model_path Path to a model.
|
||||||
|
@param bin_path Path to a data file.
|
||||||
|
For IR format (*.bin):
|
||||||
|
If path is empty, will try to read a bin file with the same name as xml.
|
||||||
|
If the bin file with the same name is not found, will load IR without weights.
|
||||||
|
For PDPD (*.pdmodel) and ONNX (*.onnx) formats bin_path isn't used.
|
||||||
|
@param device target device to use.
|
||||||
|
*/
|
||||||
|
Params(const std::string &model_path,
|
||||||
|
const std::string &bin_path,
|
||||||
|
const std::string &device)
|
||||||
|
: m_desc( detail::ParamDesc::Kind{detail::ParamDesc::Model{model_path, bin_path}}
|
||||||
|
, device
|
||||||
|
, false /* is generic */
|
||||||
|
, std::tuple_size<typename Net::InArgs>::value
|
||||||
|
, std::tuple_size<typename Net::OutArgs>::value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
Use this constructor to work with pre-compiled network.
|
||||||
|
Model is imported from a pre-compiled blob.
|
||||||
|
|
||||||
|
@param blob_path path to the compiled model (*.blob).
|
||||||
|
@param device target device to use.
|
||||||
|
*/
|
||||||
|
Params(const std::string &blob_path,
|
||||||
|
const std::string &device)
|
||||||
|
: m_desc( detail::ParamDesc::Kind{detail::ParamDesc::CompiledModel{blob_path}}
|
||||||
|
, device
|
||||||
|
, false /* is generic */
|
||||||
|
, std::tuple_size<typename Net::InArgs>::value
|
||||||
|
, std::tuple_size<typename Net::OutArgs>::value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies sequence of network input layers names for inference.
|
||||||
|
|
||||||
|
The function is used to associate cv::gapi::infer<> inputs with the model inputs.
|
||||||
|
Number of names has to match the number of network inputs as defined in G_API_NET().
|
||||||
|
In case a network has only single input layer, there is no need to specify name manually.
|
||||||
|
|
||||||
|
@param layer_names std::array<std::string, N> where N is the number of inputs
|
||||||
|
as defined in the @ref G_API_NET. Contains names of input layers.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgInputLayers(const std::vector<std::string> &layer_names) {
|
||||||
|
m_desc.input_names = layer_names;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies sequence of network output layers names for inference.
|
||||||
|
|
||||||
|
The function is used to associate cv::gapi::infer<> outputs with the model outputs.
|
||||||
|
Number of names has to match the number of network outputs as defined in G_API_NET().
|
||||||
|
In case a network has only single output layer, there is no need to specify name manually.
|
||||||
|
|
||||||
|
@param layer_names std::array<std::string, N> where N is the number of outputs
|
||||||
|
as defined in the @ref G_API_NET. Contains names of output layers.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgOutputLayers(const std::vector<std::string> &layer_names) {
|
||||||
|
m_desc.output_names = layer_names;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies OpenVINO plugin configuration.
|
||||||
|
|
||||||
|
The function is used to set configuration for OpenVINO plugin. Some parameters
|
||||||
|
can be different for each plugin. Please follow https://docs.openvinotoolkit.org/latest/index.html
|
||||||
|
to check information about specific plugin.
|
||||||
|
|
||||||
|
@param config Map of pairs: (config parameter name, config parameter value).
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgPluginConfig(const detail::ParamDesc::PluginConfigT &config) {
|
||||||
|
m_desc.config = config;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies tensor layout for an input layer.
|
||||||
|
|
||||||
|
The function is used to set tensor layout for an input layer.
|
||||||
|
|
||||||
|
@param layout Tensor layout ("NCHW", "NWHC", etc)
|
||||||
|
will be applied to all input layers.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgInputTensorLayout(std::string layout) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout")
|
||||||
|
.input_tensor_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
@param layout_map Map of pairs: name of corresponding input layer
|
||||||
|
and its tensor layout represented in std::string ("NCHW", "NHWC", etc)
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>&
|
||||||
|
cfgInputTensorLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout")
|
||||||
|
.input_tensor_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies model layout for an input layer.
|
||||||
|
|
||||||
|
The function is used to set model layout for an input layer.
|
||||||
|
|
||||||
|
@param layout Model layout ("NCHW", "NHWC", etc)
|
||||||
|
will be applied to all input layers.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgInputModelLayout(std::string layout) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout")
|
||||||
|
.input_model_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
@param layout_map Map of pairs: name of corresponding input layer
|
||||||
|
and its model layout ("NCHW", "NHWC", etc)
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>&
|
||||||
|
cfgInputModelLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout")
|
||||||
|
.input_model_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies tensor layout for an output layer.
|
||||||
|
|
||||||
|
The function is used to set tensor layout for an output layer.
|
||||||
|
|
||||||
|
@param layout Tensor layout ("NCHW", "NWHC", etc)
|
||||||
|
will be applied to all output layers.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgOutputTensorLayout(std::string layout) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout")
|
||||||
|
.output_tensor_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
@param layout_map Map of pairs: name of corresponding output layer
|
||||||
|
and its tensor layout represented in std::string ("NCHW", "NHWC", etc)
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>&
|
||||||
|
cfgOutputTensorLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout")
|
||||||
|
.output_tensor_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies model layout for an output layer.
|
||||||
|
|
||||||
|
The function is used to set model layout for an output layer.
|
||||||
|
|
||||||
|
@param layout Model layout ("NCHW", "NHWC", etc)
|
||||||
|
will be applied to all output layers.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgOutputModelLayout(std::string layout) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout")
|
||||||
|
.output_model_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
@param layout_map Map of pairs: name of corresponding output layer
|
||||||
|
and its model layout ("NCHW", "NHWC", etc)
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>&
|
||||||
|
cfgOutputModelLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout")
|
||||||
|
.output_model_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies tensor precision for an output layer.
|
||||||
|
|
||||||
|
The function is used to set tensor precision for an output layer..
|
||||||
|
|
||||||
|
@param precision Precision in OpenCV format (CV_8U, CV_32F, ...)
|
||||||
|
will be applied to all output layers.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgOutputTensorPrecision(int precision) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision")
|
||||||
|
.output_tensor_precision = precision;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
|
||||||
|
@param precision_map Map of pairs: name of corresponding output layer
|
||||||
|
and its precision in OpenCV format (CV_8U, CV_32F, ...)
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>&
|
||||||
|
cfgOutputTensorPrecision(detail::AttrMap<int> precision_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision")
|
||||||
|
.output_tensor_precision = std::move(precision_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies the new shape for input layers.
|
||||||
|
|
||||||
|
The function is used to set new shape for input layers.
|
||||||
|
|
||||||
|
@param new_shape New shape will be applied to all input layers.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>&
|
||||||
|
cfgReshape(std::vector<size_t> new_shape) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape")
|
||||||
|
.new_shapes = std::move(new_shape);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
|
||||||
|
@param new_shape_map Map of pairs: name of corresponding output layer
|
||||||
|
and its new shape.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>&
|
||||||
|
cfgReshape(detail::AttrMap<std::vector<size_t>> new_shape_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape")
|
||||||
|
.new_shapes = std::move(new_shape_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies number of asynchronous inference requests.
|
||||||
|
|
||||||
|
@param nireq Number of inference asynchronous requests.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgNumRequests(const size_t nireq) {
|
||||||
|
if (nireq == 0) {
|
||||||
|
cv::util::throw_error(
|
||||||
|
std::logic_error("Number of inference requests"
|
||||||
|
" must be greater than zero."));
|
||||||
|
}
|
||||||
|
m_desc.nireq = nireq;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies mean values for preprocessing.
|
||||||
|
*
|
||||||
|
The function is used to set mean values for input layer preprocessing.
|
||||||
|
|
||||||
|
@param mean_values Float vector contains mean values
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgMean(std::vector<float> mean_values) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values")
|
||||||
|
.mean_values = std::move(mean_values);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
|
||||||
|
@param mean_map Map of pairs: name of corresponding input layer
|
||||||
|
and its mean values.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgMean(detail::AttrMap<std::vector<float>> mean_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values")
|
||||||
|
.mean_values = std::move(mean_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies scale values for preprocessing.
|
||||||
|
*
|
||||||
|
The function is used to set scale values for input layer preprocessing.
|
||||||
|
|
||||||
|
@param scale_values Float vector contains scale values
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgScale(std::vector<float> scale_values) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values")
|
||||||
|
.scale_values = std::move(scale_values);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
|
||||||
|
@param scale_map Map of pairs: name of corresponding input layer
|
||||||
|
and its mean values.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgScale(detail::AttrMap<std::vector<float>> scale_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values")
|
||||||
|
.scale_values = std::move(scale_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Specifies resize interpolation algorithm.
|
||||||
|
*
|
||||||
|
The function is used to configure resize preprocessing for input layer.
|
||||||
|
|
||||||
|
@param interpolation Resize interpolation algorithm.
|
||||||
|
Supported algorithms: #INTER_NEAREST, #INTER_LINEAR, #INTER_CUBIC.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgResize(int interpolation) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing")
|
||||||
|
.interpolation = std::move(interpolation);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
|
||||||
|
@param interpolation Map of pairs: name of corresponding input layer
|
||||||
|
and its resize algorithm.
|
||||||
|
@return reference to this parameter structure.
|
||||||
|
*/
|
||||||
|
Params<Net>& cfgResize(detail::AttrMap<int> interpolation) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing")
|
||||||
|
.interpolation = std::move(interpolation);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BEGIN(G-API's network parametrization API)
|
||||||
|
GBackend backend() const { return cv::gapi::ov::backend(); }
|
||||||
|
std::string tag() const { return Net::tag(); }
|
||||||
|
cv::util::any params() const { return { m_desc }; }
|
||||||
|
// END(G-API's network parametrization API)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
detail::ParamDesc m_desc;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief This structure provides functions for generic network type that
|
||||||
|
* fill inference parameters.
|
||||||
|
* @see struct Generic
|
||||||
|
*/
|
||||||
|
template<>
|
||||||
|
class Params<cv::gapi::Generic> {
|
||||||
|
public:
|
||||||
|
/** @brief Class constructor.
|
||||||
|
|
||||||
|
Constructs Params based on model information and specifies default values for other
|
||||||
|
inference description parameters. Model is loaded and compiled using "OpenVINO Toolkit".
|
||||||
|
|
||||||
|
@param tag string tag of the network for which these parameters are intended.
|
||||||
|
@param model_path Path to a model.
|
||||||
|
@param bin_path Path to a data file.
|
||||||
|
For IR format (*.bin):
|
||||||
|
If path is empty, will try to read a bin file with the same name as xml.
|
||||||
|
If the bin file with the same name is not found, will load IR without weights.
|
||||||
|
For PDPD (*.pdmodel) and ONNX (*.onnx) formats bin_path isn't used.
|
||||||
|
@param device target device to use.
|
||||||
|
*/
|
||||||
|
Params(const std::string &tag,
|
||||||
|
const std::string &model_path,
|
||||||
|
const std::string &bin_path,
|
||||||
|
const std::string &device)
|
||||||
|
: m_tag(tag),
|
||||||
|
m_desc( detail::ParamDesc::Kind{detail::ParamDesc::Model{model_path, bin_path}}
|
||||||
|
, device
|
||||||
|
, true /* is generic */
|
||||||
|
, 0u
|
||||||
|
, 0u) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload
|
||||||
|
|
||||||
|
This constructor for pre-compiled networks. Model is imported from pre-compiled
|
||||||
|
blob.
|
||||||
|
|
||||||
|
@param tag string tag of the network for which these parameters are intended.
|
||||||
|
@param blob_path path to the compiled model (*.blob).
|
||||||
|
@param device target device to use.
|
||||||
|
*/
|
||||||
|
Params(const std::string &tag,
|
||||||
|
const std::string &blob_path,
|
||||||
|
const std::string &device)
|
||||||
|
: m_tag(tag),
|
||||||
|
m_desc( detail::ParamDesc::Kind{detail::ParamDesc::CompiledModel{blob_path}}
|
||||||
|
, device
|
||||||
|
, true /* is generic */
|
||||||
|
, 0u
|
||||||
|
, 0u) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ov::Params::cfgPluginConfig. */
|
||||||
|
Params& cfgPluginConfig(const detail::ParamDesc::PluginConfigT &config) {
|
||||||
|
m_desc.config = config;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ov::Params::cfgInputTensorLayout. */
|
||||||
|
Params& cfgInputTensorLayout(std::string layout) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout")
|
||||||
|
.input_tensor_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params&
|
||||||
|
cfgInputTensorLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "input tensor layout")
|
||||||
|
.input_tensor_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ov::Params::cfgInputModelLayout. */
|
||||||
|
Params& cfgInputModelLayout(std::string layout) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout")
|
||||||
|
.input_model_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params&
|
||||||
|
cfgInputModelLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "input model layout")
|
||||||
|
.input_model_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ov::Params::cfgOutputTensorLayout. */
|
||||||
|
Params& cfgOutputTensorLayout(std::string layout) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout")
|
||||||
|
.output_tensor_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params&
|
||||||
|
cfgOutputTensorLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor layout")
|
||||||
|
.output_tensor_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ov::Params::cfgOutputModelLayout. */
|
||||||
|
Params& cfgOutputModelLayout(std::string layout) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout")
|
||||||
|
.output_model_layout = std::move(layout);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params&
|
||||||
|
cfgOutputModelLayout(detail::AttrMap<std::string> layout_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output model layout")
|
||||||
|
.output_model_layout = std::move(layout_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ov::Params::cfgOutputTensorPrecision. */
|
||||||
|
Params& cfgOutputTensorPrecision(int precision) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision")
|
||||||
|
.output_tensor_precision = precision;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params&
|
||||||
|
cfgOutputTensorPrecision(detail::AttrMap<int> precision_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "output tensor precision")
|
||||||
|
.output_tensor_precision = std::move(precision_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ov::Params::cfgReshape. */
|
||||||
|
Params& cfgReshape(std::vector<size_t> new_shape) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape")
|
||||||
|
.new_shapes = std::move(new_shape);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params&
|
||||||
|
cfgReshape(detail::AttrMap<std::vector<size_t>> new_shape_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "reshape")
|
||||||
|
.new_shapes = std::move(new_shape_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ov::Params::cfgNumRequests. */
|
||||||
|
Params& cfgNumRequests(const size_t nireq) {
|
||||||
|
if (nireq == 0) {
|
||||||
|
cv::util::throw_error(
|
||||||
|
std::logic_error("Number of inference requests"
|
||||||
|
" must be greater than zero."));
|
||||||
|
}
|
||||||
|
m_desc.nireq = nireq;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ov::Params::cfgMean. */
|
||||||
|
Params& cfgMean(std::vector<float> mean_values) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values")
|
||||||
|
.mean_values = std::move(mean_values);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params& cfgMean(detail::AttrMap<std::vector<float>> mean_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "mean values")
|
||||||
|
.mean_values = std::move(mean_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ov::Params::cfgScale. */
|
||||||
|
Params& cfgScale(std::vector<float> scale_values) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values")
|
||||||
|
.scale_values = std::move(scale_values);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params& cfgScale(detail::AttrMap<std::vector<float>> scale_map) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "scale values")
|
||||||
|
.scale_values = std::move(scale_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see ov::Params::cfgResize. */
|
||||||
|
Params& cfgResize(int interpolation) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing")
|
||||||
|
.interpolation = std::move(interpolation);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @overload */
|
||||||
|
Params& cfgResize(detail::AttrMap<int> interpolation) {
|
||||||
|
detail::getModelToSetAttrOrThrow(m_desc.kind, "resize preprocessing")
|
||||||
|
.interpolation = std::move(interpolation);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BEGIN(G-API's network parametrization API)
|
||||||
|
GBackend backend() const { return cv::gapi::ov::backend(); }
|
||||||
|
std::string tag() const { return m_tag; }
|
||||||
|
cv::util::any params() const { return { m_desc }; }
|
||||||
|
// END(G-API's network parametrization API)
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string m_tag;
|
||||||
|
detail::ParamDesc m_desc;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ov
|
||||||
|
} // namespace gapi
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif // OPENCV_GAPI_INFER_OV_HPP
|
@ -22,6 +22,7 @@
|
|||||||
* because of this file.
|
* because of this file.
|
||||||
*/
|
*/
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include <opencv2/videoio.hpp>
|
#include <opencv2/videoio.hpp>
|
||||||
#include <opencv2/gapi/garg.hpp>
|
#include <opencv2/gapi/garg.hpp>
|
||||||
@ -47,8 +48,16 @@ namespace wip {
|
|||||||
class GCaptureSource: public IStreamSource
|
class GCaptureSource: public IStreamSource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit GCaptureSource(int id) : cap(id) { prep(); }
|
explicit GCaptureSource(int id, const std::map<int, double> &properties = {})
|
||||||
explicit GCaptureSource(const std::string &path) : cap(path) { prep(); }
|
: cap(id) { prep(properties); }
|
||||||
|
|
||||||
|
explicit GCaptureSource(const std::string &path,
|
||||||
|
const std::map<int, double> &properties = {})
|
||||||
|
: cap(path) { prep(properties); }
|
||||||
|
|
||||||
|
void set(int propid, double value) {
|
||||||
|
cap.set(propid, value);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Add more constructor overloads to make it
|
// TODO: Add more constructor overloads to make it
|
||||||
// fully compatible with VideoCapture's interface.
|
// fully compatible with VideoCapture's interface.
|
||||||
@ -59,8 +68,12 @@ protected:
|
|||||||
bool first_pulled = false;
|
bool first_pulled = false;
|
||||||
int64_t counter = 0;
|
int64_t counter = 0;
|
||||||
|
|
||||||
void prep()
|
void prep(const std::map<int, double> &properties)
|
||||||
{
|
{
|
||||||
|
for (const auto &it : properties) {
|
||||||
|
cap.set(it.first, it.second);
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare first frame to report its meta to engine
|
// Prepare first frame to report its meta to engine
|
||||||
// when needed
|
// when needed
|
||||||
GAPI_Assert(first.empty());
|
GAPI_Assert(first.empty());
|
||||||
@ -114,15 +127,19 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// NB: Overload for using from python
|
// NB: Overload for using from python
|
||||||
GAPI_EXPORTS_W cv::Ptr<IStreamSource> inline make_capture_src(const std::string& path)
|
GAPI_EXPORTS_W cv::Ptr<IStreamSource>
|
||||||
|
inline make_capture_src(const std::string& path,
|
||||||
|
const std::map<int, double>& properties = {})
|
||||||
{
|
{
|
||||||
return make_src<GCaptureSource>(path);
|
return make_src<GCaptureSource>(path, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NB: Overload for using from python
|
// NB: Overload for using from python
|
||||||
GAPI_EXPORTS_W cv::Ptr<IStreamSource> inline make_capture_src(const int id)
|
GAPI_EXPORTS_W cv::Ptr<IStreamSource>
|
||||||
|
inline make_capture_src(const int id,
|
||||||
|
const std::map<int, double>& properties = {})
|
||||||
{
|
{
|
||||||
return make_src<GCaptureSource>(id);
|
return make_src<GCaptureSource>(id, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace wip
|
} // namespace wip
|
||||||
|
@ -15,6 +15,7 @@ using gapi_GKernelPackage = cv::GKernelPackage;
|
|||||||
using gapi_GNetPackage = cv::gapi::GNetPackage;
|
using gapi_GNetPackage = cv::gapi::GNetPackage;
|
||||||
using gapi_ie_PyParams = cv::gapi::ie::PyParams;
|
using gapi_ie_PyParams = cv::gapi::ie::PyParams;
|
||||||
using gapi_onnx_PyParams = cv::gapi::onnx::PyParams;
|
using gapi_onnx_PyParams = cv::gapi::onnx::PyParams;
|
||||||
|
using gapi_ov_PyParams = cv::gapi::ov::PyParams;
|
||||||
using gapi_wip_IStreamSource_Ptr = cv::Ptr<cv::gapi::wip::IStreamSource>;
|
using gapi_wip_IStreamSource_Ptr = cv::Ptr<cv::gapi::wip::IStreamSource>;
|
||||||
using detail_ExtractArgsCallback = cv::detail::ExtractArgsCallback;
|
using detail_ExtractArgsCallback = cv::detail::ExtractArgsCallback;
|
||||||
using detail_ExtractMetaCallback = cv::detail::ExtractMetaCallback;
|
using detail_ExtractMetaCallback = cv::detail::ExtractMetaCallback;
|
||||||
@ -22,6 +23,12 @@ using vector_GNetParam = std::vector<cv::gapi::GNetParam>;
|
|||||||
using vector_GMat = std::vector<cv::GMat>;
|
using vector_GMat = std::vector<cv::GMat>;
|
||||||
using gapi_streaming_queue_capacity = cv::gapi::streaming::queue_capacity;
|
using gapi_streaming_queue_capacity = cv::gapi::streaming::queue_capacity;
|
||||||
using GStreamerSource_OutputType = cv::gapi::wip::GStreamerSource::OutputType;
|
using GStreamerSource_OutputType = cv::gapi::wip::GStreamerSource::OutputType;
|
||||||
|
using map_string_and_int = std::map<std::string, int>;
|
||||||
|
using map_string_and_string = std::map<std::string, std::string>;
|
||||||
|
using map_string_and_string = std::map<std::string, std::string>;
|
||||||
|
using map_string_and_vector_size_t = std::map<std::string, std::vector<size_t>>;
|
||||||
|
using map_string_and_vector_float = std::map<std::string, std::vector<float>>;
|
||||||
|
using map_int_and_double = std::map<int, double>;
|
||||||
|
|
||||||
// NB: Python wrapper generate T_U for T<U>
|
// NB: Python wrapper generate T_U for T<U>
|
||||||
// This behavior is only observed for inputs
|
// This behavior is only observed for inputs
|
||||||
|
@ -80,5 +80,6 @@ namespace detail
|
|||||||
{
|
{
|
||||||
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::ie::PyParams params);
|
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::ie::PyParams params);
|
||||||
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::onnx::PyParams params);
|
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::onnx::PyParams params);
|
||||||
|
gapi::GNetParam GAPI_EXPORTS_W strip(gapi::ov::PyParams params);
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace cv
|
} // namespace cv
|
||||||
|
238
modules/gapi/misc/python/test/test_gapi_infer_ov.py
Normal file
238
modules/gapi/misc/python/test/test_gapi_infer_ov.py
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import cv2 as cv
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from tests_common import NewOpenCVTests
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
if sys.version_info[:2] < (3, 0):
|
||||||
|
raise unittest.SkipTest('Python 2.x is not supported')
|
||||||
|
|
||||||
|
|
||||||
|
openvino_is_available = True
|
||||||
|
try:
|
||||||
|
from openvino.runtime import Core, Type, Layout, PartialShape
|
||||||
|
from openvino.preprocess import ResizeAlgorithm, PrePostProcessor
|
||||||
|
except ImportError:
|
||||||
|
openvino_is_available = False
|
||||||
|
|
||||||
|
|
||||||
|
def skip_if_openvino_not_available():
|
||||||
|
if not openvino_is_available:
|
||||||
|
raise unittest.SkipTest("OpenVINO isn't available from python.")
|
||||||
|
|
||||||
|
|
||||||
|
class AgeGenderOV:
|
||||||
|
def __init__(self, model_path, bin_path, device):
|
||||||
|
self.device = device
|
||||||
|
self.core = Core()
|
||||||
|
self.model = self.core.read_model(model_path, bin_path)
|
||||||
|
|
||||||
|
|
||||||
|
def reshape(self, new_shape):
|
||||||
|
self.model.reshape(new_shape)
|
||||||
|
|
||||||
|
|
||||||
|
def cfgPrePostProcessing(self, pp_callback):
|
||||||
|
ppp = PrePostProcessor(self.model)
|
||||||
|
pp_callback(ppp)
|
||||||
|
self.model = ppp.build()
|
||||||
|
|
||||||
|
|
||||||
|
def apply(self, in_data):
|
||||||
|
compiled_model = self.core.compile_model(self.model, self.device)
|
||||||
|
infer_request = compiled_model.create_infer_request()
|
||||||
|
results = infer_request.infer(in_data)
|
||||||
|
ov_age = results['age_conv3'].squeeze()
|
||||||
|
ov_gender = results['prob'].squeeze()
|
||||||
|
return ov_age, ov_gender
|
||||||
|
|
||||||
|
|
||||||
|
class AgeGenderGAPI:
|
||||||
|
tag = 'age-gender-net'
|
||||||
|
|
||||||
|
def __init__(self, model_path, bin_path, device):
|
||||||
|
g_in = cv.GMat()
|
||||||
|
inputs = cv.GInferInputs()
|
||||||
|
inputs.setInput('data', g_in)
|
||||||
|
# TODO: It'd be nice to pass dict instead.
|
||||||
|
# E.g cv.gapi.infer("net", {'data': g_in})
|
||||||
|
outputs = cv.gapi.infer(AgeGenderGAPI.tag, inputs)
|
||||||
|
age_g = outputs.at("age_conv3")
|
||||||
|
gender_g = outputs.at("prob")
|
||||||
|
|
||||||
|
self.comp = cv.GComputation(cv.GIn(g_in), cv.GOut(age_g, gender_g))
|
||||||
|
self.pp = cv.gapi.ov.params(AgeGenderGAPI.tag, \
|
||||||
|
model_path, bin_path, device)
|
||||||
|
|
||||||
|
|
||||||
|
def apply(self, in_data):
|
||||||
|
compile_args = cv.gapi.compile_args(cv.gapi.networks(self.pp))
|
||||||
|
gapi_age, gapi_gender = self.comp.apply(cv.gin(in_data), compile_args)
|
||||||
|
gapi_gender = gapi_gender.squeeze()
|
||||||
|
gapi_age = gapi_age.squeeze()
|
||||||
|
return gapi_age, gapi_gender
|
||||||
|
|
||||||
|
|
||||||
|
class test_gapi_infer_ov(NewOpenCVTests):
|
||||||
|
|
||||||
|
def test_age_gender_infer_image(self):
|
||||||
|
skip_if_openvino_not_available()
|
||||||
|
|
||||||
|
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
|
||||||
|
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
|
||||||
|
bin_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
|
||||||
|
device_id = 'CPU'
|
||||||
|
|
||||||
|
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
|
||||||
|
img = cv.imread(img_path)
|
||||||
|
|
||||||
|
# OpenVINO
|
||||||
|
def preproc(ppp):
|
||||||
|
ppp.input().model().set_layout(Layout("NCHW"))
|
||||||
|
ppp.input().tensor().set_element_type(Type.u8) \
|
||||||
|
.set_spatial_static_shape(img.shape[0], img.shape[1]) \
|
||||||
|
.set_layout(Layout("NHWC"))
|
||||||
|
ppp.input().preprocess().resize(ResizeAlgorithm.RESIZE_LINEAR)
|
||||||
|
|
||||||
|
|
||||||
|
ref = AgeGenderOV(model_path, bin_path, device_id)
|
||||||
|
ref.cfgPrePostProcessing(preproc)
|
||||||
|
ov_age, ov_gender = ref.apply(np.expand_dims(img, 0))
|
||||||
|
|
||||||
|
# OpenCV G-API (No preproc required)
|
||||||
|
comp = AgeGenderGAPI(model_path, bin_path, device_id)
|
||||||
|
gapi_age, gapi_gender = comp.apply(img)
|
||||||
|
|
||||||
|
# Check
|
||||||
|
self.assertEqual(0.0, cv.norm(ov_gender, gapi_gender, cv.NORM_INF))
|
||||||
|
self.assertEqual(0.0, cv.norm(ov_age, gapi_age, cv.NORM_INF))
|
||||||
|
|
||||||
|
|
||||||
|
def test_age_gender_infer_tensor(self):
|
||||||
|
skip_if_openvino_not_available()
|
||||||
|
|
||||||
|
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
|
||||||
|
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
|
||||||
|
bin_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
|
||||||
|
device_id = 'CPU'
|
||||||
|
|
||||||
|
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
|
||||||
|
img = cv.imread(img_path)
|
||||||
|
|
||||||
|
# Prepare data manually
|
||||||
|
tensor = cv.resize(img, (62, 62)).astype(np.float32)
|
||||||
|
tensor = np.transpose(tensor, (2, 0, 1))
|
||||||
|
tensor = np.expand_dims(tensor, 0)
|
||||||
|
|
||||||
|
# OpenVINO (No preproce required)
|
||||||
|
ref = AgeGenderOV(model_path, bin_path, device_id)
|
||||||
|
ov_age, ov_gender = ref.apply(tensor)
|
||||||
|
|
||||||
|
# OpenCV G-API (No preproc required)
|
||||||
|
comp = AgeGenderGAPI(model_path, bin_path, device_id)
|
||||||
|
gapi_age, gapi_gender = comp.apply(tensor)
|
||||||
|
|
||||||
|
# Check
|
||||||
|
self.assertEqual(0.0, cv.norm(ov_gender, gapi_gender, cv.NORM_INF))
|
||||||
|
self.assertEqual(0.0, cv.norm(ov_age, gapi_age, cv.NORM_INF))
|
||||||
|
|
||||||
|
|
||||||
|
def test_age_gender_infer_batch(self):
|
||||||
|
skip_if_openvino_not_available()
|
||||||
|
|
||||||
|
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
|
||||||
|
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
|
||||||
|
bin_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
|
||||||
|
device_id = 'CPU'
|
||||||
|
|
||||||
|
img_path1 = self.find_file('cv/face/david1.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
|
||||||
|
img_path2 = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
|
||||||
|
img1 = cv.imread(img_path1)
|
||||||
|
img2 = cv.imread(img_path2)
|
||||||
|
# img1 and img2 have the same size
|
||||||
|
batch_img = np.array([img1, img2])
|
||||||
|
|
||||||
|
# OpenVINO
|
||||||
|
def preproc(ppp):
|
||||||
|
ppp.input().model().set_layout(Layout("NCHW"))
|
||||||
|
ppp.input().tensor().set_element_type(Type.u8) \
|
||||||
|
.set_spatial_static_shape(img1.shape[0], img2.shape[1]) \
|
||||||
|
.set_layout(Layout("NHWC"))
|
||||||
|
ppp.input().preprocess().resize(ResizeAlgorithm.RESIZE_LINEAR)
|
||||||
|
|
||||||
|
|
||||||
|
ref = AgeGenderOV(model_path, bin_path, device_id)
|
||||||
|
ref.reshape(PartialShape([2, 3, 62, 62]))
|
||||||
|
ref.cfgPrePostProcessing(preproc)
|
||||||
|
ov_age, ov_gender = ref.apply(batch_img)
|
||||||
|
|
||||||
|
# OpenCV G-API
|
||||||
|
comp = AgeGenderGAPI(model_path, bin_path, device_id)
|
||||||
|
comp.pp.cfgReshape([2, 3, 62, 62]) \
|
||||||
|
.cfgInputModelLayout("NCHW") \
|
||||||
|
.cfgInputTensorLayout("NHWC") \
|
||||||
|
.cfgResize(cv.INTER_LINEAR)
|
||||||
|
gapi_age, gapi_gender = comp.apply(batch_img)
|
||||||
|
|
||||||
|
# Check
|
||||||
|
self.assertEqual(0.0, cv.norm(ov_gender, gapi_gender, cv.NORM_INF))
|
||||||
|
self.assertEqual(0.0, cv.norm(ov_age, gapi_age, cv.NORM_INF))
|
||||||
|
|
||||||
|
|
||||||
|
def test_age_gender_infer_planar(self):
|
||||||
|
skip_if_openvino_not_available()
|
||||||
|
|
||||||
|
root_path = '/omz_intel_models/intel/age-gender-recognition-retail-0013/FP32/age-gender-recognition-retail-0013'
|
||||||
|
model_path = self.find_file(root_path + '.xml', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
|
||||||
|
bin_path = self.find_file(root_path + '.bin', [os.environ.get('OPENCV_DNN_TEST_DATA_PATH')])
|
||||||
|
device_id = 'CPU'
|
||||||
|
|
||||||
|
img_path = self.find_file('cv/face/david2.jpg', [os.environ.get('OPENCV_TEST_DATA_PATH')])
|
||||||
|
img = cv.imread(img_path)
|
||||||
|
planar_img = np.transpose(img, (2, 0, 1))
|
||||||
|
planar_img = np.expand_dims(planar_img, 0)
|
||||||
|
|
||||||
|
# OpenVINO
|
||||||
|
def preproc(ppp):
|
||||||
|
ppp.input().tensor().set_element_type(Type.u8) \
|
||||||
|
.set_spatial_static_shape(img.shape[0], img.shape[1])
|
||||||
|
ppp.input().preprocess().resize(ResizeAlgorithm.RESIZE_LINEAR)
|
||||||
|
|
||||||
|
|
||||||
|
ref = AgeGenderOV(model_path, bin_path, device_id)
|
||||||
|
ref.cfgPrePostProcessing(preproc)
|
||||||
|
ov_age, ov_gender = ref.apply(planar_img)
|
||||||
|
|
||||||
|
# OpenCV G-API
|
||||||
|
comp = AgeGenderGAPI(model_path, bin_path, device_id)
|
||||||
|
comp.pp.cfgResize(cv.INTER_LINEAR)
|
||||||
|
gapi_age, gapi_gender = comp.apply(planar_img)
|
||||||
|
|
||||||
|
# Check
|
||||||
|
self.assertEqual(0.0, cv.norm(ov_gender, gapi_gender, cv.NORM_INF))
|
||||||
|
self.assertEqual(0.0, cv.norm(ov_age, gapi_age, cv.NORM_INF))
|
||||||
|
|
||||||
|
|
||||||
|
except unittest.SkipTest as e:
|
||||||
|
|
||||||
|
message = str(e)
|
||||||
|
|
||||||
|
class TestSkip(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.skipTest('Skip tests: ' + message)
|
||||||
|
|
||||||
|
def test_skip():
|
||||||
|
pass
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
NewOpenCVTests.bootstrap()
|
@ -62,7 +62,7 @@ namespace opencv_test
|
|||||||
class InRangePerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, cv::GCompileArgs>> {};
|
class InRangePerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, cv::GCompileArgs>> {};
|
||||||
class Split3PerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {};
|
class Split3PerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {};
|
||||||
class Split4PerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {};
|
class Split4PerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {};
|
||||||
class Merge3PerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {};
|
class Merge3PerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, cv::GCompileArgs>> {};
|
||||||
class Merge4PerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {};
|
class Merge4PerfTest : public TestPerfParams<tuple<compare_f, cv::Size, cv::GCompileArgs>> {};
|
||||||
class RemapPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, cv::GCompileArgs>> {};
|
class RemapPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, cv::GCompileArgs>> {};
|
||||||
class FlipPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, int, cv::GCompileArgs>> {};
|
class FlipPerfTest : public TestPerfParams<tuple<compare_f, cv::Size, MatType, int, cv::GCompileArgs>> {};
|
||||||
|
@ -1577,11 +1577,12 @@ PERF_TEST_P_(Merge3PerfTest, TestPerformance)
|
|||||||
{
|
{
|
||||||
compare_f cmpF;
|
compare_f cmpF;
|
||||||
cv::Size sz;
|
cv::Size sz;
|
||||||
|
MatType type = -1;
|
||||||
cv::GCompileArgs compile_args;
|
cv::GCompileArgs compile_args;
|
||||||
std::tie(cmpF, sz, compile_args) = GetParam();
|
std::tie(cmpF, sz, type, compile_args) = GetParam();
|
||||||
|
|
||||||
initMatsRandU(CV_8UC1, sz, CV_8UC3);
|
initMatsRandU(type, sz, CV_MAKETYPE(type, 3));
|
||||||
cv::Mat in_mat3(sz, CV_8UC1);
|
cv::Mat in_mat3(sz, type);
|
||||||
cv::Scalar mean = cv::Scalar::all(127);
|
cv::Scalar mean = cv::Scalar::all(127);
|
||||||
cv::Scalar stddev = cv::Scalar::all(40.f);
|
cv::Scalar stddev = cv::Scalar::all(40.f);
|
||||||
cv::randn(in_mat3, mean, stddev);
|
cv::randn(in_mat3, mean, stddev);
|
||||||
|
@ -252,6 +252,7 @@ INSTANTIATE_TEST_CASE_P(Split4PerfTestCPU, Split4PerfTest,
|
|||||||
INSTANTIATE_TEST_CASE_P(Merge3PerfTestCPU, Merge3PerfTest,
|
INSTANTIATE_TEST_CASE_P(Merge3PerfTestCPU, Merge3PerfTest,
|
||||||
Combine(Values(AbsExact().to_compare_f()),
|
Combine(Values(AbsExact().to_compare_f()),
|
||||||
Values(szSmall128, szVGA, sz720p, sz1080p),
|
Values(szSmall128, szVGA, sz720p, sz1080p),
|
||||||
|
Values(CV_8U),
|
||||||
Values(cv::compile_args(CORE_CPU))));
|
Values(cv::compile_args(CORE_CPU))));
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(Merge4PerfTestCPU, Merge4PerfTest,
|
INSTANTIATE_TEST_CASE_P(Merge4PerfTestCPU, Merge4PerfTest,
|
||||||
|
@ -253,6 +253,7 @@ INSTANTIATE_TEST_CASE_P(Split4PerfTestFluid, Split4PerfTest,
|
|||||||
INSTANTIATE_TEST_CASE_P(Merge3PerfTestFluid, Merge3PerfTest,
|
INSTANTIATE_TEST_CASE_P(Merge3PerfTestFluid, Merge3PerfTest,
|
||||||
Combine(Values(AbsExact().to_compare_f()),
|
Combine(Values(AbsExact().to_compare_f()),
|
||||||
Values(szSmall128, szVGA, sz720p, sz1080p),
|
Values(szSmall128, szVGA, sz720p, sz1080p),
|
||||||
|
Values(CV_8U, CV_16S, CV_16U, CV_32F),
|
||||||
Values(cv::compile_args(CORE_FLUID))));
|
Values(cv::compile_args(CORE_FLUID))));
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(Merge4PerfTestFluid, Merge4PerfTest,
|
INSTANTIATE_TEST_CASE_P(Merge4PerfTestFluid, Merge4PerfTest,
|
||||||
|
@ -242,6 +242,7 @@ INSTANTIATE_TEST_CASE_P(Split4PerfTestGPU, Split4PerfTest,
|
|||||||
INSTANTIATE_TEST_CASE_P(Merge3PerfTestGPU, Merge3PerfTest,
|
INSTANTIATE_TEST_CASE_P(Merge3PerfTestGPU, Merge3PerfTest,
|
||||||
Combine(Values(AbsExact().to_compare_f()),
|
Combine(Values(AbsExact().to_compare_f()),
|
||||||
Values( szSmall128, szVGA, sz720p, sz1080p ),
|
Values( szSmall128, szVGA, sz720p, sz1080p ),
|
||||||
|
Values(CV_8U),
|
||||||
Values(cv::compile_args(CORE_GPU))));
|
Values(cv::compile_args(CORE_GPU))));
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(Merge4PerfTestGPU, Merge4PerfTest,
|
INSTANTIATE_TEST_CASE_P(Merge4PerfTestGPU, Merge4PerfTest,
|
||||||
|
@ -5,34 +5,41 @@
|
|||||||
#include <opencv2/gapi/operators.hpp>
|
#include <opencv2/gapi/operators.hpp>
|
||||||
#include <opencv2/highgui.hpp>
|
#include <opencv2/highgui.hpp>
|
||||||
|
|
||||||
|
#include <opencv2/gapi/streaming/desync.hpp>
|
||||||
|
#include <opencv2/gapi/streaming/format.hpp>
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
const std::string keys =
|
const std::string keys =
|
||||||
"{ h help | | Print this help message }"
|
"{ h help | | Print this help message }"
|
||||||
|
"{ desync | false | Desynchronize inference }"
|
||||||
"{ input | | Path to the input video file }"
|
"{ input | | Path to the input video file }"
|
||||||
"{ output | | Path to the output video file }"
|
"{ output | | Path to the output video file }"
|
||||||
"{ ssm | semantic-segmentation-adas-0001.xml | Path to OpenVINO IE semantic segmentation model (.xml) }";
|
"{ ssm | semantic-segmentation-adas-0001.xml | Path to OpenVINO IE semantic segmentation model (.xml) }";
|
||||||
|
|
||||||
// 20 colors for 20 classes of semantic-segmentation-adas-0001
|
// 20 colors for 20 classes of semantic-segmentation-adas-0001
|
||||||
const std::vector<cv::Vec3b> colors = {
|
static std::vector<cv::Vec3b> colors = {
|
||||||
{ 128, 64, 128 },
|
{ 0, 0, 0 },
|
||||||
{ 232, 35, 244 },
|
{ 0, 0, 128 },
|
||||||
{ 70, 70, 70 },
|
{ 0, 128, 0 },
|
||||||
{ 156, 102, 102 },
|
{ 0, 128, 128 },
|
||||||
{ 153, 153, 190 },
|
{ 128, 0, 0 },
|
||||||
{ 153, 153, 153 },
|
{ 128, 0, 128 },
|
||||||
{ 30, 170, 250 },
|
{ 128, 128, 0 },
|
||||||
{ 0, 220, 220 },
|
{ 128, 128, 128 },
|
||||||
{ 35, 142, 107 },
|
{ 0, 0, 64 },
|
||||||
{ 152, 251, 152 },
|
{ 0, 0, 192 },
|
||||||
{ 180, 130, 70 },
|
{ 0, 128, 64 },
|
||||||
{ 60, 20, 220 },
|
{ 0, 128, 192 },
|
||||||
{ 0, 0, 255 },
|
{ 128, 0, 64 },
|
||||||
{ 142, 0, 0 },
|
{ 128, 0, 192 },
|
||||||
{ 70, 0, 0 },
|
{ 128, 128, 64 },
|
||||||
{ 100, 60, 0 },
|
{ 128, 128, 192 },
|
||||||
{ 90, 0, 0 },
|
{ 0, 64, 0 },
|
||||||
{ 230, 0, 0 },
|
{ 0, 64, 128 },
|
||||||
{ 32, 11, 119 },
|
{ 0, 192, 0 },
|
||||||
{ 0, 74, 111 },
|
{ 0, 192, 128 },
|
||||||
|
{ 128, 64, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -43,12 +50,23 @@ std::string get_weights_path(const std::string &model_path) {
|
|||||||
|
|
||||||
auto ext = model_path.substr(sz - EXT_LEN);
|
auto ext = model_path.substr(sz - EXT_LEN);
|
||||||
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){
|
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){
|
||||||
return static_cast<unsigned char>(std::tolower(c));
|
return static_cast<unsigned char>(std::tolower(c));
|
||||||
});
|
});
|
||||||
CV_Assert(ext == ".xml");
|
CV_Assert(ext == ".xml");
|
||||||
return model_path.substr(0u, sz - EXT_LEN) + ".bin";
|
return model_path.substr(0u, sz - EXT_LEN) + ".bin";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isNumber(const std::string &str) {
|
||||||
|
return !str.empty() && std::all_of(str.begin(), str.end(),
|
||||||
|
[](unsigned char ch) { return std::isdigit(ch); });
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string toStr(double value) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::fixed << std::setprecision(1) << value;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
void classesToColors(const cv::Mat &out_blob,
|
void classesToColors(const cv::Mat &out_blob,
|
||||||
cv::Mat &mask_img) {
|
cv::Mat &mask_img) {
|
||||||
const int H = out_blob.size[0];
|
const int H = out_blob.size[0];
|
||||||
@ -97,6 +115,25 @@ void probsToClasses(const cv::Mat& probs, cv::Mat& classes) {
|
|||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
namespace vis {
|
||||||
|
|
||||||
|
static void putText(cv::Mat& mat, const cv::Point &position, const std::string &message) {
|
||||||
|
auto fontFace = cv::FONT_HERSHEY_COMPLEX;
|
||||||
|
int thickness = 2;
|
||||||
|
cv::Scalar color = {200, 10, 10};
|
||||||
|
double fontScale = 0.65;
|
||||||
|
|
||||||
|
cv::putText(mat, message, position, fontFace,
|
||||||
|
fontScale, cv::Scalar(255, 255, 255), thickness + 1);
|
||||||
|
cv::putText(mat, message, position, fontFace, fontScale, color, thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawResults(cv::Mat &img, const cv::Mat &color_mask) {
|
||||||
|
img = img / 2 + color_mask / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vis
|
||||||
|
|
||||||
namespace custom {
|
namespace custom {
|
||||||
G_API_OP(PostProcessing, <cv::GMat(cv::GMat, cv::GMat)>, "sample.custom.post_processing") {
|
G_API_OP(PostProcessing, <cv::GMat(cv::GMat, cv::GMat)>, "sample.custom.post_processing") {
|
||||||
static cv::GMatDesc outMeta(const cv::GMatDesc &in, const cv::GMatDesc &) {
|
static cv::GMatDesc outMeta(const cv::GMatDesc &in, const cv::GMatDesc &) {
|
||||||
@ -106,19 +143,34 @@ G_API_OP(PostProcessing, <cv::GMat(cv::GMat, cv::GMat)>, "sample.custom.post_pro
|
|||||||
|
|
||||||
GAPI_OCV_KERNEL(OCVPostProcessing, PostProcessing) {
|
GAPI_OCV_KERNEL(OCVPostProcessing, PostProcessing) {
|
||||||
static void run(const cv::Mat &in, const cv::Mat &out_blob, cv::Mat &out) {
|
static void run(const cv::Mat &in, const cv::Mat &out_blob, cv::Mat &out) {
|
||||||
|
int C = -1, H = -1, W = -1;
|
||||||
|
if (out_blob.size.dims() == 4u) {
|
||||||
|
C = 1; H = 2, W = 3;
|
||||||
|
} else if (out_blob.size.dims() == 3u) {
|
||||||
|
C = 0; H = 1, W = 2;
|
||||||
|
} else {
|
||||||
|
throw std::logic_error(
|
||||||
|
"Number of dimmensions for model output must be 3 or 4!");
|
||||||
|
}
|
||||||
cv::Mat classes;
|
cv::Mat classes;
|
||||||
// NB: If output has more than single plane, it contains probabilities
|
// NB: If output has more than single plane, it contains probabilities
|
||||||
// otherwise class id.
|
// otherwise class id.
|
||||||
if (out_blob.size[1] > 1) {
|
if (out_blob.size[C] > 1) {
|
||||||
probsToClasses(out_blob, classes);
|
probsToClasses(out_blob, classes);
|
||||||
} else {
|
} else {
|
||||||
out_blob.convertTo(classes, CV_8UC1);
|
if (out_blob.depth() != CV_32S) {
|
||||||
classes = classes.reshape(1, out_blob.size[2]);
|
throw std::logic_error(
|
||||||
|
"Single channel output must have integer precision!");
|
||||||
|
}
|
||||||
|
cv::Mat view(out_blob.size[H], // cols
|
||||||
|
out_blob.size[W], // rows
|
||||||
|
CV_32SC1,
|
||||||
|
out_blob.data);
|
||||||
|
view.convertTo(classes, CV_8UC1);
|
||||||
}
|
}
|
||||||
|
|
||||||
cv::Mat mask_img;
|
cv::Mat mask_img;
|
||||||
classesToColors(classes, mask_img);
|
classesToColors(classes, mask_img);
|
||||||
cv::resize(mask_img, out, in.size());
|
cv::resize(mask_img, out, in.size(), 0, 0, cv::INTER_NEAREST);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace custom
|
} // namespace custom
|
||||||
@ -134,6 +186,7 @@ int main(int argc, char *argv[]) {
|
|||||||
const std::string input = cmd.get<std::string>("input");
|
const std::string input = cmd.get<std::string>("input");
|
||||||
const std::string output = cmd.get<std::string>("output");
|
const std::string output = cmd.get<std::string>("output");
|
||||||
const auto model_path = cmd.get<std::string>("ssm");
|
const auto model_path = cmd.get<std::string>("ssm");
|
||||||
|
const bool desync = cmd.get<bool>("desync");
|
||||||
const auto weights_path = get_weights_path(model_path);
|
const auto weights_path = get_weights_path(model_path);
|
||||||
const auto device = "CPU";
|
const auto device = "CPU";
|
||||||
G_API_NET(SemSegmNet, <cv::GMat(cv::GMat)>, "semantic-segmentation");
|
G_API_NET(SemSegmNet, <cv::GMat(cv::GMat)>, "semantic-segmentation");
|
||||||
@ -145,40 +198,87 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
// Now build the graph
|
// Now build the graph
|
||||||
cv::GMat in;
|
cv::GMat in;
|
||||||
cv::GMat out_blob = cv::gapi::infer<SemSegmNet>(in);
|
cv::GMat bgr = cv::gapi::copy(in);
|
||||||
cv::GMat post_proc_out = custom::PostProcessing::on(in, out_blob);
|
cv::GMat frame = desync ? cv::gapi::streaming::desync(bgr) : bgr;
|
||||||
cv::GMat blending_in = in * 0.3f;
|
cv::GMat out_blob = cv::gapi::infer<SemSegmNet>(frame);
|
||||||
cv::GMat blending_out = post_proc_out * 0.7f;
|
cv::GMat out = custom::PostProcessing::on(frame, out_blob);
|
||||||
cv::GMat out = blending_in + blending_out;
|
|
||||||
|
|
||||||
cv::GStreamingCompiled pipeline = cv::GComputation(cv::GIn(in), cv::GOut(out))
|
cv::GStreamingCompiled pipeline = cv::GComputation(cv::GIn(in), cv::GOut(bgr, out))
|
||||||
.compileStreaming(cv::compile_args(kernels, networks));
|
.compileStreaming(cv::compile_args(kernels, networks,
|
||||||
auto inputs = cv::gin(cv::gapi::wip::make_src<cv::gapi::wip::GCaptureSource>(input));
|
cv::gapi::streaming::queue_capacity{1}));
|
||||||
|
|
||||||
|
std::shared_ptr<cv::gapi::wip::GCaptureSource> source;
|
||||||
|
if (isNumber(input)) {
|
||||||
|
source = std::make_shared<cv::gapi::wip::GCaptureSource>(
|
||||||
|
std::stoi(input),
|
||||||
|
std::map<int, double> {
|
||||||
|
{cv::CAP_PROP_FRAME_WIDTH, 1280},
|
||||||
|
{cv::CAP_PROP_FRAME_HEIGHT, 720},
|
||||||
|
{cv::CAP_PROP_BUFFERSIZE, 1},
|
||||||
|
{cv::CAP_PROP_AUTOFOCUS, true}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
source = std::make_shared<cv::gapi::wip::GCaptureSource>(input);
|
||||||
|
}
|
||||||
|
auto inputs = cv::gin(
|
||||||
|
static_cast<cv::gapi::wip::IStreamSource::Ptr>(source));
|
||||||
|
|
||||||
// The execution part
|
// The execution part
|
||||||
pipeline.setSource(std::move(inputs));
|
pipeline.setSource(std::move(inputs));
|
||||||
|
|
||||||
cv::VideoWriter writer;
|
|
||||||
cv::TickMeter tm;
|
cv::TickMeter tm;
|
||||||
cv::Mat outMat;
|
cv::VideoWriter writer;
|
||||||
|
|
||||||
|
cv::util::optional<cv::Mat> color_mask;
|
||||||
|
cv::util::optional<cv::Mat> image;
|
||||||
|
cv::Mat last_image;
|
||||||
|
cv::Mat last_color_mask;
|
||||||
|
|
||||||
|
pipeline.start();
|
||||||
|
tm.start();
|
||||||
|
|
||||||
std::size_t frames = 0u;
|
std::size_t frames = 0u;
|
||||||
tm.start();
|
std::size_t masks = 0u;
|
||||||
pipeline.start();
|
while (pipeline.pull(cv::gout(image, color_mask))) {
|
||||||
while (pipeline.pull(cv::gout(outMat))) {
|
if (image.has_value()) {
|
||||||
++frames;
|
++frames;
|
||||||
cv::imshow("Out", outMat);
|
last_image = std::move(*image);
|
||||||
cv::waitKey(1);
|
}
|
||||||
if (!output.empty()) {
|
|
||||||
if (!writer.isOpened()) {
|
if (color_mask.has_value()) {
|
||||||
const auto sz = cv::Size{outMat.cols, outMat.rows};
|
++masks;
|
||||||
writer.open(output, cv::VideoWriter::fourcc('M','J','P','G'), 25.0, sz);
|
last_color_mask = std::move(*color_mask);
|
||||||
CV_Assert(writer.isOpened());
|
}
|
||||||
|
|
||||||
|
if (!last_image.empty() && !last_color_mask.empty()) {
|
||||||
|
tm.stop();
|
||||||
|
|
||||||
|
std::string stream_fps = "Stream FPS: " + toStr(frames / tm.getTimeSec());
|
||||||
|
std::string inference_fps = "Inference FPS: " + toStr(masks / tm.getTimeSec());
|
||||||
|
|
||||||
|
cv::Mat tmp = last_image.clone();
|
||||||
|
|
||||||
|
vis::drawResults(tmp, last_color_mask);
|
||||||
|
vis::putText(tmp, {10, 22}, stream_fps);
|
||||||
|
vis::putText(tmp, {10, 22 + 30}, inference_fps);
|
||||||
|
|
||||||
|
cv::imshow("Out", tmp);
|
||||||
|
cv::waitKey(1);
|
||||||
|
if (!output.empty()) {
|
||||||
|
if (!writer.isOpened()) {
|
||||||
|
const auto sz = cv::Size{tmp.cols, tmp.rows};
|
||||||
|
writer.open(output, cv::VideoWriter::fourcc('M','J','P','G'), 25.0, sz);
|
||||||
|
CV_Assert(writer.isOpened());
|
||||||
|
}
|
||||||
|
writer << tmp;
|
||||||
}
|
}
|
||||||
writer << outMat;
|
|
||||||
|
tm.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tm.stop();
|
tm.stop();
|
||||||
std::cout << "Processed " << frames << " frames" << " (" << frames / tm.getTimeSec() << " FPS)" << std::endl;
|
std::cout << "Processed " << frames << " frames" << " ("
|
||||||
|
<< frames / tm.getTimeSec()<< " FPS)" << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -153,10 +153,18 @@ std::ostream& operator<<(std::ostream& os, const cv::GMatDesc &desc)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
os << "C" << desc.chan;
|
if (desc.isND()) {
|
||||||
if (desc.planar) os << "p";
|
os << " [";
|
||||||
os << " ";
|
for (size_t i = 0; i < desc.dims.size() - 1; ++i) {
|
||||||
os << desc.size.width << "x" << desc.size.height;
|
os << desc.dims[i] << "x";
|
||||||
|
}
|
||||||
|
os << desc.dims.back() << "]";
|
||||||
|
} else {
|
||||||
|
os << "C" << desc.chan;
|
||||||
|
if (desc.planar) os << "p";
|
||||||
|
os << " ";
|
||||||
|
os << desc.size.width << "x" << desc.size.height;
|
||||||
|
}
|
||||||
|
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
@ -227,6 +227,12 @@ inline void convertInt64ToInt32(const int64_t* src, int* dst, size_t size)
|
|||||||
[](int64_t el) { return static_cast<int>(el); });
|
[](int64_t el) { return static_cast<int>(el); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void convertInt32ToInt64(const int* src, int64_t* dst, size_t size)
|
||||||
|
{
|
||||||
|
std::transform(src, src + size, dst,
|
||||||
|
[](int el) { return static_cast<int64_t>(el); });
|
||||||
|
}
|
||||||
|
|
||||||
}} // cv::gimpl
|
}} // cv::gimpl
|
||||||
|
|
||||||
#endif // OPENCV_GAPI_GBACKEND_HPP
|
#endif // OPENCV_GAPI_GBACKEND_HPP
|
||||||
|
@ -2320,12 +2320,15 @@ GAPI_FLUID_KERNEL(GFluidSplit3, cv::gapi::core::GSplit3, false)
|
|||||||
|
|
||||||
static void run(const View &src, Buffer &dst1, Buffer &dst2, Buffer &dst3)
|
static void run(const View &src, Buffer &dst1, Buffer &dst2, Buffer &dst3)
|
||||||
{
|
{
|
||||||
|
GAPI_Assert((src.meta().depth == CV_8U) && (dst1.meta().depth == CV_8U) &&
|
||||||
|
(dst2.meta().depth == CV_8U) && (dst3.meta().depth == CV_8U) &&
|
||||||
|
(3 == src.meta().chan));
|
||||||
|
|
||||||
const auto *in = src.InLine<uchar>(0);
|
const auto *in = src.InLine<uchar>(0);
|
||||||
auto *out1 = dst1.OutLine<uchar>();
|
auto *out1 = dst1.OutLine<uchar>();
|
||||||
auto *out2 = dst2.OutLine<uchar>();
|
auto *out2 = dst2.OutLine<uchar>();
|
||||||
auto *out3 = dst3.OutLine<uchar>();
|
auto *out3 = dst3.OutLine<uchar>();
|
||||||
|
|
||||||
GAPI_Assert(3 == src.meta().chan);
|
|
||||||
int width = src.length();
|
int width = src.length();
|
||||||
int w = 0;
|
int w = 0;
|
||||||
|
|
||||||
@ -2348,13 +2351,16 @@ GAPI_FLUID_KERNEL(GFluidSplit4, cv::gapi::core::GSplit4, false)
|
|||||||
|
|
||||||
static void run(const View &src, Buffer &dst1, Buffer &dst2, Buffer &dst3, Buffer &dst4)
|
static void run(const View &src, Buffer &dst1, Buffer &dst2, Buffer &dst3, Buffer &dst4)
|
||||||
{
|
{
|
||||||
|
GAPI_Assert((src.meta().depth == CV_8U) && (dst1.meta().depth == CV_8U) &&
|
||||||
|
(dst2.meta().depth == CV_8U) && (dst3.meta().depth == CV_8U) &&
|
||||||
|
(dst4.meta().depth == CV_8U) && (4 == src.meta().chan));
|
||||||
|
|
||||||
const auto *in = src.InLine<uchar>(0);
|
const auto *in = src.InLine<uchar>(0);
|
||||||
auto *out1 = dst1.OutLine<uchar>();
|
auto *out1 = dst1.OutLine<uchar>();
|
||||||
auto *out2 = dst2.OutLine<uchar>();
|
auto *out2 = dst2.OutLine<uchar>();
|
||||||
auto *out3 = dst3.OutLine<uchar>();
|
auto *out3 = dst3.OutLine<uchar>();
|
||||||
auto *out4 = dst4.OutLine<uchar>();
|
auto *out4 = dst4.OutLine<uchar>();
|
||||||
|
|
||||||
GAPI_Assert(4 == src.meta().chan);
|
|
||||||
int width = src.length();
|
int width = src.length();
|
||||||
int w = 0;
|
int w = 0;
|
||||||
|
|
||||||
@ -2372,31 +2378,46 @@ GAPI_FLUID_KERNEL(GFluidSplit4, cv::gapi::core::GSplit4, false)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
CV_ALWAYS_INLINE void run_merge3(Buffer& dst, const View& src1, const View& src2, const View& src3)
|
||||||
|
{
|
||||||
|
const auto* in1 = src1.InLine<T>(0);
|
||||||
|
const auto* in2 = src2.InLine<T>(0);
|
||||||
|
const auto* in3 = src3.InLine<T>(0);
|
||||||
|
auto* out = dst.OutLine<T>();
|
||||||
|
|
||||||
|
int width = dst.length();
|
||||||
|
int w = 0;
|
||||||
|
|
||||||
|
#if CV_SIMD
|
||||||
|
w = merge3_simd(in1, in2, in3, out, width);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (; w < width; w++)
|
||||||
|
{
|
||||||
|
out[3 * w] = in1[w];
|
||||||
|
out[3 * w + 1] = in2[w];
|
||||||
|
out[3 * w + 2] = in3[w];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GAPI_FLUID_KERNEL(GFluidMerge3, cv::gapi::core::GMerge3, false)
|
GAPI_FLUID_KERNEL(GFluidMerge3, cv::gapi::core::GMerge3, false)
|
||||||
{
|
{
|
||||||
static const int Window = 1;
|
static const int Window = 1;
|
||||||
|
|
||||||
static void run(const View &src1, const View &src2, const View &src3, Buffer &dst)
|
static void run(const View& src1, const View& src2, const View& src3, Buffer& dst)
|
||||||
{
|
{
|
||||||
const auto *in1 = src1.InLine<uchar>(0);
|
GAPI_Assert((src1.meta().depth == dst.meta().depth) &&
|
||||||
const auto *in2 = src2.InLine<uchar>(0);
|
(src1.meta().depth == src2.meta().depth) &&
|
||||||
const auto *in3 = src3.InLine<uchar>(0);
|
(src1.meta().depth == src3.meta().depth));
|
||||||
auto *out = dst.OutLine<uchar>();
|
|
||||||
|
|
||||||
GAPI_Assert(3 == dst.meta().chan);
|
// SRC/DST TYPE OP __VA_ARGS__
|
||||||
int width = dst.length();
|
MERGE3_(uchar, run_merge3, dst, src1, src2, src3);
|
||||||
int w = 0;
|
MERGE3_(ushort, run_merge3, dst, src1, src2, src3);
|
||||||
|
MERGE3_(short, run_merge3, dst, src1, src2, src3);
|
||||||
|
MERGE3_(float, run_merge3, dst, src1, src2, src3);
|
||||||
|
|
||||||
#if CV_SIMD
|
CV_Error(cv::Error::StsBadArg, "unsupported combination of types");
|
||||||
w = merge3_simd(in1, in2, in3, out, width);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (; w < width; w++)
|
|
||||||
{
|
|
||||||
out[3*w ] = in1[w];
|
|
||||||
out[3*w + 1] = in2[w];
|
|
||||||
out[3*w + 2] = in3[w];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2407,13 +2428,16 @@ GAPI_FLUID_KERNEL(GFluidMerge4, cv::gapi::core::GMerge4, false)
|
|||||||
static void run(const View &src1, const View &src2, const View &src3, const View &src4,
|
static void run(const View &src1, const View &src2, const View &src3, const View &src4,
|
||||||
Buffer &dst)
|
Buffer &dst)
|
||||||
{
|
{
|
||||||
|
GAPI_Assert((dst.meta().depth == CV_8U) && (src1.meta().depth == CV_8U) &&
|
||||||
|
(src2.meta().depth == CV_8U) && (src3.meta().depth == CV_8U) &&
|
||||||
|
(4 == dst.meta().chan));
|
||||||
|
|
||||||
const auto *in1 = src1.InLine<uchar>(0);
|
const auto *in1 = src1.InLine<uchar>(0);
|
||||||
const auto *in2 = src2.InLine<uchar>(0);
|
const auto *in2 = src2.InLine<uchar>(0);
|
||||||
const auto *in3 = src3.InLine<uchar>(0);
|
const auto *in3 = src3.InLine<uchar>(0);
|
||||||
const auto *in4 = src4.InLine<uchar>(0);
|
const auto *in4 = src4.InLine<uchar>(0);
|
||||||
auto *out = dst.OutLine<uchar>();
|
auto *out = dst.OutLine<uchar>();
|
||||||
|
|
||||||
GAPI_Assert(4 == dst.meta().chan);
|
|
||||||
int width = dst.length();
|
int width = dst.length();
|
||||||
|
|
||||||
int w = 0; // cycle counter
|
int w = 0; // cycle counter
|
||||||
|
@ -277,13 +277,21 @@ int split4_simd(const uchar in[], uchar out1[], uchar out2[],
|
|||||||
CV_CPU_DISPATCH_MODES_ALL);
|
CV_CPU_DISPATCH_MODES_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
int merge3_simd(const uchar in1[], const uchar in2[], const uchar in3[],
|
#define MERGE3_SIMD(T) \
|
||||||
uchar out[], const int width)
|
int merge3_simd(const T in1[], const T in2[], const T in3[], \
|
||||||
{
|
T out[], const int width) \
|
||||||
CV_CPU_DISPATCH(merge3_simd, (in1, in2, in3, out, width),
|
{ \
|
||||||
CV_CPU_DISPATCH_MODES_ALL);
|
CV_CPU_DISPATCH(merge3_simd, (in1, in2, in3, out, width), \
|
||||||
|
CV_CPU_DISPATCH_MODES_ALL); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MERGE3_SIMD(uchar)
|
||||||
|
MERGE3_SIMD(short)
|
||||||
|
MERGE3_SIMD(ushort)
|
||||||
|
MERGE3_SIMD(float)
|
||||||
|
|
||||||
|
#undef MERGE3_SIMD
|
||||||
|
|
||||||
int merge4_simd(const uchar in1[], const uchar in2[], const uchar in3[],
|
int merge4_simd(const uchar in1[], const uchar in2[], const uchar in3[],
|
||||||
const uchar in4[], uchar out[], const int width)
|
const uchar in4[], uchar out[], const int width)
|
||||||
{
|
{
|
||||||
|
@ -216,8 +216,16 @@ int split3_simd(const uchar in[], uchar out1[], uchar out2[],
|
|||||||
int split4_simd(const uchar in[], uchar out1[], uchar out2[],
|
int split4_simd(const uchar in[], uchar out1[], uchar out2[],
|
||||||
uchar out3[], uchar out4[], const int width);
|
uchar out3[], uchar out4[], const int width);
|
||||||
|
|
||||||
int merge3_simd(const uchar in1[], const uchar in2[], const uchar in3[],
|
#define MERGE3_SIMD(T) \
|
||||||
uchar out[], const int width);
|
int merge3_simd(const T in1[], const T in2[], const T in3[], \
|
||||||
|
T out[], const int width);
|
||||||
|
|
||||||
|
MERGE3_SIMD(uchar)
|
||||||
|
MERGE3_SIMD(short)
|
||||||
|
MERGE3_SIMD(ushort)
|
||||||
|
MERGE3_SIMD(float)
|
||||||
|
|
||||||
|
#undef MERGE3_SIMD
|
||||||
|
|
||||||
int merge4_simd(const uchar in1[], const uchar in2[], const uchar in3[],
|
int merge4_simd(const uchar in1[], const uchar in2[], const uchar in3[],
|
||||||
const uchar in4[], uchar out[], const int width);
|
const uchar in4[], uchar out[], const int width);
|
||||||
|
@ -322,12 +322,21 @@ int split3_simd(const uchar in[], uchar out1[], uchar out2[],
|
|||||||
int split4_simd(const uchar in[], uchar out1[], uchar out2[],
|
int split4_simd(const uchar in[], uchar out1[], uchar out2[],
|
||||||
uchar out3[], uchar out4[], const int width);
|
uchar out3[], uchar out4[], const int width);
|
||||||
|
|
||||||
int merge3_simd(const uchar in1[], const uchar in2[], const uchar in3[],
|
#define MERGE3_SIMD(T) \
|
||||||
uchar out[], const int width);
|
int merge3_simd(const T in1[], const T in2[], const T in3[], \
|
||||||
|
T out[], const int width);
|
||||||
|
|
||||||
|
MERGE3_SIMD(uchar)
|
||||||
|
MERGE3_SIMD(short)
|
||||||
|
MERGE3_SIMD(ushort)
|
||||||
|
MERGE3_SIMD(float)
|
||||||
|
|
||||||
|
#undef MERGE3_SIMD
|
||||||
|
|
||||||
int merge4_simd(const uchar in1[], const uchar in2[], const uchar in3[],
|
int merge4_simd(const uchar in1[], const uchar in2[], const uchar in3[],
|
||||||
const uchar in4[], uchar out[], const int width);
|
const uchar in4[], uchar out[], const int width);
|
||||||
|
|
||||||
|
|
||||||
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
|
#ifndef CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY
|
||||||
|
|
||||||
#define SRC_SHORT_OR_USHORT std::is_same<SRC, short>::value || std::is_same<SRC, ushort>::value
|
#define SRC_SHORT_OR_USHORT std::is_same<SRC, short>::value || std::is_same<SRC, ushort>::value
|
||||||
@ -2530,34 +2539,42 @@ int split4_simd(const uchar in[], uchar out1[], uchar out2[],
|
|||||||
//
|
//
|
||||||
//-------------------------
|
//-------------------------
|
||||||
|
|
||||||
int merge3_simd(const uchar in1[], const uchar in2[], const uchar in3[],
|
#define MERGE3_SIMD(T) \
|
||||||
uchar out[], const int width)
|
int merge3_simd(const T in1[], const T in2[], const T in3[], \
|
||||||
{
|
T out[], const int width) \
|
||||||
constexpr int nlanes = v_uint8::nlanes;
|
{ \
|
||||||
if (width < nlanes)
|
constexpr int nlanes = vector_type_of_t<T>::nlanes; \
|
||||||
return 0;
|
if (width < nlanes) \
|
||||||
|
return 0; \
|
||||||
int x = 0;
|
\
|
||||||
for (;;)
|
int x = 0; \
|
||||||
{
|
for (;;) \
|
||||||
for (; x <= width - nlanes; x += nlanes)
|
{ \
|
||||||
{
|
for (; x <= width - nlanes; x += nlanes) \
|
||||||
v_uint8 a, b, c;
|
{ \
|
||||||
a = vx_load(&in1[x]);
|
vector_type_of_t<T> a, b, c; \
|
||||||
b = vx_load(&in2[x]);
|
a = vx_load(&in1[x]); \
|
||||||
c = vx_load(&in3[x]);
|
b = vx_load(&in2[x]); \
|
||||||
v_store_interleave(&out[3 * x], a, b, c);
|
c = vx_load(&in3[x]); \
|
||||||
}
|
v_store_interleave(&out[3 * x], a, b, c); \
|
||||||
if (x < width)
|
} \
|
||||||
{
|
if (x < width) \
|
||||||
x = width - nlanes;
|
{ \
|
||||||
continue;
|
x = width - nlanes; \
|
||||||
}
|
continue; \
|
||||||
break;
|
} \
|
||||||
}
|
break; \
|
||||||
return x;
|
} \
|
||||||
|
return x; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MERGE3_SIMD(uchar)
|
||||||
|
MERGE3_SIMD(short)
|
||||||
|
MERGE3_SIMD(ushort)
|
||||||
|
MERGE3_SIMD(float)
|
||||||
|
|
||||||
|
#undef MERGE3_SIMD
|
||||||
|
|
||||||
//-------------------------
|
//-------------------------
|
||||||
//
|
//
|
||||||
// Fluid kernels: Merge4
|
// Fluid kernels: Merge4
|
||||||
@ -2926,6 +2943,8 @@ CV_ALWAYS_INLINE void convertto_simd_nocoeff_impl(const SRC* inx, float* outx)
|
|||||||
int convertto_simd(const SRC in[], DST out[], const int length) \
|
int convertto_simd(const SRC in[], DST out[], const int length) \
|
||||||
{ \
|
{ \
|
||||||
constexpr int nlanes = vector_type_of_t<DST>::nlanes; \
|
constexpr int nlanes = vector_type_of_t<DST>::nlanes; \
|
||||||
|
if (length < nlanes) \
|
||||||
|
return 0; \
|
||||||
\
|
\
|
||||||
int x = 0; \
|
int x = 0; \
|
||||||
for (;;) \
|
for (;;) \
|
||||||
@ -3093,6 +3112,9 @@ int convertto_scaled_simd(const SRC in[], DST out[], const float alpha, \
|
|||||||
const float beta, const int length) \
|
const float beta, const int length) \
|
||||||
{ \
|
{ \
|
||||||
constexpr int nlanes = vector_type_of_t<DST>::nlanes; \
|
constexpr int nlanes = vector_type_of_t<DST>::nlanes; \
|
||||||
|
if (length < nlanes) \
|
||||||
|
return 0; \
|
||||||
|
\
|
||||||
v_float32 v_alpha = vx_setall_f32(alpha); \
|
v_float32 v_alpha = vx_setall_f32(alpha); \
|
||||||
v_float32 v_beta = vx_setall_f32(beta); \
|
v_float32 v_beta = vx_setall_f32(beta); \
|
||||||
\
|
\
|
||||||
|
@ -86,6 +86,23 @@ using cv::gapi::own::rintd;
|
|||||||
return; \
|
return; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MERGE3_(T, OP, ...) \
|
||||||
|
if (cv::DataType<T>::depth == dst.meta().depth && \
|
||||||
|
cv::DataType<T>::depth == src1.meta().depth) \
|
||||||
|
{ \
|
||||||
|
GAPI_DbgAssert(dst.length() == src1.length()); \
|
||||||
|
GAPI_DbgAssert(dst.length() == src2.length()); \
|
||||||
|
GAPI_DbgAssert(dst.length() == src3.length()); \
|
||||||
|
\
|
||||||
|
GAPI_DbgAssert(1 == src1.meta().chan); \
|
||||||
|
GAPI_DbgAssert(1 == src2.meta().chan); \
|
||||||
|
GAPI_DbgAssert(1 == src3.meta().chan); \
|
||||||
|
GAPI_DbgAssert(3 == dst.meta().chan); \
|
||||||
|
\
|
||||||
|
OP<T>(__VA_ARGS__); \
|
||||||
|
return; \
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace fluid
|
} // namespace fluid
|
||||||
} // namespace gapi
|
} // namespace gapi
|
||||||
} // namespace cv
|
} // namespace cv
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
// of this distribution and at http://opencv.org/license.html.
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2018-2022 Intel Corporation
|
// Copyright (C) 2018-2023 Intel Corporation
|
||||||
|
|
||||||
#include "precomp.hpp"
|
#include "precomp.hpp"
|
||||||
|
|
||||||
@ -71,6 +71,28 @@ namespace IE = InferenceEngine;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
IE::Layout toIE(const std::string &layout) {
|
||||||
|
const std::unordered_map<std::string, IE::Layout> layouts = {
|
||||||
|
{"NCDHW", IE::Layout::NCDHW},
|
||||||
|
{"NDHWC", IE::Layout::NDHWC},
|
||||||
|
{"NHWC" , IE::Layout::NHWC },
|
||||||
|
{"NCHW" , IE::Layout::NCHW },
|
||||||
|
{"CHW" , IE::Layout::CHW },
|
||||||
|
{"HWC" , IE::Layout::HWC },
|
||||||
|
{"HW" , IE::Layout::HW },
|
||||||
|
{"NC" , IE::Layout::NC },
|
||||||
|
{"CN" , IE::Layout::CN },
|
||||||
|
{"C" , IE::Layout::C },
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto it = layouts.find(layout);
|
||||||
|
if (it == layouts.end()) {
|
||||||
|
cv::util::throw_error(
|
||||||
|
std::logic_error("IE Backend: Unsupported layout: " + layout));
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
};
|
||||||
|
|
||||||
inline IE::ROI toIE(const cv::Rect &rc) {
|
inline IE::ROI toIE(const cv::Rect &rc) {
|
||||||
return IE::ROI
|
return IE::ROI
|
||||||
{ 0u
|
{ 0u
|
||||||
@ -130,11 +152,90 @@ inline int toCV(IE::Precision prec) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline IE::ResizeAlgorithm toIEInterp(int interpolation) {
|
||||||
|
switch (interpolation) {
|
||||||
|
case cv::INTER_LINEAR: return IE::RESIZE_BILINEAR;
|
||||||
|
case cv::INTER_AREA: return IE::RESIZE_AREA;
|
||||||
|
default: GAPI_Error("IE Backend: Unsupported resize algorithm");
|
||||||
|
}
|
||||||
|
// Unreachable code
|
||||||
|
GAPI_Assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Attr>
|
||||||
|
using AttrMap = cv::gapi::ie::detail::AttrMap<Attr>;
|
||||||
|
|
||||||
|
template <typename Attr>
|
||||||
|
using LayerVariantAttr = cv::gapi::ie::detail::LayerVariantAttr<Attr>;
|
||||||
|
|
||||||
|
template <typename Attr> AttrMap<Attr>
|
||||||
|
broadcastLayerAttr(const LayerVariantAttr<Attr> &layer_attr,
|
||||||
|
const std::vector<std::string> &layer_names) {
|
||||||
|
AttrMap<Attr> map;
|
||||||
|
if (cv::util::holds_alternative<AttrMap<Attr>>(layer_attr)) {
|
||||||
|
map = cv::util::get<AttrMap<Attr>>(layer_attr);
|
||||||
|
// NB: Validate map:
|
||||||
|
std::unordered_set<std::string> existing_layers =
|
||||||
|
{layer_names.begin(), layer_names.end()};
|
||||||
|
|
||||||
|
for (const auto &p : map) {
|
||||||
|
const auto it = existing_layers.find(p.first);
|
||||||
|
if (it == existing_layers.end()) {
|
||||||
|
cv::util::throw_error(
|
||||||
|
std::logic_error("IE Backend: Failed to"
|
||||||
|
" find layer with name: " + p.first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (cv::util::holds_alternative<Attr>(layer_attr)) {
|
||||||
|
// NB: Broadcast value to all layers.
|
||||||
|
auto elem = cv::util::get<Attr>(layer_attr);
|
||||||
|
for (auto &&layer_name : layer_names) {
|
||||||
|
map.emplace(layer_name, elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move it to some common place
|
||||||
|
template <typename K, typename V>
|
||||||
|
cv::optional<V> lookUp(const std::map<K, V> &map, const K& key) {
|
||||||
|
const auto it = map.find(key);
|
||||||
|
if (it == map.end()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return cv::util::make_optional(std::move(it->second));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isImage(const cv::GMatDesc &desc,
|
||||||
|
const IE::SizeVector &model_dims) {
|
||||||
|
return (model_dims.size() == 4u) &&
|
||||||
|
(!desc.isND()) /* dims == 2 */ &&
|
||||||
|
(desc.chan == 1 || desc.chan == 3) &&
|
||||||
|
(desc.size.height != 1 && desc.size.width != 1) &&
|
||||||
|
(desc.depth == CV_8U);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ie::TraitAs clarifyTrait(const cv::GMatDesc &mat_desc,
|
||||||
|
const IE::SizeVector &model_dims) {
|
||||||
|
if (isImage(mat_desc, model_dims)) {
|
||||||
|
return cv::gapi::ie::TraitAs::IMAGE;
|
||||||
|
}
|
||||||
|
return cv::gapi::ie::TraitAs::TENSOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ie::TraitAs clarifyTrait(const cv::GMetaArg &meta,
|
||||||
|
const IE::SizeVector &model_dims) {
|
||||||
|
// NB: All media formats: BGR, NV12, Gray
|
||||||
|
// are traited as image.
|
||||||
|
if (cv::util::holds_alternative<cv::GFrameDesc>(meta)) {
|
||||||
|
return cv::gapi::ie::TraitAs::IMAGE;
|
||||||
|
}
|
||||||
|
GAPI_Assert(cv::util::holds_alternative<cv::GMatDesc>(meta));
|
||||||
|
return clarifyTrait(cv::util::get<cv::GMatDesc>(meta), model_dims);
|
||||||
|
}
|
||||||
|
|
||||||
inline IE::TensorDesc toIE(const cv::Mat &mat, cv::gapi::ie::TraitAs hint) {
|
inline IE::TensorDesc toIE(const cv::Mat &mat, cv::gapi::ie::TraitAs hint) {
|
||||||
const auto &sz = mat.size;
|
const auto &sz = mat.size;
|
||||||
// NB: For some reason RGB image is 2D image
|
|
||||||
// (since channel component is not counted here).
|
|
||||||
// Note: regular 2D vectors also fall into this category
|
|
||||||
if (sz.dims() == 2 && hint == cv::gapi::ie::TraitAs::IMAGE)
|
if (sz.dims() == 2 && hint == cv::gapi::ie::TraitAs::IMAGE)
|
||||||
{
|
{
|
||||||
// NB: This logic is mainly taken from IE samples
|
// NB: This logic is mainly taken from IE samples
|
||||||
@ -155,8 +256,72 @@ inline IE::TensorDesc toIE(const cv::Mat &mat, cv::gapi::ie::TraitAs hint) {
|
|||||||
return IE::TensorDesc(toIE(mat.depth()), toIE(sz), toIELayout(sz.dims()));
|
return IE::TensorDesc(toIE(mat.depth()), toIE(sz), toIELayout(sz.dims()));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline IE::Blob::Ptr wrapIE(const cv::Mat &mat, cv::gapi::ie::TraitAs hint) {
|
// NB: Inference dimmensions always follow NCDHW order
|
||||||
const auto tDesc = toIE(mat, hint);
|
// even though the real layout is different.
|
||||||
|
// E.g if user provided Mat({1, 240, 320, 3}, CV_8U) + NHWC layout
|
||||||
|
// need to create Blob(U8, {1, 3, 240, 320}, NHWC).
|
||||||
|
inline IE::SizeVector toIEDims(const IE::SizeVector &dims,
|
||||||
|
const IE::Layout layout) {
|
||||||
|
switch (layout) {
|
||||||
|
case IE::Layout::NDHWC: // NCDHW
|
||||||
|
return {dims[0], dims[4], dims[1], dims[2], dims[3]};
|
||||||
|
case IE::Layout::NHWC: // NCHW
|
||||||
|
return {dims[0], dims[3], dims[1], dims[2]};
|
||||||
|
case IE::Layout::HWC: // CHW
|
||||||
|
return {dims[2], dims[0], dims[1]};
|
||||||
|
default: return dims;
|
||||||
|
}
|
||||||
|
GAPI_Assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: Inference dimmensions always follow NCDHW order
|
||||||
|
// even though the real layout is different.
|
||||||
|
// E.g if U8 blob has {1, 3, 240, 320} dims and NHWC layout
|
||||||
|
// need to create cv::Mat({1, 240, 320, 3}, CV_8U);
|
||||||
|
inline std::vector<int> toCVDims(const std::vector<int> &dims,
|
||||||
|
const IE::Layout layout) {
|
||||||
|
switch (layout) {
|
||||||
|
case IE::Layout::NDHWC: // NCDHW
|
||||||
|
return {dims[0], dims[2], dims[3], dims[4], dims[1]};
|
||||||
|
case IE::Layout::NHWC: // NCHW
|
||||||
|
return {dims[0], dims[2], dims[3], dims[1]};
|
||||||
|
case IE::Layout::HWC: // CHW
|
||||||
|
return {dims[1], dims[2], dims[0]};
|
||||||
|
default: return dims;
|
||||||
|
}
|
||||||
|
GAPI_Assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline IE::TensorDesc toIE(const cv::Mat &mat,
|
||||||
|
const cv::gapi::ie::TraitAs hint,
|
||||||
|
const IE::Layout layout) {
|
||||||
|
const auto &sz = mat.size;
|
||||||
|
if (sz.dims() == 2 && hint == cv::gapi::ie::TraitAs::IMAGE)
|
||||||
|
{
|
||||||
|
// NB: This logic is mainly taken from IE samples
|
||||||
|
const size_t channels = mat.channels();
|
||||||
|
const size_t height = mat.size().height;
|
||||||
|
const size_t width = mat.size().width;
|
||||||
|
|
||||||
|
const size_t strideH = mat.step1();
|
||||||
|
IE::BlockingDesc bdesc({1, height, width, channels} /* blocking dims */,
|
||||||
|
{0, 2, 3, 1} /* order for NHWC */,
|
||||||
|
0 /* offset */,
|
||||||
|
{0, 0, 0, 0} /* offsets for dims */,
|
||||||
|
{strideH * height, strideH, channels, 1} /* strides for dims */);
|
||||||
|
|
||||||
|
return IE::TensorDesc(toIE(mat.depth()),
|
||||||
|
IE::SizeVector{1, channels, height, width}, bdesc);
|
||||||
|
}
|
||||||
|
return IE::TensorDesc(toIE(mat.depth()),
|
||||||
|
toIEDims(toIE(sz), layout),
|
||||||
|
layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline IE::Blob::Ptr wrapIE(const cv::Mat &mat,
|
||||||
|
cv::gapi::ie::TraitAs hint,
|
||||||
|
const IE::Layout layout = IE::Layout::ANY) {
|
||||||
|
const auto tDesc = toIE(mat, hint, layout);
|
||||||
switch (mat.depth()) {
|
switch (mat.depth()) {
|
||||||
// NB: Seems there's no way to create an untyped (T-less) Blob::Ptr
|
// NB: Seems there's no way to create an untyped (T-less) Blob::Ptr
|
||||||
// in IE given only precision via TensorDesc. So we have to do this:
|
// in IE given only precision via TensorDesc. So we have to do this:
|
||||||
@ -303,6 +468,7 @@ struct IEUnit {
|
|||||||
};
|
};
|
||||||
|
|
||||||
InputFramesDesc net_input_params;
|
InputFramesDesc net_input_params;
|
||||||
|
std::unordered_map<std::string, cv::gapi::ie::TraitAs> inputs_type;
|
||||||
|
|
||||||
explicit IEUnit(const cv::gapi::ie::detail::ParamDesc &pp)
|
explicit IEUnit(const cv::gapi::ie::detail::ParamDesc &pp)
|
||||||
: params(pp) {
|
: params(pp) {
|
||||||
@ -481,6 +647,8 @@ public:
|
|||||||
cv::GRunArgP output (std::size_t idx);
|
cv::GRunArgP output (std::size_t idx);
|
||||||
cv::Mat& outMatR(std::size_t idx);
|
cv::Mat& outMatR(std::size_t idx);
|
||||||
|
|
||||||
|
cv::gapi::ie::TraitAs getInputType(const std::string &layer_name) const;
|
||||||
|
|
||||||
const IEUnit &uu;
|
const IEUnit &uu;
|
||||||
cv::gimpl::GIslandExecutable::IOutput &out;
|
cv::gimpl::GIslandExecutable::IOutput &out;
|
||||||
|
|
||||||
@ -524,6 +692,9 @@ private:
|
|||||||
// keep alive preprocessed frames
|
// keep alive preprocessed frames
|
||||||
std::mutex keep_alive_frames_mutex;
|
std::mutex keep_alive_frames_mutex;
|
||||||
std::unordered_map<req_key_t, cv::MediaFrame> keep_alive_pp_frames;
|
std::unordered_map<req_key_t, cv::MediaFrame> keep_alive_pp_frames;
|
||||||
|
|
||||||
|
// NB: Hint to wrap input data properly into IE::Blob (see: wrapIE)
|
||||||
|
std::unordered_map<std::string, cv::gapi::ie::TraitAs> input_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
IECallContext::IECallContext(const IEUnit & unit,
|
IECallContext::IECallContext(const IEUnit & unit,
|
||||||
@ -558,6 +729,16 @@ IECallContext::IECallContext(const IEUnit &
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cv::gapi::ie::TraitAs
|
||||||
|
IECallContext::getInputType(const std::string &layer_name) const {
|
||||||
|
const auto it = uu.inputs_type.find(layer_name);
|
||||||
|
if (it == uu.inputs_type.end()) {
|
||||||
|
cv::util::throw_error(std::logic_error(
|
||||||
|
"Failed to find input type for layer: \"" + layer_name + "\""));
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
const cv::GArgs& IECallContext::inArgs() const {
|
const cv::GArgs& IECallContext::inArgs() const {
|
||||||
return m_args;
|
return m_args;
|
||||||
}
|
}
|
||||||
@ -732,7 +913,8 @@ cv::MediaFrame preprocess_frame_impl(cv::MediaFrame &&in_frame, const std::strin
|
|||||||
|
|
||||||
inline IE::Blob::Ptr extractBlob(IECallContext& ctx,
|
inline IE::Blob::Ptr extractBlob(IECallContext& ctx,
|
||||||
std::size_t i,
|
std::size_t i,
|
||||||
cv::gapi::ie::TraitAs hint,
|
const cv::gapi::ie::TraitAs hint,
|
||||||
|
const IE::Layout &layout,
|
||||||
const std::string& layer_name,
|
const std::string& layer_name,
|
||||||
const cv::util::optional<cv::Rect> &opt_roi,
|
const cv::util::optional<cv::Rect> &opt_roi,
|
||||||
cv::MediaFrame* out_keep_alive_frame = nullptr,
|
cv::MediaFrame* out_keep_alive_frame = nullptr,
|
||||||
@ -780,7 +962,7 @@ inline IE::Blob::Ptr extractBlob(IECallContext& ctx,
|
|||||||
return wrapIE(*(ctx.views.back()), frame.desc());
|
return wrapIE(*(ctx.views.back()), frame.desc());
|
||||||
}
|
}
|
||||||
case cv::GShape::GMAT: {
|
case cv::GShape::GMAT: {
|
||||||
return wrapIE(ctx.inMat(i), hint);
|
return wrapIE(ctx.inMat(i), hint, layout);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
GAPI_Assert("Unsupported input shape for IE backend");
|
GAPI_Assert("Unsupported input shape for IE backend");
|
||||||
@ -788,7 +970,6 @@ inline IE::Blob::Ptr extractBlob(IECallContext& ctx,
|
|||||||
GAPI_Error("InternalError");
|
GAPI_Error("InternalError");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void setBlob(InferenceEngine::InferRequest& req,
|
static void setBlob(InferenceEngine::InferRequest& req,
|
||||||
const std::string& layer_name,
|
const std::string& layer_name,
|
||||||
const IE::Blob::Ptr& blob,
|
const IE::Blob::Ptr& blob,
|
||||||
@ -1162,55 +1343,109 @@ static void configureInputReshapeByImage(const IE::InputInfo::Ptr& ii,
|
|||||||
input_reshape_table.emplace(layer_name, input_dims);
|
input_reshape_table.emplace(layer_name, input_dims);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void configureInputInfo(const IE::InputInfo::Ptr& ii, const cv::GMetaArg mm) {
|
static void cfgInputPrecision(const IE::InputInfo::Ptr& ii, const cv::GMetaArg mm) {
|
||||||
switch (mm.index()) {
|
switch (mm.index()) {
|
||||||
case cv::GMetaArg::index_of<cv::GMatDesc>():
|
case cv::GMetaArg::index_of<cv::GMatDesc>(): {
|
||||||
{
|
const auto &desc = util::get<cv::GMatDesc>(mm);
|
||||||
ii->setPrecision(toIE(util::get<cv::GMatDesc>(mm).depth));
|
ii->setPrecision(toIE(desc.depth));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case cv::GMetaArg::index_of<cv::GFrameDesc>():
|
case cv::GMetaArg::index_of<cv::GFrameDesc>():
|
||||||
{
|
|
||||||
const auto &meta = util::get<cv::GFrameDesc>(mm);
|
|
||||||
switch (meta.fmt) {
|
|
||||||
case cv::MediaFormat::NV12:
|
|
||||||
ii->getPreProcess().setColorFormat(IE::ColorFormat::NV12);
|
|
||||||
break;
|
|
||||||
case cv::MediaFormat::BGR:
|
|
||||||
// NB: Do nothing
|
|
||||||
break;
|
|
||||||
case cv::MediaFormat::GRAY:
|
|
||||||
// NB: Do nothing
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
GAPI_Error("Unsupported media format for IE backend");
|
|
||||||
}
|
|
||||||
ii->setPrecision(toIE(CV_8U));
|
ii->setPrecision(toIE(CV_8U));
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
util::throw_error(std::runtime_error("Unsupported input meta for IE backend"));
|
util::throw_error(std::runtime_error("Unsupported input meta for IE backend"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isApplicableForResize(const IE::TensorDesc& desc) {
|
static void cfgImagePreprocessing(const IE::InputInfo::Ptr &ii,
|
||||||
const auto layout = desc.getLayout();
|
const cv::GMetaArg &mm,
|
||||||
const auto prec = desc.getPrecision();
|
const IE::ResizeAlgorithm interp) {
|
||||||
return (layout == IE::Layout::NCHW || layout == IE::Layout::NHWC) &&
|
if (!cv::util::holds_alternative<cv::GMatDesc>(mm) &&
|
||||||
(prec == IE::Precision::FP32 || prec == IE::Precision::U8);
|
!cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
|
||||||
|
util::throw_error(std::runtime_error("Unsupported input meta for IE backend"));
|
||||||
|
}
|
||||||
|
|
||||||
|
ii->getPreProcess().setResizeAlgorithm(interp);
|
||||||
|
if (cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
|
||||||
|
const auto &meta = util::get<cv::GFrameDesc>(mm);
|
||||||
|
if (meta.fmt == cv::MediaFormat::NV12) {
|
||||||
|
ii->getPreProcess().setColorFormat(IE::ColorFormat::NV12);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static IE::PreProcessInfo configurePreProcInfo(const IE::InputInfo::CPtr& ii,
|
// NB: This function is used in order to configure
|
||||||
const cv::GMetaArg& mm) {
|
// preprocessing for "Load" case networks.
|
||||||
|
static void cfgInputPreprocessing(const cv::gapi::ie::TraitAs trait,
|
||||||
|
const IE::InputInfo::Ptr &ii,
|
||||||
|
const cv::GMetaArg &mm,
|
||||||
|
const std::string &layer_name,
|
||||||
|
const AttrMap<std::string> &layout_map,
|
||||||
|
const AttrMap<int> &interp_map) {
|
||||||
|
cfgInputPrecision(ii, mm);
|
||||||
|
const auto explicit_input_layout = lookUp(layout_map, layer_name);
|
||||||
|
const auto explicit_resize = lookUp(interp_map, layer_name);
|
||||||
|
if (trait == cv::gapi::ie::TraitAs::IMAGE) {
|
||||||
|
// NB: Image case - preprocessing is configured automatically.
|
||||||
|
GAPI_LOG_DEBUG(NULL, "IE Backend: Input: \"" <<
|
||||||
|
layer_name << " " << mm << "\" is image.");
|
||||||
|
// NB: BlockingDesc is used instead (see wrapIE)
|
||||||
|
if (explicit_input_layout) {
|
||||||
|
util::throw_error(std::logic_error("Input data provided for layer: \"" +
|
||||||
|
layer_name + "\" is recognized as \"image\". Explicitly" +
|
||||||
|
" specified layout is prohibited."));
|
||||||
|
}
|
||||||
|
const auto interp = explicit_resize ? toIEInterp(*explicit_resize)
|
||||||
|
: IE::RESIZE_BILINEAR;
|
||||||
|
cfgImagePreprocessing(ii, mm, interp);
|
||||||
|
} else {
|
||||||
|
// NB: Tensor case - preprocessing is configured only if user asked.
|
||||||
|
GAPI_LOG_DEBUG(NULL, "IE Backend: Input: \"" <<
|
||||||
|
layer_name << "\" " << mm << " is tensor.");
|
||||||
|
if (explicit_input_layout) {
|
||||||
|
GAPI_LOG_DEBUG(NULL, "IE Backend: Set input layout \"" <<
|
||||||
|
*explicit_input_layout << "\" for layer \"" << layer_name << "\"");
|
||||||
|
ii->setLayout(toIE(*explicit_input_layout));
|
||||||
|
}
|
||||||
|
if (explicit_resize) {
|
||||||
|
GAPI_LOG_DEBUG(NULL, "IE Backend: Set resize for layer \"" << layer_name << "\"");
|
||||||
|
ii->getPreProcess().setResizeAlgorithm(toIEInterp(*explicit_resize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static IE::PreProcessInfo createImagePreProcInfo(const cv::GMetaArg &mm,
|
||||||
|
const IE::ResizeAlgorithm interp) {
|
||||||
|
if (!cv::util::holds_alternative<cv::GMatDesc>(mm) &&
|
||||||
|
!cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
|
||||||
|
util::throw_error(std::runtime_error("Unsupported input meta for IE backend"));
|
||||||
|
}
|
||||||
IE::PreProcessInfo info;
|
IE::PreProcessInfo info;
|
||||||
|
info.setResizeAlgorithm(interp);
|
||||||
if (cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
|
if (cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
|
||||||
auto desc = cv::util::get<cv::GFrameDesc>(mm);
|
const auto &meta = util::get<cv::GFrameDesc>(mm);
|
||||||
if (desc.fmt == cv::MediaFormat::NV12) {
|
if (meta.fmt == cv::MediaFormat::NV12) {
|
||||||
info.setColorFormat(IE::ColorFormat::NV12);
|
info.setColorFormat(IE::ColorFormat::NV12);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isApplicableForResize(ii->getTensorDesc())) {
|
return info;
|
||||||
info.setResizeAlgorithm(IE::RESIZE_BILINEAR);
|
}
|
||||||
|
|
||||||
|
// NB: This function is used in order to create
|
||||||
|
// preprocessing for "Import" case networks.
|
||||||
|
static IE::PreProcessInfo createPreProcInfo(const cv::gapi::ie::TraitAs trait,
|
||||||
|
const cv::GMetaArg& mm,
|
||||||
|
const cv::optional<int> explicit_resize) {
|
||||||
|
if (trait == cv::gapi::ie::TraitAs::IMAGE) {
|
||||||
|
const auto interp = explicit_resize ? toIEInterp(*explicit_resize)
|
||||||
|
: IE::RESIZE_BILINEAR;
|
||||||
|
return createImagePreProcInfo(mm, interp);
|
||||||
|
}
|
||||||
|
// NB: In case "tensor" only resize can't be spefied for "import" models.
|
||||||
|
IE::PreProcessInfo info;
|
||||||
|
if (explicit_resize) {
|
||||||
|
info.setResizeAlgorithm(toIEInterp(*explicit_resize));
|
||||||
}
|
}
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
@ -1237,6 +1472,13 @@ static void configureOutputPrecision(const IE::OutputsDataMap &outputs
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void configureOutputLayout(const IE::OutputsDataMap &outputs_info,
|
||||||
|
const AttrMap<std::string> &output_layout) {
|
||||||
|
for (const auto it : output_layout) {
|
||||||
|
outputs_info.at(it.first)->setLayout(toIE(it.second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NB: This is a callback used by async infer
|
// NB: This is a callback used by async infer
|
||||||
// to post outputs blobs (cv::GMat's).
|
// to post outputs blobs (cv::GMat's).
|
||||||
static void PostOutputs(InferenceEngine::InferRequest &request,
|
static void PostOutputs(InferenceEngine::InferRequest &request,
|
||||||
@ -1356,6 +1598,10 @@ struct Infer: public cv::detail::KernelTag {
|
|||||||
GAPI_Assert(uu.params.input_names.size() == in_metas.size()
|
GAPI_Assert(uu.params.input_names.size() == in_metas.size()
|
||||||
&& "Known input layers count doesn't match input meta count");
|
&& "Known input layers count doesn't match input meta count");
|
||||||
|
|
||||||
|
const auto input_layout = broadcastLayerAttr(uu.params.input_layout,
|
||||||
|
uu.params.input_names);
|
||||||
|
const auto interpolation = broadcastLayerAttr(uu.params.interpolation,
|
||||||
|
uu.params.input_names);
|
||||||
// NB: Configuring input/output precision and network reshape must be done
|
// NB: Configuring input/output precision and network reshape must be done
|
||||||
// only in the loadNetwork case.
|
// only in the loadNetwork case.
|
||||||
using namespace cv::gapi::ie::detail;
|
using namespace cv::gapi::ie::detail;
|
||||||
@ -1365,25 +1611,24 @@ struct Infer: public cv::detail::KernelTag {
|
|||||||
ade::util::toRange(in_metas))) {
|
ade::util::toRange(in_metas))) {
|
||||||
const auto &input_name = std::get<0>(it);
|
const auto &input_name = std::get<0>(it);
|
||||||
auto ii = inputs.at(input_name);
|
auto ii = inputs.at(input_name);
|
||||||
const auto & mm = std::get<1>(it);
|
const auto &mm = std::get<1>(it);
|
||||||
|
|
||||||
configureInputInfo(ii, mm);
|
|
||||||
if (uu.params.layer_names_to_reshape.find(input_name) !=
|
if (uu.params.layer_names_to_reshape.find(input_name) !=
|
||||||
uu.params.layer_names_to_reshape.end()) {
|
uu.params.layer_names_to_reshape.end()) {
|
||||||
configureInputReshapeByImage(ii, mm, input_reshape_table);
|
configureInputReshapeByImage(ii, mm, input_reshape_table);
|
||||||
}
|
}
|
||||||
|
const auto trait = clarifyTrait(mm, ii->getTensorDesc().getDims());
|
||||||
if (isApplicableForResize(ii->getTensorDesc())) {
|
// FIXME: This is the only place where information about input type
|
||||||
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
|
// can be stored for the futher execution.
|
||||||
}
|
const_cast<IEUnit&>(uu).inputs_type.emplace(input_name, trait);
|
||||||
|
cfgInputPreprocessing(trait, ii, mm, input_name,
|
||||||
|
input_layout, interpolation);
|
||||||
// NB: configure input param for further preproc
|
// NB: configure input param for further preproc
|
||||||
if (uu.net_input_params.is_applicable(mm)) {
|
if (uu.net_input_params.is_applicable(mm)) {
|
||||||
const_cast<IEUnit::InputFramesDesc &>(uu.net_input_params)
|
const_cast<IEUnit::InputFramesDesc &>(uu.net_input_params)
|
||||||
.set_param(input_name, ii->getTensorDesc());
|
.set_param(input_name, ii->getTensorDesc());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &&p : uu.params.const_inputs) {
|
for (auto &&p : uu.params.const_inputs) {
|
||||||
const auto ii = inputs.at(p.first);
|
const auto ii = inputs.at(p.first);
|
||||||
ii->setPrecision(toIE(p.second.first.depth()));
|
ii->setPrecision(toIE(p.second.first.depth()));
|
||||||
@ -1395,6 +1640,10 @@ struct Infer: public cv::detail::KernelTag {
|
|||||||
if (!input_reshape_table.empty()) {
|
if (!input_reshape_table.empty()) {
|
||||||
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
|
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto output_layout = broadcastLayerAttr(uu.params.output_layout,
|
||||||
|
uu.params.output_names);
|
||||||
|
configureOutputLayout(uu.net.getOutputsInfo(), output_layout);
|
||||||
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
|
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
|
||||||
} else {
|
} else {
|
||||||
GAPI_Assert(uu.params.kind == ParamDesc::Kind::Import);
|
GAPI_Assert(uu.params.kind == ParamDesc::Kind::Import);
|
||||||
@ -1406,7 +1655,13 @@ struct Infer: public cv::detail::KernelTag {
|
|||||||
const auto &input_name = std::get<0>(it);
|
const auto &input_name = std::get<0>(it);
|
||||||
auto ii = inputs.at(input_name);
|
auto ii = inputs.at(input_name);
|
||||||
const auto & mm = std::get<1>(it);
|
const auto & mm = std::get<1>(it);
|
||||||
non_const_prepm->emplace(input_name, configurePreProcInfo(ii, mm));
|
const auto trait = clarifyTrait(mm, ii->getTensorDesc().getDims());
|
||||||
|
// FIXME: This is the only place where information about input type
|
||||||
|
// can be stored for the futher execution.
|
||||||
|
const_cast<IEUnit&>(uu).inputs_type.emplace(input_name, trait);
|
||||||
|
const auto explicit_resize = lookUp(interpolation, input_name);
|
||||||
|
non_const_prepm->emplace(
|
||||||
|
input_name, createPreProcInfo(trait, mm, explicit_resize));
|
||||||
|
|
||||||
// NB: configure input param for further preproc
|
// NB: configure input param for further preproc
|
||||||
if (uu.net_input_params.is_applicable(mm)) {
|
if (uu.net_input_params.is_applicable(mm)) {
|
||||||
@ -1428,7 +1683,7 @@ struct Infer: public cv::detail::KernelTag {
|
|||||||
: uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
|
: uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
|
||||||
|
|
||||||
cv::GMatDesc outm(toCV(desc.getPrecision()),
|
cv::GMatDesc outm(toCV(desc.getPrecision()),
|
||||||
toCV(desc.getDims()));
|
toCVDims(toCV(desc.getDims()), desc.getLayout()));
|
||||||
result.emplace_back(outm);
|
result.emplace_back(outm);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -1444,15 +1699,10 @@ struct Infer: public cv::detail::KernelTag {
|
|||||||
// - assumes all inputs/outputs are always Mats
|
// - assumes all inputs/outputs are always Mats
|
||||||
for (auto i : ade::util::iota(ctx->uu.params.num_in)) {
|
for (auto i : ade::util::iota(ctx->uu.params.num_in)) {
|
||||||
const auto& layer_name = ctx->uu.params.input_names[i];
|
const auto& layer_name = ctx->uu.params.input_names[i];
|
||||||
auto layout =
|
const auto hint = ctx->getInputType(layer_name);
|
||||||
ctx->uu.this_network.GetInputsInfo().
|
const auto layout = req.GetBlob(layer_name)->getTensorDesc().getLayout();
|
||||||
at(layer_name)->getTensorDesc().getLayout();
|
|
||||||
auto hint =
|
|
||||||
(layout == IE::Layout::NCHW || layout == IE::Layout::NHWC)
|
|
||||||
? cv::gapi::ie::TraitAs::IMAGE : cv::gapi::ie::TraitAs::TENSOR;
|
|
||||||
|
|
||||||
IE::Blob::Ptr this_blob = extractBlob(*ctx, i, hint,
|
IE::Blob::Ptr this_blob = extractBlob(*ctx, i, hint,
|
||||||
layer_name,
|
layout, layer_name,
|
||||||
cv::util::optional<cv::Rect>{});
|
cv::util::optional<cv::Rect>{});
|
||||||
setBlob(req, layer_name, this_blob, *ctx);
|
setBlob(req, layer_name, this_blob, *ctx);
|
||||||
}
|
}
|
||||||
@ -1485,20 +1735,43 @@ struct InferROI: public cv::detail::KernelTag {
|
|||||||
|
|
||||||
const auto &input_name = uu.params.input_names.at(0);
|
const auto &input_name = uu.params.input_names.at(0);
|
||||||
auto &&mm = in_metas.at(1u);
|
auto &&mm = in_metas.at(1u);
|
||||||
|
const auto &tensor_desc =
|
||||||
|
(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load)
|
||||||
|
? uu.net.getInputsInfo().at(input_name)->getTensorDesc()
|
||||||
|
: uu.this_network.GetInputsInfo().at(input_name)->getTensorDesc();
|
||||||
|
|
||||||
|
if (cv::util::holds_alternative<cv::GMatDesc>(mm) ||
|
||||||
|
cv::util::holds_alternative<cv::GFrameDesc>(mm)) {
|
||||||
|
const auto trait = clarifyTrait(mm, tensor_desc.getDims());
|
||||||
|
if (trait != cv::gapi::ie::TraitAs::IMAGE) {
|
||||||
|
util::throw_error(std::runtime_error(
|
||||||
|
"IE Backend: Only image is supported"
|
||||||
|
" as the 1th argument for InferROI"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
util::throw_error(std::runtime_error(
|
||||||
|
"IE Backend: Unsupported input meta for"
|
||||||
|
" 1th argument for InferROI"));
|
||||||
|
}
|
||||||
|
|
||||||
// NB: Configuring input precision and network reshape must be done
|
// NB: Configuring input precision and network reshape must be done
|
||||||
// only in the loadNetwork case.
|
// only in the loadNetwork case.
|
||||||
|
const auto input_layout = broadcastLayerAttr(uu.params.input_layout,
|
||||||
|
uu.params.input_names);
|
||||||
|
const auto interpolation = broadcastLayerAttr(uu.params.interpolation,
|
||||||
|
uu.params.input_names);
|
||||||
|
const auto trait = cv::gapi::ie::TraitAs::IMAGE;
|
||||||
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
|
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
|
||||||
// 0th is ROI, 1st is input image
|
// 0th is ROI, 1st is input image
|
||||||
auto inputs = uu.net.getInputsInfo();
|
auto inputs = uu.net.getInputsInfo();
|
||||||
auto ii = inputs.at(input_name);
|
auto ii = inputs.at(input_name);
|
||||||
configureInputInfo(ii, mm);
|
|
||||||
if (uu.params.layer_names_to_reshape.find(input_name) !=
|
if (uu.params.layer_names_to_reshape.find(input_name) !=
|
||||||
uu.params.layer_names_to_reshape.end()) {
|
uu.params.layer_names_to_reshape.end()) {
|
||||||
configureInputReshapeByImage(ii, mm, input_reshape_table);
|
configureInputReshapeByImage(ii, mm, input_reshape_table);
|
||||||
}
|
}
|
||||||
if (isApplicableForResize(ii->getTensorDesc())) {
|
cfgInputPreprocessing(trait, ii, mm, input_name,
|
||||||
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
|
input_layout, interpolation);
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: This isn't the best place to call reshape function.
|
// FIXME: This isn't the best place to call reshape function.
|
||||||
// Сorrect solution would be to do this in compile() method of network,
|
// Сorrect solution would be to do this in compile() method of network,
|
||||||
@ -1517,6 +1790,9 @@ struct InferROI: public cv::detail::KernelTag {
|
|||||||
inputs.at(p.first)->setPrecision(toIE(p.second.first.depth()));
|
inputs.at(p.first)->setPrecision(toIE(p.second.first.depth()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto output_layout = broadcastLayerAttr(uu.params.output_layout,
|
||||||
|
uu.params.output_names);
|
||||||
|
configureOutputLayout(uu.net.getOutputsInfo(), output_layout);
|
||||||
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
|
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
|
||||||
} else {
|
} else {
|
||||||
GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import);
|
GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import);
|
||||||
@ -1524,7 +1800,9 @@ struct InferROI: public cv::detail::KernelTag {
|
|||||||
// FIXME: This isn't the best place to collect PreProcMap.
|
// FIXME: This isn't the best place to collect PreProcMap.
|
||||||
auto* non_const_prepm = const_cast<IEUnit::PreProcMap*>(&uu.preproc_map);
|
auto* non_const_prepm = const_cast<IEUnit::PreProcMap*>(&uu.preproc_map);
|
||||||
auto ii = inputs.at(input_name);
|
auto ii = inputs.at(input_name);
|
||||||
non_const_prepm->emplace(input_name, configurePreProcInfo(ii, mm));
|
const auto explicit_resize = lookUp(interpolation, input_name);
|
||||||
|
non_const_prepm->emplace(
|
||||||
|
input_name, createPreProcInfo(trait, mm, explicit_resize));
|
||||||
|
|
||||||
// NB: configure intput param for further preproc
|
// NB: configure intput param for further preproc
|
||||||
if (uu.net_input_params.is_applicable(mm)) {
|
if (uu.net_input_params.is_applicable(mm)) {
|
||||||
@ -1545,7 +1823,7 @@ struct InferROI: public cv::detail::KernelTag {
|
|||||||
: uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
|
: uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
|
||||||
|
|
||||||
cv::GMatDesc outm(toCV(desc.getPrecision()),
|
cv::GMatDesc outm(toCV(desc.getPrecision()),
|
||||||
toCV(desc.getDims()));
|
toCVDims(toCV(desc.getDims()), desc.getLayout()));
|
||||||
result.emplace_back(outm);
|
result.emplace_back(outm);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -1568,6 +1846,7 @@ struct InferROI: public cv::detail::KernelTag {
|
|||||||
bool preprocessed = false;
|
bool preprocessed = false;
|
||||||
IE::Blob::Ptr this_blob =
|
IE::Blob::Ptr this_blob =
|
||||||
extractBlob(*ctx, 1, cv::gapi::ie::TraitAs::IMAGE,
|
extractBlob(*ctx, 1, cv::gapi::ie::TraitAs::IMAGE,
|
||||||
|
IE::Layout::ANY,
|
||||||
*(ctx->uu.params.input_names.begin()),
|
*(ctx->uu.params.input_names.begin()),
|
||||||
cv::util::make_optional(this_roi),
|
cv::util::make_optional(this_roi),
|
||||||
slot_ptr, &preprocessed);
|
slot_ptr, &preprocessed);
|
||||||
@ -1613,20 +1892,31 @@ struct InferList: public cv::detail::KernelTag {
|
|||||||
|
|
||||||
// NB: Configuring input precision and network reshape must be done
|
// NB: Configuring input precision and network reshape must be done
|
||||||
// only in the loadNetwork case.
|
// only in the loadNetwork case.
|
||||||
|
const auto input_layout = broadcastLayerAttr(uu.params.input_layout,
|
||||||
|
uu.params.input_names);
|
||||||
|
const auto interpolation = broadcastLayerAttr(uu.params.interpolation,
|
||||||
|
uu.params.input_names);
|
||||||
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
|
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
|
||||||
std::size_t idx = 1u;
|
std::size_t idx = 1u;
|
||||||
auto inputs = uu.net.getInputsInfo();
|
auto inputs = uu.net.getInputsInfo();
|
||||||
for (auto &&input_name : uu.params.input_names) {
|
for (auto &&input_name : uu.params.input_names) {
|
||||||
auto ii = inputs.at(input_name);
|
auto ii = inputs.at(input_name);
|
||||||
const auto & mm = in_metas[idx++];
|
const auto & mm = in_metas[idx++];
|
||||||
configureInputInfo(ii, mm);
|
|
||||||
|
// NB: InferList expects the input starts with index 1 wil be the images.
|
||||||
|
const auto input_trait = clarifyTrait(mm, ii->getTensorDesc().getDims());
|
||||||
|
if (input_trait != cv::gapi::ie::TraitAs::IMAGE) {
|
||||||
|
util::throw_error(std::runtime_error(
|
||||||
|
"IE Backend: Only image is supported"
|
||||||
|
" as the " + std::to_string(idx) + "th argument for InferList"));
|
||||||
|
}
|
||||||
|
|
||||||
if (uu.params.layer_names_to_reshape.find(input_name) !=
|
if (uu.params.layer_names_to_reshape.find(input_name) !=
|
||||||
uu.params.layer_names_to_reshape.end()) {
|
uu.params.layer_names_to_reshape.end()) {
|
||||||
configureInputReshapeByImage(ii, mm, input_reshape_table);
|
configureInputReshapeByImage(ii, mm, input_reshape_table);
|
||||||
}
|
}
|
||||||
if (isApplicableForResize(ii->getTensorDesc())) {
|
cfgInputPreprocessing(input_trait, ii, mm,
|
||||||
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
|
input_name, input_layout, interpolation);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: This isn't the best place to call reshape function.
|
// FIXME: This isn't the best place to call reshape function.
|
||||||
@ -1641,6 +1931,9 @@ struct InferList: public cv::detail::KernelTag {
|
|||||||
ii->setPrecision(toIE(p.second.first.depth()));
|
ii->setPrecision(toIE(p.second.first.depth()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto output_layout = broadcastLayerAttr(uu.params.output_layout,
|
||||||
|
uu.params.output_names);
|
||||||
|
configureOutputLayout(uu.net.getOutputsInfo(), output_layout);
|
||||||
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
|
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
|
||||||
} else {
|
} else {
|
||||||
GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import);
|
GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import);
|
||||||
@ -1650,7 +1943,18 @@ struct InferList: public cv::detail::KernelTag {
|
|||||||
for (auto &&input_name : uu.params.input_names) {
|
for (auto &&input_name : uu.params.input_names) {
|
||||||
auto ii = inputs.at(input_name);
|
auto ii = inputs.at(input_name);
|
||||||
const auto & mm = in_metas[idx++];
|
const auto & mm = in_metas[idx++];
|
||||||
non_const_prepm->emplace(input_name, configurePreProcInfo(ii, mm));
|
|
||||||
|
// NB: InferList expects the input starts with index 1 wil be the images.
|
||||||
|
const auto input_trait = clarifyTrait(mm, ii->getTensorDesc().getDims());
|
||||||
|
if (input_trait != cv::gapi::ie::TraitAs::IMAGE) {
|
||||||
|
util::throw_error(std::runtime_error(
|
||||||
|
"IE Backend: Only image is supported"
|
||||||
|
" as the " + std::to_string(idx) + "th argument for InferList"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto explicit_resize = lookUp(interpolation, input_name);
|
||||||
|
non_const_prepm->emplace(
|
||||||
|
input_name, createPreProcInfo(input_trait, mm, explicit_resize));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1678,6 +1982,7 @@ struct InferList: public cv::detail::KernelTag {
|
|||||||
// NB: This blob will be used to make roi from its, so
|
// NB: This blob will be used to make roi from its, so
|
||||||
// it should be treated as image
|
// it should be treated as image
|
||||||
IE::Blob::Ptr this_blob = extractBlob(*ctx, 1, cv::gapi::ie::TraitAs::IMAGE,
|
IE::Blob::Ptr this_blob = extractBlob(*ctx, 1, cv::gapi::ie::TraitAs::IMAGE,
|
||||||
|
IE::Layout::ANY,
|
||||||
ctx->uu.params.input_names[0u],
|
ctx->uu.params.input_names[0u],
|
||||||
cv::util::optional<cv::Rect>{});
|
cv::util::optional<cv::Rect>{});
|
||||||
|
|
||||||
@ -1688,7 +1993,7 @@ struct InferList: public cv::detail::KernelTag {
|
|||||||
ctx->uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load
|
ctx->uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load
|
||||||
? ctx->uu.net.getOutputsInfo().at(out_name)->getTensorDesc()
|
? ctx->uu.net.getOutputsInfo().at(out_name)->getTensorDesc()
|
||||||
: ctx->uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
|
: ctx->uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
|
||||||
cached_dims[i] = toCV(desc.getDims());
|
cached_dims[i] = toCVDims(toCV(desc.getDims()), desc.getLayout());
|
||||||
// FIXME: Isn't this should be done automatically
|
// FIXME: Isn't this should be done automatically
|
||||||
// by some resetInternalData(), etc? (Probably at the GExecutor level)
|
// by some resetInternalData(), etc? (Probably at the GExecutor level)
|
||||||
auto& out_vec = ctx->outVecR<cv::Mat>(i);
|
auto& out_vec = ctx->outVecR<cv::Mat>(i);
|
||||||
@ -1744,51 +2049,52 @@ struct InferList2: public cv::detail::KernelTag {
|
|||||||
// "blob"-based ones)
|
// "blob"-based ones)
|
||||||
// FIXME: this is filtering not done, actually! GArrayDesc has
|
// FIXME: this is filtering not done, actually! GArrayDesc has
|
||||||
// no hint for its underlying type!
|
// no hint for its underlying type!
|
||||||
const auto &mm_0 = in_metas[0u];
|
|
||||||
switch (in_metas[0u].index()) {
|
|
||||||
case cv::GMetaArg::index_of<cv::GMatDesc>(): {
|
|
||||||
const auto &meta_0 = util::get<cv::GMatDesc>(mm_0);
|
|
||||||
GAPI_Assert( !meta_0.isND()
|
|
||||||
&& !meta_0.planar
|
|
||||||
&& "Only images are supported as the 0th argument");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case cv::GMetaArg::index_of<cv::GFrameDesc>(): {
|
|
||||||
// FIXME: Is there any validation for GFrame ?
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
util::throw_error(std::runtime_error("Unsupported input meta for IE backend"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (util::holds_alternative<cv::GMatDesc>(mm_0)) {
|
const auto &input_name_0 = uu.params.input_names.front();
|
||||||
const auto &meta_0 = util::get<cv::GMatDesc>(mm_0);
|
const auto &mm_0 = in_metas[0u];
|
||||||
GAPI_Assert( !meta_0.isND()
|
const auto &tensor_desc_0 =
|
||||||
&& !meta_0.planar
|
(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load)
|
||||||
&& "Only images are supported as the 0th argument");
|
? uu.net.getInputsInfo().at(input_name_0)->getTensorDesc()
|
||||||
|
: uu.this_network.GetInputsInfo().at(input_name_0)->getTensorDesc();
|
||||||
|
|
||||||
|
if (cv::util::holds_alternative<cv::GMatDesc>(mm_0) ||
|
||||||
|
cv::util::holds_alternative<cv::GFrameDesc>(mm_0)) {
|
||||||
|
const auto trait = clarifyTrait(mm_0, tensor_desc_0.getDims());
|
||||||
|
if (trait != cv::gapi::ie::TraitAs::IMAGE) {
|
||||||
|
util::throw_error(std::runtime_error(
|
||||||
|
"IE Backend: Only images is"
|
||||||
|
" supported as the 0th argument"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
util::throw_error(std::runtime_error(
|
||||||
|
"IE Backend: Unsupported input meta"
|
||||||
|
" for 0th argument in IE backend"));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t idx = 1u;
|
std::size_t idx = 1u;
|
||||||
|
const auto input_layout = broadcastLayerAttr(uu.params.input_layout,
|
||||||
|
uu.params.input_names);
|
||||||
|
const auto interpolation = broadcastLayerAttr(uu.params.interpolation,
|
||||||
|
uu.params.input_names);
|
||||||
for (auto &&input_name : uu.params.input_names) {
|
for (auto &&input_name : uu.params.input_names) {
|
||||||
const auto &mm = in_metas[idx];
|
const auto &mm = in_metas[idx];
|
||||||
GAPI_Assert(util::holds_alternative<cv::GArrayDesc>(mm)
|
GAPI_Assert(util::holds_alternative<cv::GArrayDesc>(mm)
|
||||||
&& "Non-array inputs are not supported");
|
&& "Non-array inputs are not supported");
|
||||||
|
|
||||||
if (op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_RECT) {
|
if (op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_RECT) {
|
||||||
|
const auto input_trait = cv::gapi::ie::TraitAs::IMAGE;
|
||||||
// NB: Configuring input precision and network reshape must be done
|
// NB: Configuring input precision and network reshape must be done
|
||||||
// only in the loadNetwork case.
|
// only in the loadNetwork case.
|
||||||
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
|
if (uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load) {
|
||||||
auto inputs = uu.net.getInputsInfo();
|
auto inputs = uu.net.getInputsInfo();
|
||||||
// This is a cv::Rect -- configure the IE preprocessing
|
// This is a cv::Rect -- configure the IE preprocessing
|
||||||
auto ii = inputs.at(input_name);
|
auto ii = inputs.at(input_name);
|
||||||
configureInputInfo(ii, mm_0);
|
|
||||||
if (uu.params.layer_names_to_reshape.find(input_name) !=
|
if (uu.params.layer_names_to_reshape.find(input_name) !=
|
||||||
uu.params.layer_names_to_reshape.end()) {
|
uu.params.layer_names_to_reshape.end()) {
|
||||||
configureInputReshapeByImage(ii, mm_0, input_reshape_table);
|
configureInputReshapeByImage(ii, mm_0, input_reshape_table);
|
||||||
}
|
}
|
||||||
if (isApplicableForResize(ii->getTensorDesc())) {
|
cfgInputPreprocessing(input_trait, ii, mm_0,
|
||||||
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
|
input_name, input_layout, interpolation);
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &&p : uu.params.const_inputs) {
|
for (auto &&p : uu.params.const_inputs) {
|
||||||
inputs.at(p.first)->setPrecision(toIE(p.second.first.depth()));
|
inputs.at(p.first)->setPrecision(toIE(p.second.first.depth()));
|
||||||
@ -1800,19 +2106,32 @@ struct InferList2: public cv::detail::KernelTag {
|
|||||||
if (!input_reshape_table.empty()) {
|
if (!input_reshape_table.empty()) {
|
||||||
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
|
const_cast<IE::CNNNetwork *>(&uu.net)->reshape(input_reshape_table);
|
||||||
}
|
}
|
||||||
|
const auto output_layout = broadcastLayerAttr(uu.params.output_layout,
|
||||||
|
uu.params.output_names);
|
||||||
|
configureOutputLayout(uu.net.getOutputsInfo(), output_layout);
|
||||||
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
|
configureOutputPrecision(uu.net.getOutputsInfo(), uu.params.output_precision);
|
||||||
} else {
|
} else {
|
||||||
GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import);
|
GAPI_Assert(uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Import);
|
||||||
auto inputs = uu.this_network.GetInputsInfo();
|
auto inputs = uu.this_network.GetInputsInfo();
|
||||||
auto* non_const_prepm = const_cast<IEUnit::PreProcMap*>(&uu.preproc_map);
|
auto* non_const_prepm = const_cast<IEUnit::PreProcMap*>(&uu.preproc_map);
|
||||||
auto ii = inputs.at(input_name);
|
auto ii = inputs.at(input_name);
|
||||||
non_const_prepm->emplace(input_name, configurePreProcInfo(ii, mm_0));
|
const auto explicit_resize = lookUp(interpolation, input_name);
|
||||||
|
non_const_prepm->emplace(
|
||||||
|
input_name, createPreProcInfo(input_trait, mm_0, explicit_resize));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This is a cv::GMat (equals to: cv::Mat)
|
// This is a cv::GMat (equals to: cv::Mat)
|
||||||
// Just validate that it is really the type
|
// Just validate that it is really the type
|
||||||
// (other types are prohibited here)
|
// (other types are prohibited here)
|
||||||
GAPI_Assert(op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_MAT);
|
GAPI_Assert(op.k.inKinds[idx] == cv::detail::OpaqueKind::CV_MAT);
|
||||||
|
// NB: Well, it's even impossible to specify the precision since
|
||||||
|
// there is not such info in GArray<cv::GMat>
|
||||||
|
const auto explicit_resize = lookUp(interpolation, input_name);
|
||||||
|
const auto explicit_layout = lookUp(input_layout , input_name);
|
||||||
|
if (explicit_resize || explicit_layout) {
|
||||||
|
util::throw_error(std::logic_error(
|
||||||
|
"InferList2 doesn't support preprocessing for \"tensor\"'s arguments!"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
idx++; // NB: Never forget to increment the counter
|
idx++; // NB: Never forget to increment the counter
|
||||||
}
|
}
|
||||||
@ -1832,6 +2151,7 @@ struct InferList2: public cv::detail::KernelTag {
|
|||||||
// NB: This blob will be used to make roi from its, so
|
// NB: This blob will be used to make roi from its, so
|
||||||
// it should be treated as image
|
// it should be treated as image
|
||||||
IE::Blob::Ptr blob_0 = extractBlob(*ctx, 0, cv::gapi::ie::TraitAs::IMAGE,
|
IE::Blob::Ptr blob_0 = extractBlob(*ctx, 0, cv::gapi::ie::TraitAs::IMAGE,
|
||||||
|
IE::Layout::ANY,
|
||||||
ctx->uu.params.input_names[0u],
|
ctx->uu.params.input_names[0u],
|
||||||
cv::util::optional<cv::Rect>{});
|
cv::util::optional<cv::Rect>{});
|
||||||
const auto list_size = ctx->inArg<cv::detail::VectorRef>(1u).size();
|
const auto list_size = ctx->inArg<cv::detail::VectorRef>(1u).size();
|
||||||
@ -1851,7 +2171,7 @@ struct InferList2: public cv::detail::KernelTag {
|
|||||||
ctx->uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load
|
ctx->uu.params.kind == cv::gapi::ie::detail::ParamDesc::Kind::Load
|
||||||
? ctx->uu.net.getOutputsInfo().at(out_name)->getTensorDesc()
|
? ctx->uu.net.getOutputsInfo().at(out_name)->getTensorDesc()
|
||||||
: ctx->uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
|
: ctx->uu.this_network.GetOutputsInfo().at(out_name)->getTensorDesc();
|
||||||
cached_dims[i] = toCV(desc.getDims());
|
cached_dims[i] = toCVDims(toCV(desc.getDims()), desc.getLayout());
|
||||||
// FIXME: Isn't this should be done automatically
|
// FIXME: Isn't this should be done automatically
|
||||||
// by some resetInternalData(), etc? (Probably at the GExecutor level)
|
// by some resetInternalData(), etc? (Probably at the GExecutor level)
|
||||||
auto& out_vec = ctx->outVecR<cv::Mat>(i);
|
auto& out_vec = ctx->outVecR<cv::Mat>(i);
|
||||||
@ -1874,8 +2194,10 @@ struct InferList2: public cv::detail::KernelTag {
|
|||||||
} else if (this_vec.getKind() == cv::detail::OpaqueKind::CV_MAT) {
|
} else if (this_vec.getKind() == cv::detail::OpaqueKind::CV_MAT) {
|
||||||
const auto &vec = this_vec.rref<cv::Mat>();
|
const auto &vec = this_vec.rref<cv::Mat>();
|
||||||
const auto &mat = vec[list_idx];
|
const auto &mat = vec[list_idx];
|
||||||
setBlob(req, ctx->uu.params.input_names[in_idx],
|
const auto layer_name = ctx->uu.params.input_names[in_idx];
|
||||||
wrapIE(mat, cv::gapi::ie::TraitAs::TENSOR),
|
const auto layout = req.GetBlob(layer_name)->getTensorDesc().getLayout();
|
||||||
|
setBlob(req, layer_name,
|
||||||
|
wrapIE(mat, cv::gapi::ie::TraitAs::TENSOR, layout),
|
||||||
*ctx);
|
*ctx);
|
||||||
} else {
|
} else {
|
||||||
GAPI_Assert(false &&
|
GAPI_Assert(false &&
|
||||||
|
168
modules/gapi/src/backends/ov/bindings_ov.cpp
Normal file
168
modules/gapi/src/backends/ov/bindings_ov.cpp
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
#include <opencv2/gapi/infer/bindings_ov.hpp>
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams::PyParams(const std::string &tag,
|
||||||
|
const std::string &model_path,
|
||||||
|
const std::string &bin_path,
|
||||||
|
const std::string &device)
|
||||||
|
: m_priv(std::make_shared<Params<cv::gapi::Generic>>(tag, model_path, bin_path, device)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams::PyParams(const std::string &tag,
|
||||||
|
const std::string &blob_path,
|
||||||
|
const std::string &device)
|
||||||
|
: m_priv(std::make_shared<Params<cv::gapi::Generic>>(tag, blob_path, device)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::GBackend cv::gapi::ov::PyParams::backend() const {
|
||||||
|
return m_priv->backend();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cv::gapi::ov::PyParams::tag() const {
|
||||||
|
return m_priv->tag();
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::util::any cv::gapi::ov::PyParams::params() const {
|
||||||
|
return m_priv->params();
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgPluginConfig(
|
||||||
|
const std::map<std::string, std::string> &config) {
|
||||||
|
m_priv->cfgPluginConfig(config);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgInputTensorLayout(std::string tensor_layout) {
|
||||||
|
m_priv->cfgInputTensorLayout(std::move(tensor_layout));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgInputTensorLayout(
|
||||||
|
std::map<std::string, std::string> layout_map) {
|
||||||
|
m_priv->cfgInputTensorLayout(std::move(layout_map));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgInputModelLayout(std::string tensor_layout) {
|
||||||
|
m_priv->cfgInputModelLayout(std::move(tensor_layout));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgInputModelLayout(
|
||||||
|
std::map<std::string, std::string> layout_map) {
|
||||||
|
m_priv->cfgInputModelLayout(std::move(layout_map));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgOutputTensorLayout(std::string tensor_layout) {
|
||||||
|
m_priv->cfgOutputTensorLayout(std::move(tensor_layout));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgOutputTensorLayout(
|
||||||
|
std::map<std::string, std::string> layout_map) {
|
||||||
|
m_priv->cfgOutputTensorLayout(std::move(layout_map));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgOutputModelLayout(std::string tensor_layout) {
|
||||||
|
m_priv->cfgOutputModelLayout(std::move(tensor_layout));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgOutputModelLayout(
|
||||||
|
std::map<std::string, std::string> layout_map) {
|
||||||
|
m_priv->cfgOutputModelLayout(std::move(layout_map));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgOutputTensorPrecision(int precision) {
|
||||||
|
m_priv->cfgOutputTensorPrecision(precision);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgOutputTensorPrecision(
|
||||||
|
std::map<std::string, int> precision_map) {
|
||||||
|
m_priv->cfgOutputTensorPrecision(precision_map);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgReshape(std::vector<size_t> new_shape) {
|
||||||
|
m_priv->cfgReshape(std::move(new_shape));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgReshape(
|
||||||
|
std::map<std::string, std::vector<size_t>> new_shape_map) {
|
||||||
|
m_priv->cfgReshape(std::move(new_shape_map));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgNumRequests(const size_t nireq) {
|
||||||
|
m_priv->cfgNumRequests(nireq);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgMean(std::vector<float> mean_values) {
|
||||||
|
m_priv->cfgMean(std::move(mean_values));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgMean(
|
||||||
|
std::map<std::string, std::vector<float>> mean_map) {
|
||||||
|
m_priv->cfgMean(std::move(mean_map));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgScale(std::vector<float> scale_values) {
|
||||||
|
m_priv->cfgScale(std::move(scale_values));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgScale(
|
||||||
|
std::map<std::string, std::vector<float>> scale_map) {
|
||||||
|
m_priv->cfgScale(std::move(scale_map));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgResize(int interpolation) {
|
||||||
|
m_priv->cfgResize(interpolation);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams&
|
||||||
|
cv::gapi::ov::PyParams::cfgResize(std::map<std::string, int> interpolation) {
|
||||||
|
m_priv->cfgResize(std::move(interpolation));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams cv::gapi::ov::params(const std::string &tag,
|
||||||
|
const std::string &model_path,
|
||||||
|
const std::string &weights,
|
||||||
|
const std::string &device) {
|
||||||
|
return {tag, model_path, weights, device};
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::gapi::ov::PyParams cv::gapi::ov::params(const std::string &tag,
|
||||||
|
const std::string &blob_path,
|
||||||
|
const std::string &device) {
|
||||||
|
return {tag, blob_path, device};
|
||||||
|
}
|
1001
modules/gapi/src/backends/ov/govbackend.cpp
Normal file
1001
modules/gapi/src/backends/ov/govbackend.cpp
Normal file
File diff suppressed because it is too large
Load Diff
66
modules/gapi/src/backends/ov/govbackend.hpp
Normal file
66
modules/gapi/src/backends/ov/govbackend.hpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2023 Intel Corporation
|
||||||
|
|
||||||
|
#ifndef OPENCV_GAPI_GOVBACKEND_HPP
|
||||||
|
#define OPENCV_GAPI_GOVBACKEND_HPP
|
||||||
|
|
||||||
|
// Include anyway - cv::gapi::ov::backend() still needs to be defined
|
||||||
|
#include "opencv2/gapi/infer/ov.hpp"
|
||||||
|
|
||||||
|
#if defined HAVE_INF_ENGINE && INF_ENGINE_RELEASE >= 2022010000
|
||||||
|
|
||||||
|
#include <openvino/openvino.hpp>
|
||||||
|
|
||||||
|
#include "backends/common/gbackend.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace gimpl {
|
||||||
|
namespace ov {
|
||||||
|
|
||||||
|
struct OVCompiled {
|
||||||
|
::ov::CompiledModel compiled_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RequestPool;
|
||||||
|
|
||||||
|
class GOVExecutable final: public GIslandExecutable
|
||||||
|
{
|
||||||
|
const ade::Graph &m_g;
|
||||||
|
GModel::ConstGraph m_gm;
|
||||||
|
|
||||||
|
// The only executable stuff in this graph
|
||||||
|
// (assuming it is always single-op)
|
||||||
|
ade::NodeHandle this_nh;
|
||||||
|
OVCompiled compiled;
|
||||||
|
|
||||||
|
// List of all resources in graph (both internal and external)
|
||||||
|
std::vector<ade::NodeHandle> m_dataNodes;
|
||||||
|
|
||||||
|
// To manage multiple async requests
|
||||||
|
std::unique_ptr<RequestPool> m_reqPool;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GOVExecutable(const ade::Graph &graph,
|
||||||
|
const std::vector<ade::NodeHandle> &nodes);
|
||||||
|
|
||||||
|
virtual inline bool canReshape() const override { return false; }
|
||||||
|
virtual inline void reshape(ade::Graph&, const GCompileArgs&) override {
|
||||||
|
GAPI_Error("InternalError"); // Not implemented yet
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void run(std::vector<InObj> &&,
|
||||||
|
std::vector<OutObj> &&) override {
|
||||||
|
GAPI_Error("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void run(GIslandExecutable::IInput &in,
|
||||||
|
GIslandExecutable::IOutput &out) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}}}
|
||||||
|
|
||||||
|
#endif // HAVE_INF_ENGINE && INF_ENGINE_RELEASE >= 2022010000
|
||||||
|
#endif // OPENCV_GAPI_GOVBACKEND_HPP
|
35
modules/gapi/src/backends/ov/util.hpp
Normal file
35
modules/gapi/src/backends/ov/util.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2023 Intel Corporation
|
||||||
|
|
||||||
|
#ifndef OPENCV_GAPI_INFER_OV_UTIL_HPP
|
||||||
|
#define OPENCV_GAPI_INFER_OV_UTIL_HPP
|
||||||
|
|
||||||
|
#if defined HAVE_INF_ENGINE && INF_ENGINE_RELEASE >= 2022010000
|
||||||
|
|
||||||
|
// NOTE: This file is not included by default in infer/ov.hpp
|
||||||
|
// and won't be. infer/ov.hpp doesn't depend on OV headers itself.
|
||||||
|
// This file does -- so needs to be included separately by those who care.
|
||||||
|
|
||||||
|
#include <openvino/openvino.hpp>
|
||||||
|
|
||||||
|
#include <opencv2/core/cvdef.h> // GAPI_EXPORTS
|
||||||
|
#include <opencv2/gapi/gkernel.hpp> // GKernelPackage
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace gapi {
|
||||||
|
namespace ov {
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
// NB: These functions are EXPORTed to make them accessible by the
|
||||||
|
// test suite only.
|
||||||
|
GAPI_EXPORTS std::vector<int> to_ocv(const ::ov::Shape &shape);
|
||||||
|
GAPI_EXPORTS int to_ocv(const ::ov::element::Type &type);
|
||||||
|
|
||||||
|
}}}}
|
||||||
|
|
||||||
|
#endif // HAVE_INF_ENGINE && INF_ENGINE_RELEASE >= 2022010000
|
||||||
|
|
||||||
|
#endif // OPENCV_GAPI_INFER_OV_UTIL_HPP
|
@ -2,7 +2,7 @@
|
|||||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
// of this distribution and at http://opencv.org/license.html.
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2019-2021 Intel Corporation
|
// Copyright (C) 2019-2023 Intel Corporation
|
||||||
|
|
||||||
#include "../test_precomp.hpp"
|
#include "../test_precomp.hpp"
|
||||||
|
|
||||||
@ -2238,7 +2238,7 @@ TEST(TestAgeGenderIE, InferWithBatch)
|
|||||||
params.weights_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
params.weights_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
params.device_id = "CPU";
|
params.device_id = "CPU";
|
||||||
|
|
||||||
cv::Mat in_mat({batch_size, 3, 320, 240}, CV_8U);
|
cv::Mat in_mat({batch_size, 3, 62, 62}, CV_8U);
|
||||||
cv::randu(in_mat, 0, 255);
|
cv::randu(in_mat, 0, 255);
|
||||||
|
|
||||||
cv::Mat gapi_age, gapi_gender;
|
cv::Mat gapi_age, gapi_gender;
|
||||||
@ -2247,8 +2247,9 @@ TEST(TestAgeGenderIE, InferWithBatch)
|
|||||||
IE::Blob::Ptr ie_age, ie_gender;
|
IE::Blob::Ptr ie_age, ie_gender;
|
||||||
{
|
{
|
||||||
auto plugin = cv::gimpl::ie::wrap::getPlugin(params);
|
auto plugin = cv::gimpl::ie::wrap::getPlugin(params);
|
||||||
auto net = cv::gimpl::ie::wrap::readNetwork(params);
|
auto net = cv::gimpl::ie::wrap::readNetwork(params);
|
||||||
setNetParameters(net);
|
auto ii = net.getInputsInfo().at("data");
|
||||||
|
ii->setPrecision(IE::Precision::U8);
|
||||||
net.setBatchSize(batch_size);
|
net.setBatchSize(batch_size);
|
||||||
auto this_network = cv::gimpl::ie::wrap::loadNetwork(plugin, net, params);
|
auto this_network = cv::gimpl::ie::wrap::loadNetwork(plugin, net, params);
|
||||||
auto infer_request = this_network.CreateInferRequest();
|
auto infer_request = this_network.CreateInferRequest();
|
||||||
@ -3056,6 +3057,73 @@ TEST_F(AgeGenderInferTest, ChangeSpecificOutputPrecison) {
|
|||||||
validate();
|
validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(AgeGenderInferTest, ThrowIfSetLayoutForImage) {
|
||||||
|
auto pp = cv::gapi::ie::Params<AgeGender> {
|
||||||
|
m_params.model_path, m_params.weights_path, m_params.device_id
|
||||||
|
}.cfgOutputLayers({ "age_conv3", "prob" })
|
||||||
|
.cfgOutputPrecision({{"prob", CV_8U}})
|
||||||
|
.cfgInputLayout("NHWC");
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(buildGraph().apply(cv::gin(m_in_mat), cv::gout(m_gapi_age, m_gapi_gender),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderIE, InferTensorWithPreproc) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
|
||||||
|
cv::gapi::ie::detail::ParamDesc params;
|
||||||
|
params.model_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
params.weights_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
params.device_id = "CPU";
|
||||||
|
|
||||||
|
// Load IE network, initialize input data using that.
|
||||||
|
cv::Mat in_mat({1, 240, 320, 3}, CV_8U);
|
||||||
|
cv::randu(in_mat, 0, 255);
|
||||||
|
cv::Mat gapi_age, gapi_gender;
|
||||||
|
|
||||||
|
IE::Blob::Ptr ie_age, ie_gender;
|
||||||
|
{
|
||||||
|
auto plugin = cv::gimpl::ie::wrap::getPlugin(params);
|
||||||
|
auto net = cv::gimpl::ie::wrap::readNetwork(params);
|
||||||
|
auto ii = net.getInputsInfo().at("data");
|
||||||
|
|
||||||
|
ii->setPrecision(IE::Precision::U8);
|
||||||
|
ii->getPreProcess().setResizeAlgorithm(IE::RESIZE_BILINEAR);
|
||||||
|
ii->setLayout(IE::Layout::NHWC);
|
||||||
|
|
||||||
|
auto this_network = cv::gimpl::ie::wrap::loadNetwork(plugin, net, params);
|
||||||
|
auto infer_request = this_network.CreateInferRequest();
|
||||||
|
IE::TensorDesc desc{IE::Precision::U8, {1, 3, 240, 320}, IE::Layout::NHWC};
|
||||||
|
auto blob = IE::make_shared_blob<uint8_t>(desc, const_cast<uint8_t*>(in_mat.ptr<uint8_t>()));
|
||||||
|
infer_request.SetBlob("data", blob);
|
||||||
|
infer_request.Infer();
|
||||||
|
ie_age = infer_request.GetBlob("age_conv3");
|
||||||
|
ie_gender = infer_request.GetBlob("prob");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure & run G-API
|
||||||
|
using AGInfo = std::tuple<cv::GMat, cv::GMat>;
|
||||||
|
G_API_NET(AgeGender, <AGInfo(cv::GMat)>, "test-age-gender");
|
||||||
|
|
||||||
|
cv::GMat in;
|
||||||
|
cv::GMat age, gender;
|
||||||
|
std::tie(age, gender) = cv::gapi::infer<AgeGender>(in);
|
||||||
|
cv::GComputation comp(cv::GIn(in), cv::GOut(age, gender));
|
||||||
|
|
||||||
|
auto pp = cv::gapi::ie::Params<AgeGender> {
|
||||||
|
params.model_path, params.weights_path, params.device_id
|
||||||
|
}.cfgOutputLayers({ "age_conv3", "prob" })
|
||||||
|
.cfgResize(cv::INTER_LINEAR)
|
||||||
|
.cfgInputLayout("NHWC");
|
||||||
|
|
||||||
|
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp)));
|
||||||
|
|
||||||
|
// Validate with IE itself (avoid DNN module dependency here)
|
||||||
|
normAssert(cv::gapi::ie::util::to_ocv(ie_age), gapi_age, "Test age output" );
|
||||||
|
normAssert(cv::gapi::ie::util::to_ocv(ie_gender), gapi_gender, "Test gender output");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace opencv_test
|
} // namespace opencv_test
|
||||||
|
|
||||||
#endif // HAVE_INF_ENGINE
|
#endif // HAVE_INF_ENGINE
|
||||||
|
540
modules/gapi/test/infer/gapi_infer_ov_tests.cpp
Normal file
540
modules/gapi/test/infer/gapi_infer_ov_tests.cpp
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
//
|
||||||
|
// Copyright (C) 2023 Intel Corporation
|
||||||
|
|
||||||
|
#if defined HAVE_INF_ENGINE && INF_ENGINE_RELEASE >= 2022010000
|
||||||
|
|
||||||
|
#include "../test_precomp.hpp"
|
||||||
|
|
||||||
|
#include "backends/ov/util.hpp"
|
||||||
|
|
||||||
|
#include <opencv2/gapi/infer/ov.hpp>
|
||||||
|
|
||||||
|
#include <openvino/openvino.hpp>
|
||||||
|
|
||||||
|
namespace opencv_test
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// FIXME: taken from DNN module
|
||||||
|
void initDLDTDataPath()
|
||||||
|
{
|
||||||
|
#ifndef WINRT
|
||||||
|
static bool initialized = false;
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
const char* omzDataPath = getenv("OPENCV_OPEN_MODEL_ZOO_DATA_PATH");
|
||||||
|
if (omzDataPath)
|
||||||
|
cvtest::addDataSearchPath(omzDataPath);
|
||||||
|
const char* dnnDataPath = getenv("OPENCV_DNN_TEST_DATA_PATH");
|
||||||
|
if (dnnDataPath) {
|
||||||
|
// Add the dnnDataPath itself - G-API is using some images there directly
|
||||||
|
cvtest::addDataSearchPath(dnnDataPath);
|
||||||
|
cvtest::addDataSearchPath(dnnDataPath + std::string("/omz_intel_models"));
|
||||||
|
}
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
#endif // WINRT
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string SUBDIR = "intel/age-gender-recognition-retail-0013/FP32/";
|
||||||
|
|
||||||
|
void copyFromOV(ov::Tensor &tensor, cv::Mat &mat) {
|
||||||
|
GAPI_Assert(tensor.get_byte_size() == mat.total() * mat.elemSize());
|
||||||
|
std::copy_n(reinterpret_cast<uint8_t*>(tensor.data()),
|
||||||
|
tensor.get_byte_size(),
|
||||||
|
mat.ptr<uint8_t>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void copyToOV(const cv::Mat &mat, ov::Tensor &tensor) {
|
||||||
|
GAPI_Assert(tensor.get_byte_size() == mat.total() * mat.elemSize());
|
||||||
|
std::copy_n(mat.ptr<uint8_t>(),
|
||||||
|
tensor.get_byte_size(),
|
||||||
|
reinterpret_cast<uint8_t*>(tensor.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: taken from the DNN module
|
||||||
|
void normAssert(cv::InputArray ref, cv::InputArray test,
|
||||||
|
const char *comment /*= ""*/,
|
||||||
|
double l1 = 0.00001, double lInf = 0.0001) {
|
||||||
|
double normL1 = cvtest::norm(ref, test, cv::NORM_L1) / ref.getMat().total();
|
||||||
|
EXPECT_LE(normL1, l1) << comment;
|
||||||
|
|
||||||
|
double normInf = cvtest::norm(ref, test, cv::NORM_INF);
|
||||||
|
EXPECT_LE(normInf, lInf) << comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
ov::Core getCore() {
|
||||||
|
static ov::Core core;
|
||||||
|
return core;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: AGNetGenComp, AGNetTypedComp, AGNetOVComp, AGNetOVCompiled
|
||||||
|
// can be generalized to work with any model and used as parameters for tests.
|
||||||
|
|
||||||
|
struct AGNetGenComp {
|
||||||
|
static constexpr const char* tag = "age-gender-generic";
|
||||||
|
using Params = cv::gapi::ov::Params<cv::gapi::Generic>;
|
||||||
|
|
||||||
|
static Params params(const std::string &xml,
|
||||||
|
const std::string &bin,
|
||||||
|
const std::string &device) {
|
||||||
|
return {tag, xml, bin, device};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Params params(const std::string &blob_path,
|
||||||
|
const std::string &device) {
|
||||||
|
return {tag, blob_path, device};
|
||||||
|
}
|
||||||
|
|
||||||
|
static cv::GComputation create() {
|
||||||
|
cv::GMat in;
|
||||||
|
GInferInputs inputs;
|
||||||
|
inputs["data"] = in;
|
||||||
|
auto outputs = cv::gapi::infer<cv::gapi::Generic>(tag, inputs);
|
||||||
|
auto age = outputs.at("age_conv3");
|
||||||
|
auto gender = outputs.at("prob");
|
||||||
|
return cv::GComputation{cv::GIn(in), cv::GOut(age, gender)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AGNetTypedComp {
|
||||||
|
using AGInfo = std::tuple<cv::GMat, cv::GMat>;
|
||||||
|
G_API_NET(AgeGender, <AGInfo(cv::GMat)>, "typed-age-gender");
|
||||||
|
using Params = cv::gapi::ov::Params<AgeGender>;
|
||||||
|
|
||||||
|
static Params params(const std::string &xml_path,
|
||||||
|
const std::string &bin_path,
|
||||||
|
const std::string &device) {
|
||||||
|
return Params {
|
||||||
|
xml_path, bin_path, device
|
||||||
|
}.cfgOutputLayers({ "age_conv3", "prob" });
|
||||||
|
}
|
||||||
|
|
||||||
|
static cv::GComputation create() {
|
||||||
|
cv::GMat in;
|
||||||
|
cv::GMat age, gender;
|
||||||
|
std::tie(age, gender) = cv::gapi::infer<AgeGender>(in);
|
||||||
|
return cv::GComputation{cv::GIn(in), cv::GOut(age, gender)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class AGNetOVCompiled {
|
||||||
|
public:
|
||||||
|
AGNetOVCompiled(ov::CompiledModel &&compiled_model)
|
||||||
|
: m_compiled_model(std::move(compiled_model)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const cv::Mat &in_mat,
|
||||||
|
cv::Mat &age_mat,
|
||||||
|
cv::Mat &gender_mat) {
|
||||||
|
auto infer_request = m_compiled_model.create_infer_request();
|
||||||
|
auto input_tensor = infer_request.get_input_tensor();
|
||||||
|
copyToOV(in_mat, input_tensor);
|
||||||
|
|
||||||
|
infer_request.infer();
|
||||||
|
|
||||||
|
auto age_tensor = infer_request.get_tensor("age_conv3");
|
||||||
|
age_mat.create(cv::gapi::ov::util::to_ocv(age_tensor.get_shape()),
|
||||||
|
cv::gapi::ov::util::to_ocv(age_tensor.get_element_type()));
|
||||||
|
copyFromOV(age_tensor, age_mat);
|
||||||
|
|
||||||
|
auto gender_tensor = infer_request.get_tensor("prob");
|
||||||
|
gender_mat.create(cv::gapi::ov::util::to_ocv(gender_tensor.get_shape()),
|
||||||
|
cv::gapi::ov::util::to_ocv(gender_tensor.get_element_type()));
|
||||||
|
copyFromOV(gender_tensor, gender_mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void export_model(const std::string &outpath) {
|
||||||
|
std::ofstream file{outpath, std::ios::out | std::ios::binary};
|
||||||
|
GAPI_Assert(file.is_open());
|
||||||
|
m_compiled_model.export_model(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ov::CompiledModel m_compiled_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImageInputPreproc {
|
||||||
|
void operator()(ov::preprocess::PrePostProcessor &ppp) {
|
||||||
|
ppp.input().tensor().set_layout(ov::Layout("NHWC"))
|
||||||
|
.set_element_type(ov::element::u8)
|
||||||
|
.set_shape({1, size.height, size.width, 3});
|
||||||
|
ppp.input().model().set_layout(ov::Layout("NCHW"));
|
||||||
|
ppp.input().preprocess().resize(::ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Size size;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AGNetOVComp {
|
||||||
|
public:
|
||||||
|
AGNetOVComp(const std::string &xml_path,
|
||||||
|
const std::string &bin_path,
|
||||||
|
const std::string &device)
|
||||||
|
: m_device(device) {
|
||||||
|
m_model = getCore().read_model(xml_path, bin_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
using PrePostProcessF = std::function<void(ov::preprocess::PrePostProcessor&)>;
|
||||||
|
|
||||||
|
void cfgPrePostProcessing(PrePostProcessF f) {
|
||||||
|
ov::preprocess::PrePostProcessor ppp(m_model);
|
||||||
|
f(ppp);
|
||||||
|
m_model = ppp.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
AGNetOVCompiled compile() {
|
||||||
|
auto compiled_model = getCore().compile_model(m_model, m_device);
|
||||||
|
return {std::move(compiled_model)};
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(const cv::Mat &in_mat,
|
||||||
|
cv::Mat &age_mat,
|
||||||
|
cv::Mat &gender_mat) {
|
||||||
|
compile()(in_mat, age_mat, gender_mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_device;
|
||||||
|
std::shared_ptr<ov::Model> m_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
// TODO: Make all of tests below parmetrized to avoid code duplication
|
||||||
|
TEST(TestAgeGenderOV, InferTypedTensor) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
cv::Mat in_mat({1, 3, 62, 62}, CV_32F);
|
||||||
|
cv::randu(in_mat, -1, 1);
|
||||||
|
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
|
||||||
|
|
||||||
|
// OpenVINO
|
||||||
|
AGNetOVComp ref(xml_path, bin_path, device);
|
||||||
|
ref.apply(in_mat, ov_age, ov_gender);
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetTypedComp::create();
|
||||||
|
auto pp = AGNetTypedComp::params(xml_path, bin_path, device);
|
||||||
|
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp)));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
normAssert(ov_age, gapi_age, "Test age output" );
|
||||||
|
normAssert(ov_gender, gapi_gender, "Test gender output");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, InferTypedImage) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
cv::Mat in_mat(300, 300, CV_8UC3);
|
||||||
|
cv::randu(in_mat, 0, 255);
|
||||||
|
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
|
||||||
|
|
||||||
|
// OpenVINO
|
||||||
|
AGNetOVComp ref(xml_path, bin_path, device);
|
||||||
|
ref.cfgPrePostProcessing(ImageInputPreproc{in_mat.size()});
|
||||||
|
ref.apply(in_mat, ov_age, ov_gender);
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetTypedComp::create();
|
||||||
|
auto pp = AGNetTypedComp::params(xml_path, bin_path, device);
|
||||||
|
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp)));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
normAssert(ov_age, gapi_age, "Test age output" );
|
||||||
|
normAssert(ov_gender, gapi_gender, "Test gender output");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, InferGenericTensor) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
cv::Mat in_mat({1, 3, 62, 62}, CV_32F);
|
||||||
|
cv::randu(in_mat, -1, 1);
|
||||||
|
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
|
||||||
|
|
||||||
|
// OpenVINO
|
||||||
|
AGNetOVComp ref(xml_path, bin_path, device);
|
||||||
|
ref.apply(in_mat, ov_age, ov_gender);
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetGenComp::create();
|
||||||
|
auto pp = AGNetGenComp::params(xml_path, bin_path, device);
|
||||||
|
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp)));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
normAssert(ov_age, gapi_age, "Test age output" );
|
||||||
|
normAssert(ov_gender, gapi_gender, "Test gender output");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, InferGenericImage) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
cv::Mat in_mat(300, 300, CV_8UC3);
|
||||||
|
cv::randu(in_mat, 0, 255);
|
||||||
|
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
|
||||||
|
|
||||||
|
// OpenVINO
|
||||||
|
AGNetOVComp ref(xml_path, bin_path, device);
|
||||||
|
ref.cfgPrePostProcessing(ImageInputPreproc{in_mat.size()});
|
||||||
|
ref.apply(in_mat, ov_age, ov_gender);
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetGenComp::create();
|
||||||
|
auto pp = AGNetGenComp::params(xml_path, bin_path, device);
|
||||||
|
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp)));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
normAssert(ov_age, gapi_age, "Test age output" );
|
||||||
|
normAssert(ov_gender, gapi_gender, "Test gender output");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, InferGenericImageBlob) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string blob_path = "age-gender-recognition-retail-0013.blob";
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
cv::Mat in_mat(300, 300, CV_8UC3);
|
||||||
|
cv::randu(in_mat, 0, 255);
|
||||||
|
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
|
||||||
|
|
||||||
|
// OpenVINO
|
||||||
|
AGNetOVComp ref(xml_path, bin_path, device);
|
||||||
|
ref.cfgPrePostProcessing(ImageInputPreproc{in_mat.size()});
|
||||||
|
auto cc_ref = ref.compile();
|
||||||
|
// NB: Output blob will contain preprocessing inside.
|
||||||
|
cc_ref.export_model(blob_path);
|
||||||
|
cc_ref(in_mat, ov_age, ov_gender);
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetGenComp::create();
|
||||||
|
auto pp = AGNetGenComp::params(blob_path, device);
|
||||||
|
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp)));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
normAssert(ov_age, gapi_age, "Test age output" );
|
||||||
|
normAssert(ov_gender, gapi_gender, "Test gender output");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, InferGenericTensorBlob) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string blob_path = "age-gender-recognition-retail-0013.blob";
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
cv::Mat in_mat({1, 3, 62, 62}, CV_32F);
|
||||||
|
cv::randu(in_mat, -1, 1);
|
||||||
|
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
|
||||||
|
|
||||||
|
// OpenVINO
|
||||||
|
AGNetOVComp ref(xml_path, bin_path, device);
|
||||||
|
auto cc_ref = ref.compile();
|
||||||
|
cc_ref.export_model(blob_path);
|
||||||
|
cc_ref(in_mat, ov_age, ov_gender);
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetGenComp::create();
|
||||||
|
auto pp = AGNetGenComp::params(blob_path, device);
|
||||||
|
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp)));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
normAssert(ov_age, gapi_age, "Test age output" );
|
||||||
|
normAssert(ov_gender, gapi_gender, "Test gender output");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, InferBothOutputsFP16) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
cv::Mat in_mat({1, 3, 62, 62}, CV_32F);
|
||||||
|
cv::randu(in_mat, -1, 1);
|
||||||
|
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
|
||||||
|
|
||||||
|
// OpenVINO
|
||||||
|
AGNetOVComp ref(xml_path, bin_path, device);
|
||||||
|
ref.cfgPrePostProcessing([](ov::preprocess::PrePostProcessor &ppp){
|
||||||
|
ppp.output(0).tensor().set_element_type(ov::element::f16);
|
||||||
|
ppp.output(1).tensor().set_element_type(ov::element::f16);
|
||||||
|
});
|
||||||
|
ref.apply(in_mat, ov_age, ov_gender);
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetGenComp::create();
|
||||||
|
auto pp = AGNetGenComp::params(xml_path, bin_path, device);
|
||||||
|
pp.cfgOutputTensorPrecision(CV_16F);
|
||||||
|
|
||||||
|
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp)));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
normAssert(ov_age, gapi_age, "Test age output" );
|
||||||
|
normAssert(ov_gender, gapi_gender, "Test gender output");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, InferOneOutputFP16) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
cv::Mat in_mat({1, 3, 62, 62}, CV_32F);
|
||||||
|
cv::randu(in_mat, -1, 1);
|
||||||
|
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
|
||||||
|
|
||||||
|
// OpenVINO
|
||||||
|
const std::string fp16_output_name = "prob";
|
||||||
|
AGNetOVComp ref(xml_path, bin_path, device);
|
||||||
|
ref.cfgPrePostProcessing([&](ov::preprocess::PrePostProcessor &ppp){
|
||||||
|
ppp.output(fp16_output_name).tensor().set_element_type(ov::element::f16);
|
||||||
|
});
|
||||||
|
ref.apply(in_mat, ov_age, ov_gender);
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetGenComp::create();
|
||||||
|
auto pp = AGNetGenComp::params(xml_path, bin_path, device);
|
||||||
|
pp.cfgOutputTensorPrecision({{fp16_output_name, CV_16F}});
|
||||||
|
|
||||||
|
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp)));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
normAssert(ov_age, gapi_age, "Test age output" );
|
||||||
|
normAssert(ov_gender, gapi_gender, "Test gender output");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, ThrowCfgOutputPrecForBlob) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string blob_path = "age-gender-recognition-retail-0013.blob";
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
// OpenVINO (Just for blob compilation)
|
||||||
|
AGNetOVComp ref(xml_path, bin_path, device);
|
||||||
|
auto cc_ref = ref.compile();
|
||||||
|
cc_ref.export_model(blob_path);
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetGenComp::create();
|
||||||
|
auto pp = AGNetGenComp::params(blob_path, device);
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(pp.cfgOutputTensorPrecision(CV_16F));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, ThrowInvalidConfigIR) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetGenComp::create();
|
||||||
|
auto pp = AGNetGenComp::params(xml_path, bin_path, device);
|
||||||
|
pp.cfgPluginConfig({{"some_key", "some_value"}});
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(comp.compile(cv::GMatDesc{CV_8U,3,cv::Size{320, 240}},
|
||||||
|
cv::compile_args(cv::gapi::networks(pp))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, ThrowInvalidConfigBlob) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string blob_path = "age-gender-recognition-retail-0013.blob";
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
// OpenVINO (Just for blob compilation)
|
||||||
|
AGNetOVComp ref(xml_path, bin_path, device);
|
||||||
|
auto cc_ref = ref.compile();
|
||||||
|
cc_ref.export_model(blob_path);
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetGenComp::create();
|
||||||
|
auto pp = AGNetGenComp::params(blob_path, device);
|
||||||
|
pp.cfgPluginConfig({{"some_key", "some_value"}});
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(comp.compile(cv::GMatDesc{CV_8U,3,cv::Size{320, 240}},
|
||||||
|
cv::compile_args(cv::gapi::networks(pp))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, ThrowInvalidImageLayout) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
// NB: This mat may only have "NHWC" layout.
|
||||||
|
cv::Mat in_mat(300, 300, CV_8UC3);
|
||||||
|
cv::randu(in_mat, 0, 255);
|
||||||
|
cv::Mat gender, gapi_age, gapi_gender;
|
||||||
|
auto comp = AGNetTypedComp::create();
|
||||||
|
auto pp = AGNetTypedComp::params(xml_path, bin_path, device);
|
||||||
|
|
||||||
|
pp.cfgInputTensorLayout("NCHW");
|
||||||
|
|
||||||
|
EXPECT_ANY_THROW(comp.compile(cv::descr_of(in_mat),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestAgeGenderOV, InferTensorWithPreproc) {
|
||||||
|
initDLDTDataPath();
|
||||||
|
const std::string xml_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.xml");
|
||||||
|
const std::string bin_path = findDataFile(SUBDIR + "age-gender-recognition-retail-0013.bin");
|
||||||
|
const std::string device = "CPU";
|
||||||
|
|
||||||
|
cv::Mat in_mat({1, 240, 320, 3}, CV_32F);
|
||||||
|
cv::randu(in_mat, -1, 1);
|
||||||
|
cv::Mat ov_age, ov_gender, gapi_age, gapi_gender;
|
||||||
|
|
||||||
|
// OpenVINO
|
||||||
|
AGNetOVComp ref(xml_path, bin_path, device);
|
||||||
|
ref.cfgPrePostProcessing([](ov::preprocess::PrePostProcessor &ppp) {
|
||||||
|
auto& input = ppp.input();
|
||||||
|
input.tensor().set_spatial_static_shape(240, 320)
|
||||||
|
.set_layout("NHWC");
|
||||||
|
input.preprocess().resize(ov::preprocess::ResizeAlgorithm::RESIZE_LINEAR);
|
||||||
|
});
|
||||||
|
ref.apply(in_mat, ov_age, ov_gender);
|
||||||
|
|
||||||
|
// G-API
|
||||||
|
auto comp = AGNetTypedComp::create();
|
||||||
|
auto pp = AGNetTypedComp::params(xml_path, bin_path, device);
|
||||||
|
pp.cfgResize(cv::INTER_LINEAR)
|
||||||
|
.cfgInputTensorLayout("NHWC");
|
||||||
|
|
||||||
|
comp.apply(cv::gin(in_mat), cv::gout(gapi_age, gapi_gender),
|
||||||
|
cv::compile_args(cv::gapi::networks(pp)));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
normAssert(ov_age, gapi_age, "Test age output" );
|
||||||
|
normAssert(ov_gender, gapi_gender, "Test gender output");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace opencv_test
|
||||||
|
|
||||||
|
#endif // HAVE_INF_ENGINE && INF_ENGINE_RELEASE >= 2022010000
|
@ -13,6 +13,11 @@ if(HAVE_WINRT_CX AND NOT WINRT)
|
|||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /ZW")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (HAVE_AVIF)
|
||||||
|
ocv_include_directories(${AVIF_INCLUDE_DIR})
|
||||||
|
list(APPEND GRFMT_LIBS ${AVIF_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
if(HAVE_JPEG)
|
if(HAVE_JPEG)
|
||||||
ocv_include_directories(${JPEG_INCLUDE_DIR} ${${JPEG_LIBRARY}_BINARY_DIR})
|
ocv_include_directories(${JPEG_INCLUDE_DIR} ${${JPEG_LIBRARY}_BINARY_DIR})
|
||||||
list(APPEND GRFMT_LIBS ${JPEG_LIBRARIES})
|
list(APPEND GRFMT_LIBS ${JPEG_LIBRARIES})
|
||||||
|
@ -105,7 +105,10 @@ enum ImwriteFlags {
|
|||||||
IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI
|
IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI
|
||||||
IMWRITE_TIFF_YDPI = 258,//!< For TIFF, use to specify the Y direction DPI
|
IMWRITE_TIFF_YDPI = 258,//!< For TIFF, use to specify the Y direction DPI
|
||||||
IMWRITE_TIFF_COMPRESSION = 259,//!< For TIFF, use to specify the image compression scheme. See libtiff for integer constants corresponding to compression formats. Note, for images whose depth is CV_32F, only libtiff's SGILOG compression scheme is used. For other supported depths, the compression scheme can be specified by this flag; LZW compression is the default.
|
IMWRITE_TIFF_COMPRESSION = 259,//!< For TIFF, use to specify the image compression scheme. See libtiff for integer constants corresponding to compression formats. Note, for images whose depth is CV_32F, only libtiff's SGILOG compression scheme is used. For other supported depths, the compression scheme can be specified by this flag; LZW compression is the default.
|
||||||
IMWRITE_JPEG2000_COMPRESSION_X1000 = 272 //!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000.
|
IMWRITE_JPEG2000_COMPRESSION_X1000 = 272,//!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000.
|
||||||
|
IMWRITE_AVIF_QUALITY = 512,//!< For AVIF, it can be a quality between 0 and 100 (the higher the better). Default is 95.
|
||||||
|
IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_32F. Default is 8.
|
||||||
|
IMWRITE_AVIF_SPEED = 514 //!< For AVIF, it is between 0 (slowest) and (fastest). Default is 9.
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ImwriteJPEGSamplingFactorParams {
|
enum ImwriteJPEGSamplingFactorParams {
|
||||||
@ -185,6 +188,7 @@ Currently, the following file formats are supported:
|
|||||||
- JPEG 2000 files - \*.jp2 (see the *Note* section)
|
- JPEG 2000 files - \*.jp2 (see the *Note* section)
|
||||||
- Portable Network Graphics - \*.png (see the *Note* section)
|
- Portable Network Graphics - \*.png (see the *Note* section)
|
||||||
- WebP - \*.webp (see the *Note* section)
|
- WebP - \*.webp (see the *Note* section)
|
||||||
|
- AVIF - \*.avif (see the *Note* section)
|
||||||
- Portable image format - \*.pbm, \*.pgm, \*.ppm \*.pxm, \*.pnm (always supported)
|
- Portable image format - \*.pbm, \*.pgm, \*.ppm \*.pxm, \*.pnm (always supported)
|
||||||
- PFM files - \*.pfm (see the *Note* section)
|
- PFM files - \*.pfm (see the *Note* section)
|
||||||
- Sun rasters - \*.sr, \*.ras (always supported)
|
- Sun rasters - \*.sr, \*.ras (always supported)
|
||||||
|
369
modules/imgcodecs/src/grfmt_avif.cpp
Normal file
369
modules/imgcodecs/src/grfmt_avif.cpp
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level
|
||||||
|
// directory of this distribution and at http://opencv.org/license.html
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
|
||||||
|
#ifdef HAVE_AVIF
|
||||||
|
|
||||||
|
#include <avif/avif.h>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include <opencv2/core/utils/configuration.private.hpp>
|
||||||
|
#include "opencv2/imgproc.hpp"
|
||||||
|
#include "grfmt_avif.hpp"
|
||||||
|
|
||||||
|
#define CV_AVIF_USE_QUALITY \
|
||||||
|
(AVIF_VERSION > ((0 * 1000000) + (11 * 10000) + (1 * 100)))
|
||||||
|
|
||||||
|
#if !CV_AVIF_USE_QUALITY
|
||||||
|
#define AVIF_QUALITY_LOSSLESS 100
|
||||||
|
#define AVIF_QUALITY_WORST 0
|
||||||
|
#define AVIF_QUALITY_BEST 100
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct AvifImageDeleter {
|
||||||
|
void operator()(avifImage *image) { avifImageDestroy(image); }
|
||||||
|
};
|
||||||
|
|
||||||
|
using AvifImageUniquePtr = std::unique_ptr<avifImage, AvifImageDeleter>;
|
||||||
|
|
||||||
|
avifResult CopyToMat(const avifImage *image, int channels, Mat *mat) {
|
||||||
|
CV_Assert((int)image->height == mat->rows);
|
||||||
|
CV_Assert((int)image->width == mat->cols);
|
||||||
|
if (channels == 1) {
|
||||||
|
const cv::Mat image_wrap =
|
||||||
|
cv::Mat(image->height, image->width,
|
||||||
|
CV_MAKE_TYPE((image->depth == 8) ? CV_8U : CV_16U, 1),
|
||||||
|
image->yuvPlanes[0], image->yuvRowBytes[0]);
|
||||||
|
if ((image->depth == 8 && mat->depth() == CV_8U) ||
|
||||||
|
(image->depth > 8 && mat->depth() == CV_16U)) {
|
||||||
|
image_wrap.copyTo(*mat);
|
||||||
|
} else {
|
||||||
|
CV_Assert(image->depth > 8 && mat->depth() == CV_8U);
|
||||||
|
image_wrap.convertTo(*mat, CV_8U, 1. / (1 << (image->depth - 8)));
|
||||||
|
}
|
||||||
|
return AVIF_RESULT_OK;
|
||||||
|
}
|
||||||
|
avifRGBImage rgba;
|
||||||
|
avifRGBImageSetDefaults(&rgba, image);
|
||||||
|
if (channels == 3) {
|
||||||
|
rgba.format = AVIF_RGB_FORMAT_BGR;
|
||||||
|
} else {
|
||||||
|
CV_Assert(channels == 4);
|
||||||
|
rgba.format = AVIF_RGB_FORMAT_BGRA;
|
||||||
|
}
|
||||||
|
rgba.rowBytes = mat->step[0];
|
||||||
|
rgba.depth = (mat->depth() == CV_16U) ? image->depth : 8;
|
||||||
|
rgba.pixels = reinterpret_cast<uint8_t *>(mat->data);
|
||||||
|
return avifImageYUVToRGB(image, &rgba);
|
||||||
|
}
|
||||||
|
|
||||||
|
AvifImageUniquePtr ConvertToAvif(const cv::Mat &img, bool lossless,
|
||||||
|
int bit_depth) {
|
||||||
|
CV_Assert(img.depth() == CV_8U || img.depth() == CV_16U);
|
||||||
|
|
||||||
|
const int width = img.cols;
|
||||||
|
const int height = img.rows;
|
||||||
|
|
||||||
|
avifImage *result;
|
||||||
|
|
||||||
|
if (img.channels() == 1) {
|
||||||
|
result = avifImageCreateEmpty();
|
||||||
|
if (result == nullptr) return nullptr;
|
||||||
|
result->width = width;
|
||||||
|
result->height = height;
|
||||||
|
result->depth = bit_depth;
|
||||||
|
result->yuvFormat = AVIF_PIXEL_FORMAT_YUV400;
|
||||||
|
result->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED;
|
||||||
|
result->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED;
|
||||||
|
result->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY;
|
||||||
|
result->yuvRange = AVIF_RANGE_FULL;
|
||||||
|
result->yuvPlanes[0] = img.data;
|
||||||
|
result->yuvRowBytes[0] = img.step[0];
|
||||||
|
result->imageOwnsYUVPlanes = AVIF_FALSE;
|
||||||
|
return AvifImageUniquePtr(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lossless) {
|
||||||
|
result =
|
||||||
|
avifImageCreate(width, height, bit_depth, AVIF_PIXEL_FORMAT_YUV444);
|
||||||
|
if (result == nullptr) return nullptr;
|
||||||
|
result->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED;
|
||||||
|
result->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED;
|
||||||
|
result->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY;
|
||||||
|
result->yuvRange = AVIF_RANGE_FULL;
|
||||||
|
} else {
|
||||||
|
result =
|
||||||
|
avifImageCreate(width, height, bit_depth, AVIF_PIXEL_FORMAT_YUV420);
|
||||||
|
if (result == nullptr) return nullptr;
|
||||||
|
result->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
|
||||||
|
result->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
|
||||||
|
result->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601;
|
||||||
|
result->yuvRange = AVIF_RANGE_FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
avifRGBImage rgba;
|
||||||
|
avifRGBImageSetDefaults(&rgba, result);
|
||||||
|
if (img.channels() == 3) {
|
||||||
|
rgba.format = AVIF_RGB_FORMAT_BGR;
|
||||||
|
} else {
|
||||||
|
CV_Assert(img.channels() == 4);
|
||||||
|
rgba.format = AVIF_RGB_FORMAT_BGRA;
|
||||||
|
}
|
||||||
|
rgba.rowBytes = img.step[0];
|
||||||
|
rgba.depth = bit_depth;
|
||||||
|
rgba.pixels =
|
||||||
|
const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(img.data));
|
||||||
|
|
||||||
|
if (avifImageRGBToYUV(result, &rgba) != AVIF_RESULT_OK) {
|
||||||
|
avifImageDestroy(result);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return AvifImageUniquePtr(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// 64Mb limit to avoid memory saturation.
|
||||||
|
static const size_t kParamMaxFileSize = utils::getConfigurationParameterSizeT(
|
||||||
|
"OPENCV_IMGCODECS_AVIF_MAX_FILE_SIZE", 64 * 1024 * 1024);
|
||||||
|
|
||||||
|
static constexpr size_t kAvifSignatureSize = 500;
|
||||||
|
|
||||||
|
AvifDecoder::AvifDecoder() {
|
||||||
|
m_buf_supported = true;
|
||||||
|
channels_ = 0;
|
||||||
|
decoder_ = avifDecoderCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
AvifDecoder::~AvifDecoder() {
|
||||||
|
if (decoder_ != nullptr) avifDecoderDestroy(decoder_);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AvifDecoder::signatureLength() const { return kAvifSignatureSize; }
|
||||||
|
|
||||||
|
bool AvifDecoder::checkSignature(const String &signature) const {
|
||||||
|
avifDecoderSetIOMemory(decoder_,
|
||||||
|
reinterpret_cast<const uint8_t *>(signature.c_str()),
|
||||||
|
signature.size());
|
||||||
|
decoder_->io->sizeHint = 1e9;
|
||||||
|
const avifResult status = avifDecoderParse(decoder_);
|
||||||
|
return (status == AVIF_RESULT_OK || status == AVIF_RESULT_TRUNCATED_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OPENCV_AVIF_CHECK_STATUS(X, ENCDEC) \
|
||||||
|
{ \
|
||||||
|
const avifResult status = (X); \
|
||||||
|
if (status != AVIF_RESULT_OK) { \
|
||||||
|
const std::string error(ENCDEC->diag.error); \
|
||||||
|
CV_Error(Error::StsParseError, \
|
||||||
|
error + " " + avifResultToString(status)); \
|
||||||
|
return false; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageDecoder AvifDecoder::newDecoder() const { return makePtr<AvifDecoder>(); }
|
||||||
|
|
||||||
|
bool AvifDecoder::readHeader() {
|
||||||
|
if (!m_buf.empty()) {
|
||||||
|
CV_Assert(m_buf.type() == CV_8UC1);
|
||||||
|
CV_Assert(m_buf.rows == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
OPENCV_AVIF_CHECK_STATUS(
|
||||||
|
m_buf.empty()
|
||||||
|
? avifDecoderSetIOFile(decoder_, m_filename.c_str())
|
||||||
|
: avifDecoderSetIOMemory(
|
||||||
|
decoder_, reinterpret_cast<const uint8_t *>(m_buf.data),
|
||||||
|
m_buf.total()),
|
||||||
|
decoder_);
|
||||||
|
OPENCV_AVIF_CHECK_STATUS(avifDecoderParse(decoder_), decoder_);
|
||||||
|
|
||||||
|
m_width = decoder_->image->width;
|
||||||
|
m_height = decoder_->image->height;
|
||||||
|
channels_ = (decoder_->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) ? 1 : 3;
|
||||||
|
if (decoder_->alphaPresent) ++channels_;
|
||||||
|
bit_depth_ = decoder_->image->depth;
|
||||||
|
CV_Assert(bit_depth_ == 8 || bit_depth_ == 10 || bit_depth_ == 12);
|
||||||
|
m_type = CV_MAKETYPE(bit_depth_ == 8 ? CV_8U : CV_16U, channels_);
|
||||||
|
is_first_image_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvifDecoder::readData(Mat &img) {
|
||||||
|
CV_CheckGE(m_width, 0, "");
|
||||||
|
CV_CheckGE(m_height, 0, "");
|
||||||
|
|
||||||
|
CV_CheckEQ(img.cols, m_width, "");
|
||||||
|
CV_CheckEQ(img.rows, m_height, "");
|
||||||
|
CV_CheckType(
|
||||||
|
img.type(),
|
||||||
|
(img.channels() == 1 || img.channels() == 3 || img.channels() == 4) &&
|
||||||
|
(img.depth() == CV_8U || img.depth() == CV_16U),
|
||||||
|
"AVIF only supports 1, 3, 4 channels and CV_8U and CV_16U");
|
||||||
|
|
||||||
|
Mat read_img;
|
||||||
|
if (img.channels() == channels_) {
|
||||||
|
read_img = img;
|
||||||
|
} else {
|
||||||
|
// Use the asked depth but keep the number of channels. OpenCV and not
|
||||||
|
// libavif will do the color conversion.
|
||||||
|
read_img.create(m_height, m_width, CV_MAKE_TYPE(img.depth(), channels_));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_first_image_) {
|
||||||
|
if (!nextPage()) return false;
|
||||||
|
is_first_image_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CopyToMat(decoder_->image, channels_, &read_img) != AVIF_RESULT_OK) {
|
||||||
|
CV_Error(Error::StsInternal, "Cannot convert from AVIF to Mat");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoder_->image->exif.size > 0) {
|
||||||
|
m_exif.parseExif(decoder_->image->exif.data, decoder_->image->exif.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (img.channels() == channels_) {
|
||||||
|
// We already wrote to the right buffer.
|
||||||
|
} else {
|
||||||
|
if (channels_ == 1 && img.channels() == 3) {
|
||||||
|
cvtColor(read_img, img, COLOR_GRAY2BGR);
|
||||||
|
} else if (channels_ == 1 && img.channels() == 4) {
|
||||||
|
cvtColor(read_img, img, COLOR_GRAY2BGRA);
|
||||||
|
} else if (channels_ == 3 && img.channels() == 1) {
|
||||||
|
cvtColor(read_img, img, COLOR_BGR2GRAY);
|
||||||
|
} else if (channels_ == 3 && img.channels() == 4) {
|
||||||
|
cvtColor(read_img, img, COLOR_BGR2BGRA);
|
||||||
|
} else if (channels_ == 4 && img.channels() == 1) {
|
||||||
|
cvtColor(read_img, img, COLOR_BGRA2GRAY);
|
||||||
|
} else if (channels_ == 4 && img.channels() == 3) {
|
||||||
|
cvtColor(read_img, img, COLOR_BGRA2BGR);
|
||||||
|
} else {
|
||||||
|
CV_Error(Error::StsInternal, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvifDecoder::nextPage() {
|
||||||
|
const avifResult status = avifDecoderNextImage(decoder_);
|
||||||
|
if (status == AVIF_RESULT_NO_IMAGES_REMAINING) return false;
|
||||||
|
if (status != AVIF_RESULT_OK) {
|
||||||
|
const std::string error(decoder_->diag.error);
|
||||||
|
CV_Error(Error::StsParseError, error + " " + avifResultToString(status));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
AvifEncoder::AvifEncoder() {
|
||||||
|
m_description = "AVIF files (*.avif)";
|
||||||
|
m_buf_supported = true;
|
||||||
|
encoder_ = avifEncoderCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
AvifEncoder::~AvifEncoder() {
|
||||||
|
if (encoder_) avifEncoderDestroy(encoder_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvifEncoder::isFormatSupported(int depth) const {
|
||||||
|
return (depth == CV_8U || depth == CV_16U);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvifEncoder::write(const Mat &img, const std::vector<int> ¶ms) {
|
||||||
|
std::vector<Mat> img_vec(1, img);
|
||||||
|
return writeToOutput(img_vec, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvifEncoder::writemulti(const std::vector<Mat> &img_vec,
|
||||||
|
const std::vector<int> ¶ms) {
|
||||||
|
return writeToOutput(img_vec, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvifEncoder::writeToOutput(const std::vector<Mat> &img_vec,
|
||||||
|
const std::vector<int> ¶ms) {
|
||||||
|
int bit_depth = 8;
|
||||||
|
int speed = AVIF_SPEED_FASTEST;
|
||||||
|
for (size_t i = 0; i < params.size(); i += 2) {
|
||||||
|
if (params[i] == IMWRITE_AVIF_QUALITY) {
|
||||||
|
const int quality = std::min(std::max(params[i + 1], AVIF_QUALITY_WORST),
|
||||||
|
AVIF_QUALITY_BEST);
|
||||||
|
#if CV_AVIF_USE_QUALITY
|
||||||
|
encoder_->quality = quality;
|
||||||
|
#else
|
||||||
|
encoder_->minQuantizer = encoder_->maxQuantizer =
|
||||||
|
(AVIF_QUANTIZER_BEST_QUALITY - AVIF_QUANTIZER_WORST_QUALITY) *
|
||||||
|
quality / (AVIF_QUALITY_BEST - AVIF_QUALITY_WORST) +
|
||||||
|
AVIF_QUANTIZER_WORST_QUALITY;
|
||||||
|
#endif
|
||||||
|
} else if (params[i] == IMWRITE_AVIF_DEPTH) {
|
||||||
|
bit_depth = params[i + 1];
|
||||||
|
} else if (params[i] == IMWRITE_AVIF_SPEED) {
|
||||||
|
speed = params[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
avifRWData output_ori = AVIF_DATA_EMPTY;
|
||||||
|
std::unique_ptr<avifRWData, decltype(&avifRWDataFree)> output(&output_ori,
|
||||||
|
avifRWDataFree);
|
||||||
|
#if CV_AVIF_USE_QUALITY
|
||||||
|
const bool do_lossless = (encoder_->quality == AVIF_QUALITY_LOSSLESS);
|
||||||
|
#else
|
||||||
|
const bool do_lossless =
|
||||||
|
(encoder_->minQuantizer == AVIF_QUANTIZER_BEST_QUALITY &&
|
||||||
|
encoder_->maxQuantizer == AVIF_QUANTIZER_BEST_QUALITY);
|
||||||
|
#endif
|
||||||
|
encoder_->speed = speed;
|
||||||
|
|
||||||
|
const avifAddImageFlags flag = (img_vec.size() == 1)
|
||||||
|
? AVIF_ADD_IMAGE_FLAG_SINGLE
|
||||||
|
: AVIF_ADD_IMAGE_FLAG_NONE;
|
||||||
|
std::vector<AvifImageUniquePtr> images;
|
||||||
|
std::vector<cv::Mat> imgs_scaled;
|
||||||
|
for (const cv::Mat &img : img_vec) {
|
||||||
|
CV_CheckType(
|
||||||
|
img.type(),
|
||||||
|
(bit_depth == 8 && img.depth() == CV_8U) ||
|
||||||
|
((bit_depth == 10 || bit_depth == 12) && img.depth() == CV_16U),
|
||||||
|
"AVIF only supports bit depth of 8 with CV_8U input or "
|
||||||
|
"bit depth of 10 or 12 with CV_16U input");
|
||||||
|
CV_Check(img.channels(),
|
||||||
|
img.channels() == 1 || img.channels() == 3 || img.channels() == 4,
|
||||||
|
"AVIF only supports 1, 3, 4 channels");
|
||||||
|
|
||||||
|
images.emplace_back(ConvertToAvif(img, do_lossless, bit_depth));
|
||||||
|
}
|
||||||
|
for (const AvifImageUniquePtr &image : images) {
|
||||||
|
OPENCV_AVIF_CHECK_STATUS(
|
||||||
|
avifEncoderAddImage(encoder_, image.get(), /*durationInTimescale=*/1,
|
||||||
|
flag),
|
||||||
|
encoder_);
|
||||||
|
}
|
||||||
|
|
||||||
|
OPENCV_AVIF_CHECK_STATUS(avifEncoderFinish(encoder_, output.get()), encoder_);
|
||||||
|
|
||||||
|
if (m_buf) {
|
||||||
|
m_buf->resize(output->size);
|
||||||
|
std::memcpy(m_buf->data(), output->data, output->size);
|
||||||
|
} else {
|
||||||
|
std::ofstream(m_filename, std::ofstream::binary)
|
||||||
|
.write(reinterpret_cast<char *>(output->data), output->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (output->size > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageEncoder AvifEncoder::newEncoder() const { return makePtr<AvifEncoder>(); }
|
||||||
|
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif
|
62
modules/imgcodecs/src/grfmt_avif.hpp
Normal file
62
modules/imgcodecs/src/grfmt_avif.hpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level
|
||||||
|
// directory of this distribution and at http://opencv.org/license.html
|
||||||
|
|
||||||
|
#ifndef _GRFMT_AVIF_H_
|
||||||
|
#define _GRFMT_AVIF_H_
|
||||||
|
|
||||||
|
#include "grfmt_base.hpp"
|
||||||
|
|
||||||
|
#ifdef HAVE_AVIF
|
||||||
|
|
||||||
|
struct avifDecoder;
|
||||||
|
struct avifEncoder;
|
||||||
|
struct avifRWData;
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
|
||||||
|
class AvifDecoder CV_FINAL : public BaseImageDecoder {
|
||||||
|
public:
|
||||||
|
AvifDecoder();
|
||||||
|
~AvifDecoder();
|
||||||
|
|
||||||
|
bool readHeader() CV_OVERRIDE;
|
||||||
|
bool readData(Mat& img) CV_OVERRIDE;
|
||||||
|
bool nextPage() CV_OVERRIDE;
|
||||||
|
|
||||||
|
size_t signatureLength() const CV_OVERRIDE;
|
||||||
|
bool checkSignature(const String& signature) const CV_OVERRIDE;
|
||||||
|
ImageDecoder newDecoder() const CV_OVERRIDE;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int channels_;
|
||||||
|
int bit_depth_;
|
||||||
|
avifDecoder* decoder_;
|
||||||
|
bool is_first_image_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AvifEncoder CV_FINAL : public BaseImageEncoder {
|
||||||
|
public:
|
||||||
|
AvifEncoder();
|
||||||
|
~AvifEncoder() CV_OVERRIDE;
|
||||||
|
|
||||||
|
bool isFormatSupported(int depth) const CV_OVERRIDE;
|
||||||
|
|
||||||
|
bool write(const Mat& img, const std::vector<int>& params) CV_OVERRIDE;
|
||||||
|
|
||||||
|
bool writemulti(const std::vector<Mat>& img_vec,
|
||||||
|
const std::vector<int>& params) CV_OVERRIDE;
|
||||||
|
|
||||||
|
ImageEncoder newEncoder() const CV_OVERRIDE;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool writeToOutput(const std::vector<Mat>& img_vec,
|
||||||
|
const std::vector<int>& params);
|
||||||
|
avifEncoder* encoder_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*_GRFMT_AVIF_H_*/
|
@ -43,6 +43,7 @@
|
|||||||
#define _GRFMTS_H_
|
#define _GRFMTS_H_
|
||||||
|
|
||||||
#include "grfmt_base.hpp"
|
#include "grfmt_base.hpp"
|
||||||
|
#include "grfmt_avif.hpp"
|
||||||
#include "grfmt_bmp.hpp"
|
#include "grfmt_bmp.hpp"
|
||||||
#include "grfmt_sunras.hpp"
|
#include "grfmt_sunras.hpp"
|
||||||
#include "grfmt_jpeg.hpp"
|
#include "grfmt_jpeg.hpp"
|
||||||
|
@ -132,6 +132,10 @@ struct ImageCodecInitializer
|
|||||||
*/
|
*/
|
||||||
ImageCodecInitializer()
|
ImageCodecInitializer()
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_AVIF
|
||||||
|
decoders.push_back(makePtr<AvifDecoder>());
|
||||||
|
encoders.push_back(makePtr<AvifEncoder>());
|
||||||
|
#endif
|
||||||
/// BMP Support
|
/// BMP Support
|
||||||
decoders.push_back( makePtr<BmpDecoder>() );
|
decoders.push_back( makePtr<BmpDecoder>() );
|
||||||
encoders.push_back( makePtr<BmpEncoder>() );
|
encoders.push_back( makePtr<BmpEncoder>() );
|
||||||
|
355
modules/imgcodecs/test/test_avif.cpp
Normal file
355
modules/imgcodecs/test/test_avif.cpp
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level
|
||||||
|
// directory of this distribution and at http://opencv.org/license.html
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "test_precomp.hpp"
|
||||||
|
|
||||||
|
#ifdef HAVE_AVIF
|
||||||
|
|
||||||
|
namespace opencv_test {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class Imgcodecs_Avif_RoundTripSuite
|
||||||
|
: public testing::TestWithParam<std::tuple<int, int, int, ImreadModes>> {
|
||||||
|
protected:
|
||||||
|
static cv::Mat modifyImage(const cv::Mat& img_original, int channels,
|
||||||
|
int bit_depth) {
|
||||||
|
cv::Mat img;
|
||||||
|
if (channels == 1) {
|
||||||
|
cv::cvtColor(img_original, img, cv::COLOR_BGR2GRAY);
|
||||||
|
} else if (channels == 4) {
|
||||||
|
std::vector<cv::Mat> imgs;
|
||||||
|
cv::split(img_original, imgs);
|
||||||
|
imgs.push_back(cv::Mat(imgs[0]));
|
||||||
|
imgs[imgs.size() - 1] = cv::Scalar::all(128);
|
||||||
|
cv::merge(imgs, img);
|
||||||
|
} else {
|
||||||
|
img = img_original.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat img_final = img;
|
||||||
|
// Convert image to CV_16U for some bit depths.
|
||||||
|
if (bit_depth > 8) img.convertTo(img_final, CV_16U, 1 << (bit_depth - 8));
|
||||||
|
|
||||||
|
return img_final;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUp() {
|
||||||
|
bit_depth_ = std::get<0>(GetParam());
|
||||||
|
channels_ = std::get<1>(GetParam());
|
||||||
|
quality_ = std::get<2>(GetParam());
|
||||||
|
imread_mode_ = std::get<3>(GetParam());
|
||||||
|
encoding_params_ = {cv::IMWRITE_AVIF_QUALITY, quality_,
|
||||||
|
cv::IMWRITE_AVIF_DEPTH, bit_depth_};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsBitDepthValid() const {
|
||||||
|
return (bit_depth_ == 8 || bit_depth_ == 10 || bit_depth_ == 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes sure images are close enough after encode/decode roundtrip.
|
||||||
|
void ValidateRead(const cv::Mat& img_original, const cv::Mat& img) const {
|
||||||
|
EXPECT_EQ(img_original.size(), img.size());
|
||||||
|
if (imread_mode_ == IMREAD_UNCHANGED) {
|
||||||
|
ASSERT_EQ(img_original.type(), img.type());
|
||||||
|
// Lossless.
|
||||||
|
if (quality_ == 100) {
|
||||||
|
EXPECT_EQ(0, cvtest::norm(img, img_original, NORM_INF));
|
||||||
|
} else {
|
||||||
|
const float norm = cvtest::norm(img, img_original, NORM_L2) /
|
||||||
|
img.channels() / img.cols / img.rows /
|
||||||
|
(1 << (bit_depth_ - 8));
|
||||||
|
if (quality_ == 50) {
|
||||||
|
EXPECT_LE(norm, 10);
|
||||||
|
} else if (quality_ == 0) {
|
||||||
|
EXPECT_LE(norm, 13);
|
||||||
|
} else {
|
||||||
|
EXPECT_FALSE(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
int bit_depth_;
|
||||||
|
int channels_;
|
||||||
|
int quality_;
|
||||||
|
int imread_mode_;
|
||||||
|
std::vector<int> encoding_params_;
|
||||||
|
};
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class Imgcodecs_Avif_Image_RoundTripSuite
|
||||||
|
: public Imgcodecs_Avif_RoundTripSuite {
|
||||||
|
public:
|
||||||
|
const cv::Mat& get_img_original() {
|
||||||
|
const Key key = {channels_, (bit_depth_ < 8) ? 8 : bit_depth_};
|
||||||
|
return imgs_[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the original image modified for different number of channels and
|
||||||
|
// bit depth.
|
||||||
|
static void SetUpTestCase() {
|
||||||
|
const string root = cvtest::TS::ptr()->get_data_path();
|
||||||
|
const string filename = root + "../cv/shared/lena.png";
|
||||||
|
const cv::Mat img_original = cv::imread(filename);
|
||||||
|
cv::Mat img_resized;
|
||||||
|
cv::resize(img_original, img_resized, cv::Size(kWidth, kHeight), 0, 0);
|
||||||
|
for (int channels : {1, 3, 4}) {
|
||||||
|
for (int bit_depth : {8, 10, 12}) {
|
||||||
|
const Key key{channels, bit_depth};
|
||||||
|
imgs_[key] = modifyImage(img_resized, channels, bit_depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int kWidth;
|
||||||
|
static const int kHeight;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::tuple<int, int> Key;
|
||||||
|
static std::map<Key, cv::Mat> imgs_;
|
||||||
|
};
|
||||||
|
std::map<std::tuple<int, int>, cv::Mat>
|
||||||
|
Imgcodecs_Avif_Image_RoundTripSuite::imgs_;
|
||||||
|
const int Imgcodecs_Avif_Image_RoundTripSuite::kWidth = 51;
|
||||||
|
const int Imgcodecs_Avif_Image_RoundTripSuite::kHeight = 31;
|
||||||
|
|
||||||
|
class Imgcodecs_Avif_Image_WriteReadSuite
|
||||||
|
: public Imgcodecs_Avif_Image_RoundTripSuite {};
|
||||||
|
|
||||||
|
TEST_P(Imgcodecs_Avif_Image_WriteReadSuite, imwrite_imread) {
|
||||||
|
const cv::Mat& img_original = get_img_original();
|
||||||
|
ASSERT_FALSE(img_original.empty());
|
||||||
|
|
||||||
|
// Encode.
|
||||||
|
const string output = cv::tempfile(".avif");
|
||||||
|
if (!IsBitDepthValid()) {
|
||||||
|
EXPECT_NO_FATAL_FAILURE(
|
||||||
|
cv::imwrite(output, img_original, encoding_params_));
|
||||||
|
EXPECT_NE(0, remove(output.c_str()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EXPECT_NO_THROW(cv::imwrite(output, img_original, encoding_params_));
|
||||||
|
|
||||||
|
// Read from file.
|
||||||
|
const cv::Mat img = cv::imread(output, imread_mode_);
|
||||||
|
|
||||||
|
ValidateRead(img_original, img);
|
||||||
|
|
||||||
|
EXPECT_EQ(0, remove(output.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(
|
||||||
|
Imgcodecs_AVIF, Imgcodecs_Avif_Image_WriteReadSuite,
|
||||||
|
::testing::Combine(::testing::ValuesIn({6, 8, 10, 12}),
|
||||||
|
::testing::ValuesIn({1, 3, 4}),
|
||||||
|
::testing::ValuesIn({0, 50, 100}),
|
||||||
|
::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
|
||||||
|
IMREAD_COLOR})));
|
||||||
|
|
||||||
|
class Imgcodecs_Avif_Image_EncodeDecodeSuite
|
||||||
|
: public Imgcodecs_Avif_Image_RoundTripSuite {};
|
||||||
|
|
||||||
|
TEST_P(Imgcodecs_Avif_Image_EncodeDecodeSuite, imencode_imdecode) {
|
||||||
|
const cv::Mat& img_original = get_img_original();
|
||||||
|
ASSERT_FALSE(img_original.empty());
|
||||||
|
|
||||||
|
// Encode.
|
||||||
|
std::vector<unsigned char> buf;
|
||||||
|
if (!IsBitDepthValid()) {
|
||||||
|
EXPECT_THROW(cv::imencode(".avif", img_original, buf, encoding_params_),
|
||||||
|
cv::Exception);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool result;
|
||||||
|
EXPECT_NO_THROW(
|
||||||
|
result = cv::imencode(".avif", img_original, buf, encoding_params_););
|
||||||
|
EXPECT_TRUE(result);
|
||||||
|
|
||||||
|
// Read back.
|
||||||
|
const cv::Mat img = cv::imdecode(buf, imread_mode_);
|
||||||
|
|
||||||
|
ValidateRead(img_original, img);
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(
|
||||||
|
Imgcodecs_AVIF, Imgcodecs_Avif_Image_EncodeDecodeSuite,
|
||||||
|
::testing::Combine(::testing::ValuesIn({6, 8, 10, 12}),
|
||||||
|
::testing::ValuesIn({1, 3, 4}),
|
||||||
|
::testing::ValuesIn({0, 50, 100}),
|
||||||
|
::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
|
||||||
|
IMREAD_COLOR})));
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
typedef testing::TestWithParam<string> Imgcodecs_AVIF_Exif;
|
||||||
|
|
||||||
|
TEST_P(Imgcodecs_AVIF_Exif, exif_orientation) {
|
||||||
|
const string root = cvtest::TS::ptr()->get_data_path();
|
||||||
|
const string filename = root + GetParam();
|
||||||
|
const int colorThresholdHigh = 250;
|
||||||
|
const int colorThresholdLow = 5;
|
||||||
|
|
||||||
|
Mat m_img = imread(filename);
|
||||||
|
ASSERT_FALSE(m_img.empty());
|
||||||
|
Vec3b vec;
|
||||||
|
|
||||||
|
// Checking the first quadrant (with supposed red)
|
||||||
|
vec = m_img.at<Vec3b>(2, 2); // some point inside the square
|
||||||
|
EXPECT_LE(vec.val[0], colorThresholdLow);
|
||||||
|
EXPECT_LE(vec.val[1], colorThresholdLow);
|
||||||
|
EXPECT_GE(vec.val[2], colorThresholdHigh);
|
||||||
|
|
||||||
|
// Checking the second quadrant (with supposed green)
|
||||||
|
vec = m_img.at<Vec3b>(2, 7); // some point inside the square
|
||||||
|
EXPECT_LE(vec.val[0], colorThresholdLow);
|
||||||
|
EXPECT_GE(vec.val[1], colorThresholdHigh);
|
||||||
|
EXPECT_LE(vec.val[2], colorThresholdLow);
|
||||||
|
|
||||||
|
// Checking the third quadrant (with supposed blue)
|
||||||
|
vec = m_img.at<Vec3b>(7, 2); // some point inside the square
|
||||||
|
EXPECT_GE(vec.val[0], colorThresholdHigh);
|
||||||
|
EXPECT_LE(vec.val[1], colorThresholdLow);
|
||||||
|
EXPECT_LE(vec.val[2], colorThresholdLow);
|
||||||
|
}
|
||||||
|
|
||||||
|
const string exif_files[] = {"readwrite/testExifOrientation_1.avif",
|
||||||
|
"readwrite/testExifOrientation_2.avif",
|
||||||
|
"readwrite/testExifOrientation_3.avif",
|
||||||
|
"readwrite/testExifOrientation_4.avif",
|
||||||
|
"readwrite/testExifOrientation_5.avif",
|
||||||
|
"readwrite/testExifOrientation_6.avif",
|
||||||
|
"readwrite/testExifOrientation_7.avif",
|
||||||
|
"readwrite/testExifOrientation_8.avif"};
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(ExifFiles, Imgcodecs_AVIF_Exif,
|
||||||
|
testing::ValuesIn(exif_files));
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class Imgcodecs_Avif_Animation_RoundTripSuite
|
||||||
|
: public Imgcodecs_Avif_RoundTripSuite {
|
||||||
|
public:
|
||||||
|
const std::vector<cv::Mat>& get_anim_original() {
|
||||||
|
const Key key = {channels_, bit_depth_};
|
||||||
|
return anims_[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the original image modified for different number of channels and
|
||||||
|
// bit depth.
|
||||||
|
static void SetUpTestCase() {
|
||||||
|
const string root = cvtest::TS::ptr()->get_data_path();
|
||||||
|
const string filename = root + "../cv/shared/lena.png";
|
||||||
|
const cv::Mat img_original = cv::imread(filename);
|
||||||
|
cv::Mat img_resized;
|
||||||
|
cv::resize(img_original, img_resized, cv::Size(kWidth, kHeight), 0, 0);
|
||||||
|
for (int channels : {1, 3, 4}) {
|
||||||
|
for (int bit_depth : {8, 10, 12}) {
|
||||||
|
const Key key{channels, bit_depth};
|
||||||
|
const cv::Mat img = modifyImage(img_resized, channels, bit_depth);
|
||||||
|
cv::Mat img2, img3;
|
||||||
|
cv::flip(img, img2, 0);
|
||||||
|
cv::flip(img, img3, -1);
|
||||||
|
anims_[key] = {img, img2, img3};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidateRead(const std::vector<cv::Mat>& anim_original,
|
||||||
|
const std::vector<cv::Mat>& anim) const {
|
||||||
|
ASSERT_EQ(anim_original.size(), anim.size());
|
||||||
|
for (size_t i = 0; i < anim.size(); ++i) {
|
||||||
|
Imgcodecs_Avif_RoundTripSuite::ValidateRead(anim_original[i], anim[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int kWidth;
|
||||||
|
static const int kHeight;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::tuple<int, int> Key;
|
||||||
|
static std::map<Key, std::vector<cv::Mat>> anims_;
|
||||||
|
};
|
||||||
|
std::map<std::tuple<int, int>, std::vector<cv::Mat>>
|
||||||
|
Imgcodecs_Avif_Animation_RoundTripSuite::anims_;
|
||||||
|
const int Imgcodecs_Avif_Animation_RoundTripSuite::kWidth = 5;
|
||||||
|
const int Imgcodecs_Avif_Animation_RoundTripSuite::kHeight = 5;
|
||||||
|
|
||||||
|
class Imgcodecs_Avif_Animation_WriteReadSuite
|
||||||
|
: public Imgcodecs_Avif_Animation_RoundTripSuite {};
|
||||||
|
|
||||||
|
TEST_P(Imgcodecs_Avif_Animation_WriteReadSuite, encode_decode) {
|
||||||
|
const std::vector<cv::Mat>& anim_original = get_anim_original();
|
||||||
|
ASSERT_FALSE(anim_original.empty());
|
||||||
|
|
||||||
|
// Encode.
|
||||||
|
const string output = cv::tempfile(".avif");
|
||||||
|
if (!IsBitDepthValid()) {
|
||||||
|
EXPECT_THROW(cv::imwritemulti(output, anim_original, encoding_params_),
|
||||||
|
cv::Exception);
|
||||||
|
EXPECT_NE(0, remove(output.c_str()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EXPECT_NO_THROW(cv::imwritemulti(output, anim_original, encoding_params_));
|
||||||
|
|
||||||
|
// Read from file.
|
||||||
|
std::vector<cv::Mat> anim;
|
||||||
|
ASSERT_TRUE(cv::imreadmulti(output, anim, imread_mode_));
|
||||||
|
|
||||||
|
ValidateRead(anim_original, anim);
|
||||||
|
|
||||||
|
EXPECT_EQ(0, remove(output.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(
|
||||||
|
Imgcodecs_AVIF, Imgcodecs_Avif_Animation_WriteReadSuite,
|
||||||
|
::testing::Combine(::testing::ValuesIn({8, 10, 12}),
|
||||||
|
::testing::ValuesIn({1, 3}), ::testing::ValuesIn({50}),
|
||||||
|
::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
|
||||||
|
IMREAD_COLOR})));
|
||||||
|
class Imgcodecs_Avif_Animation_WriteDecodeSuite
|
||||||
|
: public Imgcodecs_Avif_Animation_RoundTripSuite {};
|
||||||
|
|
||||||
|
TEST_P(Imgcodecs_Avif_Animation_WriteDecodeSuite, encode_decode) {
|
||||||
|
const std::vector<cv::Mat>& anim_original = get_anim_original();
|
||||||
|
ASSERT_FALSE(anim_original.empty());
|
||||||
|
|
||||||
|
// Encode.
|
||||||
|
const string output = cv::tempfile(".avif");
|
||||||
|
if (!IsBitDepthValid()) {
|
||||||
|
EXPECT_THROW(cv::imwritemulti(output, anim_original, encoding_params_),
|
||||||
|
cv::Exception);
|
||||||
|
EXPECT_NE(0, remove(output.c_str()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EXPECT_NO_THROW(cv::imwritemulti(output, anim_original, encoding_params_));
|
||||||
|
|
||||||
|
// Put file into buffer and read from buffer.
|
||||||
|
std::ifstream file(output, std::ios::binary | std::ios::ate);
|
||||||
|
std::streamsize size = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
std::vector<unsigned char> buf(size);
|
||||||
|
EXPECT_TRUE(file.read(reinterpret_cast<char*>(buf.data()), size));
|
||||||
|
EXPECT_EQ(0, remove(output.c_str()));
|
||||||
|
std::vector<cv::Mat> anim;
|
||||||
|
ASSERT_TRUE(cv::imdecodemulti(buf, imread_mode_, anim));
|
||||||
|
|
||||||
|
ValidateRead(anim_original, anim);
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(
|
||||||
|
Imgcodecs_AVIF, Imgcodecs_Avif_Animation_WriteDecodeSuite,
|
||||||
|
::testing::Combine(::testing::ValuesIn({8, 10, 12}),
|
||||||
|
::testing::ValuesIn({1, 3}), ::testing::ValuesIn({50}),
|
||||||
|
::testing::ValuesIn({IMREAD_UNCHANGED, IMREAD_GRAYSCALE,
|
||||||
|
IMREAD_COLOR})));
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace opencv_test
|
||||||
|
|
||||||
|
#endif // HAVE_AVIF
|
@ -813,11 +813,10 @@ struct RGB2HLS_b
|
|||||||
//TODO: fix that when v_interleave is available
|
//TODO: fix that when v_interleave is available
|
||||||
float CV_DECL_ALIGNED(CV_SIMD_WIDTH) interTmpM[VTraits<v_float32>::max_nlanes*3];
|
float CV_DECL_ALIGNED(CV_SIMD_WIDTH) interTmpM[VTraits<v_float32>::max_nlanes*3];
|
||||||
v_store_interleave(interTmpM, vx_setall_f32(1.f), vx_setall_f32(255.f), vx_setall_f32(255.f));
|
v_store_interleave(interTmpM, vx_setall_f32(1.f), vx_setall_f32(255.f), vx_setall_f32(255.f));
|
||||||
v_float32 mhls0, mhls1, mhls2, mhls3;
|
v_float32 mhls0, mhls1, mhls2;
|
||||||
mhls0 = vx_load_aligned(interTmpM);
|
mhls0 = vx_load_aligned(interTmpM);
|
||||||
mhls1 = vx_load_aligned(interTmpM + fsize);
|
mhls1 = vx_load_aligned(interTmpM + fsize);
|
||||||
mhls2 = vx_load_aligned(interTmpM + 2*fsize);
|
mhls2 = vx_load_aligned(interTmpM + 2*fsize);
|
||||||
mhls3 = vx_load_aligned(interTmpM + 3*fsize);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
for(int i = 0; i < n; i += BLOCK_SIZE, dst += BLOCK_SIZE*3 )
|
for(int i = 0; i < n; i += BLOCK_SIZE, dst += BLOCK_SIZE*3 )
|
||||||
|
@ -40,7 +40,7 @@ static const int neighbors_encode[8] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define ACOS_TABLE_SIZE 64
|
#define ACOS_TABLE_SIZE 64
|
||||||
// acos_table[x + ACOS_TABLE_SIZE] = acos(x / ACOS_TABLE_SIZE) / CV_PI (see local_cost)
|
// acos_table[x + ACOS_TABLE_SIZE] = acos(x / ACOS_TABLE_SIZE) / CV_PI (see add_local_cost)
|
||||||
// x = [ -ACOS_TABLE_SIZE .. ACOS_TABLE_SIZE ]
|
// x = [ -ACOS_TABLE_SIZE .. ACOS_TABLE_SIZE ]
|
||||||
float* getAcosTable()
|
float* getAcosTable()
|
||||||
{
|
{
|
||||||
@ -495,55 +495,76 @@ struct IntelligentScissorsMB::Impl
|
|||||||
|
|
||||||
// details: see section 3.1 of the article
|
// details: see section 3.1 of the article
|
||||||
const float* acos_table = getAcosTable();
|
const float* acos_table = getAcosTable();
|
||||||
float local_cost(const Point& p, const Point& q) const
|
const float sqrt2_inv = 0.7071067811865475f; // 1.0 / sqrt(2)
|
||||||
|
|
||||||
|
/** @brief Adds local_cost(p, q) to cost_p.
|
||||||
|
*
|
||||||
|
* local_cost(p, q) is computed as
|
||||||
|
weight_non_edge_compute * non_edge_feature.at<uchar>(q) +
|
||||||
|
weight_gradient_direction * fD +
|
||||||
|
weight_gradient_magnitude * fG
|
||||||
|
*
|
||||||
|
* @param p point p (input)
|
||||||
|
* @param q point q (input)
|
||||||
|
* @param cost_p cost for p (input/output)
|
||||||
|
* @param cost_q cost for q (input)
|
||||||
|
*
|
||||||
|
* @return The boolean result of the (cost_p < cost_q) comparison.
|
||||||
|
*
|
||||||
|
* @note The computed output cost_p can be partial if (cost_p < cost_q) is false.
|
||||||
|
*/
|
||||||
|
bool add_local_cost(const Point& p, const Point& q, float& cost_p, const float cost_q) const
|
||||||
{
|
{
|
||||||
const bool isDiag = (p.x != q.x) && (p.y != q.y);
|
if ((cost_p += weight_non_edge_compute * non_edge_feature.at<uchar>(q)) < cost_q)
|
||||||
|
|
||||||
float fG = gradient_magnitude.at<float>(q);
|
|
||||||
|
|
||||||
const Point2f diff((float)(q.x - p.x), (float)(q.y - p.y));
|
|
||||||
|
|
||||||
const Point2f Ip = gradient_direction(p);
|
|
||||||
const Point2f Iq = gradient_direction(q);
|
|
||||||
|
|
||||||
const Point2f Dp(Ip.y, -Ip.x); // D(p) - 90 degrees clockwise
|
|
||||||
const Point2f Dq(Iq.y, -Iq.x); // D(q) - 90 degrees clockwise
|
|
||||||
|
|
||||||
float dp = Dp.dot(diff); // dp(p, q)
|
|
||||||
float dq = Dq.dot(diff); // dq(p, q)
|
|
||||||
if (dp < 0)
|
|
||||||
{
|
{
|
||||||
dp = -dp; // ensure dp >= 0
|
const bool isDiag = (p.x != q.x) && (p.y != q.y);
|
||||||
dq = -dq;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float sqrt2_inv = 0.7071067811865475f; // 1.0 / sqrt(2)
|
float fG = gradient_magnitude.at<float>(q);
|
||||||
if (isDiag)
|
if (!isDiag)
|
||||||
{
|
{
|
||||||
dp *= sqrt2_inv; // normalize length of (q - p)
|
fG *= sqrt2_inv;
|
||||||
dq *= sqrt2_inv; // normalize length of (q - p)
|
}
|
||||||
}
|
|
||||||
else
|
if ((cost_p += weight_gradient_magnitude * fG) < cost_q)
|
||||||
{
|
{
|
||||||
fG *= sqrt2_inv;
|
|
||||||
}
|
const Point2f diff((float)(q.x - p.x), (float)(q.y - p.y));
|
||||||
|
|
||||||
|
const Point2f Ip = gradient_direction(p);
|
||||||
|
const Point2f Iq = gradient_direction(q);
|
||||||
|
|
||||||
|
const Point2f Dp(Ip.y, -Ip.x); // D(p) - 90 degrees clockwise
|
||||||
|
const Point2f Dq(Iq.y, -Iq.x); // D(q) - 90 degrees clockwise
|
||||||
|
|
||||||
|
float dp = Dp.dot(diff); // dp(p, q)
|
||||||
|
float dq = Dq.dot(diff); // dq(p, q)
|
||||||
|
if (dp < 0)
|
||||||
|
{
|
||||||
|
dp = -dp; // ensure dp >= 0
|
||||||
|
dq = -dq;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDiag)
|
||||||
|
{
|
||||||
|
dp *= sqrt2_inv; // normalize length of (q - p)
|
||||||
|
dq *= sqrt2_inv; // normalize length of (q - p)
|
||||||
|
}
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
int dp_i = cvFloor(dp * ACOS_TABLE_SIZE); // dp is in range 0..1
|
int dp_i = cvFloor(dp * ACOS_TABLE_SIZE); // dp is in range 0..1
|
||||||
dp_i = std::min(ACOS_TABLE_SIZE, std::max(0, dp_i));
|
dp_i = std::min(ACOS_TABLE_SIZE, std::max(0, dp_i));
|
||||||
int dq_i = cvFloor(dq * ACOS_TABLE_SIZE); // dq is in range -1..1
|
int dq_i = cvFloor(dq * ACOS_TABLE_SIZE); // dq is in range -1..1
|
||||||
dq_i = std::min(ACOS_TABLE_SIZE, std::max(-ACOS_TABLE_SIZE, dq_i));
|
dq_i = std::min(ACOS_TABLE_SIZE, std::max(-ACOS_TABLE_SIZE, dq_i));
|
||||||
const float fD = acos_table[dp_i + ACOS_TABLE_SIZE] + acos_table[dq_i + ACOS_TABLE_SIZE];
|
const float fD = acos_table[dp_i + ACOS_TABLE_SIZE] + acos_table[dq_i + ACOS_TABLE_SIZE];
|
||||||
#else
|
#else
|
||||||
const float CV_PI_inv = static_cast<float>(1.0 / CV_PI);
|
const float CV_PI_inv = static_cast<float>(1.0 / CV_PI);
|
||||||
const float fD = (acosf(dp) + acosf(dq)) * CV_PI_inv; // TODO optimize acos calls (through tables)
|
const float fD = (acosf(dp) + acosf(dq)) * CV_PI_inv; // TODO optimize acos calls (through tables)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float cost =
|
cost_p += weight_gradient_direction * fD;
|
||||||
weight_non_edge_compute * non_edge_feature.at<uchar>(q) +
|
}
|
||||||
weight_gradient_direction * fD +
|
}
|
||||||
weight_gradient_magnitude * fG;
|
return cost_p < cost_q;
|
||||||
return cost;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Pix
|
struct Pix
|
||||||
@ -625,8 +646,8 @@ struct IntelligentScissorsMB::Impl
|
|||||||
CV_DbgCheckLE(cost_q, cost_r, "INTERNAL ERROR: sorted queue is corrupted");
|
CV_DbgCheckLE(cost_q, cost_r, "INTERNAL ERROR: sorted queue is corrupted");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float cost = cost_q + local_cost(q, r); // TODO(opt): compute partially until cost < cost_r
|
float cost = cost_q;
|
||||||
if (cost < cost_r)
|
if (add_local_cost(q, r, cost, cost_r))
|
||||||
{
|
{
|
||||||
#if 0 // avoid compiler warning
|
#if 0 // avoid compiler warning
|
||||||
if (cost_r != FLT_MAX)
|
if (cost_r != FLT_MAX)
|
||||||
|
@ -147,11 +147,72 @@ void show(const Mat& img, const std::vector<Point> pts)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Size estimateContourSize(const std::vector<Point>& pts)
|
||||||
|
{
|
||||||
|
Size s(0,0);
|
||||||
|
for (size_t i = 0; i < pts.size(); i++)
|
||||||
|
{
|
||||||
|
if (s.width < pts[i].x)
|
||||||
|
s.width = pts[i].x;
|
||||||
|
if (s.height < pts[i].y)
|
||||||
|
s.height = pts[i].y;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int contoursAreaPixelsMismatch(const std::vector<Point>& pts, const std::vector<Point>& gt)
|
||||||
|
{
|
||||||
|
Size ptsSize = estimateContourSize(pts);
|
||||||
|
Size gtSize = estimateContourSize(gt);
|
||||||
|
|
||||||
|
Size imgSize(std::max(ptsSize.width, gtSize.width)+1, std::max(ptsSize.height, gtSize.height)+1);
|
||||||
|
Mat ptsArea = Mat::zeros(imgSize, CV_8UC1);
|
||||||
|
Mat gtArea = Mat::zeros(imgSize, CV_8UC1);
|
||||||
|
|
||||||
|
std::vector<std::vector<Point>> pts_wrapped = {pts};
|
||||||
|
std::vector<std::vector<Point>> gt_wrapped = {gt};
|
||||||
|
drawContours(ptsArea, pts_wrapped, -1, Scalar(255), FILLED);
|
||||||
|
drawContours(gtArea, gt_wrapped, -1, Scalar(255), FILLED);
|
||||||
|
|
||||||
|
Mat uni = ptsArea | gtArea;
|
||||||
|
Mat intersection = ptsArea & gtArea;
|
||||||
|
bitwise_not(intersection, intersection);
|
||||||
|
Mat delta = uni & intersection;
|
||||||
|
|
||||||
|
return countNonZero(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkContour(std::vector<Point>& pts,
|
||||||
|
const bool backward = false,
|
||||||
|
int allowed_mismatch = 0)
|
||||||
|
{
|
||||||
|
const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||||
|
CV_Assert(test_info);
|
||||||
|
const std::string name = std::string(cvtest::TS::ptr()->get_data_path() + "imgproc/" + test_info->test_case_name() + "-" + test_info->name() + (backward ? "-backward" : "") + ".xml");
|
||||||
|
|
||||||
|
std::vector<Point> reference_pts;
|
||||||
|
#ifdef GENERATE_TEST_DATA
|
||||||
|
{
|
||||||
|
cv::FileStorage fs(name, cv::FileStorage::WRITE);
|
||||||
|
fs << "pts" << pts;
|
||||||
|
}
|
||||||
|
reference_pts = pts;
|
||||||
|
#else
|
||||||
|
FileStorage fs(name, FileStorage::READ);
|
||||||
|
read(fs["pts"], reference_pts, std::vector<Point>());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!allowed_mismatch)
|
||||||
|
EXPECT_EQ(pts, reference_pts);
|
||||||
|
else
|
||||||
|
EXPECT_LE(contoursAreaPixelsMismatch(pts, reference_pts), allowed_mismatch);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Imgproc_IntelligentScissorsMB, rect)
|
TEST(Imgproc_IntelligentScissorsMB, rect)
|
||||||
{
|
{
|
||||||
segmentation::IntelligentScissorsMB tool;
|
segmentation::IntelligentScissorsMB tool;
|
||||||
|
Mat image = getTestImage1();
|
||||||
tool.applyImage(getTestImage1());
|
tool.applyImage(image);
|
||||||
|
|
||||||
Point source_point(50, 30);
|
Point source_point(50, 30);
|
||||||
tool.buildMap(source_point);
|
tool.buildMap(source_point);
|
||||||
@ -159,15 +220,18 @@ TEST(Imgproc_IntelligentScissorsMB, rect)
|
|||||||
Point target_point(100, 30);
|
Point target_point(100, 30);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts);
|
||||||
|
show(image, pts);
|
||||||
|
|
||||||
tool.applyImage(getTestImage2());
|
Mat image2 = getTestImage2();
|
||||||
|
tool.applyImage(image2);
|
||||||
|
|
||||||
tool.buildMap(source_point);
|
tool.buildMap(source_point);
|
||||||
|
|
||||||
std::vector<Point> pts2;
|
std::vector<Point> pts2;
|
||||||
tool.getContour(target_point, pts2, true/*backward*/);
|
tool.getContour(target_point, pts2, true/*backward*/);
|
||||||
|
checkContour(pts2, true/*backward*/);
|
||||||
EXPECT_EQ(pts.size(), pts2.size());
|
show(image2, pts2);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Imgproc_IntelligentScissorsMB, lines)
|
TEST(Imgproc_IntelligentScissorsMB, lines)
|
||||||
@ -182,8 +246,7 @@ TEST(Imgproc_IntelligentScissorsMB, lines)
|
|||||||
Point target_point(150, 50);
|
Point target_point(150, 50);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts);
|
||||||
EXPECT_EQ((size_t)121, pts.size());
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,8 +264,7 @@ TEST(Imgproc_IntelligentScissorsMB, circles)
|
|||||||
Point target_point(150, 50);
|
Point target_point(150, 50);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts);
|
||||||
EXPECT_EQ((size_t)101, pts.size());
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,13 +280,10 @@ TEST(Imgproc_IntelligentScissorsMB, circles_gradient)
|
|||||||
Point target_point(150, 50);
|
Point target_point(150, 50);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts);
|
||||||
EXPECT_EQ((size_t)101, pts.size());
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PTS_SIZE_EPS 2
|
|
||||||
|
|
||||||
TEST(Imgproc_IntelligentScissorsMB, grayscale)
|
TEST(Imgproc_IntelligentScissorsMB, grayscale)
|
||||||
{
|
{
|
||||||
segmentation::IntelligentScissorsMB tool;
|
segmentation::IntelligentScissorsMB tool;
|
||||||
@ -238,10 +297,7 @@ TEST(Imgproc_IntelligentScissorsMB, grayscale)
|
|||||||
Point target_point(413, 155);
|
Point target_point(413, 155);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts, false, 2);
|
||||||
size_t gold = 206;
|
|
||||||
EXPECT_GE(pts.size(), gold - PTS_SIZE_EPS);
|
|
||||||
EXPECT_LE(pts.size(), gold + PTS_SIZE_EPS);
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,10 +316,7 @@ TEST(Imgproc_IntelligentScissorsMB, check_features_grayscale_1_0_0_zerro_crossin
|
|||||||
Point target_point(413, 155);
|
Point target_point(413, 155);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts, false, 11);
|
||||||
size_t gold = 207;
|
|
||||||
EXPECT_GE(pts.size(), gold - PTS_SIZE_EPS);
|
|
||||||
EXPECT_LE(pts.size(), gold + PTS_SIZE_EPS);
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,10 +335,7 @@ TEST(Imgproc_IntelligentScissorsMB, check_features_grayscale_1_0_0_canny)
|
|||||||
Point target_point(413, 155);
|
Point target_point(413, 155);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts, false, 6);
|
||||||
size_t gold = 201;
|
|
||||||
EXPECT_GE(pts.size(), gold - PTS_SIZE_EPS);
|
|
||||||
EXPECT_LE(pts.size(), gold + PTS_SIZE_EPS);
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,10 +353,7 @@ TEST(Imgproc_IntelligentScissorsMB, check_features_grayscale_0_1_0)
|
|||||||
Point target_point(413, 155);
|
Point target_point(413, 155);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts, false, 4);
|
||||||
size_t gold = 166;
|
|
||||||
EXPECT_GE(pts.size(), gold - PTS_SIZE_EPS);
|
|
||||||
EXPECT_LE(pts.size(), gold + PTS_SIZE_EPS);
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,10 +371,7 @@ TEST(Imgproc_IntelligentScissorsMB, check_features_grayscale_0_0_1)
|
|||||||
Point target_point(413, 155);
|
Point target_point(413, 155);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts, false, 2);
|
||||||
size_t gold = 197;
|
|
||||||
EXPECT_GE(pts.size(), gold - PTS_SIZE_EPS);
|
|
||||||
EXPECT_LE(pts.size(), gold + PTS_SIZE_EPS);
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,10 +388,7 @@ TEST(Imgproc_IntelligentScissorsMB, color)
|
|||||||
Point target_point(413, 155);
|
Point target_point(413, 155);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts, false, 2);
|
||||||
size_t gold = 205;
|
|
||||||
EXPECT_GE(pts.size(), gold - PTS_SIZE_EPS);
|
|
||||||
EXPECT_LE(pts.size(), gold + PTS_SIZE_EPS);
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,10 +406,7 @@ TEST(Imgproc_IntelligentScissorsMB, color_canny)
|
|||||||
Point target_point(413, 155);
|
Point target_point(413, 155);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts, false, 2);
|
||||||
size_t gold = 200;
|
|
||||||
EXPECT_GE(pts.size(), gold - PTS_SIZE_EPS);
|
|
||||||
EXPECT_LE(pts.size(), gold + PTS_SIZE_EPS);
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,10 +435,7 @@ TEST(Imgproc_IntelligentScissorsMB, color_custom_features_edge)
|
|||||||
Point target_point(413, 155);
|
Point target_point(413, 155);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts, false, 2);
|
||||||
size_t gold = 201;
|
|
||||||
EXPECT_GE(pts.size(), gold - PTS_SIZE_EPS);
|
|
||||||
EXPECT_LE(pts.size(), gold + PTS_SIZE_EPS);
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -427,10 +462,7 @@ TEST(Imgproc_IntelligentScissorsMB, color_custom_features_all)
|
|||||||
Point target_point(413, 155);
|
Point target_point(413, 155);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts, false, 9);
|
||||||
size_t gold = 201;
|
|
||||||
EXPECT_GE(pts.size(), gold - PTS_SIZE_EPS);
|
|
||||||
EXPECT_LE(pts.size(), gold + PTS_SIZE_EPS);
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,10 +488,7 @@ TEST(Imgproc_IntelligentScissorsMB, color_custom_features_edge_magnitude)
|
|||||||
Point target_point(413, 155);
|
Point target_point(413, 155);
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
tool.getContour(target_point, pts);
|
tool.getContour(target_point, pts);
|
||||||
|
checkContour(pts, false, 9);
|
||||||
size_t gold = 201;
|
|
||||||
EXPECT_GE(pts.size(), gold - PTS_SIZE_EPS);
|
|
||||||
EXPECT_LE(pts.size(), gold + PTS_SIZE_EPS);
|
|
||||||
show(image, pts);
|
show(image, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,19 +319,31 @@ class JSWrapperGenerator(object):
|
|||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
self.classes[class_info.name] = class_info
|
self.classes[class_info.name] = class_info
|
||||||
|
|
||||||
if class_info.bases:
|
def resolve_class_inheritance(self):
|
||||||
chunks = class_info.bases[0].split('::')
|
new_classes = {}
|
||||||
base = '_'.join(chunks)
|
for name, class_info in self.classes.items():
|
||||||
while base not in self.classes and len(chunks) > 1:
|
|
||||||
del chunks[-2]
|
if not hasattr(class_info, 'bases'):
|
||||||
|
new_classes[name] = class_info
|
||||||
|
continue # not class
|
||||||
|
|
||||||
|
if class_info.bases:
|
||||||
|
chunks = class_info.bases[0].split('::')
|
||||||
base = '_'.join(chunks)
|
base = '_'.join(chunks)
|
||||||
if base not in self.classes:
|
while base not in self.classes and len(chunks) > 1:
|
||||||
print("Generator error: unable to resolve base %s for %s"
|
del chunks[-2]
|
||||||
% (class_info.bases[0], class_info.name))
|
base = '_'.join(chunks)
|
||||||
sys.exit(-1)
|
if base not in self.classes:
|
||||||
else:
|
print("Generator error: unable to resolve base %s for %s"
|
||||||
class_info.bases[0] = "::".join(chunks)
|
% (class_info.bases[0], class_info.name))
|
||||||
class_info.isalgorithm |= self.classes[base].isalgorithm
|
sys.exit(-1)
|
||||||
|
else:
|
||||||
|
class_info.bases[0] = "::".join(chunks)
|
||||||
|
class_info.isalgorithm |= self.classes[base].isalgorithm
|
||||||
|
|
||||||
|
new_classes[name] = class_info
|
||||||
|
|
||||||
|
self.classes = new_classes
|
||||||
|
|
||||||
def split_decl_name(self, name):
|
def split_decl_name(self, name):
|
||||||
chunks = name.split('.')
|
chunks = name.split('.')
|
||||||
@ -759,6 +771,8 @@ class JSWrapperGenerator(object):
|
|||||||
else: # class/global function
|
else: # class/global function
|
||||||
self.add_func(decl)
|
self.add_func(decl)
|
||||||
|
|
||||||
|
self.resolve_class_inheritance()
|
||||||
|
|
||||||
# step 2: generate bindings
|
# step 2: generate bindings
|
||||||
# Global functions
|
# Global functions
|
||||||
for ns_name, ns in sorted(self.namespaces.items()):
|
for ns_name, ns in sorted(self.namespaces.items()):
|
||||||
@ -812,6 +826,7 @@ class JSWrapperGenerator(object):
|
|||||||
for name, class_info in sorted(self.classes.items()):
|
for name, class_info in sorted(self.classes.items()):
|
||||||
class_bindings = []
|
class_bindings = []
|
||||||
if not name in white_list:
|
if not name in white_list:
|
||||||
|
#print('Not in whitelist: "{}" from ns={}'.format(name, ns_name))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Generate bindings for methods
|
# Generate bindings for methods
|
||||||
|
@ -89,18 +89,21 @@ using namespace cv;
|
|||||||
|
|
||||||
using namespace cv::segmentation; // FIXIT
|
using namespace cv::segmentation; // FIXIT
|
||||||
|
|
||||||
|
using namespace cv::aruco;
|
||||||
|
typedef aruco::DetectorParameters aruco_DetectorParameters;
|
||||||
|
typedef QRCodeDetectorAruco::Params QRCodeDetectorAruco_Params;
|
||||||
|
|
||||||
#ifdef HAVE_OPENCV_DNN
|
#ifdef HAVE_OPENCV_DNN
|
||||||
using namespace cv::dnn;
|
using namespace cv::dnn;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_OPENCV_ARUCO
|
|
||||||
using namespace aruco;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_OPENCV_VIDEO
|
#ifdef HAVE_OPENCV_VIDEO
|
||||||
typedef TrackerMIL::Params TrackerMIL_Params;
|
typedef TrackerMIL::Params TrackerMIL_Params;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// HACK: JS generator ommits namespace for parameter types for some reason. Added typedef to handle std::string correctly
|
||||||
|
typedef std::string string;
|
||||||
|
|
||||||
namespace binding_utils
|
namespace binding_utils
|
||||||
{
|
{
|
||||||
template<typename classT, typename enumT>
|
template<typename classT, typename enumT>
|
||||||
|
@ -199,4 +199,102 @@ QUnit.test('QR code detect and decode', function (assert) {
|
|||||||
mat.delete();
|
mat.delete();
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
QUnit.test('Aruco-based QR code detect', function (assert) {
|
||||||
|
{
|
||||||
|
let qrcode_params = new cv.QRCodeDetectorAruco_Params();
|
||||||
|
let detector = new cv.QRCodeDetectorAruco();
|
||||||
|
let mat = cv.Mat.ones(800, 600, cv.CV_8U);
|
||||||
|
assert.ok(mat);
|
||||||
|
|
||||||
|
detector.setDetectorParameters(qrcode_params);
|
||||||
|
|
||||||
|
let points = new cv.Mat();
|
||||||
|
let qrCodeFound = detector.detect(mat, points);
|
||||||
|
assert.equal(points.rows, 0)
|
||||||
|
assert.equal(points.cols, 0)
|
||||||
|
assert.equal(qrCodeFound, false);
|
||||||
|
|
||||||
|
qrcode_params.delete();
|
||||||
|
detector.delete();
|
||||||
|
points.delete();
|
||||||
|
mat.delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
QUnit.test('Bar code detect', function (assert) {
|
||||||
|
{
|
||||||
|
let detector = new cv.barcode_BarcodeDetector();
|
||||||
|
let mat = cv.Mat.ones(800, 600, cv.CV_8U);
|
||||||
|
assert.ok(mat);
|
||||||
|
|
||||||
|
let points = new cv.Mat();
|
||||||
|
let codeFound = detector.detect(mat, points);
|
||||||
|
assert.equal(points.rows, 0)
|
||||||
|
assert.equal(points.cols, 0)
|
||||||
|
assert.equal(codeFound, false);
|
||||||
|
|
||||||
|
codeContent = detector.detectAndDecode(mat);
|
||||||
|
assert.equal(typeof codeContent, 'string');
|
||||||
|
assert.equal(codeContent, '');
|
||||||
|
|
||||||
|
detector.delete();
|
||||||
|
points.delete();
|
||||||
|
mat.delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
QUnit.test('Aruco detector', function (assert) {
|
||||||
|
{
|
||||||
|
let dictionary = cv.getPredefinedDictionary(cv.DICT_4X4_50);
|
||||||
|
let aruco_image = new cv.Mat();
|
||||||
|
let detectorParameters = new cv.aruco_DetectorParameters();
|
||||||
|
let refineParameters = new cv.aruco_RefineParameters(10, 3, true);
|
||||||
|
let detector = new cv.aruco_ArucoDetector(dictionary, detectorParameters,refineParameters);
|
||||||
|
let corners = new cv.MatVector();
|
||||||
|
let ids = new cv.Mat();
|
||||||
|
|
||||||
|
dictionary.generateImageMarker(10, 128, aruco_image);
|
||||||
|
assert.ok(!aruco_image.empty());
|
||||||
|
|
||||||
|
detector.detectMarkers(aruco_image, corners, ids);
|
||||||
|
|
||||||
|
dictionary.delete();
|
||||||
|
aruco_image.delete();
|
||||||
|
detectorParameters.delete();
|
||||||
|
refineParameters.delete();
|
||||||
|
detector.delete();
|
||||||
|
corners.delete();
|
||||||
|
ids.delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
QUnit.test('Charuco detector', function (assert) {
|
||||||
|
{
|
||||||
|
let dictionary = new cv.getPredefinedDictionary(cv.DICT_4X4_50);
|
||||||
|
let boardIds = new cv.Mat();
|
||||||
|
let board = new cv.aruco_CharucoBoard(new cv.Size(3, 5), 64, 32, dictionary, boardIds);
|
||||||
|
let charucoParameters = new cv.aruco_CharucoParameters();
|
||||||
|
let detectorParameters = new cv.aruco_DetectorParameters();
|
||||||
|
let refineParameters = new cv.aruco_RefineParameters(10, 3, true);
|
||||||
|
let detector = new cv.aruco_CharucoDetector(board, charucoParameters, detectorParameters, refineParameters);
|
||||||
|
let board_image = new cv.Mat();
|
||||||
|
let corners = new cv.Mat();
|
||||||
|
let ids = new cv.Mat();
|
||||||
|
|
||||||
|
board.generateImage(new cv.Size(300, 500), board_image);
|
||||||
|
assert.ok(!board_image.empty());
|
||||||
|
|
||||||
|
detector.detectBoard(board_image, corners, ids);
|
||||||
|
assert.ok(!corners.empty());
|
||||||
|
assert.ok(!ids.empty());
|
||||||
|
|
||||||
|
dictionary.delete();
|
||||||
|
boardIds.delete();
|
||||||
|
board.delete();
|
||||||
|
board_image.delete();
|
||||||
|
charucoParameters.delete();
|
||||||
|
detectorParameters.delete();
|
||||||
|
refineParameters.delete();
|
||||||
|
detector.delete();
|
||||||
|
corners.delete();
|
||||||
|
ids.delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@ -18,3 +18,32 @@
|
|||||||
year = {2016},
|
year = {2016},
|
||||||
month = {October}
|
month = {October}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mastersthesis{Xiangmin2015research,
|
||||||
|
title={Research on Barcode Recognition Technology In a Complex Background},
|
||||||
|
author={Xiangmin, Wang},
|
||||||
|
year={2015},
|
||||||
|
school={Huazhong University of Science and Technology}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{bazen2002systematic,
|
||||||
|
title={Systematic methods for the computation of the directional fields and singular points of fingerprints},
|
||||||
|
author={Bazen, Asker M and Gerez, Sabih H},
|
||||||
|
journal={IEEE transactions on pattern analysis and machine intelligence},
|
||||||
|
volume={24},
|
||||||
|
number={7},
|
||||||
|
pages={905--919},
|
||||||
|
year={2002},
|
||||||
|
publisher={IEEE}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{kass1987analyzing,
|
||||||
|
title={Analyzing oriented patterns},
|
||||||
|
author={Kass, Michael and Witkin, Andrew},
|
||||||
|
journal={Computer vision, graphics, and image processing},
|
||||||
|
volume={37},
|
||||||
|
number={3},
|
||||||
|
pages={362--385},
|
||||||
|
year={1987},
|
||||||
|
publisher={Elsevier}
|
||||||
|
}
|
||||||
|
@ -45,6 +45,8 @@
|
|||||||
#define OPENCV_OBJDETECT_HPP
|
#define OPENCV_OBJDETECT_HPP
|
||||||
|
|
||||||
#include "opencv2/core.hpp"
|
#include "opencv2/core.hpp"
|
||||||
|
#include "opencv2/objdetect/aruco_detector.hpp"
|
||||||
|
#include "opencv2/objdetect/graphical_code_detector.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@defgroup objdetect Object Detection
|
@defgroup objdetect Object Detection
|
||||||
@ -101,6 +103,7 @@ using a Boosted Cascade of Simple Features. IEEE CVPR, 2001. The paper is availa
|
|||||||
<https://github.com/SvHey/thesis/blob/master/Literature/ObjectDetection/violaJones_CVPR2001.pdf>
|
<https://github.com/SvHey/thesis/blob/master/Literature/ObjectDetection/violaJones_CVPR2001.pdf>
|
||||||
|
|
||||||
@defgroup objdetect_hog HOG (Histogram of Oriented Gradients) descriptor and object detector
|
@defgroup objdetect_hog HOG (Histogram of Oriented Gradients) descriptor and object detector
|
||||||
|
@defgroup objdetect_barcode Barcode detection and decoding
|
||||||
@defgroup objdetect_qrcode QRCode detection and encoding
|
@defgroup objdetect_qrcode QRCode detection and encoding
|
||||||
@defgroup objdetect_dnn_face DNN-based face detection and recognition
|
@defgroup objdetect_dnn_face DNN-based face detection and recognition
|
||||||
Check @ref tutorial_dnn_face "the corresponding tutorial" for more details.
|
Check @ref tutorial_dnn_face "the corresponding tutorial" for more details.
|
||||||
@ -753,44 +756,27 @@ public:
|
|||||||
CV_WRAP virtual void encodeStructuredAppend(const String& encoded_info, OutputArrayOfArrays qrcodes) = 0;
|
CV_WRAP virtual void encodeStructuredAppend(const String& encoded_info, OutputArrayOfArrays qrcodes) = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
class CV_EXPORTS_W_SIMPLE QRCodeDetector : public GraphicalCodeDetector
|
||||||
class CV_EXPORTS_W QRCodeDetector
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CV_WRAP QRCodeDetector();
|
CV_WRAP QRCodeDetector();
|
||||||
~QRCodeDetector();
|
|
||||||
|
|
||||||
/** @brief sets the epsilon used during the horizontal scan of QR code stop marker detection.
|
/** @brief sets the epsilon used during the horizontal scan of QR code stop marker detection.
|
||||||
@param epsX Epsilon neighborhood, which allows you to determine the horizontal pattern
|
@param epsX Epsilon neighborhood, which allows you to determine the horizontal pattern
|
||||||
of the scheme 1:1:3:1:1 according to QR code standard.
|
of the scheme 1:1:3:1:1 according to QR code standard.
|
||||||
*/
|
*/
|
||||||
CV_WRAP void setEpsX(double epsX);
|
CV_WRAP QRCodeDetector& setEpsX(double epsX);
|
||||||
/** @brief sets the epsilon used during the vertical scan of QR code stop marker detection.
|
/** @brief sets the epsilon used during the vertical scan of QR code stop marker detection.
|
||||||
@param epsY Epsilon neighborhood, which allows you to determine the vertical pattern
|
@param epsY Epsilon neighborhood, which allows you to determine the vertical pattern
|
||||||
of the scheme 1:1:3:1:1 according to QR code standard.
|
of the scheme 1:1:3:1:1 according to QR code standard.
|
||||||
*/
|
*/
|
||||||
CV_WRAP void setEpsY(double epsY);
|
CV_WRAP QRCodeDetector& setEpsY(double epsY);
|
||||||
|
|
||||||
/** @brief use markers to improve the position of the corners of the QR code
|
/** @brief use markers to improve the position of the corners of the QR code
|
||||||
*
|
*
|
||||||
* alignmentMarkers using by default
|
* alignmentMarkers using by default
|
||||||
*/
|
*/
|
||||||
CV_WRAP void setUseAlignmentMarkers(bool useAlignmentMarkers);
|
CV_WRAP QRCodeDetector& setUseAlignmentMarkers(bool useAlignmentMarkers);
|
||||||
|
|
||||||
/** @brief Detects QR code in image and returns the quadrangle containing the code.
|
|
||||||
@param img grayscale or color (BGR) image containing (or not) QR code.
|
|
||||||
@param points Output vector of vertices of the minimum-area quadrangle containing the code.
|
|
||||||
*/
|
|
||||||
CV_WRAP bool detect(InputArray img, OutputArray points) const;
|
|
||||||
|
|
||||||
/** @brief Decodes QR code in image once it's found by the detect() method.
|
|
||||||
|
|
||||||
Returns UTF8-encoded output string or empty string if the code cannot be decoded.
|
|
||||||
@param img grayscale or color (BGR) image containing QR code.
|
|
||||||
@param points Quadrangle vertices found by detect() method (or some other algorithm).
|
|
||||||
@param straight_qrcode The optional output image containing rectified and binarized QR code
|
|
||||||
*/
|
|
||||||
CV_WRAP std::string decode(InputArray img, InputArray points, OutputArray straight_qrcode = noArray());
|
|
||||||
|
|
||||||
/** @brief Decodes QR code on a curved surface in image once it's found by the detect() method.
|
/** @brief Decodes QR code on a curved surface in image once it's found by the detect() method.
|
||||||
|
|
||||||
@ -801,15 +787,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
CV_WRAP cv::String decodeCurved(InputArray img, InputArray points, OutputArray straight_qrcode = noArray());
|
CV_WRAP cv::String decodeCurved(InputArray img, InputArray points, OutputArray straight_qrcode = noArray());
|
||||||
|
|
||||||
/** @brief Both detects and decodes QR code
|
|
||||||
|
|
||||||
@param img grayscale or color (BGR) image containing QR code.
|
|
||||||
@param points optional output array of vertices of the found QR code quadrangle. Will be empty if not found.
|
|
||||||
@param straight_qrcode The optional output image containing rectified and binarized QR code
|
|
||||||
*/
|
|
||||||
CV_WRAP std::string detectAndDecode(InputArray img, OutputArray points=noArray(),
|
|
||||||
OutputArray straight_qrcode = noArray());
|
|
||||||
|
|
||||||
/** @brief Both detects and decodes QR code on a curved surface
|
/** @brief Both detects and decodes QR code on a curved surface
|
||||||
|
|
||||||
@param img grayscale or color (BGR) image containing QR code.
|
@param img grayscale or color (BGR) image containing QR code.
|
||||||
@ -818,43 +795,58 @@ public:
|
|||||||
*/
|
*/
|
||||||
CV_WRAP std::string detectAndDecodeCurved(InputArray img, OutputArray points=noArray(),
|
CV_WRAP std::string detectAndDecodeCurved(InputArray img, OutputArray points=noArray(),
|
||||||
OutputArray straight_qrcode = noArray());
|
OutputArray straight_qrcode = noArray());
|
||||||
|
};
|
||||||
|
|
||||||
/** @brief Detects QR codes in image and returns the vector of the quadrangles containing the codes.
|
class CV_EXPORTS_W_SIMPLE QRCodeDetectorAruco : public GraphicalCodeDetector {
|
||||||
@param img grayscale or color (BGR) image containing (or not) QR codes.
|
public:
|
||||||
@param points Output vector of vector of vertices of the minimum-area quadrangle containing the codes.
|
CV_WRAP QRCodeDetectorAruco();
|
||||||
*/
|
|
||||||
CV_WRAP
|
|
||||||
bool detectMulti(InputArray img, OutputArray points) const;
|
|
||||||
|
|
||||||
/** @brief Decodes QR codes in image once it's found by the detect() method.
|
struct CV_EXPORTS_W_SIMPLE Params {
|
||||||
@param img grayscale or color (BGR) image containing QR codes.
|
CV_WRAP Params();
|
||||||
@param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded.
|
|
||||||
@param points vector of Quadrangle vertices found by detect() method (or some other algorithm).
|
|
||||||
@param straight_qrcode The optional output vector of images containing rectified and binarized QR codes
|
|
||||||
*/
|
|
||||||
CV_WRAP
|
|
||||||
bool decodeMulti(
|
|
||||||
InputArray img, InputArray points,
|
|
||||||
CV_OUT std::vector<std::string>& decoded_info,
|
|
||||||
OutputArrayOfArrays straight_qrcode = noArray()
|
|
||||||
) const;
|
|
||||||
|
|
||||||
/** @brief Both detects and decodes QR codes
|
/** @brief The minimum allowed pixel size of a QR module in the smallest image in the image pyramid, default 4.f */
|
||||||
@param img grayscale or color (BGR) image containing QR codes.
|
CV_PROP_RW float minModuleSizeInPyramid;
|
||||||
@param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded.
|
|
||||||
@param points optional output vector of vertices of the found QR code quadrangles. Will be empty if not found.
|
|
||||||
@param straight_qrcode The optional output vector of images containing rectified and binarized QR codes
|
|
||||||
*/
|
|
||||||
CV_WRAP
|
|
||||||
bool detectAndDecodeMulti(
|
|
||||||
InputArray img, CV_OUT std::vector<std::string>& decoded_info,
|
|
||||||
OutputArray points = noArray(),
|
|
||||||
OutputArrayOfArrays straight_qrcode = noArray()
|
|
||||||
) const;
|
|
||||||
|
|
||||||
protected:
|
/** @brief The maximum allowed relative rotation for finder patterns in the same QR code, default pi/12 */
|
||||||
struct Impl;
|
CV_PROP_RW float maxRotation;
|
||||||
Ptr<Impl> p;
|
|
||||||
|
/** @brief The maximum allowed relative mismatch in module sizes for finder patterns in the same QR code, default 1.75f */
|
||||||
|
CV_PROP_RW float maxModuleSizeMismatch;
|
||||||
|
|
||||||
|
/** @brief The maximum allowed module relative mismatch for timing pattern module, default 2.f
|
||||||
|
*
|
||||||
|
* If relative mismatch of timing pattern module more this value, penalty points will be added.
|
||||||
|
* If a lot of penalty points are added, QR code will be rejected. */
|
||||||
|
CV_PROP_RW float maxTimingPatternMismatch;
|
||||||
|
|
||||||
|
/** @brief The maximum allowed percentage of penalty points out of total pins in timing pattern, default 0.4f */
|
||||||
|
CV_PROP_RW float maxPenalties;
|
||||||
|
|
||||||
|
/** @brief The maximum allowed relative color mismatch in the timing pattern, default 0.2f*/
|
||||||
|
CV_PROP_RW float maxColorsMismatch;
|
||||||
|
|
||||||
|
/** @brief The algorithm find QR codes with almost minimum timing pattern score and minimum size, default 0.9f
|
||||||
|
*
|
||||||
|
* The QR code with the minimum "timing pattern score" and minimum "size" is selected as the best QR code.
|
||||||
|
* If for the current QR code "timing pattern score" * scaleTimingPatternScore < "previous timing pattern score" and "size" < "previous size", then
|
||||||
|
* current QR code set as the best QR code. */
|
||||||
|
CV_PROP_RW float scaleTimingPatternScore;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief QR code detector constructor for Aruco-based algorithm. See cv::QRCodeDetectorAruco::Params */
|
||||||
|
CV_WRAP explicit QRCodeDetectorAruco(const QRCodeDetectorAruco::Params& params);
|
||||||
|
|
||||||
|
/** @brief Detector parameters getter. See cv::QRCodeDetectorAruco::Params */
|
||||||
|
CV_WRAP const QRCodeDetectorAruco::Params& getDetectorParameters() const;
|
||||||
|
|
||||||
|
/** @brief Detector parameters setter. See cv::QRCodeDetectorAruco::Params */
|
||||||
|
CV_WRAP QRCodeDetectorAruco& setDetectorParameters(const QRCodeDetectorAruco::Params& params);
|
||||||
|
|
||||||
|
/** @brief Aruco detector parameters are used to search for the finder patterns. */
|
||||||
|
CV_WRAP aruco::DetectorParameters getArucoParameters();
|
||||||
|
|
||||||
|
/** @brief Aruco detector parameters are used to search for the finder patterns. */
|
||||||
|
CV_WRAP void setArucoParameters(const aruco::DetectorParameters& params);
|
||||||
};
|
};
|
||||||
|
|
||||||
//! @}
|
//! @}
|
||||||
@ -862,7 +854,7 @@ protected:
|
|||||||
|
|
||||||
#include "opencv2/objdetect/detection_based_tracker.hpp"
|
#include "opencv2/objdetect/detection_based_tracker.hpp"
|
||||||
#include "opencv2/objdetect/face.hpp"
|
#include "opencv2/objdetect/face.hpp"
|
||||||
#include "opencv2/objdetect/aruco_detector.hpp"
|
|
||||||
#include "opencv2/objdetect/charuco_detector.hpp"
|
#include "opencv2/objdetect/charuco_detector.hpp"
|
||||||
|
#include "opencv2/objdetect/barcode.hpp"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -90,7 +90,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
CV_WRAP void generateImage(Size outSize, OutputArray img, int marginSize = 0, int borderBits = 1) const;
|
CV_WRAP void generateImage(Size outSize, OutputArray img, int marginSize = 0, int borderBits = 1) const;
|
||||||
|
|
||||||
CV_DEPRECATED_EXTERNAL // avoid using in C++ code, will be moved to “protected” (need to fix bindings first)
|
CV_DEPRECATED_EXTERNAL // avoid using in C++ code, will be moved to "protected" (need to fix bindings first)
|
||||||
Board();
|
Board();
|
||||||
|
|
||||||
struct Impl;
|
struct Impl;
|
||||||
@ -122,7 +122,7 @@ public:
|
|||||||
CV_WRAP float getMarkerLength() const;
|
CV_WRAP float getMarkerLength() const;
|
||||||
CV_WRAP float getMarkerSeparation() const;
|
CV_WRAP float getMarkerSeparation() const;
|
||||||
|
|
||||||
CV_DEPRECATED_EXTERNAL // avoid using in C++ code, will be moved to “protected” (need to fix bindings first)
|
CV_DEPRECATED_EXTERNAL // avoid using in C++ code, will be moved to "protected" (need to fix bindings first)
|
||||||
GridBoard();
|
GridBoard();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
CV_WRAP bool checkCharucoCornersCollinear(InputArray charucoIds) const;
|
CV_WRAP bool checkCharucoCornersCollinear(InputArray charucoIds) const;
|
||||||
|
|
||||||
CV_DEPRECATED_EXTERNAL // avoid using in C++ code, will be moved to “protected” (need to fix bindings first)
|
CV_DEPRECATED_EXTERNAL // avoid using in C++ code, will be moved to "protected" (need to fix bindings first)
|
||||||
CharucoBoard();
|
CharucoBoard();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters {
|
|||||||
minCornerDistanceRate = 0.05;
|
minCornerDistanceRate = 0.05;
|
||||||
minDistanceToBorder = 3;
|
minDistanceToBorder = 3;
|
||||||
minMarkerDistanceRate = 0.05;
|
minMarkerDistanceRate = 0.05;
|
||||||
cornerRefinementMethod = CORNER_REFINE_NONE;
|
cornerRefinementMethod = (int)CORNER_REFINE_NONE;
|
||||||
cornerRefinementWinSize = 5;
|
cornerRefinementWinSize = 5;
|
||||||
cornerRefinementMaxIterations = 30;
|
cornerRefinementMaxIterations = 30;
|
||||||
cornerRefinementMinAccuracy = 0.1;
|
cornerRefinementMinAccuracy = 0.1;
|
||||||
@ -106,7 +106,7 @@ struct CV_EXPORTS_W_SIMPLE DetectorParameters {
|
|||||||
CV_PROP_RW double minMarkerDistanceRate;
|
CV_PROP_RW double minMarkerDistanceRate;
|
||||||
|
|
||||||
/** @brief default value CORNER_REFINE_NONE */
|
/** @brief default value CORNER_REFINE_NONE */
|
||||||
CV_PROP_RW CornerRefineMethod cornerRefinementMethod;
|
CV_PROP_RW int cornerRefinementMethod;
|
||||||
|
|
||||||
/// window size for the corner refinement process (in pixels) (default 5).
|
/// window size for the corner refinement process (in pixels) (default 5).
|
||||||
CV_PROP_RW int cornerRefinementWinSize;
|
CV_PROP_RW int cornerRefinementWinSize;
|
||||||
|
@ -110,7 +110,8 @@ enum PredefinedDictionaryType {
|
|||||||
DICT_APRILTAG_16h5, ///< 4x4 bits, minimum hamming distance between any two codes = 5, 30 codes
|
DICT_APRILTAG_16h5, ///< 4x4 bits, minimum hamming distance between any two codes = 5, 30 codes
|
||||||
DICT_APRILTAG_25h9, ///< 5x5 bits, minimum hamming distance between any two codes = 9, 35 codes
|
DICT_APRILTAG_25h9, ///< 5x5 bits, minimum hamming distance between any two codes = 9, 35 codes
|
||||||
DICT_APRILTAG_36h10, ///< 6x6 bits, minimum hamming distance between any two codes = 10, 2320 codes
|
DICT_APRILTAG_36h10, ///< 6x6 bits, minimum hamming distance between any two codes = 10, 2320 codes
|
||||||
DICT_APRILTAG_36h11 ///< 6x6 bits, minimum hamming distance between any two codes = 11, 587 codes
|
DICT_APRILTAG_36h11, ///< 6x6 bits, minimum hamming distance between any two codes = 11, 587 codes
|
||||||
|
DICT_ARUCO_MIP_36h12 ///< 6x6 bits, minimum hamming distance between any two codes = 12, 250 codes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
65
modules/objdetect/include/opencv2/objdetect/barcode.hpp
Normal file
65
modules/objdetect/include/opencv2/objdetect/barcode.hpp
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds
|
||||||
|
|
||||||
|
#ifndef OPENCV_OBJDETECT_BARCODE_HPP
|
||||||
|
#define OPENCV_OBJDETECT_BARCODE_HPP
|
||||||
|
|
||||||
|
#include <opencv2/core.hpp>
|
||||||
|
#include <opencv2/objdetect/graphical_code_detector.hpp>
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
|
||||||
|
//! @addtogroup objdetect_barcode
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
class CV_EXPORTS_W_SIMPLE BarcodeDetector : public cv::GraphicalCodeDetector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** @brief Initialize the BarcodeDetector.
|
||||||
|
*/
|
||||||
|
CV_WRAP BarcodeDetector();
|
||||||
|
/** @brief Initialize the BarcodeDetector.
|
||||||
|
*
|
||||||
|
* Parameters allow to load _optional_ Super Resolution DNN model for better quality.
|
||||||
|
* @param prototxt_path prototxt file path for the super resolution model
|
||||||
|
* @param model_path model file path for the super resolution model
|
||||||
|
*/
|
||||||
|
CV_WRAP BarcodeDetector(const std::string &prototxt_path, const std::string &model_path);
|
||||||
|
~BarcodeDetector();
|
||||||
|
|
||||||
|
/** @brief Decodes barcode in image once it's found by the detect() method.
|
||||||
|
*
|
||||||
|
* @param img grayscale or color (BGR) image containing bar code.
|
||||||
|
* @param points vector of rotated rectangle vertices found by detect() method (or some other algorithm).
|
||||||
|
* For N detected barcodes, the dimensions of this array should be [N][4].
|
||||||
|
* Order of four points in vector<Point2f> is bottomLeft, topLeft, topRight, bottomRight.
|
||||||
|
* @param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded.
|
||||||
|
* @param decoded_type vector strings, specifies the type of these barcodes
|
||||||
|
* @return true if at least one valid barcode have been found
|
||||||
|
*/
|
||||||
|
CV_WRAP bool decodeWithType(InputArray img,
|
||||||
|
InputArray points,
|
||||||
|
CV_OUT std::vector<std::string> &decoded_info,
|
||||||
|
CV_OUT std::vector<std::string> &decoded_type) const;
|
||||||
|
|
||||||
|
/** @brief Both detects and decodes barcode
|
||||||
|
|
||||||
|
* @param img grayscale or color (BGR) image containing barcode.
|
||||||
|
* @param decoded_info UTF8-encoded output vector of string(s) or empty vector of string if the codes cannot be decoded.
|
||||||
|
* @param decoded_type vector of strings, specifies the type of these barcodes
|
||||||
|
* @param points optional output vector of vertices of the found barcode rectangle. Will be empty if not found.
|
||||||
|
* @return true if at least one valid barcode have been found
|
||||||
|
*/
|
||||||
|
CV_WRAP bool detectAndDecodeWithType(InputArray img,
|
||||||
|
CV_OUT std::vector<std::string> &decoded_info,
|
||||||
|
CV_OUT std::vector<std::string> &decoded_type,
|
||||||
|
OutputArray points = noArray()) const;
|
||||||
|
};
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
}} // cv::barcode::
|
||||||
|
|
||||||
|
#endif // OPENCV_OBJDETECT_BARCODE_HPP
|
@ -0,0 +1,81 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html
|
||||||
|
#ifndef OPENCV_OBJDETECT_GRAPHICAL_CODE_DETECTOR_HPP
|
||||||
|
#define OPENCV_OBJDETECT_GRAPHICAL_CODE_DETECTOR_HPP
|
||||||
|
|
||||||
|
#include <opencv2/core.hpp>
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
|
||||||
|
//! @addtogroup objdetect_common
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
class CV_EXPORTS_W_SIMPLE GraphicalCodeDetector {
|
||||||
|
public:
|
||||||
|
CV_DEPRECATED_EXTERNAL // avoid using in C++ code, will be moved to "protected" (need to fix bindings first)
|
||||||
|
GraphicalCodeDetector();
|
||||||
|
|
||||||
|
GraphicalCodeDetector(const GraphicalCodeDetector&) = default;
|
||||||
|
GraphicalCodeDetector(GraphicalCodeDetector&&) = default;
|
||||||
|
GraphicalCodeDetector& operator=(const GraphicalCodeDetector&) = default;
|
||||||
|
GraphicalCodeDetector& operator=(GraphicalCodeDetector&&) = default;
|
||||||
|
|
||||||
|
/** @brief Detects graphical code in image and returns the quadrangle containing the code.
|
||||||
|
@param img grayscale or color (BGR) image containing (or not) graphical code.
|
||||||
|
@param points Output vector of vertices of the minimum-area quadrangle containing the code.
|
||||||
|
*/
|
||||||
|
CV_WRAP bool detect(InputArray img, OutputArray points) const;
|
||||||
|
|
||||||
|
/** @brief Decodes graphical code in image once it's found by the detect() method.
|
||||||
|
|
||||||
|
Returns UTF8-encoded output string or empty string if the code cannot be decoded.
|
||||||
|
@param img grayscale or color (BGR) image containing graphical code.
|
||||||
|
@param points Quadrangle vertices found by detect() method (or some other algorithm).
|
||||||
|
@param straight_code The optional output image containing binarized code, will be empty if not found.
|
||||||
|
*/
|
||||||
|
CV_WRAP std::string decode(InputArray img, InputArray points, OutputArray straight_code = noArray()) const;
|
||||||
|
|
||||||
|
/** @brief Both detects and decodes graphical code
|
||||||
|
|
||||||
|
@param img grayscale or color (BGR) image containing graphical code.
|
||||||
|
@param points optional output array of vertices of the found graphical code quadrangle, will be empty if not found.
|
||||||
|
@param straight_code The optional output image containing binarized code
|
||||||
|
*/
|
||||||
|
CV_WRAP std::string detectAndDecode(InputArray img, OutputArray points = noArray(),
|
||||||
|
OutputArray straight_code = noArray()) const;
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Detects graphical codes in image and returns the vector of the quadrangles containing the codes.
|
||||||
|
@param img grayscale or color (BGR) image containing (or not) graphical codes.
|
||||||
|
@param points Output vector of vector of vertices of the minimum-area quadrangle containing the codes.
|
||||||
|
*/
|
||||||
|
CV_WRAP bool detectMulti(InputArray img, OutputArray points) const;
|
||||||
|
|
||||||
|
/** @brief Decodes graphical codes in image once it's found by the detect() method.
|
||||||
|
@param img grayscale or color (BGR) image containing graphical codes.
|
||||||
|
@param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded.
|
||||||
|
@param points vector of Quadrangle vertices found by detect() method (or some other algorithm).
|
||||||
|
@param straight_code The optional output vector of images containing binarized codes
|
||||||
|
*/
|
||||||
|
CV_WRAP bool decodeMulti(InputArray img, InputArray points, CV_OUT std::vector<std::string>& decoded_info,
|
||||||
|
OutputArrayOfArrays straight_code = noArray()) const;
|
||||||
|
|
||||||
|
/** @brief Both detects and decodes graphical codes
|
||||||
|
@param img grayscale or color (BGR) image containing graphical codes.
|
||||||
|
@param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded.
|
||||||
|
@param points optional output vector of vertices of the found graphical code quadrangles. Will be empty if not found.
|
||||||
|
@param straight_code The optional vector of images containing binarized codes
|
||||||
|
*/
|
||||||
|
CV_WRAP bool detectAndDecodeMulti(InputArray img, CV_OUT std::vector<std::string>& decoded_info, OutputArray points = noArray(),
|
||||||
|
OutputArrayOfArrays straight_code = noArray()) const;
|
||||||
|
struct Impl;
|
||||||
|
protected:
|
||||||
|
Ptr<Impl> p;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
50
modules/objdetect/misc/java/test/BarcodeDetectorTest.java
Normal file
50
modules/objdetect/misc/java/test/BarcodeDetectorTest.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package org.opencv.test.barcode;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.opencv.core.Mat;
|
||||||
|
import org.opencv.objdetect.BarcodeDetector;
|
||||||
|
import org.opencv.imgcodecs.Imgcodecs;
|
||||||
|
import org.opencv.test.OpenCVTestCase;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class BarcodeDetectorTest extends OpenCVTestCase {
|
||||||
|
|
||||||
|
private final static String ENV_OPENCV_TEST_DATA_PATH = "OPENCV_TEST_DATA_PATH";
|
||||||
|
private String testDataPath;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
testDataPath = System.getenv(ENV_OPENCV_TEST_DATA_PATH);
|
||||||
|
if (testDataPath == null)
|
||||||
|
throw new Exception(ENV_OPENCV_TEST_DATA_PATH + " has to be defined!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDetectAndDecode() {
|
||||||
|
Mat img = Imgcodecs.imread(testDataPath + "/cv/barcode/multiple/4_barcodes.jpg");
|
||||||
|
assertFalse(img.empty());
|
||||||
|
BarcodeDetector detector = new BarcodeDetector();
|
||||||
|
assertNotNull(detector);
|
||||||
|
List < String > infos = new ArrayList< String >();
|
||||||
|
List < String > types = new ArrayList< String >();
|
||||||
|
|
||||||
|
boolean result = detector.detectAndDecodeWithType(img, infos, types);
|
||||||
|
assertTrue(result);
|
||||||
|
assertEquals(infos.size(), 4);
|
||||||
|
assertEquals(types.size(), 4);
|
||||||
|
final String[] correctResults = {"9787122276124", "9787118081473", "9787564350840", "9783319200064"};
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
assertEquals(types.get(i), "EAN_13");
|
||||||
|
result = false;
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
if (correctResults[j].equals(infos.get(i))) {
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
7
modules/objdetect/misc/objc/gen_dict.json
Normal file
7
modules/objdetect/misc/objc/gen_dict.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"ManualFuncs" : {
|
||||||
|
"QRCodeDetectorAruco": {
|
||||||
|
"getDetectorParameters": { "declaration" : [""], "implementation" : [""] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
modules/objdetect/misc/python/test/test_barcode_detector.py
Normal file
33
modules/objdetect/misc/python/test/test_barcode_detector.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
'''
|
||||||
|
===============================================================================
|
||||||
|
Barcode detect and decode pipeline.
|
||||||
|
===============================================================================
|
||||||
|
'''
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
import cv2 as cv
|
||||||
|
|
||||||
|
from tests_common import NewOpenCVTests
|
||||||
|
|
||||||
|
class barcode_detector_test(NewOpenCVTests):
|
||||||
|
|
||||||
|
def test_detect(self):
|
||||||
|
img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/barcode/multiple/4_barcodes.jpg'))
|
||||||
|
self.assertFalse(img is None)
|
||||||
|
detector = cv.barcode_BarcodeDetector()
|
||||||
|
retval, corners = detector.detect(img)
|
||||||
|
self.assertTrue(retval)
|
||||||
|
self.assertEqual(corners.shape, (4, 4, 2))
|
||||||
|
|
||||||
|
def test_detect_and_decode(self):
|
||||||
|
img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/barcode/single/book.jpg'))
|
||||||
|
self.assertFalse(img is None)
|
||||||
|
detector = cv.barcode_BarcodeDetector()
|
||||||
|
retval, decoded_info, decoded_type, corners = detector.detectAndDecodeWithType(img)
|
||||||
|
self.assertTrue(retval)
|
||||||
|
self.assertTrue(len(decoded_info) > 0)
|
||||||
|
self.assertTrue(len(decoded_type) > 0)
|
||||||
|
self.assertEqual(decoded_info[0], "9787115279460")
|
||||||
|
self.assertEqual(decoded_type[0], "EAN_13")
|
||||||
|
self.assertEqual(corners.shape, (1, 4, 2))
|
@ -171,7 +171,7 @@ PERF_TEST_P(EstimateAruco, ArucoFirst, ESTIMATE_PARAMS) {
|
|||||||
aruco::DetectorParameters detectorParams;
|
aruco::DetectorParameters detectorParams;
|
||||||
detectorParams.minDistanceToBorder = 1;
|
detectorParams.minDistanceToBorder = 1;
|
||||||
detectorParams.markerBorderBits = 1;
|
detectorParams.markerBorderBits = 1;
|
||||||
detectorParams.cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX;
|
detectorParams.cornerRefinementMethod = (int)cv::aruco::CORNER_REFINE_SUBPIX;
|
||||||
|
|
||||||
const int markerSize = 100;
|
const int markerSize = 100;
|
||||||
const int numMarkersInRow = 9;
|
const int numMarkersInRow = 9;
|
||||||
@ -203,7 +203,7 @@ PERF_TEST_P(EstimateAruco, ArucoSecond, ESTIMATE_PARAMS) {
|
|||||||
aruco::DetectorParameters detectorParams;
|
aruco::DetectorParameters detectorParams;
|
||||||
detectorParams.minDistanceToBorder = 1;
|
detectorParams.minDistanceToBorder = 1;
|
||||||
detectorParams.markerBorderBits = 1;
|
detectorParams.markerBorderBits = 1;
|
||||||
detectorParams.cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX;
|
detectorParams.cornerRefinementMethod = (int)cv::aruco::CORNER_REFINE_SUBPIX;
|
||||||
|
|
||||||
//USE_ARUCO3
|
//USE_ARUCO3
|
||||||
detectorParams.useAruco3Detection = get<0>(testParams);
|
detectorParams.useAruco3Detection = get<0>(testParams);
|
||||||
@ -255,7 +255,7 @@ PERF_TEST_P(EstimateLargeAruco, ArucoFHD, ESTIMATE_FHD_PARAMS) {
|
|||||||
aruco::DetectorParameters detectorParams;
|
aruco::DetectorParameters detectorParams;
|
||||||
detectorParams.minDistanceToBorder = 1;
|
detectorParams.minDistanceToBorder = 1;
|
||||||
detectorParams.markerBorderBits = 1;
|
detectorParams.markerBorderBits = 1;
|
||||||
detectorParams.cornerRefinementMethod = cv::aruco::CORNER_REFINE_SUBPIX;
|
detectorParams.cornerRefinementMethod = (int)cv::aruco::CORNER_REFINE_SUBPIX;
|
||||||
|
|
||||||
//USE_ARUCO3
|
//USE_ARUCO3
|
||||||
detectorParams.useAruco3Detection = get<0>(testParams).useAruco3Detection;
|
detectorParams.useAruco3Detection = get<0>(testParams).useAruco3Detection;
|
||||||
|
114
modules/objdetect/perf/perf_barcode.cpp
Normal file
114
modules/objdetect/perf/perf_barcode.cpp
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
|
#include "perf_precomp.hpp"
|
||||||
|
#include "opencv2/objdetect/barcode.hpp"
|
||||||
|
|
||||||
|
namespace opencv_test{namespace{
|
||||||
|
|
||||||
|
typedef ::perf::TestBaseWithParam< tuple<string, cv::Size> > Perf_Barcode_multi;
|
||||||
|
typedef ::perf::TestBaseWithParam< tuple<string, cv::Size> > Perf_Barcode_single;
|
||||||
|
|
||||||
|
PERF_TEST_P_(Perf_Barcode_multi, detect)
|
||||||
|
{
|
||||||
|
const string root = "cv/barcode/multiple/";
|
||||||
|
const string name_current_image = get<0>(GetParam());
|
||||||
|
const cv::Size sz = get<1>(GetParam());
|
||||||
|
const string image_path = findDataFile(root + name_current_image);
|
||||||
|
|
||||||
|
Mat src = imread(image_path);
|
||||||
|
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
|
||||||
|
cv::resize(src, src, sz);
|
||||||
|
|
||||||
|
vector< Point > corners;
|
||||||
|
auto bardet = barcode::BarcodeDetector();
|
||||||
|
bool res = false;
|
||||||
|
TEST_CYCLE()
|
||||||
|
{
|
||||||
|
res = bardet.detectMulti(src, corners);
|
||||||
|
}
|
||||||
|
SANITY_CHECK_NOTHING();
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
PERF_TEST_P_(Perf_Barcode_multi, detect_decode)
|
||||||
|
{
|
||||||
|
const string root = "cv/barcode/multiple/";
|
||||||
|
const string name_current_image = get<0>(GetParam());
|
||||||
|
const cv::Size sz = get<1>(GetParam());
|
||||||
|
const string image_path = findDataFile(root + name_current_image);
|
||||||
|
|
||||||
|
Mat src = imread(image_path);
|
||||||
|
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
|
||||||
|
cv::resize(src, src, sz);
|
||||||
|
|
||||||
|
vector<std::string> decoded_info;
|
||||||
|
vector<std::string> decoded_type;
|
||||||
|
vector< Point > corners;
|
||||||
|
auto bardet = barcode::BarcodeDetector();
|
||||||
|
bool res = false;
|
||||||
|
TEST_CYCLE()
|
||||||
|
{
|
||||||
|
res = bardet.detectAndDecodeWithType(src, decoded_info, decoded_type, corners);
|
||||||
|
}
|
||||||
|
SANITY_CHECK_NOTHING();
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
PERF_TEST_P_(Perf_Barcode_single, detect)
|
||||||
|
{
|
||||||
|
const string root = "cv/barcode/single/";
|
||||||
|
const string name_current_image = get<0>(GetParam());
|
||||||
|
const cv::Size sz = get<1>(GetParam());
|
||||||
|
const string image_path = findDataFile(root + name_current_image);
|
||||||
|
|
||||||
|
Mat src = imread(image_path);
|
||||||
|
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
|
||||||
|
cv::resize(src, src, sz);
|
||||||
|
|
||||||
|
vector< Point > corners;
|
||||||
|
auto bardet = barcode::BarcodeDetector();
|
||||||
|
bool res = false;
|
||||||
|
TEST_CYCLE()
|
||||||
|
{
|
||||||
|
res = bardet.detectMulti(src, corners);
|
||||||
|
}
|
||||||
|
SANITY_CHECK_NOTHING();
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
PERF_TEST_P_(Perf_Barcode_single, detect_decode)
|
||||||
|
{
|
||||||
|
const string root = "cv/barcode/single/";
|
||||||
|
const string name_current_image = get<0>(GetParam());
|
||||||
|
const cv::Size sz = get<1>(GetParam());
|
||||||
|
const string image_path = findDataFile(root + name_current_image);
|
||||||
|
|
||||||
|
Mat src = imread(image_path);
|
||||||
|
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
|
||||||
|
cv::resize(src, src, sz);
|
||||||
|
|
||||||
|
vector<std::string> decoded_info;
|
||||||
|
vector<std::string> decoded_type;
|
||||||
|
vector< Point > corners;
|
||||||
|
auto bardet = barcode::BarcodeDetector();
|
||||||
|
bool res = false;
|
||||||
|
TEST_CYCLE()
|
||||||
|
{
|
||||||
|
res = bardet.detectAndDecodeWithType(src, decoded_info, decoded_type, corners);
|
||||||
|
}
|
||||||
|
SANITY_CHECK_NOTHING();
|
||||||
|
ASSERT_TRUE(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Barcode_multi,
|
||||||
|
testing::Combine(
|
||||||
|
testing::Values("4_barcodes.jpg"),
|
||||||
|
testing::Values(cv::Size(2041, 2722), cv::Size(1361, 1815), cv::Size(680, 907))));
|
||||||
|
INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Barcode_single,
|
||||||
|
testing::Combine(
|
||||||
|
testing::Values("book.jpg", "bottle_1.jpg", "bottle_2.jpg"),
|
||||||
|
testing::Values(cv::Size(480, 360), cv::Size(640, 480), cv::Size(800, 600))));
|
||||||
|
|
||||||
|
}} //namespace
|
@ -3,6 +3,7 @@
|
|||||||
// of this distribution and at http://opencv.org/license.html.
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
|
||||||
#include "perf_precomp.hpp"
|
#include "perf_precomp.hpp"
|
||||||
|
#include "../test/test_qr_utils.hpp"
|
||||||
|
|
||||||
namespace opencv_test
|
namespace opencv_test
|
||||||
{
|
{
|
||||||
@ -23,7 +24,9 @@ PERF_TEST_P_(Perf_Objdetect_QRCode, detect)
|
|||||||
std::vector< Point > corners;
|
std::vector< Point > corners;
|
||||||
QRCodeDetector qrcode;
|
QRCodeDetector qrcode;
|
||||||
TEST_CYCLE() ASSERT_TRUE(qrcode.detect(src, corners));
|
TEST_CYCLE() ASSERT_TRUE(qrcode.detect(src, corners));
|
||||||
SANITY_CHECK(corners);
|
const int pixels_error = 3;
|
||||||
|
check_qr(root, name_current_image, "test_images", corners, {}, pixels_error);
|
||||||
|
SANITY_CHECK_NOTHING();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_QUIRC
|
#ifdef HAVE_QUIRC
|
||||||
@ -45,48 +48,52 @@ PERF_TEST_P_(Perf_Objdetect_QRCode, decode)
|
|||||||
decoded_info = qrcode.decode(src, corners, straight_barcode);
|
decoded_info = qrcode.decode(src, corners, straight_barcode);
|
||||||
ASSERT_FALSE(decoded_info.empty());
|
ASSERT_FALSE(decoded_info.empty());
|
||||||
}
|
}
|
||||||
|
const int pixels_error = 3;
|
||||||
std::vector<uint8_t> decoded_info_uint8_t(decoded_info.begin(), decoded_info.end());
|
check_qr(root, name_current_image, "test_images", corners, {decoded_info}, pixels_error);
|
||||||
SANITY_CHECK(decoded_info_uint8_t);
|
SANITY_CHECK_NOTHING();
|
||||||
SANITY_CHECK(straight_barcode);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef ::perf::TestBaseWithParam< std::string > Perf_Objdetect_QRCode_Multi;
|
typedef ::perf::TestBaseWithParam<std::tuple<std::string, std::string>> Perf_Objdetect_QRCode_Multi;
|
||||||
|
|
||||||
static inline bool compareCorners(const Point2f& corner1, const Point2f& corner2) {
|
static std::set<std::pair<std::string, std::string>> disabled_samples = {{"5_qrcodes.png", "aruco_based"}};
|
||||||
return corner1.x == corner2.x ? corner1.y < corner2.y : corner1.x < corner2.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, detectMulti)
|
PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, detectMulti)
|
||||||
{
|
{
|
||||||
const std::string name_current_image = GetParam();
|
const std::string name_current_image = get<0>(GetParam());
|
||||||
|
const std::string method = get<1>(GetParam());
|
||||||
const std::string root = "cv/qrcode/multiple/";
|
const std::string root = "cv/qrcode/multiple/";
|
||||||
|
|
||||||
std::string image_path = findDataFile(root + name_current_image);
|
std::string image_path = findDataFile(root + name_current_image);
|
||||||
Mat src = imread(image_path);
|
Mat src = imread(image_path);
|
||||||
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
|
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
|
||||||
std::vector<Point2f> corners;
|
std::vector<Point> corners;
|
||||||
QRCodeDetector qrcode;
|
GraphicalCodeDetector qrcode = QRCodeDetector();
|
||||||
|
if (method == "aruco_based") {
|
||||||
|
qrcode = QRCodeDetectorAruco();
|
||||||
|
}
|
||||||
TEST_CYCLE() ASSERT_TRUE(qrcode.detectMulti(src, corners));
|
TEST_CYCLE() ASSERT_TRUE(qrcode.detectMulti(src, corners));
|
||||||
sort(corners.begin(), corners.end(), compareCorners);
|
const int pixels_error = 7;
|
||||||
SANITY_CHECK(corners);
|
check_qr(root, name_current_image, "multiple_images", corners, {}, pixels_error, true);
|
||||||
}
|
SANITY_CHECK_NOTHING();
|
||||||
|
|
||||||
static inline bool compareQR(const pair<string, Mat>& v1, const pair<string, Mat>& v2) {
|
|
||||||
return v1.first < v2.first;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_QUIRC
|
#ifdef HAVE_QUIRC
|
||||||
PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, decodeMulti)
|
PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, decodeMulti)
|
||||||
{
|
{
|
||||||
const std::string name_current_image = GetParam();
|
const std::string name_current_image = get<0>(GetParam());
|
||||||
|
std::string method = get<1>(GetParam());
|
||||||
const std::string root = "cv/qrcode/multiple/";
|
const std::string root = "cv/qrcode/multiple/";
|
||||||
std::string image_path = findDataFile(root + name_current_image);
|
std::string image_path = findDataFile(root + name_current_image);
|
||||||
Mat src = imread(image_path);
|
Mat src = imread(image_path);
|
||||||
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
|
ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path;
|
||||||
QRCodeDetector qrcode;
|
if (disabled_samples.find({name_current_image, method}) != disabled_samples.end()) {
|
||||||
|
throw SkipTestException(name_current_image + " is disabled sample for method " + method);
|
||||||
|
}
|
||||||
|
GraphicalCodeDetector qrcode = QRCodeDetector();
|
||||||
|
if (method == "aruco_based") {
|
||||||
|
qrcode = QRCodeDetectorAruco();
|
||||||
|
}
|
||||||
std::vector<Point2f> corners;
|
std::vector<Point2f> corners;
|
||||||
ASSERT_TRUE(qrcode.detectMulti(src, corners));
|
ASSERT_TRUE(qrcode.detectMulti(src, corners));
|
||||||
std::vector<Mat> straight_barcode;
|
std::vector<Mat> straight_barcode;
|
||||||
@ -94,26 +101,20 @@ PERF_TEST_P_(Perf_Objdetect_QRCode_Multi, decodeMulti)
|
|||||||
TEST_CYCLE()
|
TEST_CYCLE()
|
||||||
{
|
{
|
||||||
ASSERT_TRUE(qrcode.decodeMulti(src, corners, decoded_info, straight_barcode));
|
ASSERT_TRUE(qrcode.decodeMulti(src, corners, decoded_info, straight_barcode));
|
||||||
for(size_t i = 0; i < decoded_info.size(); i++)
|
}
|
||||||
{
|
ASSERT_TRUE(decoded_info.size() > 0ull);
|
||||||
ASSERT_FALSE(decoded_info[i].empty());
|
for(size_t i = 0; i < decoded_info.size(); i++) {
|
||||||
}
|
ASSERT_FALSE(decoded_info[i].empty());
|
||||||
}
|
}
|
||||||
ASSERT_EQ(decoded_info.size(), straight_barcode.size());
|
ASSERT_EQ(decoded_info.size(), straight_barcode.size());
|
||||||
vector<pair<string, Mat> > result;
|
vector<Point> corners_result(corners.size());
|
||||||
for (size_t i = 0ull; i < decoded_info.size(); i++) {
|
for (size_t i = 0ull; i < corners_result.size(); i++) {
|
||||||
result.push_back(make_pair(decoded_info[i], straight_barcode[i]));
|
corners_result[i] = corners[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
sort(result.begin(), result.end(), compareQR);
|
const int pixels_error = 7;
|
||||||
vector<vector<uint8_t> > decoded_info_sort;
|
check_qr(root, name_current_image, "multiple_images", corners_result, decoded_info, pixels_error, true);
|
||||||
vector<Mat> straight_barcode_sort;
|
SANITY_CHECK_NOTHING();
|
||||||
for (size_t i = 0ull; i < result.size(); i++) {
|
|
||||||
vector<uint8_t> tmp(result[i].first.begin(), result[i].first.end());
|
|
||||||
decoded_info_sort.push_back(tmp);
|
|
||||||
straight_barcode_sort.push_back(result[i].second);
|
|
||||||
}
|
|
||||||
SANITY_CHECK(decoded_info_sort);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -127,11 +128,10 @@ INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Objdetect_QRCode,
|
|||||||
// version_5_right.jpg DISABLED after tile fix, PR #22025
|
// version_5_right.jpg DISABLED after tile fix, PR #22025
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Objdetect_QRCode_Multi,
|
INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Objdetect_QRCode_Multi,
|
||||||
::testing::Values(
|
testing::Combine(testing::Values("2_qrcodes.png", "3_close_qrcodes.png", "3_qrcodes.png", "4_qrcodes.png",
|
||||||
"2_qrcodes.png", "3_close_qrcodes.png", "3_qrcodes.png", "4_qrcodes.png",
|
"5_qrcodes.png", "6_qrcodes.png", "7_qrcodes.png", "8_close_qrcodes.png"),
|
||||||
"5_qrcodes.png", "6_qrcodes.png", "7_qrcodes.png", "8_close_qrcodes.png"
|
testing::Values("contours_based", "aruco_based")));
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
typedef ::perf::TestBaseWithParam< tuple< std::string, Size > > Perf_Objdetect_Not_QRCode;
|
typedef ::perf::TestBaseWithParam< tuple< std::string, Size > > Perf_Objdetect_Not_QRCode;
|
||||||
|
|
||||||
|
@ -881,7 +881,7 @@ void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corner
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// always turn on corner refinement in case of Aruco3, due to upsampling
|
// always turn on corner refinement in case of Aruco3, due to upsampling
|
||||||
detectorParams.cornerRefinementMethod = CORNER_REFINE_SUBPIX;
|
detectorParams.cornerRefinementMethod = (int)CORNER_REFINE_SUBPIX;
|
||||||
// only CORNER_REFINE_SUBPIX implement correctly for useAruco3Detection
|
// only CORNER_REFINE_SUBPIX implement correctly for useAruco3Detection
|
||||||
// Todo: update other CORNER_REFINE methods
|
// Todo: update other CORNER_REFINE methods
|
||||||
}
|
}
|
||||||
@ -923,7 +923,7 @@ void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corner
|
|||||||
vector<vector<vector<Point> > > contoursSet;
|
vector<vector<vector<Point> > > contoursSet;
|
||||||
|
|
||||||
/// STEP 2.a Detect marker candidates :: using AprilTag
|
/// STEP 2.a Detect marker candidates :: using AprilTag
|
||||||
if(detectorParams.cornerRefinementMethod == CORNER_REFINE_APRILTAG){
|
if(detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_APRILTAG){
|
||||||
_apriltag(grey, detectorParams, candidates, contours);
|
_apriltag(grey, detectorParams, candidates, contours);
|
||||||
|
|
||||||
candidatesSet.push_back(candidates);
|
candidatesSet.push_back(candidates);
|
||||||
@ -938,7 +938,7 @@ void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corner
|
|||||||
candidates, contours, ids, detectorParams, _rejectedImgPoints);
|
candidates, contours, ids, detectorParams, _rejectedImgPoints);
|
||||||
|
|
||||||
/// STEP 3: Corner refinement :: use corner subpix
|
/// STEP 3: Corner refinement :: use corner subpix
|
||||||
if (detectorParams.cornerRefinementMethod == CORNER_REFINE_SUBPIX) {
|
if (detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_SUBPIX) {
|
||||||
CV_Assert(detectorParams.cornerRefinementWinSize > 0 && detectorParams.cornerRefinementMaxIterations > 0 &&
|
CV_Assert(detectorParams.cornerRefinementWinSize > 0 && detectorParams.cornerRefinementMaxIterations > 0 &&
|
||||||
detectorParams.cornerRefinementMinAccuracy > 0);
|
detectorParams.cornerRefinementMinAccuracy > 0);
|
||||||
// Do subpixel estimation. In Aruco3 start on the lowest pyramid level and upscale the corners
|
// Do subpixel estimation. In Aruco3 start on the lowest pyramid level and upscale the corners
|
||||||
@ -963,7 +963,7 @@ void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corner
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// STEP 3, Optional : Corner refinement :: use contour container
|
/// STEP 3, Optional : Corner refinement :: use contour container
|
||||||
if (detectorParams.cornerRefinementMethod == CORNER_REFINE_CONTOUR){
|
if (detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_CONTOUR){
|
||||||
|
|
||||||
if (!ids.empty()) {
|
if (!ids.empty()) {
|
||||||
|
|
||||||
@ -976,7 +976,7 @@ void ArucoDetector::detectMarkers(InputArray _image, OutputArrayOfArrays _corner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (detectorParams.cornerRefinementMethod != CORNER_REFINE_SUBPIX && fxfy != 1.f) {
|
if (detectorParams.cornerRefinementMethod != (int)CORNER_REFINE_SUBPIX && fxfy != 1.f) {
|
||||||
// only CORNER_REFINE_SUBPIX implement correctly for useAruco3Detection
|
// only CORNER_REFINE_SUBPIX implement correctly for useAruco3Detection
|
||||||
// Todo: update other CORNER_REFINE methods
|
// Todo: update other CORNER_REFINE methods
|
||||||
|
|
||||||
@ -1213,7 +1213,7 @@ void ArucoDetector::refineDetectedMarkers(InputArray _image, const Board& _board
|
|||||||
if(closestCandidateIdx >= 0) {
|
if(closestCandidateIdx >= 0) {
|
||||||
|
|
||||||
// subpixel refinement
|
// subpixel refinement
|
||||||
if(detectorParams.cornerRefinementMethod == CORNER_REFINE_SUBPIX) {
|
if(detectorParams.cornerRefinementMethod == (int)CORNER_REFINE_SUBPIX) {
|
||||||
CV_Assert(detectorParams.cornerRefinementWinSize > 0 &&
|
CV_Assert(detectorParams.cornerRefinementWinSize > 0 &&
|
||||||
detectorParams.cornerRefinementMaxIterations > 0 &&
|
detectorParams.cornerRefinementMaxIterations > 0 &&
|
||||||
detectorParams.cornerRefinementMinAccuracy > 0);
|
detectorParams.cornerRefinementMinAccuracy > 0);
|
||||||
|
@ -258,6 +258,8 @@ Dictionary getPredefinedDictionary(PredefinedDictionaryType name) {
|
|||||||
static const Dictionary DICT_APRILTAG_36h10_DATA = Dictionary(Mat(2320, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_36h10_BYTES), 6, 0);
|
static const Dictionary DICT_APRILTAG_36h10_DATA = Dictionary(Mat(2320, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_36h10_BYTES), 6, 0);
|
||||||
static const Dictionary DICT_APRILTAG_36h11_DATA = Dictionary(Mat(587, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_36h11_BYTES), 6, 0);
|
static const Dictionary DICT_APRILTAG_36h11_DATA = Dictionary(Mat(587, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_APRILTAG_36h11_BYTES), 6, 0);
|
||||||
|
|
||||||
|
static const Dictionary DICT_ARUCO_MIP_36h12_DATA = Dictionary(Mat(250, (6 * 6 + 7) / 8, CV_8UC4, (uchar*)DICT_ARUCO_MIP_36h12_BYTES), 6, 12);
|
||||||
|
|
||||||
switch(name) {
|
switch(name) {
|
||||||
|
|
||||||
case DICT_ARUCO_ORIGINAL:
|
case DICT_ARUCO_ORIGINAL:
|
||||||
@ -308,6 +310,8 @@ Dictionary getPredefinedDictionary(PredefinedDictionaryType name) {
|
|||||||
case DICT_APRILTAG_36h11:
|
case DICT_APRILTAG_36h11:
|
||||||
return Dictionary(DICT_APRILTAG_36h11_DATA);
|
return Dictionary(DICT_APRILTAG_36h11_DATA);
|
||||||
|
|
||||||
|
case DICT_ARUCO_MIP_36h12:
|
||||||
|
return Dictionary(DICT_ARUCO_MIP_36h12_DATA);
|
||||||
}
|
}
|
||||||
return Dictionary(DICT_4X4_50_DATA);
|
return Dictionary(DICT_4X4_50_DATA);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
374
modules/objdetect/src/barcode.cpp
Normal file
374
modules/objdetect/src/barcode.cpp
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
#include <opencv2/objdetect/barcode.hpp>
|
||||||
|
#include <opencv2/core/utils/filesystem.hpp>
|
||||||
|
#include "barcode_decoder/ean13_decoder.hpp"
|
||||||
|
#include "barcode_decoder/ean8_decoder.hpp"
|
||||||
|
#include "barcode_detector/bardetect.hpp"
|
||||||
|
#include "barcode_decoder/common/super_scale.hpp"
|
||||||
|
#include "barcode_decoder/common/utils.hpp"
|
||||||
|
#include "graphical_code_detector_impl.hpp"
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using std::make_shared;
|
||||||
|
using std::array;
|
||||||
|
using std::shared_ptr;
|
||||||
|
using std::dynamic_pointer_cast;
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
static bool checkBarInputImage(InputArray img, Mat &gray)
|
||||||
|
{
|
||||||
|
CV_Assert(!img.empty());
|
||||||
|
CV_CheckDepthEQ(img.depth(), CV_8U, "");
|
||||||
|
if (img.cols() <= 40 || img.rows() <= 40)
|
||||||
|
{
|
||||||
|
return false; // image data is not enough for providing reliable results
|
||||||
|
}
|
||||||
|
int incn = img.channels();
|
||||||
|
CV_Check(incn, incn == 1 || incn == 3 || incn == 4, "");
|
||||||
|
if (incn == 3 || incn == 4)
|
||||||
|
{
|
||||||
|
cvtColor(img, gray, COLOR_BGR2GRAY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gray = img.getMat();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updatePointsResult(OutputArray points_, const vector<Point2f> &points)
|
||||||
|
{
|
||||||
|
if (points_.needed())
|
||||||
|
{
|
||||||
|
int N = int(points.size() / 4);
|
||||||
|
if (N > 0)
|
||||||
|
{
|
||||||
|
Mat m_p(N, 4, CV_32FC2, (void *) &points[0]);
|
||||||
|
int points_type = points_.fixedType() ? points_.type() : CV_32FC2;
|
||||||
|
m_p.reshape(2, points_.rows()).convertTo(points_, points_type); // Mat layout: N x 4 x 2cn
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
points_.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const array<shared_ptr<AbsDecoder>, 2> &getDecoders()
|
||||||
|
{
|
||||||
|
//indicate Decoder
|
||||||
|
static const array<shared_ptr<AbsDecoder>, 2> decoders{
|
||||||
|
shared_ptr<AbsDecoder>(new Ean13Decoder()), shared_ptr<AbsDecoder>(new Ean8Decoder())};
|
||||||
|
return decoders;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
class BarDecode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void init(const vector<Mat> &bar_imgs_);
|
||||||
|
|
||||||
|
const vector<Result> &getDecodeInformation()
|
||||||
|
{ return result_info; }
|
||||||
|
|
||||||
|
bool decodeMultiplyProcess();
|
||||||
|
|
||||||
|
private:
|
||||||
|
vector<Mat> bar_imgs;
|
||||||
|
vector<Result> result_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
void BarDecode::init(const vector<Mat> &bar_imgs_)
|
||||||
|
{
|
||||||
|
bar_imgs = bar_imgs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BarDecode::decodeMultiplyProcess()
|
||||||
|
{
|
||||||
|
static float constexpr THRESHOLD_CONF = 0.6f;
|
||||||
|
result_info.clear();
|
||||||
|
result_info.resize(bar_imgs.size());
|
||||||
|
parallel_for_(Range(0, int(bar_imgs.size())), [&](const Range &range) {
|
||||||
|
for (int i = range.start; i < range.end; i++)
|
||||||
|
{
|
||||||
|
Mat bin_bar;
|
||||||
|
Result max_res;
|
||||||
|
float max_conf = -1.f;
|
||||||
|
bool decoded = false;
|
||||||
|
for (const auto &decoder:getDecoders())
|
||||||
|
{
|
||||||
|
if (decoded)
|
||||||
|
{ break; }
|
||||||
|
for (const auto binary_type : binary_types)
|
||||||
|
{
|
||||||
|
binarize(bar_imgs[i], bin_bar, binary_type);
|
||||||
|
auto cur_res = decoder->decodeROI(bin_bar);
|
||||||
|
if (cur_res.second > max_conf)
|
||||||
|
{
|
||||||
|
max_res = cur_res.first;
|
||||||
|
max_conf = cur_res.second;
|
||||||
|
if (max_conf > THRESHOLD_CONF)
|
||||||
|
{
|
||||||
|
// code decoded
|
||||||
|
decoded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //binary types
|
||||||
|
} //decoder types
|
||||||
|
|
||||||
|
result_info[i] = max_res;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return !result_info.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Private class definition and implementation (pimpl)
|
||||||
|
|
||||||
|
struct BarcodeImpl : public GraphicalCodeDetector::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
shared_ptr<SuperScale> sr;
|
||||||
|
bool use_nn_sr = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
//=================
|
||||||
|
// own methods
|
||||||
|
BarcodeImpl() = default;
|
||||||
|
vector<Mat> initDecode(const Mat &src, const vector<vector<Point2f>> &points) const;
|
||||||
|
bool decodeWithType(InputArray img,
|
||||||
|
InputArray points,
|
||||||
|
vector<string> &decoded_info,
|
||||||
|
vector<string> &decoded_type) const;
|
||||||
|
bool detectAndDecodeWithType(InputArray img,
|
||||||
|
vector<string> &decoded_info,
|
||||||
|
vector<string> &decoded_type,
|
||||||
|
OutputArray points_) const;
|
||||||
|
|
||||||
|
//=================
|
||||||
|
// implement interface
|
||||||
|
~BarcodeImpl() CV_OVERRIDE {}
|
||||||
|
bool detect(InputArray img, OutputArray points) const CV_OVERRIDE;
|
||||||
|
string decode(InputArray img, InputArray points, OutputArray straight_code) const CV_OVERRIDE;
|
||||||
|
string detectAndDecode(InputArray img, OutputArray points, OutputArray straight_code) const CV_OVERRIDE;
|
||||||
|
bool detectMulti(InputArray img, OutputArray points) const CV_OVERRIDE;
|
||||||
|
bool decodeMulti(InputArray img, InputArray points, vector<string>& decoded_info, OutputArrayOfArrays straight_code) const CV_OVERRIDE;
|
||||||
|
bool detectAndDecodeMulti(InputArray img, vector<string>& decoded_info, OutputArray points, OutputArrayOfArrays straight_code) const CV_OVERRIDE;
|
||||||
|
};
|
||||||
|
|
||||||
|
// return cropped and scaled bar img
|
||||||
|
vector<Mat> BarcodeImpl::initDecode(const Mat &src, const vector<vector<Point2f>> &points) const
|
||||||
|
{
|
||||||
|
vector<Mat> bar_imgs;
|
||||||
|
for (auto &corners : points)
|
||||||
|
{
|
||||||
|
Mat bar_img;
|
||||||
|
cropROI(src, bar_img, corners);
|
||||||
|
// sharpen(bar_img, bar_img);
|
||||||
|
// empirical settings
|
||||||
|
if (bar_img.cols < 320 || bar_img.cols > 640)
|
||||||
|
{
|
||||||
|
float scale = 560.0f / static_cast<float>(bar_img.cols);
|
||||||
|
sr->processImageScale(bar_img, bar_img, scale, use_nn_sr);
|
||||||
|
}
|
||||||
|
bar_imgs.emplace_back(bar_img);
|
||||||
|
}
|
||||||
|
return bar_imgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BarcodeImpl::decodeWithType(InputArray img,
|
||||||
|
InputArray points,
|
||||||
|
vector<string> &decoded_info,
|
||||||
|
vector<string> &decoded_type) const
|
||||||
|
{
|
||||||
|
Mat inarr;
|
||||||
|
if (!checkBarInputImage(img, inarr))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CV_Assert(points.size().width > 0);
|
||||||
|
CV_Assert((points.size().width % 4) == 0);
|
||||||
|
vector<vector<Point2f>> src_points;
|
||||||
|
Mat bar_points = points.getMat();
|
||||||
|
bar_points = bar_points.reshape(2, 1);
|
||||||
|
for (int i = 0; i < bar_points.size().width; i += 4)
|
||||||
|
{
|
||||||
|
vector<Point2f> tempMat = bar_points.colRange(i, i + 4);
|
||||||
|
if (contourArea(tempMat) > 0.0)
|
||||||
|
{
|
||||||
|
src_points.push_back(tempMat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CV_Assert(!src_points.empty());
|
||||||
|
vector<Mat> bar_imgs = initDecode(inarr, src_points);
|
||||||
|
BarDecode bardec;
|
||||||
|
bardec.init(bar_imgs);
|
||||||
|
bardec.decodeMultiplyProcess();
|
||||||
|
const vector<Result> info = bardec.getDecodeInformation();
|
||||||
|
decoded_info.clear();
|
||||||
|
decoded_type.clear();
|
||||||
|
bool ok = false;
|
||||||
|
for (const auto &res : info)
|
||||||
|
{
|
||||||
|
if (res.isValid())
|
||||||
|
{
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
decoded_info.emplace_back(res.result);
|
||||||
|
decoded_type.emplace_back(res.typeString());
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BarcodeImpl::detectAndDecodeWithType(InputArray img,
|
||||||
|
vector<string> &decoded_info,
|
||||||
|
vector<string> &decoded_type,
|
||||||
|
OutputArray points_) const
|
||||||
|
{
|
||||||
|
Mat inarr;
|
||||||
|
if (!checkBarInputImage(img, inarr))
|
||||||
|
{
|
||||||
|
points_.release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
vector<Point2f> points;
|
||||||
|
bool ok = this->detect(inarr, points);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
points_.release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
updatePointsResult(points_, points);
|
||||||
|
decoded_info.clear();
|
||||||
|
decoded_type.clear();
|
||||||
|
ok = decodeWithType(inarr, points, decoded_info, decoded_type);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BarcodeImpl::detect(InputArray img, OutputArray points) const
|
||||||
|
{
|
||||||
|
Mat inarr;
|
||||||
|
if (!checkBarInputImage(img, inarr))
|
||||||
|
{
|
||||||
|
points.release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Detect bardet;
|
||||||
|
bardet.init(inarr);
|
||||||
|
bardet.localization();
|
||||||
|
if (!bardet.computeTransformationPoints())
|
||||||
|
{ return false; }
|
||||||
|
vector<vector<Point2f>> pnts2f = bardet.getTransformationPoints();
|
||||||
|
vector<Point2f> trans_points;
|
||||||
|
for (auto &i : pnts2f)
|
||||||
|
{
|
||||||
|
for (const auto &j : i)
|
||||||
|
{
|
||||||
|
trans_points.push_back(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updatePointsResult(points, trans_points);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string BarcodeImpl::decode(InputArray img, InputArray points, OutputArray straight_code) const
|
||||||
|
{
|
||||||
|
CV_UNUSED(straight_code);
|
||||||
|
vector<string> decoded_info;
|
||||||
|
vector<string> decoded_type;
|
||||||
|
if (!decodeWithType(img, points, decoded_info, decoded_type))
|
||||||
|
return string();
|
||||||
|
if (decoded_info.size() < 1)
|
||||||
|
return string();
|
||||||
|
return decoded_info[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
string BarcodeImpl::detectAndDecode(InputArray img, OutputArray points, OutputArray straight_code) const
|
||||||
|
{
|
||||||
|
CV_UNUSED(straight_code);
|
||||||
|
vector<string> decoded_info;
|
||||||
|
vector<string> decoded_type;
|
||||||
|
vector<Point> points_;
|
||||||
|
if (!detectAndDecodeWithType(img, decoded_info, decoded_type, points_))
|
||||||
|
return string();
|
||||||
|
if (points_.size() < 4 || decoded_info.size() < 1)
|
||||||
|
return string();
|
||||||
|
points_.resize(4);
|
||||||
|
points.setTo(points_);
|
||||||
|
return decoded_info[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BarcodeImpl::detectMulti(InputArray img, OutputArray points) const
|
||||||
|
{
|
||||||
|
return detect(img, points);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BarcodeImpl::decodeMulti(InputArray img, InputArray points, vector<string> &decoded_info, OutputArrayOfArrays straight_code) const
|
||||||
|
{
|
||||||
|
CV_UNUSED(straight_code);
|
||||||
|
vector<string> decoded_type;
|
||||||
|
return decodeWithType(img, points, decoded_info, decoded_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BarcodeImpl::detectAndDecodeMulti(InputArray img, vector<string> &decoded_info, OutputArray points, OutputArrayOfArrays straight_code) const
|
||||||
|
{
|
||||||
|
CV_UNUSED(straight_code);
|
||||||
|
vector<string> decoded_type;
|
||||||
|
return detectAndDecodeWithType(img, decoded_info, decoded_type, points);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Public class implementation
|
||||||
|
|
||||||
|
BarcodeDetector::BarcodeDetector()
|
||||||
|
: BarcodeDetector(string(), string())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BarcodeDetector::BarcodeDetector(const string &prototxt_path, const string &model_path)
|
||||||
|
{
|
||||||
|
Ptr<BarcodeImpl> p_ = new BarcodeImpl();
|
||||||
|
p = p_;
|
||||||
|
if (!prototxt_path.empty() && !model_path.empty())
|
||||||
|
{
|
||||||
|
CV_Assert(utils::fs::exists(prototxt_path));
|
||||||
|
CV_Assert(utils::fs::exists(model_path));
|
||||||
|
p_->sr = make_shared<SuperScale>();
|
||||||
|
int res = p_->sr->init(prototxt_path, model_path);
|
||||||
|
CV_Assert(res == 0);
|
||||||
|
p_->use_nn_sr = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BarcodeDetector::~BarcodeDetector() = default;
|
||||||
|
|
||||||
|
bool BarcodeDetector::decodeWithType(InputArray img, InputArray points, vector<string> &decoded_info, vector<string> &decoded_type) const
|
||||||
|
{
|
||||||
|
Ptr<BarcodeImpl> p_ = dynamic_pointer_cast<BarcodeImpl>(p);
|
||||||
|
CV_Assert(p_);
|
||||||
|
return p_->decodeWithType(img, points, decoded_info, decoded_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BarcodeDetector::detectAndDecodeWithType(InputArray img, vector<string> &decoded_info, vector<string> &decoded_type, OutputArray points_) const
|
||||||
|
{
|
||||||
|
Ptr<BarcodeImpl> p_ = dynamic_pointer_cast<BarcodeImpl>(p);
|
||||||
|
CV_Assert(p_);
|
||||||
|
return p_->detectAndDecodeWithType(img, decoded_info, decoded_type, points_);
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace barcode
|
||||||
|
} // namespace cv
|
118
modules/objdetect/src/barcode_decoder/abs_decoder.cpp
Normal file
118
modules/objdetect/src/barcode_decoder/abs_decoder.cpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds
|
||||||
|
|
||||||
|
#include "../precomp.hpp"
|
||||||
|
#include "abs_decoder.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
|
||||||
|
void cropROI(const Mat &src, Mat &dst, const std::vector<Point2f> &rects)
|
||||||
|
{
|
||||||
|
std::vector<Point2f> vertices = rects;
|
||||||
|
int height = cvRound(norm(vertices[0] - vertices[1]));
|
||||||
|
int width = cvRound(norm(vertices[1] - vertices[2]));
|
||||||
|
if (height > width)
|
||||||
|
{
|
||||||
|
std::swap(height, width);
|
||||||
|
Point2f v0 = vertices[0];
|
||||||
|
vertices.erase(vertices.begin());
|
||||||
|
vertices.push_back(v0);
|
||||||
|
}
|
||||||
|
std::vector<Point2f> dst_vertices{
|
||||||
|
Point2f(0, (float) (height - 1)), Point2f(0, 0), Point2f((float) (width - 1), 0),
|
||||||
|
Point2f((float) (width - 1), (float) (height - 1))};
|
||||||
|
dst.create(Size(width, height), CV_8UC1);
|
||||||
|
Mat M = getPerspectiveTransform(vertices, dst_vertices);
|
||||||
|
warpPerspective(src, dst, M, dst.size(), cv::INTER_LINEAR, BORDER_CONSTANT, Scalar(255));
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillCounter(const std::vector<uchar> &row, uint start, Counter &counter)
|
||||||
|
{
|
||||||
|
size_t counter_length = counter.pattern.size();
|
||||||
|
std::fill(counter.pattern.begin(), counter.pattern.end(), 0);
|
||||||
|
counter.sum = 0;
|
||||||
|
size_t end = row.size();
|
||||||
|
uchar color = row[start];
|
||||||
|
uint counterPosition = 0;
|
||||||
|
while (start < end)
|
||||||
|
{
|
||||||
|
if (row[start] == color)
|
||||||
|
{ // that is, exactly one is true
|
||||||
|
counter.pattern[counterPosition]++;
|
||||||
|
counter.sum++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
counterPosition++;
|
||||||
|
if (counterPosition == counter_length)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
counter.pattern[counterPosition] = 1;
|
||||||
|
counter.sum++;
|
||||||
|
color = 255 - color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint
|
||||||
|
patternMatchVariance(const Counter &counter, const std::vector<int> &pattern, uint maxIndividualVariance)
|
||||||
|
{
|
||||||
|
size_t numCounters = counter.pattern.size();
|
||||||
|
int total = static_cast<int>(counter.sum);
|
||||||
|
int patternLength = std::accumulate(pattern.cbegin(), pattern.cend(), 0);
|
||||||
|
if (total < patternLength)
|
||||||
|
{
|
||||||
|
// If we don't even have one pixel per unit of bar width, assume this is too small
|
||||||
|
// to reliably match, so fail:
|
||||||
|
// and use constexpr functions
|
||||||
|
return WHITE;// max
|
||||||
|
}
|
||||||
|
// We're going to fake floating-point math in integers. We just need to use more bits.
|
||||||
|
// Scale up patternLength so that intermediate values below like scaledCounter will have
|
||||||
|
// more "significant digits"
|
||||||
|
|
||||||
|
int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength;
|
||||||
|
maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT;
|
||||||
|
uint totalVariance = 0;
|
||||||
|
for (uint x = 0; x < numCounters; x++)
|
||||||
|
{
|
||||||
|
int cnt = counter.pattern[x] << INTEGER_MATH_SHIFT;
|
||||||
|
int scaledPattern = pattern[x] * unitBarWidth;
|
||||||
|
uint variance = std::abs(cnt - scaledPattern);
|
||||||
|
if (variance > maxIndividualVariance)
|
||||||
|
{
|
||||||
|
return WHITE;
|
||||||
|
}
|
||||||
|
totalVariance += variance;
|
||||||
|
}
|
||||||
|
return totalVariance / total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines how closely a set of observed counts of runs of black/white values matches a given
|
||||||
|
* target pattern. This is reported as the ratio of the total variance from the expected pattern
|
||||||
|
* proportions across all pattern elements, to the length of the pattern.
|
||||||
|
*
|
||||||
|
* @param counters observed counters
|
||||||
|
* @param pattern expected pattern
|
||||||
|
* @param maxIndividualVariance The most any counter can differ before we give up
|
||||||
|
* @return ratio of total variance between counters and pattern compared to total pattern size,
|
||||||
|
* where the ratio has been multiplied by 256. So, 0 means no variance (perfect match); 256 means
|
||||||
|
* the total variance between counters and patterns equals the pattern length, higher values mean
|
||||||
|
* even more variance
|
||||||
|
*/
|
||||||
|
uint patternMatch(const Counter &counters, const std::vector<int> &pattern, uint maxIndividual)
|
||||||
|
{
|
||||||
|
CV_Assert(counters.pattern.size() == pattern.size());
|
||||||
|
return patternMatchVariance(counters, pattern, maxIndividual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
modules/objdetect/src/barcode_decoder/abs_decoder.hpp
Normal file
99
modules/objdetect/src/barcode_decoder/abs_decoder.hpp
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds
|
||||||
|
|
||||||
|
#ifndef OPENCV_BARCODE_ABS_DECODER_HPP
|
||||||
|
#define OPENCV_BARCODE_ABS_DECODER_HPP
|
||||||
|
|
||||||
|
#include "opencv2/objdetect/barcode.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
constexpr static uchar BLACK = std::numeric_limits<uchar>::min();
|
||||||
|
// WHITE elemental area is 0xff
|
||||||
|
constexpr static uchar WHITE = std::numeric_limits<uchar>::max();
|
||||||
|
|
||||||
|
|
||||||
|
struct Result
|
||||||
|
{
|
||||||
|
enum BarcodeType
|
||||||
|
{
|
||||||
|
BARCODE_NONE,
|
||||||
|
BARCODE_EAN_8,
|
||||||
|
BARCODE_EAN_13,
|
||||||
|
BARCODE_UPC_A,
|
||||||
|
BARCODE_UPC_E,
|
||||||
|
BARCODE_UPC_EAN_EXTENSION
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
BarcodeType format = Result::BARCODE_NONE;
|
||||||
|
|
||||||
|
Result() = default;
|
||||||
|
|
||||||
|
Result(const std::string &_result, BarcodeType _format)
|
||||||
|
{
|
||||||
|
result = _result;
|
||||||
|
format = _format;
|
||||||
|
}
|
||||||
|
string typeString() const
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case Result::BARCODE_EAN_8: return "EAN_8";
|
||||||
|
case Result::BARCODE_EAN_13: return "EAN_13";
|
||||||
|
case Result::BARCODE_UPC_E: return "UPC_E";
|
||||||
|
case Result::BARCODE_UPC_A: return "UPC_A";
|
||||||
|
case Result::BARCODE_UPC_EAN_EXTENSION: return "UPC_EAN_EXTENSION";
|
||||||
|
default: return string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool isValid() const
|
||||||
|
{
|
||||||
|
return format != BARCODE_NONE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Counter
|
||||||
|
{
|
||||||
|
std::vector<int> pattern;
|
||||||
|
uint sum;
|
||||||
|
|
||||||
|
explicit Counter(const vector<int> &_pattern)
|
||||||
|
{
|
||||||
|
pattern = _pattern;
|
||||||
|
sum = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class AbsDecoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual std::pair<Result, float> decodeROI(const Mat &bar_img) const = 0;
|
||||||
|
|
||||||
|
virtual ~AbsDecoder() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual Result decode(const vector<uchar> &data) const = 0;
|
||||||
|
|
||||||
|
virtual bool isValid(const string &result) const = 0;
|
||||||
|
|
||||||
|
size_t bits_num{};
|
||||||
|
size_t digit_number{};
|
||||||
|
};
|
||||||
|
|
||||||
|
void cropROI(const Mat &_src, Mat &_dst, const std::vector<Point2f> &rect);
|
||||||
|
|
||||||
|
void fillCounter(const std::vector<uchar> &row, uint start, Counter &counter);
|
||||||
|
|
||||||
|
constexpr static uint INTEGER_MATH_SHIFT = 8;
|
||||||
|
constexpr static uint PATTERN_MATCH_RESULT_SCALE_FACTOR = 1 << INTEGER_MATH_SHIFT;
|
||||||
|
|
||||||
|
uint patternMatch(const Counter &counters, const std::vector<int> &pattern, uint maxIndividual);
|
||||||
|
}
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif // OPENCV_BARCODE_ABS_DECODER_HPP
|
@ -0,0 +1,195 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Modified from ZXing. Copyright ZXing authors.
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||||
|
|
||||||
|
#include "../../precomp.hpp"
|
||||||
|
#include "hybrid_binarizer.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
|
||||||
|
|
||||||
|
#define CLAMP(x, x1, x2) x < (x1) ? (x1) : ((x) > (x2) ? (x2) : (x))
|
||||||
|
|
||||||
|
// This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels.
|
||||||
|
// So this is the smallest dimension in each axis we can accept.
|
||||||
|
constexpr static int BLOCK_SIZE_POWER = 3;
|
||||||
|
constexpr static int BLOCK_SIZE = 1 << BLOCK_SIZE_POWER; // ...0100...00
|
||||||
|
constexpr static int BLOCK_SIZE_MASK = BLOCK_SIZE - 1; // ...0011...11
|
||||||
|
constexpr static int MINIMUM_DIMENSION = BLOCK_SIZE * 5;
|
||||||
|
constexpr static int MIN_DYNAMIC_RANGE = 24;
|
||||||
|
|
||||||
|
void
|
||||||
|
calculateThresholdForBlock(const std::vector<uchar> &luminances, int sub_width, int sub_height, int width, int height,
|
||||||
|
const Mat &black_points, Mat &dst)
|
||||||
|
{
|
||||||
|
int maxYOffset = height - BLOCK_SIZE;
|
||||||
|
int maxXOffset = width - BLOCK_SIZE;
|
||||||
|
for (int y = 0; y < sub_height; y++)
|
||||||
|
{
|
||||||
|
int yoffset = y << BLOCK_SIZE_POWER;
|
||||||
|
if (yoffset > maxYOffset)
|
||||||
|
{
|
||||||
|
yoffset = maxYOffset;
|
||||||
|
}
|
||||||
|
int top = CLAMP(y, 2, sub_height - 3);
|
||||||
|
for (int x = 0; x < sub_width; x++)
|
||||||
|
{
|
||||||
|
int xoffset = x << BLOCK_SIZE_POWER;
|
||||||
|
if (xoffset > maxXOffset)
|
||||||
|
{
|
||||||
|
xoffset = maxXOffset;
|
||||||
|
}
|
||||||
|
int left = CLAMP(x, 2, sub_width - 3);
|
||||||
|
int sum = 0;
|
||||||
|
const auto *black_row = black_points.ptr<uchar>(top - 2);
|
||||||
|
for (int z = 0; z <= 4; z++)
|
||||||
|
{
|
||||||
|
sum += black_row[left - 2] + black_row[left - 1] + black_row[left] + black_row[left + 1] +
|
||||||
|
black_row[left + 2];
|
||||||
|
black_row += black_points.cols;
|
||||||
|
}
|
||||||
|
int average = sum / 25;
|
||||||
|
int temp_y = 0;
|
||||||
|
|
||||||
|
auto *ptr = dst.ptr<uchar>(yoffset, xoffset);
|
||||||
|
for (int offset = yoffset * width + xoffset; temp_y < 8; offset += width)
|
||||||
|
{
|
||||||
|
for (int temp_x = 0; temp_x < 8; ++temp_x)
|
||||||
|
{
|
||||||
|
*(ptr + temp_x) = (luminances[offset + temp_x] & 255) <= average ? 0 : 255;
|
||||||
|
}
|
||||||
|
++temp_y;
|
||||||
|
ptr += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat calculateBlackPoints(std::vector<uchar> luminances, int sub_width, int sub_height, int width, int height)
|
||||||
|
{
|
||||||
|
int maxYOffset = height - BLOCK_SIZE;
|
||||||
|
int maxXOffset = width - BLOCK_SIZE;
|
||||||
|
Mat black_points(Size(sub_width, sub_height), CV_8UC1);
|
||||||
|
for (int y = 0; y < sub_height; y++)
|
||||||
|
{
|
||||||
|
int yoffset = y << BLOCK_SIZE_POWER;
|
||||||
|
if (yoffset > maxYOffset)
|
||||||
|
{
|
||||||
|
yoffset = maxYOffset;
|
||||||
|
}
|
||||||
|
for (int x = 0; x < sub_width; x++)
|
||||||
|
{
|
||||||
|
int xoffset = x << BLOCK_SIZE_POWER;
|
||||||
|
if (xoffset > maxXOffset)
|
||||||
|
{
|
||||||
|
xoffset = maxXOffset;
|
||||||
|
}
|
||||||
|
int sum = 0;
|
||||||
|
int min = 0xFF;
|
||||||
|
int max = 0;
|
||||||
|
for (int yy = 0, offset = yoffset * width + xoffset; yy < BLOCK_SIZE; yy++, offset += width)
|
||||||
|
{
|
||||||
|
for (int xx = 0; xx < BLOCK_SIZE; xx++)
|
||||||
|
{
|
||||||
|
int pixel = luminances[offset + xx] & 0xFF;
|
||||||
|
sum += pixel;
|
||||||
|
// still looking for good contrast
|
||||||
|
if (pixel < min)
|
||||||
|
{
|
||||||
|
min = pixel;
|
||||||
|
}
|
||||||
|
if (pixel > max)
|
||||||
|
{
|
||||||
|
max = pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// short-circuit min/max tests once dynamic range is met
|
||||||
|
if (max - min > MIN_DYNAMIC_RANGE)
|
||||||
|
{
|
||||||
|
// finish the rest of the rows quickly
|
||||||
|
for (yy++, offset += width; yy < BLOCK_SIZE; yy++, offset += width)
|
||||||
|
{
|
||||||
|
for (int xx = 0; xx < BLOCK_SIZE; xx++)
|
||||||
|
{
|
||||||
|
sum += luminances[offset + xx] & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default estimate is the average of the values in the block.
|
||||||
|
int average = sum >> (BLOCK_SIZE_POWER * 2);
|
||||||
|
if (max - min <= MIN_DYNAMIC_RANGE)
|
||||||
|
{
|
||||||
|
// If variation within the block is low, assume this is a block with only light or only
|
||||||
|
// dark pixels. In that case we do not want to use the average, as it would divide this
|
||||||
|
// low contrast area into black and white pixels, essentially creating data out of noise.
|
||||||
|
//
|
||||||
|
// The default assumption is that the block is light/background. Since no estimate for
|
||||||
|
// the level of dark pixels exists locally, use half the min for the block.
|
||||||
|
average = min / 2;
|
||||||
|
|
||||||
|
if (y > 0 && x > 0)
|
||||||
|
{
|
||||||
|
// Correct the "white background" assumption for blocks that have neighbors by comparing
|
||||||
|
// the pixels in this block to the previously calculated black points. This is based on
|
||||||
|
// the fact that dark barcode symbology is always surrounded by some amount of light
|
||||||
|
// background for which reasonable black point estimates were made. The bp estimated at
|
||||||
|
// the boundaries is used for the interior.
|
||||||
|
|
||||||
|
// The (min < bp) is arbitrary but works better than other heuristics that were tried.
|
||||||
|
int averageNeighborBlackPoint =
|
||||||
|
(black_points.at<uchar>(y - 1, x) + (2 * black_points.at<uchar>(y, x - 1)) +
|
||||||
|
black_points.at<uchar>(y - 1, x - 1)) / 4;
|
||||||
|
if (min < averageNeighborBlackPoint)
|
||||||
|
{
|
||||||
|
average = averageNeighborBlackPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
black_points.at<uchar>(y, x) = (uchar) average;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return black_points;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hybridBinarization(const Mat &src, Mat &dst)
|
||||||
|
{
|
||||||
|
int width = src.cols;
|
||||||
|
int height = src.rows;
|
||||||
|
|
||||||
|
if (width >= MINIMUM_DIMENSION && height >= MINIMUM_DIMENSION)
|
||||||
|
{
|
||||||
|
std::vector<uchar> luminances(src.begin<uchar>(), src.end<uchar>());
|
||||||
|
|
||||||
|
int sub_width = width >> BLOCK_SIZE_POWER;
|
||||||
|
if ((width & BLOCK_SIZE_MASK) != 0)
|
||||||
|
{
|
||||||
|
sub_width++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sub_height = height >> BLOCK_SIZE_POWER;
|
||||||
|
if ((height & BLOCK_SIZE_MASK) != 0)
|
||||||
|
{
|
||||||
|
sub_height++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat black_points = calculateBlackPoints(luminances, sub_width, sub_height, width, height);
|
||||||
|
|
||||||
|
dst.create(src.size(), src.type());
|
||||||
|
calculateThresholdForBlock(luminances, sub_width, sub_height, width, height, black_points, dst);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
threshold(src, dst, 155, 255, THRESH_OTSU + THRESH_BINARY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Modified from ZXing. Copyright ZXing authors.
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||||
|
|
||||||
|
#ifndef OPENCV_BARCODE_HYBRID_BINARIZER_HPP
|
||||||
|
#define OPENCV_BARCODE_HYBRID_BINARIZER_HPP
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
|
||||||
|
void hybridBinarization(const Mat &src, Mat &dst);
|
||||||
|
|
||||||
|
void
|
||||||
|
calculateThresholdForBlock(const std::vector<uchar> &luminances, int sub_width, int sub_height, int width, int height,
|
||||||
|
const Mat &black_points, Mat &dst);
|
||||||
|
|
||||||
|
Mat calculateBlackPoints(std::vector<uchar> luminances, int sub_width, int sub_height, int width, int height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // OPENCV_BARCODE_HYBRID_BINARIZER_HPP
|
77
modules/objdetect/src/barcode_decoder/common/super_scale.cpp
Normal file
77
modules/objdetect/src/barcode_decoder/common/super_scale.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
//
|
||||||
|
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||||
|
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
// Modified by darkliang wangberlinT
|
||||||
|
|
||||||
|
#include "../../precomp.hpp"
|
||||||
|
#include "super_scale.hpp"
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENCV_DNN
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
constexpr static float MAX_SCALE = 4.0f;
|
||||||
|
|
||||||
|
int SuperScale::init(const std::string &proto_path, const std::string &model_path)
|
||||||
|
{
|
||||||
|
srnet_ = dnn::readNetFromCaffe(proto_path, model_path);
|
||||||
|
net_loaded_ = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SuperScale::processImageScale(const Mat &src, Mat &dst, float scale, const bool &use_sr, int sr_max_size)
|
||||||
|
{
|
||||||
|
scale = min(scale, MAX_SCALE);
|
||||||
|
if (scale > .0 && scale < 1.0)
|
||||||
|
{ // down sample
|
||||||
|
resize(src, dst, Size(), scale, scale, INTER_AREA);
|
||||||
|
}
|
||||||
|
else if (scale > 1.5 && scale < 2.0)
|
||||||
|
{
|
||||||
|
resize(src, dst, Size(), scale, scale, INTER_CUBIC);
|
||||||
|
}
|
||||||
|
else if (scale >= 2.0)
|
||||||
|
{
|
||||||
|
int width = src.cols;
|
||||||
|
int height = src.rows;
|
||||||
|
if (use_sr && (int) sqrt(width * height * 1.0) < sr_max_size && net_loaded_)
|
||||||
|
{
|
||||||
|
superResolutionScale(src, dst);
|
||||||
|
if (scale > 2.0)
|
||||||
|
{
|
||||||
|
processImageScale(dst, dst, scale / 2.0f, use_sr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ resize(src, dst, Size(), scale, scale, INTER_CUBIC); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int SuperScale::superResolutionScale(const Mat &src, Mat &dst)
|
||||||
|
{
|
||||||
|
Mat blob;
|
||||||
|
dnn::blobFromImage(src, blob, 1.0 / 255, Size(src.cols, src.rows), {0.0f}, false, false);
|
||||||
|
|
||||||
|
srnet_.setInput(blob);
|
||||||
|
auto prob = srnet_.forward();
|
||||||
|
|
||||||
|
dst = Mat(prob.size[2], prob.size[3], CV_8UC1);
|
||||||
|
|
||||||
|
for (int row = 0; row < prob.size[2]; row++)
|
||||||
|
{
|
||||||
|
const float *prob_score = prob.ptr<float>(0, 0, row);
|
||||||
|
auto *dst_row = dst.ptr<uchar>(row);
|
||||||
|
for (int col = 0; col < prob.size[3]; col++)
|
||||||
|
{
|
||||||
|
dst_row[col] = saturate_cast<uchar>(prob_score[col] * 255.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace barcode
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif // HAVE_OPENCV_DNN
|
69
modules/objdetect/src/barcode_decoder/common/super_scale.hpp
Normal file
69
modules/objdetect/src/barcode_decoder/common/super_scale.hpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
//
|
||||||
|
// Tencent is pleased to support the open source community by making WeChat QRCode available.
|
||||||
|
// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
|
||||||
|
|
||||||
|
#ifndef OPENCV_BARCODE_SUPER_SCALE_HPP
|
||||||
|
#define OPENCV_BARCODE_SUPER_SCALE_HPP
|
||||||
|
|
||||||
|
#ifdef HAVE_OPENCV_DNN
|
||||||
|
|
||||||
|
#include "opencv2/dnn.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
|
||||||
|
class SuperScale
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SuperScale() = default;
|
||||||
|
|
||||||
|
~SuperScale() = default;
|
||||||
|
|
||||||
|
int init(const std::string &proto_path, const std::string &model_path);
|
||||||
|
|
||||||
|
void processImageScale(const Mat &src, Mat &dst, float scale, const bool &use_sr, int sr_max_size = 160);
|
||||||
|
|
||||||
|
private:
|
||||||
|
dnn::Net srnet_;
|
||||||
|
bool net_loaded_ = false;
|
||||||
|
|
||||||
|
int superResolutionScale(const cv::Mat &src, cv::Mat &dst);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace barcode
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#else // HAVE_OPENCV_DNN
|
||||||
|
|
||||||
|
#include "opencv2/core.hpp"
|
||||||
|
#include "opencv2/core/utils/logger.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
|
||||||
|
class SuperScale
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int init(const std::string &, const std::string &)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
void processImageScale(const Mat &src, Mat &dst, float scale, const bool & isEnabled, int)
|
||||||
|
{
|
||||||
|
if (isEnabled)
|
||||||
|
{
|
||||||
|
CV_LOG_WARNING(NULL, "objdetect/barcode: SuperScaling disabled - OpenCV has been built without DNN support");
|
||||||
|
}
|
||||||
|
resize(src, dst, Size(), scale, scale, INTER_CUBIC);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace barcode
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif // !HAVE_OPENCV_DNN
|
||||||
|
|
||||||
|
#endif // OPENCV_BARCODE_SUPER_SCALE_HPP
|
36
modules/objdetect/src/barcode_decoder/common/utils.cpp
Normal file
36
modules/objdetect/src/barcode_decoder/common/utils.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds
|
||||||
|
|
||||||
|
#include "../../precomp.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
#include "hybrid_binarizer.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
|
||||||
|
|
||||||
|
void sharpen(const Mat &src, const Mat &dst)
|
||||||
|
{
|
||||||
|
Mat blur;
|
||||||
|
GaussianBlur(src, blur, Size(0, 0), 25);
|
||||||
|
addWeighted(src, 2, blur, -1, -20, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void binarize(const Mat &src, Mat &dst, BinaryType mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case OTSU:
|
||||||
|
threshold(src, dst, 155, 255, THRESH_OTSU + THRESH_BINARY);
|
||||||
|
break;
|
||||||
|
case HYBRID:
|
||||||
|
hybridBinarization(src, dst);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
CV_Error(Error::StsNotImplemented, "This binary type is not yet implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
modules/objdetect/src/barcode_decoder/common/utils.hpp
Normal file
26
modules/objdetect/src/barcode_decoder/common/utils.hpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds
|
||||||
|
|
||||||
|
#ifndef OPENCV_BARCODE_UTILS_HPP
|
||||||
|
#define OPENCV_BARCODE_UTILS_HPP
|
||||||
|
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
|
||||||
|
enum BinaryType
|
||||||
|
{
|
||||||
|
OTSU = 0, HYBRID = 1
|
||||||
|
};
|
||||||
|
static constexpr BinaryType binary_types[] = {OTSU, HYBRID};
|
||||||
|
|
||||||
|
void sharpen(const Mat &src, const Mat &dst);
|
||||||
|
|
||||||
|
void binarize(const Mat &src, Mat &dst, BinaryType mode);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // OPENCV_BARCODE_UTILS_HPP
|
92
modules/objdetect/src/barcode_decoder/ean13_decoder.cpp
Normal file
92
modules/objdetect/src/barcode_decoder/ean13_decoder.cpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds
|
||||||
|
|
||||||
|
#include "../precomp.hpp"
|
||||||
|
#include "ean13_decoder.hpp"
|
||||||
|
|
||||||
|
// three digit decode method from https://baike.baidu.com/item/EAN-13
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
|
||||||
|
static constexpr size_t EAN13BITS_NUM = 95;
|
||||||
|
static constexpr size_t EAN13DIGIT_NUM = 13;
|
||||||
|
// default thought that mat is a matrix after binary-transfer.
|
||||||
|
/**
|
||||||
|
* decode EAN-13
|
||||||
|
* @prama: data: the input array,
|
||||||
|
* @prama: start: the index of start order, begin at 0, max-value is data.size()-1
|
||||||
|
* it scan begin at the data[start]
|
||||||
|
*/
|
||||||
|
Result Ean13Decoder::decode(const vector<uchar> &data) const
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
char decode_result[EAN13DIGIT_NUM + 1]{'\0'};
|
||||||
|
if (data.size() < EAN13BITS_NUM)
|
||||||
|
{
|
||||||
|
return Result("Wrong Size", Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
pair<uint, uint> pattern;
|
||||||
|
if (!findStartGuardPatterns(data, pattern))
|
||||||
|
{
|
||||||
|
return Result("Begin Pattern Not Found", Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
uint start = pattern.second;
|
||||||
|
Counter counter(vector<int>{0, 0, 0, 0});
|
||||||
|
size_t end = data.size();
|
||||||
|
int first_char_bit = 0;
|
||||||
|
// [1,6] are left part of EAN, [7,12] are right part, index 0 is calculated by left part
|
||||||
|
for (int i = 1; i < 7 && start < end; ++i)
|
||||||
|
{
|
||||||
|
int bestMatch = decodeDigit(data, counter, start, get_AB_Patterns());
|
||||||
|
if (bestMatch == -1)
|
||||||
|
{
|
||||||
|
return Result("Decode Error", Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
decode_result[i] = static_cast<char>('0' + bestMatch % 10);
|
||||||
|
start = counter.sum + start;
|
||||||
|
first_char_bit += (bestMatch >= 10) << i;
|
||||||
|
}
|
||||||
|
decode_result[0] = static_cast<char>(FIRST_CHAR_ARRAY()[first_char_bit >> 2] + '0');
|
||||||
|
// why there need >> 2?
|
||||||
|
// first, the i in for-cycle is begin in 1
|
||||||
|
// second, the first i = 1 is always
|
||||||
|
Counter middle_counter(vector<int>(MIDDLE_PATTERN().size()));
|
||||||
|
if (!findGuardPatterns(data, start, true, MIDDLE_PATTERN(), middle_counter, pattern))
|
||||||
|
{
|
||||||
|
return Result("Middle Pattern Not Found", Result::BARCODE_NONE);
|
||||||
|
|
||||||
|
}
|
||||||
|
start = pattern.second;
|
||||||
|
for (int i = 0; i < 6 && start < end; ++i)
|
||||||
|
{
|
||||||
|
int bestMatch = decodeDigit(data, counter, start, get_A_or_C_Patterns());
|
||||||
|
if (bestMatch == -1)
|
||||||
|
{
|
||||||
|
return Result("Decode Error", Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
decode_result[i + 7] = static_cast<char>('0' + bestMatch);
|
||||||
|
start = counter.sum + start;
|
||||||
|
}
|
||||||
|
Counter end_counter(vector<int>(BEGIN_PATTERN().size()));
|
||||||
|
if (!findGuardPatterns(data, start, false, BEGIN_PATTERN(), end_counter, pattern))
|
||||||
|
{
|
||||||
|
return Result("End Pattern Not Found", Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
result = string(decode_result);
|
||||||
|
if (!isValid(result))
|
||||||
|
{
|
||||||
|
return Result("Wrong: " + result.append(string(EAN13DIGIT_NUM - result.size(), ' ')), Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
return Result(result, Result::BARCODE_EAN_13);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ean13Decoder::Ean13Decoder()
|
||||||
|
{
|
||||||
|
this->bits_num = EAN13BITS_NUM;
|
||||||
|
this->digit_number = EAN13DIGIT_NUM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
modules/objdetect/src/barcode_decoder/ean13_decoder.hpp
Normal file
31
modules/objdetect/src/barcode_decoder/ean13_decoder.hpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds
|
||||||
|
|
||||||
|
#ifndef OPENCV_BARCODE_EAN13_DECODER_HPP
|
||||||
|
#define OPENCV_BARCODE_EAN13_DECODER_HPP
|
||||||
|
|
||||||
|
#include "upcean_decoder.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
//extern struct EncodePair;
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using std::pair;
|
||||||
|
|
||||||
|
|
||||||
|
class Ean13Decoder : public UPCEANDecoder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Ean13Decoder();
|
||||||
|
|
||||||
|
~Ean13Decoder() override = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Result decode(const vector<uchar> &data) const override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} // namespace cv
|
||||||
|
#endif // OPENCV_BARCODE_EAN13_DECODER_HPP
|
79
modules/objdetect/src/barcode_decoder/ean8_decoder.cpp
Normal file
79
modules/objdetect/src/barcode_decoder/ean8_decoder.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds
|
||||||
|
|
||||||
|
#include "../precomp.hpp"
|
||||||
|
#include "ean8_decoder.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
static constexpr size_t EAN8BITS_NUM = 70;
|
||||||
|
static constexpr size_t EAN8DIGIT_NUM = 8;
|
||||||
|
|
||||||
|
Result Ean8Decoder::decode(const vector<uchar> &data) const
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
char decode_result[EAN8DIGIT_NUM + 1]{'\0'};
|
||||||
|
if (data.size() < EAN8BITS_NUM)
|
||||||
|
{
|
||||||
|
return Result("Wrong Size", Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
pair<uint, uint> pattern;
|
||||||
|
if (!findStartGuardPatterns(data, pattern))
|
||||||
|
{
|
||||||
|
return Result("Begin Pattern Not Found", Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
uint start = pattern.second;
|
||||||
|
Counter counter(vector<int>{0, 0, 0, 0});
|
||||||
|
size_t end = data.size();
|
||||||
|
for (int i = 0; i < 4 && start < end; ++i)
|
||||||
|
{
|
||||||
|
int bestMatch = decodeDigit(data, counter, start, get_A_or_C_Patterns());
|
||||||
|
if (bestMatch == -1)
|
||||||
|
{
|
||||||
|
return Result("Decode Error", Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
decode_result[i] = static_cast<char>('0' + bestMatch % 10);
|
||||||
|
start = counter.sum + start;
|
||||||
|
}
|
||||||
|
|
||||||
|
Counter middle_counter(vector<int>(MIDDLE_PATTERN().size()));
|
||||||
|
|
||||||
|
if (!findGuardPatterns(data, start, true, MIDDLE_PATTERN(), middle_counter, pattern))
|
||||||
|
{
|
||||||
|
return Result("Middle Pattern Not Found", Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
start = pattern.second;
|
||||||
|
for (int i = 0; i < 4 && start < end; ++i)
|
||||||
|
{
|
||||||
|
int bestMatch = decodeDigit(data, counter, start, get_A_or_C_Patterns());
|
||||||
|
if (bestMatch == -1)
|
||||||
|
{
|
||||||
|
return Result("Decode Error", Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
decode_result[i + 4] = static_cast<char>('0' + bestMatch);
|
||||||
|
start = counter.sum + start;
|
||||||
|
}
|
||||||
|
Counter end_counter(vector<int>(BEGIN_PATTERN().size()));
|
||||||
|
if (!findGuardPatterns(data, start, false, BEGIN_PATTERN(), end_counter, pattern))
|
||||||
|
{
|
||||||
|
return Result("End Pattern Not Found", Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
result = string(decode_result);
|
||||||
|
if (!isValid(result))
|
||||||
|
{
|
||||||
|
return Result("Wrong: " + result.append(string(EAN8DIGIT_NUM - result.size(), ' ')), Result::BARCODE_NONE);
|
||||||
|
}
|
||||||
|
return Result(result, Result::BARCODE_EAN_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ean8Decoder::Ean8Decoder()
|
||||||
|
{
|
||||||
|
this->digit_number = EAN8DIGIT_NUM;
|
||||||
|
this->bits_num = EAN8BITS_NUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
32
modules/objdetect/src/barcode_decoder/ean8_decoder.hpp
Normal file
32
modules/objdetect/src/barcode_decoder/ean8_decoder.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html.
|
||||||
|
// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds
|
||||||
|
|
||||||
|
#ifndef OPENCV_BARCODE_EAN8_DECODER_HPP
|
||||||
|
#define OPENCV_BARCODE_EAN8_DECODER_HPP
|
||||||
|
|
||||||
|
#include "upcean_decoder.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
namespace barcode {
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using std::pair;
|
||||||
|
|
||||||
|
class Ean8Decoder : public UPCEANDecoder
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
Ean8Decoder();
|
||||||
|
|
||||||
|
~Ean8Decoder() override = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Result decode(const vector<uchar> &data) const override;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // OPENCV_BARCODE_EAN8_DECODER_HPP
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user