mirror of
https://github.com/opencv/opencv.git
synced 2025-06-13 04:52:53 +08:00
Merge branch 4.x
This commit is contained in:
commit
282c762ead
@ -45,7 +45,7 @@ cv.cvtColor(dst, dst, cv.COLOR_***2RGBA);
|
|||||||
|
|
||||||
Then, new an ImageData obj from dst:
|
Then, new an ImageData obj from dst:
|
||||||
@code{.js}
|
@code{.js}
|
||||||
let imgData = new ImageData(new Uint8ClampedArray(dst.data, dst.cols, dst.rows);
|
let imgData = new ImageData(new Uint8ClampedArray(dst.data), dst.cols, dst.rows);
|
||||||
@endcode
|
@endcode
|
||||||
|
|
||||||
Finally, display it:
|
Finally, display it:
|
||||||
|
@ -123,7 +123,7 @@ Some modules have multiple available backends, following variables allow choosin
|
|||||||
| OPENCV_PARALLEL_PRIORITY_LIST | string, `,`-separated | | list of backends in priority order |
|
| OPENCV_PARALLEL_PRIORITY_LIST | string, `,`-separated | | list of backends in priority order |
|
||||||
| OPENCV_UI_BACKEND | string | | choose highgui backend for window rendering (one of `GTK`, `GTK3`, `GTK2`, `QT`, `WIN32`) |
|
| OPENCV_UI_BACKEND | string | | choose highgui backend for window rendering (one of `GTK`, `GTK3`, `GTK2`, `QT`, `WIN32`) |
|
||||||
| OPENCV_UI_PRIORITY_${NAME} | num | | set highgui backend priority, default is 1000 |
|
| OPENCV_UI_PRIORITY_${NAME} | num | | set highgui backend priority, default is 1000 |
|
||||||
| OPENCV_UI_PRIORITY_LIST | string, `,`-separated | | list of hioghgui backends in priority order |
|
| OPENCV_UI_PRIORITY_LIST | string, `,`-separated | | list of highgui backends in priority order |
|
||||||
| OPENCV_VIDEOIO_PRIORITY_${NAME} | num | | set videoio backend priority, default is 1000 |
|
| OPENCV_VIDEOIO_PRIORITY_${NAME} | num | | set videoio backend priority, default is 1000 |
|
||||||
| OPENCV_VIDEOIO_PRIORITY_LIST | string, `,`-separated | | list of videoio backends in priority order |
|
| OPENCV_VIDEOIO_PRIORITY_LIST | string, `,`-separated | | list of videoio backends in priority order |
|
||||||
|
|
||||||
|
@ -1641,10 +1641,11 @@ void ChessBoardDetector::findQuadNeighbors()
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
float min_dist = FLT_MAX;
|
float min_dist = FLT_MAX;
|
||||||
|
int closest_neighbor_idx = -1;
|
||||||
int closest_corner_idx = -1;
|
int closest_corner_idx = -1;
|
||||||
ChessBoardQuad *closest_quad = 0;
|
ChessBoardQuad *closest_quad = 0;
|
||||||
|
|
||||||
Point2f pt = cur_quad.corners[i]->pt;
|
Point2f pt = all_quads_pts[(idx << 2) + i];
|
||||||
|
|
||||||
// find the closest corner in all other quadrangles
|
// find the closest corner in all other quadrangles
|
||||||
std::vector<float> query = Mat(pt);
|
std::vector<float> query = Mat(pt);
|
||||||
@ -1664,7 +1665,7 @@ void ChessBoardDetector::findQuadNeighbors()
|
|||||||
if (q_k.neighbors[j])
|
if (q_k.neighbors[j])
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const float dist = normL2Sqr<float>(pt - q_k.corners[j]->pt);
|
const float dist = normL2Sqr<float>(pt - all_quads_pts[neighbor_idx]);
|
||||||
if (dist < min_dist &&
|
if (dist < min_dist &&
|
||||||
dist <= cur_quad.edge_len * thresh_scale &&
|
dist <= cur_quad.edge_len * thresh_scale &&
|
||||||
dist <= q_k.edge_len * thresh_scale)
|
dist <= q_k.edge_len * thresh_scale)
|
||||||
@ -1679,6 +1680,7 @@ void ChessBoardDetector::findQuadNeighbors()
|
|||||||
DPRINTF("Incompatible edge lengths");
|
DPRINTF("Incompatible edge lengths");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
closest_neighbor_idx = neighbor_idx;
|
||||||
closest_corner_idx = j;
|
closest_corner_idx = j;
|
||||||
closest_quad = &q_k;
|
closest_quad = &q_k;
|
||||||
min_dist = dist;
|
min_dist = dist;
|
||||||
@ -1686,7 +1688,7 @@ void ChessBoardDetector::findQuadNeighbors()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we found a matching corner point?
|
// we found a matching corner point?
|
||||||
if (closest_corner_idx >= 0 && min_dist < FLT_MAX)
|
if (closest_neighbor_idx >= 0 && closest_corner_idx >= 0 && min_dist < FLT_MAX)
|
||||||
{
|
{
|
||||||
CV_Assert(closest_quad);
|
CV_Assert(closest_quad);
|
||||||
|
|
||||||
@ -1698,6 +1700,7 @@ void ChessBoardDetector::findQuadNeighbors()
|
|||||||
// This is necessary to support small squares where otherwise the wrong
|
// This is necessary to support small squares where otherwise the wrong
|
||||||
// corner will get matched to closest_quad;
|
// corner will get matched to closest_quad;
|
||||||
ChessBoardCorner& closest_corner = *closest_quad->corners[closest_corner_idx];
|
ChessBoardCorner& closest_corner = *closest_quad->corners[closest_corner_idx];
|
||||||
|
cv::Point2f closest_corner_pt = all_quads_pts[closest_neighbor_idx];
|
||||||
|
|
||||||
int j = 0;
|
int j = 0;
|
||||||
for (; j < 4; j++)
|
for (; j < 4; j++)
|
||||||
@ -1705,7 +1708,7 @@ void ChessBoardDetector::findQuadNeighbors()
|
|||||||
if (cur_quad.neighbors[j] == closest_quad)
|
if (cur_quad.neighbors[j] == closest_quad)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (normL2Sqr<float>(closest_corner.pt - cur_quad.corners[j]->pt) < min_dist)
|
if (normL2Sqr<float>(closest_corner_pt - all_quads_pts[(idx << 2) + j]) < min_dist)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (j < 4)
|
if (j < 4)
|
||||||
@ -1720,9 +1723,8 @@ void ChessBoardDetector::findQuadNeighbors()
|
|||||||
if (j < 4)
|
if (j < 4)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// check whether the closest corner to closest_corner
|
// check whether the closest corner to closest_corner is different from pt
|
||||||
// is different from cur_quad->corners[i]->pt
|
query = Mat(closest_corner_pt);
|
||||||
query = Mat(closest_corner.pt);
|
|
||||||
radius = min_dist + 1;
|
radius = min_dist + 1;
|
||||||
neighbors_count = all_quads_pts_index.radiusSearch(query, neighbors_indices, neighbors_dists, radius, search_params);
|
neighbors_count = all_quads_pts_index.radiusSearch(query, neighbors_indices, neighbors_dists, radius, search_params);
|
||||||
|
|
||||||
@ -1740,14 +1742,14 @@ void ChessBoardDetector::findQuadNeighbors()
|
|||||||
CV_DbgAssert(q);
|
CV_DbgAssert(q);
|
||||||
if (!q->neighbors[k])
|
if (!q->neighbors[k])
|
||||||
{
|
{
|
||||||
if (normL2Sqr<float>(closest_corner.pt - q->corners[k]->pt) < min_dist)
|
if (normL2Sqr<float>(closest_corner_pt - all_quads_pts[neighbor_idx]) < min_dist)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (neighbor_idx_idx < neighbors_count)
|
if (neighbor_idx_idx < neighbors_count)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
closest_corner.pt = (pt + closest_corner.pt) * 0.5f;
|
closest_corner.pt = (pt + closest_corner_pt) * 0.5f;
|
||||||
|
|
||||||
// We've found one more corner - remember it
|
// We've found one more corner - remember it
|
||||||
cur_quad.count++;
|
cur_quad.count++;
|
||||||
|
@ -248,7 +248,7 @@ CV_EXPORTS void swap( UMat& a, UMat& b );
|
|||||||
The function computes and returns the coordinate of a donor pixel corresponding to the specified
|
The function computes and returns the coordinate of a donor pixel corresponding to the specified
|
||||||
extrapolated pixel when using the specified extrapolation border mode. For example, if you use
|
extrapolated pixel when using the specified extrapolation border mode. For example, if you use
|
||||||
cv::BORDER_WRAP mode in the horizontal direction, cv::BORDER_REFLECT_101 in the vertical direction and
|
cv::BORDER_WRAP mode in the horizontal direction, cv::BORDER_REFLECT_101 in the vertical direction and
|
||||||
want to compute value of the "virtual" pixel Point(-5, 100) in a floating-point image img , it
|
want to compute value of the "virtual" pixel Point(-5, 100) in a floating-point image img, it
|
||||||
looks like:
|
looks like:
|
||||||
@code{.cpp}
|
@code{.cpp}
|
||||||
float val = img.at<float>(borderInterpolate(100, img.rows, cv::BORDER_REFLECT_101),
|
float val = img.at<float>(borderInterpolate(100, img.rows, cv::BORDER_REFLECT_101),
|
||||||
@ -259,7 +259,7 @@ copyMakeBorder.
|
|||||||
@param p 0-based coordinate of the extrapolated pixel along one of the axes, likely \<0 or \>= len
|
@param p 0-based coordinate of the extrapolated pixel along one of the axes, likely \<0 or \>= len
|
||||||
@param len Length of the array along the corresponding axis.
|
@param len Length of the array along the corresponding axis.
|
||||||
@param borderType Border type, one of the #BorderTypes, except for #BORDER_TRANSPARENT and
|
@param borderType Border type, one of the #BorderTypes, except for #BORDER_TRANSPARENT and
|
||||||
#BORDER_ISOLATED . When borderType==#BORDER_CONSTANT , the function always returns -1, regardless
|
#BORDER_ISOLATED. When borderType==#BORDER_CONSTANT, the function always returns -1, regardless
|
||||||
of p and len.
|
of p and len.
|
||||||
|
|
||||||
@sa copyMakeBorder
|
@sa copyMakeBorder
|
||||||
@ -586,9 +586,19 @@ CV_EXPORTS_AS(sumElems) Scalar sum(InputArray src);
|
|||||||
/** @brief Checks for the presence of at least one non-zero array element.
|
/** @brief Checks for the presence of at least one non-zero array element.
|
||||||
|
|
||||||
The function returns whether there are non-zero elements in src
|
The function returns whether there are non-zero elements in src
|
||||||
@note CV_16F/CV_16BF/CV_Bool/CV_64U/CV_64S/CV_32U are not supported for src.
|
|
||||||
|
The function do not work with multi-channel arrays. If you need to check non-zero array
|
||||||
|
elements across all the channels, use Mat::reshape first to reinterpret the array as
|
||||||
|
single-channel. Or you may extract the particular channel using either extractImageCOI, or
|
||||||
|
mixChannels, or split.
|
||||||
|
|
||||||
|
@note
|
||||||
|
- CV_16F/CV_16BF/CV_Bool/CV_64U/CV_64S/CV_32U are not supported for src.
|
||||||
|
- If the location of non-zero array elements is important, @ref findNonZero is helpful.
|
||||||
|
- If the count of non-zero array elements is important, @ref countNonZero is helpful.
|
||||||
@param src single-channel array.
|
@param src single-channel array.
|
||||||
@sa mean, meanStdDev, norm, minMaxLoc, calcCovarMatrix
|
@sa mean, meanStdDev, norm, minMaxLoc, calcCovarMatrix
|
||||||
|
@sa findNonZero, countNonZero
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W bool hasNonZero( InputArray src );
|
CV_EXPORTS_W bool hasNonZero( InputArray src );
|
||||||
|
|
||||||
@ -596,9 +606,19 @@ CV_EXPORTS_W bool hasNonZero( InputArray src );
|
|||||||
|
|
||||||
The function returns the number of non-zero elements in src :
|
The function returns the number of non-zero elements in src :
|
||||||
\f[\sum _{I: \; \texttt{src} (I) \ne0 } 1\f]
|
\f[\sum _{I: \; \texttt{src} (I) \ne0 } 1\f]
|
||||||
@note CV_16F/CV_16BF/CV_Bool/CV_64U/CV_64S/CV_32U are not supported for src.
|
|
||||||
|
The function do not work with multi-channel arrays. If you need to count non-zero array
|
||||||
|
elements across all the channels, use Mat::reshape first to reinterpret the array as
|
||||||
|
single-channel. Or you may extract the particular channel using either extractImageCOI, or
|
||||||
|
mixChannels, or split.
|
||||||
|
|
||||||
|
@note
|
||||||
|
- CV_16F/CV_16BF/CV_Bool/CV_64U/CV_64S/CV_32U are not supported for src.
|
||||||
|
- If only whether there are non-zero elements is important, @ref hasNonZero is helpful.
|
||||||
|
- If the location of non-zero array elements is important, @ref findNonZero is helpful.
|
||||||
@param src single-channel array.
|
@param src single-channel array.
|
||||||
@sa mean, meanStdDev, norm, minMaxLoc, calcCovarMatrix
|
@sa mean, meanStdDev, norm, minMaxLoc, calcCovarMatrix
|
||||||
|
@sa findNonZero, hasNonZero
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W int countNonZero( InputArray src );
|
CV_EXPORTS_W int countNonZero( InputArray src );
|
||||||
|
|
||||||
@ -625,9 +645,19 @@ or
|
|||||||
// access pixel coordinates
|
// access pixel coordinates
|
||||||
Point pnt = locations[i];
|
Point pnt = locations[i];
|
||||||
@endcode
|
@endcode
|
||||||
@note CV_16F/CV_16BF/CV_Bool/CV_64U/CV_64S/CV_32U are not supported for src.
|
|
||||||
|
The function do not work with multi-channel arrays. If you need to find non-zero
|
||||||
|
elements across all the channels, use Mat::reshape first to reinterpret the array as
|
||||||
|
single-channel. Or you may extract the particular channel using either extractImageCOI, or
|
||||||
|
mixChannels, or split.
|
||||||
|
|
||||||
|
@note
|
||||||
|
- CV_16F/CV_16BF/CV_Bool/CV_64U/CV_64S/CV_32U are not supported for src.
|
||||||
|
- If only count of non-zero array elements is important, @ref countNonZero is helpful.
|
||||||
|
- If only whether there are non-zero elements is important, @ref hasNonZero is helpful.
|
||||||
@param src single-channel array
|
@param src single-channel array
|
||||||
@param idx the output array, type of cv::Mat or std::vector<Point>, corresponding to non-zero indices in the input
|
@param idx the output array, type of cv::Mat or std::vector<Point>, corresponding to non-zero indices in the input
|
||||||
|
@sa countNonZero, hasNonZero
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W void findNonZero( InputArray src, OutputArray idx );
|
CV_EXPORTS_W void findNonZero( InputArray src, OutputArray idx );
|
||||||
|
|
||||||
@ -834,8 +864,8 @@ array region.
|
|||||||
|
|
||||||
The function do not work with multi-channel arrays. If you need to find minimum or maximum
|
The function do not work with multi-channel arrays. If you need to find minimum or maximum
|
||||||
elements across all the channels, use Mat::reshape first to reinterpret the array as
|
elements across all the channels, use Mat::reshape first to reinterpret the array as
|
||||||
single-channel. Or you may extract the particular channel using either extractImageCOI , or
|
single-channel. Or you may extract the particular channel using either extractImageCOI, or
|
||||||
mixChannels , or split .
|
mixChannels, or split.
|
||||||
@note CV_16F/CV_16BF/CV_Bool/CV_64U/CV_64S/CV_32U are not supported for src.
|
@note CV_16F/CV_16BF/CV_Bool/CV_64U/CV_64S/CV_32U are not supported for src.
|
||||||
@param src input single-channel array.
|
@param src input single-channel array.
|
||||||
@param minVal pointer to the returned minimum value; NULL is used if not required.
|
@param minVal pointer to the returned minimum value; NULL is used if not required.
|
||||||
@ -889,8 +919,8 @@ The function cv::minMaxIdx finds the minimum and maximum element values and thei
|
|||||||
extremums are searched across the whole array or, if mask is not an empty array, in the specified
|
extremums are searched across the whole array or, if mask is not an empty array, in the specified
|
||||||
array region. The function does not work with multi-channel arrays. If you need to find minimum or
|
array region. The function does not work with multi-channel arrays. If you need to find minimum or
|
||||||
maximum elements across all the channels, use Mat::reshape first to reinterpret the array as
|
maximum elements across all the channels, use Mat::reshape first to reinterpret the array as
|
||||||
single-channel. Or you may extract the particular channel using either extractImageCOI , or
|
single-channel. Or you may extract the particular channel using either extractImageCOI, or
|
||||||
mixChannels , or split . In case of a sparse matrix, the minimum is found among non-zero elements
|
mixChannels, or split. In case of a sparse matrix, the minimum is found among non-zero elements
|
||||||
only.
|
only.
|
||||||
@note When minIdx is not NULL, it must have at least 2 elements (as well as maxIdx), even if src is
|
@note When minIdx is not NULL, it must have at least 2 elements (as well as maxIdx), even if src is
|
||||||
a single-row or single-column matrix. In OpenCV (following MATLAB) each array has at least 2
|
a single-row or single-column matrix. In OpenCV (following MATLAB) each array has at least 2
|
||||||
@ -927,8 +957,8 @@ CV_EXPORTS void minMaxLoc(const SparseMat& a, double* minVal,
|
|||||||
The function #reduce reduces the matrix to a vector by treating the matrix rows/columns as a set of
|
The function #reduce reduces the matrix to a vector by treating the matrix rows/columns as a set of
|
||||||
1D vectors and performing the specified operation on the vectors until a single row/column is
|
1D vectors and performing the specified operation on the vectors until a single row/column is
|
||||||
obtained. For example, the function can be used to compute horizontal and vertical projections of a
|
obtained. For example, the function can be used to compute horizontal and vertical projections of a
|
||||||
raster image. In case of #REDUCE_MAX and #REDUCE_MIN , the output image should have the same type as the source one.
|
raster image. In case of #REDUCE_MAX and #REDUCE_MIN, the output image should have the same type as the source one.
|
||||||
In case of #REDUCE_SUM, #REDUCE_SUM2 and #REDUCE_AVG , the output may have a larger element bit-depth to preserve accuracy.
|
In case of #REDUCE_SUM, #REDUCE_SUM2 and #REDUCE_AVG, the output may have a larger element bit-depth to preserve accuracy.
|
||||||
And multi-channel arrays are also supported in these two reduction modes.
|
And multi-channel arrays are also supported in these two reduction modes.
|
||||||
|
|
||||||
The following code demonstrates its usage for a single channel matrix.
|
The following code demonstrates its usage for a single channel matrix.
|
||||||
@ -982,7 +1012,7 @@ CV_EXPORTS_W void merge(InputArrayOfArrays mv, OutputArray dst);
|
|||||||
The function cv::split splits a multi-channel array into separate single-channel arrays:
|
The function cv::split splits a multi-channel array into separate single-channel arrays:
|
||||||
\f[\texttt{mv} [c](I) = \texttt{src} (I)_c\f]
|
\f[\texttt{mv} [c](I) = \texttt{src} (I)_c\f]
|
||||||
If you need to extract a single channel or do some other sophisticated channel permutation, use
|
If you need to extract a single channel or do some other sophisticated channel permutation, use
|
||||||
mixChannels .
|
mixChannels.
|
||||||
|
|
||||||
The following example demonstrates how to split a 3-channel matrix into 3 single channel matrices.
|
The following example demonstrates how to split a 3-channel matrix into 3 single channel matrices.
|
||||||
@snippet snippets/core_split.cpp example
|
@snippet snippets/core_split.cpp example
|
||||||
@ -1123,7 +1153,7 @@ The example scenarios of using the function are the following:
|
|||||||
flipping around the x-axis and positive value (for example, 1) means
|
flipping around the x-axis and positive value (for example, 1) means
|
||||||
flipping around y-axis. Negative value (for example, -1) means flipping
|
flipping around y-axis. Negative value (for example, -1) means flipping
|
||||||
around both axes.
|
around both axes.
|
||||||
@sa transpose , repeat , completeSymm
|
@sa transpose, repeat, completeSymm
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W void flip(InputArray src, OutputArray dst, int flipCode);
|
CV_EXPORTS_W void flip(InputArray src, OutputArray dst, int flipCode);
|
||||||
|
|
||||||
@ -1155,7 +1185,7 @@ The function cv::rotate rotates the array in one of three different ways:
|
|||||||
@param dst output array of the same type as src. The size is the same with ROTATE_180,
|
@param dst output array of the same type as src. The size is the same with ROTATE_180,
|
||||||
and the rows and cols are switched for ROTATE_90_CLOCKWISE and ROTATE_90_COUNTERCLOCKWISE.
|
and the rows and cols are switched for ROTATE_90_CLOCKWISE and ROTATE_90_COUNTERCLOCKWISE.
|
||||||
@param rotateCode an enum to specify how to rotate the array; see the enum #RotateFlags
|
@param rotateCode an enum to specify how to rotate the array; see the enum #RotateFlags
|
||||||
@sa transpose , repeat , completeSymm, flip, RotateFlags
|
@sa transpose, repeat, completeSymm, flip, RotateFlags
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W void rotate(InputArray src, OutputArray dst, int rotateCode);
|
CV_EXPORTS_W void rotate(InputArray src, OutputArray dst, int rotateCode);
|
||||||
|
|
||||||
@ -1589,7 +1619,7 @@ converts denormalized values to zeros on output. Special values (NaN,
|
|||||||
Inf) are not handled.
|
Inf) are not handled.
|
||||||
@param src input array.
|
@param src input array.
|
||||||
@param dst output array of the same size and type as src.
|
@param dst output array of the same size and type as src.
|
||||||
@sa log , cartToPolar , polarToCart , phase , pow , sqrt , magnitude
|
@sa log, cartToPolar, polarToCart, phase, pow, sqrt, magnitude
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W void exp(InputArray src, OutputArray dst);
|
CV_EXPORTS_W void exp(InputArray src, OutputArray dst);
|
||||||
|
|
||||||
@ -1742,7 +1772,7 @@ should have the same type as src1 and src2.
|
|||||||
@param dst output matrix; it has the proper size and the same type as
|
@param dst output matrix; it has the proper size and the same type as
|
||||||
input matrices.
|
input matrices.
|
||||||
@param flags operation flags (cv::GemmFlags)
|
@param flags operation flags (cv::GemmFlags)
|
||||||
@sa mulTransposed , transform
|
@sa mulTransposed, transform
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W void gemm(InputArray src1, InputArray src2, double alpha,
|
CV_EXPORTS_W void gemm(InputArray src1, InputArray src2, double alpha,
|
||||||
InputArray src3, double beta, OutputArray dst, int flags = 0);
|
InputArray src3, double beta, OutputArray dst, int flags = 0);
|
||||||
@ -1752,7 +1782,7 @@ CV_EXPORTS_W void gemm(InputArray src1, InputArray src2, double alpha,
|
|||||||
The function cv::mulTransposed calculates the product of src and its
|
The function cv::mulTransposed calculates the product of src and its
|
||||||
transposition:
|
transposition:
|
||||||
\f[\texttt{dst} = \texttt{scale} ( \texttt{src} - \texttt{delta} )^T ( \texttt{src} - \texttt{delta} )\f]
|
\f[\texttt{dst} = \texttt{scale} ( \texttt{src} - \texttt{delta} )^T ( \texttt{src} - \texttt{delta} )\f]
|
||||||
if aTa=true , and
|
if aTa=true, and
|
||||||
\f[\texttt{dst} = \texttt{scale} ( \texttt{src} - \texttt{delta} ) ( \texttt{src} - \texttt{delta} )^T\f]
|
\f[\texttt{dst} = \texttt{scale} ( \texttt{src} - \texttt{delta} ) ( \texttt{src} - \texttt{delta} )^T\f]
|
||||||
otherwise. The function is used to calculate the covariance matrix. With
|
otherwise. The function is used to calculate the covariance matrix. With
|
||||||
zero delta, it can be used as a faster substitute for general matrix
|
zero delta, it can be used as a faster substitute for general matrix
|
||||||
@ -1765,7 +1795,7 @@ description below.
|
|||||||
@param delta Optional delta matrix subtracted from src before the
|
@param delta Optional delta matrix subtracted from src before the
|
||||||
multiplication. When the matrix is empty ( delta=noArray() ), it is
|
multiplication. When the matrix is empty ( delta=noArray() ), it is
|
||||||
assumed to be zero, that is, nothing is subtracted. If it has the same
|
assumed to be zero, that is, nothing is subtracted. If it has the same
|
||||||
size as src , it is simply subtracted. Otherwise, it is "repeated" (see
|
size as src, it is simply subtracted. Otherwise, it is "repeated" (see
|
||||||
repeat ) to cover the full src and then subtracted. Type of the delta
|
repeat ) to cover the full src and then subtracted. Type of the delta
|
||||||
matrix, when it is not empty, must be the same as the type of created
|
matrix, when it is not empty, must be the same as the type of created
|
||||||
output matrix. See the dtype parameter description below.
|
output matrix. See the dtype parameter description below.
|
||||||
@ -2039,7 +2069,7 @@ in the descending order.
|
|||||||
@param eigenvectors output matrix of eigenvectors; it has the same size and type as src; the
|
@param eigenvectors output matrix of eigenvectors; it has the same size and type as src; the
|
||||||
eigenvectors are stored as subsequent matrix rows, in the same order as the corresponding
|
eigenvectors are stored as subsequent matrix rows, in the same order as the corresponding
|
||||||
eigenvalues.
|
eigenvalues.
|
||||||
@sa eigenNonSymmetric, completeSymm , PCA
|
@sa eigenNonSymmetric, completeSymm, PCA
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W bool eigen(InputArray src, OutputArray eigenvalues,
|
CV_EXPORTS_W bool eigen(InputArray src, OutputArray eigenvalues,
|
||||||
OutputArray eigenvectors = noArray());
|
OutputArray eigenvectors = noArray());
|
||||||
@ -2179,7 +2209,7 @@ So, the function chooses an operation mode depending on the flags and size of th
|
|||||||
|
|
||||||
If #DFT_SCALE is set, the scaling is done after the transformation.
|
If #DFT_SCALE is set, the scaling is done after the transformation.
|
||||||
|
|
||||||
Unlike dct , the function supports arrays of arbitrary size. But only those arrays are processed
|
Unlike dct, the function supports arrays of arbitrary size. But only those arrays are processed
|
||||||
efficiently, whose sizes can be factorized in a product of small prime numbers (2, 3, and 5 in the
|
efficiently, whose sizes can be factorized in a product of small prime numbers (2, 3, and 5 in the
|
||||||
current implementation). Such an efficient DFT size can be calculated using the getOptimalDFTSize
|
current implementation). Such an efficient DFT size can be calculated using the getOptimalDFTSize
|
||||||
method.
|
method.
|
||||||
@ -2262,8 +2292,8 @@ nonzeroRows rows of the input array (#DFT_INVERSE is not set) or only the first
|
|||||||
output array (#DFT_INVERSE is set) contain non-zeros, thus, the function can handle the rest of the
|
output array (#DFT_INVERSE is set) contain non-zeros, thus, the function can handle the rest of the
|
||||||
rows more efficiently and save some time; this technique is very useful for calculating array
|
rows more efficiently and save some time; this technique is very useful for calculating array
|
||||||
cross-correlation or convolution using DFT.
|
cross-correlation or convolution using DFT.
|
||||||
@sa dct , getOptimalDFTSize , mulSpectrums, filter2D , matchTemplate , flip , cartToPolar ,
|
@sa dct, getOptimalDFTSize, mulSpectrums, filter2D, matchTemplate, flip, cartToPolar,
|
||||||
magnitude , phase
|
magnitude, phase
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W void dft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0);
|
CV_EXPORTS_W void dft(InputArray src, OutputArray dst, int flags = 0, int nonzeroRows = 0);
|
||||||
|
|
||||||
@ -2300,9 +2330,9 @@ floating-point array:
|
|||||||
\f[X = \left (C^{(N)} \right )^T \cdot X \cdot C^{(N)}\f]
|
\f[X = \left (C^{(N)} \right )^T \cdot X \cdot C^{(N)}\f]
|
||||||
|
|
||||||
The function chooses the mode of operation by looking at the flags and size of the input array:
|
The function chooses the mode of operation by looking at the flags and size of the input array:
|
||||||
- If (flags & #DCT_INVERSE) == 0 , the function does a forward 1D or 2D transform. Otherwise, it
|
- If (flags & #DCT_INVERSE) == 0, the function does a forward 1D or 2D transform. Otherwise, it
|
||||||
is an inverse 1D or 2D transform.
|
is an inverse 1D or 2D transform.
|
||||||
- If (flags & #DCT_ROWS) != 0 , the function performs a 1D transform of each row.
|
- If (flags & #DCT_ROWS) != 0, the function performs a 1D transform of each row.
|
||||||
- If the array is a single column or a single row, the function performs a 1D transform.
|
- If the array is a single column or a single row, the function performs a 1D transform.
|
||||||
- If none of the above is true, the function performs a 2D transform.
|
- If none of the above is true, the function performs a 2D transform.
|
||||||
|
|
||||||
@ -2318,7 +2348,7 @@ of a vector of size N/2 . Thus, the optimal DCT size N1 \>= N can be calculated
|
|||||||
@param src input floating-point array.
|
@param src input floating-point array.
|
||||||
@param dst output array of the same size and type as src .
|
@param dst output array of the same size and type as src .
|
||||||
@param flags transformation flags as a combination of cv::DftFlags (DCT_*)
|
@param flags transformation flags as a combination of cv::DftFlags (DCT_*)
|
||||||
@sa dft , getOptimalDFTSize , idct
|
@sa dft, getOptimalDFTSize, idct
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W void dct(InputArray src, OutputArray dst, int flags = 0);
|
CV_EXPORTS_W void dct(InputArray src, OutputArray dst, int flags = 0);
|
||||||
|
|
||||||
@ -2337,7 +2367,7 @@ CV_EXPORTS_W void idct(InputArray src, OutputArray dst, int flags = 0);
|
|||||||
The function cv::mulSpectrums performs the per-element multiplication of the two CCS-packed or complex
|
The function cv::mulSpectrums performs the per-element multiplication of the two CCS-packed or complex
|
||||||
matrices that are results of a real or complex Fourier transform.
|
matrices that are results of a real or complex Fourier transform.
|
||||||
|
|
||||||
The function, together with dft and idft , may be used to calculate convolution (pass conjB=false )
|
The function, together with dft and idft, may be used to calculate convolution (pass conjB=false )
|
||||||
or correlation (pass conjB=true ) of two arrays rapidly. When the arrays are complex, they are
|
or correlation (pass conjB=true ) of two arrays rapidly. When the arrays are complex, they are
|
||||||
simply multiplied (per element) with an optional conjugation of the second-array elements. When the
|
simply multiplied (per element) with an optional conjugation of the second-array elements. When the
|
||||||
arrays are real, they are assumed to be CCS-packed (see dft for details).
|
arrays are real, they are assumed to be CCS-packed (see dft for details).
|
||||||
@ -2371,7 +2401,7 @@ While the function cannot be used directly to estimate the optimal vector size f
|
|||||||
(since the current DCT implementation supports only even-size vectors), it can be easily processed
|
(since the current DCT implementation supports only even-size vectors), it can be easily processed
|
||||||
as getOptimalDFTSize((vecsize+1)/2)\*2.
|
as getOptimalDFTSize((vecsize+1)/2)\*2.
|
||||||
@param vecsize vector size.
|
@param vecsize vector size.
|
||||||
@sa dft , dct , idft , idct , mulSpectrums
|
@sa dft, dct, idft, idct, mulSpectrums
|
||||||
*/
|
*/
|
||||||
CV_EXPORTS_W int getOptimalDFTSize(int vecsize);
|
CV_EXPORTS_W int getOptimalDFTSize(int vecsize);
|
||||||
|
|
||||||
@ -2923,7 +2953,7 @@ public:
|
|||||||
|
|
||||||
The methods transform the state using the MWC algorithm and return the
|
The methods transform the state using the MWC algorithm and return the
|
||||||
next random number. The first form is equivalent to RNG::next . The
|
next random number. The first form is equivalent to RNG::next . The
|
||||||
second form returns the random number modulo N , which means that the
|
second form returns the random number modulo N, which means that the
|
||||||
result is in the range [0, N) .
|
result is in the range [0, N) .
|
||||||
*/
|
*/
|
||||||
unsigned operator ()();
|
unsigned operator ()();
|
||||||
|
@ -932,7 +932,6 @@ inline int hal_ni_flip(int src_type, const uchar* src_data, size_t src_step, int
|
|||||||
#define cv_hal_flip hal_ni_flip
|
#define cv_hal_flip hal_ni_flip
|
||||||
//! @endcond
|
//! @endcond
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief rotate90
|
@brief rotate90
|
||||||
@param src_type source and destination image type
|
@param src_type source and destination image type
|
||||||
@ -955,6 +954,20 @@ inline int hal_ni_rotate90(int src_type, const uchar* src_data, size_t src_step,
|
|||||||
#define cv_hal_rotate90 hal_ni_rotate90
|
#define cv_hal_rotate90 hal_ni_rotate90
|
||||||
//! @endcond
|
//! @endcond
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Transpose2d
|
||||||
|
@param src_data,src_step Source image
|
||||||
|
@param dst_data,dst_step Destination image
|
||||||
|
@param src_width,src_height Source image dimensions
|
||||||
|
@param element_size Size of an element in bytes
|
||||||
|
*/
|
||||||
|
inline int hal_ni_transpose2d(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int src_width,
|
||||||
|
int src_height, int element_size) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
|
||||||
|
|
||||||
|
//! @cond IGNORED
|
||||||
|
#define cv_hal_transpose2d hal_ni_transpose2d
|
||||||
|
//! @endcond
|
||||||
|
|
||||||
//! @}
|
//! @}
|
||||||
|
|
||||||
|
|
||||||
|
@ -269,6 +269,8 @@ void transpose( InputArray _src, OutputArray _dst )
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CALL_HAL(transpose2d, cv_hal_transpose2d, src.data, src.step, dst.data, dst.step, src.cols, src.rows, esz);
|
||||||
|
|
||||||
CV_IPP_RUN_FAST(ipp_transpose(src, dst))
|
CV_IPP_RUN_FAST(ipp_transpose(src, dst))
|
||||||
|
|
||||||
if( dst.data == src.data )
|
if( dst.data == src.data )
|
||||||
|
@ -209,34 +209,6 @@ void fastDepthwiseConv( const float* wptr,
|
|||||||
|
|
||||||
#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_RVV
|
#if !defined(CV_CPU_OPTIMIZATION_DECLARATIONS_ONLY) && CV_RVV
|
||||||
|
|
||||||
/*
|
|
||||||
Example for load_deinterleave:
|
|
||||||
input: ptr[16] = {1,2,3, ... ,14,15,16}
|
|
||||||
output: a = {1, 3, 5, 7, 9, 11, 13, 15}
|
|
||||||
output: b = {2, 4, 6, 8,10, 12, 14, 16}
|
|
||||||
*/
|
|
||||||
static inline void vfloat32m2_load_deinterleave(const float* ptr, vfloat32m2_t& a, vfloat32m2_t& b, int vl)
|
|
||||||
{
|
|
||||||
vuint64m4_t mask = vmv_v_x_u64m4(1,vl*2);
|
|
||||||
vuint32m4_t mask_re = vreinterpret_v_u64m4_u32m4(mask);
|
|
||||||
vbool8_t mask0 = vmseq_vx_u32m4_b8 (mask_re, 1, vl*2);
|
|
||||||
vbool8_t mask1 = vmseq_vx_u32m4_b8 (mask_re, 0, vl*2);
|
|
||||||
vfloat32m4_t tempa = vundefined_f32m4(), tempb = vundefined_f32m4();
|
|
||||||
vfloat32m4_t vw = vle32_v_f32m4(ptr, vl*2);
|
|
||||||
tempa = vcompress_vm_f32m4(mask0, tempa, vw, vl*2);
|
|
||||||
tempb = vcompress_vm_f32m4(mask1, tempb, vw, vl*2);
|
|
||||||
/* The following instructions have not to be supported by the GNU toolchain.
|
|
||||||
So we temporarily use store and load instead.
|
|
||||||
// a = vlmul_trunc_v_f32m4_f32m2(tempa);
|
|
||||||
// b = vlmul_trunc_v_f32m4_f32m2(tempb);
|
|
||||||
*/
|
|
||||||
cv::AutoBuffer<float> cvBuffer(sizeof(float)*vl*2);
|
|
||||||
float* buffer = (float*)cvBuffer.data();
|
|
||||||
vse32_v_f32m4(buffer, tempa, vl);
|
|
||||||
a = vle32_v_f32m2(buffer, vl);
|
|
||||||
vse32_v_f32m4(buffer, tempb, vl);
|
|
||||||
b = vle32_v_f32m2(buffer, vl);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fastDepthwiseConv( const float* wptr,
|
void fastDepthwiseConv( const float* wptr,
|
||||||
int kernel_h, int kernel_w,
|
int kernel_h, int kernel_w,
|
||||||
@ -292,64 +264,40 @@ void fastDepthwiseConv( const float* wptr,
|
|||||||
if( stride_w == 1 )
|
if( stride_w == 1 )
|
||||||
for( ; out_j < outW1; out_j += vl, avl -= vl)
|
for( ; out_j < outW1; out_j += vl, avl -= vl)
|
||||||
{
|
{
|
||||||
vl = vsetvl_e32m2(avl);
|
vl = vsetvl_e32m8(avl);
|
||||||
int in_j = out_j * stride_w - pad_l;
|
int in_j = out_j * stride_w - pad_l;
|
||||||
vfloat32m2_t v00 = vle32_v_f32m2(imgptr0 + in_j, vl),
|
vfloat32m8_t vout0 = vfmacc_vf_f32m8(vfmv_v_f_f32m8(bias, vl), w00, vle32_v_f32m8(imgptr0 + in_j, vl), vl);
|
||||||
v01 = vle32_v_f32m2(imgptr0 + in_j + dilation_w, vl),
|
vout0 = vfmacc_vf_f32m8(vout0, w01, vle32_v_f32m8(imgptr0 + in_j + dilation_w, vl), vl);
|
||||||
v02 = vle32_v_f32m2(imgptr0 + in_j + dilation_w*2, vl),
|
vout0 = vfmacc_vf_f32m8(vout0, w02, vle32_v_f32m8(imgptr0 + in_j + dilation_w*2, vl), vl);
|
||||||
v10 = vle32_v_f32m2(imgptr1 + in_j, vl),
|
vout0 = vfmacc_vf_f32m8(vout0, w10, vle32_v_f32m8(imgptr1 + in_j, vl),vl);
|
||||||
v11 = vle32_v_f32m2(imgptr1 + in_j + dilation_w, vl),
|
vout0 = vfmacc_vf_f32m8(vout0, w11, vle32_v_f32m8(imgptr1 + in_j + dilation_w, vl),vl);
|
||||||
v12 = vle32_v_f32m2(imgptr1 + in_j + dilation_w*2, vl),
|
vout0 = vfmacc_vf_f32m8(vout0, w12, vle32_v_f32m8(imgptr1 + in_j + dilation_w*2, vl),vl);
|
||||||
v20 = vle32_v_f32m2(imgptr2 + in_j, vl),
|
vout0 = vfmacc_vf_f32m8(vout0, w20, vle32_v_f32m8(imgptr2 + in_j, vl), vl);
|
||||||
v21 = vle32_v_f32m2(imgptr2 + in_j + dilation_w, vl),
|
vout0 = vfmacc_vf_f32m8(vout0, w21, vle32_v_f32m8(imgptr2 + in_j + dilation_w, vl), vl);
|
||||||
v22 = vle32_v_f32m2(imgptr2 + in_j + dilation_w*2, vl);
|
vout0 = vfmacc_vf_f32m8(vout0, w22, vle32_v_f32m8(imgptr2 + in_j + dilation_w*2, vl), vl);
|
||||||
|
|
||||||
vfloat32m2_t vout0 = vfmul_vf_f32m2(v00, w00, vl);
|
|
||||||
vfloat32m2_t vout1 = vfmul_vf_f32m2(v01, w01, vl);
|
|
||||||
vfloat32m2_t vout2 = vfmul_vf_f32m2(v02, w02, vl);
|
|
||||||
vout0 = vfadd_vf_f32m2(vout0, bias, vl);
|
|
||||||
|
|
||||||
vout0 = vfmacc_vf_f32m2(vout0, w10, v10, vl);
|
|
||||||
vout1 = vfmacc_vf_f32m2(vout1, w11, v11, vl);
|
|
||||||
vout2 = vfmacc_vf_f32m2(vout2, w12, v12, vl);
|
|
||||||
|
|
||||||
vout0 = vfmacc_vf_f32m2(vout0, w20, v20, vl);
|
|
||||||
vout1 = vfmacc_vf_f32m2(vout1, w21, v21, vl);
|
|
||||||
vout2 = vfmacc_vf_f32m2(vout2, w22, v22, vl);
|
|
||||||
|
|
||||||
vout0 = vfadd_vv_f32m2(vfadd_vv_f32m2(vout0, vout1, vl), vout2, vl);
|
|
||||||
if (relu)
|
if (relu)
|
||||||
{
|
{
|
||||||
vbool16_t m = vmfgt_vf_f32m2_b16(vout0, 0, vl);
|
vbool4_t m = vmfgt_vf_f32m8_b4(vout0, 0, vl);
|
||||||
vout0 = vmerge_vvm_f32m2(m, vfmul_vf_f32m2(vout0, relu_coeff, vl), vout0, vl);
|
vout0 = vmerge_vvm_f32m8(m, vfmul_vf_f32m8(vout0, relu_coeff, vl), vout0, vl);
|
||||||
}
|
}
|
||||||
vse32_v_f32m2(outptr + out_j, vout0, vl);
|
vse32_v_f32m8(outptr + out_j, vout0, vl);
|
||||||
}
|
}
|
||||||
else //stride_w == 2 && dilation_w == 1
|
else //stride_w == 2 && dilation_w == 1
|
||||||
for( ; out_j < outW1; out_j += vl, avl -= vl)
|
for( ; out_j < outW1; out_j += vl, avl -= vl)
|
||||||
{
|
{
|
||||||
vl = vsetvl_e32m2(avl);
|
vl = vsetvl_e32m2(avl);
|
||||||
int in_j = out_j * stride_w - pad_l;
|
int in_j = out_j * stride_w - pad_l;
|
||||||
vfloat32m2_t v00, v01, v02, v10, v11, v12, v20, v21, v22, unused;
|
vfloat32m2_t vout0 = vfmacc_vf_f32m2(vfmv_v_f_f32m2(bias, vl), w00, vlse32_v_f32m2(imgptr0+in_j , 8, vl), vl);
|
||||||
vfloat32m2_load_deinterleave(imgptr0 + in_j, v00, v01, vl);
|
vfloat32m2_t vout1 = vfmul_vf_f32m2(vlse32_v_f32m2(imgptr0+in_j+1, 8, vl), w01, vl);
|
||||||
vfloat32m2_load_deinterleave(imgptr0 + in_j + 2, v02, unused, vl);
|
vfloat32m2_t vout2 = vfmul_vf_f32m2(vlse32_v_f32m2(imgptr0+in_j+2, 8, vl), w02, vl);
|
||||||
vfloat32m2_load_deinterleave(imgptr1 + in_j, v10, v11, vl);
|
|
||||||
vfloat32m2_load_deinterleave(imgptr1 + in_j + 2, v12, unused, vl);
|
|
||||||
vfloat32m2_load_deinterleave(imgptr2 + in_j, v20, v21, vl);
|
|
||||||
vfloat32m2_load_deinterleave(imgptr2 + in_j + 2, v22, unused, vl);
|
|
||||||
|
|
||||||
vfloat32m2_t vout0 = vfmul_vf_f32m2(v00, w00, vl);
|
vout0 = vfmacc_vf_f32m2(vout0, w10, vlse32_v_f32m2(imgptr1+in_j , 8, vl), vl);
|
||||||
vfloat32m2_t vout1 = vfmul_vf_f32m2(v01, w01, vl);
|
vout1 = vfmacc_vf_f32m2(vout1, w11, vlse32_v_f32m2(imgptr1+in_j+1, 8, vl), vl);
|
||||||
vfloat32m2_t vout2 = vfmul_vf_f32m2(v02, w02, vl);
|
vout2 = vfmacc_vf_f32m2(vout2, w12, vlse32_v_f32m2(imgptr1+in_j+2, 8, vl), vl);
|
||||||
vout0 = vfadd_vf_f32m2(vout0, bias, vl);
|
|
||||||
|
|
||||||
vout0 = vfmacc_vf_f32m2(vout0, w10, v10, vl);
|
vout0 = vfmacc_vf_f32m2(vout0, w20, vlse32_v_f32m2(imgptr2+in_j , 8, vl), vl);
|
||||||
vout1 = vfmacc_vf_f32m2(vout1, w11, v11, vl);
|
vout1 = vfmacc_vf_f32m2(vout1, w21, vlse32_v_f32m2(imgptr2+in_j+1, 8, vl), vl);
|
||||||
vout2 = vfmacc_vf_f32m2(vout2, w12, v12, vl);
|
vout2 = vfmacc_vf_f32m2(vout2, w22, vlse32_v_f32m2(imgptr2+in_j+2, 8, vl), vl);
|
||||||
|
|
||||||
vout0 = vfmacc_vf_f32m2(vout0, w20, v20, vl);
|
|
||||||
vout1 = vfmacc_vf_f32m2(vout1, w21, v21, vl);
|
|
||||||
vout2 = vfmacc_vf_f32m2(vout2, w22, v22, vl);
|
|
||||||
|
|
||||||
vout0 = vfadd_vv_f32m2(vfadd_vv_f32m2(vout0, vout1, vl), vout2, vl);
|
vout0 = vfadd_vv_f32m2(vfadd_vv_f32m2(vout0, vout1, vl), vout2, vl);
|
||||||
if (relu)
|
if (relu)
|
||||||
|
@ -92,7 +92,8 @@ class ONNXImporter
|
|||||||
|
|
||||||
void addConstant(const std::string& name, const Mat& blob);
|
void addConstant(const std::string& name, const Mat& blob);
|
||||||
void addLayer(LayerParams& layerParams,
|
void addLayer(LayerParams& layerParams,
|
||||||
const opencv_onnx::NodeProto& node_proto);
|
const opencv_onnx::NodeProto& node_proto,
|
||||||
|
int num_inputs = std::numeric_limits<int>::max());
|
||||||
void setParamsDtype(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
|
void setParamsDtype(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
|
||||||
|
|
||||||
void lstm_extractConsts(LayerParams& layerParams, const opencv_onnx::NodeProto& lstm_proto, size_t idx, int* blobShape_, int size);
|
void lstm_extractConsts(LayerParams& layerParams, const opencv_onnx::NodeProto& lstm_proto, size_t idx, int* blobShape_, int size);
|
||||||
@ -629,7 +630,8 @@ ONNXImporter::TensorInfo ONNXImporter::getBlobExtraInfo(const std::string& input
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ONNXImporter::addLayer(LayerParams& layerParams,
|
void ONNXImporter::addLayer(LayerParams& layerParams,
|
||||||
const opencv_onnx::NodeProto& node_proto)
|
const opencv_onnx::NodeProto& node_proto,
|
||||||
|
int num_inputs)
|
||||||
{
|
{
|
||||||
int depth = layerParams.get<int>("depth", CV_32F);
|
int depth = layerParams.get<int>("depth", CV_32F);
|
||||||
int id = dstNet.addLayer(layerParams.name, layerParams.type, depth, layerParams);
|
int id = dstNet.addLayer(layerParams.name, layerParams.type, depth, layerParams);
|
||||||
@ -644,7 +646,8 @@ void ONNXImporter::addLayer(LayerParams& layerParams,
|
|||||||
|
|
||||||
std::vector<MatShape> layerInpShapes, layerOutShapes, layerInternalShapes;
|
std::vector<MatShape> layerInpShapes, layerOutShapes, layerInternalShapes;
|
||||||
int inpNum = 0;
|
int inpNum = 0;
|
||||||
for (int j = 0; j < node_proto.input_size(); j++)
|
num_inputs = std::min(node_proto.input_size(), num_inputs);
|
||||||
|
for (int j = 0; j < num_inputs; j++)
|
||||||
{
|
{
|
||||||
const std::string& input_name = node_proto.input(j);
|
const std::string& input_name = node_proto.input(j);
|
||||||
IterLayerId_t layerId = layer_id.find(input_name);
|
IterLayerId_t layerId = layer_id.find(input_name);
|
||||||
@ -1818,7 +1821,7 @@ void ONNXImporter::parseClip(LayerParams& layerParams, const opencv_onnx::NodePr
|
|||||||
|
|
||||||
layerParams.set("min_value", layerParams.get<float>("min", min_value));
|
layerParams.set("min_value", layerParams.get<float>("min", min_value));
|
||||||
layerParams.set("max_value", layerParams.get<float>("max", max_value));
|
layerParams.set("max_value", layerParams.get<float>("max", max_value));
|
||||||
addLayer(layerParams, node_proto);
|
addLayer(layerParams, node_proto, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ONNXImporter::parseLeakyRelu(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
|
void ONNXImporter::parseLeakyRelu(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
|
||||||
|
@ -300,6 +300,10 @@ void TFLiteImporter::addLayer(LayerParams& layerParams, const Operator& op) {
|
|||||||
Mat blob = allTensors[idx];
|
Mat blob = allTensors[idx];
|
||||||
layerParams.blobs.push_back(blob.u ? blob : blob.clone()); // some tensors are owned by OpenCV
|
layerParams.blobs.push_back(blob.u ? blob : blob.clone()); // some tensors are owned by OpenCV
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for (auto& blob : layerParams.blobs) {
|
||||||
|
CV_Assert(blob.u);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int dtype = CV_32F;
|
int dtype = CV_32F;
|
||||||
@ -830,9 +834,6 @@ void TFLiteImporter::parseFullyConnected(const Operator& op, const std::string&
|
|||||||
auto options = op.builtin_options_as_FullyConnectedOptions();
|
auto options = op.builtin_options_as_FullyConnectedOptions();
|
||||||
CV_Assert(options);
|
CV_Assert(options);
|
||||||
|
|
||||||
int idx = op.inputs()->Get(1);
|
|
||||||
Mat weights = allTensors[idx];
|
|
||||||
layerParams.blobs.resize(1, weights);
|
|
||||||
layerParams.set("transB", true);
|
layerParams.set("transB", true);
|
||||||
layerParams.set("constB", true);
|
layerParams.set("constB", true);
|
||||||
addLayer(layerParams, op);
|
addLayer(layerParams, op);
|
||||||
|
@ -3119,6 +3119,10 @@ TEST_P(Test_ONNX_layers, MatMulAddFusion) {
|
|||||||
testONNXModels("biased_matmul", npy, l1, lInf);
|
testONNXModels("biased_matmul", npy, l1, lInf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(Test_ONNX_layers, ClipDivSharedConstant) {
|
||||||
|
testONNXModels("clip_div_shared_constant");
|
||||||
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(/**/, Test_ONNX_nets, dnnBackendsAndTargets());
|
INSTANTIATE_TEST_CASE_P(/**/, Test_ONNX_nets, dnnBackendsAndTargets());
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
@ -58,7 +58,13 @@ void Test_TFLite::testModel(Net& net, const std::string& modelName, const Mat& i
|
|||||||
ASSERT_EQ(outs.size(), outNames.size());
|
ASSERT_EQ(outs.size(), outNames.size());
|
||||||
for (int i = 0; i < outNames.size(); ++i) {
|
for (int i = 0; i < outNames.size(); ++i) {
|
||||||
Mat ref = blobFromNPY(findDataFile(format("dnn/tflite/%s_out_%s.npy", modelName.c_str(), outNames[i].c_str())));
|
Mat ref = blobFromNPY(findDataFile(format("dnn/tflite/%s_out_%s.npy", modelName.c_str(), outNames[i].c_str())));
|
||||||
normAssert(ref.reshape(1, 1), outs[i].reshape(1, 1), outNames[i].c_str(), l1, lInf);
|
// A workaround solution for the following cases due to inconsistent shape definitions.
|
||||||
|
// The details please see: https://github.com/opencv/opencv/pull/25297#issuecomment-2039081369
|
||||||
|
if (modelName == "face_landmark" || modelName == "selfie_segmentation") {
|
||||||
|
ref = ref.reshape(1, 1);
|
||||||
|
outs[i] = outs[i].reshape(1, 1);
|
||||||
|
}
|
||||||
|
normAssert(ref, outs[i], outNames[i].c_str(), l1, lInf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,8 +246,6 @@ TEST_P(Test_TFLite, split) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(Test_TFLite, fully_connected) {
|
TEST_P(Test_TFLite, fully_connected) {
|
||||||
if (backend == DNN_BACKEND_CUDA)
|
|
||||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_CUDA, CV_TEST_TAG_DNN_SKIP_CUDA_FP16);
|
|
||||||
if (backend == DNN_BACKEND_VKCOM)
|
if (backend == DNN_BACKEND_VKCOM)
|
||||||
applyTestTag(CV_TEST_TAG_DNN_SKIP_VULKAN);
|
applyTestTag(CV_TEST_TAG_DNN_SKIP_VULKAN);
|
||||||
testLayer("fully_connected");
|
testLayer("fully_connected");
|
||||||
|
@ -41,7 +41,7 @@ if(MSVC)
|
|||||||
# and IE deprecated code warning C4996
|
# and IE deprecated code warning C4996
|
||||||
ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4503 /wd4996)
|
ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4503 /wd4996)
|
||||||
endif()
|
endif()
|
||||||
if(MSVC_VERSION LESS 1920) # MSVS 2015/2017
|
if((MSVC_VERSION LESS 1920) OR ARM OR AARCH64) # MSVS 2015/2017 on x86 and ARM
|
||||||
ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4702) # 'unreachable code'
|
ocv_warnings_disable(CMAKE_CXX_FLAGS /wd4702) # 'unreachable code'
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
@ -62,14 +62,9 @@ public final class HighGui {
|
|||||||
if (m.channels() > 1) {
|
if (m.channels() > 1) {
|
||||||
type = BufferedImage.TYPE_3BYTE_BGR;
|
type = BufferedImage.TYPE_3BYTE_BGR;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bufferSize = m.channels() * m.cols() * m.rows();
|
|
||||||
byte[] b = new byte[bufferSize];
|
|
||||||
m.get(0, 0, b); // get all the pixels
|
|
||||||
BufferedImage image = new BufferedImage(m.cols(), m.rows(), type);
|
BufferedImage image = new BufferedImage(m.cols(), m.rows(), type);
|
||||||
|
|
||||||
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
|
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
|
||||||
System.arraycopy(b, 0, targetPixels, 0, b.length);
|
m.get(0, 0, targetPixels);
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
@ -103,9 +103,9 @@ enum ImwriteFlags {
|
|||||||
IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set; see libtiff documentation for valid values
|
IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set; see libtiff documentation for valid values
|
||||||
IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI
|
IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI
|
||||||
IMWRITE_TIFF_YDPI = 258,//!< For TIFF, use to specify the Y direction DPI
|
IMWRITE_TIFF_YDPI = 258,//!< For TIFF, use to specify the Y direction DPI
|
||||||
IMWRITE_TIFF_COMPRESSION = 259,//!< For TIFF, use to specify the image compression scheme. See libtiff for integer constants corresponding to compression formats. Note, for images whose depth is CV_32F, only libtiff's SGILOG compression scheme is used. For other supported depths, the compression scheme can be specified by this flag; LZW compression is the default.
|
IMWRITE_TIFF_COMPRESSION = 259,//!< For TIFF, use to specify the image compression scheme. See cv::ImwriteTiffCompressionFlags. Note, for images whose depth is CV_32F, only libtiff's SGILOG compression scheme is used. For other supported depths, the compression scheme can be specified by this flag; LZW compression is the default.
|
||||||
IMWRITE_TIFF_ROWSPERSTRIP = 278,//!< For TIFF, use to specify the number of rows per strip.
|
IMWRITE_TIFF_ROWSPERSTRIP = 278,//!< For TIFF, use to specify the number of rows per strip.
|
||||||
IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor.
|
IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor. See cv::ImwriteTiffPredictorFlags.
|
||||||
IMWRITE_JPEG2000_COMPRESSION_X1000 = 272,//!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000.
|
IMWRITE_JPEG2000_COMPRESSION_X1000 = 272,//!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000.
|
||||||
IMWRITE_AVIF_QUALITY = 512,//!< For AVIF, it can be a quality between 0 and 100 (the higher the better). Default is 95.
|
IMWRITE_AVIF_QUALITY = 512,//!< For AVIF, it can be a quality between 0 and 100 (the higher the better). Default is 95.
|
||||||
IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_32F. Default is 8.
|
IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_32F. Default is 8.
|
||||||
@ -120,6 +120,48 @@ enum ImwriteJPEGSamplingFactorParams {
|
|||||||
IMWRITE_JPEG_SAMPLING_FACTOR_444 = 0x111111 //!< 1x1,1x1,1x1(No subsampling)
|
IMWRITE_JPEG_SAMPLING_FACTOR_444 = 0x111111 //!< 1x1,1x1,1x1(No subsampling)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ImwriteTiffCompressionFlags {
|
||||||
|
IMWRITE_TIFF_COMPRESSION_NONE = 1, //!< dump mode
|
||||||
|
IMWRITE_TIFF_COMPRESSION_CCITTRLE = 2, //!< CCITT modified Huffman RLE
|
||||||
|
IMWRITE_TIFF_COMPRESSION_CCITTFAX3 = 3, //!< CCITT Group 3 fax encoding
|
||||||
|
IMWRITE_TIFF_COMPRESSION_CCITT_T4 = 3, //!< CCITT T.4 (TIFF 6 name)
|
||||||
|
IMWRITE_TIFF_COMPRESSION_CCITTFAX4 = 4, //!< CCITT Group 4 fax encoding
|
||||||
|
IMWRITE_TIFF_COMPRESSION_CCITT_T6 = 4, //!< CCITT T.6 (TIFF 6 name)
|
||||||
|
IMWRITE_TIFF_COMPRESSION_LZW = 5, //!< Lempel-Ziv & Welch
|
||||||
|
IMWRITE_TIFF_COMPRESSION_OJPEG = 6, //!< !6.0 JPEG
|
||||||
|
IMWRITE_TIFF_COMPRESSION_JPEG = 7, //!< %JPEG DCT compression
|
||||||
|
IMWRITE_TIFF_COMPRESSION_T85 = 9, //!< !TIFF/FX T.85 JBIG compression
|
||||||
|
IMWRITE_TIFF_COMPRESSION_T43 = 10, //!< !TIFF/FX T.43 colour by layered JBIG compression
|
||||||
|
IMWRITE_TIFF_COMPRESSION_NEXT = 32766, //!< NeXT 2-bit RLE
|
||||||
|
IMWRITE_TIFF_COMPRESSION_CCITTRLEW = 32771, //!< #1 w/ word alignment
|
||||||
|
IMWRITE_TIFF_COMPRESSION_PACKBITS = 32773, //!< Macintosh RLE
|
||||||
|
IMWRITE_TIFF_COMPRESSION_THUNDERSCAN = 32809, //!< ThunderScan RLE
|
||||||
|
IMWRITE_TIFF_COMPRESSION_IT8CTPAD = 32895, //!< IT8 CT w/padding
|
||||||
|
IMWRITE_TIFF_COMPRESSION_IT8LW = 32896, //!< IT8 Linework RLE
|
||||||
|
IMWRITE_TIFF_COMPRESSION_IT8MP = 32897, //!< IT8 Monochrome picture
|
||||||
|
IMWRITE_TIFF_COMPRESSION_IT8BL = 32898, //!< IT8 Binary line art
|
||||||
|
IMWRITE_TIFF_COMPRESSION_PIXARFILM = 32908, //!< Pixar companded 10bit LZW
|
||||||
|
IMWRITE_TIFF_COMPRESSION_PIXARLOG = 32909, //!< Pixar companded 11bit ZIP
|
||||||
|
IMWRITE_TIFF_COMPRESSION_DEFLATE = 32946, //!< Deflate compression, legacy tag
|
||||||
|
IMWRITE_TIFF_COMPRESSION_ADOBE_DEFLATE = 8, //!< Deflate compression, as recognized by Adobe
|
||||||
|
IMWRITE_TIFF_COMPRESSION_DCS = 32947, //!< Kodak DCS encoding
|
||||||
|
IMWRITE_TIFF_COMPRESSION_JBIG = 34661, //!< ISO JBIG
|
||||||
|
IMWRITE_TIFF_COMPRESSION_SGILOG = 34676, //!< SGI Log Luminance RLE
|
||||||
|
IMWRITE_TIFF_COMPRESSION_SGILOG24 = 34677, //!< SGI Log 24-bit packed
|
||||||
|
IMWRITE_TIFF_COMPRESSION_JP2000 = 34712, //!< Leadtools JPEG2000
|
||||||
|
IMWRITE_TIFF_COMPRESSION_LERC = 34887, //!< ESRI Lerc codec: https://github.com/Esri/lerc
|
||||||
|
IMWRITE_TIFF_COMPRESSION_LZMA = 34925, //!< LZMA2
|
||||||
|
IMWRITE_TIFF_COMPRESSION_ZSTD = 50000, //!< ZSTD: WARNING not registered in Adobe-maintained registry
|
||||||
|
IMWRITE_TIFF_COMPRESSION_WEBP = 50001, //!< WEBP: WARNING not registered in Adobe-maintained registry
|
||||||
|
IMWRITE_TIFF_COMPRESSION_JXL = 50002 //!< JPEGXL: WARNING not registered in Adobe-maintained registry
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ImwriteTiffPredictorFlags {
|
||||||
|
IMWRITE_TIFF_PREDICTOR_NONE = 1, //!< no prediction scheme used
|
||||||
|
IMWRITE_TIFF_PREDICTOR_HORIZONTAL = 2, //!< horizontal differencing
|
||||||
|
IMWRITE_TIFF_PREDICTOR_FLOATINGPOINT = 3 //!< floating point predictor
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
enum ImwriteEXRTypeFlags {
|
enum ImwriteEXRTypeFlags {
|
||||||
/*IMWRITE_EXR_TYPE_UNIT = 0, //!< not supported */
|
/*IMWRITE_EXR_TYPE_UNIT = 0, //!< not supported */
|
||||||
|
@ -9,11 +9,6 @@ namespace opencv_test { namespace {
|
|||||||
|
|
||||||
#ifdef HAVE_TIFF
|
#ifdef HAVE_TIFF
|
||||||
|
|
||||||
// these defines are used to resolve conflict between tiff.h and opencv2/core/types_c.h
|
|
||||||
#define uint64 uint64_hack_
|
|
||||||
#define int64 int64_hack_
|
|
||||||
#include "tiff.h"
|
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
// Test disabled as it uses a lot of memory.
|
// Test disabled as it uses a lot of memory.
|
||||||
// It is killed with SIGKILL by out of memory killer.
|
// It is killed with SIGKILL by out of memory killer.
|
||||||
@ -767,7 +762,7 @@ TEST(Imgcodecs_Tiff, readWrite_32FC3_RAW)
|
|||||||
|
|
||||||
std::vector<int> params;
|
std::vector<int> params;
|
||||||
params.push_back(IMWRITE_TIFF_COMPRESSION);
|
params.push_back(IMWRITE_TIFF_COMPRESSION);
|
||||||
params.push_back(COMPRESSION_NONE);
|
params.push_back(IMWRITE_TIFF_COMPRESSION_NONE);
|
||||||
|
|
||||||
ASSERT_TRUE(cv::imwrite(filenameOutput, img, params));
|
ASSERT_TRUE(cv::imwrite(filenameOutput, img, params));
|
||||||
const Mat img2 = cv::imread(filenameOutput, IMREAD_UNCHANGED);
|
const Mat img2 = cv::imread(filenameOutput, IMREAD_UNCHANGED);
|
||||||
@ -816,8 +811,9 @@ TEST(Imgcodecs_Tiff, readWrite_predictor)
|
|||||||
|
|
||||||
cv::Mat mat(10, 16, CV_8UC1, (void*)sample_data);
|
cv::Mat mat(10, 16, CV_8UC1, (void*)sample_data);
|
||||||
int methods[] = {
|
int methods[] = {
|
||||||
COMPRESSION_NONE, COMPRESSION_LZW,
|
IMWRITE_TIFF_COMPRESSION_NONE, IMWRITE_TIFF_COMPRESSION_LZW,
|
||||||
COMPRESSION_PACKBITS, COMPRESSION_DEFLATE, COMPRESSION_ADOBE_DEFLATE
|
IMWRITE_TIFF_COMPRESSION_PACKBITS, IMWRITE_TIFF_COMPRESSION_DEFLATE,
|
||||||
|
IMWRITE_TIFF_COMPRESSION_ADOBE_DEFLATE
|
||||||
};
|
};
|
||||||
for (size_t i = 0; i < sizeof(methods) / sizeof(int); i++)
|
for (size_t i = 0; i < sizeof(methods) / sizeof(int); i++)
|
||||||
{
|
{
|
||||||
@ -827,7 +823,7 @@ TEST(Imgcodecs_Tiff, readWrite_predictor)
|
|||||||
params.push_back(IMWRITE_TIFF_COMPRESSION);
|
params.push_back(IMWRITE_TIFF_COMPRESSION);
|
||||||
params.push_back(methods[i]);
|
params.push_back(methods[i]);
|
||||||
params.push_back(IMWRITE_TIFF_PREDICTOR);
|
params.push_back(IMWRITE_TIFF_PREDICTOR);
|
||||||
params.push_back(PREDICTOR_HORIZONTAL);
|
params.push_back(IMWRITE_TIFF_PREDICTOR_HORIZONTAL);
|
||||||
|
|
||||||
EXPECT_NO_THROW(cv::imwrite(out, mat, params));
|
EXPECT_NO_THROW(cv::imwrite(out, mat, params));
|
||||||
|
|
||||||
@ -863,7 +859,7 @@ TEST_P(Imgcodecs_Tiff_Types, readWrite_alltypes)
|
|||||||
{
|
{
|
||||||
std::vector<int> params;
|
std::vector<int> params;
|
||||||
params.push_back(IMWRITE_TIFF_COMPRESSION);
|
params.push_back(IMWRITE_TIFF_COMPRESSION);
|
||||||
params.push_back(COMPRESSION_LZW);
|
params.push_back(IMWRITE_TIFF_COMPRESSION_LZW);
|
||||||
ASSERT_NO_THROW(cv::imencode(".tiff", src, bufLZW, params));
|
ASSERT_NO_THROW(cv::imencode(".tiff", src, bufLZW, params));
|
||||||
|
|
||||||
Mat dstLZW;
|
Mat dstLZW;
|
||||||
@ -878,7 +874,7 @@ TEST_P(Imgcodecs_Tiff_Types, readWrite_alltypes)
|
|||||||
{
|
{
|
||||||
std::vector<int> params;
|
std::vector<int> params;
|
||||||
params.push_back(IMWRITE_TIFF_COMPRESSION);
|
params.push_back(IMWRITE_TIFF_COMPRESSION);
|
||||||
params.push_back(COMPRESSION_NONE);
|
params.push_back(IMWRITE_TIFF_COMPRESSION_NONE);
|
||||||
ASSERT_NO_THROW(cv::imencode(".tiff", src, bufRAW, params));
|
ASSERT_NO_THROW(cv::imencode(".tiff", src, bufRAW, params));
|
||||||
|
|
||||||
Mat dstRAW;
|
Mat dstRAW;
|
||||||
|
@ -4061,6 +4061,19 @@ A program using pyramid scaling, Canny, contours and contour simplification to f
|
|||||||
squares in the input image.
|
squares in the input image.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//! @brief Find contours using link runs algorithm
|
||||||
|
//!
|
||||||
|
//! This function implements an algorithm different from cv::findContours:
|
||||||
|
//! - doesn't allocate temporary image internally, thus it has reduced memory consumption
|
||||||
|
//! - supports CV_8UC1 images only
|
||||||
|
//! - outputs 2-level hierarhy only (RETR_CCOMP mode)
|
||||||
|
//! - doesn't support approximation change other than CHAIN_APPROX_SIMPLE
|
||||||
|
//! In all other aspects this function is compatible with cv::findContours.
|
||||||
|
CV_EXPORTS_W void findContoursLinkRuns(InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy);
|
||||||
|
|
||||||
|
//! @overload
|
||||||
|
CV_EXPORTS_W void findContoursLinkRuns(InputArray image, OutputArrayOfArrays contours);
|
||||||
|
|
||||||
/** @brief Approximates a polygonal curve(s) with the specified precision.
|
/** @brief Approximates a polygonal curve(s) with the specified precision.
|
||||||
|
|
||||||
The function cv::approxPolyDP approximates a curve or a polygon with another curve/polygon with less
|
The function cv::approxPolyDP approximates a curve or a polygon with another curve/polygon with less
|
||||||
|
30
modules/imgproc/include/opencv2/imgproc/detail/legacy.hpp
Normal file
30
modules/imgproc/include/opencv2/imgproc/detail/legacy.hpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html
|
||||||
|
|
||||||
|
#ifndef OPENCV_IMGPROC_DETAIL_LEGACY_HPP
|
||||||
|
#define OPENCV_IMGPROC_DETAIL_LEGACY_HPP
|
||||||
|
|
||||||
|
#include "opencv2/imgproc.hpp"
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
|
||||||
|
#ifdef __OPENCV_BUILD
|
||||||
|
|
||||||
|
CV_EXPORTS void findContours_legacy(InputArray _image,
|
||||||
|
OutputArrayOfArrays _contours,
|
||||||
|
OutputArray _hierarchy,
|
||||||
|
int mode,
|
||||||
|
int method,
|
||||||
|
Point offset = Point());
|
||||||
|
CV_EXPORTS void findContours_legacy(InputArray image,
|
||||||
|
OutputArrayOfArrays contours,
|
||||||
|
int mode,
|
||||||
|
int method,
|
||||||
|
Point offset = Point());
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif // OPENCV_IMGPROC_DETAIL_LEGACY_HPP
|
@ -42,6 +42,7 @@
|
|||||||
#include "opencv2/core/hal/intrin.hpp"
|
#include "opencv2/core/hal/intrin.hpp"
|
||||||
#include "opencv2/core/types_c.h"
|
#include "opencv2/core/types_c.h"
|
||||||
#include "opencv2/core/core_c.h"
|
#include "opencv2/core/core_c.h"
|
||||||
|
#include "opencv2/imgproc/detail/legacy.hpp"
|
||||||
|
|
||||||
using namespace cv;
|
using namespace cv;
|
||||||
|
|
||||||
@ -1964,7 +1965,7 @@ cvFindContours_Impl( void* img, CvMemStorage* storage,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cv::findContours( InputArray _image, OutputArrayOfArrays _contours,
|
void cv::findContours_legacy( InputArray _image, OutputArrayOfArrays _contours,
|
||||||
OutputArray _hierarchy, int mode, int method, Point offset )
|
OutputArray _hierarchy, int mode, int method, Point offset )
|
||||||
{
|
{
|
||||||
CV_INSTRUMENT_REGION();
|
CV_INSTRUMENT_REGION();
|
||||||
@ -2029,7 +2030,7 @@ void cv::findContours( InputArray _image, OutputArrayOfArrays _contours,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cv::findContours( InputArray _image, OutputArrayOfArrays _contours,
|
void cv::findContours_legacy( InputArray _image, OutputArrayOfArrays _contours,
|
||||||
int mode, int method, Point offset)
|
int mode, int method, Point offset)
|
||||||
{
|
{
|
||||||
CV_INSTRUMENT_REGION();
|
CV_INSTRUMENT_REGION();
|
||||||
|
354
modules/imgproc/src/contours_approx.cpp
Normal file
354
modules/imgproc/src/contours_approx.cpp
Normal file
@ -0,0 +1,354 @@
|
|||||||
|
// 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 "opencv2/core/base.hpp"
|
||||||
|
#include "opencv2/core/types.hpp"
|
||||||
|
#include "opencv2/imgproc.hpp"
|
||||||
|
#include "contours_common.hpp"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace cv;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct ApproxItem
|
||||||
|
{
|
||||||
|
Point pt;
|
||||||
|
size_t k; // support region
|
||||||
|
int s; // 1-curvature
|
||||||
|
bool removed;
|
||||||
|
ApproxItem() : k(0), s(0), removed(false) {}
|
||||||
|
ApproxItem(const Point& pt_, int s_) : pt(pt_), k(0), s(s_), removed(false) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const schar abs_diff[16] = {1, 2, 3, 4, 3, 2, 1, 0, 1, 2, 3, 4, 3, 2, 1};
|
||||||
|
static const Point chainCodeDeltas[8] =
|
||||||
|
{{1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}};
|
||||||
|
|
||||||
|
// Pass 0.
|
||||||
|
// Restores all the digital curve points from the chain code.
|
||||||
|
// Removes the points (from the resultant polygon)
|
||||||
|
// that have zero 1-curvature
|
||||||
|
static vector<ApproxItem> pass_0(const vector<schar>& chain, Point pt, bool isApprox, bool isFull)
|
||||||
|
{
|
||||||
|
vector<ApproxItem> res;
|
||||||
|
const size_t len = chain.size();
|
||||||
|
res.reserve(len / 2);
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
const schar prev = (i == 0) ? chain[len - 1] : chain[i - 1];
|
||||||
|
const schar cur = chain[i];
|
||||||
|
const schar s = abs_diff[cur - prev + 7];
|
||||||
|
if ((!isApprox && (isFull || s != 0)) || isApprox)
|
||||||
|
{
|
||||||
|
res.push_back(ApproxItem(pt, s));
|
||||||
|
if (s == 0)
|
||||||
|
(res.end() - 1)->removed = true;
|
||||||
|
}
|
||||||
|
pt += chainCodeDeltas[cur];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static vector<Point> gatherPoints(const vector<ApproxItem>& ares)
|
||||||
|
{
|
||||||
|
vector<Point> res;
|
||||||
|
res.reserve(ares.size() / 2);
|
||||||
|
for (const ApproxItem& item : ares)
|
||||||
|
{
|
||||||
|
if (item.removed)
|
||||||
|
continue;
|
||||||
|
res.push_back(item.pt);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t calc_support(const vector<ApproxItem>& ares, size_t i)
|
||||||
|
{
|
||||||
|
const size_t len = ares.size();
|
||||||
|
/* determine support region */
|
||||||
|
int d_num = 0;
|
||||||
|
int l = 0;
|
||||||
|
size_t k = 1;
|
||||||
|
for (;; k++)
|
||||||
|
{
|
||||||
|
CV_Assert(k <= len);
|
||||||
|
/* calc indices */
|
||||||
|
const size_t i1 = (i >= k) ? (i - k) : (len - k + i);
|
||||||
|
const size_t i2 = (i + k < len) ? (i + k) : (i + k - len);
|
||||||
|
|
||||||
|
const int dx = ares[i2].pt.x - ares[i1].pt.x;
|
||||||
|
const int dy = ares[i2].pt.y - ares[i1].pt.y;
|
||||||
|
|
||||||
|
/* distance between p_(i - k) and p_(i + k) */
|
||||||
|
const int lk = dx * dx + dy * dy;
|
||||||
|
|
||||||
|
/* distance between p_i and the line (p_(i-k), p_(i+k)) */
|
||||||
|
const int dk_num =
|
||||||
|
(ares[i].pt.x - ares[i1].pt.x) * dy - (ares[i].pt.y - ares[i1].pt.y) * dx;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
float f;
|
||||||
|
} d;
|
||||||
|
d.f = (float)(((double)d_num) * lk - ((double)dk_num) * l);
|
||||||
|
|
||||||
|
if (k > 1 && (l >= lk || ((d_num > 0 && d.i <= 0) || (d_num < 0 && d.i >= 0))))
|
||||||
|
break;
|
||||||
|
|
||||||
|
d_num = dk_num;
|
||||||
|
l = lk;
|
||||||
|
}
|
||||||
|
return k - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int calc_cosine(const vector<ApproxItem>& ares, size_t i)
|
||||||
|
{
|
||||||
|
const size_t k = ares[i].k;
|
||||||
|
size_t j;
|
||||||
|
int s;
|
||||||
|
const size_t len = ares.size();
|
||||||
|
/* calc k-cosine curvature */
|
||||||
|
for (j = k, s = 0; j > 0; j--)
|
||||||
|
{
|
||||||
|
const size_t i1 = (i >= j) ? (i - j) : (len - j + i);
|
||||||
|
const size_t i2 = (i + j < len) ? (i + j) : (i + j - len);
|
||||||
|
|
||||||
|
const int dx1 = ares[i1].pt.x - ares[i].pt.x;
|
||||||
|
const int dy1 = ares[i1].pt.y - ares[i].pt.y;
|
||||||
|
const int dx2 = ares[i2].pt.x - ares[i].pt.x;
|
||||||
|
const int dy2 = ares[i2].pt.y - ares[i].pt.y;
|
||||||
|
|
||||||
|
if ((dx1 | dy1) == 0 || (dx2 | dy2) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
double temp_num = dx1 * dx2 + dy1 * dy2;
|
||||||
|
temp_num = (float)(temp_num / sqrt(((double)dx1 * dx1 + (double)dy1 * dy1) *
|
||||||
|
((double)dx2 * dx2 + (double)dy2 * dy2)));
|
||||||
|
Cv32suf sk;
|
||||||
|
sk.f = (float)(temp_num + 1.1);
|
||||||
|
|
||||||
|
CV_Assert(0 <= sk.f && sk.f <= 2.2);
|
||||||
|
if (j < k && sk.i <= s)
|
||||||
|
break;
|
||||||
|
|
||||||
|
s = sk.i;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool calc_nms_cleanup(const vector<ApproxItem>& ares, size_t i)
|
||||||
|
{
|
||||||
|
const size_t k2 = ares[i].k >> 1;
|
||||||
|
const int s = ares[i].s;
|
||||||
|
const size_t len = ares.size();
|
||||||
|
size_t j;
|
||||||
|
for (j = 1; j <= k2; j++)
|
||||||
|
{
|
||||||
|
const size_t i1 = (i >= j) ? (i - j) : (len - j + i);
|
||||||
|
const size_t i2 = (i + j < len) ? (i + j) : (i + j - len);
|
||||||
|
if (ares[i1].s > s || ares[i2].s > s)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return j <= k2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool calc_dominance(const vector<ApproxItem>& ares, size_t i)
|
||||||
|
{
|
||||||
|
const size_t len = ares.size();
|
||||||
|
CV_Assert(len > 0);
|
||||||
|
const size_t i1 = (i >= 1) ? (i - 1) : (len - 1 + i);
|
||||||
|
const size_t i2 = (i + 1 < len) ? (i + 1) : (i + 1 - len);
|
||||||
|
return ares[i].s <= ares[i1].s || ares[i].s <= ares[i2].s;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline size_t get_next_idx(const vector<ApproxItem>& ares, const size_t start)
|
||||||
|
{
|
||||||
|
const size_t len = ares.size();
|
||||||
|
size_t res = start + 1;
|
||||||
|
for (; res < len; ++res)
|
||||||
|
{
|
||||||
|
if (!ares[res].removed)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void clear_until(vector<ApproxItem>& ares, const size_t start, const size_t finish)
|
||||||
|
{
|
||||||
|
const size_t len = ares.size();
|
||||||
|
for (size_t i = start + 1; i < finish && i < len; ++i)
|
||||||
|
{
|
||||||
|
ares[i].removed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool calc_new_start(vector<ApproxItem>& ares, size_t& res)
|
||||||
|
{
|
||||||
|
const size_t len = ares.size();
|
||||||
|
CV_Assert(len > 0);
|
||||||
|
size_t i1;
|
||||||
|
// remove all previous items from the beginning
|
||||||
|
for (i1 = 1; i1 < len && ares[i1].s != 0; i1++)
|
||||||
|
{
|
||||||
|
ares[i1 - 1].s = 0;
|
||||||
|
}
|
||||||
|
if (i1 == len)
|
||||||
|
{
|
||||||
|
// all points survived - skip to the end
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
i1--;
|
||||||
|
|
||||||
|
size_t i2;
|
||||||
|
// remove all following items from the end
|
||||||
|
for (i2 = len - 2; i2 > 0 && ares[i2].s != 0; i2--)
|
||||||
|
{
|
||||||
|
clear_until(ares, i2, len);
|
||||||
|
ares[i2 + 1].s = 0;
|
||||||
|
}
|
||||||
|
i2++;
|
||||||
|
|
||||||
|
// only two points left
|
||||||
|
if (i1 == 0 && i2 == len - 1)
|
||||||
|
{
|
||||||
|
// find first non-removed element from the start
|
||||||
|
i1 = get_next_idx(ares, 0);
|
||||||
|
// append first item to the end
|
||||||
|
ares.push_back(ares[0]);
|
||||||
|
(ares.end() - 1)->removed = false;
|
||||||
|
}
|
||||||
|
res = i1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pass_cleanup(vector<ApproxItem>& ares, size_t start_idx)
|
||||||
|
{
|
||||||
|
int count = 1;
|
||||||
|
|
||||||
|
const size_t len = ares.size();
|
||||||
|
size_t first = start_idx;
|
||||||
|
for (size_t i = start_idx, prev = i; i < len; ++i)
|
||||||
|
{
|
||||||
|
ApproxItem& item = ares[i];
|
||||||
|
if (item.removed)
|
||||||
|
continue;
|
||||||
|
size_t next_idx = get_next_idx(ares, i);
|
||||||
|
if (next_idx == len || next_idx - i != 1)
|
||||||
|
{
|
||||||
|
if (count >= 2)
|
||||||
|
{
|
||||||
|
if (count == 2)
|
||||||
|
{
|
||||||
|
const int s1 = ares[prev].s;
|
||||||
|
const int s2 = ares[i].s;
|
||||||
|
|
||||||
|
if (s1 > s2 || (s1 == s2 && ares[prev].k <= ares[i].k))
|
||||||
|
/* remove second */
|
||||||
|
clear_until(ares, get_next_idx(ares, prev), get_next_idx(ares, i));
|
||||||
|
else
|
||||||
|
/* remove first */
|
||||||
|
clear_until(ares, first, i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
first = get_next_idx(ares, first);
|
||||||
|
clear_until(ares, first, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clear_until(ares, first, i);
|
||||||
|
first = i;
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
prev = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
vector<Point> cv::approximateChainTC89(vector<schar> chain, const Point& origin, const int method)
|
||||||
|
{
|
||||||
|
if (chain.size() == 0)
|
||||||
|
{
|
||||||
|
return vector<Point>({origin});
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool isApprox = method == CHAIN_APPROX_TC89_L1 || method == CHAIN_APPROX_TC89_KCOS;
|
||||||
|
|
||||||
|
ApproxItem root;
|
||||||
|
vector<ApproxItem> ares = pass_0(chain, origin, isApprox, method == CHAIN_APPROX_NONE);
|
||||||
|
|
||||||
|
if (isApprox)
|
||||||
|
{
|
||||||
|
CV_DbgAssert(ares.size() < (size_t)numeric_limits<int>::max());
|
||||||
|
|
||||||
|
// Pass 1.
|
||||||
|
// Determines support region for all the remained points */
|
||||||
|
for (size_t i = 0; i < ares.size(); ++i)
|
||||||
|
{
|
||||||
|
ApproxItem& item = ares[i];
|
||||||
|
if (item.removed)
|
||||||
|
continue;
|
||||||
|
item.k = calc_support(ares, i);
|
||||||
|
|
||||||
|
if (method == CHAIN_APPROX_TC89_KCOS)
|
||||||
|
item.s = calc_cosine(ares, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass 2.
|
||||||
|
// Performs non-maxima suppression
|
||||||
|
for (size_t i = 0; i < ares.size(); ++i)
|
||||||
|
{
|
||||||
|
ApproxItem& item = ares[i];
|
||||||
|
if (calc_nms_cleanup(ares, i))
|
||||||
|
{
|
||||||
|
item.s = 0; // "clear"
|
||||||
|
item.removed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass 3.
|
||||||
|
// Removes non-dominant points with 1-length support region */
|
||||||
|
for (size_t i = 0; i < ares.size(); ++i)
|
||||||
|
{
|
||||||
|
ApproxItem& item = ares[i];
|
||||||
|
if (item.removed)
|
||||||
|
continue;
|
||||||
|
if (item.k == 1 && calc_dominance(ares, i))
|
||||||
|
{
|
||||||
|
item.s = 0;
|
||||||
|
item.removed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == cv::CHAIN_APPROX_TC89_L1)
|
||||||
|
{
|
||||||
|
// Pass 4.
|
||||||
|
// Cleans remained couples of points
|
||||||
|
bool skip = false;
|
||||||
|
size_t new_start_idx = 0;
|
||||||
|
const size_t len = ares.size();
|
||||||
|
if (ares[0].s != 0 && ares[len - 1].s != 0)
|
||||||
|
{
|
||||||
|
if (!calc_new_start(ares, new_start_idx))
|
||||||
|
{
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!skip)
|
||||||
|
{
|
||||||
|
pass_cleanup(ares, new_start_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gatherPoints(ares);
|
||||||
|
}
|
75
modules/imgproc/src/contours_common.cpp
Normal file
75
modules/imgproc/src/contours_common.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
#include "contours_common.hpp"
|
||||||
|
#include <map>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace cv;
|
||||||
|
|
||||||
|
void cv::contourTreeToResults(CTree& tree,
|
||||||
|
int res_type,
|
||||||
|
OutputArrayOfArrays& _contours,
|
||||||
|
OutputArray& _hierarchy)
|
||||||
|
{
|
||||||
|
// check if there are no results
|
||||||
|
if (tree.isEmpty() || (tree.elem(0).body.isEmpty() && (tree.elem(0).first_child == -1)))
|
||||||
|
{
|
||||||
|
_contours.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapping for indexes (original -> resulting)
|
||||||
|
map<int, int> index_mapping;
|
||||||
|
index_mapping[-1] = -1;
|
||||||
|
index_mapping[0] = -1;
|
||||||
|
|
||||||
|
CV_Assert(tree.size() < (size_t)numeric_limits<int>::max());
|
||||||
|
const int total = (int)tree.size() - 1;
|
||||||
|
_contours.create(total, 1, 0, -1, true);
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
CIterator it(tree);
|
||||||
|
while (!it.isDone())
|
||||||
|
{
|
||||||
|
const CNode& elem = it.getNext_s();
|
||||||
|
CV_Assert(elem.self() != -1);
|
||||||
|
if (elem.self() == 0)
|
||||||
|
continue;
|
||||||
|
index_mapping[elem.self()] = i;
|
||||||
|
CV_Assert(elem.body.size() < (size_t)numeric_limits<int>::max());
|
||||||
|
const int sz = (int)elem.body.size();
|
||||||
|
_contours.create(sz, 1, res_type, i, true);
|
||||||
|
if (sz > 0)
|
||||||
|
{
|
||||||
|
Mat cmat = _contours.getMat(i);
|
||||||
|
CV_Assert(cmat.isContinuous());
|
||||||
|
elem.body.copyTo(cmat.data);
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_hierarchy.needed())
|
||||||
|
{
|
||||||
|
_hierarchy.create(1, total, CV_32SC4, -1, true);
|
||||||
|
Mat h_mat = _hierarchy.getMat();
|
||||||
|
int i = 0;
|
||||||
|
CIterator it(tree);
|
||||||
|
while (!it.isDone())
|
||||||
|
{
|
||||||
|
const CNode& elem = it.getNext_s();
|
||||||
|
if (elem.self() == 0)
|
||||||
|
continue;
|
||||||
|
Vec4i& h_vec = h_mat.at<Vec4i>(i);
|
||||||
|
h_vec = Vec4i(index_mapping.at(elem.next),
|
||||||
|
index_mapping.at(elem.prev),
|
||||||
|
index_mapping.at(elem.first_child),
|
||||||
|
index_mapping.at(elem.parent));
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
219
modules/imgproc/src/contours_common.hpp
Normal file
219
modules/imgproc/src/contours_common.hpp
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html
|
||||||
|
|
||||||
|
#ifndef OPENCV_CONTOURS_COMMON_HPP
|
||||||
|
#define OPENCV_CONTOURS_COMMON_HPP
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
|
namespace cv {
|
||||||
|
|
||||||
|
static const schar MAX_SIZE = 16;
|
||||||
|
|
||||||
|
static const cv::Point chainCodeDeltas[8] =
|
||||||
|
{{1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}};
|
||||||
|
|
||||||
|
static inline int getDelta(schar s, size_t step)
|
||||||
|
{
|
||||||
|
CV_DbgAssert(s >= 0 && s < 16);
|
||||||
|
const cv::Point res = chainCodeDeltas[s % 8];
|
||||||
|
return res.x + res.y * (int)step;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline schar clamp_direction(schar dir)
|
||||||
|
{
|
||||||
|
return std::min(dir, (schar)15);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class TreeNode
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
int self_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// tree hierarchy (parent - children)
|
||||||
|
int parent;
|
||||||
|
int first_child;
|
||||||
|
// 1st linked list - bidirectional - sibling children
|
||||||
|
int prev;
|
||||||
|
int next;
|
||||||
|
// 2nd linked list - unidirectional - not related to 1st list
|
||||||
|
int ctable_next;
|
||||||
|
T body;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TreeNode(int self) :
|
||||||
|
self_(self), parent(-1), first_child(-1), prev(-1), next(-1), ctable_next(-1)
|
||||||
|
{
|
||||||
|
CV_Assert(self >= 0);
|
||||||
|
}
|
||||||
|
int self() const
|
||||||
|
{
|
||||||
|
return self_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Tree
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::vector<TreeNode<T>> nodes;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TreeNode<T>& newElem()
|
||||||
|
{
|
||||||
|
const size_t idx = nodes.size();
|
||||||
|
CV_DbgAssert(idx < (size_t)std::numeric_limits<int>::max());
|
||||||
|
nodes.push_back(TreeNode<T>((int)idx));
|
||||||
|
return nodes[idx];
|
||||||
|
}
|
||||||
|
TreeNode<T>& elem(int idx)
|
||||||
|
{
|
||||||
|
CV_DbgAssert(idx >= 0 && (size_t)idx < nodes.size());
|
||||||
|
return nodes[(size_t)idx];
|
||||||
|
}
|
||||||
|
const TreeNode<T>& elem(int idx) const
|
||||||
|
{
|
||||||
|
CV_DbgAssert(idx >= 0 && (size_t)idx < nodes.size());
|
||||||
|
return nodes[(size_t)idx];
|
||||||
|
}
|
||||||
|
int lastSibling(int e) const
|
||||||
|
{
|
||||||
|
if (e != -1)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const TreeNode<T>& cur_elem = elem(e);
|
||||||
|
if (cur_elem.next == -1)
|
||||||
|
break;
|
||||||
|
e = cur_elem.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
void addSiblingAfter(int prev, int idx)
|
||||||
|
{
|
||||||
|
TreeNode<T>& prev_item = nodes[prev];
|
||||||
|
TreeNode<T>& child = nodes[idx];
|
||||||
|
child.parent = prev_item.parent;
|
||||||
|
if (prev_item.next != -1)
|
||||||
|
{
|
||||||
|
nodes[prev_item.next].prev = idx;
|
||||||
|
child.next = prev_item.next;
|
||||||
|
}
|
||||||
|
child.prev = prev;
|
||||||
|
prev_item.next = idx;
|
||||||
|
}
|
||||||
|
void addChild(int parent_idx, int child_idx)
|
||||||
|
{
|
||||||
|
TreeNode<T>& parent = nodes[parent_idx];
|
||||||
|
TreeNode<T>& child = nodes[child_idx];
|
||||||
|
if (parent.first_child != -1)
|
||||||
|
{
|
||||||
|
TreeNode<T>& fchild_ = nodes[parent.first_child];
|
||||||
|
fchild_.prev = child_idx;
|
||||||
|
child.next = parent.first_child;
|
||||||
|
}
|
||||||
|
parent.first_child = child_idx;
|
||||||
|
child.parent = parent_idx;
|
||||||
|
child.prev = -1;
|
||||||
|
}
|
||||||
|
bool isEmpty() const
|
||||||
|
{
|
||||||
|
return nodes.size() == 0;
|
||||||
|
}
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return nodes.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class TreeIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TreeIterator(Tree<T>& tree_) : tree(tree_)
|
||||||
|
{
|
||||||
|
CV_Assert(!tree.isEmpty());
|
||||||
|
levels.push(0);
|
||||||
|
}
|
||||||
|
bool isDone() const
|
||||||
|
{
|
||||||
|
return levels.empty();
|
||||||
|
}
|
||||||
|
const TreeNode<T>& getNext_s()
|
||||||
|
{
|
||||||
|
int idx = levels.top();
|
||||||
|
levels.pop();
|
||||||
|
const TreeNode<T>& res = tree.elem(idx);
|
||||||
|
int cur = tree.lastSibling(res.first_child);
|
||||||
|
while (cur != -1)
|
||||||
|
{
|
||||||
|
levels.push(cur);
|
||||||
|
cur = tree.elem(cur).prev;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::stack<int> levels;
|
||||||
|
Tree<T>& tree;
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
class Contour
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cv::Rect brect;
|
||||||
|
cv::Point origin;
|
||||||
|
std::vector<cv::Point> pts;
|
||||||
|
std::vector<schar> codes;
|
||||||
|
bool isHole;
|
||||||
|
bool isChain;
|
||||||
|
|
||||||
|
Contour() : isHole(false), isChain(false) {}
|
||||||
|
void updateBoundingRect() {}
|
||||||
|
bool isEmpty() const
|
||||||
|
{
|
||||||
|
return pts.size() == 0 && codes.size() == 0;
|
||||||
|
}
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return isChain ? codes.size() : pts.size();
|
||||||
|
}
|
||||||
|
void copyTo(void* data) const
|
||||||
|
{
|
||||||
|
// NOTE: Mat::copyTo doesn't work because it creates new Mat object
|
||||||
|
// instead of reusing existing vector data
|
||||||
|
if (isChain)
|
||||||
|
{
|
||||||
|
memcpy(data, &codes[0], codes.size() * sizeof(codes[0]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(data, &pts[0], pts.size() * sizeof(pts[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef TreeNode<Contour> CNode;
|
||||||
|
typedef Tree<Contour> CTree;
|
||||||
|
typedef TreeIterator<Contour> CIterator;
|
||||||
|
|
||||||
|
|
||||||
|
void contourTreeToResults(CTree& tree,
|
||||||
|
int res_type,
|
||||||
|
cv::OutputArrayOfArrays& _contours,
|
||||||
|
cv::OutputArray& _hierarchy);
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<Point>
|
||||||
|
approximateChainTC89(std::vector<schar> chain, const Point& origin, const int method);
|
||||||
|
|
||||||
|
} // namespace cv
|
||||||
|
|
||||||
|
#endif // OPENCV_CONTOURS_COMMON_HPP
|
417
modules/imgproc/src/contours_link.cpp
Normal file
417
modules/imgproc/src/contours_link.cpp
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
// This file is part of OpenCV project.
|
||||||
|
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||||
|
// of this distribution and at http://opencv.org/license.html
|
||||||
|
|
||||||
|
#include "precomp.hpp"
|
||||||
|
#include "contours_common.hpp"
|
||||||
|
#include "opencv2/core/hal/intrin.hpp"
|
||||||
|
|
||||||
|
using namespace cv;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
inline static int findStartContourPoint(uchar* src_data, Size img_size, int j)
|
||||||
|
{
|
||||||
|
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||||
|
v_uint8 v_zero = vx_setzero_u8();
|
||||||
|
for (; j <= img_size.width - VTraits<v_uint8>::vlanes(); j += VTraits<v_uint8>::vlanes())
|
||||||
|
{
|
||||||
|
v_uint8 vmask = (v_ne(vx_load((uchar*)(src_data + j)), v_zero));
|
||||||
|
if (v_check_any(vmask))
|
||||||
|
{
|
||||||
|
j += v_scan_forward(vmask);
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (; j < img_size.width && !src_data[j]; ++j)
|
||||||
|
;
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int findEndContourPoint(uchar* src_data, Size img_size, int j)
|
||||||
|
{
|
||||||
|
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||||
|
if (j < img_size.width && !src_data[j])
|
||||||
|
{
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v_uint8 v_zero = vx_setzero_u8();
|
||||||
|
for (; j <= img_size.width - VTraits<v_uint8>::vlanes(); j += VTraits<v_uint8>::vlanes())
|
||||||
|
{
|
||||||
|
v_uint8 vmask = (v_eq(vx_load((uchar*)(src_data + j)), v_zero));
|
||||||
|
if (v_check_any(vmask))
|
||||||
|
{
|
||||||
|
j += v_scan_forward(vmask);
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (; j < img_size.width && src_data[j]; ++j)
|
||||||
|
;
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
struct LinkRunPoint
|
||||||
|
{
|
||||||
|
int link;
|
||||||
|
int next;
|
||||||
|
Point pt;
|
||||||
|
LinkRunPoint() : link(-1), next(-1) {}
|
||||||
|
LinkRunPoint(const Point& pt_) : link(-1), next(-1), pt(pt_) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef LinkRunPoint LRP;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
class LinkRunner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum LinkConnectionDirection
|
||||||
|
{
|
||||||
|
ICV_SINGLE = 0,
|
||||||
|
ICV_CONNECTING_ABOVE = 1,
|
||||||
|
ICV_CONNECTING_BELOW = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
CTree tree;
|
||||||
|
|
||||||
|
vector<LRP> rns;
|
||||||
|
vector<int> ext_rns;
|
||||||
|
vector<int> int_rns;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LinkRunner()
|
||||||
|
{
|
||||||
|
tree.newElem();
|
||||||
|
rns.reserve(100);
|
||||||
|
}
|
||||||
|
void process(Mat& image);
|
||||||
|
void convertLinks(int& first, int& prev, bool isHole);
|
||||||
|
void establishLinks(int& prev_point,
|
||||||
|
int upper_run,
|
||||||
|
int lower_run,
|
||||||
|
const int upper_total,
|
||||||
|
const int lower_total);
|
||||||
|
};
|
||||||
|
|
||||||
|
void LinkRunner::convertLinks(int& first, int& prev, bool isHole)
|
||||||
|
{
|
||||||
|
const vector<int>& contours = isHole ? int_rns : ext_rns;
|
||||||
|
int count = 0;
|
||||||
|
for (int j = 0; j < (int)contours.size(); j++, count++)
|
||||||
|
{
|
||||||
|
int start = contours[j];
|
||||||
|
int cur = start;
|
||||||
|
|
||||||
|
if (rns[cur].link == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CNode& node = tree.newElem();
|
||||||
|
node.body.isHole = isHole;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
node.body.pts.push_back(rns[cur].pt);
|
||||||
|
int p_temp = cur;
|
||||||
|
cur = rns[cur].link;
|
||||||
|
rns[p_temp].link = -1;
|
||||||
|
}
|
||||||
|
while (cur != start);
|
||||||
|
|
||||||
|
if (first == 0)
|
||||||
|
{
|
||||||
|
tree.addChild(0, node.self());
|
||||||
|
prev = first = node.self();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tree.addSiblingAfter(prev, node.self());
|
||||||
|
prev = node.self();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void LinkRunner::establishLinks(int& prev_point,
|
||||||
|
int upper_run,
|
||||||
|
int lower_run,
|
||||||
|
const int upper_total,
|
||||||
|
const int lower_total)
|
||||||
|
{
|
||||||
|
int k, n;
|
||||||
|
int connect_flag = ICV_SINGLE;
|
||||||
|
for (k = 0, n = 0; k < upper_total / 2 && n < lower_total / 2;)
|
||||||
|
{
|
||||||
|
switch (connect_flag)
|
||||||
|
{
|
||||||
|
case ICV_SINGLE:
|
||||||
|
if (rns[rns[upper_run].next].pt.x < rns[rns[lower_run].next].pt.x)
|
||||||
|
{
|
||||||
|
if (rns[rns[upper_run].next].pt.x >= rns[lower_run].pt.x - 1)
|
||||||
|
{
|
||||||
|
rns[lower_run].link = upper_run;
|
||||||
|
connect_flag = ICV_CONNECTING_ABOVE;
|
||||||
|
prev_point = rns[upper_run].next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
rns[rns[upper_run].next].link = upper_run;
|
||||||
|
k++;
|
||||||
|
upper_run = rns[rns[upper_run].next].next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (rns[upper_run].pt.x <= rns[rns[lower_run].next].pt.x + 1)
|
||||||
|
{
|
||||||
|
rns[lower_run].link = upper_run;
|
||||||
|
connect_flag = ICV_CONNECTING_BELOW;
|
||||||
|
prev_point = rns[lower_run].next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rns[lower_run].link = rns[lower_run].next;
|
||||||
|
// First point of contour
|
||||||
|
ext_rns.push_back(lower_run);
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
lower_run = rns[rns[lower_run].next].next;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICV_CONNECTING_ABOVE:
|
||||||
|
if (rns[upper_run].pt.x > rns[rns[lower_run].next].pt.x + 1)
|
||||||
|
{
|
||||||
|
rns[prev_point].link = rns[lower_run].next;
|
||||||
|
connect_flag = ICV_SINGLE;
|
||||||
|
n++;
|
||||||
|
lower_run = rns[rns[lower_run].next].next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rns[prev_point].link = upper_run;
|
||||||
|
if (rns[rns[upper_run].next].pt.x < rns[rns[lower_run].next].pt.x)
|
||||||
|
{
|
||||||
|
k++;
|
||||||
|
prev_point = rns[upper_run].next;
|
||||||
|
upper_run = rns[rns[upper_run].next].next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connect_flag = ICV_CONNECTING_BELOW;
|
||||||
|
prev_point = rns[lower_run].next;
|
||||||
|
n++;
|
||||||
|
lower_run = rns[rns[lower_run].next].next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ICV_CONNECTING_BELOW:
|
||||||
|
if (rns[lower_run].pt.x > rns[rns[upper_run].next].pt.x + 1)
|
||||||
|
{
|
||||||
|
rns[rns[upper_run].next].link = prev_point;
|
||||||
|
connect_flag = ICV_SINGLE;
|
||||||
|
k++;
|
||||||
|
upper_run = rns[rns[upper_run].next].next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// First point of contour
|
||||||
|
int_rns.push_back(lower_run);
|
||||||
|
|
||||||
|
rns[lower_run].link = prev_point;
|
||||||
|
if (rns[rns[lower_run].next].pt.x < rns[rns[upper_run].next].pt.x)
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
prev_point = rns[lower_run].next;
|
||||||
|
lower_run = rns[rns[lower_run].next].next;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connect_flag = ICV_CONNECTING_ABOVE;
|
||||||
|
k++;
|
||||||
|
prev_point = rns[upper_run].next;
|
||||||
|
upper_run = rns[rns[upper_run].next].next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} // k, n
|
||||||
|
|
||||||
|
for (; n < lower_total / 2; n++)
|
||||||
|
{
|
||||||
|
if (connect_flag != ICV_SINGLE)
|
||||||
|
{
|
||||||
|
rns[prev_point].link = rns[lower_run].next;
|
||||||
|
connect_flag = ICV_SINGLE;
|
||||||
|
lower_run = rns[rns[lower_run].next].next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rns[rns[lower_run].next] = rns[rns[lower_run].next];
|
||||||
|
rns[lower_run].link = rns[lower_run].next;
|
||||||
|
|
||||||
|
// First point of contour
|
||||||
|
ext_rns.push_back(lower_run);
|
||||||
|
lower_run = rns[rns[lower_run].next].next;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; k < upper_total / 2; k++)
|
||||||
|
{
|
||||||
|
if (connect_flag != ICV_SINGLE)
|
||||||
|
{
|
||||||
|
rns[rns[upper_run].next].link = prev_point;
|
||||||
|
connect_flag = ICV_SINGLE;
|
||||||
|
upper_run = rns[rns[upper_run].next].next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rns[rns[upper_run].next] = rns[rns[upper_run].next];
|
||||||
|
rns[rns[upper_run].next].link = upper_run;
|
||||||
|
upper_run = rns[rns[upper_run].next].next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LinkRunner::process(Mat& image)
|
||||||
|
{
|
||||||
|
const Size sz = image.size();
|
||||||
|
int j;
|
||||||
|
int lower_total;
|
||||||
|
int upper_total;
|
||||||
|
int all_total;
|
||||||
|
|
||||||
|
Point cur_point;
|
||||||
|
|
||||||
|
rns.reserve(sz.height); // optimization, assuming some contours exist
|
||||||
|
|
||||||
|
// First line. None of runs is binded
|
||||||
|
rns.push_back(LRP());
|
||||||
|
int upper_line = (int)rns.size() - 1;
|
||||||
|
int cur = upper_line;
|
||||||
|
for (j = 0; j < sz.width;)
|
||||||
|
{
|
||||||
|
j = findStartContourPoint(image.ptr<uchar>(), sz, j);
|
||||||
|
|
||||||
|
if (j == sz.width)
|
||||||
|
break;
|
||||||
|
|
||||||
|
cur_point.x = j;
|
||||||
|
|
||||||
|
rns.push_back(LRP(cur_point));
|
||||||
|
rns[cur].next = (int)rns.size() - 1;
|
||||||
|
cur = rns[cur].next;
|
||||||
|
|
||||||
|
j = findEndContourPoint(image.ptr<uchar>(), sz, j + 1);
|
||||||
|
|
||||||
|
cur_point.x = j - 1;
|
||||||
|
|
||||||
|
rns.push_back(LRP(cur_point));
|
||||||
|
rns[cur].next = (int)rns.size() - 1;
|
||||||
|
rns[cur].link = rns[cur].next;
|
||||||
|
|
||||||
|
// First point of contour
|
||||||
|
ext_rns.push_back(cur);
|
||||||
|
cur = rns[cur].next;
|
||||||
|
}
|
||||||
|
upper_line = rns[upper_line].next;
|
||||||
|
upper_total = (int)rns.size() - 1; // runs->total - 1;
|
||||||
|
|
||||||
|
int last_elem = cur;
|
||||||
|
rns[cur].next = -1;
|
||||||
|
int prev_point = -1;
|
||||||
|
int lower_line = -1;
|
||||||
|
for (int i = 1; i < sz.height; i++)
|
||||||
|
{
|
||||||
|
// Find runs in next line
|
||||||
|
cur_point.y = i;
|
||||||
|
all_total = (int)rns.size(); // runs->total;
|
||||||
|
for (j = 0; j < sz.width;)
|
||||||
|
{
|
||||||
|
j = findStartContourPoint(image.ptr<uchar>(i), sz, j);
|
||||||
|
|
||||||
|
if (j == sz.width)
|
||||||
|
break;
|
||||||
|
|
||||||
|
cur_point.x = j;
|
||||||
|
|
||||||
|
rns.push_back(LRP(cur_point));
|
||||||
|
rns[cur].next = (int)rns.size() - 1;
|
||||||
|
cur = rns[cur].next;
|
||||||
|
|
||||||
|
j = findEndContourPoint(image.ptr<uchar>(i), sz, j + 1);
|
||||||
|
|
||||||
|
cur_point.x = j - 1;
|
||||||
|
rns.push_back(LRP(cur_point));
|
||||||
|
cur = rns[cur].next = (int)rns.size() - 1;
|
||||||
|
} // j
|
||||||
|
lower_line = rns[last_elem].next;
|
||||||
|
lower_total = (int)rns.size() - all_total; // runs->total - all_total;
|
||||||
|
last_elem = cur;
|
||||||
|
rns[cur].next = -1;
|
||||||
|
|
||||||
|
CV_DbgAssert(rns.size() < (size_t)numeric_limits<int>::max());
|
||||||
|
|
||||||
|
// Find links between runs of lower_line and upper_line
|
||||||
|
establishLinks(prev_point, upper_line, lower_line, upper_total, lower_total);
|
||||||
|
|
||||||
|
upper_line = lower_line;
|
||||||
|
upper_total = lower_total;
|
||||||
|
} // i
|
||||||
|
|
||||||
|
// the last line of image
|
||||||
|
int upper_run = upper_line;
|
||||||
|
for (int k = 0; k < upper_total / 2; k++)
|
||||||
|
{
|
||||||
|
rns[rns[upper_run].next].link = upper_run;
|
||||||
|
upper_run = rns[rns[upper_run].next].next;
|
||||||
|
}
|
||||||
|
|
||||||
|
int first = 0;
|
||||||
|
int prev = 0;
|
||||||
|
convertLinks(first, prev, false);
|
||||||
|
convertLinks(first, prev, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
void cv::findContoursLinkRuns(InputArray _image,
|
||||||
|
OutputArrayOfArrays _contours,
|
||||||
|
OutputArray _hierarchy)
|
||||||
|
{
|
||||||
|
CV_INSTRUMENT_REGION();
|
||||||
|
|
||||||
|
CV_CheckType(_image.type(),
|
||||||
|
_image.type() == CV_8UC1 || _image.type() == CV_8SC1,
|
||||||
|
"Bad input image type, must be CV_8UC1 or CV_8SC1");
|
||||||
|
|
||||||
|
// Sanity check: output must be of type vector<vector<Point>>
|
||||||
|
CV_Assert(_contours.kind() == _InputArray::STD_VECTOR_VECTOR ||
|
||||||
|
_contours.kind() == _InputArray::STD_VECTOR_MAT ||
|
||||||
|
_contours.kind() == _InputArray::STD_VECTOR_UMAT);
|
||||||
|
|
||||||
|
if (!_contours.empty())
|
||||||
|
CV_CheckTypeEQ(_contours.type(), CV_32SC2, "Contours must have type CV_32SC2");
|
||||||
|
|
||||||
|
if (_hierarchy.needed())
|
||||||
|
_hierarchy.clear();
|
||||||
|
|
||||||
|
Mat image = _image.getMat();
|
||||||
|
|
||||||
|
LinkRunner runner;
|
||||||
|
runner.process(image);
|
||||||
|
|
||||||
|
contourTreeToResults(runner.tree, CV_32SC2, _contours, _hierarchy);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cv::findContoursLinkRuns(InputArray _image, OutputArrayOfArrays _contours)
|
||||||
|
{
|
||||||
|
CV_INSTRUMENT_REGION();
|
||||||
|
findContoursLinkRuns(_image, _contours, noArray());
|
||||||
|
}
|
697
modules/imgproc/src/contours_new.cpp
Normal file
697
modules/imgproc/src/contours_new.cpp
Normal file
@ -0,0 +1,697 @@
|
|||||||
|
// 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 "opencv2/imgproc.hpp"
|
||||||
|
#include "precomp.hpp"
|
||||||
|
#include "opencv2/core/hal/intrin.hpp"
|
||||||
|
#include "opencv2/core/check.hpp"
|
||||||
|
#include "opencv2/core/utils/logger.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <array>
|
||||||
|
#include <limits>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "contours_common.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace cv;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Trait
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
static const schar MASK8_RIGHT = '\x80'; // 1000 0000
|
||||||
|
static const schar MASK8_NEW = '\x02'; // 0000 0010 (+2)
|
||||||
|
static const schar MASK8_FLAGS = '\xFE'; // 1111 1110 (-2)
|
||||||
|
static const schar MASK8_BLACK = '\x01'; // 0000 0001 - black pixel
|
||||||
|
|
||||||
|
static const schar MASK8_LVAL = '\x7F'; // 0111 1111 (for table)
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Trait<schar>
|
||||||
|
{
|
||||||
|
static inline bool checkValue(const schar* elem, const schar*)
|
||||||
|
{
|
||||||
|
return *elem != 0;
|
||||||
|
}
|
||||||
|
static inline bool isVal(const schar* elem, const schar*)
|
||||||
|
{
|
||||||
|
return *elem == MASK8_BLACK;
|
||||||
|
}
|
||||||
|
static inline bool isRight(const schar* elem, const schar*)
|
||||||
|
{
|
||||||
|
return (*elem & MASK8_RIGHT) != 0;
|
||||||
|
}
|
||||||
|
static inline void setRightFlag(schar* elem, const schar*, schar nbd)
|
||||||
|
{
|
||||||
|
*elem = nbd | MASK8_RIGHT;
|
||||||
|
}
|
||||||
|
static inline void setNewFlag(schar* elem, const schar*, schar nbd)
|
||||||
|
{
|
||||||
|
*elem = nbd;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int MASK_RIGHT = 0x80000000; // 100..000
|
||||||
|
static const int MASK_NEW = 0x40000000; // 010..000
|
||||||
|
static const int MASK_FLAGS = 0xC0000000; // right + new
|
||||||
|
static const int MASK_VAL = 0x3FFFFFFF; // ~flags - pixel label
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Trait<int>
|
||||||
|
{
|
||||||
|
static inline bool checkValue(const int* elem, const int* elem0)
|
||||||
|
{
|
||||||
|
return (*elem & MASK_VAL) == (*elem0 & MASK_VAL);
|
||||||
|
}
|
||||||
|
static inline bool isVal(const int* elem, const int* elem0)
|
||||||
|
{
|
||||||
|
return *elem == (*elem0 & MASK_VAL);
|
||||||
|
}
|
||||||
|
static inline bool isRight(const int* elem, const int* elem0)
|
||||||
|
{
|
||||||
|
return (*elem & MASK_RIGHT) == (*elem0 & MASK8_RIGHT);
|
||||||
|
}
|
||||||
|
static inline void setRightFlag(int* elem, const int* elem0, int)
|
||||||
|
{
|
||||||
|
*elem = (*elem0 & MASK_VAL) | MASK_NEW | MASK_RIGHT;
|
||||||
|
}
|
||||||
|
static inline void setNewFlag(int* elem, const int* elem0, int)
|
||||||
|
{
|
||||||
|
*elem = (*elem0 & MASK_VAL) | MASK_NEW;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static bool icvTraceContour(Mat& image, const Point& start, const Point& end, bool isHole)
|
||||||
|
{
|
||||||
|
const T* stop_ptr = image.ptr<T>(end.y, end.x);
|
||||||
|
const size_t step = image.step1();
|
||||||
|
const T *i0 = image.ptr<T>(start.y, start.x), *i1, *i3, *i4 = NULL;
|
||||||
|
const schar s_end = isHole ? 0 : 4;
|
||||||
|
|
||||||
|
schar s = s_end;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
s = (s - 1) & 7;
|
||||||
|
i1 = i0 + getDelta(s, step);
|
||||||
|
}
|
||||||
|
while (!Trait<T>::checkValue(i1, i0) && s != s_end);
|
||||||
|
|
||||||
|
i3 = i0;
|
||||||
|
|
||||||
|
// check single pixel domain
|
||||||
|
if (s != s_end)
|
||||||
|
{
|
||||||
|
// follow border
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
CV_Assert(i3 != NULL);
|
||||||
|
s = clamp_direction(s);
|
||||||
|
while (s < MAX_SIZE - 1)
|
||||||
|
{
|
||||||
|
++s;
|
||||||
|
i4 = i3 + getDelta(s, step);
|
||||||
|
CV_Assert(i4 != NULL);
|
||||||
|
if (Trait<T>::checkValue(i4, i0))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i3 == stop_ptr)
|
||||||
|
{
|
||||||
|
if (!Trait<T>::isRight(i3, i0))
|
||||||
|
{
|
||||||
|
// it's the only contour
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this is the last contour
|
||||||
|
// encountered during a raster scan
|
||||||
|
const T* i5;
|
||||||
|
schar t = s;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
t = (t - 1) & 7;
|
||||||
|
i5 = i3 + getDelta(t, step);
|
||||||
|
if (*i5 != 0)
|
||||||
|
break;
|
||||||
|
if (t == 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((i4 == i0 && i3 == i1))
|
||||||
|
break;
|
||||||
|
|
||||||
|
i3 = i4;
|
||||||
|
s = (s + 4) & 7;
|
||||||
|
} // end of border following loop
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return i3 == stop_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void icvFetchContourEx(Mat& image,
|
||||||
|
const Point& start,
|
||||||
|
T nbd,
|
||||||
|
Contour& res_contour,
|
||||||
|
const bool isDirect)
|
||||||
|
{
|
||||||
|
const size_t step = image.step1();
|
||||||
|
T *i0 = image.ptr<T>(start.y, start.x), *i1, *i3, *i4 = NULL;
|
||||||
|
|
||||||
|
Point pt = res_contour.origin;
|
||||||
|
|
||||||
|
cv::Rect rect(pt.x, pt.y, pt.x, pt.y);
|
||||||
|
|
||||||
|
schar s_end = res_contour.isHole ? 0 : 4;
|
||||||
|
schar s = s_end;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
s = (s - 1) & 7;
|
||||||
|
i1 = i0 + getDelta(s, step);
|
||||||
|
}
|
||||||
|
while (!Trait<T>::checkValue(i1, i0) && s != s_end);
|
||||||
|
|
||||||
|
if (s == s_end)
|
||||||
|
{
|
||||||
|
Trait<T>::setRightFlag(i0, i0, nbd);
|
||||||
|
if (!res_contour.isChain)
|
||||||
|
{
|
||||||
|
res_contour.pts.push_back(pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i3 = i0;
|
||||||
|
schar prev_s = s ^ 4;
|
||||||
|
|
||||||
|
// follow border
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
s_end = s;
|
||||||
|
s = clamp_direction(s);
|
||||||
|
while (s < MAX_SIZE - 1)
|
||||||
|
{
|
||||||
|
++s;
|
||||||
|
i4 = i3 + getDelta(s, step);
|
||||||
|
CV_Assert(i4 != NULL);
|
||||||
|
if (Trait<T>::checkValue(i4, i0))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
s &= 7;
|
||||||
|
|
||||||
|
// check "right" bound
|
||||||
|
if ((unsigned)(s - 1) < (unsigned)s_end)
|
||||||
|
{
|
||||||
|
Trait<T>::setRightFlag(i3, i0, nbd);
|
||||||
|
}
|
||||||
|
else if (Trait<T>::isVal(i3, i0))
|
||||||
|
{
|
||||||
|
Trait<T>::setNewFlag(i3, i0, nbd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res_contour.isChain)
|
||||||
|
{
|
||||||
|
res_contour.codes.push_back(s);
|
||||||
|
}
|
||||||
|
else if (s != prev_s || isDirect)
|
||||||
|
{
|
||||||
|
res_contour.pts.push_back(pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s != prev_s)
|
||||||
|
{
|
||||||
|
// update bounds
|
||||||
|
if (pt.x < rect.x)
|
||||||
|
rect.x = pt.x;
|
||||||
|
else if (pt.x > rect.width)
|
||||||
|
rect.width = pt.x;
|
||||||
|
|
||||||
|
if (pt.y < rect.y)
|
||||||
|
rect.y = pt.y;
|
||||||
|
else if (pt.y > rect.height)
|
||||||
|
rect.height = pt.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_s = s;
|
||||||
|
pt += chainCodeDeltas[s];
|
||||||
|
|
||||||
|
if (i4 == i0 && i3 == i1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
i3 = i4;
|
||||||
|
s = (s + 4) & 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rect.width -= rect.x - 1;
|
||||||
|
rect.height -= rect.y - 1;
|
||||||
|
res_contour.brect = rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
//
|
||||||
|
// Raster->Chain Tree (Suzuki algorithms)
|
||||||
|
//
|
||||||
|
|
||||||
|
// Structure that is used for sequential retrieving contours from the image.
|
||||||
|
// It supports both hierarchical and plane variants of Suzuki algorithm.
|
||||||
|
struct ContourScanner_
|
||||||
|
{
|
||||||
|
Mat image;
|
||||||
|
Point offset; // ROI offset: coordinates, added to each contour point
|
||||||
|
Point pt; // current scanner position
|
||||||
|
Point lnbd; // position of the last met contour
|
||||||
|
schar nbd; // current mark val
|
||||||
|
int approx_method1; // approx method when tracing
|
||||||
|
int approx_method2; // final approx method
|
||||||
|
int mode;
|
||||||
|
CTree tree;
|
||||||
|
array<int, 128> ctable;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ContourScanner_() {}
|
||||||
|
~ContourScanner_() {}
|
||||||
|
inline bool isInt() const
|
||||||
|
{
|
||||||
|
return (this->mode == RETR_FLOODFILL);
|
||||||
|
}
|
||||||
|
inline bool isSimple() const
|
||||||
|
{
|
||||||
|
return (this->mode == RETR_EXTERNAL || this->mode == RETR_LIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
CNode& makeContour(schar& nbd_, const bool is_hole, const int x, const int y);
|
||||||
|
bool contourScan(const int prev, int& p, Point& last_pos, const int x, const int y);
|
||||||
|
int findFirstBoundingContour(const Point& last_pos, const int y, const int lval, int par);
|
||||||
|
int findNextX(int x, int y, int& prev, int& p);
|
||||||
|
bool findNext();
|
||||||
|
|
||||||
|
static shared_ptr<ContourScanner_> create(Mat img, int mode, int method, Point offset);
|
||||||
|
}; // class ContourScanner_
|
||||||
|
|
||||||
|
typedef shared_ptr<ContourScanner_> ContourScanner;
|
||||||
|
|
||||||
|
|
||||||
|
shared_ptr<ContourScanner_> ContourScanner_::create(Mat img, int mode, int method, Point offset)
|
||||||
|
{
|
||||||
|
if (mode == RETR_CCOMP && img.type() == CV_32SC1)
|
||||||
|
mode = RETR_FLOODFILL;
|
||||||
|
|
||||||
|
if (mode == RETR_FLOODFILL)
|
||||||
|
CV_CheckTypeEQ(img.type(), CV_32SC1, "RETR_FLOODFILL mode supports only CV_32SC1 images");
|
||||||
|
else
|
||||||
|
CV_CheckTypeEQ(img.type(),
|
||||||
|
CV_8UC1,
|
||||||
|
"Modes other than RETR_FLOODFILL and RETR_CCOMP support only CV_8UC1 "
|
||||||
|
"images");
|
||||||
|
|
||||||
|
CV_Check(mode,
|
||||||
|
mode == RETR_EXTERNAL || mode == RETR_LIST || mode == RETR_CCOMP ||
|
||||||
|
mode == RETR_TREE || mode == RETR_FLOODFILL,
|
||||||
|
"Wrong extraction mode");
|
||||||
|
|
||||||
|
CV_Check(method,
|
||||||
|
method == 0 || method == CHAIN_APPROX_NONE || method == CHAIN_APPROX_SIMPLE ||
|
||||||
|
method == CHAIN_APPROX_TC89_L1 || method == CHAIN_APPROX_TC89_KCOS,
|
||||||
|
"Wrong approximation method");
|
||||||
|
|
||||||
|
Size size = img.size();
|
||||||
|
CV_Assert(size.height >= 1);
|
||||||
|
|
||||||
|
shared_ptr<ContourScanner_> scanner = make_shared<ContourScanner_>();
|
||||||
|
scanner->image = img;
|
||||||
|
scanner->mode = mode;
|
||||||
|
scanner->offset = offset;
|
||||||
|
scanner->pt = Point(1, 1);
|
||||||
|
scanner->lnbd = Point(0, 1);
|
||||||
|
scanner->nbd = 2;
|
||||||
|
CNode& root = scanner->tree.newElem();
|
||||||
|
CV_Assert(root.self() == 0);
|
||||||
|
root.body.isHole = true;
|
||||||
|
root.body.brect = Rect(Point(0, 0), size);
|
||||||
|
scanner->ctable.fill(-1);
|
||||||
|
scanner->approx_method2 = scanner->approx_method1 = method;
|
||||||
|
if (method == CHAIN_APPROX_TC89_L1 || method == CHAIN_APPROX_TC89_KCOS)
|
||||||
|
scanner->approx_method1 = CHAIN_CODE;
|
||||||
|
return scanner;
|
||||||
|
}
|
||||||
|
|
||||||
|
CNode& ContourScanner_::makeContour(schar& nbd_, const bool is_hole, const int x, const int y)
|
||||||
|
{
|
||||||
|
const bool isChain = (this->approx_method1 == CHAIN_CODE); // TODO: get rid of old constant
|
||||||
|
const bool isDirect = (this->approx_method1 == CHAIN_APPROX_NONE);
|
||||||
|
|
||||||
|
const Point start_pt(x - (is_hole ? 1 : 0), y);
|
||||||
|
|
||||||
|
CNode& res = tree.newElem();
|
||||||
|
if (isChain)
|
||||||
|
res.body.codes.reserve(200);
|
||||||
|
else
|
||||||
|
res.body.pts.reserve(200);
|
||||||
|
res.body.isHole = is_hole;
|
||||||
|
res.body.isChain = isChain;
|
||||||
|
res.body.origin = start_pt + offset;
|
||||||
|
if (isSimple())
|
||||||
|
{
|
||||||
|
icvFetchContourEx<schar>(this->image, start_pt, MASK8_NEW, res.body, isDirect);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
schar lval;
|
||||||
|
if (isInt())
|
||||||
|
{
|
||||||
|
const int start_val = this->image.at<int>(start_pt);
|
||||||
|
lval = start_val & MASK8_LVAL;
|
||||||
|
icvFetchContourEx<int>(this->image, start_pt, 0, res.body, isDirect);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lval = nbd_;
|
||||||
|
// change nbd
|
||||||
|
nbd_ = (nbd_ + 1) & MASK8_LVAL;
|
||||||
|
if (nbd_ == 0)
|
||||||
|
nbd_ = MASK8_BLACK | MASK8_NEW;
|
||||||
|
icvFetchContourEx<schar>(this->image, start_pt, lval, res.body, isDirect);
|
||||||
|
}
|
||||||
|
res.body.brect.x -= this->offset.x;
|
||||||
|
res.body.brect.y -= this->offset.y;
|
||||||
|
res.ctable_next = this->ctable[lval];
|
||||||
|
this->ctable[lval] = res.self();
|
||||||
|
}
|
||||||
|
const Point prev_origin = res.body.origin;
|
||||||
|
res.body.origin = start_pt;
|
||||||
|
if (this->approx_method1 != this->approx_method2)
|
||||||
|
{
|
||||||
|
CV_Assert(res.body.isChain);
|
||||||
|
res.body.pts = approximateChainTC89(res.body.codes, prev_origin, this->approx_method2);
|
||||||
|
res.body.isChain = false;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContourScanner_::contourScan(const int prev, int& p, Point& last_pos, const int x, const int y)
|
||||||
|
{
|
||||||
|
bool is_hole = false;
|
||||||
|
|
||||||
|
/* if not external contour */
|
||||||
|
if (isInt())
|
||||||
|
{
|
||||||
|
if (!(((prev & MASK_FLAGS) != 0 || prev == 0) && (p & MASK_FLAGS) == 0))
|
||||||
|
{
|
||||||
|
if ((prev & MASK_FLAGS) != 0 || ((p & MASK_FLAGS) != 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (prev & MASK_FLAGS)
|
||||||
|
{
|
||||||
|
last_pos.x = x - 1;
|
||||||
|
}
|
||||||
|
is_hole = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!(prev == 0 && p == 1))
|
||||||
|
{
|
||||||
|
if (p != 0 || prev < 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (prev & MASK8_FLAGS)
|
||||||
|
{
|
||||||
|
last_pos.x = x - 1;
|
||||||
|
}
|
||||||
|
is_hole = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == RETR_EXTERNAL && (is_hole || this->image.at<schar>(last_pos) > 0))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find contour parent */
|
||||||
|
int main_parent = -1;
|
||||||
|
if (isSimple() || (!is_hole && (mode == RETR_CCOMP || mode == RETR_FLOODFILL)) ||
|
||||||
|
last_pos.x <= 0)
|
||||||
|
{
|
||||||
|
main_parent = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int lval;
|
||||||
|
if (isInt())
|
||||||
|
lval = this->image.at<int>(last_pos.y, last_pos.x) & MASK8_LVAL;
|
||||||
|
else
|
||||||
|
lval = this->image.at<schar>(last_pos.y, last_pos.x) & MASK8_LVAL;
|
||||||
|
|
||||||
|
main_parent = findFirstBoundingContour(last_pos, y, lval, main_parent);
|
||||||
|
|
||||||
|
// if current contour is a hole and previous contour is a hole or
|
||||||
|
// current contour is external and previous contour is external then
|
||||||
|
// the parent of the contour is the parent of the previous contour else
|
||||||
|
// the parent is the previous contour itself.
|
||||||
|
{
|
||||||
|
CNode& main_parent_elem = tree.elem(main_parent);
|
||||||
|
if (main_parent_elem.body.isHole == is_hole)
|
||||||
|
{
|
||||||
|
if (main_parent_elem.parent != -1)
|
||||||
|
{
|
||||||
|
main_parent = main_parent_elem.parent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
main_parent = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hole flag of the parent must differ from the flag of the contour
|
||||||
|
{
|
||||||
|
CNode& main_parent_elem = tree.elem(main_parent);
|
||||||
|
CV_Assert(main_parent_elem.body.isHole != is_hole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
last_pos.x = x - (is_hole ? 1 : 0);
|
||||||
|
|
||||||
|
schar nbd_ = this->nbd;
|
||||||
|
CNode& new_contour = makeContour(nbd_, is_hole, x, y);
|
||||||
|
if (new_contour.parent == -1)
|
||||||
|
{
|
||||||
|
tree.addChild(main_parent, new_contour.self());
|
||||||
|
}
|
||||||
|
this->pt.x = !isInt() ? (x + 1) : (x + 1 - (is_hole ? 1 : 0));
|
||||||
|
this->pt.y = y;
|
||||||
|
this->nbd = nbd_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContourScanner_::findFirstBoundingContour(const Point& last_pos,
|
||||||
|
const int y,
|
||||||
|
const int lval,
|
||||||
|
int par)
|
||||||
|
{
|
||||||
|
const Point end_point(last_pos.x, y);
|
||||||
|
int res = par;
|
||||||
|
int cur = ctable[lval];
|
||||||
|
while (cur != -1)
|
||||||
|
{
|
||||||
|
CNode& cur_elem = tree.elem(cur);
|
||||||
|
if (((last_pos.x - cur_elem.body.brect.x) < cur_elem.body.brect.width) &&
|
||||||
|
((last_pos.y - cur_elem.body.brect.y) < cur_elem.body.brect.height))
|
||||||
|
{
|
||||||
|
if (res != -1)
|
||||||
|
{
|
||||||
|
CNode& res_elem = tree.elem(res);
|
||||||
|
const Point origin = res_elem.body.origin;
|
||||||
|
const bool isHole = res_elem.body.isHole;
|
||||||
|
if (isInt())
|
||||||
|
{
|
||||||
|
if (icvTraceContour<int>(this->image, origin, end_point, isHole))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (icvTraceContour<schar>(this->image, origin, end_point, isHole))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res = cur;
|
||||||
|
}
|
||||||
|
cur = cur_elem.ctable_next;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ContourScanner_::findNextX(int x, int y, int& prev, int& p)
|
||||||
|
{
|
||||||
|
const int width = this->image.size().width - 1;
|
||||||
|
if (isInt())
|
||||||
|
{
|
||||||
|
for (; x < width &&
|
||||||
|
((p = this->image.at<int>(y, x)) == prev || (p & MASK_VAL) == (prev & MASK_VAL));
|
||||||
|
x++)
|
||||||
|
prev = p;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if (CV_SIMD || CV_SIMD_SCALABLE)
|
||||||
|
if ((p = this->image.at<schar>(y, x)) != prev)
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
v_uint8 v_prev = vx_setall_u8((uchar)prev);
|
||||||
|
for (; x <= width - VTraits<v_uint8>::vlanes(); x += VTraits<v_uint8>::vlanes())
|
||||||
|
{
|
||||||
|
v_uint8 vmask = (v_ne(vx_load(this->image.ptr<uchar>(y, x)), v_prev));
|
||||||
|
if (v_check_any(vmask))
|
||||||
|
{
|
||||||
|
x += v_scan_forward(vmask);
|
||||||
|
p = this->image.at<schar>(y, x);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (; x < width && (p = this->image.at<schar>(y, x)) == prev; x++)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ContourScanner_::findNext()
|
||||||
|
{
|
||||||
|
int x = this->pt.x;
|
||||||
|
int y = this->pt.y;
|
||||||
|
int width = this->image.size().width - 1;
|
||||||
|
int height = this->image.size().height - 1;
|
||||||
|
Point last_pos = this->lnbd;
|
||||||
|
int prev = isInt() ? this->image.at<int>(y, x - 1) : this->image.at<schar>(y, x - 1);
|
||||||
|
|
||||||
|
for (; y < height; y++)
|
||||||
|
{
|
||||||
|
int p = 0;
|
||||||
|
for (; x < width; x++)
|
||||||
|
{
|
||||||
|
x = findNextX(x, y, prev, p);
|
||||||
|
if (x >= width)
|
||||||
|
break;
|
||||||
|
if (contourScan(prev, p, last_pos, x, y))
|
||||||
|
{
|
||||||
|
this->lnbd = last_pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prev = p;
|
||||||
|
if ((isInt() && (prev & MASK_FLAGS)) || (!isInt() && (prev & MASK8_FLAGS)))
|
||||||
|
{
|
||||||
|
last_pos.x = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_pos = Point(0, y + 1);
|
||||||
|
x = 1;
|
||||||
|
prev = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
void cv::findContours(InputArray _image,
|
||||||
|
OutputArrayOfArrays _contours,
|
||||||
|
OutputArray _hierarchy,
|
||||||
|
int mode,
|
||||||
|
int method,
|
||||||
|
Point offset)
|
||||||
|
{
|
||||||
|
CV_INSTRUMENT_REGION();
|
||||||
|
|
||||||
|
// TODO: remove this block in future
|
||||||
|
if (method == 5 /*CV_LINK_RUNS*/)
|
||||||
|
{
|
||||||
|
CV_LOG_ONCE_WARNING(NULL,
|
||||||
|
"LINK_RUNS mode has been extracted to separate function: "
|
||||||
|
"cv::findContoursLinkRuns. "
|
||||||
|
"Calling through cv::findContours will be removed in future.");
|
||||||
|
CV_CheckTrue(!_hierarchy.needed() || mode == RETR_CCOMP,
|
||||||
|
"LINK_RUNS mode supports only simplified hierarchy output (mode=RETR_CCOMP)");
|
||||||
|
findContoursLinkRuns(_image, _contours, _hierarchy);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: need enum value, need way to return contour starting points with chain codes
|
||||||
|
if (method == 0 /*CV_CHAIN_CODE*/)
|
||||||
|
{
|
||||||
|
CV_LOG_ONCE_WARNING(NULL,
|
||||||
|
"Chain code output is an experimental feature and might change in "
|
||||||
|
"future!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check: output must be of type vector<vector<Point>>
|
||||||
|
CV_Assert((_contours.kind() == _InputArray::STD_VECTOR_VECTOR) ||
|
||||||
|
(_contours.kind() == _InputArray::STD_VECTOR_MAT) ||
|
||||||
|
(_contours.kind() == _InputArray::STD_VECTOR_UMAT));
|
||||||
|
|
||||||
|
const int res_type = (method == 0 /*CV_CHAIN_CODE*/) ? CV_8SC1 : CV_32SC2;
|
||||||
|
if (!_contours.empty())
|
||||||
|
{
|
||||||
|
CV_CheckTypeEQ(_contours.type(),
|
||||||
|
res_type,
|
||||||
|
"Contours must have type CV_8SC1 (chain code) or CV_32SC2 (other methods)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_hierarchy.needed())
|
||||||
|
_hierarchy.clear();
|
||||||
|
|
||||||
|
// preprocess
|
||||||
|
Mat image;
|
||||||
|
copyMakeBorder(_image, image, 1, 1, 1, 1, BORDER_CONSTANT | BORDER_ISOLATED, Scalar(0));
|
||||||
|
if (image.type() != CV_32SC1)
|
||||||
|
threshold(image, image, 0, 1, THRESH_BINARY);
|
||||||
|
|
||||||
|
// find contours
|
||||||
|
ContourScanner scanner = ContourScanner_::create(image, mode, method, offset + Point(-1, -1));
|
||||||
|
while (scanner->findNext())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
contourTreeToResults(scanner->tree, res_type, _contours, _hierarchy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cv::findContours(InputArray _image,
|
||||||
|
OutputArrayOfArrays _contours,
|
||||||
|
int mode,
|
||||||
|
int method,
|
||||||
|
Point offset)
|
||||||
|
{
|
||||||
|
CV_INSTRUMENT_REGION();
|
||||||
|
findContours(_image, _contours, noArray(), mode, method, offset);
|
||||||
|
}
|
@ -613,7 +613,7 @@ void cv::createHanningWindow(OutputArray _dst, cv::Size winSize, int type)
|
|||||||
AutoBuffer<double> _wc(cols);
|
AutoBuffer<double> _wc(cols);
|
||||||
double* const wc = _wc.data();
|
double* const wc = _wc.data();
|
||||||
|
|
||||||
double coeff0 = 2.0 * CV_PI / (double)(cols - 1), coeff1 = 2.0f * CV_PI / (double)(rows - 1);
|
double coeff0 = 2.0 * CV_PI / (double)(cols - 1), coeff1 = 2.0 * CV_PI / (double)(rows - 1);
|
||||||
for(int j = 0; j < cols; j++)
|
for(int j = 0; j < cols; j++)
|
||||||
wc[j] = 0.5 * (1.0 - cos(coeff0 * j));
|
wc[j] = 0.5 * (1.0 - cos(coeff0 * j));
|
||||||
|
|
||||||
|
@ -93,7 +93,6 @@ TEST(Imgproc_FindContours, hilbert)
|
|||||||
dilate(img, img, Mat());
|
dilate(img, img, Mat());
|
||||||
vector<vector<Point> > contours;
|
vector<vector<Point> > contours;
|
||||||
findContours(img, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
|
findContours(img, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
|
||||||
printf("ncontours = %d, contour[0].npoints=%d\n", (int)contours.size(), (int)contours[0].size());
|
|
||||||
img.setTo(Scalar::all(0));
|
img.setTo(Scalar::all(0));
|
||||||
|
|
||||||
drawContours(img, contours, 0, Scalar::all(255), 1);
|
drawContours(img, contours, 0, Scalar::all(255), 1);
|
||||||
@ -164,10 +163,12 @@ TEST(Imgproc_FindContours, regression_4363_shared_nbd)
|
|||||||
|
|
||||||
if (found)
|
if (found)
|
||||||
{
|
{
|
||||||
|
ASSERT_EQ(contours.size(), hierarchy.size());
|
||||||
EXPECT_LT(hierarchy[index][3], 0) << "Desired result: (7,9) has no parent - Actual result: parent of (7,9) is another contour. index = " << index;
|
EXPECT_LT(hierarchy[index][3], 0) << "Desired result: (7,9) has no parent - Actual result: parent of (7,9) is another contour. index = " << index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST(Imgproc_PointPolygonTest, regression_10222)
|
TEST(Imgproc_PointPolygonTest, regression_10222)
|
||||||
{
|
{
|
||||||
vector<Point> contour;
|
vector<Point> contour;
|
||||||
|
605
modules/imgproc/test/test_contours_new.cpp
Normal file
605
modules/imgproc/test/test_contours_new.cpp
Normal file
@ -0,0 +1,605 @@
|
|||||||
|
// 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 "opencv2/ts/ocl_test.hpp"
|
||||||
|
#include "opencv2/imgproc/detail/legacy.hpp"
|
||||||
|
|
||||||
|
#define CHECK_OLD 1
|
||||||
|
|
||||||
|
namespace opencv_test { namespace {
|
||||||
|
|
||||||
|
// debug function
|
||||||
|
template <typename T>
|
||||||
|
inline static void print_pts(const T& c)
|
||||||
|
{
|
||||||
|
for (const auto& one_pt : c)
|
||||||
|
{
|
||||||
|
cout << one_pt << " ";
|
||||||
|
}
|
||||||
|
cout << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// debug function
|
||||||
|
template <typename T>
|
||||||
|
inline static void print_pts_2(vector<T>& cs)
|
||||||
|
{
|
||||||
|
int cnt = 0;
|
||||||
|
cout << "Contours:" << endl;
|
||||||
|
for (const auto& one_c : cs)
|
||||||
|
{
|
||||||
|
cout << cnt++ << " : ";
|
||||||
|
print_pts(one_c);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// draw 1-2 px blob with orientation defined by 'kind'
|
||||||
|
template <typename T>
|
||||||
|
inline static void drawSmallContour(Mat& img, Point pt, int kind, int color_)
|
||||||
|
{
|
||||||
|
const T color = static_cast<T>(color_);
|
||||||
|
img.at<T>(pt) = color;
|
||||||
|
switch (kind)
|
||||||
|
{
|
||||||
|
case 1: img.at<T>(pt + Point(1, 0)) = color; break;
|
||||||
|
case 2: img.at<T>(pt + Point(1, -1)) = color; break;
|
||||||
|
case 3: img.at<T>(pt + Point(0, -1)) = color; break;
|
||||||
|
case 4: img.at<T>(pt + Point(-1, -1)) = color; break;
|
||||||
|
case 5: img.at<T>(pt + Point(-1, 0)) = color; break;
|
||||||
|
case 6: img.at<T>(pt + Point(-1, 1)) = color; break;
|
||||||
|
case 7: img.at<T>(pt + Point(0, 1)) = color; break;
|
||||||
|
case 8: img.at<T>(pt + Point(1, 1)) = color; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void drawContours(Mat& img,
|
||||||
|
const vector<vector<Point>>& contours,
|
||||||
|
const Scalar& color = Scalar::all(255))
|
||||||
|
{
|
||||||
|
for (const auto& contour : contours)
|
||||||
|
{
|
||||||
|
for (size_t n = 0, end = contour.size(); n < end; ++n)
|
||||||
|
{
|
||||||
|
size_t m = n + 1;
|
||||||
|
if (n == end - 1)
|
||||||
|
m = 0;
|
||||||
|
line(img, contour[m], contour[n], color, 1, LINE_8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
// Test parameters - mode + method
|
||||||
|
typedef testing::TestWithParam<tuple<int, int>> Imgproc_FindContours_Modes1;
|
||||||
|
|
||||||
|
|
||||||
|
// Draw random rectangle and find contours
|
||||||
|
//
|
||||||
|
TEST_P(Imgproc_FindContours_Modes1, rectangle)
|
||||||
|
{
|
||||||
|
const int mode = get<0>(GetParam());
|
||||||
|
const int method = get<1>(GetParam());
|
||||||
|
|
||||||
|
const size_t ITER = 100;
|
||||||
|
RNG rng = TS::ptr()->get_rng();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ITER; ++i)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(cv::format("i=%zu", i));
|
||||||
|
const Size sz(rng.uniform(640, 1920), rng.uniform(480, 1080));
|
||||||
|
Mat img(sz, CV_8UC1, Scalar::all(0));
|
||||||
|
Mat img32s(sz, CV_32SC1, Scalar::all(0));
|
||||||
|
const Rect r(Point(rng.uniform(1, sz.width / 2 - 1), rng.uniform(1, sz.height / 2)),
|
||||||
|
Point(rng.uniform(sz.width / 2 - 1, sz.width - 1),
|
||||||
|
rng.uniform(sz.height / 2 - 1, sz.height - 1)));
|
||||||
|
rectangle(img, r, Scalar::all(255));
|
||||||
|
rectangle(img32s, r, Scalar::all(255), FILLED);
|
||||||
|
|
||||||
|
const vector<Point> ext_ref {r.tl(),
|
||||||
|
r.tl() + Point(0, r.height - 1),
|
||||||
|
r.br() + Point(-1, -1),
|
||||||
|
r.tl() + Point(r.width - 1, 0)};
|
||||||
|
const vector<Point> int_ref {ext_ref[0] + Point(0, 1),
|
||||||
|
ext_ref[0] + Point(1, 0),
|
||||||
|
ext_ref[3] + Point(-1, 0),
|
||||||
|
ext_ref[3] + Point(0, 1),
|
||||||
|
ext_ref[2] + Point(0, -1),
|
||||||
|
ext_ref[2] + Point(-1, 0),
|
||||||
|
ext_ref[1] + Point(1, 0),
|
||||||
|
ext_ref[1] + Point(0, -1)};
|
||||||
|
const size_t ext_perimeter = r.width * 2 + r.height * 2;
|
||||||
|
const size_t int_perimeter = ext_perimeter - 4;
|
||||||
|
|
||||||
|
vector<vector<Point>> contours;
|
||||||
|
vector<vector<schar>> chains;
|
||||||
|
vector<Vec4i> hierarchy;
|
||||||
|
|
||||||
|
// run functionn
|
||||||
|
if (mode == RETR_FLOODFILL)
|
||||||
|
if (method == 0)
|
||||||
|
findContours(img32s, chains, hierarchy, mode, method);
|
||||||
|
else
|
||||||
|
findContours(img32s, contours, hierarchy, mode, method);
|
||||||
|
else if (method == 0)
|
||||||
|
findContours(img, chains, hierarchy, mode, method);
|
||||||
|
else
|
||||||
|
findContours(img, contours, hierarchy, mode, method);
|
||||||
|
|
||||||
|
// verify results
|
||||||
|
if (mode == RETR_EXTERNAL)
|
||||||
|
{
|
||||||
|
if (method == 0)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(1U, chains.size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT_EQ(1U, contours.size());
|
||||||
|
if (method == CHAIN_APPROX_NONE)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(int_perimeter, contours[0].size());
|
||||||
|
}
|
||||||
|
else if (method == CHAIN_APPROX_SIMPLE)
|
||||||
|
{
|
||||||
|
EXPECT_MAT_NEAR(Mat(ext_ref), Mat(contours[0]), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (method == 0)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(2U, chains.size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ASSERT_EQ(2U, contours.size());
|
||||||
|
if (mode == RETR_LIST)
|
||||||
|
{
|
||||||
|
if (method == CHAIN_APPROX_NONE)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(int_perimeter - 4, contours[0].size());
|
||||||
|
EXPECT_EQ(int_perimeter, contours[1].size());
|
||||||
|
}
|
||||||
|
else if (method == CHAIN_APPROX_SIMPLE)
|
||||||
|
{
|
||||||
|
EXPECT_MAT_NEAR(Mat(int_ref), Mat(contours[0]), 0);
|
||||||
|
EXPECT_MAT_NEAR(Mat(ext_ref), Mat(contours[1]), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mode == RETR_CCOMP || mode == RETR_TREE)
|
||||||
|
{
|
||||||
|
if (method == CHAIN_APPROX_NONE)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(int_perimeter, contours[0].size());
|
||||||
|
EXPECT_EQ(int_perimeter - 4, contours[1].size());
|
||||||
|
}
|
||||||
|
else if (method == CHAIN_APPROX_SIMPLE)
|
||||||
|
{
|
||||||
|
EXPECT_MAT_NEAR(Mat(ext_ref), Mat(contours[0]), 0);
|
||||||
|
EXPECT_MAT_NEAR(Mat(int_ref), Mat(contours[1]), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mode == RETR_FLOODFILL)
|
||||||
|
{
|
||||||
|
if (method == CHAIN_APPROX_NONE)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(int_perimeter + 4, contours[0].size());
|
||||||
|
}
|
||||||
|
else if (method == CHAIN_APPROX_SIMPLE)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(int_ref.size(), contours[0].size());
|
||||||
|
EXPECT_MAT_NEAR(Mat(ext_ref), Mat(contours[1]), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CHECK_OLD
|
||||||
|
if (method != 0) // old doesn't support chain codes
|
||||||
|
{
|
||||||
|
if (mode != RETR_FLOODFILL)
|
||||||
|
{
|
||||||
|
vector<vector<Point>> contours_o;
|
||||||
|
vector<Vec4i> hierarchy_o;
|
||||||
|
findContours_legacy(img, contours_o, hierarchy_o, mode, method);
|
||||||
|
ASSERT_EQ(contours.size(), contours_o.size());
|
||||||
|
for (size_t j = 0; j < contours.size(); ++j)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(format("contour %zu", j));
|
||||||
|
EXPECT_MAT_NEAR(Mat(contours[j]), Mat(contours_o[j]), 0);
|
||||||
|
}
|
||||||
|
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vector<vector<Point>> contours_o;
|
||||||
|
vector<Vec4i> hierarchy_o;
|
||||||
|
findContours_legacy(img32s, contours_o, hierarchy_o, mode, method);
|
||||||
|
ASSERT_EQ(contours.size(), contours_o.size());
|
||||||
|
for (size_t j = 0; j < contours.size(); ++j)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(format("contour %zu", j));
|
||||||
|
EXPECT_MAT_NEAR(Mat(contours[j]), Mat(contours_o[j]), 0);
|
||||||
|
}
|
||||||
|
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Draw many small 1-2px blobs and find contours
|
||||||
|
//
|
||||||
|
TEST_P(Imgproc_FindContours_Modes1, small)
|
||||||
|
{
|
||||||
|
const int mode = get<0>(GetParam());
|
||||||
|
const int method = get<1>(GetParam());
|
||||||
|
|
||||||
|
const size_t DIM = 1000;
|
||||||
|
const Size sz(DIM, DIM);
|
||||||
|
const int num = (DIM / 10) * (DIM / 10); // number of 10x10 squares
|
||||||
|
|
||||||
|
Mat img(sz, CV_8UC1, Scalar::all(0));
|
||||||
|
Mat img32s(sz, CV_32SC1, Scalar::all(0));
|
||||||
|
vector<Point> pts;
|
||||||
|
int extra_contours_32s = 0;
|
||||||
|
for (int j = 0; j < num; ++j)
|
||||||
|
{
|
||||||
|
const int kind = j % 9;
|
||||||
|
Point pt {(j % 100) * 10 + 4, (j / 100) * 10 + 4};
|
||||||
|
drawSmallContour<uchar>(img, pt, kind, 255);
|
||||||
|
drawSmallContour<int>(img32s, pt, kind, j + 1);
|
||||||
|
pts.push_back(pt);
|
||||||
|
// NOTE: for some reason these small diagonal contours (NW, SE)
|
||||||
|
// result in 2 external contours for FLOODFILL mode
|
||||||
|
if (kind == 8 || kind == 4)
|
||||||
|
++extra_contours_32s;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
vector<vector<Point>> contours;
|
||||||
|
vector<vector<schar>> chains;
|
||||||
|
vector<Vec4i> hierarchy;
|
||||||
|
|
||||||
|
if (mode == RETR_FLOODFILL)
|
||||||
|
{
|
||||||
|
if (method == 0)
|
||||||
|
{
|
||||||
|
findContours(img32s, chains, hierarchy, mode, method);
|
||||||
|
ASSERT_EQ(pts.size() * 2 + extra_contours_32s, chains.size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
findContours(img32s, contours, hierarchy, mode, method);
|
||||||
|
ASSERT_EQ(pts.size() * 2 + extra_contours_32s, contours.size());
|
||||||
|
#if CHECK_OLD
|
||||||
|
vector<vector<Point>> contours_o;
|
||||||
|
vector<Vec4i> hierarchy_o;
|
||||||
|
findContours_legacy(img32s, contours_o, hierarchy_o, mode, method);
|
||||||
|
ASSERT_EQ(contours.size(), contours_o.size());
|
||||||
|
for (size_t i = 0; i < contours.size(); ++i)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(format("contour %zu", i));
|
||||||
|
EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
|
||||||
|
}
|
||||||
|
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (method == 0)
|
||||||
|
{
|
||||||
|
findContours(img, chains, hierarchy, mode, method);
|
||||||
|
ASSERT_EQ(pts.size(), chains.size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
findContours(img, contours, hierarchy, mode, method);
|
||||||
|
ASSERT_EQ(pts.size(), contours.size());
|
||||||
|
#if CHECK_OLD
|
||||||
|
vector<vector<Point>> contours_o;
|
||||||
|
vector<Vec4i> hierarchy_o;
|
||||||
|
findContours_legacy(img, contours_o, hierarchy_o, mode, method);
|
||||||
|
ASSERT_EQ(contours.size(), contours_o.size());
|
||||||
|
for (size_t i = 0; i < contours.size(); ++i)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(format("contour %zu", i));
|
||||||
|
EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
|
||||||
|
}
|
||||||
|
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Draw many nested rectangles and find contours
|
||||||
|
//
|
||||||
|
TEST_P(Imgproc_FindContours_Modes1, deep)
|
||||||
|
{
|
||||||
|
const int mode = get<0>(GetParam());
|
||||||
|
const int method = get<1>(GetParam());
|
||||||
|
|
||||||
|
const size_t DIM = 1000;
|
||||||
|
const Size sz(DIM, DIM);
|
||||||
|
const size_t NUM = 249U;
|
||||||
|
Mat img(sz, CV_8UC1, Scalar::all(0));
|
||||||
|
Mat img32s(sz, CV_32SC1, Scalar::all(0));
|
||||||
|
Rect rect(1, 1, 998, 998);
|
||||||
|
for (size_t i = 0; i < NUM; ++i)
|
||||||
|
{
|
||||||
|
rectangle(img, rect, Scalar::all(255));
|
||||||
|
rectangle(img32s, rect, Scalar::all((double)i + 1), FILLED);
|
||||||
|
rect.x += 2;
|
||||||
|
rect.y += 2;
|
||||||
|
rect.width -= 4;
|
||||||
|
rect.height -= 4;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
vector<vector<Point>> contours {{{0, 0}, {1, 1}}};
|
||||||
|
vector<vector<schar>> chains {{1, 2, 3}};
|
||||||
|
vector<Vec4i> hierarchy;
|
||||||
|
|
||||||
|
if (mode == RETR_FLOODFILL)
|
||||||
|
{
|
||||||
|
if (method == 0)
|
||||||
|
{
|
||||||
|
findContours(img32s, chains, hierarchy, mode, method);
|
||||||
|
ASSERT_EQ(2 * NUM, chains.size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
findContours(img32s, contours, hierarchy, mode, method);
|
||||||
|
ASSERT_EQ(2 * NUM, contours.size());
|
||||||
|
#if CHECK_OLD
|
||||||
|
vector<vector<Point>> contours_o;
|
||||||
|
vector<Vec4i> hierarchy_o;
|
||||||
|
findContours_legacy(img32s, contours_o, hierarchy_o, mode, method);
|
||||||
|
ASSERT_EQ(contours.size(), contours_o.size());
|
||||||
|
for (size_t i = 0; i < contours.size(); ++i)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(format("contour %zu", i));
|
||||||
|
EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
|
||||||
|
}
|
||||||
|
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const size_t expected_count = (mode == RETR_EXTERNAL) ? 1U : 2 * NUM;
|
||||||
|
if (method == 0)
|
||||||
|
{
|
||||||
|
findContours(img, chains, hierarchy, mode, method);
|
||||||
|
ASSERT_EQ(expected_count, chains.size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
findContours(img, contours, hierarchy, mode, method);
|
||||||
|
ASSERT_EQ(expected_count, contours.size());
|
||||||
|
#if CHECK_OLD
|
||||||
|
vector<vector<Point>> contours_o;
|
||||||
|
vector<Vec4i> hierarchy_o;
|
||||||
|
findContours_legacy(img, contours_o, hierarchy_o, mode, method);
|
||||||
|
ASSERT_EQ(contours.size(), contours_o.size());
|
||||||
|
for (size_t i = 0; i < contours.size(); ++i)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(format("contour %zu", i));
|
||||||
|
EXPECT_MAT_NEAR(Mat(contours[i]), Mat(contours_o[i]), 0);
|
||||||
|
}
|
||||||
|
EXPECT_MAT_NEAR(Mat(hierarchy), Mat(hierarchy_o), 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(
|
||||||
|
,
|
||||||
|
Imgproc_FindContours_Modes1,
|
||||||
|
testing::Combine(
|
||||||
|
testing::Values(RETR_EXTERNAL, RETR_LIST, RETR_CCOMP, RETR_TREE, RETR_FLOODFILL),
|
||||||
|
testing::Values(0,
|
||||||
|
CHAIN_APPROX_NONE,
|
||||||
|
CHAIN_APPROX_SIMPLE,
|
||||||
|
CHAIN_APPROX_TC89_L1,
|
||||||
|
CHAIN_APPROX_TC89_KCOS)));
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
typedef testing::TestWithParam<tuple<int, int>> Imgproc_FindContours_Modes2;
|
||||||
|
|
||||||
|
// Very approximate backport of an old accuracy test
|
||||||
|
//
|
||||||
|
TEST_P(Imgproc_FindContours_Modes2, new_accuracy)
|
||||||
|
{
|
||||||
|
const int mode = get<0>(GetParam());
|
||||||
|
const int method = get<1>(GetParam());
|
||||||
|
|
||||||
|
RNG& rng = TS::ptr()->get_rng();
|
||||||
|
const int blob_count = rng.uniform(1, 10);
|
||||||
|
const Size sz(rng.uniform(640, 1920), rng.uniform(480, 1080));
|
||||||
|
const int blob_sz = 50;
|
||||||
|
|
||||||
|
// prepare image
|
||||||
|
Mat img(sz, CV_8UC1, Scalar::all(0));
|
||||||
|
vector<RotatedRect> rects;
|
||||||
|
for (int i = 0; i < blob_count; ++i)
|
||||||
|
{
|
||||||
|
const Point2f center((float)rng.uniform(blob_sz, sz.width - blob_sz),
|
||||||
|
(float)rng.uniform(blob_sz, sz.height - blob_sz));
|
||||||
|
const Size2f rsize((float)rng.uniform(1, blob_sz), (float)rng.uniform(1, blob_sz));
|
||||||
|
RotatedRect rect(center, rsize, rng.uniform(0.f, 180.f));
|
||||||
|
rects.push_back(rect);
|
||||||
|
ellipse(img, rect, Scalar::all(100), FILLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw contours manually
|
||||||
|
Mat cont_img(sz, CV_8UC1, Scalar::all(0));
|
||||||
|
for (int y = 1; y < sz.height - 1; ++y)
|
||||||
|
{
|
||||||
|
for (int x = 1; x < sz.width - 1; ++x)
|
||||||
|
{
|
||||||
|
if (img.at<uchar>(y, x) != 0 &&
|
||||||
|
((img.at<uchar>(y - 1, x) == 0) || (img.at<uchar>(y + 1, x) == 0) ||
|
||||||
|
(img.at<uchar>(y, x + 1) == 0) || (img.at<uchar>(y, x - 1) == 0)))
|
||||||
|
{
|
||||||
|
cont_img.at<uchar>(y, x) = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find contours
|
||||||
|
vector<vector<Point>> contours;
|
||||||
|
vector<Vec4i> hierarchy;
|
||||||
|
findContours(img, contours, hierarchy, mode, method);
|
||||||
|
|
||||||
|
// 0 < contours <= rects
|
||||||
|
EXPECT_GT(contours.size(), 0U);
|
||||||
|
EXPECT_GE(rects.size(), contours.size());
|
||||||
|
|
||||||
|
// draw contours
|
||||||
|
Mat res_img(sz, CV_8UC1, Scalar::all(0));
|
||||||
|
drawContours(res_img, contours);
|
||||||
|
|
||||||
|
// compare resulting drawn contours with manually drawn contours
|
||||||
|
const double diff1 = cvtest::norm(cont_img, res_img, NORM_L1) / 255;
|
||||||
|
|
||||||
|
if (method == CHAIN_APPROX_NONE || method == CHAIN_APPROX_SIMPLE)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(0., diff1);
|
||||||
|
}
|
||||||
|
#if CHECK_OLD
|
||||||
|
vector<vector<Point>> contours_o;
|
||||||
|
vector<Vec4i> hierarchy_o;
|
||||||
|
findContours(img, contours_o, hierarchy_o, mode, method);
|
||||||
|
ASSERT_EQ(contours_o.size(), contours.size());
|
||||||
|
for (size_t i = 0; i < contours_o.size(); ++i)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(format("contour = %zu", i));
|
||||||
|
EXPECT_MAT_NEAR(Mat(contours_o[i]), Mat(contours[i]), 0);
|
||||||
|
}
|
||||||
|
EXPECT_MAT_NEAR(Mat(hierarchy_o), Mat(hierarchy), 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(Imgproc_FindContours_Modes2, approx)
|
||||||
|
{
|
||||||
|
const int mode = get<0>(GetParam());
|
||||||
|
const int method = get<1>(GetParam());
|
||||||
|
|
||||||
|
const Size sz {500, 500};
|
||||||
|
Mat img = Mat::zeros(sz, CV_8UC1);
|
||||||
|
|
||||||
|
for (int c = 0; c < 4; ++c)
|
||||||
|
{
|
||||||
|
if (c != 0)
|
||||||
|
{
|
||||||
|
// noise + filter + threshold
|
||||||
|
RNG& rng = TS::ptr()->get_rng();
|
||||||
|
cvtest::randUni(rng, img, 0, 255);
|
||||||
|
|
||||||
|
Mat fimg;
|
||||||
|
boxFilter(img, fimg, CV_8U, Size(5, 5));
|
||||||
|
|
||||||
|
Mat timg;
|
||||||
|
const int level = 44 + c * 42;
|
||||||
|
// 'level' goes through:
|
||||||
|
// 86 - some black speckles on white
|
||||||
|
// 128 - 50/50 black/white
|
||||||
|
// 170 - some white speckles on black
|
||||||
|
cv::threshold(fimg, timg, level, 255, THRESH_BINARY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// circle with cut
|
||||||
|
const Point center {250, 250};
|
||||||
|
const int r {20};
|
||||||
|
const Point cut {r, r};
|
||||||
|
circle(img, center, r, Scalar(255), FILLED);
|
||||||
|
rectangle(img, center, center + cut, Scalar(0), FILLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<vector<Point>> contours;
|
||||||
|
vector<Vec4i> hierarchy;
|
||||||
|
findContours(img, contours, hierarchy, mode, method);
|
||||||
|
|
||||||
|
#if CHECK_OLD
|
||||||
|
vector<vector<Point>> contours_o;
|
||||||
|
vector<Vec4i> hierarchy_o;
|
||||||
|
findContours_legacy(img, contours_o, hierarchy_o, mode, method);
|
||||||
|
ASSERT_EQ(contours_o.size(), contours.size());
|
||||||
|
for (size_t i = 0; i < contours_o.size(); ++i)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(format("c = %d, contour = %zu", c, i));
|
||||||
|
EXPECT_MAT_NEAR(Mat(contours_o[i]), Mat(contours[i]), 0);
|
||||||
|
}
|
||||||
|
EXPECT_MAT_NEAR(Mat(hierarchy_o), Mat(hierarchy), 0);
|
||||||
|
#endif
|
||||||
|
// TODO: check something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: offset test
|
||||||
|
|
||||||
|
// no RETR_FLOODFILL - no CV_32S input images
|
||||||
|
INSTANTIATE_TEST_CASE_P(
|
||||||
|
,
|
||||||
|
Imgproc_FindContours_Modes2,
|
||||||
|
testing::Combine(testing::Values(RETR_EXTERNAL, RETR_LIST, RETR_CCOMP, RETR_TREE),
|
||||||
|
testing::Values(CHAIN_APPROX_NONE,
|
||||||
|
CHAIN_APPROX_SIMPLE,
|
||||||
|
CHAIN_APPROX_TC89_L1,
|
||||||
|
CHAIN_APPROX_TC89_KCOS)));
|
||||||
|
|
||||||
|
TEST(Imgproc_FindContours, link_runs)
|
||||||
|
{
|
||||||
|
const Size sz {500, 500};
|
||||||
|
Mat img = Mat::zeros(sz, CV_8UC1);
|
||||||
|
|
||||||
|
// noise + filter + threshold
|
||||||
|
RNG& rng = TS::ptr()->get_rng();
|
||||||
|
cvtest::randUni(rng, img, 0, 255);
|
||||||
|
|
||||||
|
Mat fimg;
|
||||||
|
boxFilter(img, fimg, CV_8U, Size(5, 5));
|
||||||
|
|
||||||
|
const int level = 135;
|
||||||
|
cv::threshold(fimg, img, level, 255, THRESH_BINARY);
|
||||||
|
|
||||||
|
vector<vector<Point>> contours;
|
||||||
|
vector<Vec4i> hierarchy;
|
||||||
|
findContoursLinkRuns(img, contours, hierarchy);
|
||||||
|
|
||||||
|
if (cvtest::debugLevel >= 10)
|
||||||
|
{
|
||||||
|
print_pts_2(contours);
|
||||||
|
|
||||||
|
Mat res = Mat::zeros(sz, CV_8UC1);
|
||||||
|
drawContours(res, contours);
|
||||||
|
imshow("res", res);
|
||||||
|
imshow("img", img);
|
||||||
|
waitKey(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CHECK_OLD
|
||||||
|
vector<vector<Point>> contours_o;
|
||||||
|
vector<Vec4i> hierarchy_o;
|
||||||
|
findContours_legacy(img, contours_o, hierarchy_o, 0, 5); // CV_LINK_RUNS method
|
||||||
|
ASSERT_EQ(contours_o.size(), contours.size());
|
||||||
|
for (size_t i = 0; i < contours_o.size(); ++i)
|
||||||
|
{
|
||||||
|
SCOPED_TRACE(format("contour = %zu", i));
|
||||||
|
EXPECT_MAT_NEAR(Mat(contours_o[i]), Mat(contours[i]), 0);
|
||||||
|
}
|
||||||
|
EXPECT_MAT_NEAR(Mat(hierarchy_o), Mat(hierarchy), 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}} // namespace opencv_test
|
@ -1105,4 +1105,176 @@ TEST(Imgproc, morphologyEx_small_input_22893)
|
|||||||
ASSERT_EQ(0, cvtest::norm(result, gold, NORM_INF));
|
ASSERT_EQ(0, cvtest::norm(result, gold, NORM_INF));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Imgproc_sepFilter2D, identity)
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> kernelX{0, 0, 0, 1, 0, 0, 0};
|
||||||
|
std::vector<uint8_t> kernelY{0, 0, 1, 0, 0};
|
||||||
|
|
||||||
|
const string input_path = cvtest::findDataFile("../cv/shared/baboon.png");
|
||||||
|
Mat input = imread(input_path, IMREAD_GRAYSCALE);
|
||||||
|
Mat result;
|
||||||
|
|
||||||
|
cv::sepFilter2D(input, result, input.depth(), kernelX, kernelY);
|
||||||
|
|
||||||
|
EXPECT_EQ(0, cv::norm(result, input, NORM_INF));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Imgproc_sepFilter2D, shift)
|
||||||
|
{
|
||||||
|
std::vector<float> kernelX{1, 0, 0};
|
||||||
|
std::vector<float> kernelY{0, 0, 1};
|
||||||
|
|
||||||
|
const string input_path = cvtest::findDataFile("../cv/shared/baboon.png");
|
||||||
|
Mat input = imread(input_path, IMREAD_GRAYSCALE);
|
||||||
|
Mat result;
|
||||||
|
|
||||||
|
cv::sepFilter2D(input, result, input.depth(), kernelX, kernelY);
|
||||||
|
|
||||||
|
int W = input.cols;
|
||||||
|
int H = input.rows;
|
||||||
|
Mat inputCrop = input(Range(1, H), Range(0, W - 1));
|
||||||
|
Mat resultCrop = result(Range(0, H - 1), Range(1, W));
|
||||||
|
EXPECT_EQ(0, cv::norm(resultCrop, inputCrop, NORM_INF));
|
||||||
|
|
||||||
|
// Checking borders. Should be BORDER_REFLECT_101
|
||||||
|
|
||||||
|
inputCrop = input(Range(H - 2, H - 1), Range(0, W - 1));
|
||||||
|
resultCrop = result(Range(H - 1, H), Range(1, W));
|
||||||
|
EXPECT_EQ(0, cv::norm(resultCrop, inputCrop, NORM_INF));
|
||||||
|
|
||||||
|
inputCrop = input(Range(1, H), Range(1, 2));
|
||||||
|
resultCrop = result(Range(0, H - 1), Range(0, 1));
|
||||||
|
EXPECT_EQ(0, cv::norm(resultCrop, inputCrop, NORM_INF));
|
||||||
|
|
||||||
|
inputCrop = input(Range(H - 2, H - 1), Range(1, 2));
|
||||||
|
resultCrop = result(Range(H - 1, H), Range(0, 1));
|
||||||
|
EXPECT_EQ(0, cv::norm(resultCrop, inputCrop, NORM_INF));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Imgproc_sepFilter2D, zeroPadding)
|
||||||
|
{
|
||||||
|
std::vector<int> kernelX{1, 0, 0};
|
||||||
|
std::vector<int> kernelY{0, 0, 1};
|
||||||
|
Point anchor(-1, -1);
|
||||||
|
double delta = 0;
|
||||||
|
|
||||||
|
const string input_path = cvtest::findDataFile("../cv/shared/baboon.png");
|
||||||
|
Mat input = imread(input_path, IMREAD_GRAYSCALE);
|
||||||
|
Mat result;
|
||||||
|
|
||||||
|
cv::sepFilter2D(input, result, input.depth(), kernelX, kernelY, anchor, delta, BORDER_CONSTANT);
|
||||||
|
|
||||||
|
int W = input.cols;
|
||||||
|
int H = input.rows;
|
||||||
|
Mat inputCrop = input(Range(1, H), Range(0, W - 1));
|
||||||
|
Mat resultCrop = result(Range(0, H - 1), Range(1, W));
|
||||||
|
EXPECT_EQ(0, cv::norm(resultCrop, inputCrop, NORM_INF));
|
||||||
|
|
||||||
|
// Checking borders
|
||||||
|
|
||||||
|
resultCrop = result(Range(H - 1, H), Range(0, W));
|
||||||
|
EXPECT_EQ(0, cv::norm(resultCrop, NORM_INF));
|
||||||
|
|
||||||
|
resultCrop = result(Range(0, H), Range(0, 1));
|
||||||
|
EXPECT_EQ(0, cv::norm(resultCrop, NORM_INF));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Imgproc_sepFilter2D, anchor)
|
||||||
|
{
|
||||||
|
std::vector<float> kernelX{0, 1, 0};
|
||||||
|
std::vector<float> kernelY{0, 1, 0};
|
||||||
|
Point anchor(2, 0);
|
||||||
|
|
||||||
|
const string input_path = cvtest::findDataFile("../cv/shared/baboon.png");
|
||||||
|
Mat input = imread(input_path, IMREAD_GRAYSCALE);
|
||||||
|
Mat result;
|
||||||
|
|
||||||
|
cv::sepFilter2D(input, result, input.depth(), kernelX, kernelY, anchor);
|
||||||
|
|
||||||
|
int W = input.cols;
|
||||||
|
int H = input.rows;
|
||||||
|
Mat inputCrop = input(Range(1, H), Range(0, W - 1));
|
||||||
|
Mat resultCrop = result(Range(0, H - 1), Range(1, W));
|
||||||
|
EXPECT_EQ(0, cv::norm(resultCrop, inputCrop, NORM_INF));
|
||||||
|
|
||||||
|
// Checking borders. Should be BORDER_REFLECT_101
|
||||||
|
|
||||||
|
inputCrop = input(Range(H - 2, H - 1), Range(0, W - 1));
|
||||||
|
resultCrop = result(Range(H - 1, H), Range(1, W));
|
||||||
|
EXPECT_EQ(0, cv::norm(resultCrop, inputCrop, NORM_INF));
|
||||||
|
|
||||||
|
inputCrop = input(Range(1, H), Range(1, 2));
|
||||||
|
resultCrop = result(Range(0, H - 1), Range(0, 1));
|
||||||
|
EXPECT_EQ(0, cv::norm(resultCrop, inputCrop, NORM_INF));
|
||||||
|
|
||||||
|
inputCrop = input(Range(H - 2, H - 1), Range(1, 2));
|
||||||
|
resultCrop = result(Range(H - 1, H), Range(0, 1));
|
||||||
|
EXPECT_EQ(0, cv::norm(resultCrop, inputCrop, NORM_INF));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Imgproc_sepFilter2D, delta)
|
||||||
|
{
|
||||||
|
std::vector<float> kernelX{0, 0.5, 0};
|
||||||
|
std::vector<float> kernelY{0, 1, 0};
|
||||||
|
Point anchor(1, 1);
|
||||||
|
double delta = 5;
|
||||||
|
|
||||||
|
const string input_path = cvtest::findDataFile("../cv/shared/baboon.png");
|
||||||
|
Mat input = imread(input_path, IMREAD_GRAYSCALE);
|
||||||
|
Mat result;
|
||||||
|
|
||||||
|
cv::sepFilter2D(input, result, input.depth(), kernelX, kernelY, anchor, delta);
|
||||||
|
|
||||||
|
Mat gt = input / 2 + delta;
|
||||||
|
EXPECT_EQ(0, cv::norm(result, gt, NORM_INF));
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef testing::TestWithParam<int> Imgproc_sepFilter2D_outTypes;
|
||||||
|
TEST_P(Imgproc_sepFilter2D_outTypes, simple)
|
||||||
|
{
|
||||||
|
int outputType = GetParam();
|
||||||
|
std::vector<float> kernelX{0, 0.5, 0};
|
||||||
|
std::vector<float> kernelY{0, 0.5, 0};
|
||||||
|
Point anchor(1, 1);
|
||||||
|
double delta = 5;
|
||||||
|
|
||||||
|
const string input_path = cvtest::findDataFile("../cv/shared/baboon.png");
|
||||||
|
Mat input = imread(input_path, IMREAD_GRAYSCALE);
|
||||||
|
Mat result;
|
||||||
|
|
||||||
|
cv::sepFilter2D(input, result, outputType, kernelX, kernelY, anchor, delta);
|
||||||
|
|
||||||
|
input.convertTo(input, outputType);
|
||||||
|
Mat gt = input / 4 + delta;
|
||||||
|
EXPECT_EQ(0, cv::norm(result, gt, NORM_INF));
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(/**/, Imgproc_sepFilter2D_outTypes,
|
||||||
|
testing::Values(CV_16S, CV_32F, CV_64F),
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef testing::TestWithParam<int> Imgproc_sepFilter2D_types;
|
||||||
|
TEST_P(Imgproc_sepFilter2D_types, simple)
|
||||||
|
{
|
||||||
|
int outputType = GetParam();
|
||||||
|
std::vector<float> kernelX{0, 0.5, 0};
|
||||||
|
std::vector<float> kernelY{0, 0.5, 0};
|
||||||
|
Point anchor(1, 1);
|
||||||
|
double delta = 5;
|
||||||
|
|
||||||
|
const string input_path = cvtest::findDataFile("../cv/shared/baboon.png");
|
||||||
|
Mat input = imread(input_path, IMREAD_GRAYSCALE);
|
||||||
|
input.convertTo(input, outputType);
|
||||||
|
Mat result;
|
||||||
|
|
||||||
|
cv::sepFilter2D(input, result, outputType, kernelX, kernelY, anchor, delta);
|
||||||
|
|
||||||
|
Mat gt = input / 4 + delta;
|
||||||
|
EXPECT_EQ(0, cv::norm(result, gt, NORM_INF));
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(/**/, Imgproc_sepFilter2D_types,
|
||||||
|
testing::Values(CV_16S, CV_32F, CV_64F),
|
||||||
|
);
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
@ -96,4 +96,58 @@ TEST(Imgproc_Threshold, regression_THRESH_TOZERO_IPP_21258_Max)
|
|||||||
EXPECT_EQ(0, cv::norm(result, NORM_INF));
|
EXPECT_EQ(0, cv::norm(result, NORM_INF));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Imgproc_AdaptiveThreshold, mean)
|
||||||
|
{
|
||||||
|
const string input_path = cvtest::findDataFile("../cv/shared/baboon.png");
|
||||||
|
Mat input = imread(input_path, IMREAD_GRAYSCALE);
|
||||||
|
Mat result;
|
||||||
|
|
||||||
|
cv::adaptiveThreshold(input, result, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, 8);
|
||||||
|
|
||||||
|
const string gt_path = cvtest::findDataFile("../cv/imgproc/adaptive_threshold1.png");
|
||||||
|
Mat gt = imread(gt_path, IMREAD_GRAYSCALE);
|
||||||
|
EXPECT_EQ(0, cv::norm(result, gt, NORM_INF));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Imgproc_AdaptiveThreshold, mean_inv)
|
||||||
|
{
|
||||||
|
const string input_path = cvtest::findDataFile("../cv/shared/baboon.png");
|
||||||
|
Mat input = imread(input_path, IMREAD_GRAYSCALE);
|
||||||
|
Mat result;
|
||||||
|
|
||||||
|
cv::adaptiveThreshold(input, result, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY_INV, 15, 8);
|
||||||
|
|
||||||
|
const string gt_path = cvtest::findDataFile("../cv/imgproc/adaptive_threshold1.png");
|
||||||
|
Mat gt = imread(gt_path, IMREAD_GRAYSCALE);
|
||||||
|
gt = Mat(gt.rows, gt.cols, CV_8UC1, cv::Scalar(255)) - gt;
|
||||||
|
EXPECT_EQ(0, cv::norm(result, gt, NORM_INF));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Imgproc_AdaptiveThreshold, gauss)
|
||||||
|
{
|
||||||
|
const string input_path = cvtest::findDataFile("../cv/shared/baboon.png");
|
||||||
|
Mat input = imread(input_path, IMREAD_GRAYSCALE);
|
||||||
|
Mat result;
|
||||||
|
|
||||||
|
cv::adaptiveThreshold(input, result, 200, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 21, -5);
|
||||||
|
|
||||||
|
const string gt_path = cvtest::findDataFile("../cv/imgproc/adaptive_threshold2.png");
|
||||||
|
Mat gt = imread(gt_path, IMREAD_GRAYSCALE);
|
||||||
|
EXPECT_EQ(0, cv::norm(result, gt, NORM_INF));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Imgproc_AdaptiveThreshold, gauss_inv)
|
||||||
|
{
|
||||||
|
const string input_path = cvtest::findDataFile("../cv/shared/baboon.png");
|
||||||
|
Mat input = imread(input_path, IMREAD_GRAYSCALE);
|
||||||
|
Mat result;
|
||||||
|
|
||||||
|
cv::adaptiveThreshold(input, result, 200, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 21, -5);
|
||||||
|
|
||||||
|
const string gt_path = cvtest::findDataFile("../cv/imgproc/adaptive_threshold2.png");
|
||||||
|
Mat gt = imread(gt_path, IMREAD_GRAYSCALE);
|
||||||
|
gt = Mat(gt.rows, gt.cols, CV_8UC1, cv::Scalar(200)) - gt;
|
||||||
|
EXPECT_EQ(0, cv::norm(result, gt, NORM_INF));
|
||||||
|
}
|
||||||
|
|
||||||
}} // namespace
|
}} // namespace
|
||||||
|
@ -2937,7 +2937,8 @@ QRDecode::QRDecode(bool _useAlignmentMarkers):
|
|||||||
useAlignmentMarkers(_useAlignmentMarkers),
|
useAlignmentMarkers(_useAlignmentMarkers),
|
||||||
version(0),
|
version(0),
|
||||||
version_size(0),
|
version_size(0),
|
||||||
test_perspective_size(0.f)
|
test_perspective_size(0.f),
|
||||||
|
mode(QRCodeEncoder::EncodeMode::MODE_AUTO)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::string ImplContour::decode(InputArray in, InputArray points, OutputArray straight_qrcode) const {
|
std::string ImplContour::decode(InputArray in, InputArray points, OutputArray straight_qrcode) const {
|
||||||
|
@ -1321,262 +1321,6 @@ yuv411p_to_rgb24(int width, int height,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* BAYER2RGB24 ROUTINE TAKEN FROM:
|
|
||||||
*
|
|
||||||
* Sonix SN9C10x based webcam basic I/F routines
|
|
||||||
* Takafumi Mizuno <taka-qce@ls-a.jp>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static void bayer2rgb24(long int WIDTH, long int HEIGHT, unsigned char *src, unsigned char *dst)
|
|
||||||
{
|
|
||||||
long int i;
|
|
||||||
unsigned char *rawpt, *scanpt;
|
|
||||||
long int size;
|
|
||||||
|
|
||||||
rawpt = src;
|
|
||||||
scanpt = dst;
|
|
||||||
size = WIDTH*HEIGHT;
|
|
||||||
|
|
||||||
for ( i = 0; i < size; i++ ) {
|
|
||||||
if ( (i/WIDTH) % 2 == 0 ) {
|
|
||||||
if ( (i % 2) == 0 ) {
|
|
||||||
/* B */
|
|
||||||
if ( (i > WIDTH) && ((i % WIDTH) > 0) ) {
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+
|
|
||||||
*(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* R */
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+1)+
|
|
||||||
*(rawpt+WIDTH)+*(rawpt-WIDTH))/4; /* G */
|
|
||||||
*scanpt++ = *rawpt; /* B */
|
|
||||||
} else {
|
|
||||||
/* first line or left column */
|
|
||||||
*scanpt++ = *(rawpt+WIDTH+1); /* R */
|
|
||||||
*scanpt++ = (*(rawpt+1)+*(rawpt+WIDTH))/2; /* G */
|
|
||||||
*scanpt++ = *rawpt; /* B */
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* (B)G */
|
|
||||||
if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) ) {
|
|
||||||
*scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* R */
|
|
||||||
*scanpt++ = *rawpt; /* G */
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* B */
|
|
||||||
} else {
|
|
||||||
/* first line or right column */
|
|
||||||
*scanpt++ = *(rawpt+WIDTH); /* R */
|
|
||||||
*scanpt++ = *rawpt; /* G */
|
|
||||||
*scanpt++ = *(rawpt-1); /* B */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ( (i % 2) == 0 ) {
|
|
||||||
/* G(R) */
|
|
||||||
if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) ) {
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* R */
|
|
||||||
*scanpt++ = *rawpt; /* G */
|
|
||||||
*scanpt++ = (*(rawpt+WIDTH)+*(rawpt-WIDTH))/2; /* B */
|
|
||||||
} else {
|
|
||||||
/* bottom line or left column */
|
|
||||||
*scanpt++ = *(rawpt+1); /* R */
|
|
||||||
*scanpt++ = *rawpt; /* G */
|
|
||||||
*scanpt++ = *(rawpt-WIDTH); /* B */
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* R */
|
|
||||||
if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) ) {
|
|
||||||
*scanpt++ = *rawpt; /* R */
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+1)+
|
|
||||||
*(rawpt-WIDTH)+*(rawpt+WIDTH))/4; /* G */
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+
|
|
||||||
*(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* B */
|
|
||||||
} else {
|
|
||||||
/* bottom line or right column */
|
|
||||||
*scanpt++ = *rawpt; /* R */
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt-WIDTH))/2; /* G */
|
|
||||||
*scanpt++ = *(rawpt-WIDTH-1); /* B */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rawpt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// SGBRG to RGB24
|
|
||||||
// for some reason, red and blue needs to be swapped
|
|
||||||
// at least for 046d:092f Logitech, Inc. QuickCam Express Plus to work
|
|
||||||
//see: http://www.siliconimaging.com/RGB%20Bayer.htm
|
|
||||||
//and 4.6 at http://tldp.org/HOWTO/html_single/libdc1394-HOWTO/
|
|
||||||
static void sgbrg2rgb24(long int WIDTH, long int HEIGHT, unsigned char *src, unsigned char *dst)
|
|
||||||
{
|
|
||||||
long int i;
|
|
||||||
unsigned char *rawpt, *scanpt;
|
|
||||||
long int size;
|
|
||||||
|
|
||||||
rawpt = src;
|
|
||||||
scanpt = dst;
|
|
||||||
size = WIDTH*HEIGHT;
|
|
||||||
|
|
||||||
for ( i = 0; i < size; i++ )
|
|
||||||
{
|
|
||||||
if ( (i/WIDTH) % 2 == 0 ) //even row
|
|
||||||
{
|
|
||||||
if ( (i % 2) == 0 ) //even pixel
|
|
||||||
{
|
|
||||||
if ( (i > WIDTH) && ((i % WIDTH) > 0) )
|
|
||||||
{
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* R */
|
|
||||||
*scanpt++ = *(rawpt); /* G */
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH) + *(rawpt+WIDTH))/2; /* B */
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
/* first line or left column */
|
|
||||||
|
|
||||||
*scanpt++ = *(rawpt+1); /* R */
|
|
||||||
*scanpt++ = *(rawpt); /* G */
|
|
||||||
*scanpt++ = *(rawpt+WIDTH); /* B */
|
|
||||||
}
|
|
||||||
} else //odd pixel
|
|
||||||
{
|
|
||||||
if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) )
|
|
||||||
{
|
|
||||||
*scanpt++ = *(rawpt); /* R */
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+1)+*(rawpt-WIDTH)+*(rawpt+WIDTH))/4; /* G */
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH-1) + *(rawpt-WIDTH+1) + *(rawpt+WIDTH-1) + *(rawpt+WIDTH+1))/4; /* B */
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
/* first line or right column */
|
|
||||||
|
|
||||||
*scanpt++ = *(rawpt); /* R */
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+WIDTH))/2; /* G */
|
|
||||||
*scanpt++ = *(rawpt+WIDTH-1); /* B */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{ //odd row
|
|
||||||
if ( (i % 2) == 0 ) //even pixel
|
|
||||||
{
|
|
||||||
if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) )
|
|
||||||
{
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH-1)+*(rawpt-WIDTH+1)+*(rawpt+WIDTH-1)+*(rawpt+WIDTH+1))/4; /* R */
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+1)+*(rawpt-WIDTH)+*(rawpt+WIDTH))/4; /* G */
|
|
||||||
*scanpt++ = *(rawpt); /* B */
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
/* bottom line or left column */
|
|
||||||
|
|
||||||
*scanpt++ = *(rawpt-WIDTH+1); /* R */
|
|
||||||
*scanpt++ = (*(rawpt+1)+*(rawpt-WIDTH))/2; /* G */
|
|
||||||
*scanpt++ = *(rawpt); /* B */
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{ //odd pixel
|
|
||||||
if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) )
|
|
||||||
{
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH)+*(rawpt+WIDTH))/2; /* R */
|
|
||||||
*scanpt++ = *(rawpt); /* G */
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* B */
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
/* bottom line or right column */
|
|
||||||
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH)); /* R */
|
|
||||||
*scanpt++ = *(rawpt); /* G */
|
|
||||||
*scanpt++ = (*(rawpt-1)); /* B */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rawpt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SGRBG to RGB24
|
|
||||||
static void sgrbg2rgb24(long int WIDTH, long int HEIGHT, unsigned char *src, unsigned char *dst)
|
|
||||||
{
|
|
||||||
long int i;
|
|
||||||
unsigned char *rawpt, *scanpt;
|
|
||||||
long int size;
|
|
||||||
|
|
||||||
rawpt = src;
|
|
||||||
scanpt = dst;
|
|
||||||
size = WIDTH*HEIGHT;
|
|
||||||
|
|
||||||
for ( i = 0; i < size; i++ )
|
|
||||||
{
|
|
||||||
if ( (i/WIDTH) % 2 == 0 ) //even row
|
|
||||||
{
|
|
||||||
if ( (i % 2) == 0 ) //even pixel
|
|
||||||
{
|
|
||||||
if ( (i > WIDTH) && ((i % WIDTH) > 0) )
|
|
||||||
{
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH) + *(rawpt+WIDTH))/2; /* R */
|
|
||||||
*scanpt++ = *(rawpt); /* G */
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* B */
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
/* first line or left column */
|
|
||||||
|
|
||||||
*scanpt++ = *(rawpt+WIDTH); /* R */
|
|
||||||
*scanpt++ = *(rawpt); /* G */
|
|
||||||
*scanpt++ = *(rawpt+1); /* B */
|
|
||||||
}
|
|
||||||
} else //odd pixel
|
|
||||||
{
|
|
||||||
if ( (i > WIDTH) && ((i % WIDTH) < (WIDTH-1)) )
|
|
||||||
{
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH-1) + *(rawpt-WIDTH+1) +
|
|
||||||
*(rawpt+WIDTH-1) + *(rawpt+WIDTH+1)) / 4; /* R */
|
|
||||||
*scanpt++ = (*(rawpt-1) + *(rawpt+1) +
|
|
||||||
*(rawpt-WIDTH) + *(rawpt+WIDTH)) / 4; /* G */
|
|
||||||
*scanpt++ = *(rawpt); /* B */
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
/* first line or right column */
|
|
||||||
|
|
||||||
*scanpt++ = *(rawpt+WIDTH-1); /* R */
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+WIDTH))/2; /* G */
|
|
||||||
*scanpt++ = *(rawpt); /* B */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{ //odd row
|
|
||||||
if ( (i % 2) == 0 ) //even pixel
|
|
||||||
{
|
|
||||||
if ( (i < (WIDTH*(HEIGHT-1))) && ((i % WIDTH) > 0) )
|
|
||||||
{
|
|
||||||
*scanpt++ = *(rawpt); /* R */
|
|
||||||
*scanpt++ = (*(rawpt-1) + *(rawpt+1)+
|
|
||||||
*(rawpt-WIDTH) + *(rawpt+WIDTH)) / 4; /* G */
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH-1) + *(rawpt-WIDTH+1) +
|
|
||||||
*(rawpt+WIDTH-1) + *(rawpt+WIDTH+1)) / 4; /* B */
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
/* bottom line or left column */
|
|
||||||
|
|
||||||
*scanpt++ = *(rawpt); /* R */
|
|
||||||
*scanpt++ = (*(rawpt+1)+*(rawpt-WIDTH))/2; /* G */
|
|
||||||
*scanpt++ = *(rawpt-WIDTH+1); /* B */
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
{ //odd pixel
|
|
||||||
if ( i < (WIDTH*(HEIGHT-1)) && ((i % WIDTH) < (WIDTH-1)) )
|
|
||||||
{
|
|
||||||
*scanpt++ = (*(rawpt-1)+*(rawpt+1))/2; /* R */
|
|
||||||
*scanpt++ = *(rawpt); /* G */
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH)+*(rawpt+WIDTH))/2; /* B */
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
/* bottom line or right column */
|
|
||||||
|
|
||||||
*scanpt++ = (*(rawpt-1)); /* R */
|
|
||||||
*scanpt++ = *(rawpt); /* G */
|
|
||||||
*scanpt++ = (*(rawpt-WIDTH)); /* B */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rawpt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CLAMP(x) ((x)<0?0:((x)>255)?255:(x))
|
#define CLAMP(x) ((x)<0?0:((x)>255)?255:(x))
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -1778,28 +1522,6 @@ void CvCaptureCAM_V4L::convertToRgb(const Buffer ¤tBuffer)
|
|||||||
yuv411p_to_rgb24(imageSize.width, imageSize.height,
|
yuv411p_to_rgb24(imageSize.width, imageSize.height,
|
||||||
start, (unsigned char*)frame.imageData);
|
start, (unsigned char*)frame.imageData);
|
||||||
return;
|
return;
|
||||||
case V4L2_PIX_FMT_SBGGR8:
|
|
||||||
bayer2rgb24(imageSize.width, imageSize.height,
|
|
||||||
start, (unsigned char*)frame.imageData);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case V4L2_PIX_FMT_SN9C10X:
|
|
||||||
sonix_decompress_init();
|
|
||||||
sonix_decompress(imageSize.width, imageSize.height,
|
|
||||||
start, (unsigned char*)buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start);
|
|
||||||
|
|
||||||
bayer2rgb24(imageSize.width, imageSize.height,
|
|
||||||
(unsigned char*)buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start,
|
|
||||||
(unsigned char*)frame.imageData);
|
|
||||||
return;
|
|
||||||
case V4L2_PIX_FMT_SGBRG8:
|
|
||||||
sgbrg2rgb24(imageSize.width, imageSize.height,
|
|
||||||
start, (unsigned char*)frame.imageData);
|
|
||||||
return;
|
|
||||||
case V4L2_PIX_FMT_SGRBG8:
|
|
||||||
sgrbg2rgb24(imageSize.width, imageSize.height,
|
|
||||||
start, (unsigned char*)frame.imageData);
|
|
||||||
return;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1872,6 +1594,36 @@ void CvCaptureCAM_V4L::convertToRgb(const Buffer ¤tBuffer)
|
|||||||
cv::cvtColor(temp, destination, COLOR_GRAY2BGR);
|
cv::cvtColor(temp, destination, COLOR_GRAY2BGR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case V4L2_PIX_FMT_SN9C10X:
|
||||||
|
{
|
||||||
|
sonix_decompress_init();
|
||||||
|
sonix_decompress(imageSize.width, imageSize.height,
|
||||||
|
start, (unsigned char*)buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start);
|
||||||
|
|
||||||
|
cv::Mat cv_buf(imageSize, CV_8UC1, buffers[MAX_V4L_BUFFERS].memories[MEMORY_RGB].start);
|
||||||
|
cv::cvtColor(cv_buf, destination, COLOR_BayerRG2BGR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case V4L2_PIX_FMT_SRGGB8:
|
||||||
|
{
|
||||||
|
cv::cvtColor(cv::Mat(imageSize, CV_8UC1, start), destination, COLOR_BayerBG2BGR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case V4L2_PIX_FMT_SBGGR8:
|
||||||
|
{
|
||||||
|
cv::cvtColor(cv::Mat(imageSize, CV_8UC1, start), destination, COLOR_BayerRG2BGR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case V4L2_PIX_FMT_SGBRG8:
|
||||||
|
{
|
||||||
|
cv::cvtColor(cv::Mat(imageSize, CV_8UC1, start), destination, COLOR_BayerGR2BGR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case V4L2_PIX_FMT_SGRBG8:
|
||||||
|
{
|
||||||
|
cv::cvtColor(cv::Mat(imageSize, CV_8UC1, start), destination, COLOR_BayerGB2BGR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
case V4L2_PIX_FMT_GREY:
|
case V4L2_PIX_FMT_GREY:
|
||||||
cv::cvtColor(cv::Mat(imageSize, CV_8UC1, start), destination, COLOR_GRAY2BGR);
|
cv::cvtColor(cv::Mat(imageSize, CV_8UC1, start), destination, COLOR_GRAY2BGR);
|
||||||
break;
|
break;
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
|
|
||||||
#ifdef HAVE_CAMV4L2
|
#ifdef HAVE_CAMV4L2
|
||||||
|
|
||||||
|
// #define DUMP_CAMERA_FRAME
|
||||||
|
|
||||||
#include "test_precomp.hpp"
|
#include "test_precomp.hpp"
|
||||||
#include <opencv2/core/utils/configuration.private.hpp>
|
#include <opencv2/core/utils/configuration.private.hpp>
|
||||||
#include <linux/videodev2.h>
|
#include <linux/videodev2.h>
|
||||||
@ -70,6 +72,7 @@ TEST_P(videoio_v4l2, formats)
|
|||||||
const string device = devs[0];
|
const string device = devs[0];
|
||||||
const Size sz(640, 480);
|
const Size sz(640, 480);
|
||||||
const Format_Channels_Depth params = GetParam();
|
const Format_Channels_Depth params = GetParam();
|
||||||
|
const Size esz(sz.width * params.mul_width, sz.height * params.mul_height);
|
||||||
|
|
||||||
{
|
{
|
||||||
// Case with RAW output
|
// Case with RAW output
|
||||||
@ -83,7 +86,17 @@ TEST_P(videoio_v4l2, formats)
|
|||||||
Mat img;
|
Mat img;
|
||||||
EXPECT_TRUE(cap.grab());
|
EXPECT_TRUE(cap.grab());
|
||||||
EXPECT_TRUE(cap.retrieve(img));
|
EXPECT_TRUE(cap.retrieve(img));
|
||||||
EXPECT_EQ(Size(sz.width * params.mul_width, sz.height * params.mul_height), img.size());
|
if (params.pixel_format == V4L2_PIX_FMT_SRGGB8 ||
|
||||||
|
params.pixel_format == V4L2_PIX_FMT_SBGGR8 ||
|
||||||
|
params.pixel_format == V4L2_PIX_FMT_SGBRG8 ||
|
||||||
|
params.pixel_format == V4L2_PIX_FMT_SGRBG8)
|
||||||
|
{
|
||||||
|
EXPECT_EQ((size_t)esz.area(), img.total());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EXPECT_EQ(esz, img.size());
|
||||||
|
}
|
||||||
EXPECT_EQ(params.channels, img.channels());
|
EXPECT_EQ(params.channels, img.channels());
|
||||||
EXPECT_EQ(params.depth, img.depth());
|
EXPECT_EQ(params.depth, img.depth());
|
||||||
}
|
}
|
||||||
@ -102,6 +115,13 @@ TEST_P(videoio_v4l2, formats)
|
|||||||
EXPECT_EQ(sz, img.size());
|
EXPECT_EQ(sz, img.size());
|
||||||
EXPECT_EQ(3, img.channels());
|
EXPECT_EQ(3, img.channels());
|
||||||
EXPECT_EQ(CV_8U, img.depth());
|
EXPECT_EQ(CV_8U, img.depth());
|
||||||
|
#ifdef DUMP_CAMERA_FRAME
|
||||||
|
std::string img_name = "frame_" + fourccToString(params.pixel_format);
|
||||||
|
// V4L2 flag for big-endian formats
|
||||||
|
if(params.pixel_format & (1 << 31))
|
||||||
|
img_name += "-BE";
|
||||||
|
cv::imwrite(img_name + ".png", img);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,9 +136,11 @@ vector<Format_Channels_Depth> all_params = {
|
|||||||
// { V4L2_PIX_FMT_JPEG, 1, CV_8U, 1.f, 1.f },
|
// { V4L2_PIX_FMT_JPEG, 1, CV_8U, 1.f, 1.f },
|
||||||
{ V4L2_PIX_FMT_YUYV, 2, CV_8U, 1.f, 1.f },
|
{ V4L2_PIX_FMT_YUYV, 2, CV_8U, 1.f, 1.f },
|
||||||
{ V4L2_PIX_FMT_UYVY, 2, CV_8U, 1.f, 1.f },
|
{ V4L2_PIX_FMT_UYVY, 2, CV_8U, 1.f, 1.f },
|
||||||
// { V4L2_PIX_FMT_SBGGR8, 1, CV_8U, 1.f, 1.f },
|
{ V4L2_PIX_FMT_SN9C10X, 3, CV_8U, 1.f, 1.f },
|
||||||
// { V4L2_PIX_FMT_SN9C10X, 3, CV_8U, 1.f, 1.f },
|
{ V4L2_PIX_FMT_SRGGB8, 1, CV_8U, 1.f, 1.f },
|
||||||
// { V4L2_PIX_FMT_SGBRG8, 1, CV_8U, 1.f, 1.f },
|
{ V4L2_PIX_FMT_SBGGR8, 1, CV_8U, 1.f, 1.f },
|
||||||
|
{ V4L2_PIX_FMT_SGBRG8, 1, CV_8U, 1.f, 1.f },
|
||||||
|
{ V4L2_PIX_FMT_SGRBG8, 1, CV_8U, 1.f, 1.f },
|
||||||
{ V4L2_PIX_FMT_RGB24, 3, CV_8U, 1.f, 1.f },
|
{ V4L2_PIX_FMT_RGB24, 3, CV_8U, 1.f, 1.f },
|
||||||
{ V4L2_PIX_FMT_Y16, 1, CV_16U, 1.f, 1.f },
|
{ V4L2_PIX_FMT_Y16, 1, CV_16U, 1.f, 1.f },
|
||||||
{ V4L2_PIX_FMT_Y16_BE, 1, CV_16U, 1.f, 1.f },
|
{ V4L2_PIX_FMT_Y16_BE, 1, CV_16U, 1.f, 1.f },
|
||||||
|
@ -62,7 +62,19 @@ yolov8n:
|
|||||||
background_label_id: 0
|
background_label_id: 0
|
||||||
sample: "yolo_detector"
|
sample: "yolo_detector"
|
||||||
|
|
||||||
|
yolov8m:
|
||||||
|
load_info:
|
||||||
|
url: "https://github.com/CVHub520/X-AnyLabeling/releases/download/v0.1.0/yolov8m.onnx"
|
||||||
|
sha1: "656ffeb4f3b067bc30df956728b5f9c61a4cb090"
|
||||||
|
model: "yolov8m.onnx"
|
||||||
|
mean: 0.0
|
||||||
|
scale: 0.00392
|
||||||
|
width: 640
|
||||||
|
height: 640
|
||||||
|
rgb: true
|
||||||
|
classes: "object_detection_classes_yolo.txt"
|
||||||
|
background_label_id: 0
|
||||||
|
sample: "yolo_detector"
|
||||||
|
|
||||||
# YOLO4 object detection family from Darknet (https://github.com/AlexeyAB/darknet)
|
# YOLO4 object detection family from Darknet (https://github.com/AlexeyAB/darknet)
|
||||||
# YOLO object detection family from Darknet (https://pjreddie.com/darknet/yolo/)
|
# YOLO object detection family from Darknet (https://pjreddie.com/darknet/yolo/)
|
||||||
|
Loading…
Reference in New Issue
Block a user