mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 07:13:02 +04:00
Merge branch 4.x
This commit is contained in:
Vendored
+7
-7
@@ -1,23 +1,23 @@
|
||||
function(download_fastcv root_dir)
|
||||
|
||||
# Commit SHA in the opencv_3rdparty repo
|
||||
set(FASTCV_COMMIT "2265e79b3b9a8512a9c615b8c4d0244e88f45a9d")
|
||||
set(FASTCV_COMMIT "9e8d42b6d7e769548d70b2e5674e263b056de8b4")
|
||||
|
||||
# Define actual FastCV versions
|
||||
if(ANDROID)
|
||||
if(AARCH64)
|
||||
message(STATUS "Download FastCV for Android aarch64")
|
||||
set(FCV_PACKAGE_NAME "fastcv_android_aarch64_2025_04_29.tgz")
|
||||
set(FCV_PACKAGE_HASH "d9172a9a3e5d92d080a4192cc5691001")
|
||||
set(FCV_PACKAGE_NAME "fastcv_android_aarch64_2025_07_09.tgz")
|
||||
set(FCV_PACKAGE_HASH "8b9497858cf3c3502a0be4369d06ebf8")
|
||||
else()
|
||||
message(STATUS "Download FastCV for Android armv7")
|
||||
set(FCV_PACKAGE_NAME "fastcv_android_arm32_2025_04_29.tgz")
|
||||
set(FCV_PACKAGE_HASH "246b5253233391cd2c74d01d49aee9c3")
|
||||
set(FCV_PACKAGE_NAME "fastcv_android_arm32_2025_07_09.tgz")
|
||||
set(FCV_PACKAGE_HASH "e0e6009c9f2f2b96140cd6a639c7383f")
|
||||
endif()
|
||||
elseif(UNIX AND NOT APPLE AND NOT IOS AND NOT XROS)
|
||||
if(AARCH64)
|
||||
set(FCV_PACKAGE_NAME "fastcv_linux_aarch64_2025_05_29.tgz")
|
||||
set(FCV_PACKAGE_HASH "decd490524f786e103125b8b948151f3")
|
||||
set(FCV_PACKAGE_NAME "fastcv_linux_aarch64_2025_07_09.tgz")
|
||||
set(FCV_PACKAGE_HASH "05e254e0eb3c13fa23eb7213f0fe6d82")
|
||||
else()
|
||||
message("FastCV: fastcv lib for 32-bit Linux is not supported for now!")
|
||||
endif()
|
||||
|
||||
Vendored
+5
-5
@@ -2,7 +2,7 @@ function(download_ippicv root_var)
|
||||
set(${root_var} "" PARENT_SCOPE)
|
||||
|
||||
# Commit SHA in the opencv_3rdparty repo
|
||||
set(IPPICV_COMMIT "d1cbea44d326eb0421fedcdd16de4630fd8c7ed0")
|
||||
set(IPPICV_COMMIT "767426b2a40a011eb2fa7f44c677c13e60e205ad")
|
||||
# Define actual ICV versions
|
||||
if(APPLE)
|
||||
set(IPPICV_COMMIT "0cc4aa06bf2bef4b05d237c69a5a96b9cd0cb85a")
|
||||
@@ -14,8 +14,8 @@ function(download_ippicv root_var)
|
||||
set(OPENCV_ICV_PLATFORM "linux")
|
||||
set(OPENCV_ICV_PACKAGE_SUBDIR "ippicv_lnx")
|
||||
if(X86_64)
|
||||
set(OPENCV_ICV_NAME "ippicv_2022.0.0_lnx_intel64_20240904_general.tgz")
|
||||
set(OPENCV_ICV_HASH "63717ee0f918ad72fb5a737992a206d1")
|
||||
set(OPENCV_ICV_NAME "ippicv_2022.1.0_lnx_intel64_20250130_general.tgz")
|
||||
set(OPENCV_ICV_HASH "98ff71fc242d52db9cc538388e502f57")
|
||||
else()
|
||||
if(ANDROID)
|
||||
set(IPPICV_COMMIT "c7c6d527dde5fee7cb914ee9e4e20f7436aab3a1")
|
||||
@@ -31,8 +31,8 @@ function(download_ippicv root_var)
|
||||
set(OPENCV_ICV_PLATFORM "windows")
|
||||
set(OPENCV_ICV_PACKAGE_SUBDIR "ippicv_win")
|
||||
if(X86_64)
|
||||
set(OPENCV_ICV_NAME "ippicv_2022.0.0_win_intel64_20240904_general.zip")
|
||||
set(OPENCV_ICV_HASH "3a6eca7cc3bce7159eb1443c6fca4e31")
|
||||
set(OPENCV_ICV_NAME "ippicv_2022.1.0_win_intel64_20250130_general.zip")
|
||||
set(OPENCV_ICV_HASH "67a611ab22410f392239bddff6f91df7")
|
||||
else()
|
||||
set(IPPICV_COMMIT "7f55c0c26be418d494615afca15218566775c725")
|
||||
set(OPENCV_ICV_NAME "ippicv_2021.12.0_win_ia32_20240425_general.zip")
|
||||
|
||||
@@ -92,6 +92,7 @@ namespace calib
|
||||
std::string videoFileName;
|
||||
bool flipVertical;
|
||||
int camID;
|
||||
int camBackend;
|
||||
int fps;
|
||||
cv::Size cameraResolution;
|
||||
int maxFramesNum;
|
||||
|
||||
@@ -34,7 +34,7 @@ PipelineExitStatus CalibPipeline::start(std::vector<cv::Ptr<FrameProcessor> > pr
|
||||
auto open_camera = [this] () {
|
||||
if(mCaptureParams.source == Camera)
|
||||
{
|
||||
mCapture.open(mCaptureParams.camID);
|
||||
mCapture.open(mCaptureParams.camID, mCaptureParams.camBackend);
|
||||
cv::Size maxRes = getCameraResolution();
|
||||
cv::Size neededRes = mCaptureParams.cameraResolution;
|
||||
|
||||
@@ -55,7 +55,7 @@ PipelineExitStatus CalibPipeline::start(std::vector<cv::Ptr<FrameProcessor> > pr
|
||||
mCapture.set(cv::CAP_PROP_AUTOFOCUS, 0);
|
||||
}
|
||||
else if (mCaptureParams.source == File)
|
||||
mCapture.open(mCaptureParams.videoFileName);
|
||||
mCapture.open(mCaptureParams.videoFileName, mCaptureParams.camBackend);
|
||||
};
|
||||
|
||||
if(!mCapture.isOpened()) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <opencv2/calib.hpp>
|
||||
#include <opencv2/cvconfig.h>
|
||||
#include <opencv2/highgui.hpp>
|
||||
|
||||
#include <opencv2/videoio/registry.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -24,9 +24,25 @@
|
||||
|
||||
using namespace calib;
|
||||
|
||||
const std::string keys =
|
||||
static std::string getVideoIoBackendsString()
|
||||
{
|
||||
std::string result;
|
||||
auto backs = cv::videoio_registry::getBackends();
|
||||
for (const auto& b: backs)
|
||||
{
|
||||
if (!result.empty())
|
||||
result += ", ";
|
||||
|
||||
result += cv::videoio_registry::getBackendName(b);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const char* keys =
|
||||
"{v | | Input from video file }"
|
||||
"{ci | 0 | Default camera id }"
|
||||
"{ci | 0 | Camera id }"
|
||||
"{vb | | Video I/O back-end. One of: %s }"
|
||||
"{flip | false | Vertical flip of input frames }"
|
||||
"{t | circles | Template for calibration (circles, chessboard, dualCircles, charuco, symcircles) }"
|
||||
"{sz | 16.3 | Distance between two nearest centers of circles or squares on calibration board}"
|
||||
@@ -96,11 +112,13 @@ static void undistortButton(int state, void* data)
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, keys);
|
||||
cv::CommandLineParser parser(argc, argv, cv::format(keys, getVideoIoBackendsString().c_str()));
|
||||
|
||||
if(parser.has("help")) {
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << consoleHelp << std::endl;
|
||||
parametersController paramsController;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "parametersController.hpp"
|
||||
#include <opencv2/objdetect/aruco_dictionary.hpp>
|
||||
|
||||
#include <opencv2/videoio/registry.hpp>
|
||||
#include <iostream>
|
||||
|
||||
template <typename T>
|
||||
@@ -106,6 +106,27 @@ bool calib::parametersController::loadFromParser(cv::CommandLineParser &parser)
|
||||
mCapParams.camID = parser.get<int>("ci");
|
||||
}
|
||||
|
||||
mCapParams.camBackend = cv::CAP_ANY;
|
||||
if (parser.has("vb"))
|
||||
{
|
||||
std::string backendName = parser.get<std::string>("vb");
|
||||
auto backs = cv::videoio_registry::getBackends();
|
||||
bool backendSet = false;
|
||||
for (const auto& b: backs)
|
||||
{
|
||||
if (backendName == cv::videoio_registry::getBackendName(b))
|
||||
{
|
||||
mCapParams.camBackend = b;
|
||||
backendSet = true;
|
||||
}
|
||||
}
|
||||
if (!backendSet)
|
||||
{
|
||||
std::cout << "Unknown or unsupported backend " << backendName << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string templateType = parser.get<std::string>("t");
|
||||
|
||||
if(templateType.find("symcircles", 0) == 0) {
|
||||
|
||||
@@ -81,7 +81,13 @@ if(WITH_EIGEN AND NOT HAVE_EIGEN)
|
||||
set(EIGEN_WORLD_VERSION ${EIGEN3_WORLD_VERSION})
|
||||
set(EIGEN_MAJOR_VERSION ${EIGEN3_MAJOR_VERSION})
|
||||
set(EIGEN_MINOR_VERSION ${EIGEN3_MINOR_VERSION})
|
||||
else() # Eigen config file
|
||||
elseif(DEFINED Eigen3_VERSION_MAJOR) # Recommended package config variables
|
||||
# see https://github.com/opencv/opencv/issues/27530
|
||||
set(EIGEN_WORLD_VERSION ${Eigen3_VERSION_MAJOR})
|
||||
set(EIGEN_MAJOR_VERSION ${Eigen3_VERSION_MINOR})
|
||||
set(EIGEN_MINOR_VERSION ${Eigen3_VERSION_PATCH})
|
||||
else() # Deprecated package config variables
|
||||
# Removed on master at https://gitlab.com/libeigen/eigen/-/commit/f2984cd0778dd0a1d7e74216d826eaff2bc6bfab
|
||||
set(EIGEN_WORLD_VERSION ${EIGEN3_VERSION_MAJOR})
|
||||
set(EIGEN_MAJOR_VERSION ${EIGEN3_VERSION_MINOR})
|
||||
set(EIGEN_MINOR_VERSION ${EIGEN3_VERSION_PATCH})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64))
|
||||
#if defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC))
|
||||
# define _ARM64_DISTINCT_NEON_TYPES
|
||||
# include <Intrin.h>
|
||||
# include <arm_neon.h>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && defined _M_ARM64)
|
||||
#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && (defined _M_ARM64 || defined _M_ARM64EC))
|
||||
#include <stdio.h>
|
||||
#include "arm_neon.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && defined _M_ARM64)
|
||||
#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && (defined _M_ARM64 || defined _M_ARM64EC))
|
||||
#include "arm_neon.h"
|
||||
int test()
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && defined _M_ARM64)
|
||||
#if (defined __GNUC__ && (defined __arm__ || defined __aarch64__)) || (defined _MSC_VER && (defined _M_ARM64 || defined _M_ARM64EC))
|
||||
#include "arm_neon.h"
|
||||
|
||||
float16x8_t vld1q_as_f16(const float* src)
|
||||
|
||||
@@ -158,7 +158,7 @@ Structuring Element
|
||||
-------------------
|
||||
|
||||
We manually created a structuring elements in the previous examples with help of cv.Mat.ones. It is
|
||||
rectangular shape. But in some cases, you may need elliptical/circular shaped kernels. So for this
|
||||
rectangular shape. But in some cases, you may need elliptical/circular shaped kernels or diamond-shaped kernels. So for this
|
||||
purpose, OpenCV has a function, **cv.getStructuringElement()**. You just pass the shape and size of
|
||||
the kernel, you get the desired kernel.
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ There are some parameters when you get familiar with StereoBM, and you may need
|
||||
- uniqueness_ratio: Another post-filtering step. If the best matching disparity is not sufficiently better than every other disparity in the search range, the pixel is filtered out. You can try tweaking this if texture_threshold and the speckle filtering are still letting through spurious matches.
|
||||
- prefilter_size and prefilter_cap: The pre-filtering phase, which normalizes image brightness and enhances texture in preparation for block matching. Normally you should not need to adjust these.
|
||||
|
||||
These parameters are set with dedicated setters and getters after the algoritm
|
||||
These parameters are set with dedicated setters and getters after the algorithm
|
||||
initialization, such as `setTextureThreshold`, `setSpeckleRange`, `setUniquenessRatio`,
|
||||
and more. See cv::StereoBM documentation for details.
|
||||
|
||||
|
||||
@@ -147,6 +147,14 @@ array([[0, 0, 1, 0, 0],
|
||||
[1, 1, 1, 1, 1],
|
||||
[0, 0, 1, 0, 0],
|
||||
[0, 0, 1, 0, 0]], dtype=uint8)
|
||||
|
||||
# Diamond-shaped Kernel
|
||||
>>> cv.getStructuringElement(cv.MORPH_DIAMOND,(5,5))
|
||||
array([[0, 0, 1, 0, 0],
|
||||
[0, 1, 1, 1, 0],
|
||||
[1, 1, 1, 1, 1],
|
||||
[0, 1, 1, 1, 0],
|
||||
[0, 0, 1, 0, 0]], dtype=uint8)
|
||||
@endcode
|
||||
Additional Resources
|
||||
--------------------
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 75 KiB |
@@ -10,7 +10,7 @@ Goal
|
||||
----
|
||||
|
||||
Thrust is an extremely powerful library for various cuda accelerated algorithms. However thrust is designed
|
||||
to work with vectors and not pitched matricies. The following tutorial will discuss wrapping cv::cuda::GpuMat's
|
||||
to work with vectors and not pitched matrices. The following tutorial will discuss wrapping cv::cuda::GpuMat's
|
||||
into thrust iterators that can be used with thrust algorithms.
|
||||
|
||||
This tutorial should show you how to:
|
||||
|
||||
@@ -129,6 +129,7 @@ receives three arguments:
|
||||
- Rectangular box: MORPH_RECT
|
||||
- Cross: MORPH_CROSS
|
||||
- Ellipse: MORPH_ELLIPSE
|
||||
- Diamond: MORPH_DIAMOND
|
||||
|
||||
Then, we just have to specify the size of our kernel and the *anchor point*. If not
|
||||
specified, it is assumed to be in the center.
|
||||
@@ -256,6 +257,7 @@ receives two arguments and returns the processed image:
|
||||
- Rectangular box: MORPH_RECT
|
||||
- Cross: MORPH_CROSS
|
||||
- Ellipse: MORPH_ELLIPSE
|
||||
- Diamond: MORPH_DIAMOND
|
||||
|
||||
Then, we just have to specify the size of our kernel and the *anchor point*. If the anchor point not
|
||||
specified, it is assumed to be in the center.
|
||||
|
||||
+22
-14
@@ -25,7 +25,7 @@ Theory
|
||||
|
||||
- To compare two histograms ( \f$H_{1}\f$ and \f$H_{2}\f$ ), first we have to choose a *metric*
|
||||
(\f$d(H_{1}, H_{2})\f$) to express how well both histograms match.
|
||||
- OpenCV implements the function @ref cv::compareHist to perform a comparison. It also offers 4
|
||||
- OpenCV implements the function @ref cv::compareHist to perform a comparison. It also offers 6
|
||||
different metrics to compute the matching:
|
||||
-# **Correlation ( cv::HISTCMP_CORREL )**
|
||||
\f[d(H_1,H_2) = \frac{\sum_I (H_1(I) - \bar{H_1}) (H_2(I) - \bar{H_2})}{\sqrt{\sum_I(H_1(I) - \bar{H_1})^2 \sum_I(H_2(I) - \bar{H_2})^2}}\f]
|
||||
@@ -36,12 +36,18 @@ Theory
|
||||
-# **Chi-Square ( cv::HISTCMP_CHISQR )**
|
||||
\f[d(H_1,H_2) = \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)}\f]
|
||||
|
||||
-# **Intersection ( method=cv::HISTCMP_INTERSECT )**
|
||||
-# **Intersection ( cv::HISTCMP_INTERSECT )**
|
||||
\f[d(H_1,H_2) = \sum _I \min (H_1(I), H_2(I))\f]
|
||||
|
||||
-# **Bhattacharyya distance ( cv::HISTCMP_BHATTACHARYYA )**
|
||||
\f[d(H_1,H_2) = \sqrt{1 - \frac{1}{\sqrt{\bar{H_1} \bar{H_2} N^2}} \sum_I \sqrt{H_1(I) \cdot H_2(I)}}\f]
|
||||
|
||||
-# **Alternative Chi-Square ( cv::HISTCMP_CHISQR_ALT )**
|
||||
\f[d(H_1,H_2) = 2 * \sum _I \frac{\left(H_1(I)-H_2(I)\right)^2}{H_1(I)+H_2(I)}\f]
|
||||
|
||||
-# **Kullback-Leibler divergence ( cv::HISTCMP_KL_DIV )**
|
||||
\f[d(H_1,H_2) = \sum _I H_1(I) \log \left(\frac{H_1(I)}{H_2(I)}\right)\f]
|
||||
|
||||
Code
|
||||
----
|
||||
|
||||
@@ -123,7 +129,7 @@ Explanation
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_comparison/compareHist_Demo.py Convert to HSV half
|
||||
@end_toggle
|
||||
|
||||
- Initialize the arguments to calculate the histograms (bins, ranges and channels H and S ).
|
||||
- Initialize the arguments to calculate the histograms (bins, ranges and channels H and S).
|
||||
|
||||
@add_toggle_cpp
|
||||
@snippet samples/cpp/tutorial_code/Histograms_Matching/compareHist_Demo.cpp Using 50 bins for hue and 60 for saturation
|
||||
@@ -151,7 +157,7 @@ Explanation
|
||||
@snippet samples/python/tutorial_code/Histograms_Matching/histogram_comparison/compareHist_Demo.py Calculate the histograms for the HSV images
|
||||
@end_toggle
|
||||
|
||||
- Apply sequentially the 4 comparison methods between the histogram of the base image (hist_base)
|
||||
- Apply sequentially the 6 comparison methods between the histogram of the base image (hist_base)
|
||||
and the other histograms:
|
||||
|
||||
@add_toggle_cpp
|
||||
@@ -182,15 +188,17 @@ Results
|
||||
are from the same source. For the other two test images, we can observe that they have very
|
||||
different lighting conditions, so the matching should not be very good:
|
||||
|
||||
-# Here the numeric results we got with OpenCV 3.4.1:
|
||||
*Method* | Base - Base | Base - Half | Base - Test 1 | Base - Test 2
|
||||
----------------- | ------------ | ------------ | -------------- | ---------------
|
||||
*Correlation* | 1.000000 | 0.880438 | 0.20457 | 0.0664547
|
||||
*Chi-square* | 0.000000 | 4.6834 | 2697.98 | 4763.8
|
||||
*Intersection* | 18.8947 | 13.022 | 5.44085 | 2.58173
|
||||
*Bhattacharyya* | 0.000000 | 0.237887 | 0.679826 | 0.874173
|
||||
-# Here the numeric results we got with OpenCV 4.12.0:
|
||||
*Method* | Base - Base | Base - Half | Base - Test 1 | Base - Test 2
|
||||
------------------- | ------------ | ------------ | -------------- | ---------------
|
||||
*Correlation* | 1.000000 | 0.880438 | 0.20457 | 0.065752
|
||||
*Chi-square* | 0.000000 | 0.328307 | 181.674 | 80.1494
|
||||
*Intersection* | 1.000000 | 0.75005 | 0.315061 | 0.0908022
|
||||
*Bhattacharyya* | 0.000000 | 0.237866 | 0.679825 | 0.873709
|
||||
*Chi-Square alt.* | 0.000000 | 0.395046 | 2.31572 | 3.41024
|
||||
*KL divergence* | 0.000000 | 0.321064 | 2.6616 | 9.55412
|
||||
|
||||
For the *Correlation* and *Intersection* methods, the higher the metric, the more accurate the
|
||||
match. As we can see, the match *base-base* is the highest of all as expected. Also we can observe
|
||||
that the match *base-half* is the second best match (as we predicted). For the other two metrics,
|
||||
the less the result, the better the match. We can observe that the matches between the test 1 and
|
||||
test 2 with respect to the base are worse, which again, was expected.
|
||||
that the match *base-half* is the second best match (as we predicted). For the other four metrics,
|
||||
the less the result, the better the match.
|
||||
|
||||
@@ -166,7 +166,7 @@ for `cv::aruco::Dictionary`. The data member of board classes are public and can
|
||||
- Alright, but how can I render a 3d model to create an augmented reality application?
|
||||
|
||||
To do so, you will need to use an external rendering engine library, such as OpenGL. The aruco module
|
||||
only provides the functionality to obtain the camera pose, i.e. the rotation and traslation vectors,
|
||||
only provides the functionality to obtain the camera pose, i.e. the rotation and translation vectors,
|
||||
which is necessary to create the augmented reality effect. However, you will need to adapt the rotation
|
||||
and traslation vectors from the OpenCV format to the format accepted by your 3d rendering library.
|
||||
The original ArUco library contains examples of how to do it for OpenGL and Ogre3D.
|
||||
|
||||
@@ -5,6 +5,7 @@ set(IPP_HAL_LIBRARIES "ipphal" CACHE INTERNAL "")
|
||||
set(IPP_HAL_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include" CACHE INTERNAL "")
|
||||
set(IPP_HAL_HEADERS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/ipp_hal_core.hpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include/ipp_hal_imgproc.hpp"
|
||||
CACHE INTERNAL "")
|
||||
|
||||
add_library(ipphal STATIC
|
||||
@@ -13,6 +14,7 @@ add_library(ipphal STATIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/norm_ipp.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/cart_polar_ipp.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/transforms_ipp.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/warp_ipp.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src/sum_ipp.cpp"
|
||||
)
|
||||
|
||||
@@ -34,6 +36,7 @@ ocv_warnings_disable(CMAKE_CXX_FLAGS -Wno-suggest-override)
|
||||
target_include_directories(ipphal PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/src"
|
||||
${CMAKE_SOURCE_DIR}/modules/core/include
|
||||
${CMAKE_SOURCE_DIR}/modules/imgproc/include
|
||||
${IPP_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
|
||||
@@ -21,7 +21,17 @@ int ipp_hal_minMaxIdxMaskStep(const uchar* src_data, size_t src_step, int width,
|
||||
#undef cv_hal_minMaxIdxMaskStep
|
||||
#define cv_hal_minMaxIdxMaskStep ipp_hal_minMaxIdxMaskStep
|
||||
|
||||
#define IPP_DISABLE_NORM_8U 1 // accuracy difference in perf test sanity check
|
||||
#if (IPP_VERSION_X100 == 202200)
|
||||
# define IPP_DISABLE_NORM_8U 1 // accuracy difference in perf test sanity check
|
||||
# else
|
||||
# define IPP_DISABLE_NORM_8U 0
|
||||
#endif
|
||||
|
||||
#if (IPP_VERSION_X100 >= 202200 && IPP_VERSION_X100 < 202220)
|
||||
# define IPP_DISABLE_NORM_INF_16U_C1MR 1 // segmentation fault in accuracy test
|
||||
# else
|
||||
# define IPP_DISABLE_NORM_INF_16U_C1MR 0
|
||||
#endif
|
||||
|
||||
int ipp_hal_norm(const uchar* src, size_t src_step, const uchar* mask, size_t mask_step,
|
||||
int width, int height, int type, int norm_type, double* result);
|
||||
@@ -29,7 +39,6 @@ int ipp_hal_norm(const uchar* src, size_t src_step, const uchar* mask, size_t ma
|
||||
#undef cv_hal_norm
|
||||
#define cv_hal_norm ipp_hal_norm
|
||||
|
||||
|
||||
int ipp_hal_normDiff(const uchar* src1, size_t src1_step, const uchar* src2, size_t src2_step, const uchar* mask,
|
||||
size_t mask_step, int width, int height, int type, int norm_type, double* result);
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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 __IPP_HAL_IMGPROC_HPP__
|
||||
#define __IPP_HAL_IMGPROC_HPP__
|
||||
|
||||
#include <opencv2/core/base.hpp>
|
||||
#include "ipp_utils.hpp"
|
||||
|
||||
#ifdef HAVE_IPP_IW
|
||||
|
||||
int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width,
|
||||
int dst_height, const double M[6], int interpolation, int borderType, const double borderValue[4]);
|
||||
|
||||
// Does not pass tests in 5.x branch
|
||||
//#undef cv_hal_warpAffine
|
||||
//#define cv_hal_warpAffine ipp_hal_warpAffine
|
||||
#endif
|
||||
|
||||
#if IPP_VERSION_X100 >= 810
|
||||
int ipp_hal_warpPerspective(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width,
|
||||
int dst_height, const double M[9], int interpolation, int borderType, const double borderValue[4]);
|
||||
|
||||
// Does not pass tests in 5.x branch
|
||||
//#undef cv_hal_warpPerspective
|
||||
//#define cv_hal_warpPerspective ipp_hal_warpPerspective
|
||||
#endif
|
||||
|
||||
|
||||
int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height,
|
||||
uchar *dst_data, size_t dst_step, int dst_width, int dst_height,
|
||||
float* mapx, size_t mapx_step, float* mapy, size_t mapy_step,
|
||||
int interpolation, int border_type, const double border_value[4]);
|
||||
#undef cv_hal_remap32f
|
||||
#define cv_hal_remap32f ipp_hal_remap32f
|
||||
|
||||
|
||||
#endif //__IPP_HAL_IMGPROC_HPP__
|
||||
@@ -20,7 +20,9 @@ int ipp_hal_norm(const uchar* src, size_t src_step, const uchar* mask, size_t ma
|
||||
ippiMaskNormFuncC1 ippiNorm_C1MR =
|
||||
norm_type == cv::NORM_INF ?
|
||||
(type == CV_8UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_8u_C1MR :
|
||||
#if (!IPP_DISABLE_NORM_INF_16U_C1MR)
|
||||
type == CV_16UC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_16u_C1MR :
|
||||
#endif
|
||||
type == CV_32FC1 ? (ippiMaskNormFuncC1)ippiNorm_Inf_32f_C1MR :
|
||||
0) :
|
||||
norm_type == cv::NORM_L1 ?
|
||||
@@ -141,7 +143,9 @@ int ipp_hal_normDiff(const uchar* src1, size_t src1_step, const uchar* src2, siz
|
||||
ippiMaskNormDiffFuncC1 ippiNormRel_C1MR =
|
||||
norm_type == cv::NORM_INF ?
|
||||
(type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_Inf_8u_C1MR :
|
||||
#if (!IPP_DISABLE_NORM_INF_16U_C1MR)
|
||||
type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_Inf_16u_C1MR :
|
||||
#endif
|
||||
type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormRel_Inf_32f_C1MR :
|
||||
0) :
|
||||
norm_type == cv::NORM_L1 ?
|
||||
@@ -230,7 +234,9 @@ int ipp_hal_normDiff(const uchar* src1, size_t src1_step, const uchar* src2, siz
|
||||
ippiMaskNormDiffFuncC1 ippiNormDiff_C1MR =
|
||||
norm_type == cv::NORM_INF ?
|
||||
(type == CV_8UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_8u_C1MR :
|
||||
#if (!IPP_DISABLE_NORM_INF_16U_C1MR)
|
||||
type == CV_16UC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_16u_C1MR :
|
||||
#endif
|
||||
type == CV_32FC1 ? (ippiMaskNormDiffFuncC1)ippiNormDiff_Inf_32f_C1MR :
|
||||
0) :
|
||||
norm_type == cv::NORM_L1 ?
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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 __PRECOMP_IPP_HPP__
|
||||
#define __PRECOMP_IPP_HPP__
|
||||
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
#ifdef HAVE_IPP_IW
|
||||
#include "iw++/iw.hpp"
|
||||
#endif
|
||||
|
||||
static inline IppiSize ippiSize(size_t width, size_t height)
|
||||
{
|
||||
IppiSize size = { (int)width, (int)height };
|
||||
return size;
|
||||
}
|
||||
|
||||
static inline IppiSize ippiSize(const cv::Size & _size)
|
||||
{
|
||||
IppiSize size = { _size.width, _size.height };
|
||||
return size;
|
||||
}
|
||||
|
||||
static inline IppDataType ippiGetDataType(int depth)
|
||||
{
|
||||
depth = CV_MAT_DEPTH(depth);
|
||||
return depth == CV_8U ? ipp8u :
|
||||
depth == CV_8S ? ipp8s :
|
||||
depth == CV_16U ? ipp16u :
|
||||
depth == CV_16S ? ipp16s :
|
||||
depth == CV_32S ? ipp32s :
|
||||
depth == CV_32F ? ipp32f :
|
||||
depth == CV_64F ? ipp64f :
|
||||
(IppDataType)-1;
|
||||
}
|
||||
|
||||
static inline IppiInterpolationType ippiGetInterpolation(int inter)
|
||||
{
|
||||
inter &= cv::InterpolationFlags::INTER_MAX;
|
||||
return inter == cv::InterpolationFlags::INTER_NEAREST ? ippNearest :
|
||||
inter == cv::InterpolationFlags::INTER_LINEAR ? ippLinear :
|
||||
inter == cv::InterpolationFlags::INTER_CUBIC ? ippCubic :
|
||||
inter == cv::InterpolationFlags::INTER_LANCZOS4 ? ippLanczos :
|
||||
inter == cv::InterpolationFlags::INTER_AREA ? ippSuper :
|
||||
(IppiInterpolationType)-1;
|
||||
}
|
||||
|
||||
static inline IppiBorderType ippiGetBorderType(int borderTypeNI)
|
||||
{
|
||||
return borderTypeNI == cv::BorderTypes::BORDER_CONSTANT ? ippBorderConst :
|
||||
borderTypeNI == cv::BorderTypes::BORDER_TRANSPARENT ? ippBorderTransp :
|
||||
borderTypeNI == cv::BorderTypes::BORDER_REPLICATE ? ippBorderRepl :
|
||||
(IppiBorderType)-1;
|
||||
}
|
||||
|
||||
static inline int ippiSuggestThreadsNum(size_t width, size_t height, size_t elemSize, double multiplier)
|
||||
{
|
||||
int threads = cv::getNumThreads();
|
||||
if(threads > 1 && height >= 64)
|
||||
{
|
||||
size_t opMemory = (int)(width*height*elemSize*multiplier);
|
||||
int l2cache = 0;
|
||||
#if IPP_VERSION_X100 >= 201700
|
||||
ippGetL2CacheSize(&l2cache);
|
||||
#endif
|
||||
if(!l2cache)
|
||||
l2cache = 1 << 18;
|
||||
|
||||
return IPP_MAX(1, (IPP_MIN((int)(opMemory/l2cache), threads)));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPP_IW
|
||||
static inline int ippiSuggestThreadsNum(const ::ipp::IwiImage &image, double multiplier)
|
||||
{
|
||||
return ippiSuggestThreadsNum(image.m_size.width, image.m_size.height, image.m_typeSize*image.m_channels, multiplier);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__PRECOMP_IPP_HPP__
|
||||
@@ -3,6 +3,7 @@
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "ipp_hal_core.hpp"
|
||||
#include "precomp_ipp.hpp"
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/core/base.hpp>
|
||||
@@ -72,19 +73,6 @@ int ipp_hal_transpose2d(const uchar* src_data, size_t src_step, uchar* dst_data,
|
||||
|
||||
#ifdef HAVE_IPP_IW
|
||||
|
||||
static inline IppDataType ippiGetDataType(int depth)
|
||||
{
|
||||
depth = CV_MAT_DEPTH(depth);
|
||||
return depth == CV_8U ? ipp8u :
|
||||
depth == CV_8S ? ipp8s :
|
||||
depth == CV_16U ? ipp16u :
|
||||
depth == CV_16S ? ipp16s :
|
||||
depth == CV_32S ? ipp32s :
|
||||
depth == CV_32F ? ipp32f :
|
||||
depth == CV_64F ? ipp64f :
|
||||
(IppDataType)-1;
|
||||
}
|
||||
|
||||
static inline ::ipp::IwiImage ippiGetImage(int src_type, const uchar* src_data, size_t src_step, int src_width, int src_height)
|
||||
{
|
||||
::ipp::IwiImage dst;
|
||||
|
||||
@@ -0,0 +1,488 @@
|
||||
// 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 "ipp_hal_imgproc.hpp"
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/core/base.hpp>
|
||||
#include "precomp_ipp.hpp"
|
||||
|
||||
#ifdef HAVE_IPP_IW
|
||||
#include "iw++/iw.hpp"
|
||||
#endif
|
||||
|
||||
#define IPP_WARPAFFINE_PARALLEL 1
|
||||
#define CV_TYPE(src_type) (src_type & (CV_DEPTH_MAX - 1))
|
||||
#ifdef HAVE_IPP_IW
|
||||
|
||||
class ipp_warpAffineParallel: public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
ipp_warpAffineParallel(::ipp::IwiImage &src, ::ipp::IwiImage &dst, IppiInterpolationType _inter, double (&_coeffs)[2][3], ::ipp::IwiBorderType _borderType, IwTransDirection _iwTransDirection, bool *_ok):m_src(src), m_dst(dst)
|
||||
{
|
||||
pOk = _ok;
|
||||
|
||||
inter = _inter;
|
||||
borderType = _borderType;
|
||||
iwTransDirection = _iwTransDirection;
|
||||
|
||||
for( int i = 0; i < 2; i++ )
|
||||
for( int j = 0; j < 3; j++ )
|
||||
coeffs[i][j] = _coeffs[i][j];
|
||||
|
||||
*pOk = true;
|
||||
}
|
||||
~ipp_warpAffineParallel() {}
|
||||
|
||||
virtual void operator() (const cv::Range& range) const CV_OVERRIDE
|
||||
{
|
||||
//CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
if(*pOk == false)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, m_dst.m_size.width, range.end - range.start);
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiWarpAffine, m_src, m_dst, coeffs, iwTransDirection, inter, ::ipp::IwiWarpAffineParams(), borderType, tile);
|
||||
}
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
*pOk = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
private:
|
||||
::ipp::IwiImage &m_src;
|
||||
::ipp::IwiImage &m_dst;
|
||||
|
||||
IppiInterpolationType inter;
|
||||
double coeffs[2][3];
|
||||
::ipp::IwiBorderType borderType;
|
||||
IwTransDirection iwTransDirection;
|
||||
|
||||
bool *pOk;
|
||||
const ipp_warpAffineParallel& operator= (const ipp_warpAffineParallel&);
|
||||
};
|
||||
|
||||
#if (IPP_VERSION_X100 >= 700)
|
||||
int ipp_hal_warpAffine(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar *dst_data, size_t dst_step, int dst_width,
|
||||
int dst_height, const double M[6], int interpolation, int borderType, const double borderValue[4])
|
||||
{
|
||||
//CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
|
||||
if((int)ippInter < 0 || interpolation > 2)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
/* C1 C2 C3 C4 */
|
||||
char impl[CV_DEPTH_MAX][4][3]={{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {1, 0, 0}}, //16U
|
||||
{{1, 0, 0}, {0, 0, 0}, {1, 0, 0}, {1, 0, 0}}, //16S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32F
|
||||
{{1, 0, 0}, {0, 0, 0}, {1, 0, 0}, {1, 0, 0}}}; //64F
|
||||
|
||||
if(impl[CV_TYPE(src_type)][CV_MAT_CN(src_type)-1][interpolation] == 0)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
// Acquire data and begin processing
|
||||
try
|
||||
{
|
||||
::ipp::IwiImage iwSrc;
|
||||
iwSrc.Init({src_width, src_height}, ippiGetDataType(src_type), CV_MAT_CN(src_type), NULL, src_data, IwSize(src_step));
|
||||
::ipp::IwiImage iwDst({dst_width, dst_height}, ippiGetDataType(src_type), CV_MAT_CN(src_type), NULL, dst_data, dst_step);
|
||||
::ipp::IwiBorderType ippBorder(ippiGetBorderType(borderType), {borderValue[0], borderValue[1], borderValue[2], borderValue[3]});
|
||||
IwTransDirection iwTransDirection = iwTransForward;
|
||||
|
||||
if((int)ippBorder == -1)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
double coeffs[2][3];
|
||||
for( int i = 0; i < 2; i++ )
|
||||
for( int j = 0; j < 3; j++ )
|
||||
coeffs[i][j] = M[i*3 + j];
|
||||
|
||||
const int threads = ippiSuggestThreadsNum(iwDst, 2);
|
||||
|
||||
if(IPP_WARPAFFINE_PARALLEL && threads > 1)
|
||||
{
|
||||
bool ok = true;
|
||||
cv::Range range(0, (int)iwDst.m_size.height);
|
||||
ipp_warpAffineParallel invoker(iwSrc, iwDst, ippInter, coeffs, ippBorder, iwTransDirection, &ok);
|
||||
if(!ok)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
parallel_for_(range, invoker, threads*4);
|
||||
|
||||
if(!ok)
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
} else {
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiWarpAffine, iwSrc, iwDst, coeffs, iwTransDirection, ippInter, ::ipp::IwiWarpAffineParams(), ippBorder);
|
||||
}
|
||||
}
|
||||
catch (const ::ipp::IwException &)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
return CV_HAL_ERROR_OK;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef IppStatus (CV_STDCALL* ippiSetFunc)(const void*, void *, int, IppiSize);
|
||||
|
||||
template <int channels, typename Type>
|
||||
bool IPPSetSimple(const double value[4], void *dataPointer, int step, IppiSize &size, ippiSetFunc func)
|
||||
{
|
||||
//CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
Type values[channels];
|
||||
for( int i = 0; i < channels; i++ )
|
||||
values[i] = cv::saturate_cast<Type>(value[i]);
|
||||
return CV_INSTRUMENT_FUN_IPP(func, values, dataPointer, step, size) >= 0;
|
||||
}
|
||||
|
||||
static bool IPPSet(const double value[4], void *dataPointer, int step, IppiSize &size, int channels, int depth)
|
||||
{
|
||||
//CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
if( channels == 1 )
|
||||
{
|
||||
switch( depth )
|
||||
{
|
||||
case CV_8U:
|
||||
return CV_INSTRUMENT_FUN_IPP(ippiSet_8u_C1R, cv::saturate_cast<Ipp8u>(value[0]), (Ipp8u *)dataPointer, step, size) >= 0;
|
||||
case CV_16U:
|
||||
return CV_INSTRUMENT_FUN_IPP(ippiSet_16u_C1R, cv::saturate_cast<Ipp16u>(value[0]), (Ipp16u *)dataPointer, step, size) >= 0;
|
||||
case CV_32F:
|
||||
return CV_INSTRUMENT_FUN_IPP(ippiSet_32f_C1R, cv::saturate_cast<Ipp32f>(value[0]), (Ipp32f *)dataPointer, step, size) >= 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( channels == 3 )
|
||||
{
|
||||
switch( depth )
|
||||
{
|
||||
case CV_8U:
|
||||
return IPPSetSimple<3, Ipp8u>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_8u_C3R);
|
||||
case CV_16U:
|
||||
return IPPSetSimple<3, Ipp16u>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_16u_C3R);
|
||||
case CV_32F:
|
||||
return IPPSetSimple<3, Ipp32f>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_32f_C3R);
|
||||
}
|
||||
}
|
||||
else if( channels == 4 )
|
||||
{
|
||||
switch( depth )
|
||||
{
|
||||
case CV_8U:
|
||||
return IPPSetSimple<4, Ipp8u>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_8u_C4R);
|
||||
case CV_16U:
|
||||
return IPPSetSimple<4, Ipp16u>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_16u_C4R);
|
||||
case CV_32F:
|
||||
return IPPSetSimple<4, Ipp32f>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_32f_C4R);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if (IPP_VERSION_X100 >= 810)
|
||||
typedef IppStatus (CV_STDCALL* ippiWarpPerspectiveFunc)(const Ipp8u*, int, Ipp8u*, int,IppiPoint, IppiSize, const IppiWarpSpec*,Ipp8u*);
|
||||
|
||||
class IPPWarpPerspectiveInvoker :
|
||||
public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
IPPWarpPerspectiveInvoker(int _src_type, cv::Mat &_src, size_t _src_step, cv::Mat &_dst, size_t _dst_step, IppiInterpolationType _interpolation,
|
||||
double (&_coeffs)[3][3], int &_borderType, const double _borderValue[4], ippiWarpPerspectiveFunc _func,
|
||||
bool *_ok) :
|
||||
ParallelLoopBody(), src_type(_src_type), src(_src), src_step(_src_step), dst(_dst), dst_step(_dst_step), inter(_interpolation), coeffs(_coeffs),
|
||||
borderType(_borderType), func(_func), ok(_ok)
|
||||
{
|
||||
memcpy(this->borderValue, _borderValue, sizeof(this->borderValue));
|
||||
*ok = true;
|
||||
}
|
||||
|
||||
virtual void operator() (const cv::Range& range) const CV_OVERRIDE
|
||||
{
|
||||
//CV_INSTRUMENT_REGION_IPP();
|
||||
IppiWarpSpec* pSpec = 0;
|
||||
int specSize = 0, initSize = 0, bufSize = 0; Ipp8u* pBuffer = 0;
|
||||
IppiPoint dstRoiOffset = {0, 0};
|
||||
IppiWarpDirection direction = ippWarpBackward; //fixed for IPP
|
||||
const Ipp32u numChannels = CV_MAT_CN(src_type);
|
||||
|
||||
IppiSize srcsize = {src.cols, src.rows};
|
||||
IppiSize dstsize = {dst.cols, dst.rows};
|
||||
IppiRect srcroi = {0, 0, src.cols, src.rows};
|
||||
|
||||
/* Spec and init buffer sizes */
|
||||
IppStatus status = ippiWarpPerspectiveGetSize(srcsize, srcroi, dstsize, ippiGetDataType(src_type), coeffs, inter, ippWarpBackward, ippiGetBorderType(borderType), &specSize, &initSize);
|
||||
|
||||
pSpec = (IppiWarpSpec*)ippMalloc_L(specSize);
|
||||
|
||||
if (inter == ippLinear)
|
||||
{
|
||||
status = ippiWarpPerspectiveLinearInit(srcsize, srcroi, dstsize, ippiGetDataType(src_type), coeffs, direction, numChannels, ippiGetBorderType(borderType),
|
||||
borderValue, 0, pSpec);
|
||||
} else
|
||||
{
|
||||
status = ippiWarpPerspectiveNearestInit(srcsize, srcroi, dstsize, ippiGetDataType(src_type), coeffs, direction, numChannels, ippiGetBorderType(borderType),
|
||||
borderValue, 0, pSpec);
|
||||
}
|
||||
|
||||
status = ippiWarpGetBufferSize(pSpec, dstsize, &bufSize);
|
||||
pBuffer = (Ipp8u*)ippMalloc_L(bufSize);
|
||||
IppiSize dstRoiSize = dstsize;
|
||||
|
||||
int cnn = src.channels();
|
||||
|
||||
if( borderType == cv::BorderTypes::BORDER_CONSTANT )
|
||||
{
|
||||
IppiSize setSize = {dst.cols, range.end - range.start};
|
||||
void *dataPointer = dst.ptr(range.start);
|
||||
if( !IPPSet( borderValue, dataPointer, (int)dst.step[0], setSize, cnn, src.depth() ) )
|
||||
{
|
||||
*ok = false;
|
||||
ippsFree(pBuffer);
|
||||
ippsFree(pSpec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
status = CV_INSTRUMENT_FUN_IPP(func, src.ptr(), (int)src_step, dst.ptr(), (int)dst_step, dstRoiOffset, dstRoiSize, pSpec, pBuffer);
|
||||
if (status != ippStsNoErr)
|
||||
*ok = false;
|
||||
else
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
}
|
||||
ippsFree(pBuffer);
|
||||
ippsFree(pSpec);
|
||||
}
|
||||
private:
|
||||
int src_type;
|
||||
cv::Mat &src;
|
||||
size_t src_step;
|
||||
cv::Mat &dst;
|
||||
size_t dst_step;
|
||||
IppiInterpolationType inter;
|
||||
double (&coeffs)[3][3];
|
||||
int borderType;
|
||||
double borderValue[4];
|
||||
ippiWarpPerspectiveFunc func;
|
||||
bool *ok;
|
||||
|
||||
const IPPWarpPerspectiveInvoker& operator= (const IPPWarpPerspectiveInvoker&);
|
||||
};
|
||||
|
||||
int ipp_hal_warpPerspective(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height, uchar * dst_data, size_t dst_step,
|
||||
int dst_width, int dst_height, const double M[9], int interpolation, int borderType, const double borderValue[4])
|
||||
{
|
||||
//CV_INSTRUMENT_REGION_IPP();
|
||||
ippiWarpPerspectiveFunc ippFunc = 0;
|
||||
if (interpolation == cv::InterpolationFlags::INTER_NEAREST)
|
||||
{
|
||||
ippFunc = src_type == CV_8UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_8u_C1R :
|
||||
src_type == CV_8UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_8u_C3R :
|
||||
src_type == CV_8UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_8u_C4R :
|
||||
src_type == CV_16UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16u_C1R :
|
||||
src_type == CV_16UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16u_C3R :
|
||||
src_type == CV_16UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16u_C4R :
|
||||
src_type == CV_16SC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16s_C1R :
|
||||
src_type == CV_16SC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16s_C3R :
|
||||
src_type == CV_16SC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_16s_C4R :
|
||||
src_type == CV_32FC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_32f_C1R :
|
||||
src_type == CV_32FC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_32f_C3R :
|
||||
src_type == CV_32FC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveNearest_32f_C4R : 0;
|
||||
}
|
||||
else if (interpolation == cv::InterpolationFlags::INTER_LINEAR)
|
||||
{
|
||||
ippFunc = src_type == CV_8UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_8u_C1R :
|
||||
src_type == CV_8UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_8u_C3R :
|
||||
src_type == CV_8UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_8u_C4R :
|
||||
src_type == CV_16UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16u_C1R :
|
||||
src_type == CV_16UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16u_C3R :
|
||||
src_type == CV_16UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16u_C4R :
|
||||
src_type == CV_16SC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16s_C1R :
|
||||
src_type == CV_16SC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16s_C3R :
|
||||
src_type == CV_16SC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_16s_C4R :
|
||||
src_type == CV_32FC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_32f_C1R :
|
||||
src_type == CV_32FC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_32f_C3R :
|
||||
src_type == CV_32FC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveLinear_32f_C4R : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
if(src_height == 1 || src_width == 1) return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
|
||||
int mode =
|
||||
interpolation == cv::InterpolationFlags::INTER_NEAREST ? IPPI_INTER_NN :
|
||||
interpolation == cv::InterpolationFlags::INTER_LINEAR ? IPPI_INTER_LINEAR : 0;
|
||||
|
||||
if (mode == 0 || ippFunc == 0)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
/* C1 C2 C3 C4 */
|
||||
char impl[CV_DEPTH_MAX][4][2]={{{0, 0}, {1, 1}, {0, 0}, {0, 0}}, //8U
|
||||
{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, //8S
|
||||
{{0, 0}, {1, 1}, {0, 1}, {0, 1}}, //16U
|
||||
{{1, 1}, {1, 1}, {1, 1}, {1, 1}}, //16S
|
||||
{{1, 1}, {1, 1}, {1, 0}, {1, 1}}, //32S
|
||||
{{1, 0}, {1, 0}, {0, 0}, {1, 0}}, //32F
|
||||
{{1, 1}, {1, 1}, {1, 1}, {1, 1}}}; //64F
|
||||
|
||||
if(impl[CV_TYPE(src_type)][CV_MAT_CN(src_type)-1][interpolation] == 0)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
double coeffs[3][3];
|
||||
for( int i = 0; i < 3; i++ )
|
||||
for( int j = 0; j < 3; j++ )
|
||||
coeffs[i][j] = M[i*3 + j];
|
||||
|
||||
bool ok;
|
||||
cv::Range range(0, dst_height);
|
||||
cv::Mat src(cv::Size(src_width, src_height), src_type, const_cast<uchar*>(src_data), src_step);
|
||||
cv::Mat dst(cv::Size(dst_width, dst_height), src_type, dst_data, dst_step);
|
||||
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
|
||||
IPPWarpPerspectiveInvoker invoker(src_type, src, src_step, dst, dst_step, ippInter, coeffs, borderType, borderValue, ippFunc, &ok);
|
||||
parallel_for_(range, invoker, dst.total()/(double)(1<<16));
|
||||
|
||||
if( ok )
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
}
|
||||
return CV_HAL_ERROR_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef IppStatus(CV_STDCALL *ippiRemap)(const void *pSrc, IppiSize srcSize, int srcStep, IppiRect srcRoi,
|
||||
const Ipp32f *pxMap, int xMapStep, const Ipp32f *pyMap, int yMapStep,
|
||||
void *pDst, int dstStep, IppiSize dstRoiSize, int interpolation);
|
||||
|
||||
class IPPRemapInvoker : public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
IPPRemapInvoker(int _src_type, const uchar *_src_data, size_t _src_step, int _src_width, int _src_height,
|
||||
uchar *_dst_data, size_t _dst_step, int _dst_width, float *_mapx, size_t _mapx_step, float *_mapy,
|
||||
size_t _mapy_step, ippiRemap _ippFunc, int _ippInterpolation, int _borderType, const double _borderValue[4], bool *_ok) :
|
||||
ParallelLoopBody(),
|
||||
src_type(_src_type), src(_src_data), src_step(_src_step), src_width(_src_width), src_height(_src_height),
|
||||
dst(_dst_data), dst_step(_dst_step), dst_width(_dst_width), mapx(_mapx), mapx_step(_mapx_step), mapy(_mapy),
|
||||
mapy_step(_mapy_step), ippFunc(_ippFunc), ippInterpolation(_ippInterpolation), borderType(_borderType), ok(_ok)
|
||||
{
|
||||
memcpy(this->borderValue, _borderValue, sizeof(this->borderValue));
|
||||
*ok = true;
|
||||
}
|
||||
|
||||
virtual void operator()(const cv::Range &range) const
|
||||
{
|
||||
IppiRect srcRoiRect = {0, 0, src_width, src_height};
|
||||
uchar *dst_roi_data = dst + range.start * dst_step;
|
||||
IppiSize dstRoiSize = ippiSize(dst_width, range.size());
|
||||
int depth = CV_MAT_DEPTH(src_type), cn = CV_MAT_CN(src_type);
|
||||
|
||||
if (borderType == cv::BORDER_CONSTANT &&
|
||||
!IPPSet(borderValue, dst_roi_data, (int)dst_step, dstRoiSize, cn, depth))
|
||||
{
|
||||
*ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (CV_INSTRUMENT_FUN_IPP(ippFunc, src, {src_width, src_height}, (int)src_step, srcRoiRect,
|
||||
mapx, (int)mapx_step, mapy, (int)mapy_step,
|
||||
dst_roi_data, (int)dst_step, dstRoiSize, ippInterpolation) < 0)
|
||||
*ok = false;
|
||||
else
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP | CV_IMPL_MT);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int src_type;
|
||||
const uchar *src;
|
||||
size_t src_step;
|
||||
int src_width, src_height;
|
||||
uchar *dst;
|
||||
size_t dst_step;
|
||||
int dst_width;
|
||||
float *mapx;
|
||||
size_t mapx_step;
|
||||
float *mapy;
|
||||
size_t mapy_step;
|
||||
ippiRemap ippFunc;
|
||||
int ippInterpolation, borderType;
|
||||
double borderValue[4];
|
||||
bool *ok;
|
||||
};
|
||||
|
||||
int ipp_hal_remap32f(int src_type, const uchar *src_data, size_t src_step, int src_width, int src_height,
|
||||
uchar *dst_data, size_t dst_step, int dst_width, int dst_height,
|
||||
float *mapx, size_t mapx_step, float *mapy, size_t mapy_step,
|
||||
int interpolation, int border_type, const double border_value[4])
|
||||
{
|
||||
if ((interpolation == cv::INTER_LINEAR || interpolation == cv::INTER_CUBIC || interpolation == cv::INTER_NEAREST) &&
|
||||
(border_type == cv::BORDER_CONSTANT || border_type == cv::BORDER_TRANSPARENT))
|
||||
{
|
||||
int ippInterpolation =
|
||||
interpolation == cv::INTER_NEAREST ? IPPI_INTER_NN : interpolation == cv::INTER_LINEAR ? IPPI_INTER_LINEAR
|
||||
: IPPI_INTER_CUBIC;
|
||||
|
||||
/* C1 C2 C3 C4 */
|
||||
char impl[CV_DEPTH_MAX][4][3]={{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //8S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //16U
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //16S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32S
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, //32F
|
||||
{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}}}; //64F
|
||||
|
||||
if (impl[CV_TYPE(src_type)][CV_MAT_CN(src_type) - 1][interpolation] == 0)
|
||||
{
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
ippiRemap ippFunc =
|
||||
src_type == CV_8UC1 ? (ippiRemap)ippiRemap_8u_C1R : src_type == CV_8UC3 ? (ippiRemap)ippiRemap_8u_C3R
|
||||
: src_type == CV_8UC4 ? (ippiRemap)ippiRemap_8u_C4R
|
||||
: src_type == CV_16UC1 ? (ippiRemap)ippiRemap_16u_C1R
|
||||
: src_type == CV_16UC3 ? (ippiRemap)ippiRemap_16u_C3R
|
||||
: src_type == CV_16UC4 ? (ippiRemap)ippiRemap_16u_C4R
|
||||
: src_type == CV_32FC1 ? (ippiRemap)ippiRemap_32f_C1R
|
||||
: src_type == CV_32FC3 ? (ippiRemap)ippiRemap_32f_C3R
|
||||
: src_type == CV_32FC4 ? (ippiRemap)ippiRemap_32f_C4R
|
||||
: 0;
|
||||
|
||||
if (ippFunc)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
IPPRemapInvoker invoker(src_type, src_data, src_step, src_width, src_height, dst_data, dst_step, dst_width,
|
||||
mapx, mapx_step, mapy, mapy_step, ippFunc, ippInterpolation, border_type, border_value, &ok);
|
||||
cv::Range range(0, dst_height);
|
||||
cv::parallel_for_(range, invoker, dst_width * dst_height / (double)(1 << 16));
|
||||
|
||||
if (ok)
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP | CV_IMPL_MT);
|
||||
return CV_HAL_ERROR_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CV_HAL_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
function(download_kleidicv root_var)
|
||||
set(${root_var} "" PARENT_SCOPE)
|
||||
|
||||
ocv_update(KLEIDICV_SRC_COMMIT "0.3.0")
|
||||
ocv_update(KLEIDICV_SRC_HASH "51a77b0185c2bac2a968a2163869b1ed")
|
||||
ocv_update(KLEIDICV_SRC_COMMIT "0.5.0")
|
||||
ocv_update(KLEIDICV_SRC_HASH "ba5648f8df678548f337d19d8ac607d6")
|
||||
|
||||
set(THE_ROOT "${OpenCV_BINARY_DIR}/3rdparty/kleidicv")
|
||||
ocv_download(FILENAME "kleidicv-${KLEIDICV_SRC_COMMIT}.tar.gz"
|
||||
|
||||
@@ -238,8 +238,10 @@ int integral(int depth, int sdepth, int sqdepth,
|
||||
uchar* tilted_data, [[maybe_unused]] size_t tilted_step,
|
||||
int width, int height, int cn);
|
||||
|
||||
#undef cv_hal_integral
|
||||
#define cv_hal_integral cv::rvv_hal::imgproc::integral
|
||||
// Diasbled due to accuracy issue.
|
||||
// Details see https://github.com/opencv/opencv/issues/27407.
|
||||
//#undef cv_hal_integral
|
||||
//#define cv_hal_integral cv::rvv_hal::imgproc::integral
|
||||
|
||||
#endif // CV_HAL_RVV_1P0_ENABLED
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
The pose computation problem @cite Marchand16 consists in solving for the rotation and translation that minimizes the reprojection error from 3D-2D point correspondences.
|
||||
|
||||
The `solvePnP` and related functions estimate the object pose given a set of object points, their corresponding image projections, as well as the camera intrinsic matrix and the distortion coefficients, see the figure below (more precisely, the X-axis of the camera frame is pointing to the right, the Y-axis downward and the Z-axis forward).
|
||||
The `solvePnP` and related functions estimate the object pose given a set of object points, their corresponding image projections, as well as the camera intrinsic matrix and the distortion coefficients, see the figure below (more precisely, the convention in the computer vision field is to have the X-axis of the camera frame pointing to the right, the Y-axis downward and the Z-axis forward).
|
||||
|
||||

|
||||

|
||||
|
||||
Points expressed in the world frame \f$ \bf{X}_w \f$ are projected into the image plane \f$ \left[ u, v \right] \f$
|
||||
using the perspective projection model \f$ \Pi \f$ and the camera intrinsic parameters matrix \f$ \bf{A} \f$ (also denoted \f$ \bf{K} \f$ in the literature):
|
||||
|
||||
@@ -973,7 +973,9 @@ An example program about homography from the camera displacement
|
||||
Check @ref tutorial_homography "the corresponding tutorial" for more details
|
||||
*/
|
||||
|
||||
/** @brief Finds an object pose from 3D-2D point correspondences.
|
||||
/** @brief Finds an object pose \f$ {}^{c}\mathbf{T}_o \f$ from 3D-2D point correspondences:
|
||||
|
||||
{ width=50% }
|
||||
|
||||
@see @ref calib3d_solvePnP
|
||||
|
||||
@@ -1043,7 +1045,9 @@ CV_EXPORTS_W bool solvePnP( InputArray objectPoints, InputArray imagePoints,
|
||||
OutputArray rvec, OutputArray tvec,
|
||||
bool useExtrinsicGuess = false, int flags = SOLVEPNP_ITERATIVE );
|
||||
|
||||
/** @brief Finds an object pose from 3D-2D point correspondences using the RANSAC scheme.
|
||||
/** @brief Finds an object pose \f$ {}^{c}\mathbf{T}_o \f$ from 3D-2D point correspondences using the RANSAC scheme to deal with bad matches.
|
||||
|
||||
{ width=50% }
|
||||
|
||||
@see @ref calib3d_solvePnP
|
||||
|
||||
@@ -1076,8 +1080,8 @@ projections imagePoints and the projected (using @ref projectPoints ) objectPoin
|
||||
makes the function resistant to outliers.
|
||||
|
||||
@note
|
||||
- An example of how to use solvePNPRansac for object detection can be found at
|
||||
opencv_source_code/samples/cpp/tutorial_code/3d/real_time_pose_estimation/
|
||||
- An example of how to use solvePnPRansac for object detection can be found at
|
||||
@ref tutorial_real_time_pose
|
||||
- The default method used to estimate the camera pose for the Minimal Sample Sets step
|
||||
is #SOLVEPNP_EPNP. Exceptions are:
|
||||
- if you choose #SOLVEPNP_P3P or #SOLVEPNP_AP3P, these methods will be used.
|
||||
@@ -1102,7 +1106,9 @@ CV_EXPORTS_W bool solvePnPRansac( InputArray objectPoints, InputArray imagePoint
|
||||
OutputArray rvec, OutputArray tvec, OutputArray inliers,
|
||||
const UsacParams ¶ms=UsacParams());
|
||||
|
||||
/** @brief Finds an object pose from 3 3D-2D point correspondences.
|
||||
/** @brief Finds an object pose \f$ {}^{c}\mathbf{T}_o \f$ from **3** 3D-2D point correspondences.
|
||||
|
||||
{ width=50% }
|
||||
|
||||
@see @ref calib3d_solvePnP
|
||||
|
||||
@@ -1197,7 +1203,9 @@ CV_EXPORTS_W void solvePnPRefineVVS( InputArray objectPoints, InputArray imagePo
|
||||
TermCriteria::COUNT, 20, FLT_EPSILON),
|
||||
double VVSlambda = 1);
|
||||
|
||||
/** @brief Finds an object pose from 3D-2D point correspondences.
|
||||
/** @brief Finds an object pose \f$ {}^{c}\mathbf{T}_o \f$ from 3D-2D point correspondences.
|
||||
|
||||
{ width=50% }
|
||||
|
||||
@see @ref calib3d_solvePnP
|
||||
|
||||
@@ -1268,6 +1276,7 @@ More information is described in @ref calib3d_solvePnP
|
||||
- point 1: [ squareLength / 2, squareLength / 2, 0]
|
||||
- point 2: [ squareLength / 2, -squareLength / 2, 0]
|
||||
- point 3: [-squareLength / 2, -squareLength / 2, 0]
|
||||
- With @ref SOLVEPNP_SQPNP input points must be >= 3
|
||||
*/
|
||||
CV_EXPORTS_W int solvePnPGeneric( InputArray objectPoints, InputArray imagePoints,
|
||||
InputArray cameraMatrix, InputArray distCoeffs,
|
||||
@@ -2615,9 +2624,9 @@ CV_EXPORTS_W void undistortImage(InputArray distorted, OutputArray undistorted,
|
||||
@brief Finds an object pose from 3D-2D point correspondences for fisheye camera moodel.
|
||||
|
||||
@param objectPoints Array of object points in the object coordinate space, Nx3 1-channel or
|
||||
1xN/Nx1 3-channel, where N is the number of points. vector\<Point3d\> can be also passed here.
|
||||
1xN/Nx1 3-channel, where N is the number of points. vector\<Point3d\> can also be passed here.
|
||||
@param imagePoints Array of corresponding image points, Nx2 1-channel or 1xN/Nx1 2-channel,
|
||||
where N is the number of points. vector\<Point2d\> can be also passed here.
|
||||
where N is the number of points. vector\<Point2d\> can also be passed here.
|
||||
@param cameraMatrix Input camera intrinsic matrix \f$\cameramatrix{A}\f$ .
|
||||
@param distCoeffs Input vector of distortion coefficients (4x1/1x4).
|
||||
@param rvec Output rotation vector (see @ref Rodrigues ) that, together with tvec, brings points from
|
||||
|
||||
@@ -121,7 +121,7 @@ void drawFrameAxes(InputOutputArray image, InputArray cameraMatrix, InputArray d
|
||||
|
||||
if (!allIn)
|
||||
{
|
||||
CV_LOG_WARNING(NULL, "Some of projected axes endpoints are out of frame. The drawn axes may be not relaible.");
|
||||
CV_LOG_WARNING(NULL, "Some of projected axes endpoints are out of frame. The drawn axes may be not reliable.");
|
||||
}
|
||||
|
||||
// draw axes lines
|
||||
|
||||
+26
-37
@@ -63,21 +63,6 @@ const double PoseSolver::POINT_VARIANCE_THRESHOLD = 1e-5;
|
||||
const double PoseSolver::SQRT3 = std::sqrt(3);
|
||||
const int PoseSolver::SQP_MAX_ITERATION = 15;
|
||||
|
||||
// No checking done here for overflow, since this is not public all call instances
|
||||
// are assumed to be valid
|
||||
template <typename tp, int snrows, int sncols,
|
||||
int dnrows, int dncols>
|
||||
void set(int row, int col, cv::Matx<tp, dnrows, dncols>& dest,
|
||||
const cv::Matx<tp, snrows, sncols>& source)
|
||||
{
|
||||
for (int y = 0; y < snrows; y++)
|
||||
{
|
||||
for (int x = 0; x < sncols; x++)
|
||||
{
|
||||
dest(row + y, col + x) = source(y, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PoseSolver::PoseSolver()
|
||||
: num_null_vectors_(-1),
|
||||
@@ -278,7 +263,7 @@ void PoseSolver::computeOmega(InputArray objectPoints, InputArray imagePoints)
|
||||
// EVD equivalent of the SVD; less accurate
|
||||
cv::eigen(omega_, s_, u_);
|
||||
u_ = u_.t(); // eigenvectors were returned as rows
|
||||
#endif
|
||||
#endif // EVD
|
||||
|
||||
#endif // HAVE_EIGEN
|
||||
|
||||
@@ -599,9 +584,10 @@ void PoseSolver::computeRowAndNullspace(const cv::Matx<double, 9, 1>& r,
|
||||
H(5, 4) = r(8) - dot_j5q2 * H(5, 1) - dot_j5q4 * H(5, 3);
|
||||
H(6, 4) = r(3) - dot_j5q3 * H(6, 2); H(7, 4) = r(4) - dot_j5q3 * H(7, 2); H(8, 4) = r(5) - dot_j5q3 * H(8, 2);
|
||||
|
||||
Matx<double, 9, 1> q4 = H.col(4);
|
||||
q4 *= (1.0 / cv::norm(q4));
|
||||
set<double, 9, 1, 9, 6>(0, 4, H, q4);
|
||||
cv::Matx<double, 9, 1> q4 = H.col(4);
|
||||
const double inv_norm_q4 = 1.0 / cv::norm(q4);
|
||||
for (int i = 0; i < 9; ++i)
|
||||
H(i, 4) = q4(i) * inv_norm_q4;
|
||||
|
||||
K(4, 0) = 0;
|
||||
K(4, 1) = r(6) * H(3, 1) + r(7) * H(4, 1) + r(8) * H(5, 1);
|
||||
@@ -628,9 +614,10 @@ void PoseSolver::computeRowAndNullspace(const cv::Matx<double, 9, 1>& r,
|
||||
H(7, 5) = r(1) - dot_j6q3 * H(7, 2) - dot_j6q5 * H(7, 4);
|
||||
H(8, 5) = r(2) - dot_j6q3 * H(8, 2) - dot_j6q5 * H(8, 4);
|
||||
|
||||
Matx<double, 9, 1> q5 = H.col(5);
|
||||
q5 *= (1.0 / cv::norm(q5));
|
||||
set<double, 9, 1, 9, 6>(0, 5, H, q5);
|
||||
cv::Matx<double, 9, 1> q5 = H.col(5);
|
||||
const double inv_norm_q5 = 1.0 / cv::norm(q5);
|
||||
for (int i = 0; i < 9; ++i)
|
||||
H(i, 5) = q5(i) * inv_norm_q5;
|
||||
|
||||
K(5, 0) = r(6) * H(0, 0) + r(7) * H(1, 0) + r(8) * H(2, 0);
|
||||
K(5, 1) = 0; K(5, 2) = r(0) * H(6, 2) + r(1) * H(7, 2) + r(2) * H(8, 2);
|
||||
@@ -668,9 +655,10 @@ void PoseSolver::computeRowAndNullspace(const cv::Matx<double, 9, 1>& r,
|
||||
}
|
||||
}
|
||||
|
||||
Matx<double, 9, 1> v1 = Pn.col(index1);
|
||||
v1 /= max_norm1;
|
||||
set<double, 9, 1, 9, 3>(0, 0, N, v1);
|
||||
const cv::Matx<double, 9, 1>& v1 = Pn.col(index1);
|
||||
const double inv_max_norm1 = 1.0 / max_norm1;
|
||||
for (int i = 0; i < 9; ++i)
|
||||
N(i, 0) = v1(i) * inv_max_norm1;
|
||||
col_norms[index1] = -1.0; // mark to avoid use in subsequent loops
|
||||
|
||||
for (int i = 0; i < 9; i++)
|
||||
@@ -688,11 +676,12 @@ void PoseSolver::computeRowAndNullspace(const cv::Matx<double, 9, 1>& r,
|
||||
}
|
||||
}
|
||||
|
||||
Matx<double, 9, 1> v2 = Pn.col(index2);
|
||||
Matx<double, 9, 1> n0 = N.col(0);
|
||||
v2 -= v2.dot(n0) * n0;
|
||||
v2 *= (1.0 / cv::norm(v2));
|
||||
set<double, 9, 1, 9, 3>(0, 1, N, v2);
|
||||
const cv::Matx<double, 9, 1>& v2 = Pn.col(index2);
|
||||
const cv::Matx<double, 9, 1>& n0 = N.col(0);
|
||||
cv::Matx<double, 9, 1> u2 = v2 - v2.dot(n0) * n0;
|
||||
const double inv_norm_u2 = 1.0 / cv::norm(u2);
|
||||
for (int i = 0; i < 9; ++i)
|
||||
N(i, 1) = u2(i) * inv_norm_u2;
|
||||
col_norms[index2] = -1.0; // mark to avoid use in subsequent loops
|
||||
|
||||
for (int i = 0; i < 9; i++)
|
||||
@@ -707,17 +696,17 @@ void PoseSolver::computeRowAndNullspace(const cv::Matx<double, 9, 1>& r,
|
||||
if (cos_v1_x_col + cos_v2_x_col <= min_dot1323)
|
||||
{
|
||||
index3 = i;
|
||||
min_dot1323 = cos_v2_x_col + cos_v2_x_col;
|
||||
min_dot1323 = cos_v1_x_col + cos_v2_x_col;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Matx<double, 9, 1> v3 = Pn.col(index3);
|
||||
Matx<double, 9, 1> n1 = N.col(1);
|
||||
v3 -= (v3.dot(n1)) * n1 - (v3.dot(n0)) * n0;
|
||||
v3 *= (1.0 / cv::norm(v3));
|
||||
set<double, 9, 1, 9, 3>(0, 2, N, v3);
|
||||
|
||||
const cv::Matx<double, 9, 1>& v3 = Pn.col(index3);
|
||||
const cv::Matx<double, 9, 1>& n1 = N.col(1);
|
||||
cv::Matx<double, 9, 1> u3 = v3 - (v3.dot(n1) * n1) - (v3.dot(n0) * n0);
|
||||
const double inv_norm_u3 = 1.0 / cv::norm(u3);
|
||||
for (int i = 0; i < 9; ++i)
|
||||
N(i, 2) = u3(i) * inv_norm_u3;
|
||||
}
|
||||
|
||||
// if e = u*w*vt then r=u*diag([1, 1, det(u)*det(v)])*vt
|
||||
|
||||
@@ -424,6 +424,9 @@ f_{\text{mm}} = \frac{\text{sensor_size_in_mm}}{2 \times \tan{\frac{\text{fov}}{
|
||||
\f]
|
||||
This latter conversion can be useful when using a rendering software to mimic a physical camera device.
|
||||
|
||||
@note
|
||||
- See also #calibrationMatrixValues
|
||||
|
||||
<B> Additional references, notes </B><br>
|
||||
@note
|
||||
- Many functions in this module take a camera intrinsic matrix as an input parameter. Although all
|
||||
@@ -626,7 +629,8 @@ the board to make the detection more robust in various environments. Otherwise,
|
||||
border and the background is dark, the outer black squares cannot be segmented properly and so the
|
||||
square grouping and ordering algorithm fails.
|
||||
|
||||
Use gen_pattern.py (@ref tutorial_camera_calibration_pattern) to create checkerboard.
|
||||
Use the `gen_pattern.py` Python script (@ref tutorial_camera_calibration_pattern)
|
||||
to create the desired checkerboard pattern.
|
||||
*/
|
||||
CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize, OutputArray corners,
|
||||
int flags = CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE );
|
||||
@@ -684,8 +688,9 @@ which are located on the outside of the board. The following figure illustrates
|
||||
a sample checkerboard optimized for the detection. However, any other checkerboard
|
||||
can be used as well.
|
||||
|
||||
Use gen_pattern.py (@ref tutorial_camera_calibration_pattern) to create checkerboard.
|
||||

|
||||
Use the `gen_pattern.py` Python script (@ref tutorial_camera_calibration_pattern)
|
||||
to create the corresponding checkerboard pattern:
|
||||
\image html pics/checkerboard_radon.png width=60%
|
||||
*/
|
||||
CV_EXPORTS_AS(findChessboardCornersSBWithMeta)
|
||||
bool findChessboardCornersSB(InputArray image,Size patternSize, OutputArray corners,
|
||||
|
||||
@@ -749,6 +749,11 @@ Possible usage with some positive example data:
|
||||
normalize(positiveData, normalizedData_minmax, 1.0, 0.0, NORM_MINMAX);
|
||||
@endcode
|
||||
|
||||
@note Due to rounding issues, min-max normalization can result in values outside provided boundaries.
|
||||
If exact range conformity is needed, following workarounds can be used:
|
||||
- use double floating point precision (dtype = CV_64F)
|
||||
- manually clip values (`cv::max(res, left_bound, res)`, `cv::min(res, right_bound, res)` or `np.clip`)
|
||||
|
||||
@param src input array.
|
||||
@param dst output array of the same size as src .
|
||||
@param alpha norm value to normalize to or the lower range boundary in case of the range
|
||||
|
||||
@@ -240,6 +240,10 @@ public:
|
||||
|
||||
//! converts GpuMat to another datatype (Blocking call)
|
||||
void convertTo(OutputArray dst, int rtype) const;
|
||||
//! bindings overload which converts GpuMat to another datatype (Blocking call)
|
||||
CV_WRAP void convertTo(CV_OUT GpuMat& dst, int rtype) const {
|
||||
convertTo(static_cast<OutputArray>(dst), rtype);
|
||||
}
|
||||
|
||||
//! converts GpuMat to another datatype (Non-Blocking call)
|
||||
void convertTo(OutputArray dst, int rtype, Stream& stream) const;
|
||||
@@ -250,10 +254,13 @@ public:
|
||||
|
||||
//! converts GpuMat to another datatype with scaling (Blocking call)
|
||||
void convertTo(OutputArray dst, int rtype, double alpha, double beta = 0.0) const;
|
||||
|
||||
//! bindings overload which converts GpuMat to another datatype with scaling(Blocking call)
|
||||
CV_WRAP void convertTo(CV_OUT GpuMat& dst, int rtype, double alpha = 1.0, double beta = 0.0) const {
|
||||
#ifdef OPENCV_BINDINGS_PARSER
|
||||
CV_WRAP void convertTo(CV_OUT GpuMat& dst, int rtype, double alpha=1.0, double beta = 0.0) const {
|
||||
convertTo(static_cast<OutputArray>(dst), rtype, alpha, beta);
|
||||
}
|
||||
#endif
|
||||
|
||||
//! converts GpuMat to another datatype with scaling (Non-Blocking call)
|
||||
void convertTo(OutputArray dst, int rtype, double alpha, Stream& stream) const;
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
# define CV_AVX 1
|
||||
#endif
|
||||
#ifdef CV_CPU_COMPILE_FP16
|
||||
# if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64)
|
||||
# if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC)
|
||||
# include <arm_neon.h>
|
||||
# elif defined(__riscv_vector)
|
||||
# include <riscv_vector.h>
|
||||
@@ -139,7 +139,7 @@
|
||||
# define CV_FMA3 1
|
||||
#endif
|
||||
|
||||
#if defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64)) && (defined(CV_CPU_COMPILE_NEON) || !defined(_MSC_VER))
|
||||
#if defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC)) && (defined(CV_CPU_COMPILE_NEON) || !defined(_MSC_VER))
|
||||
# include <Intrin.h>
|
||||
# include <arm_neon.h>
|
||||
# define CV_NEON 1
|
||||
@@ -232,7 +232,7 @@ struct VZeroUpperGuard {
|
||||
# define CV_MMX 1
|
||||
# define CV_SSE 1
|
||||
# define CV_SSE2 1
|
||||
#elif defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64)) && (defined(CV_CPU_COMPILE_NEON) || !defined(_MSC_VER))
|
||||
#elif defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC)) && (defined(CV_CPU_COMPILE_NEON) || !defined(_MSC_VER))
|
||||
# include <Intrin.h>
|
||||
# include <arm_neon.h>
|
||||
# define CV_NEON 1
|
||||
|
||||
@@ -370,7 +370,7 @@ enum CpuFeatures {
|
||||
|
||||
#include "cv_cpu_dispatch.h"
|
||||
|
||||
#if !defined(CV_STRONG_ALIGNMENT) && defined(__arm__) && !(defined(__aarch64__) || defined(_M_ARM64))
|
||||
#if !defined(CV_STRONG_ALIGNMENT) && defined(__arm__) && !(defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC))
|
||||
// int*, int64* should be propertly aligned pointers on ARMv7
|
||||
#define CV_STRONG_ALIGNMENT 1
|
||||
#endif
|
||||
@@ -710,7 +710,7 @@ __CV_ENUM_FLAGS_BITWISE_XOR_EQ (EnumType, EnumType)
|
||||
#ifdef CV_XADD
|
||||
// allow to use user-defined macro
|
||||
#elif defined __GNUC__ || defined __clang__
|
||||
# if defined __clang__ && __clang_major__ >= 3 && !defined __ANDROID__ && !defined __EMSCRIPTEN__ && !defined(__CUDACC__) && !defined __INTEL_COMPILER
|
||||
# if defined __clang__ && __clang_major__ >= 3 && !defined __EMSCRIPTEN__ && !defined __INTEL_COMPILER
|
||||
# ifdef __ATOMIC_ACQ_REL
|
||||
# define CV_XADD(addr, delta) __c11_atomic_fetch_add((_Atomic(int)*)(addr), delta, __ATOMIC_ACQ_REL)
|
||||
# else
|
||||
|
||||
@@ -303,7 +303,7 @@ CV_INLINE int cvIsInf( double value )
|
||||
{
|
||||
#if defined CV_INLINE_ISINF_DBL
|
||||
CV_INLINE_ISINF_DBL(value);
|
||||
#elif defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__PPC64__) || defined(__loongarch64)
|
||||
#elif defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC) || defined(__PPC64__) || defined(__loongarch64)
|
||||
Cv64suf ieee754;
|
||||
ieee754.f = value;
|
||||
return (ieee754.u & 0x7fffffffffffffff) ==
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace cv
|
||||
CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN
|
||||
|
||||
#define CV_SIMD128 1
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
|
||||
#define CV_SIMD128_64F 1
|
||||
#else
|
||||
#define CV_SIMD128_64F 0
|
||||
@@ -77,7 +77,7 @@ CV_CPU_OPTIMIZATION_HAL_NAMESPACE_BEGIN
|
||||
//
|
||||
// [1] https://developer.arm.com/documentation/101028/0012/13--Advanced-SIMD--Neon--intrinsics
|
||||
// [2] https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
|
||||
#if defined(__ARM_64BIT_STATE) || defined(_M_ARM64)
|
||||
#if defined(__ARM_64BIT_STATE) || defined(_M_ARM64) || defined(_M_ARM64EC)
|
||||
#define CV_NEON_AARCH64 1
|
||||
#else
|
||||
#define CV_NEON_AARCH64 0
|
||||
@@ -1221,7 +1221,7 @@ OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_uint32x4, OPENCV_HAL_NOP, u32, u32)
|
||||
OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_int32x4, vreinterpretq_s32_u32, s32, u32)
|
||||
OPENCV_HAL_IMPL_NEON_INT_CMP_OP(v_float32x4, vreinterpretq_f32_u32, f32, u32)
|
||||
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
|
||||
static inline uint64x2_t vmvnq_u64(uint64x2_t a)
|
||||
{
|
||||
uint64x2_t vx = vreinterpretq_u64_u32(vdupq_n_u32(0xFFFFFFFF));
|
||||
@@ -2053,7 +2053,7 @@ inline v_int32x4 v_load_expand_q(const schar* ptr)
|
||||
return v_int32x4(vmovl_s16(v1));
|
||||
}
|
||||
|
||||
#if defined(__aarch64__) || defined(_M_ARM64)
|
||||
#if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
|
||||
#define OPENCV_HAL_IMPL_NEON_UNPACKS(_Tpvec, suffix) \
|
||||
inline void v_zip(const v_##_Tpvec& a0, const v_##_Tpvec& a1, v_##_Tpvec& b0, v_##_Tpvec& b1) \
|
||||
{ \
|
||||
|
||||
@@ -190,8 +190,6 @@ T* allocSingletonNew() { return new(allocSingletonNewBuffer(sizeof(T))) T(); }
|
||||
#define IPP_DISABLE_PYRAMIDS_UP 1 // Different results
|
||||
#define IPP_DISABLE_PYRAMIDS_DOWN 1 // Different results
|
||||
#define IPP_DISABLE_PYRAMIDS_BUILD 1 // Different results
|
||||
#define IPP_DISABLE_WARPAFFINE 1 // Different results
|
||||
#define IPP_DISABLE_WARPPERSPECTIVE 1 // Different results
|
||||
#define IPP_DISABLE_REMAP 1 // Different results
|
||||
#define IPP_DISABLE_YUV_RGB 1 // accuracy difference
|
||||
#define IPP_DISABLE_RGB_YUV 1 // breaks OCL accuracy tests
|
||||
@@ -202,7 +200,6 @@ T* allocSingletonNew() { return new(allocSingletonNewBuffer(sizeof(T))) T(); }
|
||||
#define IPP_DISABLE_XYZ_RGB 1 // big accuracy difference
|
||||
#define IPP_DISABLE_HOUGH 1 // improper integration/results
|
||||
#define IPP_DISABLE_FILTER2D_BIG_MASK 1 // different results on masks > 7x7
|
||||
#define IPP_DISABLE_NORM_8U 1 // accuracy difference in perf test sanity check
|
||||
|
||||
// Temporary disabled named IPP region. Performance
|
||||
#define IPP_DISABLE_PERF_COPYMAKE 1 // performance variations
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
/* no support */
|
||||
# elif defined WINRT || defined _WIN32_WCE
|
||||
/* not supported */
|
||||
# elif defined __ANDROID__ || defined __linux__ || defined _WIN32 || \
|
||||
# elif defined __ANDROID__ || defined __linux__ || defined _WIN32 || defined __CYGWIN__ || \
|
||||
defined __FreeBSD__ || defined __bsdi__ || defined __HAIKU__ || \
|
||||
defined __GNU__ || defined __QNX__
|
||||
# define OPENCV_HAVE_FILESYSTEM_SUPPORT 1
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__)
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__CYGWIN__)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
@@ -65,7 +65,7 @@ void* getSymbol_(LibHandle_t h, const char* symbolName)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return (void*)GetProcAddress(h, symbolName);
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__)
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__CYGWIN__)
|
||||
return dlsym(h, symbolName);
|
||||
#endif
|
||||
}
|
||||
@@ -79,7 +79,7 @@ LibHandle_t libraryLoad_(const FileSystemPath_t& filename)
|
||||
# else
|
||||
return LoadLibraryW(filename.c_str());
|
||||
#endif
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__)
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__CYGWIN__)
|
||||
void* handle = dlopen(filename.c_str(), RTLD_NOW);
|
||||
CV_LOG_IF_DEBUG(NULL, !handle, "dlopen() error: " << dlerror());
|
||||
return handle;
|
||||
@@ -91,7 +91,7 @@ void libraryRelease_(LibHandle_t h)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
FreeLibrary(h);
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__)
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__HAIKU__) || defined(__GLIBC__) || defined(__EMSCRIPTEN__) || defined(__CYGWIN__)
|
||||
dlclose(h);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -47,49 +47,50 @@ inline fun <reified T> Mat.at(idx: IntArray) : Atable<T> =
|
||||
|
||||
class AtableUByte(val mat: Mat, val indices: IntArray): Atable<UByte> {
|
||||
|
||||
|
||||
constructor(mat: Mat, row: Int, col: Int) : this(mat, intArrayOf(row, col))
|
||||
|
||||
override fun getV(): UByte {
|
||||
val data = UByteArray(1)
|
||||
val data = ByteArray(1)
|
||||
mat.get(indices, data)
|
||||
return data[0]
|
||||
return data[0].toUByte()
|
||||
}
|
||||
|
||||
override fun setV(v: UByte) {
|
||||
val data = ubyteArrayOf(v)
|
||||
val data = byteArrayOf(v.toByte())
|
||||
mat.put(indices, data)
|
||||
}
|
||||
|
||||
override fun getV2c(): Tuple2<UByte> {
|
||||
val data = UByteArray(2)
|
||||
val data = ByteArray(2)
|
||||
mat.get(indices, data)
|
||||
return Tuple2(data[0], data[1])
|
||||
return Tuple2(data[0].toUByte(), data[1].toUByte())
|
||||
}
|
||||
|
||||
override fun setV2c(v: Tuple2<UByte>) {
|
||||
val data = ubyteArrayOf(v._0, v._1)
|
||||
val data = byteArrayOf(v._0.toByte(), v._1.toByte())
|
||||
mat.put(indices, data)
|
||||
}
|
||||
|
||||
override fun getV3c(): Tuple3<UByte> {
|
||||
val data = UByteArray(3)
|
||||
val data = ByteArray(3)
|
||||
mat.get(indices, data)
|
||||
return Tuple3(data[0], data[1], data[2])
|
||||
return Tuple3(data[0].toUByte(), data[1].toUByte(), data[2].toUByte())
|
||||
}
|
||||
|
||||
override fun setV3c(v: Tuple3<UByte>) {
|
||||
val data = ubyteArrayOf(v._0, v._1, v._2)
|
||||
val data = byteArrayOf(v._0.toByte(), v._1.toByte(), v._2.toByte())
|
||||
mat.put(indices, data)
|
||||
}
|
||||
|
||||
override fun getV4c(): Tuple4<UByte> {
|
||||
val data = UByteArray(4)
|
||||
val data = ByteArray(4)
|
||||
mat.get(indices, data)
|
||||
return Tuple4(data[0], data[1], data[2], data[3])
|
||||
return Tuple4(data[0].toUByte(), data[1].toUByte(), data[2].toUByte(), data[3].toUByte())
|
||||
}
|
||||
|
||||
override fun setV4c(v: Tuple4<UByte>) {
|
||||
val data = ubyteArrayOf(v._0, v._1, v._2, v._3)
|
||||
val data = byteArrayOf(v._0.toByte(), v._1.toByte(), v._2.toByte(), v._3.toByte())
|
||||
mat.put(indices, data)
|
||||
}
|
||||
}
|
||||
@@ -99,46 +100,47 @@ class AtableUShort(val mat: Mat, val indices: IntArray): Atable<UShort> {
|
||||
constructor(mat: Mat, row: Int, col: Int) : this(mat, intArrayOf(row, col))
|
||||
|
||||
override fun getV(): UShort {
|
||||
val data = UShortArray(1)
|
||||
val data = ShortArray(1)
|
||||
mat.get(indices, data)
|
||||
return data[0]
|
||||
return data[0].toUShort()
|
||||
}
|
||||
|
||||
override fun setV(v: UShort) {
|
||||
val data = ushortArrayOf(v)
|
||||
val data = shortArrayOf(v.toShort())
|
||||
mat.put(indices, data)
|
||||
}
|
||||
|
||||
override fun getV2c(): Tuple2<UShort> {
|
||||
val data = UShortArray(2)
|
||||
val data = ShortArray(2)
|
||||
mat.get(indices, data)
|
||||
return Tuple2(data[0], data[1])
|
||||
return Tuple2(data[0].toUShort(), data[1].toUShort())
|
||||
}
|
||||
|
||||
|
||||
override fun setV2c(v: Tuple2<UShort>) {
|
||||
val data = ushortArrayOf(v._0, v._1)
|
||||
val data = shortArrayOf(v._0.toShort(), v._1.toShort())
|
||||
mat.put(indices, data)
|
||||
}
|
||||
|
||||
override fun getV3c(): Tuple3<UShort> {
|
||||
val data = UShortArray(3)
|
||||
val data = ShortArray(3)
|
||||
mat.get(indices, data)
|
||||
return Tuple3(data[0], data[1], data[2])
|
||||
return Tuple3(data[0].toUShort(), data[1].toUShort(), data[2].toUShort())
|
||||
}
|
||||
|
||||
override fun setV3c(v: Tuple3<UShort>) {
|
||||
val data = ushortArrayOf(v._0, v._1, v._2)
|
||||
val data = shortArrayOf(v._0.toShort(), v._1.toShort(), v._2.toShort())
|
||||
mat.put(indices, data)
|
||||
}
|
||||
|
||||
override fun getV4c(): Tuple4<UShort> {
|
||||
val data = UShortArray(4)
|
||||
val data = ShortArray(4)
|
||||
mat.get(indices, data)
|
||||
return Tuple4(data[0], data[1], data[2], data[3])
|
||||
return Tuple4(data[0].toUShort(), data[1].toUShort(), data[2].toUShort(), data[3].toUShort())
|
||||
}
|
||||
|
||||
override fun setV4c(v: Tuple4<UShort>) {
|
||||
val data = ushortArrayOf(v._0, v._1, v._2, v._3)
|
||||
val data = shortArrayOf(v._0.toShort(), v._1.toShort(), v._2.toShort(), v._3.toShort())
|
||||
mat.put(indices, data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,7 +548,7 @@ void cv::cuda::GpuMat::convertTo(OutputArray _dst, int rtype, Stream& stream) co
|
||||
return;
|
||||
}
|
||||
|
||||
CV_DbgAssert( sdepth <= CV_64F && ddepth <= CV_64F );
|
||||
CV_Assert( sdepth <= CV_64F && ddepth <= CV_64F );
|
||||
|
||||
GpuMat src = *this;
|
||||
|
||||
@@ -580,6 +580,8 @@ void cv::cuda::GpuMat::convertTo(OutputArray _dst, int rtype, double alpha, doub
|
||||
const int sdepth = depth();
|
||||
const int ddepth = CV_MAT_DEPTH(rtype);
|
||||
|
||||
CV_Assert(sdepth <= CV_64F && ddepth <= CV_64F);
|
||||
|
||||
GpuMat src = *this;
|
||||
|
||||
_dst.create(size(), rtype);
|
||||
|
||||
@@ -1598,7 +1598,7 @@ transform_32f( const float* src, float* dst, const float* m, int len, int scn, i
|
||||
// Disabled for RISC-V Vector (scalable), because of:
|
||||
// 1. v_matmuladd for RVV is 128-bit only but not scalable, this will fail the test `Core_Transform.accuracy`.
|
||||
// 2. Both gcc and clang can autovectorize this, with better performance than using Universal intrinsic.
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE) && !defined(__aarch64__) && !defined(_M_ARM64) && !(CV_TRY_RVV && CV_RVV)
|
||||
#if (CV_SIMD || CV_SIMD_SCALABLE) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !(CV_TRY_RVV && CV_RVV)
|
||||
int x = 0;
|
||||
if( scn == 3 && dcn == 3 )
|
||||
{
|
||||
|
||||
@@ -316,7 +316,7 @@ double norm( InputArray _src, int normType, InputArray _mask )
|
||||
}
|
||||
|
||||
NormFunc func = getNormFunc(normType >> 1, depth);
|
||||
CV_Assert( func != 0 );
|
||||
CV_Assert( (normType >> 1) >= 3 || func != 0 );
|
||||
|
||||
if( src.isContinuous() && mask.empty() )
|
||||
{
|
||||
|
||||
@@ -1525,6 +1525,8 @@ NormFunc getNormFunc(int normType, int depth)
|
||||
}
|
||||
};
|
||||
|
||||
if (normType >= 3 || normType < 0) return nullptr;
|
||||
|
||||
return normTab[normType][depth];
|
||||
}
|
||||
|
||||
|
||||
@@ -947,7 +947,7 @@ int getNumberOfCPUs_()
|
||||
#if defined _WIN32
|
||||
|
||||
SYSTEM_INFO sysinfo = {};
|
||||
#if (defined(_M_ARM) || defined(_M_ARM64) || defined(_M_X64) || defined(WINRT)) && _WIN32_WINNT >= 0x501
|
||||
#if (defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) || defined(_M_X64) || defined(WINRT)) && _WIN32_WINNT >= 0x501
|
||||
GetNativeSystemInfo( &sysinfo );
|
||||
#else
|
||||
GetSystemInfo( &sysinfo );
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "precomp.hpp"
|
||||
//#include "opencv2/core/core_c.h"
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
@@ -126,7 +127,6 @@ void* allocSingletonNewBuffer(size_t size) { return malloc(size); }
|
||||
#endif
|
||||
|
||||
#ifdef CV_ERROR_SET_TERMINATE_HANDLER
|
||||
#include <exception> // std::set_terminate
|
||||
#include <cstdlib> // std::abort
|
||||
#endif
|
||||
|
||||
@@ -728,7 +728,7 @@ struct HWFeatures
|
||||
#if defined _ARM_ && (defined(_WIN32_WCE) && _WIN32_WCE >= 0x800)
|
||||
have[CV_CPU_NEON] = true;
|
||||
#endif
|
||||
#if defined _M_ARM64
|
||||
#if defined _M_ARM64 || defined _M_ARM64EC
|
||||
have[CV_CPU_NEON] = true;
|
||||
#endif
|
||||
#ifdef __riscv_vector
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <errno.h>
|
||||
#include <io.h>
|
||||
#include <stdio.h>
|
||||
#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __GNU__ || defined __EMSCRIPTEN__ || defined __QNX__
|
||||
#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __GNU__ || defined __EMSCRIPTEN__ || defined __QNX__ || defined __CYGWIN__
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
@@ -194,7 +194,7 @@ cv::String getcwd()
|
||||
sz = GetCurrentDirectoryA((DWORD)buf.size(), buf.data());
|
||||
return cv::String(buf.data(), (size_t)sz);
|
||||
#endif
|
||||
#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __EMSCRIPTEN__ || defined __QNX__
|
||||
#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __EMSCRIPTEN__ || defined __QNX__ || defined __CYGWIN__
|
||||
for(;;)
|
||||
{
|
||||
char* p = ::getcwd(buf.data(), buf.size());
|
||||
@@ -228,7 +228,7 @@ bool createDirectory(const cv::String& path)
|
||||
#else
|
||||
int result = _mkdir(path.c_str());
|
||||
#endif
|
||||
#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __EMSCRIPTEN__ || defined __QNX__
|
||||
#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __EMSCRIPTEN__ || defined __QNX__ || defined __CYGWIN__
|
||||
int result = mkdir(path.c_str(), 0777);
|
||||
#else
|
||||
int result = -1;
|
||||
@@ -343,7 +343,7 @@ private:
|
||||
Impl& operator=(const Impl&); // disabled
|
||||
};
|
||||
|
||||
#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __GNU__ || defined __EMSCRIPTEN__ || defined __QNX__
|
||||
#elif defined __linux__ || defined __APPLE__ || defined __HAIKU__ || defined __FreeBSD__ || defined __GNU__ || defined __EMSCRIPTEN__ || defined __QNX__ || defined __CYGWIN__
|
||||
|
||||
struct FileLock::Impl
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#define OPENCV_DNN_VERSION_HPP
|
||||
|
||||
/// Use with major OpenCV version only.
|
||||
#define OPENCV_DNN_API_VERSION 20241127
|
||||
#define OPENCV_DNN_API_VERSION 20250619
|
||||
|
||||
#if !defined CV_DOXYGEN && !defined CV_STATIC_ANALYSIS && !defined CV_DNN_DONT_ADD_INLINE_NS
|
||||
#define CV__DNN_INLINE_NS __CV_CAT(dnn5_v, OPENCV_DNN_API_VERSION)
|
||||
|
||||
@@ -220,7 +220,7 @@ public:
|
||||
|
||||
const google::protobuf::UnknownFieldSet& unknownFields = msgRefl->GetUnknownFields(msg);
|
||||
bool hasData = fd->is_required() ||
|
||||
(fd->is_optional() && msgRefl->HasField(msg, fd)) ||
|
||||
(!fd->is_repeated() && !fd->is_required() && msgRefl->HasField(msg, fd)) ||
|
||||
(fd->is_repeated() && msgRefl->FieldSize(msg, fd) > 0) ||
|
||||
!unknownFields.empty();
|
||||
if (!hasData)
|
||||
|
||||
@@ -716,8 +716,12 @@ struct GeluFunctor : public BaseFunctor {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool supportBackend(int backendId, int) {
|
||||
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA || backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH;
|
||||
bool supportBackend(int backendId, int)
|
||||
{
|
||||
return backendId == DNN_BACKEND_OPENCV ||
|
||||
backendId == DNN_BACKEND_CUDA ||
|
||||
backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH ||
|
||||
backendId == DNN_BACKEND_CANN;
|
||||
}
|
||||
|
||||
void apply(const float* srcptr, float* dstptr, int stripeStart, int len, size_t planeSize, int cn0, int cn1) const {
|
||||
@@ -814,7 +818,19 @@ struct GeluFunctor : public BaseFunctor {
|
||||
const std::vector<Ptr<BackendWrapper> > &inputs,
|
||||
const std::vector<Ptr<BackendNode> >& nodes)
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "");
|
||||
auto input_wrapper = inputs[0].dynamicCast<CannBackendWrapper>();
|
||||
|
||||
auto op = std::make_shared<ge::op::Gelu>(name);
|
||||
|
||||
auto input_node = nodes[0].dynamicCast<CannBackendNode>()->getOp();
|
||||
op->set_input_x_by_name(*input_node, input_wrapper->name.c_str());
|
||||
auto input_desc = input_wrapper->getTensorDesc();
|
||||
op->update_input_desc_x(*input_desc);
|
||||
|
||||
auto output_desc = std::make_shared<ge::TensorDesc>(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT);
|
||||
op->update_output_desc_y(*output_desc);
|
||||
|
||||
return Ptr<BackendNode>(new CannBackendNode(op));
|
||||
}
|
||||
#endif // HAVE_CANN
|
||||
|
||||
@@ -1555,7 +1571,9 @@ struct SqrtFunctor : public BaseDefaultFunctor<SqrtFunctor>
|
||||
|
||||
bool supportBackend(int backendId, int)
|
||||
{
|
||||
return backendId == DNN_BACKEND_OPENCV || backendId == DNN_BACKEND_CUDA;
|
||||
return backendId == DNN_BACKEND_OPENCV ||
|
||||
backendId == DNN_BACKEND_CUDA ||
|
||||
backendId == DNN_BACKEND_CANN;
|
||||
}
|
||||
|
||||
inline float calculate(float x) const
|
||||
|
||||
@@ -254,7 +254,7 @@ public:
|
||||
if (backendId == DNN_BACKEND_CANN)
|
||||
return op == OPERATION::ADD || op == OPERATION::PROD || op == OPERATION::SUB ||
|
||||
op == OPERATION::DIV || op == OPERATION::MAX || op == OPERATION::MIN ||
|
||||
op == OPERATION::MOD || op == OPERATION::FMOD;
|
||||
op == OPERATION::MOD || op == OPERATION::FMOD || op == OPERATION::POW;
|
||||
#endif
|
||||
if (backendId == DNN_BACKEND_INFERENCE_ENGINE_NGRAPH)
|
||||
return (op == OPERATION::ADD ||
|
||||
@@ -1036,6 +1036,7 @@ public:
|
||||
BUILD_CANN_ELTWISE_OP(OPERATION::MIN, Minimum, name);
|
||||
BUILD_CANN_ELTWISE_OP(OPERATION::MOD, Mod, name);
|
||||
BUILD_CANN_ELTWISE_OP(OPERATION::FMOD, Mod, name);
|
||||
BUILD_CANN_ELTWISE_OP(OPERATION::POW, Pow, name);
|
||||
#undef BUILD_CANN_ELTWISE_OP
|
||||
default: CV_Error(Error::StsNotImplemented, "Unsupported eltwise operation");
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "../precomp.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
#include "../op_cann.hpp"
|
||||
|
||||
|
||||
namespace cv { namespace dnn {
|
||||
|
||||
@@ -54,6 +56,13 @@ public:
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId) CV_OVERRIDE {
|
||||
#ifdef HAVE_CANN
|
||||
if (backendId == DNN_BACKEND_CANN)
|
||||
return reduce_type == ReduceType::MAX || reduce_type == ReduceType::MIN ||
|
||||
reduce_type == ReduceType::MEAN || reduce_type == ReduceType::SUM ||
|
||||
reduce_type == ReduceType::PROD || reduce_type == ReduceType::LOG_SUM ||
|
||||
reduce_type == ReduceType::LOG_SUM_EXP;
|
||||
#endif
|
||||
return backendId == DNN_BACKEND_OPENCV;
|
||||
}
|
||||
|
||||
@@ -523,6 +532,53 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_CANN
|
||||
virtual Ptr<BackendNode> initCann(const std::vector<Ptr<BackendWrapper> > &inputs,
|
||||
const std::vector<Ptr<BackendWrapper> > &outputs,
|
||||
const std::vector<Ptr<BackendNode> >& nodes) CV_OVERRIDE
|
||||
{
|
||||
CV_CheckFalse(axes.empty(), "DNN/CANN: Reduce layers need axes to build CANN operators");
|
||||
|
||||
auto input_node = nodes[0].dynamicCast<CannBackendNode>()->getOp();
|
||||
auto input_wrapper = inputs[0].dynamicCast<CannBackendWrapper>();
|
||||
auto input_desc = input_wrapper->getTensorDesc();
|
||||
|
||||
std::vector<int> axes_shape{(int)axes.size()};
|
||||
Mat axes_mat(axes_shape, CV_32S, &axes[0]);
|
||||
auto axes_node = std::make_shared<CannConstOp>(axes_mat.data, axes_mat.type(), axes_shape, cv::format("%s_axes", name.c_str()));
|
||||
auto axes_desc = axes_node->getTensorDesc();
|
||||
|
||||
auto output_desc = std::make_shared<ge::TensorDesc>(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT);
|
||||
|
||||
std::shared_ptr<ge::Operator> reduce_op = nullptr;
|
||||
switch (reduce_type)
|
||||
{
|
||||
#define BUILD_CANN_REDUCE_OP(op_type, class_name, op_name) \
|
||||
case op_type: { \
|
||||
auto op = std::make_shared<ge::op::class_name>(op_name); \
|
||||
op->set_input_x_by_name(*input_node, input_wrapper->name.c_str()); \
|
||||
op->set_input_axes(*(axes_node)->getOp()); \
|
||||
op->set_attr_keep_dims(keepdims); \
|
||||
op->update_input_desc_x(*input_desc); \
|
||||
op->update_input_desc_axes(*axes_desc); \
|
||||
op->update_output_desc_y(*output_desc); \
|
||||
reduce_op = op; \
|
||||
} break;
|
||||
BUILD_CANN_REDUCE_OP(ReduceType::MAX, ReduceMax, name);
|
||||
BUILD_CANN_REDUCE_OP(ReduceType::MIN, ReduceMin, name);
|
||||
BUILD_CANN_REDUCE_OP(ReduceType::MEAN, ReduceMean, name);
|
||||
BUILD_CANN_REDUCE_OP(ReduceType::SUM, ReduceSum, name);
|
||||
BUILD_CANN_REDUCE_OP(ReduceType::PROD, ReduceProd, name);
|
||||
BUILD_CANN_REDUCE_OP(ReduceType::LOG_SUM, ReduceLogSum, name);
|
||||
BUILD_CANN_REDUCE_OP(ReduceType::LOG_SUM_EXP, ReduceLogSumExp, name);
|
||||
#undef BUILD_CANN_REDUCE_OP
|
||||
default: CV_Error(Error::StsNotImplemented, "Unsupported reduce operation");
|
||||
}
|
||||
|
||||
return Ptr<BackendNode>(new CannBackendNode(reduce_op));
|
||||
}
|
||||
#endif // HAVE_CANN
|
||||
|
||||
private:
|
||||
enum ReduceType
|
||||
{
|
||||
|
||||
@@ -184,6 +184,16 @@ public:
|
||||
for (i = 0; i < dims; i++)
|
||||
newShapeDesc[i] = paramShape.get<int>(i);
|
||||
}
|
||||
if (params.has("unsqueeze_axes"))
|
||||
{
|
||||
const DictValue& param_unsqueeze_axes = params.get("unsqueeze_axes");
|
||||
int len_axes = param_unsqueeze_axes.size();
|
||||
unsqueeze_axes.resize(len_axes);
|
||||
for (int i = 0; i < len_axes; ++i)
|
||||
{
|
||||
unsqueeze_axes[i] = (int64_t)param_unsqueeze_axes.get<int>(i);
|
||||
}
|
||||
}
|
||||
if (hasDynamicShapes)
|
||||
{
|
||||
dynamicShapes.clear();
|
||||
@@ -349,33 +359,56 @@ public:
|
||||
const std::vector<Ptr<BackendWrapper> > &outputs,
|
||||
const std::vector<Ptr<BackendNode> >& nodes) CV_OVERRIDE
|
||||
{
|
||||
auto x = inputs[0].dynamicCast<CannBackendWrapper>();
|
||||
auto input_wrapper = inputs[0].dynamicCast<CannBackendWrapper>();
|
||||
|
||||
// create operator
|
||||
auto op = std::make_shared<ge::op::Reshape>(name);
|
||||
if (!unsqueeze_axes.empty())
|
||||
{
|
||||
auto op = std::make_shared<ge::op::Unsqueeze>(name);
|
||||
|
||||
// set attributes
|
||||
op->set_attr_axis(axis);
|
||||
op->set_attr_num_axes(numAxes);
|
||||
// set attributes
|
||||
op->set_attr_axes(unsqueeze_axes);
|
||||
|
||||
// set inputs
|
||||
// set inputs : x
|
||||
auto op_x = nodes[0].dynamicCast<CannBackendNode>()->getOp();
|
||||
op->set_input_x_by_name(*op_x, x->name.c_str());
|
||||
auto x_desc = x->getTensorDesc();
|
||||
op->update_input_desc_x(*x_desc);
|
||||
// set inputs : shape
|
||||
std::vector<int> shape_of_shape{(int)newShapeDesc.size()};
|
||||
Mat shape_mat(shape_of_shape, CV_32S, newShapeDesc.data());
|
||||
auto op_const_shape = std::make_shared<CannConstOp>(shape_mat.data, shape_mat.type(), shape_of_shape, cv::format("%s_shape", name.c_str()));
|
||||
op->set_input_shape(*(op_const_shape->getOp()));
|
||||
op->update_input_desc_shape(*(op_const_shape->getTensorDesc()));
|
||||
// set inputs
|
||||
// set inputs : x
|
||||
auto input_node = nodes[0].dynamicCast<CannBackendNode>()->getOp();
|
||||
op->set_input_x_by_name(*input_node, input_wrapper->name.c_str());
|
||||
auto input_desc = input_wrapper->getTensorDesc();
|
||||
op->update_input_desc_x(*input_desc);
|
||||
|
||||
// set outputs
|
||||
auto output_y_desc = std::make_shared<ge::TensorDesc>(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT);
|
||||
op->update_output_desc_y(*output_y_desc);
|
||||
// set outputs
|
||||
auto desc_y = std::make_shared<ge::TensorDesc>(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT);
|
||||
op->update_output_desc_y(*desc_y);
|
||||
|
||||
return Ptr<BackendNode>(new CannBackendNode(op));
|
||||
return Ptr<BackendNode>(new CannBackendNode(op));
|
||||
}
|
||||
else
|
||||
{
|
||||
// create operator
|
||||
auto op = std::make_shared<ge::op::Reshape>(name);
|
||||
|
||||
// set attributes
|
||||
op->set_attr_axis(axis);
|
||||
op->set_attr_num_axes(numAxes);
|
||||
|
||||
// set inputs
|
||||
// set inputs : x
|
||||
auto input_node = nodes[0].dynamicCast<CannBackendNode>()->getOp();
|
||||
op->set_input_x_by_name(*input_node, input_wrapper->name.c_str());
|
||||
auto input_desc = input_wrapper->getTensorDesc();
|
||||
op->update_input_desc_x(*input_desc);
|
||||
// set inputs : shape
|
||||
std::vector<int> shape_of_shape{(int)newShapeDesc.size()};
|
||||
Mat shape_mat(shape_of_shape, CV_32S, newShapeDesc.data());
|
||||
auto op_const_shape = std::make_shared<CannConstOp>(shape_mat.data, shape_mat.type(), shape_of_shape, cv::format("%s_shape", name.c_str()));
|
||||
op->set_input_shape(*(op_const_shape->getOp()));
|
||||
op->update_input_desc_shape(*(op_const_shape->getTensorDesc()));
|
||||
|
||||
// set outputs
|
||||
auto desc_y = std::make_shared<ge::TensorDesc>(ge::Shape(), ge::FORMAT_NCHW, ge::DT_FLOAT);
|
||||
op->update_output_desc_y(*desc_y);
|
||||
|
||||
return Ptr<BackendNode>(new CannBackendNode(op));
|
||||
}
|
||||
}
|
||||
#endif // HAVE_CANN
|
||||
|
||||
@@ -524,6 +557,7 @@ private:
|
||||
bool shapesInitialized;
|
||||
float scale;
|
||||
int zeropoint;
|
||||
std::vector<int64_t> unsqueeze_axes;
|
||||
};
|
||||
|
||||
Ptr<ReshapeLayer> ReshapeLayer::create(const LayerParams& params)
|
||||
|
||||
@@ -678,7 +678,7 @@ public:
|
||||
auto op = std::make_shared<ge::op::SplitV>(name);
|
||||
|
||||
// set attr
|
||||
int n_split = static_cast<int>(sliceRanges[0].size());
|
||||
int n_split = static_cast<int>(outputs.size());
|
||||
op->set_attr_num_split(n_split);
|
||||
|
||||
// set inputs
|
||||
|
||||
@@ -2312,6 +2312,8 @@ void ONNXImporter::parseUnsqueeze(LayerParams& layerParams, const opencv_onnx::N
|
||||
if (axes.size() != 1)
|
||||
CV_Error(Error::StsNotImplemented, "Multidimensional unsqueeze");
|
||||
|
||||
layerParams.set("unsqueeze_axes", axes);
|
||||
|
||||
int depth = layerParams.get<int>("depth", CV_32F);
|
||||
|
||||
MatShape inpShape = outShapes[node_proto.input(0)];
|
||||
|
||||
@@ -61,14 +61,14 @@ CannConstOp::CannConstOp(const uint8_t* data, const int dtype, const std::vector
|
||||
{
|
||||
case CV_32F: break;
|
||||
case CV_32S: ge_dtype = ge::DT_INT32; break;
|
||||
default: CV_Error(Error::StsNotImplemented, "Unsupported data type");
|
||||
default: CV_Error(Error::StsNotImplemented, cv::format("Unsupported data type %d of node %s", dtype, name.c_str()));
|
||||
}
|
||||
auto size_of_type = sizeof(float);
|
||||
switch (dtype)
|
||||
{
|
||||
case CV_32F: break;
|
||||
case CV_32S: size_of_type = sizeof(int); break;
|
||||
default: CV_Error(Error::StsNotImplemented, "Unsupported data type");
|
||||
default: CV_Error(Error::StsNotImplemented, cv::format("Unsupported data type %d of node %s", dtype, name.c_str()));
|
||||
}
|
||||
desc_ = std::make_shared<ge::TensorDesc>(ge_shape, ge::FORMAT_NCHW, ge_dtype);
|
||||
auto ge_tensor = std::make_shared<ge::Tensor>();
|
||||
|
||||
@@ -369,7 +369,7 @@ cv::String getInferenceEngineCPUType()
|
||||
{
|
||||
auto& networkBackend = dnn_backend::createPluginDNNNetworkBackend("openvino");
|
||||
CV_UNUSED(networkBackend);
|
||||
#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM64)
|
||||
#if defined(__arm__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
|
||||
return CV_DNN_INFERENCE_ENGINE_CPU_TYPE_ARM_COMPUTE;
|
||||
#else
|
||||
return CV_DNN_INFERENCE_ENGINE_CPU_TYPE_X86;
|
||||
|
||||
@@ -54,7 +54,7 @@ NNIndex<Distance>* create_index_by_type(const Matrix<typename Distance::ElementT
|
||||
|
||||
struct AutotunedIndexParams : public IndexParams
|
||||
{
|
||||
AutotunedIndexParams(float target_precision = 0.8, float build_weight = 0.01, float memory_weight = 0, float sample_fraction = 0.1)
|
||||
AutotunedIndexParams(float target_precision = 0.8f, float build_weight = 0.01f, float memory_weight = 0.f, float sample_fraction = 0.1f)
|
||||
{
|
||||
(*this)["algorithm"] = FLANN_INDEX_AUTOTUNED;
|
||||
// precision desired (used for autotuning, -1 otherwise)
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace cvflann
|
||||
struct CompositeIndexParams : public IndexParams
|
||||
{
|
||||
CompositeIndexParams(int trees = 4, int branching = 32, int iterations = 11,
|
||||
flann_centers_init_t centers_init = FLANN_CENTERS_RANDOM, float cb_index = 0.2 )
|
||||
flann_centers_init_t centers_init = FLANN_CENTERS_RANDOM, float cb_index = 0.2f )
|
||||
{
|
||||
(*this)["algorithm"] = FLANN_INDEX_KMEANS;
|
||||
// number of randomized trees to use (for kdtree)
|
||||
|
||||
@@ -45,7 +45,7 @@ typedef unsigned __int64 uint64_t;
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#if defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64))
|
||||
#if defined _WIN32 && (defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC))
|
||||
# include <Intrin.h>
|
||||
#endif
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ struct KMeansIndexParams : public IndexParams
|
||||
{
|
||||
KMeansIndexParams(int branching = 32, int iterations = 11,
|
||||
flann_centers_init_t centers_init = FLANN_CENTERS_RANDOM,
|
||||
float cb_index = 0.2, int trees = 1 )
|
||||
float cb_index = 0.2f, int trees = 1 )
|
||||
{
|
||||
(*this)["algorithm"] = FLANN_INDEX_KMEANS;
|
||||
// branching factor
|
||||
|
||||
@@ -78,7 +78,7 @@ using namespace cv;
|
||||
static const char* trackbar_text =
|
||||
" ";
|
||||
|
||||
#if defined _M_X64 || defined __x86_64 || defined _M_ARM64
|
||||
#if defined _M_X64 || defined __x86_64 || defined _M_ARM64 || defined _M_ARM64EC
|
||||
|
||||
#define icvGetWindowLongPtr GetWindowLongPtr
|
||||
#define icvSetWindowLongPtr(hwnd, id, ptr) SetWindowLongPtr(hwnd, id, (LONG_PTR)(ptr))
|
||||
|
||||
@@ -23,11 +23,6 @@ if(HAVE_JPEG)
|
||||
list(APPEND GRFMT_LIBS ${JPEG_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(HAVE_JPEGXL)
|
||||
ocv_include_directories(${OPENJPEG_INCLUDE_DIRS})
|
||||
list(APPEND GRFMT_LIBS ${OPENJPEG_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(HAVE_WEBP)
|
||||
add_definitions(-DHAVE_WEBP)
|
||||
ocv_include_directories(${WEBP_INCLUDE_DIR})
|
||||
|
||||
@@ -251,6 +251,15 @@ enum ImwriteGIFCompressionFlags {
|
||||
IMWRITE_GIF_COLORTABLE_SIZE_256 = 8
|
||||
};
|
||||
|
||||
enum ImageMetadataType
|
||||
{
|
||||
IMAGE_METADATA_UNKNOWN = -1,
|
||||
IMAGE_METADATA_EXIF = 0,
|
||||
IMAGE_METADATA_XMP = 1,
|
||||
IMAGE_METADATA_ICCP = 2,
|
||||
IMAGE_METADATA_MAX = 2
|
||||
};
|
||||
|
||||
//! @} imgcodecs_flags
|
||||
|
||||
/** @brief Represents an animation with multiple frames.
|
||||
@@ -277,6 +286,8 @@ struct CV_EXPORTS_W_SIMPLE Animation
|
||||
CV_PROP_RW std::vector<int> durations;
|
||||
//! Vector of frames, where each Mat represents a single frame.
|
||||
CV_PROP_RW std::vector<Mat> frames;
|
||||
//! image that can be used for the format in addition to the animation or if animation is not supported in the reader (like in PNG).
|
||||
CV_PROP_RW Mat still_image;
|
||||
|
||||
/** @brief Constructs an Animation object with optional loop count and background color.
|
||||
|
||||
@@ -358,6 +369,17 @@ The image passing through the img parameter can be pre-allocated. The memory is
|
||||
*/
|
||||
CV_EXPORTS_W void imread( const String& filename, OutputArray dst, int flags = IMREAD_COLOR_BGR );
|
||||
|
||||
/** @brief Reads an image from a file together with associated metadata.
|
||||
|
||||
The function imreadWithMetadata reads image from the specified file. It does the same thing as imread, but additionally reads metadata if the corresponding file contains any.
|
||||
@param filename Name of the file to be loaded.
|
||||
@param metadataTypes Output vector with types of metadata chucks returned in metadata, see ImageMetadataType.
|
||||
@param metadata Output vector of vectors or vector of matrices to store the retrieved metadata
|
||||
@param flags Flag that can take values of cv::ImreadModes
|
||||
*/
|
||||
CV_EXPORTS_W Mat imreadWithMetadata( const String& filename, CV_OUT std::vector<int>& metadataTypes,
|
||||
OutputArrayOfArrays metadata, int flags = IMREAD_ANYCOLOR);
|
||||
|
||||
/** @brief Loads a multi-page image from a file.
|
||||
|
||||
The function imreadmulti loads a multi-page image from the specified file into a vector of Mat objects.
|
||||
@@ -462,7 +484,7 @@ filename extension (see cv::imread for the list of extensions). In general, only
|
||||
single-channel or 3-channel (with 'BGR' channel order) images
|
||||
can be saved using this function, with these exceptions:
|
||||
|
||||
- With OpenEXR encoder, only 32-bit float (CV_32F) images can be saved.
|
||||
- With OpenEXR encoder, only 32-bit float (CV_32F) images can be saved. More than 4 channels can be saved. (imread can load it then.)
|
||||
- 8-bit unsigned (CV_8U) images are not supported.
|
||||
- With Radiance HDR encoder, non 64-bit float (CV_64F) images can be saved.
|
||||
- All images will be converted to 32-bit float (CV_32F).
|
||||
@@ -507,6 +529,20 @@ It also demonstrates how to save multiple images in a TIFF file:
|
||||
CV_EXPORTS_W bool imwrite( const String& filename, InputArray img,
|
||||
const std::vector<int>& params = std::vector<int>());
|
||||
|
||||
/** @brief Saves an image to a specified file with metadata
|
||||
|
||||
The function imwriteWithMetadata saves the image to the specified file. It does the same thing as imwrite, but additionally writes metadata if the corresponding format supports it.
|
||||
@param filename Name of the file. As with imwrite, image format is determined by the file extension.
|
||||
@param img (Mat or vector of Mat) Image or Images to be saved.
|
||||
@param metadataTypes Vector with types of metadata chucks stored in metadata to write, see ImageMetadataType.
|
||||
@param metadata Vector of vectors or vector of matrices with chunks of metadata to store into the file
|
||||
@param params Format-specific parameters encoded as pairs (paramId_1, paramValue_1, paramId_2, paramValue_2, ... .) see cv::ImwriteFlags
|
||||
*/
|
||||
CV_EXPORTS_W bool imwriteWithMetadata( const String& filename, InputArray img,
|
||||
const std::vector<int>& metadataTypes,
|
||||
InputArrayOfArrays& metadata,
|
||||
const std::vector<int>& params = std::vector<int>());
|
||||
|
||||
//! @brief multi-image overload for bindings
|
||||
CV_WRAP static inline
|
||||
bool imwritemulti(const String& filename, InputArrayOfArrays img,
|
||||
@@ -528,6 +564,22 @@ See cv::imread for the list of supported formats and flags description.
|
||||
*/
|
||||
CV_EXPORTS_W Mat imdecode( InputArray buf, int flags );
|
||||
|
||||
/** @brief Reads an image from a buffer in memory together with associated metadata.
|
||||
|
||||
The function imdecode reads an image from the specified buffer in the memory. If the buffer is too short or
|
||||
contains invalid data, the function returns an empty matrix ( Mat::data==NULL ).
|
||||
|
||||
See cv::imread for the list of supported formats and flags description.
|
||||
|
||||
@note In the case of color images, the decoded images will have the channels stored in **B G R** order.
|
||||
@param buf Input array or vector of bytes.
|
||||
@param metadataTypes Output vector with types of metadata chucks returned in metadata, see ImageMetadataType.
|
||||
@param metadata Output vector of vectors or vector of matrices to store the retrieved metadata
|
||||
@param flags The same flags as in cv::imread, see cv::ImreadModes.
|
||||
*/
|
||||
CV_EXPORTS_W Mat imdecodeWithMetadata( InputArray buf, CV_OUT std::vector<int>& metadataTypes,
|
||||
OutputArrayOfArrays metadata, int flags = IMREAD_ANYCOLOR );
|
||||
|
||||
/** @overload
|
||||
@param buf Input array or vector of bytes.
|
||||
@param flags The same flags as in cv::imread, see cv::ImreadModes.
|
||||
@@ -566,6 +618,24 @@ CV_EXPORTS_W bool imencode( const String& ext, InputArray img,
|
||||
CV_OUT std::vector<uchar>& buf,
|
||||
const std::vector<int>& params = std::vector<int>());
|
||||
|
||||
/** @brief Encodes an image into a memory buffer.
|
||||
|
||||
The function imencode compresses the image and stores it in the memory buffer that is resized to fit the
|
||||
result. See cv::imwrite for the list of supported formats and flags description.
|
||||
|
||||
@param ext File extension that defines the output format. Must include a leading period.
|
||||
@param img Image to be compressed.
|
||||
@param metadataTypes Vector with types of metadata chucks stored in metadata to write, see ImageMetadataType.
|
||||
@param metadata Vector of vectors or vector of matrices with chunks of metadata to store into the file
|
||||
@param buf Output buffer resized to fit the compressed image.
|
||||
@param params Format-specific parameters. See cv::imwrite and cv::ImwriteFlags.
|
||||
*/
|
||||
CV_EXPORTS_W bool imencodeWithMetadata( const String& ext, InputArray img,
|
||||
const std::vector<int>& metadataTypes,
|
||||
InputArrayOfArrays metadata,
|
||||
CV_OUT std::vector<uchar>& buf,
|
||||
const std::vector<int>& params = std::vector<int>());
|
||||
|
||||
/** @brief Encodes array of images into a memory buffer.
|
||||
|
||||
The function is analog to cv::imencode for in-memory multi-page image compression.
|
||||
@@ -589,7 +659,7 @@ This can be useful for verifying support for a given image format before attempt
|
||||
@return true if an image reader for the specified file is available and the file can be opened, false otherwise.
|
||||
|
||||
@note The function checks the availability of image codecs that are either built into OpenCV or dynamically loaded.
|
||||
It does not check for the actual existence of the file but rather the ability to read the specified file type.
|
||||
It does not load the image codec implementation and decode data, but uses signature check.
|
||||
If the file cannot be opened or the format is unsupported, the function will return false.
|
||||
|
||||
@sa cv::haveImageWriter, cv::imread, cv::imdecode
|
||||
|
||||
@@ -15,6 +15,10 @@ import java.util.List;
|
||||
public class ImgcodecsTest extends OpenCVTestCase {
|
||||
|
||||
public void testAnimation() {
|
||||
if (!Imgcodecs.haveImageWriter("*.apng")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Mat src = Imgcodecs.imread(OpenCVTestRunner.LENA_PATH, Imgcodecs.IMREAD_REDUCED_COLOR_4);
|
||||
assertFalse(src.empty());
|
||||
|
||||
|
||||
@@ -94,6 +94,10 @@ ExifEntry_t ExifReader::getTag(const ExifTagName tag) const
|
||||
return entry;
|
||||
}
|
||||
|
||||
const std::vector<unsigned char>& ExifReader::getData() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parsing the exif data buffer and prepare (internal) exif directory
|
||||
|
||||
@@ -175,6 +175,10 @@ public:
|
||||
*/
|
||||
ExifEntry_t getTag( const ExifTagName tag ) const;
|
||||
|
||||
/**
|
||||
* @brief Get the whole exif buffer
|
||||
*/
|
||||
const std::vector<unsigned char>& getData() const;
|
||||
|
||||
private:
|
||||
std::vector<unsigned char> m_data;
|
||||
|
||||
@@ -68,8 +68,8 @@ avifResult CopyToMat(const avifImage *image, int channels, bool useRGB , Mat *ma
|
||||
return avifImageYUVToRGB(image, &rgba);
|
||||
}
|
||||
|
||||
AvifImageUniquePtr ConvertToAvif(const cv::Mat &img, bool lossless,
|
||||
int bit_depth) {
|
||||
AvifImageUniquePtr ConvertToAvif(const cv::Mat &img, bool lossless, int bit_depth,
|
||||
const std::vector<std::vector<uchar> >& metadata) {
|
||||
CV_Assert(img.depth() == CV_8U || img.depth() == CV_16U);
|
||||
|
||||
const int width = img.cols;
|
||||
@@ -112,6 +112,33 @@ AvifImageUniquePtr ConvertToAvif(const cv::Mat &img, bool lossless,
|
||||
result->yuvRange = AVIF_RANGE_FULL;
|
||||
}
|
||||
|
||||
if (!metadata.empty()) {
|
||||
const std::vector<uchar>& metadata_exif = metadata[IMAGE_METADATA_EXIF];
|
||||
const std::vector<uchar>& metadata_xmp = metadata[IMAGE_METADATA_XMP];
|
||||
const std::vector<uchar>& metadata_iccp = metadata[IMAGE_METADATA_ICCP];
|
||||
#if AVIF_VERSION_MAJOR >= 1
|
||||
if ((!metadata_exif.empty() &&
|
||||
avifImageSetMetadataExif(result, (const uint8_t *)metadata_exif.data(),
|
||||
metadata_exif.size()) != AVIF_RESULT_OK) ||
|
||||
(!metadata_xmp.empty() &&
|
||||
avifImageSetMetadataXMP(result, (const uint8_t *)metadata_xmp.data(),
|
||||
metadata_xmp.size()) != AVIF_RESULT_OK) ||
|
||||
(!metadata_iccp.empty() &&
|
||||
avifImageSetProfileICC(result, (const uint8_t *)metadata_iccp.data(),
|
||||
metadata_iccp.size()) != AVIF_RESULT_OK)) {
|
||||
avifImageDestroy(result);
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
if (!metadata_exif.empty())
|
||||
avifImageSetMetadataExif(result, (const uint8_t*)metadata_exif.data(), metadata_exif.size());
|
||||
if (!metadata_xmp.empty())
|
||||
avifImageSetMetadataXMP(result, (const uint8_t*)metadata_xmp.data(), metadata_xmp.size());
|
||||
if (!metadata_iccp.empty())
|
||||
avifImageSetProfileICC(result, (const uint8_t*)metadata_iccp.data(), metadata_iccp.size());
|
||||
#endif
|
||||
}
|
||||
|
||||
avifRGBImage rgba;
|
||||
avifRGBImageSetDefaults(&rgba, result);
|
||||
if (img.channels() == 3) {
|
||||
@@ -120,7 +147,7 @@ AvifImageUniquePtr ConvertToAvif(const cv::Mat &img, bool lossless,
|
||||
CV_Assert(img.channels() == 4);
|
||||
rgba.format = AVIF_RGB_FORMAT_BGRA;
|
||||
}
|
||||
rgba.rowBytes = img.step[0];
|
||||
rgba.rowBytes = (uint32_t)img.step[0];
|
||||
rgba.depth = bit_depth;
|
||||
rgba.pixels =
|
||||
const_cast<uint8_t *>(reinterpret_cast<const uint8_t *>(img.data));
|
||||
@@ -287,6 +314,10 @@ bool AvifDecoder::nextPage() {
|
||||
AvifEncoder::AvifEncoder() {
|
||||
m_description = "AVIF files (*.avif)";
|
||||
m_buf_supported = true;
|
||||
m_support_metadata.assign((size_t)IMAGE_METADATA_MAX + 1, false);
|
||||
m_support_metadata[(size_t)IMAGE_METADATA_EXIF] = true;
|
||||
m_support_metadata[(size_t)IMAGE_METADATA_XMP] = true;
|
||||
m_support_metadata[(size_t)IMAGE_METADATA_ICCP] = true;
|
||||
encoder_ = avifEncoderCreate();
|
||||
}
|
||||
|
||||
@@ -349,7 +380,7 @@ bool AvifEncoder::writeanimation(const Animation& animation,
|
||||
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));
|
||||
images.emplace_back(ConvertToAvif(img, do_lossless, bit_depth, m_metadata));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < images.size(); i++)
|
||||
|
||||
@@ -58,11 +58,30 @@ BaseImageDecoder::BaseImageDecoder()
|
||||
m_frame_count = 1;
|
||||
}
|
||||
|
||||
bool BaseImageDecoder::haveMetadata(ImageMetadataType type) const
|
||||
{
|
||||
if (type == IMAGE_METADATA_EXIF)
|
||||
return !m_exif.getData().empty();
|
||||
return false;
|
||||
}
|
||||
|
||||
Mat BaseImageDecoder::getMetadata(ImageMetadataType type) const
|
||||
{
|
||||
if (type == IMAGE_METADATA_EXIF) {
|
||||
const std::vector<unsigned char>& exif = m_exif.getData();
|
||||
if (!exif.empty()) {
|
||||
Mat exifmat(1, (int)exif.size(), CV_8U, (void*)exif.data());
|
||||
return exifmat;
|
||||
}
|
||||
}
|
||||
return Mat();
|
||||
}
|
||||
|
||||
ExifEntry_t BaseImageDecoder::getExifTag(const ExifTagName tag) const
|
||||
{
|
||||
return m_exif.getTag(tag);
|
||||
}
|
||||
|
||||
bool BaseImageDecoder::setSource( const String& filename )
|
||||
{
|
||||
m_filename = filename;
|
||||
@@ -140,6 +159,23 @@ bool BaseImageEncoder::setDestination( std::vector<uchar>& buf )
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseImageEncoder::addMetadata(ImageMetadataType type, const Mat& metadata)
|
||||
{
|
||||
CV_Assert_N(type >= IMAGE_METADATA_EXIF, type <= IMAGE_METADATA_MAX);
|
||||
if (metadata.empty())
|
||||
return true;
|
||||
size_t itype = (size_t)type;
|
||||
if (itype >= m_support_metadata.size() || !m_support_metadata[itype])
|
||||
return false;
|
||||
if (m_metadata.empty())
|
||||
m_metadata.resize((size_t)IMAGE_METADATA_MAX+1);
|
||||
CV_Assert(metadata.elemSize() == 1);
|
||||
CV_Assert(metadata.isContinuous());
|
||||
const unsigned char* data = metadata.ptr<unsigned char>();
|
||||
m_metadata[itype].assign(data, data + metadata.total());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseImageEncoder::write(const Mat &img, const std::vector<int> ¶ms) {
|
||||
std::vector<Mat> img_vec(1, img);
|
||||
return writemulti(img_vec, params);
|
||||
|
||||
@@ -58,12 +58,31 @@ public:
|
||||
*/
|
||||
size_t getFrameCount() const { return m_frame_count; }
|
||||
|
||||
/**
|
||||
* @brief Set the internal m_frame_count variable to 1.
|
||||
*/
|
||||
void resetFrameCount() { m_frame_count = 1; }
|
||||
|
||||
/**
|
||||
* @brief Get the type of the image (e.g., color format, depth).
|
||||
* @return The type of the image.
|
||||
*/
|
||||
virtual int type() const { return m_type; }
|
||||
|
||||
/**
|
||||
* @brief Checks whether file contains metadata of the certain type.
|
||||
* @param type The type of metadata to look for
|
||||
*/
|
||||
virtual bool haveMetadata(ImageMetadataType type) const;
|
||||
|
||||
/**
|
||||
* @brief Retrieves metadata (if any) of the certain kind.
|
||||
* If there is no such metadata, the method returns empty array.
|
||||
*
|
||||
* @param type The type of metadata to look for
|
||||
*/
|
||||
virtual Mat getMetadata(ImageMetadataType type) const;
|
||||
|
||||
/**
|
||||
* @brief Fetch a specific EXIF tag from the image's metadata.
|
||||
* @param tag The EXIF tag to retrieve.
|
||||
@@ -200,6 +219,13 @@ public:
|
||||
*/
|
||||
virtual bool setDestination(std::vector<uchar>& buf);
|
||||
|
||||
/**
|
||||
* @brief Sets the metadata to write together with the image data
|
||||
* @param type The type of metadata to add
|
||||
* @param metadata The packed metadata (Exif, XMP, ...)
|
||||
*/
|
||||
virtual bool addMetadata(ImageMetadataType type, const Mat& metadata);
|
||||
|
||||
/**
|
||||
* @brief Encode and write the image data.
|
||||
* @param img The Mat object containing the image data to be encoded.
|
||||
@@ -238,6 +264,8 @@ public:
|
||||
virtual void throwOnError() const;
|
||||
|
||||
protected:
|
||||
std::vector<std::vector<unsigned char> > m_metadata; // see IMAGE_METADATA_...
|
||||
std::vector<bool> m_support_metadata;
|
||||
String m_description; ///< Description of the encoder (e.g., format name, capabilities).
|
||||
String m_filename; ///< Destination file name for encoded data.
|
||||
std::vector<uchar>* m_buf; ///< Pointer to the buffer for encoded data if using memory-based destination.
|
||||
|
||||
@@ -97,7 +97,8 @@ ExrDecoder::ExrDecoder()
|
||||
m_ischroma = false;
|
||||
m_hasalpha = false;
|
||||
m_native_depth = false;
|
||||
|
||||
m_multispectral = false;
|
||||
m_channels = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +120,7 @@ void ExrDecoder::close()
|
||||
|
||||
int ExrDecoder::type() const
|
||||
{
|
||||
return CV_MAKETYPE((m_isfloat ? CV_32F : CV_32S), ((m_iscolor && m_hasalpha) ? 4 : m_iscolor ? 3 : m_hasalpha ? 2 : 1));
|
||||
return CV_MAKETYPE((m_isfloat ? CV_32F : CV_32S), (m_multispectral ? m_channels : (m_iscolor && m_hasalpha) ? 4 : m_iscolor ? 3 : m_hasalpha ? 2 : 1));
|
||||
}
|
||||
|
||||
|
||||
@@ -148,6 +149,7 @@ bool ExrDecoder::readHeader()
|
||||
m_green = channels.findChannel( "G" );
|
||||
m_blue = channels.findChannel( "B" );
|
||||
m_alpha = channels.findChannel( "A" );
|
||||
m_multispectral = channels.findChannel( "0" ) != nullptr;
|
||||
|
||||
if( m_alpha ) // alpha channel supported in RGB, Y, and YC scenarios
|
||||
m_hasalpha = true;
|
||||
@@ -158,6 +160,23 @@ bool ExrDecoder::readHeader()
|
||||
m_ischroma = false;
|
||||
result = true;
|
||||
}
|
||||
else if( m_multispectral )
|
||||
{
|
||||
m_channels = 0;
|
||||
for( auto it = channels.begin(); it != channels.end(); it++ )
|
||||
m_channels++;
|
||||
|
||||
m_iscolor = true; // ??? false
|
||||
m_ischroma = false;
|
||||
m_hasalpha = false;
|
||||
result = m_channels <= CV_CN_MAX;
|
||||
|
||||
for ( int i = 1; result && i < m_channels; i++ ) // channel 0 was found previously
|
||||
{
|
||||
const Channel *ch = channels.findChannel( std::to_string(i) );
|
||||
result = ch && ch->xSampling == 1 && ch->ySampling == 1; // subsampling is not supported
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_green = channels.findChannel( "Y" );
|
||||
@@ -193,8 +212,9 @@ bool ExrDecoder::readHeader()
|
||||
bool ExrDecoder::readData( Mat& img )
|
||||
{
|
||||
m_native_depth = CV_MAT_DEPTH(type()) == img.depth();
|
||||
bool multispectral = img.channels() > 4;
|
||||
bool color = img.channels() > 2; // output mat has 3+ channels; Y or YA are the 1 and 2 channel scenario
|
||||
bool alphasupported = ( img.channels() % 2 == 0 ); // even number of channels indicates alpha
|
||||
bool alphasupported = !multispectral && ( img.channels() % 2 == 0 ); // even number of channels indicates alpha
|
||||
int channels = 0;
|
||||
uchar* data = img.ptr();
|
||||
size_t step = img.step;
|
||||
@@ -210,10 +230,17 @@ bool ExrDecoder::readData( Mat& img )
|
||||
const size_t floatsize = sizeof(float);
|
||||
size_t xstep = m_native_depth ? floatsize : 1; // 4 bytes if native depth (FLOAT), otherwise converting to 1 byte U8 depth
|
||||
size_t ystep = 0;
|
||||
const int channelstoread = ( (m_iscolor && alphasupported) ? 4 :
|
||||
const int channelstoread = ( multispectral ? img.channels() : (m_iscolor && alphasupported) ? 4 :
|
||||
( (m_iscolor && !m_ischroma) || color) ? 3 : alphasupported ? 2 : 1 ); // number of channels to read may exceed channels in output img
|
||||
size_t xStride = floatsize * channelstoread;
|
||||
|
||||
if ( m_multispectral ) // possible gray/RGB conversions
|
||||
{
|
||||
CV_CheckChannelsEQ(img.channels(), CV_MAT_CN(type()), "OpenCV EXR decoder needs more number of channels for multispectral images. Use cv::IMREAD_UNCHANGED mode for imread."); // IMREAD_ANYCOLOR needed
|
||||
CV_CheckDepthEQ(img.depth(), CV_MAT_DEPTH(type()), "OpenCV EXR decoder supports CV_32F depth only for multispectral images. Use cv::IMREAD_UNCHANGED mode for imread."); // IMREAD_ANYDEPTH needed
|
||||
}
|
||||
CV_Assert( multispectral == m_multispectral && (!multispectral || justcopy) ); // should be true after previous checks
|
||||
|
||||
// See https://github.com/opencv/opencv/issues/26705
|
||||
// If ALGO_HINT_ACCURATE is set, read BGR and swap to RGB.
|
||||
// If ALGO_HINT_APPROX is set, read RGB directly.
|
||||
@@ -291,6 +318,15 @@ bool ExrDecoder::readData( Mat& img )
|
||||
xsample[0] = m_green->xSampling;
|
||||
}
|
||||
}
|
||||
else if( m_multispectral )
|
||||
{
|
||||
for ( int i = 0; i < m_channels; i++ )
|
||||
{
|
||||
frame.insert( std::to_string(i), Slice( m_type,
|
||||
buffer - m_datawindow.min.x * xStride - m_datawindow.min.y * ystep + (floatsize * i),
|
||||
xStride, ystep, 1, 1, 0.0 ));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_blue )
|
||||
@@ -361,39 +397,42 @@ bool ExrDecoder::readData( Mat& img )
|
||||
{
|
||||
m_file->readPixels( m_datawindow.min.y, m_datawindow.max.y );
|
||||
|
||||
if( m_iscolor )
|
||||
if( !m_multispectral )
|
||||
{
|
||||
if (doReadRGB)
|
||||
if( m_iscolor )
|
||||
{
|
||||
if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
|
||||
UpSample( data, channelstoread, step / xstep, m_red->xSampling, m_red->ySampling );
|
||||
if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
|
||||
UpSample( data + xstep, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling );
|
||||
if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
|
||||
UpSample( data + 2 * xstep, channelstoread, step / xstep, m_blue->xSampling, m_blue->ySampling );
|
||||
if (doReadRGB)
|
||||
{
|
||||
if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
|
||||
UpSample( data, channelstoread, step / xstep, m_red->xSampling, m_red->ySampling );
|
||||
if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
|
||||
UpSample( data + xstep, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling );
|
||||
if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
|
||||
UpSample( data + 2 * xstep, channelstoread, step / xstep, m_blue->xSampling, m_blue->ySampling );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
|
||||
UpSample( data, channelstoread, step / xstep, m_blue->xSampling, m_blue->ySampling );
|
||||
if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
|
||||
UpSample( data + xstep, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling );
|
||||
if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
|
||||
UpSample( data + 2 * xstep, channelstoread, step / xstep, m_red->xSampling, m_red->ySampling );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_blue && (m_blue->xSampling != 1 || m_blue->ySampling != 1) )
|
||||
UpSample( data, channelstoread, step / xstep, m_blue->xSampling, m_blue->ySampling );
|
||||
if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
|
||||
UpSample( data + xstep, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling );
|
||||
if( m_red && (m_red->xSampling != 1 || m_red->ySampling != 1) )
|
||||
UpSample( data + 2 * xstep, channelstoread, step / xstep, m_red->xSampling, m_red->ySampling );
|
||||
}
|
||||
}
|
||||
else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
|
||||
UpSample( data, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling );
|
||||
else if( m_green && (m_green->xSampling != 1 || m_green->ySampling != 1) )
|
||||
UpSample( data, channelstoread, step / xstep, m_green->xSampling, m_green->ySampling );
|
||||
|
||||
if( chromatorgb )
|
||||
{
|
||||
if (doReadRGB)
|
||||
ChromaToRGB( (float *)data, m_height, channelstoread, step / xstep );
|
||||
else
|
||||
ChromaToBGR( (float *)data, m_height, channelstoread, step / xstep );
|
||||
if( chromatorgb )
|
||||
{
|
||||
if (doReadRGB)
|
||||
ChromaToRGB( (float *)data, m_height, channelstoread, step / xstep );
|
||||
else
|
||||
ChromaToBGR( (float *)data, m_height, channelstoread, step / xstep );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
else // m_multispectral should be false
|
||||
{
|
||||
uchar *out = data;
|
||||
int x, y;
|
||||
@@ -782,13 +821,19 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
header.channels().insert( "B", Channel( type ) );
|
||||
//printf("bunt\n");
|
||||
}
|
||||
else
|
||||
else if( channels == 1 || channels == 2 )
|
||||
{
|
||||
header.channels().insert( "Y", Channel( type ) );
|
||||
//printf("gray\n");
|
||||
}
|
||||
else if( channels > 4 )
|
||||
{
|
||||
for ( int i = 0; i < channels; i++ )
|
||||
header.channels().insert( std::to_string(i), Channel( type ) );
|
||||
//printf("multi-channel\n");
|
||||
}
|
||||
|
||||
if( channels % 2 == 0 )
|
||||
if( channels % 2 == 0 && channels <= 4)
|
||||
{ // even number of channels indicates Alpha
|
||||
header.channels().insert( "A", Channel( type ) );
|
||||
}
|
||||
@@ -821,10 +866,15 @@ bool ExrEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
frame.insert( "G", Slice( type, buffer + size, size * channels, bufferstep ));
|
||||
frame.insert( "R", Slice( type, buffer + size * 2, size * channels, bufferstep ));
|
||||
}
|
||||
else
|
||||
else if( channels == 1 || channels == 2 )
|
||||
frame.insert( "Y", Slice( type, buffer, size * channels, bufferstep ));
|
||||
else if( channels > 4 )
|
||||
{
|
||||
for ( int i = 0; i < channels; i++ )
|
||||
frame.insert( std::to_string(i), Slice( type, buffer + size * i, size * channels, bufferstep ));
|
||||
}
|
||||
|
||||
if( channels % 2 == 0 )
|
||||
if( channels % 2 == 0 && channels <= 4 )
|
||||
{ // even channel count indicates Alpha channel
|
||||
frame.insert( "A", Slice( type, buffer + size * (channels - 1), size * channels, bufferstep ));
|
||||
}
|
||||
|
||||
@@ -100,6 +100,8 @@ protected:
|
||||
bool m_iscolor;
|
||||
bool m_isfloat;
|
||||
bool m_hasalpha;
|
||||
bool m_multispectral;
|
||||
int m_channels;
|
||||
|
||||
private:
|
||||
ExrDecoder(const ExrDecoder &); // copy disabled
|
||||
|
||||
@@ -408,6 +408,9 @@ bool GdalDecoder::readData( Mat& img ){
|
||||
case GCI_AlphaBand:
|
||||
color = 3;
|
||||
break;
|
||||
case GCI_Undefined:
|
||||
color = c;
|
||||
break;
|
||||
default:
|
||||
CV_Error(cv::Error::StsError, "Invalid/unsupported mode");
|
||||
}
|
||||
|
||||
@@ -600,6 +600,8 @@ JpegEncoder::JpegEncoder()
|
||||
{
|
||||
m_description = "JPEG files (*.jpeg;*.jpg;*.jpe)";
|
||||
m_buf_supported = true;
|
||||
m_support_metadata.assign((size_t)IMAGE_METADATA_MAX + 1, false);
|
||||
m_support_metadata[(size_t)IMAGE_METADATA_EXIF] = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -815,6 +817,22 @@ bool JpegEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
|
||||
jpeg_start_compress( &cinfo, TRUE );
|
||||
|
||||
if (!m_metadata.empty()) {
|
||||
const std::vector<uchar>& metadata_exif = m_metadata[IMAGE_METADATA_EXIF];
|
||||
size_t exif_size = metadata_exif.size();
|
||||
if (exif_size > 0u) {
|
||||
const char app1_exif_prefix[] = {'E', 'x', 'i', 'f', '\0', '\0'};
|
||||
size_t app1_exif_prefix_size = sizeof(app1_exif_prefix);
|
||||
size_t data_size = exif_size + app1_exif_prefix_size;
|
||||
|
||||
std::vector<uchar> metadata_app1(data_size);
|
||||
uchar* data = metadata_app1.data();
|
||||
memcpy(data, app1_exif_prefix, app1_exif_prefix_size);
|
||||
memcpy(data + app1_exif_prefix_size, metadata_exif.data(), exif_size);
|
||||
jpeg_write_marker(&cinfo, JPEG_APP0 + 1, data, (unsigned)data_size);
|
||||
}
|
||||
}
|
||||
|
||||
if( doDirectWrite )
|
||||
{
|
||||
for( int y = 0; y < height; y++ )
|
||||
|
||||
+142
-110
@@ -156,7 +156,7 @@ bool APNGFrame::setMat(const cv::Mat& src, unsigned delayNum, unsigned delayDen)
|
||||
|
||||
if (!src.empty())
|
||||
{
|
||||
png_uint_32 rowbytes = src.depth() == CV_16U ? src.cols * src.channels() * 2 : src.cols * src.channels();
|
||||
png_uint_32 rowbytes = src.cols * (uint32_t)src.elemSize();
|
||||
_width = src.cols;
|
||||
_height = src.rows;
|
||||
_colorType = src.channels() == 1 ? PNG_COLOR_TYPE_GRAY : src.channels() == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
|
||||
@@ -416,14 +416,17 @@ bool PngDecoder::readData( Mat& img )
|
||||
|
||||
if (m_frame_no == 0)
|
||||
{
|
||||
if (m_mat_raw.empty())
|
||||
{
|
||||
if (m_f)
|
||||
fseek(m_f, -8, SEEK_CUR);
|
||||
else
|
||||
m_buf_pos -= 8;
|
||||
}
|
||||
m_mat_raw = Mat(img.rows, img.cols, m_type);
|
||||
m_mat_next = Mat(img.rows, img.cols, m_type);
|
||||
frameRaw.setMat(m_mat_raw);
|
||||
frameNext.setMat(m_mat_next);
|
||||
if (m_f)
|
||||
fseek(m_f, -8, SEEK_CUR);
|
||||
else
|
||||
m_buf_pos -= 8;
|
||||
}
|
||||
else
|
||||
m_mat_next.copyTo(mat_cur);
|
||||
@@ -433,9 +436,6 @@ bool PngDecoder::readData( Mat& img )
|
||||
if (!processing_start((void*)&frameRaw, mat_cur))
|
||||
return false;
|
||||
|
||||
if(setjmp(png_jmpbuf(m_png_ptr)))
|
||||
return false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
id = read_chunk(chunk);
|
||||
@@ -446,53 +446,56 @@ bool PngDecoder::readData( Mat& img )
|
||||
{
|
||||
if (!m_is_fcTL_loaded)
|
||||
{
|
||||
m_is_fcTL_loaded = true;
|
||||
w0 = m_width;
|
||||
h0 = m_height;
|
||||
}
|
||||
|
||||
if (processing_finish())
|
||||
{
|
||||
if (dop == 2)
|
||||
memcpy(frameNext.getPixels(), frameCur.getPixels(), imagesize);
|
||||
|
||||
compose_frame(frameCur.getRows(), frameRaw.getRows(), bop, x0, y0, w0, h0, mat_cur);
|
||||
if (!delay_den)
|
||||
delay_den = 100;
|
||||
m_animation.durations.push_back(cvRound(1000.*delay_num/delay_den));
|
||||
|
||||
if (mat_cur.channels() == img.channels())
|
||||
{
|
||||
if (mat_cur.depth() == CV_16U && img.depth() == CV_8U)
|
||||
mat_cur.convertTo(img, CV_8U, 1. / 255);
|
||||
else
|
||||
mat_cur.copyTo(img);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat mat_cur_scaled;
|
||||
if (mat_cur.depth() == CV_16U && img.depth() == CV_8U)
|
||||
mat_cur.convertTo(mat_cur_scaled, CV_8U, 1. / 255);
|
||||
else
|
||||
mat_cur_scaled = mat_cur;
|
||||
|
||||
if (img.channels() == 1)
|
||||
cvtColor(mat_cur_scaled, img, COLOR_BGRA2GRAY);
|
||||
else if (img.channels() == 3)
|
||||
cvtColor(mat_cur_scaled, img, COLOR_BGRA2BGR);
|
||||
}
|
||||
|
||||
if (dop != 2)
|
||||
{
|
||||
memcpy(frameNext.getPixels(), frameCur.getPixels(), imagesize);
|
||||
if (dop == 1)
|
||||
for (j = 0; j < h0; j++)
|
||||
memset(frameNext.getRows()[y0 + j] + x0 * img.channels(), 0, w0 * img.channels());
|
||||
}
|
||||
m_mat_raw.copyTo(m_animation.still_image);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
if (processing_finish())
|
||||
{
|
||||
if (dop == 2)
|
||||
memcpy(frameNext.getPixels(), frameCur.getPixels(), imagesize);
|
||||
|
||||
if (x0 + w0 > frameCur.getWidth() || y0 + h0 > frameCur.getHeight())
|
||||
return false;
|
||||
|
||||
compose_frame(frameCur.getRows(), frameRaw.getRows(), bop, x0, y0, w0, h0, mat_cur);
|
||||
if (!delay_den)
|
||||
delay_den = 100;
|
||||
m_animation.durations.push_back(cvRound(1000. * delay_num / delay_den));
|
||||
|
||||
if (mat_cur.channels() == img.channels())
|
||||
{
|
||||
if (mat_cur.depth() == CV_16U && img.depth() == CV_8U)
|
||||
mat_cur.convertTo(img, CV_8U, 1. / 255);
|
||||
else
|
||||
mat_cur.copyTo(img);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat mat_cur_scaled;
|
||||
if (mat_cur.depth() == CV_16U && img.depth() == CV_8U)
|
||||
mat_cur.convertTo(mat_cur_scaled, CV_8U, 1. / 255);
|
||||
else
|
||||
mat_cur_scaled = mat_cur;
|
||||
|
||||
if (img.channels() == 1)
|
||||
cvtColor(mat_cur_scaled, img, COLOR_BGRA2GRAY);
|
||||
else if (img.channels() == 3)
|
||||
cvtColor(mat_cur_scaled, img, COLOR_BGRA2BGR);
|
||||
}
|
||||
|
||||
if (dop != 2)
|
||||
{
|
||||
memcpy(frameNext.getPixels(), frameCur.getPixels(), imagesize);
|
||||
if (dop == 1)
|
||||
for (j = 0; j < h0; j++)
|
||||
memset(frameNext.getRows()[y0 + j] + x0 * img.channels(), 0, w0 * img.channels());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
w0 = png_get_uint_32(&chunk.p[12]);
|
||||
@@ -508,14 +511,18 @@ bool PngDecoder::readData( Mat& img )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Asking for blend over with no alpha is invalid.
|
||||
if (bop == 1 && mat_cur.channels() != 4)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&m_chunkIHDR.p[8], &chunk.p[12], 8);
|
||||
return true;
|
||||
|
||||
if (m_is_fcTL_loaded)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
m_is_fcTL_loaded = true;
|
||||
ClearPngPtr();
|
||||
if (!processing_start((void*)&frameRaw, mat_cur))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (id == id_IDAT)
|
||||
{
|
||||
@@ -650,8 +657,8 @@ void PngDecoder::compose_frame(std::vector<png_bytep>& rows_dst, const std::vect
|
||||
const size_t elem_size = img.elemSize();
|
||||
if (_bop == 0) {
|
||||
// Overwrite mode: copy source row directly to destination
|
||||
for(uint32_t j = 0; j < h; ++j) {
|
||||
std::memcpy(rows_dst[j + y] + x * elem_size,rows_src[j], w * elem_size);
|
||||
for (uint32_t j = 0; j < h; ++j) {
|
||||
std::memcpy(rows_dst[j + y] + x * elem_size, rows_src[j], w * elem_size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -665,23 +672,24 @@ void PngDecoder::compose_frame(std::vector<png_bytep>& rows_dst, const std::vect
|
||||
|
||||
// Blending mode
|
||||
for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) {
|
||||
if (channels < 4 || sp[3] == 65535) { // Fully opaque in 16-bit (max value)
|
||||
uint16_t alpha = sp[3];
|
||||
|
||||
if (channels < 4 || alpha == 65535 || dp[3] == 0) {
|
||||
// Fully opaque OR destination fully transparent: direct copy
|
||||
memcpy(dp, sp, elem_size);
|
||||
continue;
|
||||
}
|
||||
else if (sp[3] != 0) { // Partially transparent
|
||||
if (dp[3] != 0) { // Both source and destination have alpha
|
||||
uint32_t u = sp[3] * 65535; // 16-bit max
|
||||
uint32_t v = (65535 - sp[3]) * dp[3];
|
||||
uint32_t al = u + v;
|
||||
dp[0] = static_cast<uint16_t>((sp[0] * u + dp[0] * v) / al); // Red
|
||||
dp[1] = static_cast<uint16_t>((sp[1] * u + dp[1] * v) / al); // Green
|
||||
dp[2] = static_cast<uint16_t>((sp[2] * u + dp[2] * v) / al); // Blue
|
||||
dp[3] = static_cast<uint16_t>(al / 65535); // Alpha
|
||||
}
|
||||
else {
|
||||
// If destination alpha is 0, copy source pixel
|
||||
memcpy(dp, sp, elem_size);
|
||||
}
|
||||
|
||||
if (alpha != 0) {
|
||||
// Alpha blending
|
||||
uint64_t u = static_cast<uint64_t>(alpha) * 65535;
|
||||
uint64_t v = static_cast<uint64_t>(65535 - alpha) * dp[3];
|
||||
uint64_t al = u + v;
|
||||
|
||||
dp[0] = static_cast<uint16_t>((sp[0] * u + dp[0] * v) / al); // Red
|
||||
dp[1] = static_cast<uint16_t>((sp[1] * u + dp[1] * v) / al); // Green
|
||||
dp[2] = static_cast<uint16_t>((sp[2] * u + dp[2] * v) / al); // Blue
|
||||
dp[3] = static_cast<uint16_t>(al / 65535); // Alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -694,25 +702,24 @@ void PngDecoder::compose_frame(std::vector<png_bytep>& rows_dst, const std::vect
|
||||
|
||||
// Blending mode
|
||||
for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) {
|
||||
if (channels < 4 || sp[3] == 255) {
|
||||
// Fully opaque: copy source pixel directly
|
||||
uint8_t alpha = sp[3];
|
||||
|
||||
if (channels < 4 || alpha == 255 || dp[3] == 0) {
|
||||
// Fully opaque OR destination fully transparent: direct copy
|
||||
memcpy(dp, sp, elem_size);
|
||||
continue;
|
||||
}
|
||||
else if (sp[3] != 0) {
|
||||
|
||||
if (alpha != 0) {
|
||||
// Alpha blending
|
||||
if (dp[3] != 0) {
|
||||
int u = sp[3] * 255;
|
||||
int v = (255 - sp[3]) * dp[3];
|
||||
int al = u + v;
|
||||
dp[0] = (sp[0] * u + dp[0] * v) / al; // Red
|
||||
dp[1] = (sp[1] * u + dp[1] * v) / al; // Green
|
||||
dp[2] = (sp[2] * u + dp[2] * v) / al; // Blue
|
||||
dp[3] = al / 255; // Alpha
|
||||
}
|
||||
else {
|
||||
// If destination alpha is 0, copy source pixel
|
||||
memcpy(dp, sp, elem_size);
|
||||
}
|
||||
uint32_t u = alpha * 255;
|
||||
uint32_t v = (255 - alpha) * dp[3];
|
||||
uint32_t al = u + v;
|
||||
|
||||
dp[0] = static_cast<uint8_t>((sp[0] * u + dp[0] * v) / al); // Red
|
||||
dp[1] = static_cast<uint8_t>((sp[1] * u + dp[1] * v) / al); // Green
|
||||
dp[2] = static_cast<uint8_t>((sp[2] * u + dp[2] * v) / al); // Blue
|
||||
dp[3] = static_cast<uint8_t>(al / 255); // Alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -845,6 +852,8 @@ void PngDecoder::row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_
|
||||
{
|
||||
CV_UNUSED(pass);
|
||||
APNGFrame* frame = (APNGFrame*)png_get_progressive_ptr(png_ptr);
|
||||
if(row_num >= frame->getHeight())
|
||||
return;
|
||||
png_progressive_combine_row(png_ptr, frame->getRows()[row_num], new_row);
|
||||
}
|
||||
|
||||
@@ -852,8 +861,10 @@ void PngDecoder::row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_
|
||||
|
||||
PngEncoder::PngEncoder()
|
||||
{
|
||||
m_description = "Portable Network Graphics files (*.png)";
|
||||
m_description = "Portable Network Graphics files (*.png;*.apng)";
|
||||
m_buf_supported = true;
|
||||
m_support_metadata.assign((size_t)IMAGE_METADATA_MAX+1, false);
|
||||
m_support_metadata[IMAGE_METADATA_EXIF] = true;
|
||||
op_zstream1.zalloc = NULL;
|
||||
op_zstream2.zalloc = NULL;
|
||||
next_seq_num = 0;
|
||||
@@ -985,6 +996,16 @@ bool PngEncoder::write( const Mat& img, const std::vector<int>& params )
|
||||
for( y = 0; y < height; y++ )
|
||||
buffer[y] = img.data + y*img.step;
|
||||
|
||||
if (!m_metadata.empty()) {
|
||||
std::vector<uchar>& exif = m_metadata[IMAGE_METADATA_EXIF];
|
||||
if (!exif.empty()) {
|
||||
writeChunk(f, "eXIf", exif.data(), (uint32_t)exif.size());
|
||||
}
|
||||
// [TODO] add xmp and icc. They need special handling,
|
||||
// see https://dev.exiv2.org/projects/exiv2/wiki/The_Metadata_in_PNG_files and
|
||||
// https://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html.
|
||||
}
|
||||
|
||||
png_write_image( png_ptr, buffer.data() );
|
||||
png_write_end( png_ptr, info_ptr );
|
||||
|
||||
@@ -1483,7 +1504,7 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
|
||||
|
||||
if (m_isBilevel)
|
||||
CV_LOG_WARNING(NULL, "IMWRITE_PNG_BILEVEL parameter is not supported yet.");
|
||||
uint32_t first =0;
|
||||
|
||||
uint32_t loops= animation.loop_count;
|
||||
uint32_t coltype= animation.frames[0].channels() == 1 ? PNG_COLOR_TYPE_GRAY : animation.frames[0].channels() == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
|
||||
|
||||
@@ -1568,7 +1589,7 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
|
||||
buf_IHDR[11] = 0;
|
||||
buf_IHDR[12] = 0;
|
||||
|
||||
png_save_uint_32(buf_acTL, num_frames - first);
|
||||
png_save_uint_32(buf_acTL, num_frames);
|
||||
png_save_uint_32(buf_acTL + 4, loops);
|
||||
|
||||
writeToStreamOrBuffer(header, 8, m_f);
|
||||
@@ -1577,8 +1598,6 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
|
||||
|
||||
if (num_frames > 1)
|
||||
writeChunk(m_f, "acTL", buf_acTL, 8);
|
||||
else
|
||||
first = 0;
|
||||
|
||||
if (palsize > 0)
|
||||
writeChunk(m_f, "PLTE", (unsigned char*)(&palette), palsize * 3);
|
||||
@@ -1634,19 +1653,32 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
|
||||
|
||||
for (j = 0; j < 6; j++)
|
||||
op[j].valid = 0;
|
||||
|
||||
if (!animation.still_image.empty() && num_frames > 1)
|
||||
{
|
||||
CV_Assert(animation.still_image.type() == animation.frames[0].type() && animation.still_image.size() == animation.frames[0].size());
|
||||
APNGFrame apngFrame;
|
||||
Mat tmp;
|
||||
if (animation.still_image.depth() == CV_16U)
|
||||
{
|
||||
animation.still_image.convertTo(tmp, CV_8U, 1.0 / 255);
|
||||
}
|
||||
else
|
||||
tmp = animation.still_image;
|
||||
|
||||
if (tmp.channels() > 2)
|
||||
cvtColor(tmp, tmp, COLOR_BGRA2RGBA);
|
||||
apngFrame.setMat(tmp);
|
||||
|
||||
deflateRectOp(apngFrame.getPixels(), x0, y0, w0, h0, bpp, rowbytes, zbuf_size, 0);
|
||||
deflateRectFin(zbuf.data(), &zsize, bpp, rowbytes, rows.data(), zbuf_size, 0);
|
||||
writeIDATs(m_f, 0, zbuf.data(), zsize, idat_size);
|
||||
}
|
||||
|
||||
deflateRectOp(frames[0].getPixels(), x0, y0, w0, h0, bpp, rowbytes, zbuf_size, 0);
|
||||
deflateRectFin(zbuf.data(), &zsize, bpp, rowbytes, rows.data(), zbuf_size, 0);
|
||||
|
||||
if (first)
|
||||
{
|
||||
writeIDATs(m_f, 0, zbuf.data(), zsize, idat_size);
|
||||
for (j = 0; j < 6; j++)
|
||||
op[j].valid = 0;
|
||||
deflateRectOp(frames[1].getPixels(), x0, y0, w0, h0, bpp, rowbytes, zbuf_size, 0);
|
||||
deflateRectFin(zbuf.data(), &zsize, bpp, rowbytes, rows.data(), zbuf_size, 0);
|
||||
}
|
||||
|
||||
for (i = first; i < num_frames - 1; i++)
|
||||
for (i = 0; i < num_frames - 1; i++)
|
||||
{
|
||||
uint32_t op_min;
|
||||
int op_best;
|
||||
@@ -1673,7 +1705,7 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
|
||||
}
|
||||
|
||||
/* dispose = previous */
|
||||
if (i > first)
|
||||
if (i > 0)
|
||||
getRect(width, height, rest.data(), frames[i + 1].getPixels(), over3.data(), bpp, rowbytes, zbuf_size, has_tcolor, tcolor, 2);
|
||||
|
||||
op_min = op[0].size;
|
||||
@@ -1699,9 +1731,9 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
|
||||
png_save_uint_16(buf_fcTL + 22, frames[i].getDelayDen());
|
||||
buf_fcTL[24] = dop;
|
||||
buf_fcTL[25] = bop;
|
||||
writeChunk(m_f, "fcTL", buf_fcTL, 26);
|
||||
|
||||
writeIDATs(m_f, i, zbuf.data(), zsize, idat_size);
|
||||
writeChunk(m_f, "fcTL", buf_fcTL, 26);
|
||||
writeIDATs(m_f, animation.still_image.empty() ? i : 1, zbuf.data(), zsize, idat_size);
|
||||
|
||||
/* process apng dispose - begin */
|
||||
if (dop != 2)
|
||||
@@ -1728,7 +1760,7 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
|
||||
deflateRectFin(zbuf.data(), &zsize, bpp, rowbytes, rows.data(), zbuf_size, op_best);
|
||||
}
|
||||
|
||||
if (num_frames > 1)
|
||||
if (num_frames > 1 /* don't write fcTL chunk if animation has only one frame */)
|
||||
{
|
||||
png_save_uint_32(buf_fcTL, next_seq_num++);
|
||||
png_save_uint_32(buf_fcTL + 4, w0);
|
||||
|
||||
@@ -31,18 +31,18 @@
|
||||
* with these values. (png_set_rgb_to_gray( png_ptr, 1, 0.299, 0.587 );) For this codec implementation,
|
||||
* slightly modified versions are implemented in the below of this page.
|
||||
*/
|
||||
void spngCvt_BGR2Gray_8u_C3C1R(const uchar *bgr, int bgr_step,
|
||||
uchar *gray, int gray_step,
|
||||
cv::Size size, int _swap_rb);
|
||||
|
||||
void spngCvt_BGRA2Gray_8u_C4C1R(const uchar *bgra, int rgba_step,
|
||||
void spngCvt_BGRA2Gray_8u_CnC1R(const uchar *bgr, int bgr_step,
|
||||
uchar *gray, int gray_step,
|
||||
cv::Size size, int _swap_rb);
|
||||
cv::Size size, int ncn, int _swap_rb);
|
||||
|
||||
void spngCvt_BGRA2Gray_16u_CnC1R(const ushort *bgr, int bgr_step,
|
||||
ushort *gray, int gray_step,
|
||||
cv::Size size, int ncn, int _swap_rb);
|
||||
|
||||
void spngCvt_BGRA2Gray_16u28u_CnC1R(const ushort *bgr, int bgr_step,
|
||||
uchar *gray, int gray_step,
|
||||
cv::Size size, int ncn, int _swap_rb);
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
@@ -109,7 +109,7 @@ int SPngDecoder::readDataFromBuf(void *sp_ctx, void *user, void *dst, size_t siz
|
||||
|
||||
bool SPngDecoder::readHeader()
|
||||
{
|
||||
volatile bool result = false;
|
||||
bool result = false;
|
||||
close();
|
||||
|
||||
spng_ctx *ctx = spng_ctx_new(SPNG_CTX_IGNORE_ADLER32);
|
||||
@@ -136,40 +136,36 @@ bool SPngDecoder::readHeader()
|
||||
if (!m_buf.empty() || m_f)
|
||||
{
|
||||
struct spng_ihdr ihdr;
|
||||
int ret = spng_get_ihdr(ctx, &ihdr);
|
||||
|
||||
if (ret == SPNG_OK)
|
||||
if (spng_get_ihdr(ctx, &ihdr) == SPNG_OK)
|
||||
{
|
||||
m_width = static_cast<int>(ihdr.width);
|
||||
m_height = static_cast<int>(ihdr.height);
|
||||
m_color_type = ihdr.color_type;
|
||||
m_bit_depth = ihdr.bit_depth;
|
||||
|
||||
if (ihdr.bit_depth <= 8 || ihdr.bit_depth == 16)
|
||||
int num_trans;
|
||||
switch (ihdr.color_type)
|
||||
{
|
||||
int num_trans;
|
||||
switch (ihdr.color_type)
|
||||
{
|
||||
case SPNG_COLOR_TYPE_TRUECOLOR:
|
||||
case SPNG_COLOR_TYPE_INDEXED:
|
||||
struct spng_trns trns;
|
||||
num_trans = !spng_get_trns(ctx, &trns);
|
||||
if (num_trans > 0)
|
||||
m_type = CV_8UC4;
|
||||
else
|
||||
m_type = CV_8UC3;
|
||||
break;
|
||||
case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:
|
||||
case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:
|
||||
case SPNG_COLOR_TYPE_TRUECOLOR:
|
||||
case SPNG_COLOR_TYPE_INDEXED:
|
||||
struct spng_trns trns;
|
||||
num_trans = !spng_get_trns(ctx, &trns);
|
||||
if (num_trans > 0)
|
||||
m_type = CV_8UC4;
|
||||
break;
|
||||
default:
|
||||
m_type = CV_8UC1;
|
||||
}
|
||||
if (ihdr.bit_depth == 16)
|
||||
m_type = CV_MAKETYPE(CV_16U, CV_MAT_CN(m_type));
|
||||
result = true;
|
||||
else
|
||||
m_type = CV_8UC3;
|
||||
break;
|
||||
case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA:
|
||||
case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA:
|
||||
m_type = CV_8UC4;
|
||||
break;
|
||||
default:
|
||||
m_type = CV_8UC1;
|
||||
}
|
||||
if (ihdr.bit_depth == 16)
|
||||
m_type = CV_MAKETYPE(CV_16U, CV_MAT_CN(m_type));
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,98 +174,86 @@ bool SPngDecoder::readHeader()
|
||||
|
||||
bool SPngDecoder::readData(Mat &img)
|
||||
{
|
||||
volatile bool result = false;
|
||||
bool color = img.channels() > 1;
|
||||
|
||||
struct spng_ctx *png_ptr = (struct spng_ctx *)m_ctx;
|
||||
bool result = false;
|
||||
|
||||
if (m_ctx && m_width && m_height)
|
||||
{
|
||||
int fmt = SPNG_FMT_PNG;
|
||||
struct spng_ctx* png_ptr = (struct spng_ctx*)m_ctx;
|
||||
bool color = img.channels() > 1;
|
||||
int fmt = img.channels() == 4 ? m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8 : SPNG_FMT_PNG;
|
||||
int decode_flags = img.channels() == 4 ? SPNG_DECODE_TRNS : 0;
|
||||
|
||||
struct spng_trns trns;
|
||||
int have_trns = spng_get_trns((struct spng_ctx *)m_ctx, &trns);
|
||||
|
||||
int decode_flags = 0;
|
||||
if (have_trns == SPNG_OK)
|
||||
{
|
||||
decode_flags = SPNG_DECODE_TRNS;
|
||||
}
|
||||
if (img.channels() == 4)
|
||||
{
|
||||
if (m_color_type == SPNG_COLOR_TYPE_TRUECOLOR ||
|
||||
m_color_type == SPNG_COLOR_TYPE_INDEXED ||
|
||||
m_color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA)
|
||||
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
|
||||
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE)
|
||||
fmt = m_bit_depth == 16 ? SPNG_FMT_GA16 : SPNG_FMT_GA8;
|
||||
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA)
|
||||
{
|
||||
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
|
||||
}
|
||||
else
|
||||
fmt = SPNG_FMT_RGBA8;
|
||||
}
|
||||
if (img.channels() == 3)
|
||||
if (img.type() == CV_8UC3)
|
||||
{
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
if ((m_color_type == SPNG_COLOR_TYPE_GRAYSCALE || m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA) &&
|
||||
m_bit_depth == 16)
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
else if (m_bit_depth == 16)
|
||||
fmt = SPNG_FMT_PNG;
|
||||
}
|
||||
else if (img.channels() == 1)
|
||||
{
|
||||
if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE && m_bit_depth <= 8)
|
||||
fmt = SPNG_FMT_G8;
|
||||
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE && m_bit_depth == 16)
|
||||
{
|
||||
if (img.depth() == CV_8U || img.depth() == CV_8S)
|
||||
{
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt = SPNG_FMT_PNG;
|
||||
}
|
||||
}
|
||||
else if (m_color_type == SPNG_COLOR_TYPE_INDEXED ||
|
||||
m_color_type == SPNG_COLOR_TYPE_TRUECOLOR)
|
||||
{
|
||||
if (img.depth() == CV_8U || img.depth() == CV_8S)
|
||||
{
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGB8;
|
||||
}
|
||||
}
|
||||
else if (m_color_type == SPNG_COLOR_TYPE_GRAYSCALE_ALPHA || fmt == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA)
|
||||
{
|
||||
if (img.depth() == CV_8U || img.depth() == CV_8S)
|
||||
{
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt = m_bit_depth == 16 ? SPNG_FMT_RGBA16 : SPNG_FMT_RGBA8;
|
||||
}
|
||||
}
|
||||
else
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
fmt = img.depth() == CV_16U ? SPNG_FMT_RGBA16 : SPNG_FMT_RGB8;
|
||||
}
|
||||
|
||||
if (fmt == SPNG_FMT_PNG && m_bit_depth == 16 && m_color_type >= SPNG_COLOR_TYPE_GRAYSCALE_ALPHA)
|
||||
{
|
||||
Mat tmp(m_height, m_width, CV_16UC4);
|
||||
if (SPNG_OK != spng_decode_image(png_ptr, tmp.data, tmp.total() * tmp.elemSize(), SPNG_FMT_RGBA16, 0))
|
||||
return false;
|
||||
cvtColor(tmp, img, m_use_rgb ? COLOR_RGBA2RGB : COLOR_RGBA2BGR);
|
||||
return true;
|
||||
}
|
||||
|
||||
struct spng_ihdr ihdr;
|
||||
spng_get_ihdr(png_ptr, &ihdr);
|
||||
|
||||
size_t image_width, image_size = 0;
|
||||
int ret = spng_decoded_image_size(png_ptr, fmt, &image_size);
|
||||
struct spng_ihdr ihdr;
|
||||
spng_get_ihdr(png_ptr, &ihdr);
|
||||
|
||||
if (ret == SPNG_OK)
|
||||
{
|
||||
image_width = image_size / m_height;
|
||||
|
||||
if (!color && fmt == SPNG_FMT_RGB8 && m_bit_depth == 16 && (m_color_type == SPNG_COLOR_TYPE_TRUECOLOR || m_color_type == SPNG_COLOR_TYPE_TRUECOLOR_ALPHA))
|
||||
{
|
||||
Mat tmp(m_height, m_width, CV_16UC4);
|
||||
if (SPNG_OK != spng_decode_image(png_ptr, tmp.data, tmp.total() * tmp.elemSize(), SPNG_FMT_RGBA16, 0))
|
||||
return false;
|
||||
spngCvt_BGRA2Gray_16u28u_CnC1R(reinterpret_cast<const ushort*>(tmp.data), (int)tmp.step1(),
|
||||
img.data, (int)img.step1(), Size(m_width, m_height), 4, 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!color && ihdr.interlace_method && (fmt == SPNG_FMT_RGB8 || fmt == SPNG_FMT_RGBA16))
|
||||
{
|
||||
if (fmt == SPNG_FMT_RGBA16)
|
||||
{
|
||||
Mat tmp(m_height, m_width, CV_16UC4);
|
||||
if (SPNG_OK != spng_decode_image(png_ptr, tmp.data, tmp.total() * tmp.elemSize(), fmt, 0))
|
||||
return false;
|
||||
spngCvt_BGRA2Gray_16u_CnC1R(reinterpret_cast<const ushort*>(tmp.data), (int)tmp.step1(),
|
||||
reinterpret_cast<ushort*>(img.data), (int)img.step1(), Size(m_width, m_height), 4, 2);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat tmp(m_height, m_width, CV_8UC3);
|
||||
if (SPNG_OK != spng_decode_image(png_ptr, tmp.data, image_size, fmt, 0))
|
||||
return false;
|
||||
spngCvt_BGRA2Gray_8u_CnC1R(tmp.data, (int)tmp.step1(), img.data, (int)img.step1(), Size(m_width, m_height), 3, 2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fmt == SPNG_FMT_PNG && img.elemSize() * m_width / 3 == image_width)
|
||||
{
|
||||
Mat tmp(m_height, m_width, CV_16U);
|
||||
if (SPNG_OK != spng_decode_image(png_ptr, tmp.data, image_size, SPNG_FMT_PNG, 0))
|
||||
return false;
|
||||
cvtColor(tmp, img, COLOR_GRAY2BGR);
|
||||
return true;
|
||||
}
|
||||
|
||||
ret = spng_decode_image(png_ptr, nullptr, 0, fmt, SPNG_DECODE_PROGRESSIVE | decode_flags);
|
||||
if (ret == SPNG_OK)
|
||||
{
|
||||
@@ -279,88 +263,46 @@ bool SPngDecoder::readData(Mat &img)
|
||||
// decode image then convert to grayscale
|
||||
if (!color && (fmt == SPNG_FMT_RGB8 || fmt == SPNG_FMT_RGBA8 || fmt == SPNG_FMT_RGBA16))
|
||||
{
|
||||
if (ihdr.interlace_method == 0)
|
||||
AutoBuffer<unsigned char> buffer;
|
||||
buffer.allocate(image_width);
|
||||
if (fmt == SPNG_FMT_RGB8)
|
||||
{
|
||||
AutoBuffer<unsigned char> buffer;
|
||||
buffer.allocate(image_width);
|
||||
if (fmt == SPNG_FMT_RGB8)
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
|
||||
spngCvt_BGR2Gray_8u_C3C1R(
|
||||
buffer.data(),
|
||||
0,
|
||||
img.data + row_info.row_num * img.step,
|
||||
0, Size(m_width, 1), 2);
|
||||
} while (ret == SPNG_OK);
|
||||
}
|
||||
else if (fmt == SPNG_FMT_RGBA8)
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
|
||||
spngCvt_BGRA2Gray_8u_C4C1R(
|
||||
buffer.data(),
|
||||
0,
|
||||
img.data + row_info.row_num * img.step,
|
||||
0, Size(m_width, 1), 2);
|
||||
} while (ret == SPNG_OK);
|
||||
}
|
||||
else if (fmt == SPNG_FMT_RGBA16)
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
|
||||
spngCvt_BGRA2Gray_16u_CnC1R(
|
||||
reinterpret_cast<const ushort *>(buffer.data()), 0,
|
||||
reinterpret_cast<ushort *>(img.data + row_info.row_num * img.step),
|
||||
0, Size(m_width, 1),
|
||||
4, 2);
|
||||
} while (ret == SPNG_OK);
|
||||
}
|
||||
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
|
||||
spngCvt_BGRA2Gray_8u_CnC1R(buffer.data(), 0, img.data + row_info.row_num * img.step, 0, Size(m_width, 1), 3, 2);
|
||||
} while (ret == SPNG_OK);
|
||||
}
|
||||
else
|
||||
else if (fmt == SPNG_FMT_RGBA8)
|
||||
{
|
||||
AutoBuffer<unsigned char> imageBuffer(image_size);
|
||||
ret = spng_decode_image(png_ptr, imageBuffer.data(), image_size, fmt, 0);
|
||||
int step = m_width * img.channels();
|
||||
if (fmt == SPNG_FMT_RGB8)
|
||||
do
|
||||
{
|
||||
spngCvt_BGR2Gray_8u_C3C1R(
|
||||
imageBuffer.data(),
|
||||
step,
|
||||
img.data,
|
||||
step, Size(m_width, m_height), 2);
|
||||
}
|
||||
else if (fmt == SPNG_FMT_RGBA8)
|
||||
{
|
||||
spngCvt_BGRA2Gray_8u_C4C1R(
|
||||
imageBuffer.data(),
|
||||
step,
|
||||
img.data,
|
||||
step, Size(m_width, m_height), 2);
|
||||
}
|
||||
else if (fmt == SPNG_FMT_RGBA16)
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
|
||||
spngCvt_BGRA2Gray_8u_CnC1R(buffer.data(), 0, img.data + row_info.row_num * img.step, 0, Size(m_width, 1), 4, 2);
|
||||
} while (ret == SPNG_OK);
|
||||
}
|
||||
else if (fmt == SPNG_FMT_RGBA16)
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer.data(), image_width);
|
||||
spngCvt_BGRA2Gray_16u_CnC1R(
|
||||
reinterpret_cast<const ushort *>(imageBuffer.data()), step / 3,
|
||||
reinterpret_cast<ushort *>(img.data),
|
||||
step / 3, Size(m_width, m_height),
|
||||
4, 2);
|
||||
}
|
||||
reinterpret_cast<const ushort*>(buffer.data()), 0,
|
||||
reinterpret_cast<ushort*>(img.data + row_info.row_num * img.step),
|
||||
0, Size(m_width, 1), 4, 2);
|
||||
} while (ret == SPNG_OK);
|
||||
}
|
||||
}
|
||||
else if (color)
|
||||
@@ -383,9 +325,8 @@ bool SPngDecoder::readData(Mat &img)
|
||||
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
|
||||
if (ihdr.interlace_method == 0 && !m_use_rgb)
|
||||
{
|
||||
icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast<const ushort *>(buffer[row_info.row_num]), 0,
|
||||
reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0,
|
||||
Size(m_width, 1));
|
||||
icvCvt_RGBA2BGRA_16u_C4R(reinterpret_cast<const ushort*>(buffer[row_info.row_num]), 0,
|
||||
reinterpret_cast<ushort*>(buffer[row_info.row_num]), 0, Size(m_width, 1));
|
||||
}
|
||||
} while (ret == SPNG_OK);
|
||||
if (ihdr.interlace_method && !m_use_rgb)
|
||||
@@ -414,6 +355,8 @@ bool SPngDecoder::readData(Mat &img)
|
||||
}
|
||||
else if (fmt == SPNG_FMT_PNG)
|
||||
{
|
||||
AutoBuffer<unsigned char> bufcn4;
|
||||
bufcn4.allocate(image_width);
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
@@ -421,16 +364,17 @@ bool SPngDecoder::readData(Mat &img)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, buffer[row_info.row_num], image_width);
|
||||
|
||||
if (ihdr.interlace_method == 0 && !m_use_rgb)
|
||||
{
|
||||
icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort *>(buffer[row_info.row_num]), 0,
|
||||
reinterpret_cast<ushort *>(buffer[row_info.row_num]), 0, Size(m_width, 1));
|
||||
icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort*>(buffer[row_info.row_num]), 0,
|
||||
reinterpret_cast<ushort*>(buffer[row_info.row_num]), 0, Size(m_width, 1));
|
||||
}
|
||||
} while (ret == SPNG_OK);
|
||||
if (ihdr.interlace_method && !m_use_rgb)
|
||||
{
|
||||
icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort *>(img.data), step,
|
||||
reinterpret_cast<ushort *>(img.data), step, Size(m_width, m_height));
|
||||
icvCvt_RGB2BGR_16u_C3R(reinterpret_cast<const ushort*>(img.data), step,
|
||||
reinterpret_cast<ushort*>(img.data), step, Size(m_width, m_height));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -454,7 +398,6 @@ bool SPngDecoder::readData(Mat &img)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
ret = spng_get_row_info(png_ptr, &row_info);
|
||||
@@ -462,8 +405,8 @@ bool SPngDecoder::readData(Mat &img)
|
||||
break;
|
||||
|
||||
ret = spng_decode_row(png_ptr, img.data + row_info.row_num * image_width, image_width);
|
||||
|
||||
} while (ret == SPNG_OK);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == SPNG_EOI)
|
||||
@@ -687,45 +630,32 @@ bool SPngEncoder::write(const Mat &img, const std::vector<int> ¶ms)
|
||||
|
||||
}
|
||||
|
||||
void spngCvt_BGR2Gray_8u_C3C1R(const uchar *bgr, int bgr_step,
|
||||
uchar *gray, int gray_step,
|
||||
cv::Size size, int _swap_rb)
|
||||
void spngCvt_BGRA2Gray_8u_CnC1R(const uchar *bgr, int bgr_step,
|
||||
uchar *gray, int gray_step,
|
||||
cv::Size size, int ncn, int _swap_rb)
|
||||
{
|
||||
int i;
|
||||
for (; size.height--; gray += gray_step)
|
||||
{
|
||||
double cBGR0 = 0.1140441895;
|
||||
double cBGR2 = 0.2989807129;
|
||||
if (_swap_rb)
|
||||
std::swap(cBGR0, cBGR2);
|
||||
for (i = 0; i < size.width; i++, bgr += 3)
|
||||
{
|
||||
int t = static_cast<int>(cBGR0 * bgr[0] + 0.5869750977 * bgr[1] + cBGR2 * bgr[2]);
|
||||
gray[i] = (uchar)t;
|
||||
}
|
||||
|
||||
bgr += bgr_step - size.width * 3;
|
||||
}
|
||||
}
|
||||
|
||||
void spngCvt_BGRA2Gray_8u_C4C1R(const uchar *bgra, int rgba_step,
|
||||
uchar *gray, int gray_step,
|
||||
cv::Size size, int _swap_rb)
|
||||
{
|
||||
for (; size.height--; gray += gray_step)
|
||||
{
|
||||
double cBGR0 = 0.1140441895;
|
||||
double cBGR1 = 0.5869750977;
|
||||
double cBGR2 = 0.2989807129;
|
||||
int cBGR0 = 3737;
|
||||
int cBGR1 = 19234;
|
||||
int cBGR2 = 9797;
|
||||
|
||||
if (_swap_rb)
|
||||
std::swap(cBGR0, cBGR2);
|
||||
for (int i = 0; i < size.width; i++, bgra += 4)
|
||||
for (i = 0; i < size.width; i++, bgr += ncn)
|
||||
{
|
||||
gray[i] = cv::saturate_cast<uchar>(cBGR0 * bgra[0] + cBGR1 * bgra[1] + cBGR2 * bgra[2]);
|
||||
if (bgr[0] != bgr[1] || bgr[0] != bgr[2])
|
||||
{
|
||||
gray[i] = (uchar)((cBGR0 * bgr[0] + cBGR1 * bgr[1] + cBGR2 * bgr[2]) >> 15);
|
||||
}
|
||||
else
|
||||
{
|
||||
gray[i] = bgr[0];
|
||||
}
|
||||
}
|
||||
|
||||
bgra += rgba_step - size.width * 4;
|
||||
bgr += bgr_step - size.width * ncn;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -735,15 +665,43 @@ void spngCvt_BGRA2Gray_16u_CnC1R(const ushort *bgr, int bgr_step,
|
||||
{
|
||||
for (; size.height--; gray += gray_step)
|
||||
{
|
||||
double cBGR0 = 0.1140441895;
|
||||
double cBGR1 = 0.5869750977;
|
||||
double cBGR2 = 0.2989807129;
|
||||
int cBGR0 = 3737;
|
||||
int cBGR1 = 19234;
|
||||
int cBGR2 = 9797;
|
||||
|
||||
if (_swap_rb)
|
||||
std::swap(cBGR0, cBGR2);
|
||||
for (int i = 0; i < size.width; i++, bgr += ncn)
|
||||
{
|
||||
gray[i] = (ushort)(cBGR0 * bgr[0] + cBGR1 * bgr[1] + cBGR2 * bgr[2]);
|
||||
if (bgr[0] != bgr[1] || bgr[0] != bgr[2])
|
||||
{
|
||||
gray[i] = (ushort)((cBGR0 * bgr[0] + cBGR1 * bgr[1] + cBGR2 * bgr[2] + 16384) >> 15);
|
||||
}
|
||||
else
|
||||
{
|
||||
gray[i] = bgr[0];
|
||||
}
|
||||
}
|
||||
|
||||
bgr += bgr_step - size.width * ncn;
|
||||
}
|
||||
}
|
||||
|
||||
void spngCvt_BGRA2Gray_16u28u_CnC1R(const ushort *bgr, int bgr_step,
|
||||
uchar *gray, int gray_step,
|
||||
cv::Size size, int ncn, int _swap_rb)
|
||||
{
|
||||
int cBGR0 = 3737;
|
||||
int cBGR1 = 19234;
|
||||
int cBGR2 = 9797;
|
||||
if (_swap_rb)
|
||||
std::swap(cBGR0, cBGR2);
|
||||
|
||||
for (; size.height--; gray += gray_step)
|
||||
{
|
||||
for (int i = 0; i < size.width; i++, bgr += ncn)
|
||||
{
|
||||
gray[i] = static_cast<uchar>(((cBGR0 * bgr[0] + cBGR1 * bgr[1] + cBGR2 * bgr[2] + 16384) >> 15) >> 8);
|
||||
}
|
||||
|
||||
bgr += bgr_step - size.width * ncn;
|
||||
|
||||
@@ -155,14 +155,16 @@ bool WebPDecoder::readHeader()
|
||||
webp_data.size = data.total();
|
||||
|
||||
WebPAnimDecoderOptions dec_options;
|
||||
WebPAnimDecoderOptionsInit(&dec_options);
|
||||
if (!WebPAnimDecoderOptionsInit(&dec_options))
|
||||
CV_Error(Error::StsInternal, "Failed to initialize animated WebP decoding options");
|
||||
|
||||
dec_options.color_mode = m_use_rgb ? MODE_RGBA : MODE_BGRA;
|
||||
anim_decoder.reset(WebPAnimDecoderNew(&webp_data, &dec_options));
|
||||
CV_Assert(anim_decoder.get() && "Error parsing image");
|
||||
|
||||
WebPAnimInfo anim_info;
|
||||
WebPAnimDecoderGetInfo(anim_decoder.get(), &anim_info);
|
||||
if (!WebPAnimDecoderGetInfo(anim_decoder.get(), &anim_info))
|
||||
CV_Error(Error::StsInternal, "Failed to get animated WebP information");
|
||||
m_animation.loop_count = anim_info.loop_count;
|
||||
|
||||
m_animation.bgcolor[0] = (anim_info.bgcolor >> 24) & 0xFF;
|
||||
@@ -216,7 +218,8 @@ bool WebPDecoder::readData(Mat &img)
|
||||
uint8_t* buf;
|
||||
int timestamp;
|
||||
|
||||
WebPAnimDecoderGetNext(anim_decoder.get(), &buf, ×tamp);
|
||||
if (!WebPAnimDecoderGetNext(anim_decoder.get(), &buf, ×tamp))
|
||||
CV_Error(Error::StsInternal, "Failed to decode animated WebP frame");
|
||||
Mat tmp(Size(m_width, m_height), CV_8UC4, buf);
|
||||
|
||||
if (img.type() == CV_8UC1)
|
||||
@@ -446,7 +449,6 @@ bool WebPEncoder::writeanimation(const Animation& animation, const std::vector<i
|
||||
pic.height = height;
|
||||
pic.use_argb = 1;
|
||||
pic.argb_stride = width;
|
||||
WebPEncode(&config, &pic);
|
||||
|
||||
bool is_input_rgba = animation.frames[0].channels() == 4;
|
||||
Size canvas_size = Size(animation.frames[0].cols,animation.frames[0].rows);
|
||||
|
||||
@@ -98,6 +98,9 @@ static inline int calcType(int type, int flags)
|
||||
if( (flags & IMREAD_ANYDEPTH) == 0 )
|
||||
type = CV_MAKETYPE(CV_8U, CV_MAT_CN(type));
|
||||
|
||||
//if( (flags & IMREAD_ANYCOLOR) != 0 /*&& CV_MAT_CN(type) > 1*/ )
|
||||
// type = CV_MAKETYPE(CV_MAT_DEPTH(type), CV_MAT_CN(type));
|
||||
//else if( (flags & IMREAD_COLOR) != 0 || (flags & IMREAD_COLOR_RGB) != 0 )
|
||||
if( (flags & IMREAD_COLOR) != 0 || (flags & IMREAD_COLOR_RGB) != 0 ||
|
||||
((flags & IMREAD_ANYCOLOR) != 0 && CV_MAT_CN(type) > 1) )
|
||||
type = CV_MAKETYPE(CV_MAT_DEPTH(type), 3);
|
||||
@@ -410,6 +413,76 @@ static void ApplyExifOrientation(ExifEntry_t orientationTag, OutputArray img)
|
||||
}
|
||||
}
|
||||
|
||||
static void readMetadata(ImageDecoder& decoder,
|
||||
std::vector<int>* metadata_types,
|
||||
OutputArrayOfArrays metadata)
|
||||
{
|
||||
if (!metadata_types)
|
||||
return;
|
||||
int kind = metadata.kind();
|
||||
void* obj = metadata.getObj();
|
||||
std::vector<Mat>* matvector = nullptr;
|
||||
std::vector<std::vector<uchar> >* vecvector = nullptr;
|
||||
if (kind == _InputArray::STD_VECTOR_MAT) {
|
||||
matvector = (std::vector<Mat>*)obj;
|
||||
} else if (kind == _InputArray::STD_VECTOR_VECTOR) {
|
||||
int elemtype = metadata.type(0);
|
||||
CV_Assert(elemtype == CV_8UC1 || elemtype == CV_8SC1);
|
||||
vecvector = (std::vector<std::vector<uint8_t> >*)obj;
|
||||
} else {
|
||||
CV_Error(Error::StsBadArg,
|
||||
"unsupported metadata type, should be a vector of matrices or vector of byte vectors");
|
||||
}
|
||||
std::vector<Mat> src_metadata;
|
||||
for (int m = (int)IMAGE_METADATA_EXIF; m <= (int)IMAGE_METADATA_MAX; m++) {
|
||||
Mat mm = decoder->getMetadata((ImageMetadataType)m);
|
||||
if (!mm.empty()) {
|
||||
CV_Assert(mm.isContinuous());
|
||||
CV_Assert(mm.elemSize() == 1u);
|
||||
metadata_types->push_back(m);
|
||||
src_metadata.push_back(mm);
|
||||
}
|
||||
}
|
||||
size_t nmetadata = metadata_types->size();
|
||||
if (matvector) {
|
||||
matvector->resize(nmetadata);
|
||||
for (size_t m = 0; m < nmetadata; m++)
|
||||
src_metadata[m].copyTo(matvector->at(m));
|
||||
} else {
|
||||
vecvector->resize(nmetadata);
|
||||
for (size_t m = 0; m < nmetadata; m++) {
|
||||
const Mat& mm = src_metadata[m];
|
||||
const uchar* data = (uchar*)mm.data;
|
||||
vecvector->at(m).assign(data, data + mm.total());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char* metadataTypeToString(ImageMetadataType type)
|
||||
{
|
||||
return type == IMAGE_METADATA_EXIF ? "Exif" :
|
||||
type == IMAGE_METADATA_XMP ? "XMP" :
|
||||
type == IMAGE_METADATA_ICCP ? "ICC Profile" : "???";
|
||||
}
|
||||
|
||||
static void addMetadata(ImageEncoder& encoder,
|
||||
const std::vector<int>& metadata_types,
|
||||
InputArrayOfArrays metadata)
|
||||
{
|
||||
size_t nmetadata_chunks = metadata_types.size();
|
||||
for (size_t i = 0; i < nmetadata_chunks; i++) {
|
||||
ImageMetadataType metadata_type = (ImageMetadataType)metadata_types[i];
|
||||
bool ok = encoder->addMetadata(metadata_type, metadata.getMat((int)i));
|
||||
if (!ok) {
|
||||
std::string desc = encoder->getDescription();
|
||||
CV_LOG_WARNING(NULL, "Imgcodecs: metadata of type '"
|
||||
<< metadataTypeToString(metadata_type)
|
||||
<< "' is not supported when encoding '"
|
||||
<< desc << "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an image into memory and return the information
|
||||
*
|
||||
@@ -419,11 +492,15 @@ static void ApplyExifOrientation(ExifEntry_t orientationTag, OutputArray img)
|
||||
*
|
||||
*/
|
||||
static bool
|
||||
imread_( const String& filename, int flags, OutputArray mat )
|
||||
imread_( const String& filename, int flags, OutputArray mat,
|
||||
std::vector<int>* metadata_types, OutputArrayOfArrays metadata)
|
||||
{
|
||||
/// Search for the relevant decoder to handle the imagery
|
||||
ImageDecoder decoder;
|
||||
|
||||
if (metadata_types)
|
||||
metadata_types->clear();
|
||||
|
||||
#ifdef HAVE_GDAL
|
||||
if(flags != IMREAD_UNCHANGED && (flags & IMREAD_LOAD_GDAL) == IMREAD_LOAD_GDAL ){
|
||||
decoder = GdalDecoder().newDecoder();
|
||||
@@ -501,13 +578,16 @@ imread_( const String& filename, int flags, OutputArray mat )
|
||||
Mat real_mat = mat.getMat();
|
||||
const void * original_ptr = real_mat.data;
|
||||
bool success = false;
|
||||
decoder->resetFrameCount(); // this is needed for PngDecoder. it should be called before decoder->readData()
|
||||
try
|
||||
{
|
||||
if (decoder->readData(real_mat))
|
||||
{
|
||||
CV_CheckTrue((decoder->getFrameCount() > 1) || original_ptr == real_mat.data, "Internal imread issue");
|
||||
CV_CheckTrue(original_ptr == real_mat.data, "Internal imread issue");
|
||||
success = true;
|
||||
}
|
||||
|
||||
readMetadata(decoder, metadata_types, metadata);
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
@@ -661,7 +741,24 @@ Mat imread( const String& filename, int flags )
|
||||
Mat img;
|
||||
|
||||
/// load the data
|
||||
imread_( filename, flags, img );
|
||||
imread_( filename, flags, img, nullptr, noArray() );
|
||||
|
||||
/// return a reference to the data
|
||||
return img;
|
||||
}
|
||||
|
||||
Mat imreadWithMetadata( const String& filename,
|
||||
std::vector<int>& metadata_types,
|
||||
OutputArrayOfArrays metadata,
|
||||
int flags )
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
/// create the basic container
|
||||
Mat img;
|
||||
|
||||
/// load the data
|
||||
imread_( filename, flags, img, &metadata_types, metadata );
|
||||
|
||||
/// return a reference to the data
|
||||
return img;
|
||||
@@ -672,7 +769,7 @@ void imread( const String& filename, OutputArray dst, int flags )
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
/// load the data
|
||||
imread_(filename, flags, dst);
|
||||
imread_(filename, flags, dst, nullptr, noArray());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -800,6 +897,7 @@ imreadanimation_(const String& filename, int flags, int start, int count, Animat
|
||||
}
|
||||
animation.bgcolor = decoder->animation().bgcolor;
|
||||
animation.loop_count = decoder->animation().loop_count;
|
||||
animation.still_image = decoder->animation().still_image;
|
||||
|
||||
return success;
|
||||
}
|
||||
@@ -910,6 +1008,7 @@ static bool imdecodeanimation_(InputArray buf, int flags, int start, int count,
|
||||
}
|
||||
animation.bgcolor = decoder->animation().bgcolor;
|
||||
animation.loop_count = decoder->animation().loop_count;
|
||||
animation.still_image = decoder->animation().still_image;
|
||||
|
||||
return success;
|
||||
}
|
||||
@@ -943,6 +1042,8 @@ size_t imcount(const String& filename, int flags)
|
||||
|
||||
|
||||
static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
|
||||
const std::vector<int>& metadata_types,
|
||||
InputArrayOfArrays metadata,
|
||||
const std::vector<int>& params, bool flipv )
|
||||
{
|
||||
bool isMultiImg = img_vec.size() > 1;
|
||||
@@ -957,7 +1058,12 @@ static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
|
||||
Mat image = img_vec[page];
|
||||
CV_Assert(!image.empty());
|
||||
|
||||
#ifdef HAVE_OPENEXR
|
||||
CV_Assert( image.channels() == 1 || image.channels() == 3 || image.channels() == 4 || encoder.dynamicCast<ExrEncoder>() );
|
||||
#else
|
||||
CV_Assert( image.channels() == 1 || image.channels() == 3 || image.channels() == 4 );
|
||||
#endif
|
||||
|
||||
|
||||
Mat temp;
|
||||
if( !encoder->isFormatSupported(image.depth()) )
|
||||
@@ -978,6 +1084,7 @@ static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
|
||||
}
|
||||
|
||||
encoder->setDestination( filename );
|
||||
addMetadata(encoder, metadata_types, metadata);
|
||||
|
||||
CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs");
|
||||
CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), "");
|
||||
@@ -1034,7 +1141,26 @@ bool imwrite( const String& filename, InputArray _img,
|
||||
img_vec.push_back(_img.getMat());
|
||||
|
||||
CV_Assert(!img_vec.empty());
|
||||
return imwrite_(filename, img_vec, params, false);
|
||||
return imwrite_(filename, img_vec, {}, noArray(), params, false);
|
||||
}
|
||||
|
||||
bool imwriteWithMetadata( const String& filename, InputArray _img,
|
||||
const std::vector<int>& metadata_types,
|
||||
InputArrayOfArrays metadata,
|
||||
const std::vector<int>& params )
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
CV_Assert(!_img.empty());
|
||||
|
||||
std::vector<Mat> img_vec;
|
||||
if (_img.isMatVector() || _img.isUMatVector())
|
||||
_img.getMatVector(img_vec);
|
||||
else
|
||||
img_vec.push_back(_img.getMat());
|
||||
|
||||
CV_Assert(!img_vec.empty());
|
||||
return imwrite_(filename, img_vec, metadata_types, metadata, params, false);
|
||||
}
|
||||
|
||||
static bool imwriteanimation_(const String& filename, const Animation& animation, const std::vector<int>& params)
|
||||
@@ -1119,8 +1245,13 @@ bool imencodeanimation(const String& ext, const Animation& animation, std::vecto
|
||||
}
|
||||
|
||||
static bool
|
||||
imdecode_( const Mat& buf, int flags, Mat& mat )
|
||||
imdecode_( const Mat& buf, int flags, Mat& mat,
|
||||
std::vector<int>* metadata_types,
|
||||
OutputArrayOfArrays metadata )
|
||||
{
|
||||
if (metadata_types)
|
||||
metadata_types->clear();
|
||||
|
||||
CV_Assert(!buf.empty());
|
||||
CV_Assert(buf.isContinuous());
|
||||
CV_Assert(buf.checkVector(1, CV_8U) > 0);
|
||||
@@ -1210,6 +1341,7 @@ imdecode_( const Mat& buf, int flags, Mat& mat )
|
||||
{
|
||||
if (decoder->readData(mat))
|
||||
success = true;
|
||||
readMetadata(decoder, metadata_types, metadata);
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
@@ -1253,7 +1385,7 @@ Mat imdecode( InputArray _buf, int flags )
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
Mat buf = _buf.getMat(), img;
|
||||
if (!imdecode_(buf, flags, img))
|
||||
if (!imdecode_(buf, flags, img, nullptr, noArray()))
|
||||
img.release();
|
||||
|
||||
return img;
|
||||
@@ -1265,12 +1397,24 @@ Mat imdecode( InputArray _buf, int flags, Mat* dst )
|
||||
|
||||
Mat buf = _buf.getMat(), img;
|
||||
dst = dst ? dst : &img;
|
||||
if (imdecode_(buf, flags, *dst))
|
||||
if (imdecode_(buf, flags, *dst, nullptr, noArray()))
|
||||
return *dst;
|
||||
else
|
||||
return cv::Mat();
|
||||
}
|
||||
|
||||
Mat imdecodeWithMetadata( InputArray _buf, std::vector<int>& metadata_types,
|
||||
OutputArrayOfArrays metadata, int flags )
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
Mat buf = _buf.getMat(), img;
|
||||
if (!imdecode_(buf, flags, img, &metadata_types, metadata))
|
||||
img.release();
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
static bool
|
||||
imdecodemulti_(const Mat& buf, int flags, std::vector<Mat>& mats, int start, int count)
|
||||
{
|
||||
@@ -1426,8 +1570,10 @@ bool imdecodemulti(InputArray _buf, int flags, CV_OUT std::vector<Mat>& mats, co
|
||||
}
|
||||
}
|
||||
|
||||
bool imencode( const String& ext, InputArray _img,
|
||||
std::vector<uchar>& buf, const std::vector<int>& params )
|
||||
bool imencodeWithMetadata( const String& ext, InputArray _img,
|
||||
const std::vector<int>& metadata_types,
|
||||
InputArrayOfArrays metadata,
|
||||
std::vector<uchar>& buf, const std::vector<int>& params )
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
@@ -1452,7 +1598,11 @@ bool imencode( const String& ext, InputArray _img,
|
||||
CV_Assert(!image.empty());
|
||||
|
||||
const int channels = image.channels();
|
||||
#ifdef HAVE_OPENEXR
|
||||
CV_Assert( channels == 1 || channels == 3 || channels == 4 || encoder.dynamicCast<ExrEncoder>() );
|
||||
#else
|
||||
CV_Assert( channels == 1 || channels == 3 || channels == 4 );
|
||||
#endif
|
||||
|
||||
Mat temp;
|
||||
if( !encoder->isFormatSupported(image.depth()) )
|
||||
@@ -1477,6 +1627,7 @@ bool imencode( const String& ext, InputArray _img,
|
||||
code = encoder->setDestination(filename);
|
||||
CV_Assert( code );
|
||||
}
|
||||
addMetadata(encoder, metadata_types, metadata);
|
||||
|
||||
try {
|
||||
if (!isMultiImg)
|
||||
@@ -1513,6 +1664,12 @@ bool imencode( const String& ext, InputArray _img,
|
||||
return code;
|
||||
}
|
||||
|
||||
bool imencode( const String& ext, InputArray img,
|
||||
std::vector<uchar>& buf, const std::vector<int>& params_ )
|
||||
{
|
||||
return imencodeWithMetadata(ext, img, {}, noArray(), buf, params_);
|
||||
}
|
||||
|
||||
bool imencodemulti( const String& ext, InputArrayOfArrays imgs,
|
||||
std::vector<uchar>& buf, const std::vector<int>& params)
|
||||
{
|
||||
|
||||
@@ -636,6 +636,52 @@ TEST(Imgcodecs_APNG, imencode_animation)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_APNG, animation_has_hidden_frame)
|
||||
{
|
||||
// Set the path to the test image directory and filename for loading.
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filename = root + "readwrite/033.png";
|
||||
Animation animation1, animation2, animation3;
|
||||
|
||||
imreadanimation(filename, animation1);
|
||||
|
||||
EXPECT_FALSE(animation1.still_image.empty());
|
||||
EXPECT_EQ((size_t)2, animation1.frames.size());
|
||||
|
||||
std::vector<unsigned char> buf;
|
||||
EXPECT_TRUE(imencodeanimation(".png", animation1, buf));
|
||||
EXPECT_TRUE(imdecodeanimation(buf, animation2));
|
||||
|
||||
EXPECT_FALSE(animation2.still_image.empty());
|
||||
EXPECT_EQ(animation1.frames.size(), animation2.frames.size());
|
||||
|
||||
animation1.frames.erase(animation1.frames.begin());
|
||||
animation1.durations.erase(animation1.durations.begin());
|
||||
EXPECT_TRUE(imencodeanimation(".png", animation1, buf));
|
||||
EXPECT_TRUE(imdecodeanimation(buf, animation3));
|
||||
|
||||
EXPECT_FALSE(animation1.still_image.empty());
|
||||
EXPECT_TRUE(animation3.still_image.empty());
|
||||
EXPECT_EQ((size_t)1, animation3.frames.size());
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_APNG, animation_imread_preview)
|
||||
{
|
||||
// Set the path to the test image directory and filename for loading.
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filename = root + "readwrite/034.png";
|
||||
cv::Mat imread_result;
|
||||
cv::imread(filename, imread_result, cv::IMREAD_UNCHANGED);
|
||||
EXPECT_FALSE(imread_result.empty());
|
||||
|
||||
Animation animation;
|
||||
ASSERT_TRUE(imreadanimation(filename, animation));
|
||||
EXPECT_FALSE(animation.still_image.empty());
|
||||
EXPECT_EQ((size_t)2, animation.frames.size());
|
||||
|
||||
EXPECT_EQ(0, cv::norm(animation.still_image, imread_result, cv::NORM_INF));
|
||||
}
|
||||
|
||||
#endif // HAVE_PNG
|
||||
|
||||
#if defined(HAVE_PNG) || defined(HAVE_SPNG)
|
||||
@@ -676,7 +722,7 @@ TEST(Imgcodecs_APNG, imread_animation_16u)
|
||||
img = imread(filename, IMREAD_ANYDEPTH);
|
||||
ASSERT_FALSE(img.empty());
|
||||
EXPECT_TRUE(img.type() == CV_16UC1);
|
||||
EXPECT_EQ(19519, img.at<ushort>(0, 0));
|
||||
EXPECT_EQ(19517, img.at<ushort>(0, 0));
|
||||
|
||||
img = imread(filename, IMREAD_COLOR | IMREAD_ANYDEPTH);
|
||||
ASSERT_FALSE(img.empty());
|
||||
|
||||
@@ -148,7 +148,246 @@ const std::vector<std::string> exif_files
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Imgcodecs, Exif,
|
||||
testing::ValuesIn(exif_files));
|
||||
testing::ValuesIn(exif_files));
|
||||
|
||||
static Mat makeCirclesImage(Size size, int type, int nbits)
|
||||
{
|
||||
Mat img(size, type);
|
||||
img.setTo(Scalar::all(0));
|
||||
RNG& rng = theRNG();
|
||||
int maxval = (int)(1 << nbits);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
int x = rng.uniform(0, img.cols);
|
||||
int y = rng.uniform(0, img.rows);
|
||||
int radius = rng.uniform(5, std::min(img.cols, img.rows)/5);
|
||||
int b = rng.uniform(0, maxval);
|
||||
int g = rng.uniform(0, maxval);
|
||||
int r = rng.uniform(0, maxval);
|
||||
circle(img, Point(x, y), radius, Scalar(b, g, r), -1, LINE_AA);
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
#ifdef HAVE_AVIF
|
||||
TEST(Imgcodecs_Avif, ReadWriteWithExif)
|
||||
{
|
||||
static const uchar exif_data[] = {
|
||||
'M', 'M', 0, '*', 0, 0, 0, 8, 0, 10, 1, 0, 0, 4, 0, 0, 0, 1, 0, 0, 5,
|
||||
0, 1, 1, 0, 4, 0, 0, 0, 1, 0, 0, 2, 208, 1, 2, 0, 3, 0, 0, 0, 1,
|
||||
0, 10, 0, 0, 1, 18, 0, 3, 0, 0, 0, 1, 0, 1, 0, 0, 1, 14, 0, 2, 0, 0,
|
||||
0, '"', 0, 0, 0, 176, 1, '1', 0, 2, 0, 0, 0, 7, 0, 0, 0, 210, 1, 26,
|
||||
0, 5, 0, 0, 0, 1, 0, 0, 0, 218, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0,
|
||||
226, 1, '(', 0, 3, 0, 0, 0, 1, 0, 2, 0, 0, 135, 'i', 0, 4, 0, 0, 0,
|
||||
1, 0, 0, 0, 134, 0, 0, 0, 0, 0, 3, 144, 0, 0, 7, 0, 0, 0, 4, '0', '2',
|
||||
'2', '1', 160, 2, 0, 4, 0, 0, 0, 1, 0, 0, 5, 0, 160, 3, 0, 4, 0, 0,
|
||||
0, 1, 0, 0, 2, 208, 0, 0, 0, 0, 'S', 'a', 'm', 'p', 'l', 'e', ' ', '1', '0',
|
||||
'-', 'b', 'i', 't', ' ', 'i', 'm', 'a', 'g', 'e', ' ', 'w', 'i', 't', 'h', ' ',
|
||||
'm', 'e', 't', 'a', 'd', 'a', 't', 'a', 0, 'O', 'p', 'e', 'n', 'C', 'V', 0, 0,
|
||||
0, 0, 0, 'H', 0, 0, 0, 1, 0, 0, 0, 'H', 0, 0, 0, 1
|
||||
};
|
||||
|
||||
int avif_nbits = 10;
|
||||
int avif_speed = 10;
|
||||
int avif_quality = 85;
|
||||
int imgdepth = avif_nbits > 8 ? CV_16U : CV_8U;
|
||||
int imgtype = CV_MAKETYPE(imgdepth, 3);
|
||||
const string outputname = cv::tempfile(".avif");
|
||||
Mat img = makeCirclesImage(Size(1280, 720), imgtype, avif_nbits);
|
||||
|
||||
std::vector<int> metadata_types = {IMAGE_METADATA_EXIF};
|
||||
std::vector<std::vector<uchar> > metadata(1);
|
||||
metadata[0].assign(exif_data, exif_data + sizeof(exif_data));
|
||||
|
||||
std::vector<int> write_params = {
|
||||
IMWRITE_AVIF_DEPTH, avif_nbits,
|
||||
IMWRITE_AVIF_SPEED, avif_speed,
|
||||
IMWRITE_AVIF_QUALITY, avif_quality
|
||||
};
|
||||
|
||||
imwriteWithMetadata(outputname, img, metadata_types, metadata, write_params);
|
||||
std::vector<uchar> compressed;
|
||||
imencodeWithMetadata(outputname, img, metadata_types, metadata, compressed, write_params);
|
||||
|
||||
std::vector<int> read_metadata_types, read_metadata_types2;
|
||||
std::vector<std::vector<uchar> > read_metadata, read_metadata2;
|
||||
Mat img2 = imreadWithMetadata(outputname, read_metadata_types, read_metadata, IMREAD_UNCHANGED);
|
||||
Mat img3 = imdecodeWithMetadata(compressed, read_metadata_types2, read_metadata2, IMREAD_UNCHANGED);
|
||||
EXPECT_EQ(img2.cols, img.cols);
|
||||
EXPECT_EQ(img2.rows, img.rows);
|
||||
EXPECT_EQ(img2.type(), imgtype);
|
||||
EXPECT_EQ(read_metadata_types, read_metadata_types2);
|
||||
EXPECT_GE(read_metadata_types.size(), 1u);
|
||||
EXPECT_EQ(read_metadata, read_metadata2);
|
||||
EXPECT_EQ(read_metadata_types[0], IMAGE_METADATA_EXIF);
|
||||
EXPECT_EQ(read_metadata_types.size(), read_metadata.size());
|
||||
EXPECT_EQ(read_metadata[0], metadata[0]);
|
||||
EXPECT_EQ(cv::norm(img2, img3, NORM_INF), 0.);
|
||||
double mse = cv::norm(img, img2, NORM_L2SQR)/(img.rows*img.cols);
|
||||
EXPECT_LT(mse, 1500);
|
||||
remove(outputname.c_str());
|
||||
}
|
||||
#endif // HAVE_AVIF
|
||||
|
||||
TEST(Imgcodecs_Jpeg, ReadWriteWithExif)
|
||||
{
|
||||
static const uchar exif_data[] = {
|
||||
'M', 'M', 0, '*', 0, 0, 0, 8, 0, 10, 1, 0, 0, 4, 0, 0, 0, 1, 0, 0, 5,
|
||||
0, 1, 1, 0, 4, 0, 0, 0, 1, 0, 0, 2, 208, 1, 2, 0, 3, 0, 0, 0, 1,
|
||||
0, 8, 0, 0, 1, 18, 0, 3, 0, 0, 0, 1, 0, 1, 0, 0, 1, 14, 0, 2, 0, 0,
|
||||
0, '!', 0, 0, 0, 176, 1, '1', 0, 2, 0, 0, 0, 7, 0, 0, 0, 210, 1, 26,
|
||||
0, 5, 0, 0, 0, 1, 0, 0, 0, 218, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0,
|
||||
226, 1, '(', 0, 3, 0, 0, 0, 1, 0, 2, 0, 0, 135, 'i', 0, 4, 0, 0, 0,
|
||||
1, 0, 0, 0, 134, 0, 0, 0, 0, 0, 3, 144, 0, 0, 7, 0, 0, 0, 4, '0', '2',
|
||||
'2', '1', 160, 2, 0, 4, 0, 0, 0, 1, 0, 0, 5, 0, 160, 3, 0, 4, 0, 0,
|
||||
0, 1, 0, 0, 2, 208, 0, 0, 0, 0, 'S', 'a', 'm', 'p', 'l', 'e', ' ', '8', '-',
|
||||
'b', 'i', 't', ' ', 'i', 'm', 'a', 'g', 'e', ' ', 'w', 'i', 't', 'h', ' ', 'm',
|
||||
'e', 't', 'a', 'd', 'a', 't', 'a', 0, 0, 'O', 'p', 'e', 'n', 'C', 'V', 0, 0,
|
||||
0, 0, 0, 'H', 0, 0, 0, 1, 0, 0, 0, 'H', 0, 0, 0, 1
|
||||
};
|
||||
|
||||
int jpeg_quality = 95;
|
||||
int imgtype = CV_MAKETYPE(CV_8U, 3);
|
||||
const string outputname = cv::tempfile(".jpeg");
|
||||
Mat img = makeCirclesImage(Size(1280, 720), imgtype, 8);
|
||||
|
||||
std::vector<int> metadata_types = {IMAGE_METADATA_EXIF};
|
||||
std::vector<std::vector<uchar> > metadata(1);
|
||||
metadata[0].assign(exif_data, exif_data + sizeof(exif_data));
|
||||
|
||||
std::vector<int> write_params = {
|
||||
IMWRITE_JPEG_QUALITY, jpeg_quality
|
||||
};
|
||||
|
||||
imwriteWithMetadata(outputname, img, metadata_types, metadata, write_params);
|
||||
std::vector<uchar> compressed;
|
||||
imencodeWithMetadata(outputname, img, metadata_types, metadata, compressed, write_params);
|
||||
|
||||
std::vector<int> read_metadata_types, read_metadata_types2;
|
||||
std::vector<std::vector<uchar> > read_metadata, read_metadata2;
|
||||
Mat img2 = imreadWithMetadata(outputname, read_metadata_types, read_metadata, IMREAD_UNCHANGED);
|
||||
Mat img3 = imdecodeWithMetadata(compressed, read_metadata_types2, read_metadata2, IMREAD_UNCHANGED);
|
||||
EXPECT_EQ(img2.cols, img.cols);
|
||||
EXPECT_EQ(img2.rows, img.rows);
|
||||
EXPECT_EQ(img2.type(), imgtype);
|
||||
EXPECT_EQ(read_metadata_types, read_metadata_types2);
|
||||
EXPECT_GE(read_metadata_types.size(), 1u);
|
||||
EXPECT_EQ(read_metadata, read_metadata2);
|
||||
EXPECT_EQ(read_metadata_types[0], IMAGE_METADATA_EXIF);
|
||||
EXPECT_EQ(read_metadata_types.size(), read_metadata.size());
|
||||
EXPECT_EQ(read_metadata[0], metadata[0]);
|
||||
EXPECT_EQ(cv::norm(img2, img3, NORM_INF), 0.);
|
||||
double mse = cv::norm(img, img2, NORM_L2SQR)/(img.rows*img.cols);
|
||||
EXPECT_LT(mse, 80);
|
||||
remove(outputname.c_str());
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_Png, ReadWriteWithExif)
|
||||
{
|
||||
static const uchar exif_data[] = {
|
||||
'M', 'M', 0, '*', 0, 0, 0, 8, 0, 10, 1, 0, 0, 4, 0, 0, 0, 1, 0, 0, 5,
|
||||
0, 1, 1, 0, 4, 0, 0, 0, 1, 0, 0, 2, 208, 1, 2, 0, 3, 0, 0, 0, 1,
|
||||
0, 8, 0, 0, 1, 18, 0, 3, 0, 0, 0, 1, 0, 1, 0, 0, 1, 14, 0, 2, 0, 0,
|
||||
0, '!', 0, 0, 0, 176, 1, '1', 0, 2, 0, 0, 0, 7, 0, 0, 0, 210, 1, 26,
|
||||
0, 5, 0, 0, 0, 1, 0, 0, 0, 218, 1, 27, 0, 5, 0, 0, 0, 1, 0, 0, 0,
|
||||
226, 1, '(', 0, 3, 0, 0, 0, 1, 0, 2, 0, 0, 135, 'i', 0, 4, 0, 0, 0,
|
||||
1, 0, 0, 0, 134, 0, 0, 0, 0, 0, 3, 144, 0, 0, 7, 0, 0, 0, 4, '0', '2',
|
||||
'2', '1', 160, 2, 0, 4, 0, 0, 0, 1, 0, 0, 5, 0, 160, 3, 0, 4, 0, 0,
|
||||
0, 1, 0, 0, 2, 208, 0, 0, 0, 0, 'S', 'a', 'm', 'p', 'l', 'e', ' ', '8', '-',
|
||||
'b', 'i', 't', ' ', 'i', 'm', 'a', 'g', 'e', ' ', 'w', 'i', 't', 'h', ' ', 'm',
|
||||
'e', 't', 'a', 'd', 'a', 't', 'a', 0, 0, 'O', 'p', 'e', 'n', 'C', 'V', 0, 0,
|
||||
0, 0, 0, 'H', 0, 0, 0, 1, 0, 0, 0, 'H', 0, 0, 0, 1
|
||||
};
|
||||
|
||||
int png_compression = 3;
|
||||
int imgtype = CV_MAKETYPE(CV_8U, 3);
|
||||
const string outputname = cv::tempfile(".png");
|
||||
Mat img = makeCirclesImage(Size(1280, 720), imgtype, 8);
|
||||
|
||||
std::vector<int> metadata_types = {IMAGE_METADATA_EXIF};
|
||||
std::vector<std::vector<uchar> > metadata(1);
|
||||
metadata[0].assign(exif_data, exif_data + sizeof(exif_data));
|
||||
|
||||
std::vector<int> write_params = {
|
||||
IMWRITE_PNG_COMPRESSION, png_compression
|
||||
};
|
||||
|
||||
imwriteWithMetadata(outputname, img, metadata_types, metadata, write_params);
|
||||
std::vector<uchar> compressed;
|
||||
imencodeWithMetadata(outputname, img, metadata_types, metadata, compressed, write_params);
|
||||
|
||||
std::vector<int> read_metadata_types, read_metadata_types2;
|
||||
std::vector<std::vector<uchar> > read_metadata, read_metadata2;
|
||||
Mat img2 = imreadWithMetadata(outputname, read_metadata_types, read_metadata, IMREAD_UNCHANGED);
|
||||
Mat img3 = imdecodeWithMetadata(compressed, read_metadata_types2, read_metadata2, IMREAD_UNCHANGED);
|
||||
EXPECT_EQ(img2.cols, img.cols);
|
||||
EXPECT_EQ(img2.rows, img.rows);
|
||||
EXPECT_EQ(img2.type(), imgtype);
|
||||
EXPECT_EQ(read_metadata_types, read_metadata_types2);
|
||||
EXPECT_GE(read_metadata_types.size(), 1u);
|
||||
EXPECT_EQ(read_metadata, read_metadata2);
|
||||
EXPECT_EQ(read_metadata_types[0], IMAGE_METADATA_EXIF);
|
||||
EXPECT_EQ(read_metadata_types.size(), read_metadata.size());
|
||||
EXPECT_EQ(read_metadata[0], metadata[0]);
|
||||
EXPECT_EQ(cv::norm(img2, img3, NORM_INF), 0.);
|
||||
double mse = cv::norm(img, img2, NORM_L2SQR)/(img.rows*img.cols);
|
||||
EXPECT_EQ(mse, 0); // png is lossless
|
||||
remove(outputname.c_str());
|
||||
}
|
||||
|
||||
static size_t locateString(const uchar* exif, size_t exif_size, const std::string& pattern)
|
||||
{
|
||||
size_t plen = pattern.size();
|
||||
for (size_t i = 0; i + plen <= exif_size; i++) {
|
||||
if (exif[i] == pattern[0] && memcmp(&exif[i], pattern.c_str(), plen) == 0)
|
||||
return i;
|
||||
}
|
||||
return 0xFFFFFFFFu;
|
||||
}
|
||||
|
||||
typedef std::tuple<std::string, size_t, std::string, size_t> ReadExif_Sanity_Params;
|
||||
typedef testing::TestWithParam<ReadExif_Sanity_Params> ReadExif_Sanity;
|
||||
|
||||
TEST_P(ReadExif_Sanity, Check)
|
||||
{
|
||||
std::string filename = get<0>(GetParam());
|
||||
size_t exif_size = get<1>(GetParam());
|
||||
std::string pattern = get<2>(GetParam());
|
||||
size_t ploc = get<3>(GetParam());
|
||||
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
filename = root + filename;
|
||||
|
||||
std::vector<int> metadata_types;
|
||||
std::vector<Mat> metadata;
|
||||
Mat img = imreadWithMetadata(filename, metadata_types, metadata, 1);
|
||||
|
||||
EXPECT_EQ(img.type(), CV_8UC3);
|
||||
ASSERT_GE(metadata_types.size(), 1u);
|
||||
EXPECT_EQ(metadata_types.size(), metadata.size());
|
||||
const Mat& exif = metadata[IMAGE_METADATA_EXIF];
|
||||
EXPECT_EQ(exif.type(), CV_8U);
|
||||
EXPECT_EQ(exif.total(), exif_size);
|
||||
ASSERT_GE(exif_size, 26u); // minimal exif should take at least 26 bytes
|
||||
// (the header + IDF0 with at least 1 entry).
|
||||
EXPECT_TRUE(exif.data[0] == 'I' || exif.data[0] == 'M');
|
||||
EXPECT_EQ(exif.data[0], exif.data[1]);
|
||||
EXPECT_EQ(locateString(exif.data, exif_size, pattern), ploc);
|
||||
}
|
||||
|
||||
static const std::vector<ReadExif_Sanity_Params> exif_sanity_params
|
||||
{
|
||||
#ifdef HAVE_JPEG
|
||||
{"readwrite/testExifOrientation_3.jpg", 916, "Photoshop", 120},
|
||||
#endif
|
||||
#ifdef OPENCV_IMGCODECS_PNG_WITH_EXIF
|
||||
{"readwrite/testExifOrientation_5.png", 112, "ExifTool", 102},
|
||||
#endif
|
||||
#ifdef HAVE_AVIF
|
||||
{"readwrite/testExifOrientation_7.avif", 913, "Photoshop", 120},
|
||||
#endif
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Imgcodecs, ReadExif_Sanity,
|
||||
testing::ValuesIn(exif_sanity_params));
|
||||
|
||||
}}
|
||||
|
||||
@@ -71,6 +71,36 @@ TEST(Imgcodecs_EXR, readWrite_32FC3)
|
||||
EXPECT_EQ(0, remove(filenameOutput.c_str()));
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_EXR, readWrite_32FC7)
|
||||
{ // 0-6 channels (multispectral)
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filenameInput = root + "readwrite/test32FC7.exr";
|
||||
const string filenameOutput = cv::tempfile(".exr");
|
||||
#ifndef GENERATE_DATA
|
||||
const Mat img = cv::imread(filenameInput, IMREAD_UNCHANGED);
|
||||
#else
|
||||
const Size sz(3, 5);
|
||||
Mat img(sz, CV_32FC7);
|
||||
img.at<cv::Vec<float, 7>>(0, 0)[0] = 101.125;
|
||||
img.at<cv::Vec<float, 7>>(2, 1)[3] = 203.500;
|
||||
img.at<cv::Vec<float, 7>>(4, 2)[6] = 305.875;
|
||||
ASSERT_TRUE(cv::imwrite(filenameInput, img));
|
||||
#endif
|
||||
ASSERT_FALSE(img.empty());
|
||||
ASSERT_EQ(CV_MAKETYPE(CV_32F, 7), img.type());
|
||||
|
||||
ASSERT_TRUE(cv::imwrite(filenameOutput, img));
|
||||
const Mat img2 = cv::imread(filenameOutput, IMREAD_UNCHANGED);
|
||||
EXPECT_EQ(img2.type(), img.type());
|
||||
EXPECT_EQ(img2.size(), img.size());
|
||||
EXPECT_LE(cvtest::norm(img, img2, NORM_INF | NORM_RELATIVE), 1e-3);
|
||||
EXPECT_EQ(0, remove(filenameOutput.c_str()));
|
||||
const Mat img3 = cv::imread(filenameInput, IMREAD_GRAYSCALE);
|
||||
ASSERT_TRUE(img3.empty());
|
||||
const Mat img4 = cv::imread(filenameInput, IMREAD_COLOR);
|
||||
ASSERT_TRUE(img4.empty());
|
||||
}
|
||||
|
||||
|
||||
TEST(Imgcodecs_EXR, readWrite_32FC1_half)
|
||||
{
|
||||
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
// 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 "test_precomp.hpp"
|
||||
#include "test_common.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
#ifdef HAVE_GDAL
|
||||
|
||||
static void test_gdal_read(const string filename, bool required = true) {
|
||||
const string path = cvtest::findDataFile(filename);
|
||||
Mat img;
|
||||
ASSERT_NO_THROW(img = imread(path, cv::IMREAD_LOAD_GDAL | cv::IMREAD_ANYDEPTH | cv::IMREAD_ANYCOLOR));
|
||||
if(!required && img.empty())
|
||||
{
|
||||
throw SkipTestException("GDAL is built wihout required back-end support");
|
||||
}
|
||||
ASSERT_FALSE(img.empty());
|
||||
EXPECT_EQ(3, img.cols);
|
||||
EXPECT_EQ(5, img.rows);
|
||||
EXPECT_EQ(CV_MAKETYPE(CV_32F, 7), img.type());
|
||||
EXPECT_EQ(101.125, (img.at<Vec<float, 7>>(0, 0)[0]));
|
||||
EXPECT_EQ(203.500, (img.at<Vec<float, 7>>(2, 1)[3]));
|
||||
EXPECT_EQ(305.875, (img.at<Vec<float, 7>>(4, 2)[6]));
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_gdal, read_envi)
|
||||
{
|
||||
test_gdal_read("../cv/gdal/envi_test.raw");
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_gdal, read_fits)
|
||||
{
|
||||
// .fit test is optional because GDAL may be built wihtout CFITSIO library support
|
||||
test_gdal_read("../cv/gdal/fits_test.fit", false);
|
||||
}
|
||||
|
||||
#endif // HAVE_GDAL
|
||||
|
||||
}} // namespace
|
||||
@@ -150,19 +150,107 @@ TEST(Imgcodecs_Png, decode_regression27295)
|
||||
|
||||
typedef testing::TestWithParam<string> Imgcodecs_Png_PngSuite;
|
||||
|
||||
// Parameterized test for decoding PNG files from the PNGSuite test set
|
||||
TEST_P(Imgcodecs_Png_PngSuite, decode)
|
||||
{
|
||||
// Construct full paths for the PNG image and corresponding ground truth XML file
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filename = root + "pngsuite/" + GetParam() + ".png";
|
||||
const string xml_filename = root + "pngsuite/" + GetParam() + ".xml";
|
||||
FileStorage fs(xml_filename, FileStorage::READ);
|
||||
EXPECT_TRUE(fs.isOpened());
|
||||
|
||||
// Load the XML file containing the ground truth data
|
||||
FileStorage fs(xml_filename, FileStorage::READ);
|
||||
ASSERT_TRUE(fs.isOpened()); // Ensure the file was opened successfully
|
||||
|
||||
// Load the image using IMREAD_UNCHANGED to preserve original format
|
||||
Mat src = imread(filename, IMREAD_UNCHANGED);
|
||||
ASSERT_FALSE(src.empty()); // Ensure the image was loaded successfully
|
||||
|
||||
// Load the ground truth matrix from XML
|
||||
Mat gt;
|
||||
fs.getFirstTopLevelNode() >> gt;
|
||||
|
||||
// Compare the image loaded with IMREAD_UNCHANGED to the ground truth
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), src, gt);
|
||||
|
||||
// Declare matrices for ground truth in different imread flag combinations
|
||||
Mat gt_0, gt_1, gt_2, gt_3, gt_256, gt_258;
|
||||
|
||||
// Handle grayscale 8-bit and 16-bit images
|
||||
if (gt.channels() == 1)
|
||||
{
|
||||
gt.copyTo(gt_2); // For IMREAD_ANYDEPTH
|
||||
if (gt.depth() == CV_16U)
|
||||
gt_2.convertTo(gt_0, CV_8U, 1. / 256);
|
||||
else
|
||||
gt_0 = gt_2; // For IMREAD_GRAYSCALE
|
||||
|
||||
cvtColor(gt_2, gt_3, COLOR_GRAY2BGR); // For IMREAD_COLOR | IMREAD_ANYDEPTH
|
||||
|
||||
if (gt.depth() == CV_16U)
|
||||
gt_3.convertTo(gt_1, CV_8U, 1. / 256);
|
||||
else
|
||||
gt_1 = gt_3; // For IMREAD_COLOR
|
||||
|
||||
gt_256 = gt_1; // For IMREAD_COLOR_RGB
|
||||
gt_258 = gt_3; // For IMREAD_COLOR_RGB | IMREAD_ANYDEPTH
|
||||
}
|
||||
|
||||
// Handle color images (3 or 4 channels) with 8-bit and 16-bit depth
|
||||
if (gt.channels() > 1)
|
||||
{
|
||||
// Convert to grayscale
|
||||
cvtColor(gt, gt_2, COLOR_BGRA2GRAY);
|
||||
if (gt.depth() == CV_16U)
|
||||
gt_2.convertTo(gt_0, CV_8U, 1. / 256);
|
||||
else
|
||||
gt_0 = gt_2;
|
||||
|
||||
// Convert to 3-channel BGR
|
||||
if (gt.channels() == 3)
|
||||
gt.copyTo(gt_3);
|
||||
else
|
||||
cvtColor(gt, gt_3, COLOR_BGRA2BGR);
|
||||
|
||||
if (gt.depth() == CV_16U)
|
||||
gt_3.convertTo(gt_1, CV_8U, 1. / 256);
|
||||
else
|
||||
gt_1 = gt_3;
|
||||
|
||||
// Convert to RGB for IMREAD_COLOR_RGB variants
|
||||
cvtColor(gt_1, gt_256, COLOR_BGR2RGB);
|
||||
cvtColor(gt_3, gt_258, COLOR_BGR2RGB);
|
||||
}
|
||||
|
||||
// Perform comparisons with different imread flags
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(1, 0), imread(filename, IMREAD_GRAYSCALE), gt_0);
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(1, 0), imread(filename, IMREAD_COLOR), gt_1);
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(4, 0), imread(filename, IMREAD_ANYDEPTH), gt_2);
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), imread(filename, IMREAD_COLOR | IMREAD_ANYDEPTH), gt_3);
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(1, 0), imread(filename, IMREAD_COLOR_RGB), gt_256);
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), imread(filename, IMREAD_COLOR_RGB | IMREAD_ANYDEPTH), gt_258);
|
||||
|
||||
// Uncomment this block to write out the decoded images for visual/manual inspection
|
||||
// or for regenerating expected ground truth PNGs (for example, after changing decoder logic).
|
||||
#if 0
|
||||
imwrite(filename + "_0.png", imread(filename, IMREAD_GRAYSCALE));
|
||||
imwrite(filename + "_1.png", imread(filename, IMREAD_COLOR));
|
||||
imwrite(filename + "_2.png", imread(filename, IMREAD_ANYDEPTH));
|
||||
imwrite(filename + "_3.png", imread(filename, IMREAD_COLOR | IMREAD_ANYDEPTH));
|
||||
imwrite(filename + "_256.png", imread(filename, IMREAD_COLOR_RGB));
|
||||
imwrite(filename + "_258.png", imread(filename, IMREAD_COLOR_RGB | IMREAD_ANYDEPTH));
|
||||
#endif
|
||||
|
||||
// Uncomment this block to verify that saved images (from above) load identically
|
||||
// when read back with IMREAD_UNCHANGED. Helps ensure write-read symmetry.
|
||||
#if 0
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), imread(filename, IMREAD_GRAYSCALE), imread(filename + "_0.png", IMREAD_UNCHANGED));
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), imread(filename, IMREAD_COLOR), imread(filename + "_1.png", IMREAD_UNCHANGED));
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), imread(filename, IMREAD_ANYDEPTH), imread(filename + "_2.png", IMREAD_UNCHANGED));
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), imread(filename, IMREAD_COLOR | IMREAD_ANYDEPTH), imread(filename + "_3.png", IMREAD_UNCHANGED));
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), imread(filename, IMREAD_COLOR_RGB), imread(filename + "_256.png", IMREAD_UNCHANGED));
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), imread(filename, IMREAD_COLOR_RGB | IMREAD_ANYDEPTH), imread(filename + "_258.png", IMREAD_UNCHANGED));
|
||||
#endif
|
||||
}
|
||||
|
||||
const string pngsuite_files[] =
|
||||
@@ -243,23 +331,13 @@ const string pngsuite_files[] =
|
||||
"f04n2c08",
|
||||
"f99n0g04",
|
||||
"g03n0g16",
|
||||
"g03n2c08",
|
||||
"g03n3p04",
|
||||
"g04n0g16",
|
||||
"g04n2c08",
|
||||
"g04n3p04",
|
||||
"g05n0g16",
|
||||
"g05n2c08",
|
||||
"g05n3p04",
|
||||
"g07n0g16",
|
||||
"g07n2c08",
|
||||
"g07n3p04",
|
||||
"g10n0g16",
|
||||
"g10n2c08",
|
||||
"g10n3p04",
|
||||
"g25n0g16",
|
||||
"g25n2c08",
|
||||
"g25n3p04",
|
||||
"oi1n0g16",
|
||||
"oi1n2c16",
|
||||
"oi2n0g16",
|
||||
@@ -333,6 +411,49 @@ const string pngsuite_files[] =
|
||||
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_PngSuite,
|
||||
testing::ValuesIn(pngsuite_files));
|
||||
|
||||
typedef testing::TestWithParam<string> Imgcodecs_Png_PngSuite_Gamma;
|
||||
|
||||
// Parameterized test for decoding PNG files from the PNGSuite test set
|
||||
TEST_P(Imgcodecs_Png_PngSuite_Gamma, decode)
|
||||
{
|
||||
// Construct full paths for the PNG image and corresponding ground truth XML file
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filename = root + "pngsuite/" + GetParam() + ".png";
|
||||
const string xml_filename = root + "pngsuite/" + GetParam() + ".xml";
|
||||
|
||||
// Load the XML file containing the ground truth data
|
||||
FileStorage fs(xml_filename, FileStorage::READ);
|
||||
ASSERT_TRUE(fs.isOpened()); // Ensure the file was opened successfully
|
||||
|
||||
// Load the image using IMREAD_UNCHANGED to preserve original format
|
||||
Mat src = imread(filename, IMREAD_UNCHANGED);
|
||||
ASSERT_FALSE(src.empty()); // Ensure the image was loaded successfully
|
||||
|
||||
// Load the ground truth matrix from XML
|
||||
Mat gt;
|
||||
fs.getFirstTopLevelNode() >> gt;
|
||||
|
||||
// Compare the image loaded with IMREAD_UNCHANGED to the ground truth
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), src, gt);
|
||||
}
|
||||
|
||||
const string pngsuite_files_gamma[] =
|
||||
{
|
||||
"g03n2c08",
|
||||
"g03n3p04",
|
||||
"g04n2c08",
|
||||
"g04n3p04",
|
||||
"g05n2c08",
|
||||
"g05n3p04",
|
||||
"g07n2c08",
|
||||
"g07n3p04",
|
||||
"g25n2c08",
|
||||
"g25n3p04"
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_PngSuite_Gamma,
|
||||
testing::ValuesIn(pngsuite_files_gamma));
|
||||
|
||||
typedef testing::TestWithParam<string> Imgcodecs_Png_PngSuite_Corrupted;
|
||||
|
||||
TEST_P(Imgcodecs_Png_PngSuite_Corrupted, decode)
|
||||
|
||||
@@ -235,8 +235,9 @@ enum MorphShapes {
|
||||
MORPH_RECT = 0, //!< a rectangular structuring element: \f[E_{ij}=1\f]
|
||||
MORPH_CROSS = 1, //!< a cross-shaped structuring element:
|
||||
//!< \f[E_{ij} = \begin{cases} 1 & \texttt{if } {i=\texttt{anchor.y } {or } {j=\texttt{anchor.x}}} \\0 & \texttt{otherwise} \end{cases}\f]
|
||||
MORPH_ELLIPSE = 2 //!< an elliptic structuring element, that is, a filled ellipse inscribed
|
||||
MORPH_ELLIPSE = 2, //!< an elliptic structuring element, that is, a filled ellipse inscribed
|
||||
//!< into the rectangle Rect(0, 0, esize.width, esize.height)
|
||||
MORPH_DIAMOND = 3 //!< a diamond structuring element defined by Manhattan distance
|
||||
};
|
||||
|
||||
//! @} imgproc_filter
|
||||
@@ -2944,9 +2945,9 @@ Calculates the cross-power spectrum of two supplied source arrays. The arrays ar
|
||||
with getOptimalDFTSize.
|
||||
|
||||
The function performs the following equations:
|
||||
- First it applies a Hanning window (see <https://en.wikipedia.org/wiki/Hann_function>) to each
|
||||
image to remove possible edge effects. This window is cached until the array size changes to speed
|
||||
up processing time.
|
||||
- First it applies a Hanning window to each image to remove possible edge effects, if it's provided
|
||||
by user. See @ref createHanningWindow and <https://en.wikipedia.org/wiki/Hann_function>. This window may
|
||||
be cached until the array size changes to speed up processing time.
|
||||
- Next it computes the forward DFTs of each source array:
|
||||
\f[\mathbf{G}_a = \mathcal{F}\{src_1\}, \; \mathbf{G}_b = \mathcal{F}\{src_2\}\f]
|
||||
where \f$\mathcal{F}\f$ is the forward DFT.
|
||||
|
||||
@@ -796,6 +796,22 @@ void cv::distanceTransform( InputArray _src, OutputArray _dst, OutputArray _labe
|
||||
ippFree( pBuffer );
|
||||
if (status>=0)
|
||||
{
|
||||
// https://github.com/opencv/opencv/issues/24082
|
||||
// There is probably a rounding issue that leads to non-deterministic behavior
|
||||
// between runs on positions closer to zeros by x-axis in straight direction.
|
||||
// As a workaround, we detect the distances that expected to be exact
|
||||
// number of pixels and round manually.
|
||||
static const float correctionDiff = 1.0f / (1 << 11);
|
||||
for (int i = 0; i < dst.rows; ++i)
|
||||
{
|
||||
float* row = dst.ptr<float>(i);
|
||||
for (int j = 0; j < dst.cols; ++j)
|
||||
{
|
||||
float rounded = static_cast<float>(cvRound(row[j]));
|
||||
if (fabs(row[j] - rounded) <= correctionDiff)
|
||||
row[j] = rounded;
|
||||
}
|
||||
}
|
||||
CV_IMPL_ADD(CV_IMPL_IPP);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -271,20 +271,20 @@ static LineSegmentIntersection parallelInt( Point2f a, Point2f b, Point2f c, Poi
|
||||
static LineSegmentIntersection intersectLineSegments( Point2f a, Point2f b, Point2f c,
|
||||
Point2f d, Point2f& p, Point2f& q )
|
||||
{
|
||||
double denom = (a.x - b.x) * (double)(d.y - c.y) - (a.y - b.y) * (double)(d.x - c.x);
|
||||
double denom = ((double)a.x - b.x) * ((double)d.y - c.y) - ((double)a.y - b.y) * ((double)d.x - c.x);
|
||||
|
||||
// If denom is zero, then segments are parallel: handle separately.
|
||||
if( denom == 0. )
|
||||
return parallelInt(a, b, c, d, p, q);
|
||||
|
||||
double num = (d.y - a.y) * (double)(a.x - c.x) + (a.x - d.x) * (double)(a.y - c.y);
|
||||
double num = ((double)d.y - a.y) * ((double)a.x - c.x) + ((double)a.x - d.x) * ((double)a.y - c.y);
|
||||
double s = num / denom;
|
||||
|
||||
num = (b.y - a.y) * (double)(a.x - c.x) + (c.y - a.y) * (double)(b.x - a.x);
|
||||
num = ((double)b.y - a.y) * ((double)a.x - c.x) + ((double)c.y - a.y) * ((double)b.x - a.x);
|
||||
double t = num / denom;
|
||||
|
||||
p.x = (float)(a.x + s*(b.x - a.x));
|
||||
p.y = (float)(a.y + s*(b.y - a.y));
|
||||
p.x = (float)(a.x + s*((double)b.x - a.x));
|
||||
p.y = (float)(a.y + s*((double)b.y - a.y));
|
||||
q = p;
|
||||
|
||||
return s < 0. || s > 1. || t < 0. || t > 1. ? LS_NO_INTERSECTION :
|
||||
|
||||
@@ -63,67 +63,6 @@ using namespace cv;
|
||||
namespace cv
|
||||
{
|
||||
|
||||
#if defined (HAVE_IPP) && (!IPP_DISABLE_WARPAFFINE || !IPP_DISABLE_WARPPERSPECTIVE || !IPP_DISABLE_REMAP)
|
||||
typedef IppStatus (CV_STDCALL* ippiSetFunc)(const void*, void *, int, IppiSize);
|
||||
|
||||
template <int channels, typename Type>
|
||||
bool IPPSetSimple(cv::Scalar value, void *dataPointer, int step, IppiSize &size, ippiSetFunc func)
|
||||
{
|
||||
CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
Type values[channels];
|
||||
for( int i = 0; i < channels; i++ )
|
||||
values[i] = saturate_cast<Type>(value[i]);
|
||||
return func(values, dataPointer, step, size) >= 0;
|
||||
}
|
||||
|
||||
static bool IPPSet(const cv::Scalar &value, void *dataPointer, int step, IppiSize &size, int channels, int depth)
|
||||
{
|
||||
CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
if( channels == 1 )
|
||||
{
|
||||
switch( depth )
|
||||
{
|
||||
case CV_8U:
|
||||
return CV_INSTRUMENT_FUN_IPP(ippiSet_8u_C1R, saturate_cast<Ipp8u>(value[0]), (Ipp8u *)dataPointer, step, size) >= 0;
|
||||
case CV_16U:
|
||||
return CV_INSTRUMENT_FUN_IPP(ippiSet_16u_C1R, saturate_cast<Ipp16u>(value[0]), (Ipp16u *)dataPointer, step, size) >= 0;
|
||||
case CV_32F:
|
||||
return CV_INSTRUMENT_FUN_IPP(ippiSet_32f_C1R, saturate_cast<Ipp32f>(value[0]), (Ipp32f *)dataPointer, step, size) >= 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( channels == 3 )
|
||||
{
|
||||
switch( depth )
|
||||
{
|
||||
case CV_8U:
|
||||
return IPPSetSimple<3, Ipp8u>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_8u_C3R);
|
||||
case CV_16U:
|
||||
return IPPSetSimple<3, Ipp16u>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_16u_C3R);
|
||||
case CV_32F:
|
||||
return IPPSetSimple<3, Ipp32f>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_32f_C3R);
|
||||
}
|
||||
}
|
||||
else if( channels == 4 )
|
||||
{
|
||||
switch( depth )
|
||||
{
|
||||
case CV_8U:
|
||||
return IPPSetSimple<4, Ipp8u>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_8u_C4R);
|
||||
case CV_16U:
|
||||
return IPPSetSimple<4, Ipp16u>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_16u_C4R);
|
||||
case CV_32F:
|
||||
return IPPSetSimple<4, Ipp32f>(value, dataPointer, step, size, (ippiSetFunc)ippiSet_32f_C4R);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/************** interpolation formulas and tables ***************/
|
||||
|
||||
const int INTER_REMAP_COEF_BITS=15;
|
||||
@@ -1434,58 +1373,6 @@ static bool ocl_remap(InputArray _src, OutputArray _dst, InputArray _map1, Input
|
||||
|
||||
#endif
|
||||
|
||||
#if defined HAVE_IPP && !IPP_DISABLE_REMAP
|
||||
|
||||
typedef IppStatus (CV_STDCALL * ippiRemap)(const void * pSrc, IppiSize srcSize, int srcStep, IppiRect srcRoi,
|
||||
const Ipp32f* pxMap, int xMapStep, const Ipp32f* pyMap, int yMapStep,
|
||||
void * pDst, int dstStep, IppiSize dstRoiSize, int interpolation);
|
||||
|
||||
class IPPRemapInvoker :
|
||||
public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
IPPRemapInvoker(Mat & _src, Mat & _dst, Mat & _xmap, Mat & _ymap, ippiRemap _ippFunc,
|
||||
int _ippInterpolation, int _borderType, const Scalar & _borderValue, bool * _ok) :
|
||||
ParallelLoopBody(), src(_src), dst(_dst), map1(_xmap), map2(_ymap), ippFunc(_ippFunc),
|
||||
ippInterpolation(_ippInterpolation), borderType(_borderType), borderValue(_borderValue), ok(_ok)
|
||||
{
|
||||
*ok = true;
|
||||
}
|
||||
|
||||
virtual void operator() (const Range & range) const
|
||||
{
|
||||
IppiRect srcRoiRect = { 0, 0, src.cols, src.rows };
|
||||
Mat dstRoi = dst.rowRange(range);
|
||||
IppiSize dstRoiSize = ippiSize(dstRoi.size());
|
||||
int type = dst.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
|
||||
if (borderType == BORDER_CONSTANT &&
|
||||
!IPPSet(borderValue, dstRoi.ptr(), (int)dstRoi.step, dstRoiSize, cn, depth))
|
||||
{
|
||||
*ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (CV_INSTRUMENT_FUN_IPP(ippFunc, src.ptr(), ippiSize(src.size()), (int)src.step, srcRoiRect,
|
||||
map1.ptr<Ipp32f>(), (int)map1.step, map2.ptr<Ipp32f>(), (int)map2.step,
|
||||
dstRoi.ptr(), (int)dstRoi.step, dstRoiSize, ippInterpolation) < 0)
|
||||
*ok = false;
|
||||
else
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Mat & src, & dst, & map1, & map2;
|
||||
ippiRemap ippFunc;
|
||||
int ippInterpolation, borderType;
|
||||
Scalar borderValue;
|
||||
bool * ok;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void cv::remap( InputArray _src, OutputArray _dst,
|
||||
@@ -1652,47 +1539,6 @@ void cv::remap( InputArray _src, OutputArray _dst,
|
||||
}
|
||||
}
|
||||
|
||||
#if defined HAVE_IPP && !IPP_DISABLE_REMAP
|
||||
CV_IPP_CHECK()
|
||||
{
|
||||
if ((interpolation == INTER_LINEAR || interpolation == INTER_CUBIC || interpolation == INTER_NEAREST) &&
|
||||
map1.type() == CV_32FC1 && map2.type() == CV_32FC1 &&
|
||||
(borderType == BORDER_CONSTANT || borderType == BORDER_TRANSPARENT))
|
||||
{
|
||||
int ippInterpolation =
|
||||
interpolation == INTER_NEAREST ? IPPI_INTER_NN :
|
||||
interpolation == INTER_LINEAR ? IPPI_INTER_LINEAR : IPPI_INTER_CUBIC;
|
||||
|
||||
ippiRemap ippFunc =
|
||||
type == CV_8UC1 ? (ippiRemap)ippiRemap_8u_C1R :
|
||||
type == CV_8UC3 ? (ippiRemap)ippiRemap_8u_C3R :
|
||||
type == CV_8UC4 ? (ippiRemap)ippiRemap_8u_C4R :
|
||||
type == CV_16UC1 ? (ippiRemap)ippiRemap_16u_C1R :
|
||||
type == CV_16UC3 ? (ippiRemap)ippiRemap_16u_C3R :
|
||||
type == CV_16UC4 ? (ippiRemap)ippiRemap_16u_C4R :
|
||||
type == CV_32FC1 ? (ippiRemap)ippiRemap_32f_C1R :
|
||||
type == CV_32FC3 ? (ippiRemap)ippiRemap_32f_C3R :
|
||||
type == CV_32FC4 ? (ippiRemap)ippiRemap_32f_C4R : 0;
|
||||
|
||||
if (ippFunc)
|
||||
{
|
||||
bool ok;
|
||||
IPPRemapInvoker invoker(src, dst, map1, map2, ippFunc, ippInterpolation,
|
||||
borderType, borderValue, &ok);
|
||||
Range range(0, dst.rows);
|
||||
parallel_for_(range, invoker, dst.total() / (double)(1 << 16));
|
||||
|
||||
if (ok)
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
return;
|
||||
}
|
||||
setIppErrorStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RemapNNFunc nnfunc = 0;
|
||||
RemapFunc ifunc = 0;
|
||||
const void* ctab = 0;
|
||||
@@ -2188,62 +2034,6 @@ private:
|
||||
const double *M;
|
||||
};
|
||||
|
||||
|
||||
#if defined (HAVE_IPP) && IPP_VERSION_X100 >= 810 && !IPP_DISABLE_WARPAFFINE
|
||||
typedef IppStatus (CV_STDCALL* ippiWarpAffineBackFunc)(const void*, IppiSize, int, IppiRect, void *, int, IppiRect, double [2][3], int);
|
||||
|
||||
class IPPWarpAffineInvoker :
|
||||
public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
IPPWarpAffineInvoker(Mat &_src, Mat &_dst, double (&_coeffs)[2][3], int &_interpolation, int _borderType,
|
||||
const Scalar &_borderValue, ippiWarpAffineBackFunc _func, bool *_ok) :
|
||||
ParallelLoopBody(), src(_src), dst(_dst), mode(_interpolation), coeffs(_coeffs),
|
||||
borderType(_borderType), borderValue(_borderValue), func(_func), ok(_ok)
|
||||
{
|
||||
*ok = true;
|
||||
}
|
||||
|
||||
virtual void operator() (const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
IppiSize srcsize = { src.cols, src.rows };
|
||||
IppiRect srcroi = { 0, 0, src.cols, src.rows };
|
||||
IppiRect dstroi = { 0, range.start, dst.cols, range.end - range.start };
|
||||
int cnn = src.channels();
|
||||
if( borderType == BORDER_CONSTANT )
|
||||
{
|
||||
IppiSize setSize = { dst.cols, range.end - range.start };
|
||||
void *dataPointer = dst.ptr(range.start);
|
||||
if( !IPPSet( borderValue, dataPointer, (int)dst.step[0], setSize, cnn, src.depth() ) )
|
||||
{
|
||||
*ok = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Aug 2013: problem in IPP 7.1, 8.0 : sometimes function return ippStsCoeffErr
|
||||
IppStatus status = CV_INSTRUMENT_FUN_IPP(func,( src.ptr(), srcsize, (int)src.step[0], srcroi, dst.ptr(),
|
||||
(int)dst.step[0], dstroi, coeffs, mode ));
|
||||
if( status < 0)
|
||||
*ok = false;
|
||||
else
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
}
|
||||
}
|
||||
private:
|
||||
Mat &src;
|
||||
Mat &dst;
|
||||
int mode;
|
||||
double (&coeffs)[2][3];
|
||||
int borderType;
|
||||
Scalar borderValue;
|
||||
ippiWarpAffineBackFunc func;
|
||||
bool *ok;
|
||||
const IPPWarpAffineInvoker& operator= (const IPPWarpAffineInvoker&);
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENCL
|
||||
|
||||
enum { OCL_OP_PERSPECTIVE = 1, OCL_OP_AFFINE = 0 };
|
||||
@@ -2435,132 +2225,6 @@ static bool ocl_warpTransform(InputArray _src, OutputArray _dst, InputArray _M0,
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IPP
|
||||
#define IPP_WARPAFFINE_PARALLEL 1
|
||||
|
||||
#ifdef HAVE_IPP_IW
|
||||
|
||||
class ipp_warpAffineParallel: public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
ipp_warpAffineParallel(::ipp::IwiImage &src, ::ipp::IwiImage &dst, IppiInterpolationType _inter, double (&_coeffs)[2][3], ::ipp::IwiBorderType _borderType, IwTransDirection _iwTransDirection, bool *_ok):m_src(src), m_dst(dst)
|
||||
{
|
||||
pOk = _ok;
|
||||
|
||||
inter = _inter;
|
||||
borderType = _borderType;
|
||||
iwTransDirection = _iwTransDirection;
|
||||
|
||||
for( int i = 0; i < 2; i++ )
|
||||
for( int j = 0; j < 3; j++ )
|
||||
coeffs[i][j] = _coeffs[i][j];
|
||||
|
||||
*pOk = true;
|
||||
}
|
||||
~ipp_warpAffineParallel() {}
|
||||
|
||||
virtual void operator() (const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
if(*pOk == false)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
::ipp::IwiTile tile = ::ipp::IwiRoi(0, range.start, m_dst.m_size.width, range.end - range.start);
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiWarpAffine, m_src, m_dst, coeffs, iwTransDirection, inter, ::ipp::IwiWarpAffineParams(), borderType, tile);
|
||||
}
|
||||
catch(const ::ipp::IwException &)
|
||||
{
|
||||
*pOk = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
private:
|
||||
::ipp::IwiImage &m_src;
|
||||
::ipp::IwiImage &m_dst;
|
||||
|
||||
IppiInterpolationType inter;
|
||||
double coeffs[2][3];
|
||||
::ipp::IwiBorderType borderType;
|
||||
IwTransDirection iwTransDirection;
|
||||
|
||||
bool *pOk;
|
||||
const ipp_warpAffineParallel& operator= (const ipp_warpAffineParallel&);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static bool ipp_warpAffine( InputArray _src, OutputArray _dst, int interpolation, int borderType, const Scalar & borderValue, InputArray _M, int flags )
|
||||
{
|
||||
#ifdef HAVE_IPP_IW
|
||||
CV_INSTRUMENT_REGION_IPP();
|
||||
|
||||
if (!cv::ipp::useIPP_NotExact())
|
||||
return false;
|
||||
|
||||
IppiInterpolationType ippInter = ippiGetInterpolation(interpolation);
|
||||
if((int)ippInter < 0)
|
||||
return false;
|
||||
|
||||
// Acquire data and begin processing
|
||||
try
|
||||
{
|
||||
Mat src = _src.getMat();
|
||||
Mat dst = _dst.getMat();
|
||||
::ipp::IwiImage iwSrc = ippiGetImage(src);
|
||||
::ipp::IwiImage iwDst = ippiGetImage(dst);
|
||||
::ipp::IwiBorderType ippBorder(ippiGetBorderType(borderType), ippiGetValue(borderValue));
|
||||
IwTransDirection iwTransDirection;
|
||||
if(!ippBorder)
|
||||
return false;
|
||||
|
||||
if( !(flags & WARP_INVERSE_MAP) )
|
||||
iwTransDirection = iwTransForward;
|
||||
else
|
||||
iwTransDirection = iwTransInverse;
|
||||
|
||||
Mat M = _M.getMat();
|
||||
double coeffs[2][3];
|
||||
for( int i = 0; i < 2; i++ )
|
||||
for( int j = 0; j < 3; j++ )
|
||||
coeffs[i][j] = M.at<double>(i, j);
|
||||
|
||||
const int threads = ippiSuggestThreadsNum(iwDst, 2);
|
||||
|
||||
if(IPP_WARPAFFINE_PARALLEL && threads > 1)
|
||||
{
|
||||
bool ok = true;
|
||||
Range range(0, (int)iwDst.m_size.height);
|
||||
ipp_warpAffineParallel invoker(iwSrc, iwDst, ippInter, coeffs, ippBorder, iwTransDirection, &ok);
|
||||
if(!ok)
|
||||
return false;
|
||||
|
||||
parallel_for_(range, invoker, threads*4);
|
||||
|
||||
if(!ok)
|
||||
return false;
|
||||
} else {
|
||||
CV_INSTRUMENT_FUN_IPP(::ipp::iwiWarpAffine, iwSrc, iwDst, coeffs, iwTransDirection, ippInter, ::ipp::IwiWarpAffineParams(), ippBorder);
|
||||
}
|
||||
|
||||
}
|
||||
catch (const ::ipp::IwException &)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
CV_UNUSED(_src); CV_UNUSED(_dst); CV_UNUSED(interpolation);
|
||||
CV_UNUSED(borderType); CV_UNUSED(borderValue); CV_UNUSED(_M); CV_UNUSED(flags);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace hal {
|
||||
|
||||
static void warpAffine(int src_type,
|
||||
@@ -2812,8 +2476,6 @@ void cv::warpAffine( InputArray _src, OutputArray _dst,
|
||||
CV_Assert( (M0.type() == CV_32F || M0.type() == CV_64F) && M0.rows == 2 && M0.cols == 3 );
|
||||
M0.convertTo(matM, matM.type());
|
||||
|
||||
CV_IPP_RUN_FAST(ipp_warpAffine(src, dst, interpolation, borderType, borderValue, matM, flags));
|
||||
|
||||
if( !(flags & WARP_INVERSE_MAP) )
|
||||
{
|
||||
double D = M[0]*M[4] - M[1]*M[3];
|
||||
@@ -2826,70 +2488,6 @@ void cv::warpAffine( InputArray _src, OutputArray _dst,
|
||||
M[2] = b1; M[5] = b2;
|
||||
}
|
||||
|
||||
#if defined (HAVE_IPP) && IPP_VERSION_X100 >= 810 && !IPP_DISABLE_WARPAFFINE
|
||||
CV_IPP_CHECK()
|
||||
{
|
||||
int type = src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
if( ( depth == CV_8U || depth == CV_16U || depth == CV_32F ) &&
|
||||
( cn == 1 || cn == 3 || cn == 4 ) &&
|
||||
( interpolation == INTER_NEAREST || interpolation == INTER_LINEAR || interpolation == INTER_CUBIC) &&
|
||||
( borderType == cv::BORDER_TRANSPARENT || borderType == cv::BORDER_CONSTANT) )
|
||||
{
|
||||
ippiWarpAffineBackFunc ippFunc = 0;
|
||||
if ((flags & WARP_INVERSE_MAP) != 0)
|
||||
{
|
||||
ippFunc =
|
||||
type == CV_8UC1 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_8u_C1R :
|
||||
type == CV_8UC3 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_8u_C3R :
|
||||
type == CV_8UC4 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_8u_C4R :
|
||||
type == CV_16UC1 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_16u_C1R :
|
||||
type == CV_16UC3 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_16u_C3R :
|
||||
type == CV_16UC4 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_16u_C4R :
|
||||
type == CV_32FC1 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_32f_C1R :
|
||||
type == CV_32FC3 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_32f_C3R :
|
||||
type == CV_32FC4 ? (ippiWarpAffineBackFunc)ippiWarpAffineBack_32f_C4R :
|
||||
0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ippFunc =
|
||||
type == CV_8UC1 ? (ippiWarpAffineBackFunc)ippiWarpAffine_8u_C1R :
|
||||
type == CV_8UC3 ? (ippiWarpAffineBackFunc)ippiWarpAffine_8u_C3R :
|
||||
type == CV_8UC4 ? (ippiWarpAffineBackFunc)ippiWarpAffine_8u_C4R :
|
||||
type == CV_16UC1 ? (ippiWarpAffineBackFunc)ippiWarpAffine_16u_C1R :
|
||||
type == CV_16UC3 ? (ippiWarpAffineBackFunc)ippiWarpAffine_16u_C3R :
|
||||
type == CV_16UC4 ? (ippiWarpAffineBackFunc)ippiWarpAffine_16u_C4R :
|
||||
type == CV_32FC1 ? (ippiWarpAffineBackFunc)ippiWarpAffine_32f_C1R :
|
||||
type == CV_32FC3 ? (ippiWarpAffineBackFunc)ippiWarpAffine_32f_C3R :
|
||||
type == CV_32FC4 ? (ippiWarpAffineBackFunc)ippiWarpAffine_32f_C4R :
|
||||
0;
|
||||
}
|
||||
int mode =
|
||||
interpolation == INTER_LINEAR ? IPPI_INTER_LINEAR :
|
||||
interpolation == INTER_NEAREST ? IPPI_INTER_NN :
|
||||
interpolation == INTER_CUBIC ? IPPI_INTER_CUBIC :
|
||||
0;
|
||||
CV_Assert(mode && ippFunc);
|
||||
|
||||
double coeffs[2][3];
|
||||
for( int i = 0; i < 2; i++ )
|
||||
for( int j = 0; j < 3; j++ )
|
||||
coeffs[i][j] = matM.at<double>(i, j);
|
||||
|
||||
bool ok;
|
||||
Range range(0, dst.rows);
|
||||
IPPWarpAffineInvoker invoker(src, dst, coeffs, mode, borderType, borderValue, ippFunc, &ok);
|
||||
parallel_for_(range, invoker, dst.total()/(double)(1<<16));
|
||||
if( ok )
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
return;
|
||||
}
|
||||
setIppErrorStatus();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
hal::warpAffine(src.type(), src.data, src.step, src.cols, src.rows, dst.data, dst.step, dst.cols, dst.rows,
|
||||
M, interpolation, borderType, borderValue.val, hint);
|
||||
}
|
||||
@@ -3212,61 +2810,6 @@ private:
|
||||
Scalar borderValue;
|
||||
};
|
||||
|
||||
#if defined (HAVE_IPP) && IPP_VERSION_X100 >= 810 && !IPP_DISABLE_WARPPERSPECTIVE
|
||||
typedef IppStatus (CV_STDCALL* ippiWarpPerspectiveFunc)(const void*, IppiSize, int, IppiRect, void *, int, IppiRect, double [3][3], int);
|
||||
|
||||
class IPPWarpPerspectiveInvoker :
|
||||
public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
IPPWarpPerspectiveInvoker(Mat &_src, Mat &_dst, double (&_coeffs)[3][3], int &_interpolation,
|
||||
int &_borderType, const Scalar &_borderValue, ippiWarpPerspectiveFunc _func, bool *_ok) :
|
||||
ParallelLoopBody(), src(_src), dst(_dst), mode(_interpolation), coeffs(_coeffs),
|
||||
borderType(_borderType), borderValue(_borderValue), func(_func), ok(_ok)
|
||||
{
|
||||
*ok = true;
|
||||
}
|
||||
|
||||
virtual void operator() (const Range& range) const CV_OVERRIDE
|
||||
{
|
||||
IppiSize srcsize = {src.cols, src.rows};
|
||||
IppiRect srcroi = {0, 0, src.cols, src.rows};
|
||||
IppiRect dstroi = {0, range.start, dst.cols, range.end - range.start};
|
||||
int cnn = src.channels();
|
||||
|
||||
if( borderType == BORDER_CONSTANT )
|
||||
{
|
||||
IppiSize setSize = {dst.cols, range.end - range.start};
|
||||
void *dataPointer = dst.ptr(range.start);
|
||||
if( !IPPSet( borderValue, dataPointer, (int)dst.step[0], setSize, cnn, src.depth() ) )
|
||||
{
|
||||
*ok = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
IppStatus status = CV_INSTRUMENT_FUN_IPP(func,(src.ptr();, srcsize, (int)src.step[0], srcroi, dst.ptr(), (int)dst.step[0], dstroi, coeffs, mode));
|
||||
if (status != ippStsNoErr)
|
||||
*ok = false;
|
||||
else
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
}
|
||||
}
|
||||
private:
|
||||
Mat &src;
|
||||
Mat &dst;
|
||||
int mode;
|
||||
double (&coeffs)[3][3];
|
||||
int borderType;
|
||||
const Scalar borderValue;
|
||||
ippiWarpPerspectiveFunc func;
|
||||
bool *ok;
|
||||
|
||||
const IPPWarpPerspectiveInvoker& operator= (const IPPWarpPerspectiveInvoker&);
|
||||
};
|
||||
#endif
|
||||
|
||||
namespace hal {
|
||||
|
||||
static void warpPerspective(int src_type,
|
||||
@@ -3489,65 +3032,6 @@ void cv::warpPerspective( InputArray _src, OutputArray _dst, InputArray _M0,
|
||||
CV_Assert( (M0.type() == CV_32F || M0.type() == CV_64F) && M0.rows == 3 && M0.cols == 3 );
|
||||
M0.convertTo(matM, matM.type());
|
||||
|
||||
#if defined (HAVE_IPP) && IPP_VERSION_X100 >= 810 && !IPP_DISABLE_WARPPERSPECTIVE
|
||||
CV_IPP_CHECK()
|
||||
{
|
||||
int type = src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
|
||||
if( (depth == CV_8U || depth == CV_16U || depth == CV_32F) &&
|
||||
(cn == 1 || cn == 3 || cn == 4) &&
|
||||
( borderType == cv::BORDER_TRANSPARENT || borderType == cv::BORDER_CONSTANT ) &&
|
||||
(interpolation == INTER_NEAREST || interpolation == INTER_LINEAR || interpolation == INTER_CUBIC))
|
||||
{
|
||||
ippiWarpPerspectiveFunc ippFunc = 0;
|
||||
if ((flags & WARP_INVERSE_MAP) != 0)
|
||||
{
|
||||
ippFunc = type == CV_8UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveBack_8u_C1R :
|
||||
type == CV_8UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveBack_8u_C3R :
|
||||
type == CV_8UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveBack_8u_C4R :
|
||||
type == CV_16UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveBack_16u_C1R :
|
||||
type == CV_16UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveBack_16u_C3R :
|
||||
type == CV_16UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveBack_16u_C4R :
|
||||
type == CV_32FC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveBack_32f_C1R :
|
||||
type == CV_32FC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveBack_32f_C3R :
|
||||
type == CV_32FC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspectiveBack_32f_C4R : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ippFunc = type == CV_8UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspective_8u_C1R :
|
||||
type == CV_8UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspective_8u_C3R :
|
||||
type == CV_8UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspective_8u_C4R :
|
||||
type == CV_16UC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspective_16u_C1R :
|
||||
type == CV_16UC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspective_16u_C3R :
|
||||
type == CV_16UC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspective_16u_C4R :
|
||||
type == CV_32FC1 ? (ippiWarpPerspectiveFunc)ippiWarpPerspective_32f_C1R :
|
||||
type == CV_32FC3 ? (ippiWarpPerspectiveFunc)ippiWarpPerspective_32f_C3R :
|
||||
type == CV_32FC4 ? (ippiWarpPerspectiveFunc)ippiWarpPerspective_32f_C4R : 0;
|
||||
}
|
||||
int mode =
|
||||
interpolation == INTER_NEAREST ? IPPI_INTER_NN :
|
||||
interpolation == INTER_LINEAR ? IPPI_INTER_LINEAR :
|
||||
interpolation == INTER_CUBIC ? IPPI_INTER_CUBIC : 0;
|
||||
CV_Assert(mode && ippFunc);
|
||||
|
||||
double coeffs[3][3];
|
||||
for( int i = 0; i < 3; i++ )
|
||||
for( int j = 0; j < 3; j++ )
|
||||
coeffs[i][j] = matM.at<double>(i, j);
|
||||
|
||||
bool ok;
|
||||
Range range(0, dst.rows);
|
||||
IPPWarpPerspectiveInvoker invoker(src, dst, coeffs, mode, borderType, borderValue, ippFunc, &ok);
|
||||
parallel_for_(range, invoker, dst.total()/(double)(1<<16));
|
||||
if( ok )
|
||||
{
|
||||
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
|
||||
return;
|
||||
}
|
||||
setIppErrorStatus();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( !(flags & WARP_INVERSE_MAP) )
|
||||
invert(matM, matM);
|
||||
|
||||
|
||||
@@ -1076,6 +1076,10 @@ void LineSegmentDetectorImpl::drawSegments(InputOutputArray _image, InputArray l
|
||||
}
|
||||
|
||||
Mat _lines = lines.getMat();
|
||||
if (_lines.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
const int N = _lines.checkVector(4);
|
||||
|
||||
CV_Assert(_lines.depth() == CV_32F || _lines.depth() == CV_32S);
|
||||
|
||||
@@ -138,7 +138,7 @@ Mat getStructuringElement(int shape, Size ksize, Point anchor)
|
||||
int r = 0, c = 0;
|
||||
double inv_r2 = 0;
|
||||
|
||||
CV_Assert( shape == MORPH_RECT || shape == MORPH_CROSS || shape == MORPH_ELLIPSE );
|
||||
CV_Assert( shape == MORPH_RECT || shape == MORPH_CROSS || shape == MORPH_ELLIPSE || shape == MORPH_DIAMOND );
|
||||
|
||||
anchor = normalizeAnchor(anchor, ksize);
|
||||
|
||||
@@ -151,6 +151,11 @@ Mat getStructuringElement(int shape, Size ksize, Point anchor)
|
||||
c = ksize.width/2;
|
||||
inv_r2 = r ? 1./((double)r*r) : 0;
|
||||
}
|
||||
else if( shape == MORPH_DIAMOND )
|
||||
{
|
||||
r = ksize.height/2;
|
||||
c = ksize.width/2;
|
||||
}
|
||||
|
||||
Mat elem(ksize, CV_8U);
|
||||
|
||||
@@ -163,6 +168,16 @@ Mat getStructuringElement(int shape, Size ksize, Point anchor)
|
||||
j2 = ksize.width;
|
||||
else if( shape == MORPH_CROSS )
|
||||
j1 = anchor.x, j2 = j1 + 1;
|
||||
else if( shape == MORPH_DIAMOND )
|
||||
{
|
||||
int dy = std::abs(i - r);
|
||||
if( dy <= r )
|
||||
{
|
||||
int dx = r - dy;
|
||||
j1 = std::max( c - dx, 0 );
|
||||
j2 = std::min( c + dx + 1, ksize.width );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int dy = i - r;
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <vector>
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
@@ -614,8 +615,27 @@ void cv::createHanningWindow(OutputArray _dst, cv::Size winSize, int type)
|
||||
double* const wc = _wc.data();
|
||||
|
||||
double coeff0 = 2.0 * CV_PI / (double)(cols - 1), coeff1 = 2.0 * CV_PI / (double)(rows - 1);
|
||||
for(int j = 0; j < cols; j++)
|
||||
wc[j] = 0.5 * (1.0 - cos(coeff0 * j));
|
||||
int c = 0;
|
||||
#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F
|
||||
const int nlanes32 = VTraits<v_float32>::vlanes();
|
||||
const int nlanes64 = VTraits<v_float64>::vlanes();
|
||||
const int max_nlanes = VTraits<v_float64>::max_nlanes;
|
||||
std::array<double, max_nlanes> index;
|
||||
std::iota(index.data(), index.data()+max_nlanes, 0.f);
|
||||
v_float64 vindex = vx_load(index.data());
|
||||
v_float64 delta = vx_setall_f64(VTraits<v_float64>::vlanes());
|
||||
v_float64 vcoeff0 = vx_setall_f64(coeff0);
|
||||
v_float64 one = vx_setall_f64(1.f);
|
||||
v_float64 half = vx_setall_f64(0.5f);
|
||||
for (; c <= cols - nlanes64; c += nlanes64)
|
||||
{
|
||||
v_float64 v = v_mul(half, v_sub(one, v_cos(v_mul(vcoeff0, vindex))));
|
||||
vx_store(wc + c, v);
|
||||
vindex = v_add(vindex, delta);
|
||||
}
|
||||
#endif
|
||||
for(; c < cols; c++)
|
||||
wc[c] = 0.5 * (1.0 - cos(coeff0 * c));
|
||||
|
||||
if(dst.depth() == CV_32F)
|
||||
{
|
||||
@@ -623,7 +643,17 @@ void cv::createHanningWindow(OutputArray _dst, cv::Size winSize, int type)
|
||||
{
|
||||
float* dstData = dst.ptr<float>(i);
|
||||
double wr = 0.5 * (1.0 - cos(coeff1 * i));
|
||||
for(int j = 0; j < cols; j++)
|
||||
int j = 0;
|
||||
#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F
|
||||
v_float64 vwr = vx_setall_f64(wr);
|
||||
for (; j <= cols - nlanes32; j += nlanes32)
|
||||
{
|
||||
v_float64 v0 = v_mul(vwr, vx_load(wc + j));
|
||||
v_float64 v1 = v_mul(vwr, vx_load(wc + j + nlanes64));
|
||||
vx_store(dstData + j, v_cvt_f32(v0, v1));
|
||||
}
|
||||
#endif
|
||||
for(; j < cols; j++)
|
||||
dstData[j] = (float)(wr * wc[j]);
|
||||
}
|
||||
}
|
||||
@@ -633,7 +663,16 @@ void cv::createHanningWindow(OutputArray _dst, cv::Size winSize, int type)
|
||||
{
|
||||
double* dstData = dst.ptr<double>(i);
|
||||
double wr = 0.5 * (1.0 - cos(coeff1 * i));
|
||||
for(int j = 0; j < cols; j++)
|
||||
int j = 0;
|
||||
#if CV_SIMD_64F || CV_SIMD_SCALABLE_64F
|
||||
v_float64 vwr = vx_setall_f64(wr);
|
||||
for (; j <= cols - nlanes64; j += nlanes64)
|
||||
{
|
||||
v_float64 v = v_mul(vwr, vx_load(wc + j));
|
||||
vx_store(dstData + j, v);
|
||||
}
|
||||
#endif
|
||||
for(; j < cols; j++)
|
||||
dstData[j] = wr * wc[j];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,7 +850,8 @@ static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _
|
||||
|
||||
// CCorr(I', T') = CCorr(I, T'*M) - sum(T'*M)/sum(M)*CCorr(I, M)
|
||||
// It does not matter what to use Mat/MatExpr, it should be evaluated to perform assign subtraction
|
||||
Mat temp_res = img_mask_corr.mul(sum(templx_mask).div(mask_sum));
|
||||
Mat temp_res;
|
||||
multiply(img_mask_corr, sum(templx_mask).div(mask_sum), temp_res);
|
||||
if (img.channels() == 1)
|
||||
{
|
||||
result -= temp_res;
|
||||
@@ -881,8 +882,11 @@ static void matchTemplateMask( InputArray _img, InputArray _templ, OutputArray _
|
||||
Mat img_mask2_corr(corrSize, img.type());
|
||||
crossCorr(img2, mask2, norm_imgx, Point(0,0), 0, 0);
|
||||
crossCorr(img, mask2, img_mask2_corr, Point(0,0), 0, 0);
|
||||
temp_res = img_mask_corr.mul(Scalar(1.0, 1.0, 1.0, 1.0).div(mask_sum))
|
||||
.mul(img_mask_corr.mul(mask2_sum.div(mask_sum)) - 2 * img_mask2_corr);
|
||||
Mat temp_res1;
|
||||
multiply(img_mask_corr, Scalar(1.0, 1.0, 1.0, 1.0).div(mask_sum), temp_res1);
|
||||
Mat temp_res2;
|
||||
multiply(img_mask_corr, mask2_sum.div(mask_sum), temp_res2);
|
||||
temp_res = temp_res1.mul(temp_res2 - 2 * img_mask2_corr);
|
||||
if (img.channels() == 1)
|
||||
{
|
||||
norm_imgx += temp_res;
|
||||
|
||||
@@ -176,4 +176,37 @@ TEST(Imgproc_DistanceTransform, precise_long_dist)
|
||||
EXPECT_EQ(cv::norm(expected, dist, NORM_INF), 0);
|
||||
}
|
||||
|
||||
TEST(Imgproc_DistanceTransform, ipp_deterministic_corner)
|
||||
{
|
||||
setNumThreads(1);
|
||||
|
||||
Mat src(1, 4096, CV_8U, Scalar(255)), dist;
|
||||
src.at<uint8_t>(0, 0) = 0;
|
||||
distanceTransform(src, dist, DIST_L2, DIST_MASK_PRECISE);
|
||||
for (int i = 0; i < src.cols; ++i)
|
||||
{
|
||||
float expected = static_cast<float>(i);
|
||||
ASSERT_EQ(expected, dist.at<float>(0, i)) << cv::format("diff: %e", expected - dist.at<float>(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Imgproc_DistanceTransform, ipp_deterministic)
|
||||
{
|
||||
setNumThreads(1);
|
||||
RNG& rng = TS::ptr()->get_rng();
|
||||
Mat src(1, 800, CV_8U, Scalar(255)), dist;
|
||||
int p1 = cvtest::randInt(rng) % src.cols;
|
||||
int p2 = cvtest::randInt(rng) % src.cols;
|
||||
int p3 = cvtest::randInt(rng) % src.cols;
|
||||
src.at<uint8_t>(0, p1) = 0;
|
||||
src.at<uint8_t>(0, p2) = 0;
|
||||
src.at<uint8_t>(0, p3) = 0;
|
||||
distanceTransform(src, dist, DIST_L2, DIST_MASK_PRECISE);
|
||||
for (int i = 0; i < src.cols; ++i)
|
||||
{
|
||||
float expected = static_cast<float>(min(min(abs(i - p1), abs(i - p2)), abs(i - p3)));
|
||||
ASSERT_EQ(expected, dist.at<float>(0, i)) << cv::format("diff: %e", expected - dist.at<float>(0, i));
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -292,5 +292,27 @@ TEST(Imgproc_IntersectConvexConvex, not_convex)
|
||||
EXPECT_LE(area, 0.f);
|
||||
}
|
||||
|
||||
// The intersection was not properly detected when one line sneaked its way in through an edge point
|
||||
TEST(Imgproc_IntersectConvexConvex, intersection_at_line_transition)
|
||||
{
|
||||
std::vector<cv::Point2f> convex1 = {
|
||||
{ -1.7604526f, -0.00028443217f },
|
||||
{1276.5778f , 0.2091252f},
|
||||
{1276.4617f , 719.27f},
|
||||
{ -1.8754264f, 719.06866f}
|
||||
|
||||
};
|
||||
std::vector<cv::Point2f> convex2 = {
|
||||
{ 0.f , 0.f },
|
||||
{1280.f , 0.f },
|
||||
{1280.f , 720.f},
|
||||
{ 0.f , 720.f }
|
||||
};
|
||||
std::vector<cv::Point> intersection;
|
||||
|
||||
float area = cv::intersectConvexConvex(convex1, convex2, intersection, false);
|
||||
EXPECT_GE(cv::contourArea(convex1), area);
|
||||
EXPECT_GE(cv::contourArea(convex2), area);
|
||||
}
|
||||
} // namespace
|
||||
} // opencv_test
|
||||
|
||||
@@ -402,4 +402,18 @@ TEST_F(Imgproc_LSD_Common, compareSegmentsVec4i)
|
||||
ASSERT_EQ(result2, 11);
|
||||
}
|
||||
|
||||
TEST_F(Imgproc_LSD_Common, drawSegmentsEmpty)
|
||||
{
|
||||
Ptr<LineSegmentDetector> detector = createLineSegmentDetector(LSD_REFINE_STD);
|
||||
Mat1b img = Mat1b::zeros(240, 320);
|
||||
|
||||
std::vector<Vec4i> lines_4i;
|
||||
detector->detect(img, lines_4i);
|
||||
|
||||
Mat3b img_color = Mat3b::zeros(240, 320);
|
||||
ASSERT_NO_THROW(
|
||||
detector->drawSegments(img_color, lines_4i);
|
||||
);
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
TEST(MorphShapes, getStructuringElementDiamond)
|
||||
{
|
||||
cv::Mat element = cv::getStructuringElement(cv::MORPH_DIAMOND, cv::Size(5,5));
|
||||
cv::Mat expected = (cv::Mat_<uchar>(5,5) <<
|
||||
0,0,1,0,0,
|
||||
0,1,1,1,0,
|
||||
1,1,1,1,1,
|
||||
0,1,1,1,0,
|
||||
0,0,1,0,0);
|
||||
EXPECT_EQ(0, cvtest::norm(element, expected, cv::NORM_INF));
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -275,4 +275,16 @@ INSTANTIATE_TEST_CASE_P(MultiChannelMask, Imgproc_MatchTemplateWithMask2,
|
||||
Values(cv::TM_SQDIFF, cv::TM_SQDIFF_NORMED, cv::TM_CCORR, cv::TM_CCORR_NORMED,
|
||||
cv::TM_CCOEFF, cv::TM_CCOEFF_NORMED)));
|
||||
|
||||
TEST(Imgproc_MatchTemplateWithMask, bug_26389) {
|
||||
const Mat image = Mat::ones(Size(10, 10), CV_8UC1);
|
||||
const Mat templ = Mat::ones(Size(10, 7), CV_8UC1);
|
||||
const Mat mask = Mat::ones(Size(10, 7), CV_8UC1);
|
||||
|
||||
for (const int method : {TM_CCOEFF, TM_CCOEFF_NORMED})
|
||||
{
|
||||
Mat result;
|
||||
matchTemplate(image, templ, result, method, mask);
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
|
||||
@@ -1015,6 +1015,9 @@ class JavaWrapperGenerator(object):
|
||||
ret = "return (jlong) _retval_;"
|
||||
elif type_dict[fi.ctype]["jni_type"] == "jdoubleArray":
|
||||
ret = "return _da_retval_;"
|
||||
elif "jni_var" in type_dict[ret_type]:
|
||||
c_epilogue.append(type_dict[ret_type]["jni_var"] % {"n" : '_retval_'})
|
||||
ret = f"return {type_dict[ret_type]['jni_name'] % {'n' : '_retval_'}};"
|
||||
|
||||
# hack: replacing func call with property set/get
|
||||
name = fi.name
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user