From fdc8ed8d05d6f073dab21a6f0fdb3f20953738be Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Sat, 25 Sep 2021 09:31:44 +0300 Subject: [PATCH 01/11] Update perf_bgfg_mog2.cpp, perf_bgfg_knn.cpp --- modules/video/perf/opencl/perf_bgfg_knn.cpp | 6 +++--- modules/video/perf/opencl/perf_bgfg_mog2.cpp | 6 +++--- modules/video/perf/perf_bgfg_knn.cpp | 6 +++--- modules/video/perf/perf_bgfg_mog2.cpp | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/video/perf/opencl/perf_bgfg_knn.cpp b/modules/video/perf/opencl/perf_bgfg_knn.cpp index 30419af422..c96ba6881a 100644 --- a/modules/video/perf/opencl/perf_bgfg_knn.cpp +++ b/modules/video/perf/opencl/perf_bgfg_knn.cpp @@ -20,7 +20,7 @@ typedef TestBaseWithParam KNN_GetBackgroundImage; using namespace opencv_test; -OCL_PERF_TEST_P(KNN_Apply, KNN, Combine(Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), Values(1,3))) +OCL_PERF_TEST_P(KNN_Apply, KNN, Combine(Values("cv/video/768x576.avi", "cv/video/1920x1080.avi"), Values(1,3))) { VideoKNNParamType params = GetParam(); @@ -51,8 +51,8 @@ OCL_PERF_TEST_P(KNN_Apply, KNN, Combine(Values("gpu/video/768x576.avi", "gpu/vid } OCL_PERF_TEST_P(KNN_GetBackgroundImage, KNN, Values( - std::make_pair("gpu/video/768x576.avi", 5), - std::make_pair("gpu/video/1920x1080.avi", 5))) + std::make_pair("cv/video/768x576.avi", 5), + std::make_pair("cv/video/1920x1080.avi", 5))) { VideoKNNParamType params = GetParam(); diff --git a/modules/video/perf/opencl/perf_bgfg_mog2.cpp b/modules/video/perf/opencl/perf_bgfg_mog2.cpp index 9952be79f6..b127a50d8d 100644 --- a/modules/video/perf/opencl/perf_bgfg_mog2.cpp +++ b/modules/video/perf/opencl/perf_bgfg_mog2.cpp @@ -20,7 +20,7 @@ typedef TestBaseWithParam MOG2_GetBackgroundImage; using namespace opencv_test; -OCL_PERF_TEST_P(MOG2_Apply, Mog2, Combine(Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), Values(1,3))) +OCL_PERF_TEST_P(MOG2_Apply, Mog2, Combine(Values("cv/video/768x576.avi", "cv/video/1920x1080.avi"), Values(1,3))) { VideoMOG2ParamType params = GetParam(); @@ -51,8 +51,8 @@ OCL_PERF_TEST_P(MOG2_Apply, Mog2, Combine(Values("gpu/video/768x576.avi", "gpu/v } OCL_PERF_TEST_P(MOG2_GetBackgroundImage, Mog2, Values( - std::make_pair("gpu/video/768x576.avi", 5), - std::make_pair("gpu/video/1920x1080.avi", 5))) + std::make_pair("cv/video/768x576.avi", 5), + std::make_pair("cv/video/1920x1080.avi", 5))) { VideoMOG2ParamType params = GetParam(); diff --git a/modules/video/perf/perf_bgfg_knn.cpp b/modules/video/perf/perf_bgfg_knn.cpp index d9ead09fd9..5d4f3bc400 100644 --- a/modules/video/perf/perf_bgfg_knn.cpp +++ b/modules/video/perf/perf_bgfg_knn.cpp @@ -15,7 +15,7 @@ typedef tuple VideoKNNParamType; typedef TestBaseWithParam KNN_Apply; typedef TestBaseWithParam KNN_GetBackgroundImage; -PERF_TEST_P(KNN_Apply, KNN, Combine(Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), Values(1,3))) +PERF_TEST_P(KNN_Apply, KNN, Combine(Values("cv/video/768x576.avi", "cv/video/1920x1080.avi"), Values(1,3))) { VideoKNNParamType params = GetParam(); @@ -46,8 +46,8 @@ PERF_TEST_P(KNN_Apply, KNN, Combine(Values("gpu/video/768x576.avi", "gpu/video/1 } PERF_TEST_P(KNN_GetBackgroundImage, KNN, Values( - std::make_pair("gpu/video/768x576.avi", 5), - std::make_pair("gpu/video/1920x1080.avi", 5))) + std::make_pair("cv/video/768x576.avi", 5), + std::make_pair("cv/video/1920x1080.avi", 5))) { VideoKNNParamType params = GetParam(); diff --git a/modules/video/perf/perf_bgfg_mog2.cpp b/modules/video/perf/perf_bgfg_mog2.cpp index 92e5d0283f..b932d6a22c 100644 --- a/modules/video/perf/perf_bgfg_mog2.cpp +++ b/modules/video/perf/perf_bgfg_mog2.cpp @@ -15,7 +15,7 @@ typedef tuple VideoMOG2ParamType; typedef TestBaseWithParam MOG2_Apply; typedef TestBaseWithParam MOG2_GetBackgroundImage; -PERF_TEST_P(MOG2_Apply, Mog2, Combine(Values("gpu/video/768x576.avi", "gpu/video/1920x1080.avi"), Values(1,3))) +PERF_TEST_P(MOG2_Apply, Mog2, Combine(Values("cv/video/768x576.avi", "cv/video/1920x1080.avi"), Values(1,3))) { VideoMOG2ParamType params = GetParam(); @@ -46,8 +46,8 @@ PERF_TEST_P(MOG2_Apply, Mog2, Combine(Values("gpu/video/768x576.avi", "gpu/video } PERF_TEST_P(MOG2_GetBackgroundImage, Mog2, Values( - std::make_pair("gpu/video/768x576.avi", 5), - std::make_pair("gpu/video/1920x1080.avi", 5))) + std::make_pair("cv/video/768x576.avi", 5), + std::make_pair("cv/video/1920x1080.avi", 5))) { VideoMOG2ParamType params = GetParam(); From 2cc14bd0fb2b898d7667c01bd24754dc9df0d5bd Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 27 Sep 2021 10:53:23 +0300 Subject: [PATCH 02/11] Verbose output for errors found by header parser. --- modules/python/src2/hdr_parser.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/python/src2/hdr_parser.py b/modules/python/src2/hdr_parser.py index 951dfe11c3..80cdea339f 100755 --- a/modules/python/src2/hdr_parser.py +++ b/modules/python/src2/hdr_parser.py @@ -53,13 +53,13 @@ class CppHeaderParser(object): def get_macro_arg(self, arg_str, npos): npos2 = npos3 = arg_str.find("(", npos) if npos2 < 0: - print("Error: no arguments for the macro at %d" % (self.lineno,)) + print("Error: no arguments for the macro at %s:%d" % (self.hname, self.lineno)) sys.exit(-1) balance = 1 while 1: t, npos3 = self.find_next_token(arg_str, ['(', ')'], npos3+1) if npos3 < 0: - print("Error: no matching ')' in the macro call at %d" % (self.lineno,)) + print("Error: no matching ')' in the macro call at %s:%d" % (self.hname, self.lineno)) sys.exit(-1) if t == '(': balance += 1 @@ -161,7 +161,7 @@ class CppHeaderParser(object): angle_stack.append(0) elif w == "," or w == '>': if not angle_stack: - print("Error at %d: argument contains ',' or '>' not within template arguments" % (self.lineno,)) + print("Error at %s:%d: argument contains ',' or '>' not within template arguments" % (self.hname, self.lineno)) sys.exit(-1) if w == ",": arg_type += "_and_" @@ -191,7 +191,7 @@ class CppHeaderParser(object): p1 = arg_name.find("[") p2 = arg_name.find("]",p1+1) if p2 < 0: - print("Error at %d: no closing ]" % (self.lineno,)) + print("Error at %s:%d: no closing ]" % (self.hname, self.lineno)) sys.exit(-1) counter_str = arg_name[p1+1:p2].strip() if counter_str == "": From b25ad12f1aab0c37afa41a729db85bf97730cedc Mon Sep 17 00:00:00 2001 From: Dmitriy Fishman Date: Tue, 28 Sep 2021 15:29:47 +0300 Subject: [PATCH 03/11] Update video_input_psnr_ssim.markdown --- .../video-input-psnr-ssim/video_input_psnr_ssim.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorials/videoio/video-input-psnr-ssim/video_input_psnr_ssim.markdown b/doc/tutorials/videoio/video-input-psnr-ssim/video_input_psnr_ssim.markdown index ffd4d0213e..7bc8561347 100644 --- a/doc/tutorials/videoio/video-input-psnr-ssim/video_input_psnr_ssim.markdown +++ b/doc/tutorials/videoio/video-input-psnr-ssim/video_input_psnr_ssim.markdown @@ -180,7 +180,7 @@ implementation below. This will return a similarity index for each channel of the image. This value is between zero and one, where one corresponds to perfect fit. Unfortunately, the many Gaussian blurring is quite -costly, so while the PSNR may work in a real time like environment (24 frame per second) this will +costly, so while the PSNR may work in a real time like environment (24 frames per second) this will take significantly more than to accomplish similar performance results. Therefore, the source code presented at the start of the tutorial will perform the PSNR measurement From 2f83c3b689423c74a296473057333cef20673109 Mon Sep 17 00:00:00 2001 From: Cavendish-Koo <1020057909@qq.com> Date: Tue, 28 Sep 2021 21:18:07 +0800 Subject: [PATCH 04/11] fix the bug of HoughlinesSDIV --- modules/imgproc/src/hough.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/imgproc/src/hough.cpp b/modules/imgproc/src/hough.cpp index bbb6dfcbdb..e66be8b4b7 100644 --- a/modules/imgproc/src/hough.cpp +++ b/modules/imgproc/src/hough.cpp @@ -435,12 +435,14 @@ HoughLinesSDiv( InputArray image, OutputArray lines, int type, } } + int pos = (int)(lst.size() - 1); + if( pos >= 0 && lst[pos].rho < 0 ) + lst.pop_back(); + lines.create((int)lst.size(), 1, type); Mat _lines = lines.getMat(); for( size_t idx = 0; idx < lst.size(); idx++ ) { - if( lst[idx].rho < 0 ) - continue; if (type == CV_32FC2) { _lines.at((int)idx) = Vec2f(lst[idx].rho, lst[idx].theta); From 846317ef372e37332fca8804c76cfa612cdf66e8 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Wed, 29 Sep 2021 13:16:46 +0000 Subject: [PATCH 05/11] dnn(ocl): fix conv BASIC workgroup --- .../dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp | 13 ++++++++++--- modules/dnn/src/opencl/conv_layer_spatial.cl | 12 ++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp index 5eee1da4a0..a1164273ac 100644 --- a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp +++ b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp @@ -1081,9 +1081,16 @@ bool OCL4DNNConvSpatial::convolve(const UMat &bottom, UMat &top, kernel.set(argIdx++, (uint16_t)output_h_); kernel.set(argIdx++, (uint16_t)pad_w_); kernel.set(argIdx++, (uint16_t)pad_h_); - if (!kernel.run_(3, config->global_work_size, - (config->use_null_local) ? NULL : config->local_work_size, - false)) + + size_t wgs = kernel.workGroupSize(); + if (!wgs) + { + CV_LOG_ERROR(NULL, "DNN/OpenCL: Can't query workGroupSize of Basic kernel"); + return false; + } + size_t lws[1] = { wgs }; + size_t gws[1] = { roundUp((size_t)output_w_ * output_h_ * M_, (unsigned)lws[0]) }; + if (!kernel.run_(1, gws, lws, false)) { CV_LOG_ERROR(NULL, "DNN/OpenCL: Basic kernel run failed"); return false; diff --git a/modules/dnn/src/opencl/conv_layer_spatial.cl b/modules/dnn/src/opencl/conv_layer_spatial.cl index e7bbacd4c4..455f0ed7ea 100644 --- a/modules/dnn/src/opencl/conv_layer_spatial.cl +++ b/modules/dnn/src/opencl/conv_layer_spatial.cl @@ -158,10 +158,14 @@ __kernel void ConvolveBasic( ) { __global Dtype* convolved_image = convolved_image_base + convolved_image_base_offset; - const int outputX = get_global_id(0); - const int outputY = get_global_id(1); - const int kernelNum = get_global_id(2) * ZPAR; - if (outputX < output_width && outputY < output_height) + const int out_idx = get_global_id(0); // 1D task layout: [output_width * output_height * OUTPUT_Z] + const int plane_size = output_width * output_height; + const int out_plane_idx = out_idx % plane_size; + const int outputZ = out_idx / plane_size; + const int outputY = out_plane_idx / output_width; + const int outputX = out_plane_idx % output_width; + const int kernelNum = outputZ * ZPAR; + if (kernelNum < OUTPUT_Z) { Dtype sum[ZPAR]; for (int kern = 0; kern < ZPAR; kern++) From 9b768727080b3279c244ad595115b1d5126d32ed Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Fri, 1 Oct 2021 16:23:16 +0300 Subject: [PATCH 06/11] restore LSD --- modules/imgproc/include/opencv2/imgproc.hpp | 10 +- modules/imgproc/src/lsd.cpp | 983 +++++++++++++++++++- modules/imgproc/test/test_lsd.cpp | 4 - samples/cpp/lsd_lines.cpp | 73 ++ 4 files changed, 1055 insertions(+), 15 deletions(-) create mode 100644 samples/cpp/lsd_lines.cpp diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index f7583c1926..855cbaf159 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1220,12 +1220,14 @@ protected: //! @addtogroup imgproc_feature //! @{ +/** @example samples/cpp/lsd_lines.cpp +An example using the LineSegmentDetector +\image html building_lsd.png "Sample output image" width=434 height=300 +*/ + /** @brief Line segment detector class following the algorithm described at @cite Rafael12 . - -@note Implementation has been removed due original code license conflict - */ class CV_EXPORTS_W LineSegmentDetector : public Algorithm { @@ -1288,8 +1290,6 @@ to edit those, as to tailor it for their own application. @param log_eps Detection threshold: -log10(NFA) \> log_eps. Used only when advance refinement is chosen. @param density_th Minimal density of aligned region points in the enclosing rectangle. @param n_bins Number of bins in pseudo-ordering of gradient modulus. - -@note Implementation has been removed due original code license conflict */ CV_EXPORTS_W Ptr createLineSegmentDetector( int refine = LSD_REFINE_STD, double scale = 0.8, diff --git a/modules/imgproc/src/lsd.cpp b/modules/imgproc/src/lsd.cpp index 1ec984d290..d06759c2bb 100644 --- a/modules/imgproc/src/lsd.cpp +++ b/modules/imgproc/src/lsd.cpp @@ -42,7 +42,123 @@ #include "precomp.hpp" #include -namespace cv { +///////////////////////////////////////////////////////////////////////////////////////// +// Default LSD parameters +// SIGMA_SCALE 0.6 - Sigma for Gaussian filter is computed as sigma = sigma_scale/scale. +// QUANT 2.0 - Bound to the quantization error on the gradient norm. +// ANG_TH 22.5 - Gradient angle tolerance in degrees. +// LOG_EPS 0.0 - Detection threshold: -log10(NFA) > log_eps +// DENSITY_TH 0.7 - Minimal density of region points in rectangle. +// N_BINS 1024 - Number of bins in pseudo-ordering of gradient modulus. + +#define M_3_2_PI (3 * CV_PI) / 2 // 3/2 pi +#define M_2__PI (2 * CV_PI) // 2 pi + +#ifndef M_LN10 +#define M_LN10 2.30258509299404568402 +#endif + +#define NOTDEF double(-1024.0) // Label for pixels with undefined gradient. + +#define NOTUSED 0 // Label for pixels not used in yet. +#define USED 1 // Label for pixels already used in detection. + +#define RELATIVE_ERROR_FACTOR 100.0 + +const double DEG_TO_RADS = CV_PI / 180; + +#define log_gamma(x) ((x)>15.0?log_gamma_windschitl(x):log_gamma_lanczos(x)) + +struct edge +{ + cv::Point p; + bool taken; +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +inline double distSq(const double x1, const double y1, + const double x2, const double y2) +{ + return (x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1); +} + +inline double dist(const double x1, const double y1, + const double x2, const double y2) +{ + return sqrt(distSq(x1, y1, x2, y2)); +} + +// Signed angle difference +inline double angle_diff_signed(const double& a, const double& b) +{ + double diff = a - b; + while(diff <= -CV_PI) diff += M_2__PI; + while(diff > CV_PI) diff -= M_2__PI; + return diff; +} + +// Absolute value angle difference +inline double angle_diff(const double& a, const double& b) +{ + return std::fabs(angle_diff_signed(a, b)); +} + +// Compare doubles by relative error. +inline bool double_equal(const double& a, const double& b) +{ + // trivial case + if(a == b) return true; + + double abs_diff = fabs(a - b); + double aa = fabs(a); + double bb = fabs(b); + double abs_max = (aa > bb)? aa : bb; + + if(abs_max < DBL_MIN) abs_max = DBL_MIN; + + return (abs_diff / abs_max) <= (RELATIVE_ERROR_FACTOR * DBL_EPSILON); +} + +inline bool AsmallerB_XoverY(const edge& a, const edge& b) +{ + if (a.p.x == b.p.x) return a.p.y < b.p.y; + else return a.p.x < b.p.x; +} + +/** + * Computes the natural logarithm of the absolute value of + * the gamma function of x using Windschitl method. + * See http://www.rskey.org/gamma.htm + */ +inline double log_gamma_windschitl(const double& x) +{ + return 0.918938533204673 + (x-0.5)*log(x) - x + + 0.5*x*log(x*sinh(1/x) + 1/(810.0*pow(x, 6.0))); +} + +/** + * Computes the natural logarithm of the absolute value of + * the gamma function of x using the Lanczos approximation. + * See http://www.rskey.org/gamma.htm + */ +inline double log_gamma_lanczos(const double& x) +{ + static double q[7] = { 75122.6331530, 80916.6278952, 36308.2951477, + 8687.24529705, 1168.92649479, 83.8676043424, + 2.50662827511 }; + double a = (x + 0.5) * log(x + 5.5) - (x + 5.5); + double b = 0; + for(int n = 0; n < 7; ++n) + { + a -= log(x + double(n)); + b += q[n] * pow(x, double(n)); + } + return a + log(b); +} +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace cv{ class LineSegmentDetectorImpl CV_FINAL : public LineSegmentDetector { @@ -113,7 +229,164 @@ public: int compareSegments(const Size& size, InputArray lines1, InputArray lines2, InputOutputArray _image = noArray()) CV_OVERRIDE; private: + Mat image; + Mat scaled_image; + Mat_ angles; // in rads + Mat_ modgrad; + Mat_ used; + + int img_width; + int img_height; + double LOG_NT; + + bool w_needed; + bool p_needed; + bool n_needed; + + const double SCALE; + const int doRefine; + const double SIGMA_SCALE; + const double QUANT; + const double ANG_TH; + const double LOG_EPS; + const double DENSITY_TH; + const int N_BINS; + + struct RegionPoint { + int x; + int y; + uchar* used; + double angle; + double modgrad; + }; + + struct normPoint + { + Point2i p; + int norm; + }; + + std::vector ordered_points; + + struct rect + { + double x1, y1, x2, y2; // first and second point of the line segment + double width; // rectangle width + double x, y; // center of the rectangle + double theta; // angle + double dx,dy; // (dx,dy) is vector oriented as the line segment + double prec; // tolerance angle + double p; // probability of a point with angle within 'prec' + }; + LineSegmentDetectorImpl& operator= (const LineSegmentDetectorImpl&); // to quiet MSVC + +/** + * Detect lines in the whole input image. + * + * @param lines Return: A vector of Vec4f elements specifying the beginning and ending point of a line. + * Where Vec4f is (x1, y1, x2, y2), point 1 is the start, point 2 - end. + * Returned lines are strictly oriented depending on the gradient. + * @param widths Return: Vector of widths of the regions, where the lines are found. E.g. Width of line. + * @param precisions Return: Vector of precisions with which the lines are found. + * @param nfas Return: Vector containing number of false alarms in the line region, with precision of 10%. + * The bigger the value, logarithmically better the detection. + * * -1 corresponds to 10 mean false alarms + * * 0 corresponds to 1 mean false alarm + * * 1 corresponds to 0.1 mean false alarms + */ + void flsd(std::vector& lines, + std::vector& widths, std::vector& precisions, + std::vector& nfas); + +/** + * Finds the angles and the gradients of the image. Generates a list of pseudo ordered points. + * + * @param threshold The minimum value of the angle that is considered defined, otherwise NOTDEF + * @param n_bins The number of bins with which gradients are ordered by, using bucket sort. + * @param ordered_points Return: Vector of coordinate points that are pseudo ordered by magnitude. + * Pixels would be ordered by norm value, up to a precision given by max_grad/n_bins. + */ + void ll_angle(const double& threshold, const unsigned int& n_bins); + +/** + * Grow a region starting from point s with a defined precision, + * returning the containing points size and the angle of the gradients. + * + * @param s Starting point for the region. + * @param reg Return: Vector of points, that are part of the region + * @param reg_angle Return: The mean angle of the region. + * @param prec The precision by which each region angle should be aligned to the mean. + */ + void region_grow(const Point2i& s, std::vector& reg, + double& reg_angle, const double& prec); + +/** + * Finds the bounding rotated rectangle of a region. + * + * @param reg The region of points, from which the rectangle to be constructed from. + * @param reg_angle The mean angle of the region. + * @param prec The precision by which points were found. + * @param p Probability of a point with angle within 'prec'. + * @param rec Return: The generated rectangle. + */ + void region2rect(const std::vector& reg, const double reg_angle, + const double prec, const double p, rect& rec) const; + +/** + * Compute region's angle as the principal inertia axis of the region. + * @return Regions angle. + */ + double get_theta(const std::vector& reg, const double& x, + const double& y, const double& reg_angle, const double& prec) const; + +/** + * An estimation of the angle tolerance is performed by the standard deviation of the angle at points + * near the region's starting point. Then, a new region is grown starting from the same point, but using the + * estimated angle tolerance. If this fails to produce a rectangle with the right density of region points, + * 'reduce_region_radius' is called to try to satisfy this condition. + */ + bool refine(std::vector& reg, double reg_angle, + const double prec, double p, rect& rec, const double& density_th); + +/** + * Reduce the region size, by elimination the points far from the starting point, until that leads to + * rectangle with the right density of region points or to discard the region if too small. + */ + bool reduce_region_radius(std::vector& reg, double reg_angle, + const double prec, double p, rect& rec, double density, const double& density_th); + +/** + * Try some rectangles variations to improve NFA value. Only if the rectangle is not meaningful (i.e., log_nfa <= log_eps). + * @return The new NFA value. + */ + double rect_improve(rect& rec) const; + +/** + * Calculates the number of correctly aligned points within the rectangle. + * @return The new NFA value. + */ + double rect_nfa(const rect& rec) const; + +/** + * Computes the NFA values based on the total number of points, points that agree. + * n, k, p are the binomial parameters. + * @return The new NFA value. + */ + double nfa(const int& n, const int& k, const double& p) const; + +/** + * Is the point at place 'address' aligned to angle theta, up to precision 'prec'? + * @return Whether the point is aligned. + */ + bool isAligned(int x, int y, const double& theta, const double& prec) const; + +public: + // Compare norm + static inline bool compare_norm( const normPoint& n1, const normPoint& n2 ) + { + return (n1.norm > n2.norm); + } }; ///////////////////////////////////////////////////////////////////////////////////////// @@ -131,12 +404,13 @@ CV_EXPORTS Ptr createLineSegmentDetector( LineSegmentDetectorImpl::LineSegmentDetectorImpl(int _refine, double _scale, double _sigma_scale, double _quant, double _ang_th, double _log_eps, double _density_th, int _n_bins) + : img_width(0), img_height(0), LOG_NT(0), w_needed(false), p_needed(false), n_needed(false), + SCALE(_scale), doRefine(_refine), SIGMA_SCALE(_sigma_scale), QUANT(_quant), + ANG_TH(_ang_th), LOG_EPS(_log_eps), DENSITY_TH(_density_th), N_BINS(_n_bins) { CV_Assert(_scale > 0 && _sigma_scale > 0 && _quant >= 0 && _ang_th > 0 && _ang_th < 180 && _density_th >= 0 && _density_th < 1 && _n_bins > 0); - CV_UNUSED(_refine); CV_UNUSED(_log_eps); - CV_Error(Error::StsNotImplemented, "Implementation has been removed due original code license issues"); } void LineSegmentDetectorImpl::detect(InputArray _image, OutputArray _lines, @@ -144,11 +418,708 @@ void LineSegmentDetectorImpl::detect(InputArray _image, OutputArray _lines, { CV_INSTRUMENT_REGION(); - CV_UNUSED(_image); CV_UNUSED(_lines); - CV_UNUSED(_width); CV_UNUSED(_prec); CV_UNUSED(_nfa); - CV_Error(Error::StsNotImplemented, "Implementation has been removed due original code license issues"); + image = _image.getMat(); + CV_Assert(!image.empty() && image.type() == CV_8UC1); + + std::vector lines; + std::vector w, p, n; + w_needed = _width.needed(); + p_needed = _prec.needed(); + if (doRefine < LSD_REFINE_ADV) + n_needed = false; + else + n_needed = _nfa.needed(); + + flsd(lines, w, p, n); + + Mat(lines).copyTo(_lines); + if(w_needed) Mat(w).copyTo(_width); + if(p_needed) Mat(p).copyTo(_prec); + if(n_needed) Mat(n).copyTo(_nfa); + + // Clear used structures + ordered_points.clear(); } +void LineSegmentDetectorImpl::flsd(std::vector& lines, + std::vector& widths, std::vector& precisions, + std::vector& nfas) +{ + // Angle tolerance + const double prec = CV_PI * ANG_TH / 180; + const double p = ANG_TH / 180; + const double rho = QUANT / sin(prec); // gradient magnitude threshold + + if(SCALE != 1) + { + Mat gaussian_img; + const double sigma = (SCALE < 1)?(SIGMA_SCALE / SCALE):(SIGMA_SCALE); + const double sprec = 3; + const unsigned int h = (unsigned int)(ceil(sigma * sqrt(2 * sprec * log(10.0)))); + Size ksize(1 + 2 * h, 1 + 2 * h); // kernel size + GaussianBlur(image, gaussian_img, ksize, sigma); + // Scale image to needed size + resize(gaussian_img, scaled_image, Size(), SCALE, SCALE, INTER_LINEAR_EXACT); + ll_angle(rho, N_BINS); + } + else + { + scaled_image = image; + ll_angle(rho, N_BINS); + } + + LOG_NT = 5 * (log10(double(img_width)) + log10(double(img_height))) / 2 + log10(11.0); + const size_t min_reg_size = size_t(-LOG_NT/log10(p)); // minimal number of points in region that can give a meaningful event + + // // Initialize region only when needed + // Mat region = Mat::zeros(scaled_image.size(), CV_8UC1); + used = Mat_::zeros(scaled_image.size()); // zeros = NOTUSED + std::vector reg; + + // Search for line segments + for(size_t i = 0, points_size = ordered_points.size(); i < points_size; ++i) + { + const Point2i& point = ordered_points[i].p; + if((used.at(point) == NOTUSED) && (angles.at(point) != NOTDEF)) + { + double reg_angle; + region_grow(ordered_points[i].p, reg, reg_angle, prec); + + // Ignore small regions + if(reg.size() < min_reg_size) { continue; } + + // Construct rectangular approximation for the region + rect rec; + region2rect(reg, reg_angle, prec, p, rec); + + double log_nfa = -1; + if(doRefine > LSD_REFINE_NONE) + { + // At least REFINE_STANDARD lvl. + if(!refine(reg, reg_angle, prec, p, rec, DENSITY_TH)) { continue; } + + if(doRefine >= LSD_REFINE_ADV) + { + // Compute NFA + log_nfa = rect_improve(rec); + if(log_nfa <= LOG_EPS) { continue; } + } + } + // Found new line + + // Add the offset + rec.x1 += 0.5; rec.y1 += 0.5; + rec.x2 += 0.5; rec.y2 += 0.5; + + // scale the result values if a sub-sampling was performed + if(SCALE != 1) + { + rec.x1 /= SCALE; rec.y1 /= SCALE; + rec.x2 /= SCALE; rec.y2 /= SCALE; + rec.width /= SCALE; + } + + //Store the relevant data + lines.push_back(Vec4f(float(rec.x1), float(rec.y1), float(rec.x2), float(rec.y2))); + if(w_needed) widths.push_back(rec.width); + if(p_needed) precisions.push_back(rec.p); + if(n_needed && doRefine >= LSD_REFINE_ADV) nfas.push_back(log_nfa); + } + } +} + +void LineSegmentDetectorImpl::ll_angle(const double& threshold, + const unsigned int& n_bins) +{ + //Initialize data + angles = Mat_(scaled_image.size()); + modgrad = Mat_(scaled_image.size()); + + img_width = scaled_image.cols; + img_height = scaled_image.rows; + + // Undefined the down and right boundaries + angles.row(img_height - 1).setTo(NOTDEF); + angles.col(img_width - 1).setTo(NOTDEF); + + // Computing gradient for remaining pixels + double max_grad = -1; + for(int y = 0; y < img_height - 1; ++y) + { + const uchar* scaled_image_row = scaled_image.ptr(y); + const uchar* next_scaled_image_row = scaled_image.ptr(y+1); + double* angles_row = angles.ptr(y); + double* modgrad_row = modgrad.ptr(y); + for(int x = 0; x < img_width-1; ++x) + { + int DA = next_scaled_image_row[x + 1] - scaled_image_row[x]; + int BC = scaled_image_row[x + 1] - next_scaled_image_row[x]; + int gx = DA + BC; // gradient x component + int gy = DA - BC; // gradient y component + double norm = std::sqrt((gx * gx + gy * gy) / 4.0); // gradient norm + + modgrad_row[x] = norm; // store gradient + + if (norm <= threshold) // norm too small, gradient no defined + { + angles_row[x] = NOTDEF; + } + else + { + angles_row[x] = fastAtan2(float(gx), float(-gy)) * DEG_TO_RADS; // gradient angle computation + if (norm > max_grad) { max_grad = norm; } + } + + } + } + + // Compute histogram of gradient values + double bin_coef = (max_grad > 0) ? double(n_bins - 1) / max_grad : 0; // If all image is smooth, max_grad <= 0 + for(int y = 0; y < img_height - 1; ++y) + { + const double* modgrad_row = modgrad.ptr(y); + for(int x = 0; x < img_width - 1; ++x) + { + normPoint _point; + int i = int(modgrad_row[x] * bin_coef); + _point.p = Point(x, y); + _point.norm = i; + ordered_points.push_back(_point); + } + } + + // Sort + std::sort(ordered_points.begin(), ordered_points.end(), compare_norm); +} + +void LineSegmentDetectorImpl::region_grow(const Point2i& s, std::vector& reg, + double& reg_angle, const double& prec) +{ + reg.clear(); + + // Point to this region + RegionPoint seed; + seed.x = s.x; + seed.y = s.y; + seed.used = &used.at(s); + reg_angle = angles.at(s); + seed.angle = reg_angle; + seed.modgrad = modgrad.at(s); + reg.push_back(seed); + + float sumdx = float(std::cos(reg_angle)); + float sumdy = float(std::sin(reg_angle)); + *seed.used = USED; + + //Try neighboring regions + for (size_t i = 0;i(yy); + const double* angles_row = angles.ptr(yy); + const double* modgrad_row = modgrad.ptr(yy); + for(int xx = xx_min; xx <= xx_max; ++xx) + { + uchar& is_used = used_row[xx]; + if(is_used != USED && + (isAligned(xx, yy, reg_angle, prec))) + { + const double& angle = angles_row[xx]; + // Add point + is_used = USED; + RegionPoint region_point; + region_point.x = xx; + region_point.y = yy; + region_point.used = &is_used; + region_point.modgrad = modgrad_row[xx]; + region_point.angle = angle; + reg.push_back(region_point); + + // Update region's angle + sumdx += cos(float(angle)); + sumdy += sin(float(angle)); + // reg_angle is used in the isAligned, so it needs to be updates? + reg_angle = fastAtan2(sumdy, sumdx) * DEG_TO_RADS; + } + } + } + } +} + +void LineSegmentDetectorImpl::region2rect(const std::vector& reg, + const double reg_angle, const double prec, const double p, rect& rec) const +{ + double x = 0, y = 0, sum = 0; + for(size_t i = 0; i < reg.size(); ++i) + { + const RegionPoint& pnt = reg[i]; + const double& weight = pnt.modgrad; + x += double(pnt.x) * weight; + y += double(pnt.y) * weight; + sum += weight; + } + + // Weighted sum must differ from 0 + CV_Assert(sum > 0); + + x /= sum; + y /= sum; + + double theta = get_theta(reg, x, y, reg_angle, prec); + + // Find length and width + double dx = cos(theta); + double dy = sin(theta); + double l_min = 0, l_max = 0, w_min = 0, w_max = 0; + + for(size_t i = 0; i < reg.size(); ++i) + { + double regdx = double(reg[i].x) - x; + double regdy = double(reg[i].y) - y; + + double l = regdx * dx + regdy * dy; + double w = -regdx * dy + regdy * dx; + + if(l > l_max) l_max = l; + else if(l < l_min) l_min = l; + if(w > w_max) w_max = w; + else if(w < w_min) w_min = w; + } + + // Store values + rec.x1 = x + l_min * dx; + rec.y1 = y + l_min * dy; + rec.x2 = x + l_max * dx; + rec.y2 = y + l_max * dy; + rec.width = w_max - w_min; + rec.x = x; + rec.y = y; + rec.theta = theta; + rec.dx = dx; + rec.dy = dy; + rec.prec = prec; + rec.p = p; + + // Min width of 1 pixel + if(rec.width < 1.0) rec.width = 1.0; +} + +double LineSegmentDetectorImpl::get_theta(const std::vector& reg, const double& x, + const double& y, const double& reg_angle, const double& prec) const +{ + double Ixx = 0.0; + double Iyy = 0.0; + double Ixy = 0.0; + + // Compute inertia matrix + for(size_t i = 0; i < reg.size(); ++i) + { + const double& regx = reg[i].x; + const double& regy = reg[i].y; + const double& weight = reg[i].modgrad; + double dx = regx - x; + double dy = regy - y; + Ixx += dy * dy * weight; + Iyy += dx * dx * weight; + Ixy -= dx * dy * weight; + } + + // Check if inertia matrix is null + CV_Assert(!(double_equal(Ixx, 0) && double_equal(Iyy, 0) && double_equal(Ixy, 0))); + + // Compute smallest eigenvalue + double lambda = 0.5 * (Ixx + Iyy - sqrt((Ixx - Iyy) * (Ixx - Iyy) + 4.0 * Ixy * Ixy)); + + // Compute angle + double theta = (fabs(Ixx)>fabs(Iyy))? + double(fastAtan2(float(lambda - Ixx), float(Ixy))): + double(fastAtan2(float(Ixy), float(lambda - Iyy))); // in degs + theta *= DEG_TO_RADS; + + // Correct angle by 180 deg if necessary + if(angle_diff(theta, reg_angle) > prec) { theta += CV_PI; } + + return theta; +} + +bool LineSegmentDetectorImpl::refine(std::vector& reg, double reg_angle, + const double prec, double p, rect& rec, const double& density_th) +{ + double density = double(reg.size()) / (dist(rec.x1, rec.y1, rec.x2, rec.y2) * rec.width); + + if (density >= density_th) { return true; } + + // Try to reduce angle tolerance + double xc = double(reg[0].x); + double yc = double(reg[0].y); + const double& ang_c = reg[0].angle; + double sum = 0, s_sum = 0; + int n = 0; + + for (size_t i = 0; i < reg.size(); ++i) + { + *(reg[i].used) = NOTUSED; + if (dist(xc, yc, reg[i].x, reg[i].y) < rec.width) + { + const double& angle = reg[i].angle; + double ang_d = angle_diff_signed(angle, ang_c); + sum += ang_d; + s_sum += ang_d * ang_d; + ++n; + } + } + CV_Assert(n > 0); + double mean_angle = sum / double(n); + // 2 * standard deviation + double tau = 2.0 * sqrt((s_sum - 2.0 * mean_angle * sum) / double(n) + mean_angle * mean_angle); + + // Try new region + region_grow(Point(reg[0].x, reg[0].y), reg, reg_angle, tau); + + if (reg.size() < 2) { return false; } + + region2rect(reg, reg_angle, prec, p, rec); + density = double(reg.size()) / (dist(rec.x1, rec.y1, rec.x2, rec.y2) * rec.width); + + if (density < density_th) + { + return reduce_region_radius(reg, reg_angle, prec, p, rec, density, density_th); + } + else + { + return true; + } +} + +bool LineSegmentDetectorImpl::reduce_region_radius(std::vector& reg, double reg_angle, + const double prec, double p, rect& rec, double density, const double& density_th) +{ + // Compute region's radius + double xc = double(reg[0].x); + double yc = double(reg[0].y); + double radSq1 = distSq(xc, yc, rec.x1, rec.y1); + double radSq2 = distSq(xc, yc, rec.x2, rec.y2); + double radSq = radSq1 > radSq2 ? radSq1 : radSq2; + + while(density < density_th) + { + radSq *= 0.75*0.75; // Reduce region's radius to 75% of its value + // Remove points from the region and update 'used' map + for (size_t i = 0; i < reg.size(); ++i) + { + if(distSq(xc, yc, double(reg[i].x), double(reg[i].y)) > radSq) + { + // Remove point from the region + *(reg[i].used) = NOTUSED; + std::swap(reg[i], reg[reg.size() - 1]); + reg.pop_back(); + --i; // To avoid skipping one point + } + } + + if(reg.size() < 2) { return false; } + + // Re-compute rectangle + region2rect(reg ,reg_angle, prec, p, rec); + + // Re-compute region points density + density = double(reg.size()) / + (dist(rec.x1, rec.y1, rec.x2, rec.y2) * rec.width); + } + + return true; +} + +double LineSegmentDetectorImpl::rect_improve(rect& rec) const +{ + double delta = 0.5; + double delta_2 = delta / 2.0; + + double log_nfa = rect_nfa(rec); + + if(log_nfa > LOG_EPS) return log_nfa; // Good rectangle + + // Try to improve + // Finer precision + rect r = rect(rec); // Copy + for(int n = 0; n < 5; ++n) + { + r.p /= 2; + r.prec = r.p * CV_PI; + double log_nfa_new = rect_nfa(r); + if(log_nfa_new > log_nfa) + { + log_nfa = log_nfa_new; + rec = rect(r); + } + } + if(log_nfa > LOG_EPS) return log_nfa; + + // Try to reduce width + r = rect(rec); + for(unsigned int n = 0; n < 5; ++n) + { + if((r.width - delta) >= 0.5) + { + r.width -= delta; + double log_nfa_new = rect_nfa(r); + if(log_nfa_new > log_nfa) + { + rec = rect(r); + log_nfa = log_nfa_new; + } + } + } + if(log_nfa > LOG_EPS) return log_nfa; + + // Try to reduce one side of rectangle + r = rect(rec); + for(unsigned int n = 0; n < 5; ++n) + { + if((r.width - delta) >= 0.5) + { + r.x1 += -r.dy * delta_2; + r.y1 += r.dx * delta_2; + r.x2 += -r.dy * delta_2; + r.y2 += r.dx * delta_2; + r.width -= delta; + double log_nfa_new = rect_nfa(r); + if(log_nfa_new > log_nfa) + { + rec = rect(r); + log_nfa = log_nfa_new; + } + } + } + if(log_nfa > LOG_EPS) return log_nfa; + + // Try to reduce other side of rectangle + r = rect(rec); + for(unsigned int n = 0; n < 5; ++n) + { + if((r.width - delta) >= 0.5) + { + r.x1 -= -r.dy * delta_2; + r.y1 -= r.dx * delta_2; + r.x2 -= -r.dy * delta_2; + r.y2 -= r.dx * delta_2; + r.width -= delta; + double log_nfa_new = rect_nfa(r); + if(log_nfa_new > log_nfa) + { + rec = rect(r); + log_nfa = log_nfa_new; + } + } + } + if(log_nfa > LOG_EPS) return log_nfa; + + // Try finer precision + r = rect(rec); + for(unsigned int n = 0; n < 5; ++n) + { + if((r.width - delta) >= 0.5) + { + r.p /= 2; + r.prec = r.p * CV_PI; + double log_nfa_new = rect_nfa(r); + if(log_nfa_new > log_nfa) + { + rec = rect(r); + log_nfa = log_nfa_new; + } + } + } + + return log_nfa; +} + +double LineSegmentDetectorImpl::rect_nfa(const rect& rec) const +{ + int total_pts = 0, alg_pts = 0; + double half_width = rec.width / 2.0; + double dyhw = rec.dy * half_width; + double dxhw = rec.dx * half_width; + + edge ordered_x[4]; + edge* min_y = &ordered_x[0]; + edge* max_y = &ordered_x[0]; // Will be used for loop range + + ordered_x[0].p.x = int(rec.x1 - dyhw); ordered_x[0].p.y = int(rec.y1 + dxhw); ordered_x[0].taken = false; + ordered_x[1].p.x = int(rec.x2 - dyhw); ordered_x[1].p.y = int(rec.y2 + dxhw); ordered_x[1].taken = false; + ordered_x[2].p.x = int(rec.x2 + dyhw); ordered_x[2].p.y = int(rec.y2 - dxhw); ordered_x[2].taken = false; + ordered_x[3].p.x = int(rec.x1 + dyhw); ordered_x[3].p.y = int(rec.y1 - dxhw); ordered_x[3].taken = false; + + std::sort(ordered_x, ordered_x + 4, AsmallerB_XoverY); + + // Find min y. And mark as taken. find max y. + for(unsigned int i = 1; i < 4; ++i) + { + if(min_y->p.y > ordered_x[i].p.y) {min_y = &ordered_x[i]; } + if(max_y->p.y < ordered_x[i].p.y) {max_y = &ordered_x[i]; } + } + min_y->taken = true; + + // Find leftmost untaken point; + edge* leftmost = 0; + for(unsigned int i = 0; i < 4; ++i) + { + if(!ordered_x[i].taken) + { + if(!leftmost) // if uninitialized + { + leftmost = &ordered_x[i]; + } + else if (leftmost->p.x > ordered_x[i].p.x) + { + leftmost = &ordered_x[i]; + } + } + } + CV_Assert(leftmost != NULL); + leftmost->taken = true; + + // Find rightmost untaken point; + edge* rightmost = 0; + for(unsigned int i = 0; i < 4; ++i) + { + if(!ordered_x[i].taken) + { + if(!rightmost) // if uninitialized + { + rightmost = &ordered_x[i]; + } + else if (rightmost->p.x < ordered_x[i].p.x) + { + rightmost = &ordered_x[i]; + } + } + } + CV_Assert(rightmost != NULL); + rightmost->taken = true; + + // Find last untaken point; + edge* tailp = 0; + for(unsigned int i = 0; i < 4; ++i) + { + if(!ordered_x[i].taken) + { + if(!tailp) // if uninitialized + { + tailp = &ordered_x[i]; + } + else if (tailp->p.x > ordered_x[i].p.x) + { + tailp = &ordered_x[i]; + } + } + } + CV_Assert(tailp != NULL); + tailp->taken = true; + + double flstep = (min_y->p.y != leftmost->p.y) ? + (min_y->p.x - leftmost->p.x) / (min_y->p.y - leftmost->p.y) : 0; //first left step + double slstep = (leftmost->p.y != tailp->p.x) ? + (leftmost->p.x - tailp->p.x) / (leftmost->p.y - tailp->p.x) : 0; //second left step + + double frstep = (min_y->p.y != rightmost->p.y) ? + (min_y->p.x - rightmost->p.x) / (min_y->p.y - rightmost->p.y) : 0; //first right step + double srstep = (rightmost->p.y != tailp->p.x) ? + (rightmost->p.x - tailp->p.x) / (rightmost->p.y - tailp->p.x) : 0; //second right step + + double lstep = flstep, rstep = frstep; + + double left_x = min_y->p.x, right_x = min_y->p.x; + + // Loop around all points in the region and count those that are aligned. + int min_iter = min_y->p.y; + int max_iter = max_y->p.y; + for(int y = min_iter; y <= max_iter; ++y) + { + if (y < 0 || y >= img_height) continue; + + for(int x = int(left_x); x <= int(right_x); ++x) + { + if (x < 0 || x >= img_width) continue; + + ++total_pts; + if(isAligned(x, y, rec.theta, rec.prec)) + { + ++alg_pts; + } + } + + if(y >= leftmost->p.y) { lstep = slstep; } + if(y >= rightmost->p.y) { rstep = srstep; } + + left_x += lstep; + right_x += rstep; + } + + return nfa(total_pts, alg_pts, rec.p); +} + +double LineSegmentDetectorImpl::nfa(const int& n, const int& k, const double& p) const +{ + // Trivial cases + if(n == 0 || k == 0) { return -LOG_NT; } + if(n == k) { return -LOG_NT - double(n) * log10(p); } + + double p_term = p / (1 - p); + + double log1term = (double(n) + 1) - log_gamma(double(k) + 1) + - log_gamma(double(n-k) + 1) + + double(k) * log(p) + double(n-k) * log(1.0 - p); + double term = exp(log1term); + + if(double_equal(term, 0)) + { + if(k > n * p) return -log1term / M_LN10 - LOG_NT; + else return -LOG_NT; + } + + // Compute more terms if needed + double bin_tail = term; + double tolerance = 0.1; // an error of 10% in the result is accepted + for(int i = k + 1; i <= n; ++i) + { + double bin_term = double(n - i + 1) / double(i); + double mult_term = bin_term * p_term; + term *= mult_term; + bin_tail += term; + if(bin_term < 1) + { + double err = term * ((1 - pow(mult_term, double(n-i+1))) / (1 - mult_term) - 1); + if(err < tolerance * fabs(-log10(bin_tail) - LOG_NT) * bin_tail) break; + } + + } + return -log10(bin_tail) - LOG_NT; +} + +inline bool LineSegmentDetectorImpl::isAligned(int x, int y, const double& theta, const double& prec) const +{ + if(x < 0 || y < 0 || x >= angles.cols || y >= angles.rows) { return false; } + const double& a = angles.at(y, x); + if(a == NOTDEF) { return false; } + + // It is assumed that 'theta' and 'a' are in the range [-pi,pi] + double n_theta = theta - a; + if(n_theta < 0) { n_theta = -n_theta; } + if(n_theta > M_3_2_PI) + { + n_theta -= M_2__PI; + if(n_theta < 0) n_theta = -n_theta; + } + + return n_theta <= prec; +} + + void LineSegmentDetectorImpl::drawSegments(InputOutputArray _image, InputArray lines) { CV_INSTRUMENT_REGION(); diff --git a/modules/imgproc/test/test_lsd.cpp b/modules/imgproc/test/test_lsd.cpp index e162a3c6f4..43d00b4928 100644 --- a/modules/imgproc/test/test_lsd.cpp +++ b/modules/imgproc/test/test_lsd.cpp @@ -5,8 +5,6 @@ namespace opencv_test { namespace { -#if 0 // LSD implementation has been removed due original code license issues - const Size img_size(640, 480); const int LSD_TEST_SEED = 0x134679; const int EPOCHS = 20; @@ -404,6 +402,4 @@ TEST_F(Imgproc_LSD_Common, compareSegmentsVec4i) ASSERT_EQ(result2, 11); } -#endif - }} // namespace diff --git a/samples/cpp/lsd_lines.cpp b/samples/cpp/lsd_lines.cpp new file mode 100644 index 0000000000..3feed9cbc2 --- /dev/null +++ b/samples/cpp/lsd_lines.cpp @@ -0,0 +1,73 @@ +#include "opencv2/imgproc.hpp" +#include "opencv2/imgcodecs.hpp" +#include "opencv2/highgui.hpp" +#include + +using namespace std; +using namespace cv; + +int main(int argc, char** argv) +{ + cv::CommandLineParser parser(argc, argv, + "{input i|building.jpg|input image}" + "{refine r|false|if true use LSD_REFINE_STD method, if false use LSD_REFINE_NONE method}" + "{canny c|false|use Canny edge detector}" + "{overlay o|false|show result on input image}" + "{help h|false|show help message}"); + + if (parser.get("help")) + { + parser.printMessage(); + return 0; + } + + parser.printMessage(); + + String filename = samples::findFile(parser.get("input")); + bool useRefine = parser.get("refine"); + bool useCanny = parser.get("canny"); + bool overlay = parser.get("overlay"); + + Mat image = imread(filename, IMREAD_GRAYSCALE); + + if( image.empty() ) + { + cout << "Unable to load " << filename; + return 1; + } + + imshow("Source Image", image); + + if (useCanny) + { + Canny(image, image, 50, 200, 3); // Apply Canny edge detector + } + + // Create and LSD detector with standard or no refinement. + Ptr ls = useRefine ? createLineSegmentDetector(LSD_REFINE_STD) : createLineSegmentDetector(LSD_REFINE_NONE); + + double start = double(getTickCount()); + vector lines_std; + + // Detect the lines + ls->detect(image, lines_std); + + double duration_ms = (double(getTickCount()) - start) * 1000 / getTickFrequency(); + std::cout << "It took " << duration_ms << " ms." << std::endl; + + // Show found lines + if (!overlay || useCanny) + { + image = Scalar(0, 0, 0); + } + + ls->drawSegments(image, lines_std); + + String window_name = useRefine ? "Result - standard refinement" : "Result - no refinement"; + window_name += useCanny ? " - Canny edge detector used" : ""; + + imshow(window_name, image); + + waitKey(); + return 0; +} From 1e74f5850bec99979584e0e37eeeab8c133be3a9 Mon Sep 17 00:00:00 2001 From: Tomoaki Teshima Date: Fri, 1 Oct 2021 23:17:02 +0900 Subject: [PATCH 07/11] suppress GaussianBlur to generate empty images * sharp Gaussian kernel causes over flow and ends up in blank image --- modules/cudafilters/test/test_filters.cpp | 4 ++-- modules/imgproc/src/smooth.simd.hpp | 5 ++++- modules/imgproc/test/test_smooth_bitexact.cpp | 11 +++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/modules/cudafilters/test/test_filters.cpp b/modules/cudafilters/test/test_filters.cpp index a86d8c9fff..bb235dad09 100644 --- a/modules/cudafilters/test/test_filters.cpp +++ b/modules/cudafilters/test/test_filters.cpp @@ -445,8 +445,8 @@ PARAM_TEST_CASE(GaussianBlur, cv::cuda::DeviceInfo, cv::Size, MatDepth, Channels CUDA_TEST_P(GaussianBlur, Accuracy) { cv::Mat src = randomMat(size, type); - double sigma1 = randomDouble(0.1, 1.0); - double sigma2 = randomDouble(0.1, 1.0); + double sigma1 = randomDouble(0.0, 1.0); + double sigma2 = randomDouble(0.0, 1.0); cv::Ptr gauss = cv::cuda::createGaussianFilter(src.type(), -1, ksize, sigma1, sigma2, borderType); diff --git a/modules/imgproc/src/smooth.simd.hpp b/modules/imgproc/src/smooth.simd.hpp index 3a39765c71..62ff31ac94 100644 --- a/modules/imgproc/src/smooth.simd.hpp +++ b/modules/imgproc/src/smooth.simd.hpp @@ -1958,7 +1958,10 @@ public: } else if (kxlen % 2 == 1) { - hlineSmoothFunc = hlineSmoothONa_yzy_a; + if (kx[(kxlen - 1)/ 2] == FT::one()) + hlineSmoothFunc = hlineSmooth1N1; + else + hlineSmoothFunc = hlineSmoothONa_yzy_a; for (int i = 0; i < kxlen / 2; i++) if (!(kx[i] == kx[kxlen - 1 - i])) { diff --git a/modules/imgproc/test/test_smooth_bitexact.cpp b/modules/imgproc/test/test_smooth_bitexact.cpp index 246f1df798..d4ae2af833 100644 --- a/modules/imgproc/test/test_smooth_bitexact.cpp +++ b/modules/imgproc/test/test_smooth_bitexact.cpp @@ -249,4 +249,15 @@ TEST(GaussianBlur_Bitexact, regression_9863) checkGaussianBlur_8Uvs32F(src8u, src32f, 151, 30); } +TEST(GaussianBlur_Bitexact, overflow_20792) +{ + Mat src(128, 128, CV_16UC1, Scalar(255)); + Mat dst; + double sigma = theRNG().uniform(0.0, 0.2); // a peaky kernel + GaussianBlur(src, dst, Size(7, 7), sigma, 0.9); + int count = (int)countNonZero(dst); + int nintyPercent = (int)(src.rows*src.cols * 0.9); + EXPECT_GT(count, nintyPercent); +} + }} // namespace From f977d10a19e1da13532ea038ac8d7e2cf882040f Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Fri, 1 Oct 2021 18:51:01 +0000 Subject: [PATCH 08/11] dnn(ocl): fix conv DWCONV workgroup --- .../dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp | 15 +++++++++------ modules/dnn/src/opencl/conv_layer_spatial.cl | 11 +++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp index a1164273ac..45bd249e5d 100644 --- a/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp +++ b/modules/dnn/src/ocl4dnn/src/ocl4dnn_conv_spatial.cpp @@ -1034,12 +1034,15 @@ bool OCL4DNNConvSpatial::convolve(const UMat &bottom, UMat &top, kernel.set(argIdx++, (uint16_t)output_w_); kernel.set(argIdx++, (uint16_t)output_h_); - size_t global_size[3]; - global_size[0] = output_w_; - global_size[1] = output_h_; - global_size[2] = num_output_ * num_; - - if (!kernel.run_(3, global_size, NULL, false)) + size_t wgs = kernel.workGroupSize(); + if (!wgs) + { + CV_LOG_ERROR(NULL, "DNN/OpenCL: Can't query workGroupSize of DWCONV kernel"); + return false; + } + size_t lws[1] = { wgs }; + size_t gws[1] = { roundUp((size_t)output_w_ * output_h_ * num_output_ * num_, (unsigned)lws[0]) }; + if (!kernel.run_(1, gws, lws, false)) { CV_LOG_ERROR(NULL, "DNN/OpenCL: DWCONV kernel run failed"); return false; diff --git a/modules/dnn/src/opencl/conv_layer_spatial.cl b/modules/dnn/src/opencl/conv_layer_spatial.cl index 455f0ed7ea..eb5d354020 100644 --- a/modules/dnn/src/opencl/conv_layer_spatial.cl +++ b/modules/dnn/src/opencl/conv_layer_spatial.cl @@ -1850,10 +1850,13 @@ __kernel void DWCONV( const ushort output_width, const ushort output_height) { __global Dtype* convolved_image = convolved_image_base + convolved_image_offset; - const int outputX = get_global_id(0); - const int outputY = get_global_id(1); - const int outputZ = get_global_id(2); - if(outputX < output_width && outputY < output_height) + const int out_idx = get_global_id(0); // 1D task layout: [output_width * output_height * OUTPUT_Z] + const int plane_size = output_width * output_height; + const int out_plane_idx = out_idx % plane_size; + const int outputZ = out_idx / plane_size; + const int outputY = out_plane_idx / output_width; + const int outputX = out_plane_idx % output_width; + if (outputZ < OUTPUT_Z) { Dtype sum = 0.; From 259c39a63aa0be0d1bcda6bc6741548b24ecd13d Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Sat, 2 Oct 2021 13:32:11 +0300 Subject: [PATCH 09/11] additional changes --- modules/imgproc/include/opencv2/imgproc.hpp | 7 +++++-- modules/imgproc/src/lsd.cpp | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 855cbaf159..576bebc21b 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -1228,6 +1228,9 @@ An example using the LineSegmentDetector /** @brief Line segment detector class following the algorithm described at @cite Rafael12 . + +@note Implementation has been removed from OpenCV version 3.4.6 to 3.4.15 and version 4.1.0 to 4.5.3 due original code license conflict. +restored again after [Computation of a NFA](https://github.com/rafael-grompone-von-gioi/binomial_nfa) code published under the MIT license. */ class CV_EXPORTS_W LineSegmentDetector : public Algorithm { @@ -1241,8 +1244,8 @@ public: @param image A grayscale (CV_8UC1) input image. If only a roi needs to be selected, use: `lsd_ptr-\>detect(image(roi), lines, ...); lines += Scalar(roi.x, roi.y, roi.x, roi.y);` - @param lines A vector of Vec4i or Vec4f elements specifying the beginning and ending point of a line. Where - Vec4i/Vec4f is (x1, y1, x2, y2), point 1 is the start, point 2 - end. Returned lines are strictly + @param lines A vector of Vec4f elements specifying the beginning and ending point of a line. Where + Vec4f is (x1, y1, x2, y2), point 1 is the start, point 2 - end. Returned lines are strictly oriented depending on the gradient. @param width Vector of widths of the regions, where the lines are found. E.g. Width of line. @param prec Vector of precisions with which the lines are found. diff --git a/modules/imgproc/src/lsd.cpp b/modules/imgproc/src/lsd.cpp index d06759c2bb..c4a77ee265 100644 --- a/modules/imgproc/src/lsd.cpp +++ b/modules/imgproc/src/lsd.cpp @@ -191,8 +191,8 @@ public: * If only a roi needs to be selected, use * lsd_ptr->detect(image(roi), ..., lines); * lines += Scalar(roi.x, roi.y, roi.x, roi.y); - * @param _lines Return: A vector of Vec4i or Vec4f elements specifying the beginning and ending point of a line. - * Where Vec4i/Vec4f is (x1, y1, x2, y2), point 1 is the start, point 2 - end. + * @param _lines Return: A vector of Vec4f elements specifying the beginning and ending point of a line. + * Where Vec4f is (x1, y1, x2, y2), point 1 is the start, point 2 - end. * Returned lines are strictly oriented depending on the gradient. * @param width Return: Vector of widths of the regions, where the lines are found. E.g. Width of line. * @param prec Return: Vector of precisions with which the lines are found. From ef53a9229f4f666459480b2366a4680921d45e5a Mon Sep 17 00:00:00 2001 From: Jannik Bamberger Date: Sat, 2 Oct 2021 00:04:23 +0200 Subject: [PATCH 10/11] Automatically set the correct OpenCV version in build.gradle Automatically sets the correct OpenCV version in the CMAKE example contained in the build.gradle file of the Android SDK. --- modules/java/android_sdk/build.gradle.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/java/android_sdk/build.gradle.in b/modules/java/android_sdk/build.gradle.in index 6d317fcd39..14a7b2c6fe 100644 --- a/modules/java/android_sdk/build.gradle.in +++ b/modules/java/android_sdk/build.gradle.in @@ -58,7 +58,7 @@ // // - Use find_package() in app/CMakeLists.txt: // -// find_package(OpenCV 3.4 REQUIRED java) +// find_package(OpenCV @OPENCV_VERSION_MAJOR@.@OPENCV_VERSION_MINOR@ REQUIRED java) // ... // target_link_libraries(native-lib ${OpenCV_LIBRARIES}) // From de5b6386e01d2e6ea622913627b8776c73d6ada3 Mon Sep 17 00:00:00 2001 From: Jonas Vautherin Date: Sat, 2 Oct 2021 03:07:25 +0200 Subject: [PATCH 11/11] Fix gst_initializer Use the return value of gst_init_check instead of testing the error pointer --- modules/videoio/src/cap_gstreamer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/videoio/src/cap_gstreamer.cpp b/modules/videoio/src/cap_gstreamer.cpp index 6bad528909..d8390691ff 100644 --- a/modules/videoio/src/cap_gstreamer.cpp +++ b/modules/videoio/src/cap_gstreamer.cpp @@ -216,10 +216,10 @@ private: call_deinit = utils::getConfigurationParameterBool("OPENCV_VIDEOIO_GSTREAMER_CALL_DEINIT", false); GSafePtr err; - gst_init_check(NULL, NULL, err.getRef()); - if (err) + gboolean gst_init_res = gst_init_check(NULL, NULL, err.getRef()); + if (!gst_init_res) { - CV_WARN("Can't initialize GStreamer: " << err->message); + CV_WARN("Can't initialize GStreamer: " << (err ? err->message : "")); isFailed = true; return; }