1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 23:33:05 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2025-06-11 15:16:34 +03:00
35 changed files with 773 additions and 90 deletions
+3 -3
View File
@@ -1,7 +1,7 @@
function(download_fastcv root_dir)
# Commit SHA in the opencv_3rdparty repo
set(FASTCV_COMMIT "abe340d0fb7f19fa9315080e3c8616642e98a296")
set(FASTCV_COMMIT "2265e79b3b9a8512a9c615b8c4d0244e88f45a9d")
# Define actual FastCV versions
if(ANDROID)
@@ -16,8 +16,8 @@ function(download_fastcv root_dir)
endif()
elseif(UNIX AND NOT APPLE AND NOT IOS AND NOT XROS)
if(AARCH64)
set(FCV_PACKAGE_NAME "fastcv_linux_aarch64_2025_04_29.tgz")
set(FCV_PACKAGE_HASH "e2ce60e25c8e4113a7af2bd243118f4c")
set(FCV_PACKAGE_NAME "fastcv_linux_aarch64_2025_05_29.tgz")
set(FCV_PACKAGE_HASH "decd490524f786e103125b8b948151f3")
else()
message("FastCV: fastcv lib for 32-bit Linux is not supported for now!")
endif()
+9 -3
View File
@@ -17,9 +17,15 @@ function(download_ippicv root_var)
set(OPENCV_ICV_NAME "ippicv_2022.0.0_lnx_intel64_20240904_general.tgz")
set(OPENCV_ICV_HASH "63717ee0f918ad72fb5a737992a206d1")
else()
set(IPPICV_COMMIT "7f55c0c26be418d494615afca15218566775c725")
set(OPENCV_ICV_NAME "ippicv_2021.12.0_lnx_ia32_20240425_general.tgz")
set(OPENCV_ICV_HASH "85ffa2b9ed7802b93c23fa27b0097d36")
if(ANDROID)
set(IPPICV_COMMIT "c7c6d527dde5fee7cb914ee9e4e20f7436aab3a1")
set(OPENCV_ICV_NAME "ippicv_2021.10.1_lnx_ia32_20231206_general.tgz")
set(OPENCV_ICV_HASH "d9510f3ce08f6074aac472a5c19a3b53")
else()
set(IPPICV_COMMIT "7f55c0c26be418d494615afca15218566775c725")
set(OPENCV_ICV_NAME "ippicv_2021.12.0_lnx_ia32_20240425_general.tgz")
set(OPENCV_ICV_HASH "85ffa2b9ed7802b93c23fa27b0097d36")
endif()
endif()
elseif(WIN32 AND NOT ARM)
set(OPENCV_ICV_PLATFORM "windows")
+7
View File
@@ -276,6 +276,13 @@
publisher = {Walter de Gruyter},
url = {https://hal.science/hal-00437581v1}
}
@misc{Chatfield2017,
author = {Chatfield, Carl},
title = {A Simple Method for Distance to Ellipse},
year = {2017},
publisher = {GitHub},
howpublished = {\url{https://blog.chatfield.io/simple-method-for-distance-to-ellipse/}},
}
@article{Chaumette06,
author = {Chaumette, Fran{\c c}ois and Hutchinson, S.},
title = {{Visual servo control, Part I: Basic approaches}},
@@ -137,11 +137,12 @@ HAL and Extension list of APIs
| | |fcvFilterSobel7x7u8s16 |
| |boxFilter |fcvBoxFilter3x3u8_v3 |
| | |fcvBoxFilter5x5u8_v2 |
| | |fcvBoxFilterNxNf32 |
| |adaptiveThreshold |fcvAdaptiveThresholdGaussian3x3u8_v2 |
| | |fcvAdaptiveThresholdGaussian5x5u8_v2 |
| | |fcvAdaptiveThresholdMean3x3u8_v2 |
| | |fcvAdaptiveThresholdMean5x5u8_v2 |
| |pyrUp & pyrDown |fcvPyramidCreateu8_v4 |
| |pyrDown |fcvPyramidCreateu8_v4 |
| |cvtColor |fcvColorRGB888toYCrCbu8_v3 |
| | |fcvColorRGB888ToHSV888u8 |
| |gaussianBlur |fcvFilterGaussian5x5u8_v3 |
@@ -167,10 +168,15 @@ HAL and Extension list of APIs
| |addWeighted |fcvAddWeightedu8_v2 |
| |subtract |fcvImageDiffu8f32_v2 |
| |SVD & solve |fcvSVDf32_v2 |
| |gemm |fcvMatrixMultiplyf32_v2 |
| | |fcvMultiplyScalarf32 |
| | |fcvAddf32_v2 |
**FastCV based OpenCV Extensions APIs list :**
These OpenCV extension APIs are implemented under the **cv::fastcv** namespace.
|OpenCV Extension APIs |Underlying FastCV API for OpenCV acceleration |
|----------------------|----------------------------------------------|
|matmuls8s32 |fcvMatrixMultiplys8s32 |
@@ -195,8 +201,10 @@ HAL and Extension list of APIs
|remap |fcvRemapu8_v2 |
|remapRGBA |fcvRemapRGBA8888BLu8 |
| |fcvRemapRGBA8888NNu8 |
|resizeDownBy2 |fcvScaleDownBy2u8_v2 |
|resizeDownBy4 |fcvScaleDownBy4u8_v2 |
|resizeDown |fcvScaleDownBy2u8_v2 |
| |fcvScaleDownBy4u8_v2 |
| |fcvScaleDownMNInterleaveu8 |
| |fcvScaleDownMNu8 |
|meanShift |fcvMeanShiftu8 |
| |fcvMeanShifts32 |
| |fcvMeanShiftf32 |
@@ -246,3 +254,65 @@ HAL and Extension list of APIs
| |fcvTrackLKOpticalFlowu8 |
|warpPerspective2Plane |fcv2PlaneWarpPerspectiveu8 |
|warpPerspective |fcvWarpPerspectiveu8_v5 |
|arithmetic_op |fcvAddu8 |
| |fcvAdds16_v2 |
| |fcvAddf32 |
| |fcvSubtractu8 |
| |fcvSubtracts16 |
|integrateYUV |fcvIntegrateImageYCbCr420PseudoPlanaru8 |
|normalizeLocalBox |fcvNormalizeLocalBoxu8 |
| |fcvNormalizeLocalBoxf32 |
|merge |fcvChannelCombine2Planesu8 |
| |fcvChannelCombine3Planesu8 |
| |fcvChannelCombine4Planesu8 |
|split |fcvDeinterleaveu8 |
| |fcvChannelExtractu8 |
|warpAffine |fcvTransformAffineu8_v2 |
| |fcvTransformAffineClippedu8_v3 |
| |fcv3ChannelTransformAffineClippedBCu8 |
**FastCV QDSP based OpenCV Extension APIs list :**
These OpenCV extension APIs are implemented under the **cv::fastcv::dsp** namespace.
This namespace provides optimized implementations that leverage QDSP (**Qualcomm's Digital Signal Processor**) acceleration using FastCV's Q-suffixed APIs. These functions require DSP initialization (fcvQ6Init).
|OpenCV Extension APIs |Underlying FastCV API for OpenCV acceleration |
|----------------------|----------------------------------------------|
|filter2D |fcvFilterCorr3x3s8_v2Q |
| |fcvFilterCorrNxNu8Q |
| |fcvFilterCorrNxNu8s16Q |
| |fcvFilterCorrNxNu8f32Q |
|FFT |fcvFFTu8Q |
|IFFT |fcvIFFTf32Q |
|fcvdspinit |fcvQ6Init |
|fcvdspdeinit |fcvQ6DeInit |
|Canny |fcvFilterCannyu8Q |
|sumOfAbsoluteDiffs |fcvSumOfAbsoluteDiffs8x8u8_v2Q |
|thresholdOtsu |fcvFilterThresholdOtsuu8Q |
**How to Use FastCV QDSP based OpenCV Extension APIs**
This section outlines the essential steps required to use OpenCV Extension APIs that are accelerated using FastCV on QDSP(**Qualcomm's Digital Signal Processor**).
1. Initialize QDSP:
- Call **cv::fastcv::dsp::fcvdspinit()** to initialize the QDSP.
2. Allocate memory using **Qualcomm's memory allocator** for all buffers that are being fed to the OpenCV extension API.:
- Use **cv::fastcv::getQcAllocator()** to assign the allocator to the buffers.
- Example:
cv::Mat src;
src.allocator = cv::fastcv::getQcAllocator(); **// Set Qualcomm's memory allocator**
\
After setting Qualcomm's memory allocator, any buffer created using methods like src.create(...), cv::imread(...) etc., will have its memory allocated using Qualcomm's memory allocator.
3. Call the OpenCV extension API from 'cv::fastcv::dsp':
- Example: **cv::fastcv::dsp::thresholdOtsu(src, dst, binaryType);**
where 'src' and 'dst' are 'cv::Mat' objects with the Qualcomm's memory allocator,
     and 'binaryType' is a boolean indicating the thresholding mode.
4. Deinitialize QDSP:
- Call **cv::fastcv::dsp::fcvdspdeinit()** to deinitialize the QDSP.
**Reference Example**:
Refer to a working test case using the OpenCV Extension APIs in the opencv_contrib repository:[opencv_contrib/modules/fastcv/test/test_thresh_dsp.cpp](https://github.com/opencv/opencv_contrib/blob/4.x/modules/fastcv/test/test_thresh_dsp.cpp)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 KiB

After

Width:  |  Height:  |  Size: 535 KiB

+1 -1
View File
@@ -231,7 +231,7 @@ private:
for (int i = 0; i < 8; i++)
coeffs[t*8+i] = 0;
coeffs[t*8+3] = 1;
return;
continue;
}
float sum = 0;
+5 -1
View File
@@ -344,7 +344,11 @@ protected:
}
}
solvePnPRansac(points, projectedPoints, intrinsics, distCoeffs, rvec, tvec, false, pointsCount, 0.5f, 0.99, inliers, method);
bool isEstimateSuccess = solvePnPRansac(points, projectedPoints, intrinsics, distCoeffs, rvec, tvec, false, pointsCount, 0.5f, 0.99, inliers, method);
if (!isEstimateSuccess)
{
return false;
}
bool isTestSuccess = inliers.size() + numOutliers >= points.size();
+2 -2
View File
@@ -722,7 +722,7 @@ void finalizeHdr(Mat& m)
m.rows = m.cols = -1;
if(m.u)
m.datastart = m.data = m.u->data;
if( m.data )
if( m.data && d > 0 )
{
m.datalimit = m.datastart + m.size[0]*m.step[0];
if( m.size[0] > 0 )
@@ -736,7 +736,7 @@ void finalizeHdr(Mat& m)
m.dataend = m.datalimit;
}
else
m.dataend = m.datalimit = 0;
m.dataend = m.datalimit = m.data;
}
//======================================= Mat ======================================================
+19
View File
@@ -1351,6 +1351,25 @@ TEST(Core_Mat, regression_9507)
EXPECT_EQ(25u, m2.total());
}
TEST(Core_Mat, empty)
{
// Should not crash.
uint8_t data[2] = {0, 1};
cv::Mat mat_nd(/*ndims=*/0, /*sizes=*/nullptr, CV_8UC1, /*data=*/data);
cv::Mat1b mat(0, 0, /*data=*/data, /*steps=*/1);
EXPECT_EQ(mat_nd.dims, 0);
EXPECT_EQ(mat.dims, 2);
#if CV_VERSION_MAJOR < 5
EXPECT_LE(mat_nd.total(), 0u);
EXPECT_TRUE(mat_nd.empty());
#else
EXPECT_LE(mat_nd.total(), 1u);
EXPECT_FALSE(mat_nd.empty());
#endif
EXPECT_EQ(mat.total(), 0u);
EXPECT_TRUE(mat.empty());
}
TEST(Core_InputArray, empty)
{
vector<vector<Point> > data;
+16 -2
View File
@@ -115,8 +115,22 @@ if(HAVE_PROTOBUF)
if(PROTOBUF_UPDATE_FILES)
file(GLOB proto_files "${CMAKE_CURRENT_LIST_DIR}/src/tensorflow/*.proto" "${CMAKE_CURRENT_LIST_DIR}/src/caffe/opencv-caffe.proto" "${CMAKE_CURRENT_LIST_DIR}/src/onnx/opencv-onnx.proto")
set(PROTOBUF_GENERATE_CPP_APPEND_PATH ON) # required for tensorflow
protobuf_generate_cpp(fw_srcs fw_hdrs ${proto_files})
if(CMAKE_VERSION VERSION_LESS "3.13.0")
set(PROTOBUF_GENERATE_CPP_APPEND_PATH ON) # required for tensorflow
protobuf_generate_cpp(fw_srcs fw_hdrs ${proto_files})
else()
protobuf_generate(
APPEND_PATH # required for tensorflow
LANGUAGE cpp
IMPORT_DIRS ${Protobuf_IMPORT_DIRS}
OUT_VAR fw_srcs
PROTOC_EXE ${Protobuf_PROTOC_EXECUTABLE}
PROTOS ${proto_files})
set(fw_hdrs "${fw_srcs}")
# separate the header files and source files
list(FILTER fw_srcs EXCLUDE REGEX ".+\.h$")
list(FILTER fw_hdrs INCLUDE REGEX ".+\.h$")
endif()
else()
file(GLOB fw_srcs "${CMAKE_CURRENT_LIST_DIR}/misc/tensorflow/*.cc" "${CMAKE_CURRENT_LIST_DIR}/misc/caffe/opencv-caffe.pb.cc" "${CMAKE_CURRENT_LIST_DIR}/misc/onnx/opencv-onnx.pb.cc")
file(GLOB fw_hdrs "${CMAKE_CURRENT_LIST_DIR}/misc/tensorflow/*.h" "${CMAKE_CURRENT_LIST_DIR}/misc/caffe/opencv-caffe.pb.h" "${CMAKE_CURRENT_LIST_DIR}/misc/onnx/opencv-onnx.pb.h")
+41 -22
View File
@@ -518,11 +518,34 @@ bool JpegXLEncoder::write(const Mat& img, const std::vector<int>& params)
return false;
}
// get distance param for JxlBasicInfo.
float distance = -1.0; // Negative means not set
for( size_t i = 0; i < params.size(); i += 2 )
{
if( params[i] == IMWRITE_JPEGXL_QUALITY )
{
#if JPEGXL_MAJOR_VERSION > 0 || JPEGXL_MINOR_VERSION >= 10
int quality = params[i+1];
quality = MIN(MAX(quality, 0), 100);
distance = JxlEncoderDistanceFromQuality(static_cast<float>(quality));
#else
CV_LOG_ONCE_WARNING(NULL, "Quality parameter is supported with libjxl v0.10.0 or later");
#endif
}
if( params[i] == IMWRITE_JPEGXL_DISTANCE )
{
int distanceInt = params[i+1];
distanceInt = MIN(MAX(distanceInt, 0), 25);
distance = static_cast<float>(distanceInt);
}
}
JxlBasicInfo info;
JxlEncoderInitBasicInfo(&info);
info.xsize = img.cols;
info.ysize = img.rows;
info.uses_original_profile = JXL_FALSE;
// Lossless encoding requires uses_original_profile = true.
info.uses_original_profile = (distance == 0.0) ? JXL_TRUE : JXL_FALSE;
if( img.channels() == 4 )
{
@@ -576,30 +599,26 @@ bool JpegXLEncoder::write(const Mat& img, const std::vector<int>& params)
return false;
JxlEncoderFrameSettings* frame_settings = JxlEncoderFrameSettingsCreate(encoder.get(), nullptr);
// set frame settings with distance params
if(distance == 0.0) // lossless
{
if( JXL_ENC_SUCCESS != JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE) )
{
CV_LOG_WARNING(NULL, "Failed to call JxlEncoderSetFrameLossless()");
}
}
else if(distance > 0.0) // lossy
{
if( JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frame_settings, distance) )
{
CV_LOG_WARNING(NULL, "Failed to call JxlEncoderSetFrameDistance()");
}
}
// set frame settings from params if available
for( size_t i = 0; i < params.size(); i += 2 )
{
if( params[i] == IMWRITE_JPEGXL_QUALITY )
{
#if JPEGXL_MAJOR_VERSION > 0 || JPEGXL_MINOR_VERSION >= 10
int quality = params[i+1];
quality = MIN(MAX(quality, 0), 100);
const float distance = JxlEncoderDistanceFromQuality(static_cast<float>(quality));
JxlEncoderSetFrameDistance(frame_settings, distance);
if (distance == 0)
JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE);
#else
CV_LOG_ONCE_WARNING(NULL, "Quality parameter is supported with libjxl v0.10.0 or later");
#endif
}
if( params[i] == IMWRITE_JPEGXL_DISTANCE )
{
int distance = params[i+1];
distance = MIN(MAX(distance, 0), 25);
JxlEncoderSetFrameDistance(frame_settings, distance);
if (distance == 0)
JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE);
}
if( params[i] == IMWRITE_JPEGXL_EFFORT )
{
int effort = params[i+1];
+60 -8
View File
@@ -7,6 +7,8 @@ namespace opencv_test { namespace {
#ifdef HAVE_JPEGXL
#include <jxl/version.h> // For JPEGXL_MAJOR_VERSION and JPEGXL_MINOR_VERSION
typedef tuple<perf::MatType, int> MatType_and_Distance;
typedef testing::TestWithParam<MatType_and_Distance> Imgcodecs_JpegXL_MatType;
@@ -16,8 +18,8 @@ TEST_P(Imgcodecs_JpegXL_MatType, write_read)
const int distanceParam = get<1>(GetParam());
cv::Scalar col;
// Jpeg XL is lossy compression.
// There may be small differences in decoding results by environments.
// Jpeg XL supports lossy and lossless compressions.
// Lossy compression may be small differences in decoding results by environments.
double th;
switch( CV_MAT_DEPTH(matType) )
@@ -38,7 +40,7 @@ TEST_P(Imgcodecs_JpegXL_MatType, write_read)
}
// If increasing distanceParam, threshold should be increased.
th *= (distanceParam >= 25) ? 5 : ( distanceParam > 2 ) ? 3 : (distanceParam == 2) ? 2: 1;
th *= (distanceParam >= 25) ? 5 : (distanceParam > 2) ? 3 : distanceParam;
bool ret = false;
string tmp_fname = cv::tempfile(".jxl");
@@ -63,8 +65,8 @@ TEST_P(Imgcodecs_JpegXL_MatType, encode_decode)
const int distanceParam = get<1>(GetParam());
cv::Scalar col;
// Jpeg XL is lossy compression.
// There may be small differences in decoding results by environments.
// Jpeg XL supports lossy and lossless compressions.
// Lossy compression may be small differences in decoding results by environments.
double th;
// If alpha=0, libjxl modify color channels(BGR). So do not set it.
@@ -86,7 +88,7 @@ TEST_P(Imgcodecs_JpegXL_MatType, encode_decode)
}
// If increasing distanceParam, threshold should be increased.
th *= (distanceParam >= 25) ? 5 : ( distanceParam > 2 ) ? 3 : (distanceParam == 2) ? 2: 1;
th *= (distanceParam >= 25) ? 5 : (distanceParam > 2) ? 3 : distanceParam;
bool ret = false;
vector<uchar> buff;
@@ -130,8 +132,8 @@ TEST_P(Imgcodecs_JpegXL_Effort_DecodingSpeed, encode_decode)
const int speed = get<1>(GetParam());
cv::Scalar col = cv::Scalar(124,76,42);
// Jpeg XL is lossy compression.
// There may be small differences in decoding results by environments.
// Jpeg XL supports lossy and lossless compression.
// Lossy compression may be small differences in decoding results by environments.
double th = 3; // = 255 / 100 (1%);
bool ret = false;
@@ -305,6 +307,56 @@ TEST(Imgcodecs_JpegXL, imread_truncated_stream)
remove(tmp_fname.c_str());
}
// See https://github.com/opencv/opencv/issues/27382
TEST(Imgcodecs_JpegXL, imencode_regression27382)
{
cv::Mat image(1024, 1024, CV_16U);
cv::RNG rng(1024);
rng.fill(image, cv::RNG::NORMAL, 0, 65535);
std::vector<unsigned char> buffer;
std::vector<int> params = {cv::IMWRITE_JPEGXL_DISTANCE, 0}; // lossless
EXPECT_NO_THROW(cv::imencode(".jxl", image, buffer, params));
cv::Mat decoded;
EXPECT_NO_THROW(decoded = cv::imdecode(buffer, cv::IMREAD_UNCHANGED));
EXPECT_FALSE(decoded.empty());
cv::Mat diff;
cv::absdiff(image, decoded, diff);
double max_diff = 0.0;
cv::minMaxLoc(diff, nullptr, &max_diff);
EXPECT_EQ(max_diff, 0 );
}
TEST(Imgcodecs_JpegXL, imencode_regression27382_2)
{
cv::Mat image(1024, 1024, CV_16U);
cv::RNG rng(1024);
rng.fill(image, cv::RNG::NORMAL, 0, 65535);
std::vector<unsigned char> buffer;
std::vector<int> params = {cv::IMWRITE_JPEGXL_QUALITY, 100}; // lossless
EXPECT_NO_THROW(cv::imencode(".jxl", image, buffer, params));
cv::Mat decoded;
EXPECT_NO_THROW(decoded = cv::imdecode(buffer, cv::IMREAD_UNCHANGED));
EXPECT_FALSE(decoded.empty());
cv::Mat diff;
cv::absdiff(image, decoded, diff);
double max_diff = 0.0;
cv::minMaxLoc(diff, nullptr, &max_diff);
#if JPEGXL_MAJOR_VERSION > 0 || JPEGXL_MINOR_VERSION >= 10
// Quality parameter is supported with libjxl v0.10.0 or later
EXPECT_EQ(max_diff, 0); // Lossless
#else
EXPECT_NE(max_diff, 0); // Lossy
#endif
}
#endif // HAVE_JPEGXL
+36 -13
View File
@@ -118,7 +118,7 @@ This module offers a comprehensive suite of image processing functions, enabling
coordinates needs to be retrieved. In the simplest case, the coordinates can be just rounded to the
nearest integer coordinates and the corresponding pixel can be used. This is called a
nearest-neighbor interpolation. However, a better result can be achieved by using more
sophisticated [interpolation methods](http://en.wikipedia.org/wiki/Multivariate_interpolation) ,
sophisticated [interpolation methods](https://en.wikipedia.org/wiki/Multivariate_interpolation) ,
where a polynomial function is fit into some neighborhood of the computed pixel \f$(f_x(x,y),
f_y(x,y))\f$, and then the value of the polynomial at \f$(f_x(x,y), f_y(x,y))\f$ is taken as the
interpolated pixel value. In OpenCV, you can choose between several interpolation methods. See
@@ -1467,7 +1467,7 @@ CV_EXPORTS_W void getDerivKernels( OutputArray kx, OutputArray ky,
/** @brief Returns Gabor filter coefficients.
For more details about gabor filter equations and parameters, see: [Gabor
Filter](http://en.wikipedia.org/wiki/Gabor_filter).
Filter](https://en.wikipedia.org/wiki/Gabor_filter).
@param ksize Size of the filter returned.
@param sigma Standard deviation of the gaussian envelope.
@@ -1549,7 +1549,7 @@ CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize,
/** @brief Applies the bilateral filter to an image.
The function applies bilateral filtering to the input image, as described in
http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html
https://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html
bilateralFilter can reduce unwanted noise very well while keeping edges fairly sharp. However, it is
very slow compared to most filters.
@@ -1659,7 +1659,7 @@ stackBlur can generate similar results as Gaussian blur, and the time consumptio
It creates a kind of moving stack of colors whilst scanning through the image. Thereby it just has to add one new block of color to the right side
of the stack and remove the leftmost color. The remaining colors on the topmost layer of the stack are either added on or reduced by one,
depending on if they are on the right or on the left side of the stack. The only supported borderType is BORDER_REPLICATE.
Original paper was proposed by Mario Klingemann, which can be found http://underdestruction.com/2004/02/25/stackblur-2004.
Original paper was proposed by Mario Klingemann, which can be found https://underdestruction.com/2004/02/25/stackblur-2004.
@param src input image. The number of channels can be arbitrary, but the depth should be one of
CV_8U, CV_16U, CV_16S or CV_32F.
@@ -1892,7 +1892,7 @@ Check @ref tutorial_canny_detector "the corresponding tutorial" for more details
The function finds edges in the input image and marks them in the output map edges using the
Canny algorithm. The smallest value between threshold1 and threshold2 is used for edge linking. The
largest value is used to find initial segments of strong edges. See
<http://en.wikipedia.org/wiki/Canny_edge_detector>
<https://en.wikipedia.org/wiki/Canny_edge_detector>
@param image 8-bit input image.
@param edges output edge map; single channels 8-bit image, which has the same size as image .
@@ -2168,7 +2168,7 @@ An example using the Hough line detector in python
/** @brief Finds lines in a binary image using the standard Hough transform.
The function implements the standard or standard multi-scale Hough transform algorithm for line
detection. See <http://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm> for a good explanation of Hough
detection. See <https://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm> for a good explanation of Hough
transform.
@param image 8-bit, single-channel binary source image. The image may be modified by the function.
@@ -2938,13 +2938,13 @@ An example using the phaseCorrelate function
The operation takes advantage of the Fourier shift theorem for detecting the translational shift in
the frequency domain. It can be used for fast image registration as well as motion estimation. For
more information please see <http://en.wikipedia.org/wiki/Phase_correlation>
more information please see <https://en.wikipedia.org/wiki/Phase_correlation>
Calculates the cross-power spectrum of two supplied source arrays. The arrays are padded if needed
with getOptimalDFTSize.
The function performs the following equations:
- First it applies a Hanning window (see <http://en.wikipedia.org/wiki/Hann_function>) to each
- 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.
- Next it computes the forward DFTs of each source array:
@@ -2974,7 +2974,7 @@ CV_EXPORTS_W Point2d phaseCorrelate(InputArray src1, InputArray src2,
/** @brief This function computes a Hanning window coefficients in two dimensions.
See (http://en.wikipedia.org/wiki/Hann_function) and (http://en.wikipedia.org/wiki/Window_function)
See (https://en.wikipedia.org/wiki/Hann_function) and (https://en.wikipedia.org/wiki/Window_function)
for more information.
An example is shown below:
@@ -3462,7 +3462,7 @@ An example using the GrabCut algorithm
/** @brief Runs the GrabCut algorithm.
The function implements the [GrabCut image segmentation algorithm](http://en.wikipedia.org/wiki/GrabCut).
The function implements the [GrabCut image segmentation algorithm](https://en.wikipedia.org/wiki/GrabCut).
@param img Input 8-bit 3-channel image.
@param mask Input/output 8-bit single-channel mask. The mask is initialized by the function when
@@ -3792,7 +3792,7 @@ CV_EXPORTS_W Moments moments( InputArray array, bool binaryImage = false );
/** @brief Calculates seven Hu invariants.
The function calculates seven Hu invariants (introduced in @cite Hu62; see also
<http://en.wikipedia.org/wiki/Image_moment>) defined as:
<https://en.wikipedia.org/wiki/Image_moment>) defined as:
\f[\begin{array}{l} hu[0]= \eta _{20}+ \eta _{02} \\ hu[1]=( \eta _{20}- \eta _{02})^{2}+4 \eta _{11}^{2} \\ hu[2]=( \eta _{30}-3 \eta _{12})^{2}+ (3 \eta _{21}- \eta _{03})^{2} \\ hu[3]=( \eta _{30}+ \eta _{12})^{2}+ ( \eta _{21}+ \eta _{03})^{2} \\ hu[4]=( \eta _{30}-3 \eta _{12})( \eta _{30}+ \eta _{12})[( \eta _{30}+ \eta _{12})^{2}-3( \eta _{21}+ \eta _{03})^{2}]+(3 \eta _{21}- \eta _{03})( \eta _{21}+ \eta _{03})[3( \eta _{30}+ \eta _{12})^{2}-( \eta _{21}+ \eta _{03})^{2}] \\ hu[5]=( \eta _{20}- \eta _{02})[( \eta _{30}+ \eta _{12})^{2}- ( \eta _{21}+ \eta _{03})^{2}]+4 \eta _{11}( \eta _{30}+ \eta _{12})( \eta _{21}+ \eta _{03}) \\ hu[6]=(3 \eta _{21}- \eta _{03})( \eta _{21}+ \eta _{03})[3( \eta _{30}+ \eta _{12})^{2}-( \eta _{21}+ \eta _{03})^{2}]-( \eta _{30}-3 \eta _{12})( \eta _{21}+ \eta _{03})[3( \eta _{30}+ \eta _{12})^{2}-( \eta _{21}+ \eta _{03})^{2}] \\ \end{array}\f]
@@ -4037,7 +4037,7 @@ An example using approxPolyDP function in python.
The function cv::approxPolyDP approximates a curve or a polygon with another curve/polygon with less
vertices so that the distance between them is less or equal to the specified precision. It uses the
Douglas-Peucker algorithm <http://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm>
Douglas-Peucker algorithm <https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm>
@param curve Input vector of a 2D point stored in std::vector or Mat
@param approxCurve Result of the approximation. The type should match the type of the input curve.
@@ -4286,6 +4286,9 @@ ellipse/rotatedRect data contains negative indices, due to the data points being
border of the containing Mat element.
@param points Input 2D point set, stored in std::vector\<\> or Mat
@note Input point types are @ref Point2i or @ref Point2f and at least 5 points are required.
@note @ref getClosestEllipsePoints function can be used to compute the ellipse fitting error.
*/
CV_EXPORTS_W RotatedRect fitEllipse( InputArray points );
@@ -4323,6 +4326,9 @@ CV_EXPORTS_W RotatedRect fitEllipse( InputArray points );
\f}
@param points Input 2D point set, stored in std::vector\<\> or Mat
@note Input point types are @ref Point2i or @ref Point2f and at least 5 points are required.
@note @ref getClosestEllipsePoints function can be used to compute the ellipse fitting error.
*/
CV_EXPORTS_W RotatedRect fitEllipseAMS( InputArray points );
@@ -4368,6 +4374,9 @@ CV_EXPORTS_W RotatedRect fitEllipseAMS( InputArray points );
The scaling factor guarantees that \f$A^T C A =1\f$.
@param points Input 2D point set, stored in std::vector\<\> or Mat
@note Input point types are @ref Point2i or @ref Point2f and at least 5 points are required.
@note @ref getClosestEllipsePoints function can be used to compute the ellipse fitting error.
*/
CV_EXPORTS_W RotatedRect fitEllipseDirect( InputArray points );
@@ -4375,6 +4384,20 @@ CV_EXPORTS_W RotatedRect fitEllipseDirect( InputArray points );
An example for fitting line in python
*/
/** @brief Compute for each 2d point the nearest 2d point located on a given ellipse.
The function computes the nearest 2d location on a given ellipse for a vector of 2d points and is based on @cite Chatfield2017 code.
This function can be used to compute for instance the ellipse fitting error.
@param ellipse_params Ellipse parameters
@param points Input 2d points
@param closest_pts For each 2d point, their corresponding closest 2d point located on a given ellipse
@note Input point types are @ref Point2i or @ref Point2f
@see fitEllipse, fitEllipseAMS, fitEllipseDirect
*/
CV_EXPORTS_W void getClosestEllipsePoints( const RotatedRect& ellipse_params, InputArray points, OutputArray closest_pts );
/** @brief Fits a line to a 2D or 3D point set.
The function fitLine fits a line to a 2D or 3D point set by minimizing \f$\sum_i \rho(r_i)\f$ where
@@ -4393,7 +4416,7 @@ of the following:
- DIST_HUBER
\f[\rho (r) = \fork{r^2/2}{if \(r < C\)}{C \cdot (r-C/2)}{otherwise} \quad \text{where} \quad C=1.345\f]
The algorithm is based on the M-estimator ( <http://en.wikipedia.org/wiki/M-estimator> ) technique
The algorithm is based on the M-estimator ( <https://en.wikipedia.org/wiki/M-estimator> ) technique
that iteratively fits the line using the weighted least-squares algorithm. After each iteration the
weights \f$w_i\f$ are adjusted to be inversely proportional to \f$\rho(r_i)\f$ .
@@ -21,14 +21,34 @@ void HoughLinesWithAccumulator(
InputArray image, OutputArray lines,
double rho, double theta, int threshold,
double srn = 0, double stn = 0,
double min_theta = 0, double max_theta = CV_PI
double min_theta = 0, double max_theta = CV_PI,
bool use_edgeval = false
)
{
std::vector<Vec3f> lines_acc;
HoughLines(image, lines_acc, rho, theta, threshold, srn, stn, min_theta, max_theta);
HoughLines(image, lines_acc, rho, theta, threshold, srn, stn, min_theta, max_theta, use_edgeval);
Mat(lines_acc).copyTo(lines);
}
/** @brief Finds circles in a grayscale image using the Hough transform and get accumulator.
*
* @note This function is for bindings use only. Use original function in C++ code
*
* @sa HoughCircles
*/
CV_WRAP static inline
void HoughCirclesWithAccumulator(
InputArray image, OutputArray circles,
int method, double dp, double minDist,
double param1 = 100, double param2 = 100,
int minRadius = 0, int maxRadius = 0
)
{
std::vector<Vec4f> circles_acc;
HoughCircles(image, circles_acc, method, dp, minDist, param1, param2, minRadius, maxRadius);
Mat(1, static_cast<int>(circles_acc.size()), CV_32FC4, &circles_acc.front()).copyTo(circles);
}
} // namespace
#endif // OPENCV_IMGPROC_BINDINGS_HPP
+117
View File
@@ -876,3 +876,120 @@ cv::RotatedRect cv::fitEllipseDirect( InputArray _points )
}
return box;
}
namespace cv
{
// @misc{Chatfield2017,
// author = {Chatfield, Carl},
// title = {A Simple Method for Distance to Ellipse},
// year = {2017},
// publisher = {GitHub},
// howpublished = {\url{https://blog.chatfield.io/simple-method-for-distance-to-ellipse/}},
// }
// https://github.com/0xfaded/ellipse_demo/blob/master/ellipse_trig_free.py
static void solveFast(float semi_major, float semi_minor, const cv::Point2f& pt, cv::Point2f& closest_pt)
{
float px = std::abs(pt.x);
float py = std::abs(pt.y);
float tx = 0.707f;
float ty = 0.707f;
float a = semi_major;
float b = semi_minor;
for (int iter = 0; iter < 3; iter++)
{
float x = a * tx;
float y = b * ty;
float ex = (a*a - b*b) * tx*tx*tx / a;
float ey = (b*b - a*a) * ty*ty*ty / b;
float rx = x - ex;
float ry = y - ey;
float qx = px - ex;
float qy = py - ey;
float r = std::hypotf(rx, ry);
float q = std::hypotf(qx, qy);
tx = std::min(1.0f, std::max(0.0f, (qx * r / q + ex) / a));
ty = std::min(1.0f, std::max(0.0f, (qy * r / q + ey) / b));
float t = std::hypotf(tx, ty);
tx /= t;
ty /= t;
}
closest_pt.x = std::copysign(a * tx, pt.x);
closest_pt.y = std::copysign(b * ty, pt.y);
}
} // namespace cv
void cv::getClosestEllipsePoints( const RotatedRect& ellipse_params, InputArray _points, OutputArray closest_pts )
{
CV_INSTRUMENT_REGION();
Mat points = _points.getMat();
int n = points.checkVector(2);
int depth = points.depth();
CV_Assert(depth == CV_32F || depth == CV_32S);
CV_Assert(n > 0);
bool is_float = (depth == CV_32F);
const Point* ptsi = points.ptr<Point>();
const Point2f* ptsf = points.ptr<Point2f>();
float semi_major = ellipse_params.size.width / 2.0f;
float semi_minor = ellipse_params.size.height / 2.0f;
float angle_deg = ellipse_params.angle;
if (semi_major < semi_minor)
{
std::swap(semi_major, semi_minor);
angle_deg += 90;
}
Matx23f align_T_ori_f32;
float theta_rad = static_cast<float>(angle_deg * M_PI / 180);
float co = std::cos(theta_rad);
float si = std::sin(theta_rad);
float shift_x = ellipse_params.center.x;
float shift_y = ellipse_params.center.y;
align_T_ori_f32(0,0) = co;
align_T_ori_f32(0,1) = si;
align_T_ori_f32(0,2) = -co*shift_x - si*shift_y;
align_T_ori_f32(1,0) = -si;
align_T_ori_f32(1,1) = co;
align_T_ori_f32(1,2) = si*shift_x - co*shift_y;
Matx23f ori_T_align_f32;
ori_T_align_f32(0,0) = co;
ori_T_align_f32(0,1) = -si;
ori_T_align_f32(0,2) = shift_x;
ori_T_align_f32(1,0) = si;
ori_T_align_f32(1,1) = co;
ori_T_align_f32(1,2) = shift_y;
std::vector<Point2f> closest_pts_list;
closest_pts_list.reserve(n);
for (int i = 0; i < n; i++)
{
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
Matx31f pmat(p.x, p.y, 1);
Matx21f X_align = align_T_ori_f32 * pmat;
Point2f closest_pt;
solveFast(semi_major, semi_minor, Point2f(X_align(0,0), X_align(1,0)), closest_pt);
pmat(0,0) = closest_pt.x;
pmat(1,0) = closest_pt.y;
Matx21f closest_pt_ori = ori_T_align_f32 * pmat;
closest_pts_list.push_back(Point2f(closest_pt_ori(0,0), closest_pt_ori(1,0)));
}
cv::Mat(closest_pts_list).convertTo(closest_pts, CV_32F);
}
/* End of file. */
+1 -1
View File
@@ -493,7 +493,7 @@ void Subdiv2D::initDelaunay( Rect rect )
{
CV_INSTRUMENT_REGION();
float big_coord = 3.f * MAX( rect.width, rect.height );
float big_coord = 6.f * MAX( rect.width, rect.height );
float rx = (float)rect.x;
float ry = (float)rect.y;
+2 -2
View File
@@ -500,10 +500,10 @@ PARAM_TEST_CASE(RemapRelative, MatDepth, Channels, Interpolation, BorderType, bo
data64FC1.reshape(nChannels, size.height).convertTo(src, srcType);
cv::Mat mapRelativeX32F(size, CV_32FC1);
mapRelativeX32F.setTo(cv::Scalar::all(-0.33));
mapRelativeX32F.setTo(cv::Scalar::all(-0.25));
cv::Mat mapRelativeY32F(size, CV_32FC1);
mapRelativeY32F.setTo(cv::Scalar::all(-0.33));
mapRelativeY32F.setTo(cv::Scalar::all(-0.25));
cv::Mat mapAbsoluteX32F = mapRelativeX32F.clone();
mapAbsoluteX32F.forEach<float>([&](float& pixel, const int* position) {
@@ -69,6 +69,8 @@ TEST(Imgproc_CornerSubPix, out_of_image_corners)
TEST(Imgproc_CornerSubPix, corners_on_the_edge)
{
cv::Mat image(500, 500, CV_8UC1);
RNG& rng = TS::ptr()->get_rng();
cvtest::randUni(rng, image, 0, 255);
cv::Size win(1, 1);
cv::Size zeroZone(-1, -1);
cv::TermCriteria criteria;
+193
View File
@@ -113,4 +113,197 @@ TEST(Imgproc_FitEllipse_HorizontalLine, accuracy) {
EXPECT_NEAR(el.angle, 90, 0.1);
}
template<typename T>
static float get_ellipse_fitting_error(const std::vector<T>& points, const Mat& closest_points) {
float mse = 0.0f;
for (int i = 0; i < static_cast<int>(points.size()); i++)
{
Point2f pt_err = Point2f(static_cast<float>(points[i].x), static_cast<float>(points[i].y)) - closest_points.at<Point2f>(i);
mse += pt_err.x*pt_err.x + pt_err.y*pt_err.y;
}
return mse / points.size();
}
TEST(Imgproc_getClosestEllipsePoints, ellipse_mse) {
// https://github.com/opencv/opencv/issues/26078
std::vector<Point2i> points_list;
// [1434, 308], [1434, 309], [1433, 310], [1427, 310], [1427, 312], [1426, 313], [1422, 313], [1422, 314],
points_list.push_back(Point2i(1434, 308));
points_list.push_back(Point2i(1434, 309));
points_list.push_back(Point2i(1433, 310));
points_list.push_back(Point2i(1427, 310));
points_list.push_back(Point2i(1427, 312));
points_list.push_back(Point2i(1426, 313));
points_list.push_back(Point2i(1422, 313));
points_list.push_back(Point2i(1422, 314));
// [1421, 315], [1415, 315], [1415, 316], [1414, 317], [1408, 317], [1408, 319], [1407, 320], [1403, 320],
points_list.push_back(Point2i(1421, 315));
points_list.push_back(Point2i(1415, 315));
points_list.push_back(Point2i(1415, 316));
points_list.push_back(Point2i(1414, 317));
points_list.push_back(Point2i(1408, 317));
points_list.push_back(Point2i(1408, 319));
points_list.push_back(Point2i(1407, 320));
points_list.push_back(Point2i(1403, 320));
// [1403, 321], [1402, 322], [1396, 322], [1396, 323], [1395, 324], [1389, 324], [1389, 326], [1388, 327],
points_list.push_back(Point2i(1403, 321));
points_list.push_back(Point2i(1402, 322));
points_list.push_back(Point2i(1396, 322));
points_list.push_back(Point2i(1396, 323));
points_list.push_back(Point2i(1395, 324));
points_list.push_back(Point2i(1389, 324));
points_list.push_back(Point2i(1389, 326));
points_list.push_back(Point2i(1388, 327));
// [1382, 327], [1382, 328], [1381, 329], [1376, 329], [1376, 330], [1375, 331], [1369, 331], [1369, 333],
points_list.push_back(Point2i(1382, 327));
points_list.push_back(Point2i(1382, 328));
points_list.push_back(Point2i(1381, 329));
points_list.push_back(Point2i(1376, 329));
points_list.push_back(Point2i(1376, 330));
points_list.push_back(Point2i(1375, 331));
points_list.push_back(Point2i(1369, 331));
points_list.push_back(Point2i(1369, 333));
// [1368, 334], [1362, 334], [1362, 335], [1361, 336], [1359, 336], [1359, 1016], [1365, 1016], [1366, 1017],
points_list.push_back(Point2i(1368, 334));
points_list.push_back(Point2i(1362, 334));
points_list.push_back(Point2i(1362, 335));
points_list.push_back(Point2i(1361, 336));
points_list.push_back(Point2i(1359, 336));
points_list.push_back(Point2i(1359, 1016));
points_list.push_back(Point2i(1365, 1016));
points_list.push_back(Point2i(1366, 1017));
// [1366, 1019], [1430, 1019], [1430, 1017], [1431, 1016], [1440, 1016], [1440, 308]
points_list.push_back(Point2i(1366, 1019));
points_list.push_back(Point2i(1430, 1019));
points_list.push_back(Point2i(1430, 1017));
points_list.push_back(Point2i(1431, 1016));
points_list.push_back(Point2i(1440, 1016));
points_list.push_back(Point2i(1440, 308));
RotatedRect fit_ellipse_params(
Point2f(1442.97900390625, 662.1879272460938),
Size2f(579.5570678710938, 730.834228515625),
20.190902709960938
);
// Point2i
{
Mat pointsi(points_list);
Mat closest_pts;
getClosestEllipsePoints(fit_ellipse_params, pointsi, closest_pts);
EXPECT_TRUE(pointsi.rows == closest_pts.rows);
EXPECT_TRUE(pointsi.cols == closest_pts.cols);
EXPECT_TRUE(pointsi.channels() == closest_pts.channels());
float fit_ellipse_mse = get_ellipse_fitting_error(points_list, closest_pts);
EXPECT_NEAR(fit_ellipse_mse, 1.61994, 1e-4);
}
// Point2f
{
Mat pointsf;
Mat(points_list).convertTo(pointsf, CV_32F);
Mat closest_pts;
getClosestEllipsePoints(fit_ellipse_params, pointsf, closest_pts);
EXPECT_TRUE(pointsf.rows == closest_pts.rows);
EXPECT_TRUE(pointsf.cols == closest_pts.cols);
EXPECT_TRUE(pointsf.channels() == closest_pts.channels());
float fit_ellipse_mse = get_ellipse_fitting_error(points_list, closest_pts);
EXPECT_NEAR(fit_ellipse_mse, 1.61994, 1e-4);
}
}
static std::vector<Point2f> sample_ellipse_pts(const RotatedRect& ellipse_params) {
// Sample N points using the ellipse parametric form
float xc = ellipse_params.center.x;
float yc = ellipse_params.center.y;
float a = ellipse_params.size.width / 2;
float b = ellipse_params.size.height / 2;
float theta = static_cast<float>(ellipse_params.angle * M_PI / 180);
float cos_th = std::cos(theta);
float sin_th = std::sin(theta);
int nb_samples = 180;
std::vector<Point2f> ellipse_pts(nb_samples);
for (int i = 0; i < nb_samples; i++) {
float ax = a * cos_th;
float ay = a * sin_th;
float bx = -b * sin_th;
float by = b * cos_th;
float t = static_cast<float>(i / static_cast<float>(nb_samples) * 2*M_PI);
float cos_t = std::cos(t);
float sin_t = std::sin(t);
ellipse_pts[i].x = xc + ax*cos_t + bx*sin_t;
ellipse_pts[i].y = yc + ay*cos_t + by*sin_t;
}
return ellipse_pts;
}
TEST(Imgproc_getClosestEllipsePoints, ellipse_mse_2) {
const float tol = 1e-3f;
// bb height > width
// Check correctness of the minor/major axes swapping and updated angle in getClosestEllipsePoints
{
RotatedRect ellipse_params(
Point2f(-142.97f, -662.1878f),
Size2f(539.557f, 730.83f),
27.09960938f
);
std::vector<Point2f> ellipse_pts = sample_ellipse_pts(ellipse_params);
Mat pointsf, closest_pts;
Mat(ellipse_pts).convertTo(pointsf, CV_32F);
getClosestEllipsePoints(ellipse_params, pointsf, closest_pts);
float ellipse_pts_mse = get_ellipse_fitting_error(ellipse_pts, closest_pts);
EXPECT_NEAR(ellipse_pts_mse, 0, tol);
}
// bb height > width + negative angle
{
RotatedRect ellipse_params(
Point2f(-142.97f, 562.1878f),
Size2f(53.557f, 730.83f),
-75.09960938f
);
std::vector<Point2f> ellipse_pts = sample_ellipse_pts(ellipse_params);
Mat pointsf, closest_pts;
Mat(ellipse_pts).convertTo(pointsf, CV_32F);
getClosestEllipsePoints(ellipse_params, pointsf, closest_pts);
float ellipse_pts_mse = get_ellipse_fitting_error(ellipse_pts, closest_pts);
EXPECT_NEAR(ellipse_pts_mse, 0, tol);
}
// Negative angle
{
RotatedRect ellipse_params(
Point2f(742.97f, -462.1878f),
Size2f(535.57f, 130.83f),
-75.09960938f
);
std::vector<Point2f> ellipse_pts = sample_ellipse_pts(ellipse_params);
Mat pointsf, closest_pts;
Mat(ellipse_pts).convertTo(pointsf, CV_32F);
getClosestEllipsePoints(ellipse_params, pointsf, closest_pts);
float ellipse_pts_mse = get_ellipse_fitting_error(ellipse_pts, closest_pts);
EXPECT_NEAR(ellipse_pts_mse, 0, tol);
}
}
}} // namespace
+3 -3
View File
@@ -772,10 +772,10 @@ TEST_P(Imgproc_RemapRelative, validity)
data64FC1.reshape(nChannels, size.height).convertTo(src, srcType);
cv::Mat mapRelativeX32F(size, CV_32FC1);
mapRelativeX32F.setTo(cv::Scalar::all(-0.33));
mapRelativeX32F.setTo(cv::Scalar::all(-0.25));
cv::Mat mapRelativeY32F(size, CV_32FC1);
mapRelativeY32F.setTo(cv::Scalar::all(-0.33));
mapRelativeY32F.setTo(cv::Scalar::all(-0.25));
cv::Mat mapAbsoluteX32F = mapRelativeX32F.clone();
mapAbsoluteX32F.forEach<float>([&](float& pixel, const int* position) {
@@ -811,7 +811,7 @@ TEST_P(Imgproc_RemapRelative, validity)
cv::remap(src, dstRelative, mapRelativeX32F, mapRelativeY32F, interpolation | WARP_RELATIVE_MAP, borderType);
}
EXPECT_EQ(cvtest::norm(dstAbsolute, dstRelative, NORM_INF), 0);
EXPECT_LE(cvtest::norm(dstAbsolute, dstRelative, NORM_INF), 1);
};
INSTANTIATE_TEST_CASE_P(ImgProc, Imgproc_RemapRelative, testing::Combine(
@@ -50,4 +50,18 @@ TEST(Imgproc_Subdiv2D_getTriangleList, regression_5788)
EXPECT_EQ(trig_cnt, 105);
}
TEST(Imgproc_Subdiv2D, issue_25696) {
std::vector<cv::Point2f> points{
{0, 0}, {40, 40}, {84, 104}, {86, 108}
};
cv::Rect subdivRect{cv::Point{-10, -10}, cv::Point{96, 118}};
cv::Subdiv2D subdiv{subdivRect};
subdiv.insert(points);
std::vector<cv::Vec6f> triangles;
subdiv.getTriangleList(triangles);
ASSERT_EQ(static_cast<size_t>(2), triangles.size());
}
}}
@@ -96,8 +96,10 @@ public class JavaCamera2View extends CameraBridgeViewBase {
Log.e(LOGTAG, "Error: camera isn't detected.");
return false;
}
boolean chosen = false; // remember whether the camera ID is set.
if (mCameraIndex == CameraBridgeViewBase.CAMERA_ID_ANY) {
mCameraID = camList[0];
chosen = true;
} else {
for (String cameraID : camList) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraID);
@@ -107,11 +109,12 @@ public class JavaCamera2View extends CameraBridgeViewBase {
characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT)
) {
mCameraID = cameraID;
chosen = true;
break;
}
}
}
if (mCameraID == null) { // make JavaCamera2View behaves in the same way as JavaCameraView
if (mCameraID == null || !chosen) { // make JavaCamera2View behaves in the same way as JavaCameraView
Log.i(LOGTAG, "Selecting camera by index (" + mCameraIndex + ")");
if (mCameraIndex < camList.length) {
mCameraID = camList[mCameraIndex];
+5 -1
View File
@@ -65,10 +65,14 @@ Mat triangleWeights()
Mat w(LDR_SIZE, 1, CV_32F);
int half = LDR_SIZE / 2;
int maxVal = LDR_SIZE - 1;
for (int i = 0; i < LDR_SIZE; i++)
float epsilon = 1e-6f;
w.at<float>(0) = epsilon;
w.at<float>(LDR_SIZE-1) = epsilon;
for (int i = 1; i < LDR_SIZE-1; i++){
w.at<float>(i) = (i < half)
? static_cast<float>(i)
: static_cast<float>(maxVal - i);
}
return w;
}
+14 -10
View File
@@ -72,8 +72,10 @@ public:
double min, max;
minMaxLoc(src, &min, &max);
float fmin = static_cast<float>(min);
float fmax = static_cast<float>(max);
if(max - min > DBL_EPSILON) {
dst = (src - min) / (max - min);
dst = (src - fmin) / (fmax - fmin);
} else {
src.copyTo(dst);
}
@@ -139,8 +141,9 @@ public:
gray_img /= mean;
log_img.release();
double max;
minMaxLoc(gray_img, NULL, &max);
double dmax;
minMaxLoc(gray_img, NULL, &dmax);
float max = static_cast<float>(dmax);
CV_Assert(max > 0);
Mat map;
@@ -150,7 +153,6 @@ public:
log(2.0f + 8.0f * div, div);
map = map.mul(1.0f / div);
div.release();
mapLuminance(img, img, gray_img, map, saturation);
linear->setGamma(gamma);
@@ -223,12 +225,14 @@ public:
log_(gray_img, log_img);
float log_mean = static_cast<float>(sum(log_img)[0] / log_img.total());
double log_min, log_max;
minMaxLoc(log_img, &log_min, &log_max);
double dlog_min, dlog_max;
minMaxLoc(log_img, &dlog_min, &dlog_max);
float log_max = static_cast<float>(dlog_max);
float log_min = static_cast<float>(dlog_min);
log_img.release();
double key = static_cast<float>((log_max - log_mean) / (log_max - log_min));
float map_key = 0.3f + 0.7f * pow(static_cast<float>(key), 1.4f);
float key = (log_max - log_mean) / (log_max - log_min);
float map_key = 0.3f + 0.7f * pow(key, 1.4f);
intensity = exp(-intensity);
Scalar chan_mean = mean(img);
float gray_mean = static_cast<float>(mean(gray_img)[0]);
@@ -287,9 +291,9 @@ protected:
float gamma, intensity, light_adapt, color_adapt;
};
Ptr<TonemapReinhard> createTonemapReinhard(float gamma, float contrast, float sigma_color, float sigma_space)
Ptr<TonemapReinhard> createTonemapReinhard(float gamma, float intensity, float light_adapt, float color_adapt)
{
return makePtr<TonemapReinhardImpl>(gamma, contrast, sigma_color, sigma_space);
return makePtr<TonemapReinhardImpl>(gamma, intensity, light_adapt, color_adapt);
}
class TonemapMantiukImpl CV_FINAL : public TonemapMantiuk
+30
View File
@@ -79,6 +79,20 @@ class houghcircles_test(NewOpenCVTests):
self.assertGreater(float(matches_counter) / len(testCircles), .5)
self.assertLess(float(len(circles) - matches_counter) / len(circles), .75)
circles_acc = cv.HoughCirclesWithAccumulator(
image=img,
method=cv.HOUGH_GRADIENT,
dp=1,
minDist=10,
circles=np.array([]),
param1=150,
param2=45,
minRadius=1,
maxRadius=30)
self.assertEqual(circles_acc.shape, (1, 2, 4))
self.assertEqual(circles_acc[0, 0, 3], 66.)
self.assertEqual(circles_acc[0, 1, 3], 62.)
def test_houghcircles_alt(self):
@@ -127,5 +141,21 @@ class houghcircles_test(NewOpenCVTests):
self.assertGreater(float(matches_counter) / len(testCircles), .5)
self.assertLess(float(len(circles) - matches_counter) / len(circles), .75)
circles_acc = cv.HoughCirclesWithAccumulator(
image=img,
method=cv.HOUGH_GRADIENT_ALT,
dp=1,
minDist=10,
circles=np.array([]),
param1=300,
param2=0.9,
minRadius=13,
maxRadius=15)
self.assertEqual(circles_acc.shape, (1, 3, 4))
self.assertEqual(circles_acc[0, 0, 3], 62.)
self.assertEqual(circles_acc[0, 1, 3], 59.)
self.assertEqual(circles_acc[0, 2, 3], 47.)
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
+2 -1
View File
@@ -194,7 +194,8 @@ public:
}
}
icvWriteFrame_FFMPEG_p(ffmpegWriter, (const uchar*)image.getMat().ptr(), (int)image.step(), image.cols(), image.rows(), image.channels(), 0);
if (!icvWriteFrame_FFMPEG_p(ffmpegWriter, (const uchar*)image.getMat().ptr(), (int)image.step(), image.cols(), image.rows(), image.channels(), 0))
CV_LOG_WARNING(NULL, "FFmpeg: Failed to write frame");
}
virtual bool open( const cv::String& filename, int fourcc, double fps, cv::Size frameSize, const VideoWriterParameters& params )
{
+6 -2
View File
@@ -2525,11 +2525,13 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
// check parameters
if (input_pix_fmt == AV_PIX_FMT_BGR24) {
if (cn != 3) {
CV_LOG_WARNING(NULL, "write frame skipped - expected 3 channels but got " << cn);
return false;
}
}
else if (input_pix_fmt == AV_PIX_FMT_GRAY8 || input_pix_fmt == AV_PIX_FMT_GRAY16LE) {
if (cn != 1) {
CV_LOG_WARNING(NULL, "write frame skipped - expected 1 channel but got " << cn);
return false;
}
}
@@ -2640,14 +2642,16 @@ bool CvVideoWriter_FFMPEG::writeFrame( const unsigned char* data, int step, int
}
hw_frame->pts = frame_idx;
int ret_write = icv_av_write_frame_FFMPEG(oc, video_st, context, outbuf, outbuf_size, hw_frame, frame_idx);
ret = ret_write >= 0 ? true : false;
// AVERROR(EAGAIN): continue sending input, not an error
ret = (ret_write >= 0 || ret_write == AVERROR(EAGAIN));
av_frame_free(&hw_frame);
} else
#endif
{
picture->pts = frame_idx;
int ret_write = icv_av_write_frame_FFMPEG(oc, video_st, context, outbuf, outbuf_size, picture, frame_idx);
ret = ret_write >= 0 ? true : false;
// AVERROR(EAGAIN): continue sending input, not an error
ret = (ret_write >= 0 || ret_write == AVERROR(EAGAIN));
}
frame_idx++;
+1 -1
View File
@@ -2699,7 +2699,7 @@ void CvVideoWriter_GStreamer::write(InputArray image)
}
else if (input_pix_fmt == GST_VIDEO_FORMAT_GRAY16_LE) {
if (image.type() != CV_16UC1) {
CV_WARN("write frame skipped - expected CV_16UC3");
CV_WARN("write frame skipped - expected CV_16UC1");
return;
}
}
+77
View File
@@ -953,4 +953,81 @@ inline static std::string videoio_ffmpeg_16bit_name_printer(const testing::TestP
INSTANTIATE_TEST_CASE_P(/**/, videoio_ffmpeg_16bit, testing::ValuesIn(sixteen_bit_modes), videoio_ffmpeg_16bit_name_printer);
typedef tuple<int /*inputType*/, int /*Depth*/, bool /*isColor*/, bool /*isValid*/, string /*description*/> ChannelMismatchTestParams;
typedef testing::TestWithParam< ChannelMismatchTestParams > videoio_ffmpeg_channel_mismatch;
TEST_P(videoio_ffmpeg_channel_mismatch, basic)
{
if (!videoio_registry::hasBackend(CAP_FFMPEG))
throw SkipTestException("FFmpeg backend was not found");
const string filename = "mismatch_video.mp4";
int input_type = get<0>(GetParam());
int depth = get<1>(GetParam());
bool is_Color = get<2>(GetParam());
bool is_valid = get<3>(GetParam());
const string description = get<4>(GetParam());
const double fps = 15.0;
const int fourcc = VideoWriter::fourcc('m', 'p', '4', 'v');
const Mat frame(480, 640, input_type, Scalar::all(0));
VideoWriter writer(filename, fourcc, fps, frame.size(),
{
cv::VIDEOWRITER_PROP_DEPTH, depth,
VIDEOWRITER_PROP_IS_COLOR, is_Color
});
if (!writer.isOpened())
throw SkipTestException("Failed to open video writer");
for (int i = 1; i <= 15; i++)
{
// In case of mismatch between input frame channels and
// expected depth/isColor configuration a warning should be printed communicating it
writer.write(frame);
}
writer.release();
VideoCapture cap(filename, CAP_FFMPEG);
if (is_valid) {
ASSERT_TRUE(cap.isOpened()) << "Can't open video for " << description;
EXPECT_EQ(cap.get(CAP_PROP_FRAME_COUNT), 15) << "All frames should be written for: " << description;
} else {
ASSERT_FALSE(cap.isOpened()) << "Video capture should fail to open for: " << description;
}
std::remove(filename.c_str());
}
const ChannelMismatchTestParams mismatch_cases[] =
{
// Testing input frame channels and expected depth/isColor combinations
// Open VideoWriter depth/isColor combination: CV_8U/true, everything with 3 channels should be valid
make_tuple(CV_16UC1, CV_8U, true, false, "input_CV_16UC1_expected_CV_8U_isColor_true"),
make_tuple(CV_8UC1, CV_8U, true, false, "input_CV_8UC1_expected_CV_8U_isColor_true"),
make_tuple(CV_8UC3, CV_8U, true, true, "input_CV_8UC3_expected_CV_8U_isColor_true_valid"),
make_tuple(CV_16UC3, CV_8U, true, true, "input_CV_16UC3_expected_CV_8U_isColor_true_valid"),
// Open VideoWriter depth/isColor combination: 16U,8U/false, everything with 1 channel should be valid
make_tuple(CV_8UC3, CV_8U, false, false, "input_CV_8UC3_expected_CV_8U_isColor_false"),
make_tuple(CV_16UC3, CV_8U, false, false, "input_CV_16UC3_expected_CV_8U_isColor_false"),
make_tuple(CV_8UC3, CV_16U, false, false, "input_CV_8UC3_expected_CV_16U_isColor_false"),
make_tuple(CV_16UC3, CV_16U, false, false, "input_CV_16UC3_expected_CV_16U_isColor_false"),
make_tuple(CV_8UC1, CV_16U, false, true, "input_CV_8UC1_expected_CV_16U_isColor_false_valid"),
make_tuple(CV_16UC1, CV_8U, false, true, "input_CV_16UC1_expected_CV_8U_isColor_false_valid"),
};
inline static std::string videoio_ffmpeg_mismatch_name_printer(const testing::TestParamInfo<videoio_ffmpeg_channel_mismatch::ParamType>& info)
{
std::ostringstream os;
os << get<4>(info.param);
return os.str();
}
INSTANTIATE_TEST_CASE_P(/**/, videoio_ffmpeg_channel_mismatch, testing::ValuesIn(mismatch_cases), videoio_ffmpeg_mismatch_name_printer);
}} // namespace
+1 -1
View File
@@ -138,7 +138,7 @@ class ABI:
def __str__(self):
return "%s (%s)" % (self.name, self.toolchain)
def haveIPP(self):
return self.name == "x86_64"
return self.name == "x86" or self.name == "x86_64"
def haveKleidiCV(self):
return self.name == "arm64-v8a"
+1 -1
View File
@@ -2,5 +2,5 @@ ABIs = [
ABI("2", "armeabi-v7a", None, 21, cmake_vars=dict(ANDROID_ABI='armeabi-v7a with NEON')),
ABI("3", "arm64-v8a", None, 21, cmake_vars=dict(ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES='ON')),
ABI("5", "x86_64", None, 21, cmake_vars=dict(ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES='ON')),
ABI("4", "x86", None, 21, cmake_vars=dict(WITH_IPP='OFF')),
ABI("4", "x86", None, 21),
]
+1 -1
View File
@@ -2,5 +2,5 @@ ABIs = [
ABI("2", "armeabi-v7a", None, 21, cmake_vars=dict(ANDROID_ABI='armeabi-v7a with NEON', WITH_FASTCV='ON')),
ABI("3", "arm64-v8a", None, 21, cmake_vars=dict(ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES='ON', WITH_FASTCV='ON')),
ABI("5", "x86_64", None, 21, cmake_vars=dict(ANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES='ON')),
ABI("4", "x86", None, 21, cmake_vars=dict(WITH_IPP='OFF')),
ABI("4", "x86", None, 21),
]
@@ -35,7 +35,7 @@ int main(int argc, char**argv)
//! [Tonemap HDR image]
Mat ldr;
Ptr<Tonemap> tonemap = createTonemap(2.2f);
Ptr<TonemapDrago> tonemap = createTonemapDrago(2.2f);
tonemap->process(hdr, ldr);
//! [Tonemap HDR image]
+3 -3
View File
@@ -58,7 +58,7 @@ def ellipse():
for i in range(NUMBER*2):
center = []
center.append(np.random.randint(x1, x2))
center.append(np.random.randint(x1, x2))
center.append(np.random.randint(y1, y2))
axes = []
axes.append(np.random.randint(0, 200))
axes.append(np.random.randint(0, 200))
@@ -136,7 +136,7 @@ def circles():
for i in range(NUMBER):
center = []
center.append(np.random.randint(x1, x2))
center.append(np.random.randint(x1, x2))
center.append(np.random.randint(y1, y2))
color = "%06x" % np.random.randint(0, 0xFFFFFF)
color = tuple(int(color[i:i+2], 16) for i in (0, 2 ,4))
cv.circle(image, tuple(center), np.random.randint(0, 300), color, np.random.randint(-1, 9), lineType)
@@ -149,7 +149,7 @@ def string():
for i in range(NUMBER):
org = []
org.append(np.random.randint(x1, x2))
org.append(np.random.randint(x1, x2))
org.append(np.random.randint(y1, y2))
color = "%06x" % np.random.randint(0, 0xFFFFFF)
color = tuple(int(color[i:i+2], 16) for i in (0, 2 ,4))
cv.putText(image, "Testing text rendering", tuple(org), np.random.randint(0, 8), np.random.randint(0, 100)*0.05+0.1, color, np.random.randint(1, 10), lineType)
@@ -40,7 +40,7 @@ hdr = merge_debevec.process(images, times, response)
## [Make HDR image]
## [Tonemap HDR image]
tonemap = cv.createTonemap(2.2)
tonemap = cv.createTonemapDrago(2.2)
ldr = tonemap.process(hdr)
## [Tonemap HDR image]