mirror of
https://github.com/opencv/opencv.git
synced 2026-07-31 08:13:04 +04:00
Merge pull request #29101 from asmorkalov:as/geometry_module
Moved geometry transformations from imgproc to 3d, future geometry module #29101 The first step of 2d geometry operations migration to the future geometry module. I created 2d.hpp to isolate the moved functions for now. I propose to create geometry.hpp when the module is renamed and include all things there. OpenCV contrib: https://github.com/opencv/opencv_contrib/pull/4126 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
committed by
GitHub
parent
ccb808ba55
commit
aac582119c
@@ -1,539 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
#include <queue>
|
||||
|
||||
/****************************************************************************************\
|
||||
* Polygonal Approximation *
|
||||
\****************************************************************************************/
|
||||
|
||||
/* Ramer-Douglas-Peucker algorithm for polygon simplification */
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
template<typename T> static int
|
||||
approxPolyDP_( const Point_<T>* src_contour, int count0, Point_<T>* dst_contour,
|
||||
bool is_closed0, double eps, AutoBuffer<Range>& _stack )
|
||||
{
|
||||
#define PUSH_SLICE(slice) \
|
||||
if( top >= stacksz ) \
|
||||
{ \
|
||||
_stack.resize(stacksz*3/2); \
|
||||
stack = _stack.data(); \
|
||||
stacksz = _stack.size(); \
|
||||
} \
|
||||
stack[top++] = slice
|
||||
|
||||
#define READ_PT(pt, pos) \
|
||||
pt = src_contour[pos]; \
|
||||
if( ++pos >= count ) pos = 0
|
||||
|
||||
#define READ_DST_PT(pt, pos) \
|
||||
pt = dst_contour[pos]; \
|
||||
if( ++pos >= count ) pos = 0
|
||||
|
||||
#define WRITE_PT(pt) \
|
||||
dst_contour[new_count++] = pt
|
||||
|
||||
typedef cv::Point_<T> PT;
|
||||
int init_iters = 3;
|
||||
Range slice(0, 0), right_slice(0, 0);
|
||||
PT start_pt((T)-1000000, (T)-1000000), end_pt(0, 0), pt(0,0);
|
||||
int i = 0, j, pos = 0, wpos, count = count0, new_count=0;
|
||||
int is_closed = is_closed0;
|
||||
bool le_eps = false;
|
||||
size_t top = 0, stacksz = _stack.size();
|
||||
Range* stack = _stack.data();
|
||||
|
||||
if( count == 0 )
|
||||
return 0;
|
||||
|
||||
eps *= eps;
|
||||
|
||||
if( !is_closed )
|
||||
{
|
||||
right_slice.start = count;
|
||||
end_pt = src_contour[0];
|
||||
start_pt = src_contour[count-1];
|
||||
|
||||
if( start_pt.x != end_pt.x || start_pt.y != end_pt.y )
|
||||
{
|
||||
slice.start = 0;
|
||||
slice.end = count - 1;
|
||||
PUSH_SLICE(slice);
|
||||
}
|
||||
else
|
||||
{
|
||||
is_closed = 1;
|
||||
init_iters = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if( is_closed )
|
||||
{
|
||||
// 1. Find approximately two farthest points of the contour
|
||||
right_slice.start = 0;
|
||||
|
||||
for( i = 0; i < init_iters; i++ )
|
||||
{
|
||||
double dist, max_dist = 0;
|
||||
pos = (pos + right_slice.start) % count;
|
||||
READ_PT(start_pt, pos);
|
||||
|
||||
for( j = 1; j < count; j++ )
|
||||
{
|
||||
double dx, dy;
|
||||
|
||||
READ_PT(pt, pos);
|
||||
dx = pt.x - start_pt.x;
|
||||
dy = pt.y - start_pt.y;
|
||||
|
||||
dist = dx * dx + dy * dy;
|
||||
|
||||
if( dist > max_dist )
|
||||
{
|
||||
max_dist = dist;
|
||||
right_slice.start = j;
|
||||
}
|
||||
}
|
||||
|
||||
le_eps = max_dist <= eps;
|
||||
}
|
||||
|
||||
// 2. initialize the stack
|
||||
if( !le_eps )
|
||||
{
|
||||
right_slice.end = slice.start = pos % count;
|
||||
slice.end = right_slice.start = (right_slice.start + slice.start) % count;
|
||||
|
||||
PUSH_SLICE(right_slice);
|
||||
PUSH_SLICE(slice);
|
||||
}
|
||||
else
|
||||
WRITE_PT(start_pt);
|
||||
}
|
||||
|
||||
// 3. run recursive process
|
||||
while( top > 0 )
|
||||
{
|
||||
slice = stack[--top];
|
||||
end_pt = src_contour[slice.end];
|
||||
pos = slice.start;
|
||||
READ_PT(start_pt, pos);
|
||||
|
||||
if( pos != slice.end )
|
||||
{
|
||||
double dx, dy, max_dist_2_mul_segment_len_2 = 0;
|
||||
|
||||
dx = end_pt.x - start_pt.x;
|
||||
dy = end_pt.y - start_pt.y;
|
||||
double segment_len_2 = dx * dx + dy * dy;
|
||||
|
||||
CV_Assert( dx != 0 || dy != 0 );
|
||||
|
||||
while( pos != slice.end )
|
||||
{
|
||||
READ_PT(pt, pos);
|
||||
double projection = ((pt.x - start_pt.x) * dx + (pt.y - start_pt.y) * dy);
|
||||
|
||||
double dist_2_mul_segment_len_2;
|
||||
if ( projection < 0 )
|
||||
{
|
||||
dist_2_mul_segment_len_2 = ((pt.x - start_pt.x) * (pt.x - start_pt.x) + (pt.y - start_pt.y) * (pt.y - start_pt.y)) * segment_len_2;
|
||||
} else if ( projection > segment_len_2 )
|
||||
{
|
||||
dist_2_mul_segment_len_2 = ((pt.x - end_pt.x) * (pt.x - end_pt.x) + (pt.y - end_pt.y) * (pt.y - end_pt.y)) * segment_len_2;
|
||||
} else
|
||||
{
|
||||
double dist = ((pt.y - start_pt.y) * dx - (pt.x - start_pt.x) * dy);
|
||||
dist_2_mul_segment_len_2 = dist * dist;
|
||||
}
|
||||
|
||||
if( dist_2_mul_segment_len_2 > max_dist_2_mul_segment_len_2 )
|
||||
{
|
||||
max_dist_2_mul_segment_len_2 = dist_2_mul_segment_len_2;
|
||||
right_slice.start = (pos+count-1)%count;
|
||||
}
|
||||
}
|
||||
|
||||
le_eps = max_dist_2_mul_segment_len_2 <= eps * segment_len_2;
|
||||
}
|
||||
else
|
||||
{
|
||||
le_eps = true;
|
||||
// read starting point
|
||||
start_pt = src_contour[slice.start];
|
||||
}
|
||||
|
||||
if( le_eps )
|
||||
{
|
||||
WRITE_PT(start_pt);
|
||||
}
|
||||
else
|
||||
{
|
||||
right_slice.end = slice.end;
|
||||
slice.end = right_slice.start;
|
||||
PUSH_SLICE(right_slice);
|
||||
PUSH_SLICE(slice);
|
||||
}
|
||||
}
|
||||
|
||||
if( !is_closed )
|
||||
WRITE_PT( src_contour[count-1] );
|
||||
|
||||
// last stage: do final clean-up of the approximated contour -
|
||||
// remove extra points on the [almost] straight lines.
|
||||
is_closed = is_closed0;
|
||||
count = new_count;
|
||||
pos = is_closed ? count - 1 : 0;
|
||||
READ_DST_PT(start_pt, pos);
|
||||
wpos = pos;
|
||||
READ_DST_PT(pt, pos);
|
||||
|
||||
for( i = !is_closed; i < count - !is_closed && new_count > 2; i++ )
|
||||
{
|
||||
double dx, dy, dist, successive_inner_product;
|
||||
READ_DST_PT( end_pt, pos );
|
||||
|
||||
dx = end_pt.x - start_pt.x;
|
||||
dy = end_pt.y - start_pt.y;
|
||||
dist = fabs((pt.x - start_pt.x)*dy - (pt.y - start_pt.y)*dx);
|
||||
successive_inner_product = (pt.x - start_pt.x) * (end_pt.x - pt.x) +
|
||||
(pt.y - start_pt.y) * (end_pt.y - pt.y);
|
||||
|
||||
if( dist * dist <= 0.5*eps*(dx*dx + dy*dy) && dx != 0 && dy != 0 &&
|
||||
successive_inner_product >= 0 )
|
||||
{
|
||||
new_count--;
|
||||
dst_contour[wpos] = start_pt = end_pt;
|
||||
if(++wpos >= count) wpos = 0;
|
||||
READ_DST_PT(pt, pos);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
dst_contour[wpos] = start_pt = pt;
|
||||
if(++wpos >= count) wpos = 0;
|
||||
pt = end_pt;
|
||||
}
|
||||
|
||||
if( !is_closed )
|
||||
dst_contour[wpos] = pt;
|
||||
|
||||
return new_count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void cv::approxPolyDP( InputArray _curve, OutputArray _approxCurve,
|
||||
double epsilon, bool closed )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
//Prevent unreasonable error values (Douglas-Peucker algorithm)
|
||||
//from being used.
|
||||
if (epsilon < 0.0 || !(epsilon < 1e30))
|
||||
{
|
||||
CV_Error(cv::Error::StsOutOfRange, "Epsilon not valid.");
|
||||
}
|
||||
|
||||
Mat curve = _curve.getMat();
|
||||
int npoints = curve.checkVector(2), depth = curve.depth();
|
||||
CV_Assert( npoints >= 0 && (depth == CV_32S || depth == CV_32F));
|
||||
|
||||
if( npoints == 0 )
|
||||
{
|
||||
_approxCurve.release();
|
||||
return;
|
||||
}
|
||||
|
||||
AutoBuffer<Point> _buf(npoints);
|
||||
AutoBuffer<Range> _stack(npoints);
|
||||
Point* buf = _buf.data();
|
||||
int nout = 0;
|
||||
|
||||
if( depth == CV_32S )
|
||||
nout = approxPolyDP_(curve.ptr<Point>(), npoints, buf, closed, epsilon, _stack);
|
||||
else if( depth == CV_32F )
|
||||
nout = approxPolyDP_(curve.ptr<Point2f>(), npoints, (Point2f*)buf, closed, epsilon, _stack);
|
||||
else
|
||||
CV_Error( cv::Error::StsUnsupportedFormat, "" );
|
||||
|
||||
Mat(nout, 1, CV_MAKETYPE(depth, 2), buf).copyTo(_approxCurve);
|
||||
}
|
||||
|
||||
enum class PointStatus : int8_t
|
||||
{
|
||||
REMOVED = -1,
|
||||
RECALCULATE = 0,
|
||||
CALCULATED = 1
|
||||
};
|
||||
|
||||
struct neighbours
|
||||
{
|
||||
PointStatus pointStatus;
|
||||
cv::Point2f point;
|
||||
int next;
|
||||
int prev;
|
||||
|
||||
explicit neighbours(int next_ = -1, int prev_ = -1, const cv::Point2f& point_ = { -1, -1 })
|
||||
{
|
||||
next = next_;
|
||||
prev = prev_;
|
||||
point = point_;
|
||||
pointStatus = PointStatus::CALCULATED;
|
||||
}
|
||||
};
|
||||
|
||||
struct changes
|
||||
{
|
||||
float area;
|
||||
int vertex;
|
||||
cv::Point2f intersection;
|
||||
|
||||
explicit changes(float area_, int vertex_, const cv::Point2f& intersection_)
|
||||
{
|
||||
area = area_;
|
||||
vertex = vertex_;
|
||||
intersection = intersection_;
|
||||
}
|
||||
|
||||
bool operator < (const changes& elem) const
|
||||
{
|
||||
return (area < elem.area) || ((area == elem.area) && (vertex < elem.vertex));
|
||||
}
|
||||
bool operator > (const changes& elem) const
|
||||
{
|
||||
return (area > elem.area) || ((area == elem.area) && (vertex > elem.vertex));
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
returns intersection point and extra area
|
||||
*/
|
||||
static void recalculation(std::vector<neighbours>& hull, int vertex_id, float& area_, float& x, float& y)
|
||||
{
|
||||
cv::Point2f vertex = hull[vertex_id].point,
|
||||
next_vertex = hull[hull[vertex_id].next].point,
|
||||
extra_vertex_1 = hull[hull[vertex_id].prev].point,
|
||||
extra_vertex_2 = hull[hull[hull[vertex_id].next].next].point;
|
||||
|
||||
cv::Point2f curr_edge = next_vertex - vertex,
|
||||
prev_edge = vertex - extra_vertex_1,
|
||||
next_edge = extra_vertex_2 - next_vertex;
|
||||
|
||||
float cross = prev_edge.x * next_edge.y - prev_edge.y * next_edge.x;
|
||||
if (abs(cross) < 1e-8)
|
||||
{
|
||||
area_ = FLT_MAX;
|
||||
x = -1;
|
||||
y = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
float t = (curr_edge.x * next_edge.y - curr_edge.y * next_edge.x) / cross;
|
||||
cv::Point2f intersection = vertex + cv::Point2f(prev_edge.x * t, prev_edge.y * t);
|
||||
|
||||
float area = 0.5f * abs((next_vertex.x - vertex.x) * (intersection.y - vertex.y)
|
||||
- (intersection.x - vertex.x) * (next_vertex.y - vertex.y));
|
||||
|
||||
area_ = area;
|
||||
x = intersection.x;
|
||||
y = intersection.y;
|
||||
}
|
||||
|
||||
static void update(std::vector<neighbours>& hull, int vertex_id)
|
||||
{
|
||||
neighbours& v1 = hull[vertex_id], & removed = hull[v1.next], & v2 = hull[removed.next];
|
||||
|
||||
removed.pointStatus = PointStatus::REMOVED;
|
||||
v1.pointStatus = PointStatus::RECALCULATE;
|
||||
v2.pointStatus = PointStatus::RECALCULATE;
|
||||
hull[v1.prev].pointStatus = PointStatus::RECALCULATE;
|
||||
v1.next = removed.next;
|
||||
v2.prev = removed.prev;
|
||||
}
|
||||
|
||||
/*
|
||||
A greedy algorithm based on contraction of vertices for approximating a convex contour by a bounding polygon
|
||||
*/
|
||||
void cv::approxPolyN(InputArray _curve, OutputArray _approxCurve,
|
||||
int nsides, float epsilon_percentage, bool ensure_convex)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CV_Assert(epsilon_percentage > 0 || epsilon_percentage == -1);
|
||||
CV_Assert(nsides > 2);
|
||||
|
||||
if (_approxCurve.fixedType())
|
||||
{
|
||||
CV_Assert(_approxCurve.type() == CV_32FC2 || _approxCurve.type() == CV_32SC2);
|
||||
}
|
||||
|
||||
Mat curve;
|
||||
int depth = _curve.depth();
|
||||
|
||||
CV_Assert(depth == CV_32F || depth == CV_32S);
|
||||
|
||||
if (ensure_convex)
|
||||
{
|
||||
cv::convexHull(_curve, curve);
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Assert(isContourConvex(_curve));
|
||||
curve = _curve.getMat();
|
||||
}
|
||||
|
||||
CV_Assert((curve.cols == 1 && curve.rows >= nsides)
|
||||
|| (curve.rows == 1 && curve.cols >= nsides));
|
||||
|
||||
if (curve.rows == 1)
|
||||
{
|
||||
curve = curve.reshape(0, curve.cols);
|
||||
}
|
||||
|
||||
std::vector<neighbours> hull(curve.rows);
|
||||
int size = curve.rows;
|
||||
std::priority_queue<changes, std::vector<changes>, std::greater<changes>> areas;
|
||||
float extra_area = 0, max_extra_area = epsilon_percentage * static_cast<float>(contourArea(_curve));
|
||||
|
||||
if (curve.depth() == CV_32S)
|
||||
{
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
Point t = curve.at<cv::Point>(i, 0);
|
||||
hull[i] = neighbours(i + 1, i - 1, Point2f(static_cast<float>(t.x), static_cast<float>(t.y)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
Point2f t = curve.at<cv::Point2f>(i, 0);
|
||||
hull[i] = neighbours(i + 1, i - 1, t);
|
||||
}
|
||||
}
|
||||
hull[0].prev = size - 1;
|
||||
hull[size - 1].next = 0;
|
||||
|
||||
if (size > nsides)
|
||||
{
|
||||
for (int vertex_id = 0; vertex_id < size; ++vertex_id)
|
||||
{
|
||||
float area, new_x, new_y;
|
||||
recalculation(hull, vertex_id, area, new_x, new_y);
|
||||
|
||||
areas.push(changes(area, vertex_id, Point2f(new_x, new_y)));
|
||||
}
|
||||
}
|
||||
|
||||
while (size > nsides)
|
||||
{
|
||||
changes base = areas.top();
|
||||
int vertex_id = base.vertex;
|
||||
|
||||
if (hull[vertex_id].pointStatus == PointStatus::REMOVED)
|
||||
{
|
||||
areas.pop();
|
||||
}
|
||||
else if (hull[vertex_id].pointStatus == PointStatus::RECALCULATE)
|
||||
{
|
||||
float area, new_x, new_y;
|
||||
areas.pop();
|
||||
recalculation(hull, vertex_id, area, new_x, new_y);
|
||||
|
||||
areas.push(changes(area, vertex_id, Point2f(new_x, new_y)));
|
||||
hull[vertex_id].pointStatus = PointStatus::CALCULATED;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (epsilon_percentage != -1)
|
||||
{
|
||||
extra_area += base.area;
|
||||
if (extra_area > max_extra_area)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size--;
|
||||
hull[vertex_id].point = base.intersection;
|
||||
update(hull, vertex_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (_approxCurve.fixedType())
|
||||
{
|
||||
depth = _approxCurve.depth();
|
||||
}
|
||||
_approxCurve.create(1, size, CV_MAKETYPE(depth, 2));
|
||||
Mat buf = _approxCurve.getMat();
|
||||
int last_free = 0;
|
||||
|
||||
if (depth == CV_32S)
|
||||
{
|
||||
for (int i = 0; i < curve.rows; ++i)
|
||||
{
|
||||
if (hull[i].pointStatus != PointStatus::REMOVED)
|
||||
{
|
||||
Point t = Point(static_cast<int>(round(hull[i].point.x)),
|
||||
static_cast<int>(round(hull[i].point.y)));
|
||||
|
||||
buf.at<Point>(0, last_free) = t;
|
||||
last_free++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < curve.rows; ++i)
|
||||
{
|
||||
if (hull[i].pointStatus != PointStatus::REMOVED)
|
||||
{
|
||||
buf.at<Point2f>(0, last_free) = hull[i].point;
|
||||
last_free++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* End of file. */
|
||||
@@ -6,10 +6,290 @@
|
||||
#include "contours_common.hpp"
|
||||
#include <map>
|
||||
#include <limits>
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
#include "opencv2/core/check.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
// calculates length of a curve (e.g. contour perimeter)
|
||||
double cv::arcLength( InputArray _curve, bool is_closed )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat curve = _curve.getMat();
|
||||
int count = curve.checkVector(2);
|
||||
int depth = curve.depth();
|
||||
CV_Assert( count >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
double perimeter = 0;
|
||||
|
||||
int i;
|
||||
|
||||
if( count <= 1 )
|
||||
return 0.;
|
||||
|
||||
bool is_float = depth == CV_32F;
|
||||
int last = is_closed ? count-1 : 0;
|
||||
const Point* pti = curve.ptr<Point>();
|
||||
const Point2f* ptf = curve.ptr<Point2f>();
|
||||
|
||||
Point2f prev = is_float ? ptf[last] : Point2f((float)pti[last].x,(float)pti[last].y);
|
||||
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
Point2f p = is_float ? ptf[i] : Point2f((float)pti[i].x,(float)pti[i].y);
|
||||
float dx = p.x - prev.x, dy = p.y - prev.y;
|
||||
perimeter += std::sqrt(dx*dx + dy*dy);
|
||||
|
||||
prev = p;
|
||||
}
|
||||
|
||||
return perimeter;
|
||||
}
|
||||
|
||||
static Rect maskBoundingRect( const Mat& img )
|
||||
{
|
||||
CV_Assert( img.depth() <= CV_8S && img.channels() == 1 );
|
||||
|
||||
Size size = img.size();
|
||||
int xmin = size.width, ymin = -1, xmax = -1, ymax = -1, i, j, k;
|
||||
|
||||
for( i = 0; i < size.height; i++ )
|
||||
{
|
||||
const uchar* _ptr = img.ptr(i);
|
||||
const uchar* ptr = (const uchar*)alignPtr(_ptr, 4);
|
||||
int have_nz = 0, k_min, offset = (int)(ptr - _ptr);
|
||||
j = 0;
|
||||
offset = MIN(offset, size.width);
|
||||
for( ; j < offset; j++ )
|
||||
if( _ptr[j] )
|
||||
{
|
||||
if( j < xmin )
|
||||
xmin = j;
|
||||
if( j > xmax )
|
||||
xmax = j;
|
||||
have_nz = 1;
|
||||
}
|
||||
if( offset < size.width )
|
||||
{
|
||||
xmin -= offset;
|
||||
xmax -= offset;
|
||||
size.width -= offset;
|
||||
j = 0;
|
||||
for( ; j <= xmin - 4; j += 4 )
|
||||
if( *((int*)(ptr+j)) )
|
||||
break;
|
||||
for( ; j < xmin; j++ )
|
||||
if( ptr[j] )
|
||||
{
|
||||
xmin = j;
|
||||
if( j > xmax )
|
||||
xmax = j;
|
||||
have_nz = 1;
|
||||
break;
|
||||
}
|
||||
k_min = MAX(j-1, xmax);
|
||||
k = size.width - 1;
|
||||
for( ; k > k_min && (k&3) != 3; k-- )
|
||||
if( ptr[k] )
|
||||
break;
|
||||
if( k > k_min && (k&3) == 3 )
|
||||
{
|
||||
for( ; k > k_min+3; k -= 4 )
|
||||
if( *((int*)(ptr+k-3)) )
|
||||
break;
|
||||
}
|
||||
for( ; k > k_min; k-- )
|
||||
if( ptr[k] )
|
||||
{
|
||||
xmax = k;
|
||||
have_nz = 1;
|
||||
break;
|
||||
}
|
||||
if( !have_nz )
|
||||
{
|
||||
j &= ~3;
|
||||
for( ; j <= k - 3; j += 4 )
|
||||
if( *((int*)(ptr+j)) )
|
||||
break;
|
||||
for( ; j <= k; j++ )
|
||||
if( ptr[j] )
|
||||
{
|
||||
have_nz = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
xmin += offset;
|
||||
xmax += offset;
|
||||
size.width += offset;
|
||||
}
|
||||
if( have_nz )
|
||||
{
|
||||
if( ymin < 0 )
|
||||
ymin = i;
|
||||
ymax = i;
|
||||
}
|
||||
}
|
||||
|
||||
if( xmin >= size.width )
|
||||
xmin = ymin = 0;
|
||||
return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
|
||||
}
|
||||
|
||||
// Calculates bounding rectangle of a point set or retrieves already calculated
|
||||
static Rect pointSetBoundingRect( const Mat& points )
|
||||
{
|
||||
int npoints = points.checkVector(2);
|
||||
int depth = points.depth();
|
||||
CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
|
||||
int xmin = 0, ymin = 0, xmax = -1, ymax = -1, i = 0;
|
||||
bool is_float = depth == CV_32F;
|
||||
|
||||
if( npoints == 0 )
|
||||
return Rect();
|
||||
|
||||
if( !is_float )
|
||||
{
|
||||
const int32_t* pts = points.ptr<int32_t>();
|
||||
int64_t firstval = 0;
|
||||
std::memcpy(&firstval, pts, sizeof(pts[0]) * 2);
|
||||
xmin = xmax = pts[0];
|
||||
ymin = ymax = pts[1];
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
v_int32 minval, maxval;
|
||||
minval = maxval = v_reinterpret_as_s32(vx_setall_s64(firstval)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y
|
||||
const int nlanes = VTraits<v_int32>::vlanes()/2;
|
||||
for (; i < npoints; i += nlanes)
|
||||
{
|
||||
if (i > npoints - nlanes)
|
||||
{
|
||||
if (i == 0)
|
||||
break;
|
||||
i = npoints - nlanes;
|
||||
}
|
||||
v_int32 ptXY2 = vx_load(pts + 2 * i);
|
||||
minval = v_min(ptXY2, minval);
|
||||
maxval = v_max(ptXY2, maxval);
|
||||
}
|
||||
constexpr int max_nlanes = VTraits<v_int32>::max_nlanes;
|
||||
int arr_minval[max_nlanes], arr_maxval[max_nlanes];
|
||||
vx_store(arr_minval, minval);
|
||||
vx_store(arr_maxval, maxval);
|
||||
for (int j = 0; j < nlanes; j++)
|
||||
{
|
||||
xmin = std::min(xmin, arr_minval[2*j]);
|
||||
ymin = std::min(ymin, arr_minval[2*j+1]);
|
||||
xmax = std::max(xmax, arr_maxval[2*j]);
|
||||
ymax = std::max(ymax, arr_maxval[2*j+1]);
|
||||
}
|
||||
#endif
|
||||
for( ; i < npoints; i++ )
|
||||
{
|
||||
int pt_x = pts[2*i];
|
||||
int pt_y = pts[2*i+1];
|
||||
|
||||
xmin = std::min(xmin, pt_x);
|
||||
xmax = std::max(xmax, pt_x);
|
||||
ymin = std::min(ymin, pt_y);
|
||||
ymax = std::max(ymax, pt_y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const float* pts = points.ptr<float>();
|
||||
int64_t firstval = 0;
|
||||
std::memcpy(&firstval, pts, sizeof(pts[0]) * 2);
|
||||
xmin = xmax = cvFloor(pts[0]);
|
||||
ymin = ymax = cvFloor(pts[1]);
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
v_float32 minval, maxval;
|
||||
minval = maxval = v_reinterpret_as_f32(vx_setall_s64(firstval)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y
|
||||
const int nlanes = VTraits<v_float32>::vlanes()/2;
|
||||
for (; i < npoints; i += nlanes)
|
||||
{
|
||||
if (i > npoints - nlanes)
|
||||
{
|
||||
if (i == 0)
|
||||
break;
|
||||
i = npoints - nlanes;
|
||||
}
|
||||
v_float32 ptXY2 = vx_load(pts + 2 * i);
|
||||
minval = v_min(ptXY2, minval);
|
||||
maxval = v_max(ptXY2, maxval);
|
||||
}
|
||||
constexpr int max_nlanes = VTraits<v_int32>::max_nlanes;
|
||||
float arr_minval[max_nlanes], arr_maxval[max_nlanes];
|
||||
vx_store(arr_minval, minval);
|
||||
vx_store(arr_maxval, maxval);
|
||||
for (int j = 0; j < nlanes; j++)
|
||||
{
|
||||
int _xmin = cvFloor(arr_minval[2*j]), _ymin = cvFloor(arr_minval[2*j+1]);
|
||||
int _xmax = cvFloor(arr_maxval[2*j]), _ymax = cvFloor(arr_maxval[2*j+1]);
|
||||
xmin = std::min(xmin, _xmin);
|
||||
ymin = std::min(ymin, _ymin);
|
||||
xmax = std::max(xmax, _xmax);
|
||||
ymax = std::max(ymax, _ymax);
|
||||
}
|
||||
#endif
|
||||
for( ; i < npoints; i++ )
|
||||
{
|
||||
// because right and bottom sides of the bounding rectangle are not inclusive
|
||||
// (note +1 in width and height calculation below), cvFloor is used here instead of cvCeil
|
||||
int pt_x = cvFloor(pts[2*i]);
|
||||
int pt_y = cvFloor(pts[2*i+1]);
|
||||
|
||||
xmin = std::min(xmin, pt_x);
|
||||
xmax = std::max(xmax, pt_x);
|
||||
ymin = std::min(ymin, pt_y);
|
||||
ymax = std::max(ymax, pt_y);
|
||||
}
|
||||
}
|
||||
|
||||
return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
|
||||
}
|
||||
|
||||
cv::Rect cv::boundingRect(InputArray array)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat m = array.getMat();
|
||||
return m.depth() <= CV_8U ? maskBoundingRect(m) : pointSetBoundingRect(m);
|
||||
}
|
||||
|
||||
// area of a whole sequence
|
||||
double cv::contourArea( InputArray _contour, bool oriented )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat contour = _contour.getMat();
|
||||
int npoints = contour.checkVector(2);
|
||||
int depth = contour.depth();
|
||||
CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
|
||||
if( npoints == 0 )
|
||||
return 0.;
|
||||
|
||||
double a00 = 0;
|
||||
bool is_float = depth == CV_32F;
|
||||
const Point* ptsi = contour.ptr<Point>();
|
||||
const Point2f* ptsf = contour.ptr<Point2f>();
|
||||
Point2f prev = is_float ? ptsf[npoints-1] : Point2f((float)ptsi[npoints-1].x, (float)ptsi[npoints-1].y);
|
||||
|
||||
for( int i = 0; i < npoints; i++ )
|
||||
{
|
||||
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
|
||||
a00 += (double)prev.x * p.y - (double)prev.y * p.x;
|
||||
prev = p;
|
||||
}
|
||||
|
||||
a00 *= 0.5;
|
||||
if( !oriented )
|
||||
a00 = fabs(a00);
|
||||
|
||||
return a00;
|
||||
}
|
||||
|
||||
void cv::contourTreeToResults(CTree& tree,
|
||||
int res_type,
|
||||
OutputArrayOfArrays& _contours,
|
||||
|
||||
@@ -1,495 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
template<typename _Tp, typename _DotTp>
|
||||
static int Sklansky_( Point_<_Tp>** array, int start, int end, int* stack, int nsign, int sign2 )
|
||||
{
|
||||
int incr = end > start ? 1 : -1;
|
||||
// prepare first triangle
|
||||
int pprev = start, pcur = pprev + incr, pnext = pcur + incr;
|
||||
int stacksize = 3;
|
||||
|
||||
if( start == end ||
|
||||
(array[start]->x == array[end]->x &&
|
||||
array[start]->y == array[end]->y) )
|
||||
{
|
||||
stack[0] = start;
|
||||
return 1;
|
||||
}
|
||||
|
||||
stack[0] = pprev;
|
||||
stack[1] = pcur;
|
||||
stack[2] = pnext;
|
||||
|
||||
end += incr; // make end = afterend
|
||||
|
||||
while( pnext != end )
|
||||
{
|
||||
// check the angle p1,p2,p3
|
||||
_Tp cury = array[pcur]->y;
|
||||
_Tp nexty = array[pnext]->y;
|
||||
_Tp by = nexty - cury;
|
||||
|
||||
if( CV_SIGN( by ) != nsign )
|
||||
{
|
||||
Vec<_Tp, 2> a(array[pcur]->x - array[pprev]->x, cury - array[pprev]->y);
|
||||
Vec<_Tp, 2> b(array[pnext]->x - array[pcur]->x, by);
|
||||
if (std::is_floating_point<_Tp>::value)
|
||||
{
|
||||
a = normalize(a);
|
||||
b = normalize(b);
|
||||
}
|
||||
_DotTp convexity = (_DotTp)a[1]*b[0] - (_DotTp)a[0]*b[1]; // if >0 then convex angle
|
||||
|
||||
if( CV_SIGN( convexity ) == sign2 && (a[0] != 0 || a[1] != 0) )
|
||||
{
|
||||
pprev = pcur;
|
||||
pcur = pnext;
|
||||
pnext += incr;
|
||||
stack[stacksize] = pnext;
|
||||
stacksize++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( pprev == start )
|
||||
{
|
||||
pcur = pnext;
|
||||
stack[1] = pcur;
|
||||
pnext += incr;
|
||||
stack[2] = pnext;
|
||||
}
|
||||
else
|
||||
{
|
||||
stack[stacksize-2] = pnext;
|
||||
pcur = pprev;
|
||||
pprev = stack[stacksize-4];
|
||||
stacksize--;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pnext += incr;
|
||||
stack[stacksize-1] = pnext;
|
||||
}
|
||||
}
|
||||
|
||||
return --stacksize;
|
||||
}
|
||||
|
||||
|
||||
template<typename _Tp>
|
||||
struct CHullCmpPoints
|
||||
{
|
||||
bool operator()(const Point_<_Tp>* p1, const Point_<_Tp>* p2) const
|
||||
{
|
||||
if( p1->x != p2->x )
|
||||
return p1->x < p2->x;
|
||||
if( p1->y != p2->y )
|
||||
return p1->y < p2->y;
|
||||
return p1 < p2;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void convexHull( InputArray _points, OutputArray _hull, bool clockwise, bool returnPoints )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
CV_Assert(_points.getObj() != _hull.getObj());
|
||||
Mat points = _points.getMat();
|
||||
int i, total = points.checkVector(2), depth = points.depth(), nout = 0;
|
||||
int miny_ind = 0, maxy_ind = 0;
|
||||
CV_Assert(total >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
|
||||
if( total == 0 )
|
||||
{
|
||||
_hull.release();
|
||||
return;
|
||||
}
|
||||
|
||||
returnPoints = !_hull.fixedType() ? returnPoints : _hull.type() != CV_32S;
|
||||
|
||||
bool is_float = depth == CV_32F;
|
||||
AutoBuffer<Point*> _pointer(total);
|
||||
AutoBuffer<int> _stack(total + 2), _hullbuf(total);
|
||||
Point** pointer = _pointer.data();
|
||||
Point2f** pointerf = (Point2f**)pointer;
|
||||
Point* data0 = points.ptr<Point>();
|
||||
int* stack = _stack.data();
|
||||
int* hullbuf = _hullbuf.data();
|
||||
|
||||
CV_Assert(points.isContinuous());
|
||||
|
||||
for( i = 0; i < total; i++ )
|
||||
pointer[i] = &data0[i];
|
||||
|
||||
// sort the point set by x-coordinate, find min and max y
|
||||
if( !is_float )
|
||||
{
|
||||
std::sort(pointer, pointer + total, CHullCmpPoints<int>());
|
||||
for( i = 1; i < total; i++ )
|
||||
{
|
||||
int y = pointer[i]->y;
|
||||
if( pointer[miny_ind]->y > y )
|
||||
miny_ind = i;
|
||||
if( pointer[maxy_ind]->y < y )
|
||||
maxy_ind = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::sort(pointerf, pointerf + total, CHullCmpPoints<float>());
|
||||
for( i = 1; i < total; i++ )
|
||||
{
|
||||
float y = pointerf[i]->y;
|
||||
if( pointerf[miny_ind]->y > y )
|
||||
miny_ind = i;
|
||||
if( pointerf[maxy_ind]->y < y )
|
||||
maxy_ind = i;
|
||||
}
|
||||
}
|
||||
|
||||
if( pointer[0]->x == pointer[total-1]->x &&
|
||||
pointer[0]->y == pointer[total-1]->y )
|
||||
{
|
||||
hullbuf[nout++] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// upper half
|
||||
int *tl_stack = stack;
|
||||
int tl_count = !is_float ?
|
||||
Sklansky_<int, int64>( pointer, 0, maxy_ind, tl_stack, -1, 1) :
|
||||
Sklansky_<float, double>( pointerf, 0, maxy_ind, tl_stack, -1, 1);
|
||||
int *tr_stack = stack + tl_count;
|
||||
int tr_count = !is_float ?
|
||||
Sklansky_<int, int64>( pointer, total-1, maxy_ind, tr_stack, -1, -1) :
|
||||
Sklansky_<float, double>( pointerf, total-1, maxy_ind, tr_stack, -1, -1);
|
||||
|
||||
// gather upper part of convex hull to output
|
||||
if( !clockwise )
|
||||
{
|
||||
std::swap( tl_stack, tr_stack );
|
||||
std::swap( tl_count, tr_count );
|
||||
}
|
||||
|
||||
for( i = 0; i < tl_count-1; i++ )
|
||||
hullbuf[nout++] = tl_stack[i];
|
||||
for( i = tr_count - 1; i > 0; i-- )
|
||||
hullbuf[nout++] = tr_stack[i];
|
||||
int stop_idx = tr_count > 2 ? tr_stack[1] : tl_count > 2 ? tl_stack[tl_count - 2] : -1;
|
||||
|
||||
// lower half
|
||||
int *bl_stack = stack;
|
||||
int bl_count = !is_float ?
|
||||
Sklansky_<int, int64>( pointer, 0, miny_ind, bl_stack, 1, -1) :
|
||||
Sklansky_<float, double>( pointerf, 0, miny_ind, bl_stack, 1, -1);
|
||||
int *br_stack = stack + bl_count;
|
||||
int br_count = !is_float ?
|
||||
Sklansky_<int, int64>( pointer, total-1, miny_ind, br_stack, 1, 1) :
|
||||
Sklansky_<float, double>( pointerf, total-1, miny_ind, br_stack, 1, 1);
|
||||
|
||||
if( clockwise )
|
||||
{
|
||||
std::swap( bl_stack, br_stack );
|
||||
std::swap( bl_count, br_count );
|
||||
}
|
||||
|
||||
if( stop_idx >= 0 )
|
||||
{
|
||||
int check_idx = bl_count > 2 ? bl_stack[1] :
|
||||
bl_count + br_count > 2 ? br_stack[2-bl_count] : -1;
|
||||
if( check_idx == stop_idx || (check_idx >= 0 &&
|
||||
pointer[check_idx]->x == pointer[stop_idx]->x &&
|
||||
pointer[check_idx]->y == pointer[stop_idx]->y) )
|
||||
{
|
||||
// if all the points lie on the same line, then
|
||||
// the bottom part of the convex hull is the mirrored top part
|
||||
// (except the exteme points).
|
||||
bl_count = MIN( bl_count, 2 );
|
||||
br_count = MIN( br_count, 2 );
|
||||
}
|
||||
}
|
||||
|
||||
for( i = 0; i < bl_count-1; i++ )
|
||||
hullbuf[nout++] = bl_stack[i];
|
||||
for( i = br_count-1; i > 0; i-- )
|
||||
hullbuf[nout++] = br_stack[i];
|
||||
|
||||
if (!returnPoints)
|
||||
{
|
||||
// Try keep monotonous indices in case of self-intersection.
|
||||
for (i = 0; i < nout; ++i)
|
||||
{
|
||||
auto prev = pointer[hullbuf[(i == 0 ? nout : i) - 1]];
|
||||
auto next = pointer[hullbuf[(i + 1) % nout]];
|
||||
auto cur = pointer[hullbuf[i]];
|
||||
if ((prev < cur && cur < next) || (prev > cur && cur > next))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (int j = hullbuf[i] + 1; j < total; ++j)
|
||||
{
|
||||
cur = pointer[j];
|
||||
if (*pointer[hullbuf[i]] == *cur)
|
||||
{
|
||||
if ((prev < cur && cur < next) || (prev > cur && cur > next))
|
||||
{
|
||||
hullbuf[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
for (i = 0; i < nout; ++i)
|
||||
{
|
||||
hullbuf[i] = int(pointer[hullbuf[i]] - data0);
|
||||
}
|
||||
|
||||
// try to make the convex hull indices form
|
||||
// an ascending or descending sequence by the cyclic
|
||||
// shift of the output sequence.
|
||||
if( nout >= 3 )
|
||||
{
|
||||
int min_idx = 0, max_idx = 0, lt = 0;
|
||||
for( i = 1; i < nout; i++ )
|
||||
{
|
||||
int idx = hullbuf[i];
|
||||
lt += hullbuf[i-1] < idx;
|
||||
if( lt > 1 && lt <= i-2 )
|
||||
break;
|
||||
if( idx < hullbuf[min_idx] )
|
||||
min_idx = i;
|
||||
if( idx > hullbuf[max_idx] )
|
||||
max_idx = i;
|
||||
}
|
||||
int mmdist = std::abs(max_idx - min_idx);
|
||||
if( (mmdist == 1 || mmdist == nout-1) && (lt <= 1 || lt >= nout-2) )
|
||||
{
|
||||
int ascending = (max_idx + 1) % nout == min_idx;
|
||||
int i0 = ascending ? min_idx : max_idx, j = i0;
|
||||
if( i0 > 0 )
|
||||
{
|
||||
for( i = 0; i < nout; i++ )
|
||||
{
|
||||
int curr_idx = stack[i] = hullbuf[j];
|
||||
int next_j = j+1 < nout ? j+1 : 0;
|
||||
int next_idx = hullbuf[next_j];
|
||||
if( i < nout-1 && (ascending != (curr_idx < next_idx)) )
|
||||
break;
|
||||
j = next_j;
|
||||
}
|
||||
if( i == nout )
|
||||
memcpy(hullbuf, stack, nout*sizeof(hullbuf[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !returnPoints )
|
||||
Mat(nout, 1, CV_32S, hullbuf).copyTo(_hull);
|
||||
else
|
||||
{
|
||||
_hull.create(nout, 1, CV_MAKETYPE(depth, 2));
|
||||
Mat hull = _hull.getMat();
|
||||
size_t step = !hull.isContinuous() ? hull.step[0] : sizeof(Point);
|
||||
for( i = 0; i < nout; i++ )
|
||||
*(Point*)(hull.ptr() + i*step) = data0[hullbuf[i]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void convexityDefects( InputArray _points, InputArray _hull, OutputArray _defects )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat points = _points.getMat();
|
||||
int i, j = 0, npoints = points.checkVector(2, CV_32S);
|
||||
CV_Assert( npoints >= 0 );
|
||||
|
||||
if( npoints <= 3 )
|
||||
{
|
||||
_defects.release();
|
||||
return;
|
||||
}
|
||||
|
||||
Mat hull = _hull.getMat();
|
||||
int hpoints = hull.checkVector(1, CV_32S);
|
||||
CV_Assert( hpoints > 0 );
|
||||
|
||||
const Point* ptr = points.ptr<Point>();
|
||||
const int* hptr = hull.ptr<int>();
|
||||
std::vector<Vec4i> defects;
|
||||
if ( hpoints < 3 ) //if hull consists of one or two points, contour is always convex
|
||||
{
|
||||
_defects.release();
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. recognize co-orientation of the contour and its hull
|
||||
bool rev_orientation = ((hptr[1] > hptr[0]) + (hptr[2] > hptr[1]) + (hptr[0] > hptr[2])) != 2;
|
||||
|
||||
// 2. cycle through points and hull, compute defects
|
||||
int hcurr = hptr[rev_orientation ? 0 : hpoints-1];
|
||||
CV_Assert( 0 <= hcurr && hcurr < npoints );
|
||||
|
||||
int increasing_idx = -1;
|
||||
|
||||
for( i = 0; i < hpoints; i++ )
|
||||
{
|
||||
int hnext = hptr[rev_orientation ? hpoints - i - 1 : i];
|
||||
CV_Assert( 0 <= hnext && hnext < npoints );
|
||||
|
||||
Point pt0 = ptr[hcurr], pt1 = ptr[hnext];
|
||||
if( increasing_idx < 0 )
|
||||
increasing_idx = !(hcurr < hnext);
|
||||
else if( increasing_idx != (hcurr < hnext))
|
||||
{
|
||||
CV_Error(Error::StsBadArg,
|
||||
"The convex hull indices are not monotonous, which can be in the case when the input contour contains self-intersections");
|
||||
}
|
||||
|
||||
double dx0 = pt1.x - pt0.x;
|
||||
double dy0 = pt1.y - pt0.y;
|
||||
double scale = dx0 == 0 && dy0 == 0 ? 0. : 1./std::sqrt(dx0*dx0 + dy0*dy0);
|
||||
|
||||
int defect_deepest_point = -1;
|
||||
double defect_depth = 0;
|
||||
bool is_defect = false;
|
||||
j=hcurr;
|
||||
for(;;)
|
||||
{
|
||||
// go through points to achieve next hull point
|
||||
j++;
|
||||
j &= j >= npoints ? 0 : -1;
|
||||
if( j == hnext )
|
||||
break;
|
||||
|
||||
// compute distance from current point to hull edge
|
||||
double dx = ptr[j].x - pt0.x;
|
||||
double dy = ptr[j].y - pt0.y;
|
||||
double dist = fabs(-dy0*dx + dx0*dy) * scale;
|
||||
|
||||
if( dist > defect_depth )
|
||||
{
|
||||
defect_depth = dist;
|
||||
defect_deepest_point = j;
|
||||
is_defect = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( is_defect )
|
||||
{
|
||||
int idepth = cvRound(defect_depth*256);
|
||||
defects.push_back(Vec4i(hcurr, hnext, defect_deepest_point, idepth));
|
||||
}
|
||||
|
||||
hcurr = hnext;
|
||||
}
|
||||
|
||||
Mat(defects).copyTo(_defects);
|
||||
}
|
||||
|
||||
|
||||
template<typename _Tp>
|
||||
static bool isContourConvex_( const Point_<_Tp>* p, int n )
|
||||
{
|
||||
Point_<_Tp> prev_pt = p[(n-2+n) % n];
|
||||
Point_<_Tp> cur_pt = p[n-1];
|
||||
|
||||
_Tp dx0 = cur_pt.x - prev_pt.x;
|
||||
_Tp dy0 = cur_pt.y - prev_pt.y;
|
||||
int orientation = 0;
|
||||
|
||||
for( int i = 0; i < n; i++ )
|
||||
{
|
||||
_Tp dxdy0, dydx0;
|
||||
_Tp dx, dy;
|
||||
|
||||
prev_pt = cur_pt;
|
||||
cur_pt = p[i];
|
||||
|
||||
dx = cur_pt.x - prev_pt.x;
|
||||
dy = cur_pt.y - prev_pt.y;
|
||||
dxdy0 = dx * dy0;
|
||||
dydx0 = dy * dx0;
|
||||
|
||||
// find orientation
|
||||
// orient = -dy0 * dx + dx0 * dy;
|
||||
// orientation |= (orient > 0) ? 1 : 2;
|
||||
orientation |= (dydx0 > dxdy0) ? 1 : ((dydx0 < dxdy0) ? 2 : 3);
|
||||
if( orientation == 3 )
|
||||
return false;
|
||||
|
||||
dx0 = dx;
|
||||
dy0 = dy;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool isContourConvex( InputArray _contour )
|
||||
{
|
||||
Mat contour = _contour.getMat();
|
||||
int total = contour.checkVector(2), depth = contour.depth();
|
||||
CV_Assert(total >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
|
||||
if( total == 0 )
|
||||
return false;
|
||||
|
||||
return depth == CV_32S ?
|
||||
isContourConvex_(contour.ptr<Point>(), total ) :
|
||||
isContourConvex_(contour.ptr<Point2f>(), total );
|
||||
}
|
||||
|
||||
}
|
||||
Executable → Regular
@@ -1,767 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
double cv::pointPolygonTest( InputArray _contour, Point2f pt, bool measureDist )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
double result = 0;
|
||||
Mat contour = _contour.getMat();
|
||||
int i, total = contour.checkVector(2), counter = 0;
|
||||
int depth = contour.depth();
|
||||
CV_Assert( total >= 0 && (depth == CV_32S || depth == CV_32F));
|
||||
|
||||
bool is_float = depth == CV_32F;
|
||||
double min_dist_num = FLT_MAX, min_dist_denom = 1;
|
||||
Point ip(cvRound(pt.x), cvRound(pt.y));
|
||||
|
||||
if( total == 0 )
|
||||
return measureDist ? -DBL_MAX : -1;
|
||||
|
||||
const Point* cnt = contour.ptr<Point>();
|
||||
const Point2f* cntf = (const Point2f*)cnt;
|
||||
|
||||
if( !is_float && !measureDist && ip.x == pt.x && ip.y == pt.y )
|
||||
{
|
||||
// the fastest "purely integer" branch
|
||||
Point v0, v = cnt[total-1];
|
||||
|
||||
for( i = 0; i < total; i++ )
|
||||
{
|
||||
v0 = v;
|
||||
v = cnt[i];
|
||||
|
||||
if( (v0.y <= ip.y && v.y <= ip.y) ||
|
||||
(v0.y > ip.y && v.y > ip.y) ||
|
||||
(v0.x < ip.x && v.x < ip.x) )
|
||||
{
|
||||
if( ip.y == v.y && (ip.x == v.x || (ip.y == v0.y &&
|
||||
((v0.x <= ip.x && ip.x <= v.x) || (v.x <= ip.x && ip.x <= v0.x)))) )
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int64 dist = static_cast<int64>(ip.y - v0.y)*(v.x - v0.x)
|
||||
- static_cast<int64>(ip.x - v0.x)*(v.y - v0.y);
|
||||
if( dist == 0 )
|
||||
return 0;
|
||||
if( v.y < v0.y )
|
||||
dist = -dist;
|
||||
counter += dist > 0;
|
||||
}
|
||||
|
||||
result = counter % 2 == 0 ? -1 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Point2f v0, v;
|
||||
|
||||
if( is_float )
|
||||
{
|
||||
v = cntf[total-1];
|
||||
}
|
||||
else
|
||||
{
|
||||
v = cnt[total-1];
|
||||
}
|
||||
|
||||
if( !measureDist )
|
||||
{
|
||||
for( i = 0; i < total; i++ )
|
||||
{
|
||||
double dist;
|
||||
v0 = v;
|
||||
if( is_float )
|
||||
v = cntf[i];
|
||||
else
|
||||
v = cnt[i];
|
||||
|
||||
if( (v0.y <= pt.y && v.y <= pt.y) ||
|
||||
(v0.y > pt.y && v.y > pt.y) ||
|
||||
(v0.x < pt.x && v.x < pt.x) )
|
||||
{
|
||||
if( pt.y == v.y && (pt.x == v.x || (pt.y == v0.y &&
|
||||
((v0.x <= pt.x && pt.x <= v.x) || (v.x <= pt.x && pt.x <= v0.x)))) )
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
dist = (double)(pt.y - v0.y)*(v.x - v0.x) - (double)(pt.x - v0.x)*(v.y - v0.y);
|
||||
if( dist == 0 )
|
||||
return 0;
|
||||
if( v.y < v0.y )
|
||||
dist = -dist;
|
||||
counter += dist > 0;
|
||||
}
|
||||
|
||||
result = counter % 2 == 0 ? -1 : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < total; i++ )
|
||||
{
|
||||
double dx, dy, dx1, dy1, dx2, dy2, dist_num, dist_denom = 1;
|
||||
|
||||
v0 = v;
|
||||
if( is_float )
|
||||
v = cntf[i];
|
||||
else
|
||||
v = cnt[i];
|
||||
|
||||
dx = v.x - v0.x; dy = v.y - v0.y;
|
||||
dx1 = pt.x - v0.x; dy1 = pt.y - v0.y;
|
||||
dx2 = pt.x - v.x; dy2 = pt.y - v.y;
|
||||
|
||||
if( dx1*dx + dy1*dy <= 0 )
|
||||
dist_num = dx1*dx1 + dy1*dy1;
|
||||
else if( dx2*dx + dy2*dy >= 0 )
|
||||
dist_num = dx2*dx2 + dy2*dy2;
|
||||
else
|
||||
{
|
||||
dist_num = (dy1*dx - dx1*dy);
|
||||
dist_num *= dist_num;
|
||||
dist_denom = dx*dx + dy*dy;
|
||||
}
|
||||
|
||||
if( dist_num*min_dist_denom < min_dist_num*dist_denom )
|
||||
{
|
||||
min_dist_num = dist_num;
|
||||
min_dist_denom = dist_denom;
|
||||
if( min_dist_num == 0 )
|
||||
break;
|
||||
}
|
||||
|
||||
if( (v0.y <= pt.y && v.y <= pt.y) ||
|
||||
(v0.y > pt.y && v.y > pt.y) ||
|
||||
(v0.x < pt.x && v.x < pt.x) )
|
||||
continue;
|
||||
|
||||
dist_num = dy1*dx - dx1*dy;
|
||||
if( dy < 0 )
|
||||
dist_num = -dist_num;
|
||||
counter += dist_num > 0;
|
||||
}
|
||||
|
||||
result = std::sqrt(min_dist_num/min_dist_denom);
|
||||
if( counter % 2 == 0 )
|
||||
result = -result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
This code is described in "Computational Geometry in C" (Second Edition),
|
||||
Chapter 7. It is not written to be comprehensible without the
|
||||
explanation in that book.
|
||||
|
||||
Written by Joseph O'Rourke.
|
||||
Last modified: December 1997
|
||||
Questions to orourke@cs.smith.edu.
|
||||
--------------------------------------------------------------------
|
||||
This code is Copyright 1997 by Joseph O'Rourke. It may be freely
|
||||
redistributed in its entirety provided that this copyright notice is
|
||||
not removed.
|
||||
--------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
namespace cv
|
||||
{
|
||||
typedef enum { Pin, Qin, Unknown } tInFlag;
|
||||
|
||||
static int areaSign( Point2f a, Point2f b, Point2f c )
|
||||
{
|
||||
static const double eps = 1e-5;
|
||||
double area2 = (b.x - a.x) * (double)(c.y - a.y) - (c.x - a.x ) * (double)(b.y - a.y);
|
||||
return area2 > eps ? 1 : area2 < -eps ? -1 : 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Returns true iff point c lies on the closed segment ab.
|
||||
// Assumes it is already known that abc are collinear.
|
||||
//---------------------------------------------------------------------
|
||||
static bool between( Point2f a, Point2f b, Point2f c )
|
||||
{
|
||||
Point2f ba, ca;
|
||||
|
||||
// If ab not vertical, check betweenness on x; else on y.
|
||||
if ( a.x != b.x )
|
||||
return ((a.x <= c.x) && (c.x <= b.x)) ||
|
||||
((a.x >= c.x) && (c.x >= b.x));
|
||||
else
|
||||
return ((a.y <= c.y) && (c.y <= b.y)) ||
|
||||
((a.y >= c.y) && (c.y >= b.y));
|
||||
}
|
||||
|
||||
enum LineSegmentIntersection
|
||||
{
|
||||
LS_NO_INTERSECTION = 0,
|
||||
LS_SINGLE_INTERSECTION = 1,
|
||||
LS_OVERLAP = 2,
|
||||
LS_ENDPOINT_INTERSECTION = 3
|
||||
};
|
||||
|
||||
static LineSegmentIntersection parallelInt( Point2f a, Point2f b, Point2f c, Point2f d, Point2f& p, Point2f& q )
|
||||
{
|
||||
LineSegmentIntersection code = LS_OVERLAP;
|
||||
if( areaSign(a, b, c) != 0 )
|
||||
code = LS_NO_INTERSECTION;
|
||||
else if( between(a, b, c) && between(a, b, d))
|
||||
p = c, q = d;
|
||||
else if( between(c, d, a) && between(c, d, b))
|
||||
p = a, q = b;
|
||||
else if( between(a, b, c) && between(c, d, b))
|
||||
p = c, q = b;
|
||||
else if( between(a, b, c) && between(c, d, a))
|
||||
p = c, q = a;
|
||||
else if( between(a, b, d) && between(c, d, b))
|
||||
p = d, q = b;
|
||||
else if( between(a, b, d) && between(c, d, a))
|
||||
p = d, q = a;
|
||||
else
|
||||
code = LS_NO_INTERSECTION;
|
||||
return code;
|
||||
}
|
||||
|
||||
// Finds intersection of two line segments: (a, b) and (c, d).
|
||||
static LineSegmentIntersection intersectLineSegments( Point2f a, Point2f b, Point2f c,
|
||||
Point2f d, Point2f& p, Point2f& q )
|
||||
{
|
||||
double denom = ((double)a.x - b.x) * ((double)d.y - c.y) - ((double)a.y - b.y) * ((double)d.x - c.x);
|
||||
|
||||
// If denom is zero, then segments are parallel: handle separately.
|
||||
if( denom == 0. )
|
||||
return parallelInt(a, b, c, d, p, q);
|
||||
|
||||
double num = ((double)d.y - a.y) * ((double)a.x - c.x) + ((double)a.x - d.x) * ((double)a.y - c.y);
|
||||
double s = num / denom;
|
||||
|
||||
num = ((double)b.y - a.y) * ((double)a.x - c.x) + ((double)c.y - a.y) * ((double)b.x - a.x);
|
||||
double t = num / denom;
|
||||
|
||||
p.x = (float)(a.x + s*((double)b.x - a.x));
|
||||
p.y = (float)(a.y + s*((double)b.y - a.y));
|
||||
q = p;
|
||||
|
||||
return s < 0. || s > 1. || t < 0. || t > 1. ? LS_NO_INTERSECTION :
|
||||
s == 0. || s == 1. || t == 0. || t == 1. ? LS_ENDPOINT_INTERSECTION : LS_SINGLE_INTERSECTION;
|
||||
}
|
||||
|
||||
static tInFlag inOut( Point2f p, tInFlag inflag, int aHB, int bHA, Point2f*& result )
|
||||
{
|
||||
if( p != result[-1] )
|
||||
*result++ = p;
|
||||
// Update inflag.
|
||||
return aHB > 0 ? Pin : bHA > 0 ? Qin : inflag;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Advances and prints out an inside vertex if appropriate.
|
||||
//---------------------------------------------------------------------
|
||||
static int advance( int a, int *aa, int n, bool inside, Point2f v, Point2f*& result )
|
||||
{
|
||||
if( inside && v != result[-1] )
|
||||
*result++ = v;
|
||||
(*aa)++;
|
||||
return (a+1) % n;
|
||||
}
|
||||
|
||||
static void addSharedSeg( Point2f p, Point2f q, Point2f*& result )
|
||||
{
|
||||
if( p != result[-1] )
|
||||
*result++ = p;
|
||||
if( q != result[-1] )
|
||||
*result++ = q;
|
||||
}
|
||||
|
||||
// Note: The function and subroutings use direct pointer arithmetics instead of arrays indexing.
|
||||
// Each loop iteration may push to result array up to 3 times.
|
||||
// It means that we need +3 spare result elements against result_size
|
||||
// to catch agorithmic overflow and prevent actual result array overflow.
|
||||
static int intersectConvexConvex_( const Point2f* P, int n, const Point2f* Q, int m,
|
||||
Point2f* result, int result_size, float* _area )
|
||||
{
|
||||
Point2f* result0 = result;
|
||||
// P has n vertices, Q has m vertices.
|
||||
int a=0, b=0; // indices on P and Q (resp.)
|
||||
Point2f Origin(0,0);
|
||||
tInFlag inflag=Unknown; // {Pin, Qin, Unknown}: which inside
|
||||
int aa=0, ba=0; // # advances on a & b indices (after 1st inter.)
|
||||
bool FirstPoint=true;// Is this the first point? (used to initialize).
|
||||
Point2f p0; // The first point.
|
||||
*result++ = Point2f(FLT_MAX, FLT_MAX);
|
||||
|
||||
do
|
||||
{
|
||||
// Computations of key variables.
|
||||
int a1 = (a + n - 1) % n; // a-1, b-1 (resp.)
|
||||
int b1 = (b + m - 1) % m;
|
||||
|
||||
Point2f A = P[a] - P[a1], B = Q[b] - Q[b1]; // directed edges on P and Q (resp.)
|
||||
|
||||
int cross = areaSign( Origin, A, B ); // sign of z-component of A x B
|
||||
int aHB = areaSign( Q[b1], Q[b], P[a] ); // a in H(b).
|
||||
int bHA = areaSign( P[a1], P[a], Q[b] ); // b in H(A);
|
||||
|
||||
// If A & B intersect, update inflag.
|
||||
Point2f p, q;
|
||||
LineSegmentIntersection code = intersectLineSegments( P[a1], P[a], Q[b1], Q[b], p, q );
|
||||
if( code == LS_SINGLE_INTERSECTION || code == LS_ENDPOINT_INTERSECTION )
|
||||
{
|
||||
if( inflag == Unknown && FirstPoint )
|
||||
{
|
||||
aa = ba = 0;
|
||||
FirstPoint = false;
|
||||
p0 = p;
|
||||
*result++ = p;
|
||||
}
|
||||
inflag = inOut( p, inflag, aHB, bHA, result );
|
||||
}
|
||||
|
||||
//-----Advance rules-----
|
||||
|
||||
// Special case: A & B overlap and oppositely oriented.
|
||||
if( code == LS_OVERLAP && A.ddot(B) < 0 )
|
||||
{
|
||||
addSharedSeg( p, q, result );
|
||||
return (int)(result - result0);
|
||||
}
|
||||
|
||||
// Special case: A & B parallel and separated.
|
||||
if( cross == 0 && aHB < 0 && bHA < 0 )
|
||||
return (int)(result - result0);
|
||||
|
||||
// Special case: A & B collinear.
|
||||
else if ( cross == 0 && aHB == 0 && bHA == 0 ) {
|
||||
// Advance but do not output point.
|
||||
if ( inflag == Pin )
|
||||
b = advance( b, &ba, m, inflag == Qin, Q[b], result );
|
||||
else
|
||||
a = advance( a, &aa, n, inflag == Pin, P[a], result );
|
||||
}
|
||||
|
||||
// Generic cases.
|
||||
else if( cross >= 0 )
|
||||
{
|
||||
if( bHA > 0)
|
||||
a = advance( a, &aa, n, inflag == Pin, P[a], result );
|
||||
else
|
||||
b = advance( b, &ba, m, inflag == Qin, Q[b], result );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( aHB > 0)
|
||||
b = advance( b, &ba, m, inflag == Qin, Q[b], result );
|
||||
else
|
||||
a = advance( a, &aa, n, inflag == Pin, P[a], result );
|
||||
}
|
||||
// Quit when both adv. indices have cycled, or one has cycled twice.
|
||||
}
|
||||
while ( ((aa < n) || (ba < m)) && (aa < 2*n) && (ba < 2*m) && ((int)(result - result0) <= result_size) );
|
||||
|
||||
// Deal with special cases: not implemented.
|
||||
if( inflag == Unknown )
|
||||
{
|
||||
// The boundaries of P and Q do not cross.
|
||||
// ...
|
||||
}
|
||||
|
||||
int nr = (int)(result - result0);
|
||||
if (nr > result_size)
|
||||
{
|
||||
*_area = -1.f;
|
||||
return -1;
|
||||
}
|
||||
|
||||
double area = 0;
|
||||
Point2f prev = result0[nr-1];
|
||||
for(int i = 1; i < nr; i++ )
|
||||
{
|
||||
result0[i-1] = result0[i];
|
||||
area += (double)prev.x*result0[i].y - (double)prev.y*result0[i].x;
|
||||
prev = result0[i];
|
||||
}
|
||||
|
||||
*_area = (float)(area*0.5);
|
||||
|
||||
if( result0[nr-2] == result0[0] && nr > 1 )
|
||||
nr--;
|
||||
return nr-1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
float cv::intersectConvexConvex( InputArray _p1, InputArray _p2, OutputArray _p12, bool handleNested )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat p1 = _p1.getMat(), p2 = _p2.getMat();
|
||||
CV_Assert( p1.depth() == CV_32S || p1.depth() == CV_32F );
|
||||
CV_Assert( p2.depth() == CV_32S || p2.depth() == CV_32F );
|
||||
|
||||
int n = p1.checkVector(2, p1.depth(), true);
|
||||
int m = p2.checkVector(2, p2.depth(), true);
|
||||
|
||||
CV_Assert( n >= 0 && m >= 0 );
|
||||
|
||||
if( n < 2 || m < 2 )
|
||||
{
|
||||
_p12.release();
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
AutoBuffer<Point2f> _result(n + m + n+m+1+3);
|
||||
Point2f* fp1 = _result.data();
|
||||
Point2f* fp2 = fp1 + n;
|
||||
Point2f* result = fp2 + m;
|
||||
|
||||
int orientation = 0;
|
||||
|
||||
for( int k = 1; k <= 2; k++ )
|
||||
{
|
||||
Mat& p = k == 1 ? p1 : p2;
|
||||
int len = k == 1 ? n : m;
|
||||
Point2f* dst = k == 1 ? fp1 : fp2;
|
||||
|
||||
Mat temp(p.size(), CV_MAKETYPE(CV_32F, p.channels()), dst);
|
||||
p.convertTo(temp, CV_32F);
|
||||
CV_Assert( temp.ptr<Point2f>() == dst );
|
||||
Point2f diff0 = dst[0] - dst[len-1];
|
||||
for( int i = 1; i < len; i++ )
|
||||
{
|
||||
double s = diff0.cross(dst[i] - dst[i-1]);
|
||||
if( s != 0 )
|
||||
{
|
||||
if( s < 0 )
|
||||
{
|
||||
orientation++;
|
||||
flip( temp, temp, temp.rows > 1 ? 0 : 1 );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float area = 0.f;
|
||||
int nr = intersectConvexConvex_(fp1, n, fp2, m, result, n+m+1, &area);
|
||||
|
||||
if (nr < 0)
|
||||
{
|
||||
// The algorithm did not converge, e.g. some of inputs is not convex
|
||||
_p12.release();
|
||||
return -1.f;
|
||||
}
|
||||
|
||||
if( nr == 0 )
|
||||
{
|
||||
if( !handleNested )
|
||||
{
|
||||
_p12.release();
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
bool intersected = false;
|
||||
|
||||
// check if all of fp2's vertices is inside/on the edge of fp1.
|
||||
int nVertices = 0;
|
||||
for (int i=0; i<m; ++i)
|
||||
nVertices += pointPolygonTest(_InputArray(fp1, n), fp2[i], false) >= 0;
|
||||
|
||||
// if all of fp2's vertices is inside/on the edge of fp1.
|
||||
if (nVertices == m)
|
||||
{
|
||||
intersected = true;
|
||||
result = fp2;
|
||||
nr = m;
|
||||
}
|
||||
else // otherwise check if fp2 is inside fp1.
|
||||
{
|
||||
nVertices = 0;
|
||||
for (int i=0; i<n; ++i)
|
||||
nVertices += pointPolygonTest(_InputArray(fp2, m), fp1[i], false) >= 0;
|
||||
|
||||
// // if all of fp1's vertices is inside/on the edge of fp2.
|
||||
if (nVertices == n)
|
||||
{
|
||||
intersected = true;
|
||||
result = fp1;
|
||||
nr = n;
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersected)
|
||||
{
|
||||
_p12.release();
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
area = (float)contourArea(_InputArray(result, nr), false);
|
||||
}
|
||||
|
||||
if( _p12.needed() )
|
||||
{
|
||||
Mat temp(nr, 1, CV_32FC2, result);
|
||||
// if both input contours were reflected,
|
||||
// let's orient the result as the input vectors
|
||||
if( orientation == 2 )
|
||||
flip(temp, temp, 0);
|
||||
|
||||
temp.copyTo(_p12);
|
||||
}
|
||||
return (float)fabs(area);
|
||||
}
|
||||
|
||||
static Rect maskBoundingRect( const Mat& img )
|
||||
{
|
||||
CV_Assert( img.depth() <= CV_8S && img.channels() == 1 );
|
||||
|
||||
Size size = img.size();
|
||||
int xmin = size.width, ymin = -1, xmax = -1, ymax = -1, i, j, k;
|
||||
|
||||
for( i = 0; i < size.height; i++ )
|
||||
{
|
||||
const uchar* _ptr = img.ptr(i);
|
||||
const uchar* ptr = (const uchar*)alignPtr(_ptr, 4);
|
||||
int have_nz = 0, k_min, offset = (int)(ptr - _ptr);
|
||||
j = 0;
|
||||
offset = MIN(offset, size.width);
|
||||
for( ; j < offset; j++ )
|
||||
if( _ptr[j] )
|
||||
{
|
||||
if( j < xmin )
|
||||
xmin = j;
|
||||
if( j > xmax )
|
||||
xmax = j;
|
||||
have_nz = 1;
|
||||
}
|
||||
if( offset < size.width )
|
||||
{
|
||||
xmin -= offset;
|
||||
xmax -= offset;
|
||||
size.width -= offset;
|
||||
j = 0;
|
||||
for( ; j <= xmin - 4; j += 4 )
|
||||
if( *((int*)(ptr+j)) )
|
||||
break;
|
||||
for( ; j < xmin; j++ )
|
||||
if( ptr[j] )
|
||||
{
|
||||
xmin = j;
|
||||
if( j > xmax )
|
||||
xmax = j;
|
||||
have_nz = 1;
|
||||
break;
|
||||
}
|
||||
k_min = MAX(j-1, xmax);
|
||||
k = size.width - 1;
|
||||
for( ; k > k_min && (k&3) != 3; k-- )
|
||||
if( ptr[k] )
|
||||
break;
|
||||
if( k > k_min && (k&3) == 3 )
|
||||
{
|
||||
for( ; k > k_min+3; k -= 4 )
|
||||
if( *((int*)(ptr+k-3)) )
|
||||
break;
|
||||
}
|
||||
for( ; k > k_min; k-- )
|
||||
if( ptr[k] )
|
||||
{
|
||||
xmax = k;
|
||||
have_nz = 1;
|
||||
break;
|
||||
}
|
||||
if( !have_nz )
|
||||
{
|
||||
j &= ~3;
|
||||
for( ; j <= k - 3; j += 4 )
|
||||
if( *((int*)(ptr+j)) )
|
||||
break;
|
||||
for( ; j <= k; j++ )
|
||||
if( ptr[j] )
|
||||
{
|
||||
have_nz = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
xmin += offset;
|
||||
xmax += offset;
|
||||
size.width += offset;
|
||||
}
|
||||
if( have_nz )
|
||||
{
|
||||
if( ymin < 0 )
|
||||
ymin = i;
|
||||
ymax = i;
|
||||
}
|
||||
}
|
||||
|
||||
if( xmin >= size.width )
|
||||
xmin = ymin = 0;
|
||||
return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
|
||||
}
|
||||
|
||||
// Calculates bounding rectangle of a point set or retrieves already calculated
|
||||
static Rect pointSetBoundingRect( const Mat& points )
|
||||
{
|
||||
int npoints = points.checkVector(2);
|
||||
int depth = points.depth();
|
||||
CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
|
||||
int xmin = 0, ymin = 0, xmax = -1, ymax = -1, i = 0;
|
||||
bool is_float = depth == CV_32F;
|
||||
|
||||
if( npoints == 0 )
|
||||
return Rect();
|
||||
|
||||
if( !is_float )
|
||||
{
|
||||
const int32_t* pts = points.ptr<int32_t>();
|
||||
int64_t firstval = 0;
|
||||
std::memcpy(&firstval, pts, sizeof(pts[0]) * 2);
|
||||
xmin = xmax = pts[0];
|
||||
ymin = ymax = pts[1];
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
v_int32 minval, maxval;
|
||||
minval = maxval = v_reinterpret_as_s32(vx_setall_s64(firstval)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y
|
||||
const int nlanes = VTraits<v_int32>::vlanes()/2;
|
||||
for (; i < npoints; i += nlanes)
|
||||
{
|
||||
if (i > npoints - nlanes)
|
||||
{
|
||||
if (i == 0)
|
||||
break;
|
||||
i = npoints - nlanes;
|
||||
}
|
||||
v_int32 ptXY2 = vx_load(pts + 2 * i);
|
||||
minval = v_min(ptXY2, minval);
|
||||
maxval = v_max(ptXY2, maxval);
|
||||
}
|
||||
constexpr int max_nlanes = VTraits<v_int32>::max_nlanes;
|
||||
int arr_minval[max_nlanes], arr_maxval[max_nlanes];
|
||||
vx_store(arr_minval, minval);
|
||||
vx_store(arr_maxval, maxval);
|
||||
for (int j = 0; j < nlanes; j++)
|
||||
{
|
||||
xmin = std::min(xmin, arr_minval[2*j]);
|
||||
ymin = std::min(ymin, arr_minval[2*j+1]);
|
||||
xmax = std::max(xmax, arr_maxval[2*j]);
|
||||
ymax = std::max(ymax, arr_maxval[2*j+1]);
|
||||
}
|
||||
#endif
|
||||
for( ; i < npoints; i++ )
|
||||
{
|
||||
int pt_x = pts[2*i];
|
||||
int pt_y = pts[2*i+1];
|
||||
|
||||
xmin = std::min(xmin, pt_x);
|
||||
xmax = std::max(xmax, pt_x);
|
||||
ymin = std::min(ymin, pt_y);
|
||||
ymax = std::max(ymax, pt_y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const float* pts = points.ptr<float>();
|
||||
int64_t firstval = 0;
|
||||
std::memcpy(&firstval, pts, sizeof(pts[0]) * 2);
|
||||
xmin = xmax = cvFloor(pts[0]);
|
||||
ymin = ymax = cvFloor(pts[1]);
|
||||
#if CV_SIMD || CV_SIMD_SCALABLE
|
||||
v_float32 minval, maxval;
|
||||
minval = maxval = v_reinterpret_as_f32(vx_setall_s64(firstval)); //min[0]=pt.x, min[1]=pt.y, min[2]=pt.x, min[3]=pt.y
|
||||
const int nlanes = VTraits<v_float32>::vlanes()/2;
|
||||
for (; i < npoints; i += nlanes)
|
||||
{
|
||||
if (i > npoints - nlanes)
|
||||
{
|
||||
if (i == 0)
|
||||
break;
|
||||
i = npoints - nlanes;
|
||||
}
|
||||
v_float32 ptXY2 = vx_load(pts + 2 * i);
|
||||
minval = v_min(ptXY2, minval);
|
||||
maxval = v_max(ptXY2, maxval);
|
||||
}
|
||||
constexpr int max_nlanes = VTraits<v_int32>::max_nlanes;
|
||||
float arr_minval[max_nlanes], arr_maxval[max_nlanes];
|
||||
vx_store(arr_minval, minval);
|
||||
vx_store(arr_maxval, maxval);
|
||||
for (int j = 0; j < nlanes; j++)
|
||||
{
|
||||
int _xmin = cvFloor(arr_minval[2*j]), _ymin = cvFloor(arr_minval[2*j+1]);
|
||||
int _xmax = cvFloor(arr_maxval[2*j]), _ymax = cvFloor(arr_maxval[2*j+1]);
|
||||
xmin = std::min(xmin, _xmin);
|
||||
ymin = std::min(ymin, _ymin);
|
||||
xmax = std::max(xmax, _xmax);
|
||||
ymax = std::max(ymax, _ymax);
|
||||
}
|
||||
#endif
|
||||
for( ; i < npoints; i++ )
|
||||
{
|
||||
// because right and bottom sides of the bounding rectangle are not inclusive
|
||||
// (note +1 in width and height calculation below), cvFloor is used here instead of cvCeil
|
||||
int pt_x = cvFloor(pts[2*i]);
|
||||
int pt_y = cvFloor(pts[2*i+1]);
|
||||
|
||||
xmin = std::min(xmin, pt_x);
|
||||
xmax = std::max(xmax, pt_x);
|
||||
ymin = std::min(ymin, pt_y);
|
||||
ymax = std::max(ymax, pt_y);
|
||||
}
|
||||
}
|
||||
|
||||
return Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
|
||||
}
|
||||
|
||||
|
||||
cv::Rect cv::boundingRect(InputArray array)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat m = array.getMat();
|
||||
return m.depth() <= CV_8U ? maskBoundingRect(m) : pointSetBoundingRect(m);
|
||||
}
|
||||
@@ -1,358 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
|
||||
// Copyright (C) 2008-2011, Willow Garage Inc., all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// @Authors
|
||||
// Nghia Ho, nghiaho12@yahoo.com
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of OpenCV Foundation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the OpenCV Foundation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
static inline bool _isOnPositiveSide(const Point2f& line_vec, const Point2f& line_pt, const Point2f& pt)
|
||||
{
|
||||
//we are interested by the cross product between the line vector (line_vec) and the line-to-pt vector (pt-line_pt)
|
||||
//the sign of the only non-null component of the result determining which side of the line 'pt' is on
|
||||
//the "positive" side meaning depends on the context usage of the current function and how line_vec and line_pt were filled
|
||||
return (line_vec.y*(line_pt.x-pt.x) >= line_vec.x*(line_pt.y-pt.y));
|
||||
}
|
||||
|
||||
static int _rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, std::vector<Point2f> &intersection )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Point2f vec1[4], vec2[4];
|
||||
Point2f pts1[4], pts2[4];
|
||||
|
||||
rect1.points(pts1);
|
||||
rect2.points(pts2);
|
||||
|
||||
// L2 metric
|
||||
float samePointEps = 1e-6f * (float)std::max(rect1.size.area(), rect2.size.area());
|
||||
|
||||
int ret = INTERSECT_FULL;
|
||||
|
||||
// Specical case of rect1 == rect2
|
||||
{
|
||||
bool same = true;
|
||||
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
if( fabs(pts1[i].x - pts2[i].x) > samePointEps || (fabs(pts1[i].y - pts2[i].y) > samePointEps) )
|
||||
{
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(same)
|
||||
{
|
||||
intersection.resize(4);
|
||||
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
intersection[i] = pts1[i];
|
||||
}
|
||||
|
||||
return INTERSECT_FULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Line vector
|
||||
// A line from p1 to p2 is: p1 + (p2-p1)*t, t=[0,1]
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
vec1[i].x = pts1[(i+1)%4].x - pts1[i].x;
|
||||
vec1[i].y = pts1[(i+1)%4].y - pts1[i].y;
|
||||
|
||||
vec2[i].x = pts2[(i+1)%4].x - pts2[i].x;
|
||||
vec2[i].y = pts2[(i+1)%4].y - pts2[i].y;
|
||||
}
|
||||
|
||||
//we adapt the epsilon to the smallest dimension of the rects
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
samePointEps = std::min(samePointEps, std::sqrt(vec1[i].x*vec1[i].x+vec1[i].y*vec1[i].y));
|
||||
samePointEps = std::min(samePointEps, std::sqrt(vec2[i].x*vec2[i].x+vec2[i].y*vec2[i].y));
|
||||
}
|
||||
samePointEps = std::max(1e-16f, samePointEps);
|
||||
|
||||
// Line test - test all line combos for intersection
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
for( int j = 0; j < 4; j++ )
|
||||
{
|
||||
// Solve for 2x2 Ax=b
|
||||
const float x21 = pts2[j].x - pts1[i].x;
|
||||
const float y21 = pts2[j].y - pts1[i].y;
|
||||
|
||||
float vx1 = vec1[i].x;
|
||||
float vy1 = vec1[i].y;
|
||||
|
||||
float vx2 = vec2[j].x;
|
||||
float vy2 = vec2[j].y;
|
||||
|
||||
const float det = vx2*vy1 - vx1*vy2;
|
||||
if (std::abs(det) < 1e-12)//we consider accuracy around 1e-6, i.e. 1e-12 when squared
|
||||
continue;
|
||||
const float detInvScaled = 1.f/det;
|
||||
|
||||
const float t1 = (vx2*y21 - vy2*x21)*detInvScaled;
|
||||
const float t2 = (vx1*y21 - vy1*x21)*detInvScaled;
|
||||
|
||||
// This takes care of parallel lines
|
||||
if( cvIsInf(t1) || cvIsInf(t2) || cvIsNaN(t1) || cvIsNaN(t2) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( t1 >= 0.0f && t1 <= 1.0f && t2 >= 0.0f && t2 <= 1.0f )
|
||||
{
|
||||
const float xi = pts1[i].x + vec1[i].x*t1;
|
||||
const float yi = pts1[i].y + vec1[i].y*t1;
|
||||
|
||||
intersection.push_back(Point2f(xi,yi));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !intersection.empty() )
|
||||
{
|
||||
ret = INTERSECT_PARTIAL;
|
||||
}
|
||||
|
||||
// Check for vertices from rect1 inside recct2
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
// We do a sign test to see which side the point lies.
|
||||
// If the point all lie on the same sign for all 4 sides of the rect,
|
||||
// then there's an intersection
|
||||
int posSign = 0;
|
||||
int negSign = 0;
|
||||
|
||||
const Point2f& pt = pts1[i];
|
||||
|
||||
for( int j = 0; j < 4; j++ )
|
||||
{
|
||||
// line equation: Ax + By + C = 0 where
|
||||
// A = -vec2[j].y ; B = vec2[j].x ; C = -(A * pts2[j].x + B * pts2[j].y)
|
||||
// check which side of the line this point is at
|
||||
// A*x + B*y + C <> 0
|
||||
// + computation reordered for better numerical stability
|
||||
|
||||
const bool isPositive = _isOnPositiveSide(vec2[j], pts2[j], pt);
|
||||
|
||||
if( isPositive )
|
||||
{
|
||||
posSign++;
|
||||
}
|
||||
else
|
||||
{
|
||||
negSign++;
|
||||
}
|
||||
}
|
||||
|
||||
if( posSign == 4 || negSign == 4 )
|
||||
{
|
||||
intersection.push_back(pts1[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the check - check for vertices from rect2 inside recct1
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
// We do a sign test to see which side the point lies.
|
||||
// If the point all lie on the same sign for all 4 sides of the rect,
|
||||
// then there's an intersection
|
||||
int posSign = 0;
|
||||
int negSign = 0;
|
||||
|
||||
const Point2f& pt = pts2[i];
|
||||
|
||||
for( int j = 0; j < 4; j++ )
|
||||
{
|
||||
// line equation: Ax + By + C = 0 where
|
||||
// A = -vec1[j].y ; B = vec1[j].x ; C = -(A * pts1[j].x + B * pts1[j].y)
|
||||
// check which side of the line this point is at
|
||||
// A*x + B*y + C <> 0
|
||||
// + computation reordered for better numerical stability
|
||||
|
||||
const bool isPositive = _isOnPositiveSide(vec1[j], pts1[j], pt);
|
||||
|
||||
if( isPositive )
|
||||
{
|
||||
posSign++;
|
||||
}
|
||||
else
|
||||
{
|
||||
negSign++;
|
||||
}
|
||||
}
|
||||
|
||||
if( posSign == 4 || negSign == 4 )
|
||||
{
|
||||
intersection.push_back(pts2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int N = (int)intersection.size();
|
||||
if (N == 0)
|
||||
{
|
||||
return INTERSECT_NONE;
|
||||
}
|
||||
|
||||
// Get rid of duplicated points
|
||||
const int Nstride = N;
|
||||
cv::AutoBuffer<float, 100> distPt(N * N);
|
||||
cv::AutoBuffer<int> ptDistRemap(N);
|
||||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
const Point2f pt0 = intersection[i];
|
||||
ptDistRemap[i] = i;
|
||||
for (int j = i + 1; j < N; )
|
||||
{
|
||||
const Point2f pt1 = intersection[j];
|
||||
const float d2 = normL2Sqr<float>(pt1 - pt0);
|
||||
if(d2 <= samePointEps)
|
||||
{
|
||||
if (j < N - 1)
|
||||
intersection[j] = intersection[N - 1];
|
||||
N--;
|
||||
continue;
|
||||
}
|
||||
distPt[i*Nstride + j] = d2;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
while (N > 8) // we still have duplicate points after samePointEps threshold (eliminate closest points)
|
||||
{
|
||||
int minI = 0;
|
||||
int minJ = 1;
|
||||
float minD = distPt[1];
|
||||
for (int i = 0; i < N - 1; ++i)
|
||||
{
|
||||
const float* pDist = distPt.data() + Nstride * ptDistRemap[i];
|
||||
for (int j = i + 1; j < N; ++j)
|
||||
{
|
||||
const float d = pDist[ptDistRemap[j]];
|
||||
if (d < minD)
|
||||
{
|
||||
minD = d;
|
||||
minI = i;
|
||||
minJ = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
CV_Assert(fabs(normL2Sqr<float>(intersection[minI] - intersection[minJ]) - minD) < 1e-6); // ptDistRemap is not corrupted
|
||||
// drop minJ point
|
||||
if (minJ < N - 1)
|
||||
{
|
||||
intersection[minJ] = intersection[N - 1];
|
||||
ptDistRemap[minJ] = ptDistRemap[N - 1];
|
||||
}
|
||||
N--;
|
||||
}
|
||||
|
||||
// order points
|
||||
for (int i = 0; i < N - 1; ++i)
|
||||
{
|
||||
Point2f diffI = intersection[i + 1] - intersection[i];
|
||||
for (int j = i + 2; j < N; ++j)
|
||||
{
|
||||
Point2f diffJ = intersection[j] - intersection[i];
|
||||
if (diffI.cross(diffJ) < 0)
|
||||
{
|
||||
std::swap(intersection[i + 1], intersection[j]);
|
||||
diffI = diffJ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intersection.resize(N);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rotatedRectangleIntersection( const RotatedRect& rect1, const RotatedRect& rect2, OutputArray intersectingRegion )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
if (rect1.size.empty() || rect2.size.empty())
|
||||
{
|
||||
intersectingRegion.release();
|
||||
return INTERSECT_NONE;
|
||||
}
|
||||
|
||||
// Shift rectangles closer to origin (0, 0) to improve the calculation of the intesection region
|
||||
// To do that, the average center of the rectangles is moved to the origin
|
||||
const Point2f averageCenter = (rect1.center + rect2.center) / 2.0f;
|
||||
|
||||
RotatedRect shiftedRect1(rect1);
|
||||
RotatedRect shiftedRect2(rect2);
|
||||
|
||||
// Move rectangles closer to origin
|
||||
shiftedRect1.center -= averageCenter;
|
||||
shiftedRect2.center -= averageCenter;
|
||||
|
||||
std::vector <Point2f> intersection; intersection.reserve(24);
|
||||
|
||||
const int ret = _rotatedRectangleIntersection(shiftedRect1, shiftedRect2, intersection);
|
||||
|
||||
// If return is not None, the intersection Points are shifted back to the original position
|
||||
// and copied to the interesectingRegion
|
||||
if (ret != INTERSECT_NONE)
|
||||
{
|
||||
for (size_t i = 0; i < intersection.size(); ++i)
|
||||
{
|
||||
intersection[i] += averageCenter;
|
||||
}
|
||||
|
||||
Mat(intersection).copyTo(intersectingRegion);
|
||||
}
|
||||
else
|
||||
{
|
||||
intersectingRegion.release();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
@@ -1,637 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
static const double eps = 1e-6;
|
||||
|
||||
static void fitLine2D_wods( const Point2f* points, int count, float *weights, float *line )
|
||||
{
|
||||
CV_Assert(count > 0);
|
||||
double x = 0, y = 0, x2 = 0, y2 = 0, xy = 0, w = 0;
|
||||
double dx2, dy2, dxy;
|
||||
int i;
|
||||
float t;
|
||||
|
||||
// Calculating the average of x and y...
|
||||
if( weights == 0 )
|
||||
{
|
||||
for( i = 0; i < count; i += 1 )
|
||||
{
|
||||
x += points[i].x;
|
||||
y += points[i].y;
|
||||
x2 += points[i].x * points[i].x;
|
||||
y2 += points[i].y * points[i].y;
|
||||
xy += points[i].x * points[i].y;
|
||||
}
|
||||
w = (float) count;
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < count; i += 1 )
|
||||
{
|
||||
x += weights[i] * points[i].x;
|
||||
y += weights[i] * points[i].y;
|
||||
x2 += weights[i] * points[i].x * points[i].x;
|
||||
y2 += weights[i] * points[i].y * points[i].y;
|
||||
xy += weights[i] * points[i].x * points[i].y;
|
||||
w += weights[i];
|
||||
}
|
||||
}
|
||||
|
||||
x /= w;
|
||||
y /= w;
|
||||
x2 /= w;
|
||||
y2 /= w;
|
||||
xy /= w;
|
||||
|
||||
dx2 = x2 - x * x;
|
||||
dy2 = y2 - y * y;
|
||||
dxy = xy - x * y;
|
||||
|
||||
t = (float) atan2( 2 * dxy, dx2 - dy2 ) / 2;
|
||||
line[0] = (float) cos( t );
|
||||
line[1] = (float) sin( t );
|
||||
|
||||
line[2] = (float) x;
|
||||
line[3] = (float) y;
|
||||
}
|
||||
|
||||
static void fitLine3D_wods( const Point3f * points, int count, float *weights, float *line )
|
||||
{
|
||||
CV_Assert(count > 0);
|
||||
int i;
|
||||
float w0 = 0;
|
||||
float x0 = 0, y0 = 0, z0 = 0;
|
||||
float x2 = 0, y2 = 0, z2 = 0, xy = 0, yz = 0, xz = 0;
|
||||
float dx2, dy2, dz2, dxy, dxz, dyz;
|
||||
float *v;
|
||||
float n;
|
||||
float det[9], evc[9], evl[3];
|
||||
|
||||
memset( evl, 0, 3*sizeof(evl[0]));
|
||||
memset( evc, 0, 9*sizeof(evl[0]));
|
||||
|
||||
if( weights )
|
||||
{
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
float x = points[i].x;
|
||||
float y = points[i].y;
|
||||
float z = points[i].z;
|
||||
float w = weights[i];
|
||||
|
||||
|
||||
x2 += x * x * w;
|
||||
xy += x * y * w;
|
||||
xz += x * z * w;
|
||||
y2 += y * y * w;
|
||||
yz += y * z * w;
|
||||
z2 += z * z * w;
|
||||
x0 += x * w;
|
||||
y0 += y * w;
|
||||
z0 += z * w;
|
||||
w0 += w;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
float x = points[i].x;
|
||||
float y = points[i].y;
|
||||
float z = points[i].z;
|
||||
|
||||
x2 += x * x;
|
||||
xy += x * y;
|
||||
xz += x * z;
|
||||
y2 += y * y;
|
||||
yz += y * z;
|
||||
z2 += z * z;
|
||||
x0 += x;
|
||||
y0 += y;
|
||||
z0 += z;
|
||||
}
|
||||
w0 = (float) count;
|
||||
}
|
||||
|
||||
x2 /= w0;
|
||||
xy /= w0;
|
||||
xz /= w0;
|
||||
y2 /= w0;
|
||||
yz /= w0;
|
||||
z2 /= w0;
|
||||
|
||||
x0 /= w0;
|
||||
y0 /= w0;
|
||||
z0 /= w0;
|
||||
|
||||
dx2 = x2 - x0 * x0;
|
||||
dxy = xy - x0 * y0;
|
||||
dxz = xz - x0 * z0;
|
||||
dy2 = y2 - y0 * y0;
|
||||
dyz = yz - y0 * z0;
|
||||
dz2 = z2 - z0 * z0;
|
||||
|
||||
det[0] = dz2 + dy2;
|
||||
det[1] = -dxy;
|
||||
det[2] = -dxz;
|
||||
det[3] = det[1];
|
||||
det[4] = dx2 + dz2;
|
||||
det[5] = -dyz;
|
||||
det[6] = det[2];
|
||||
det[7] = det[5];
|
||||
det[8] = dy2 + dx2;
|
||||
|
||||
// Searching for a eigenvector of det corresponding to the minimal eigenvalue
|
||||
Mat _det( 3, 3, CV_32F, det );
|
||||
Mat _evc( 3, 3, CV_32F, evc );
|
||||
Mat _evl( 3, 1, CV_32F, evl );
|
||||
eigen( _det, _evl, _evc );
|
||||
i = evl[0] < evl[1] ? (evl[0] < evl[2] ? 0 : 2) : (evl[1] < evl[2] ? 1 : 2);
|
||||
|
||||
v = &evc[i * 3];
|
||||
n = (float) std::sqrt( (double)v[0] * v[0] + (double)v[1] * v[1] + (double)v[2] * v[2] );
|
||||
n = (float)MAX(n, eps);
|
||||
line[0] = v[0] / n;
|
||||
line[1] = v[1] / n;
|
||||
line[2] = v[2] / n;
|
||||
line[3] = x0;
|
||||
line[4] = y0;
|
||||
line[5] = z0;
|
||||
}
|
||||
|
||||
static double calcDist2D( const Point2f* points, int count, float *_line, float *dist )
|
||||
{
|
||||
int j;
|
||||
float px = _line[2], py = _line[3];
|
||||
float nx = _line[1], ny = -_line[0];
|
||||
double sum_dist = 0.;
|
||||
|
||||
for( j = 0; j < count; j++ )
|
||||
{
|
||||
float x, y;
|
||||
|
||||
x = points[j].x - px;
|
||||
y = points[j].y - py;
|
||||
|
||||
dist[j] = (float) fabs( nx * x + ny * y );
|
||||
sum_dist += dist[j];
|
||||
}
|
||||
|
||||
return sum_dist;
|
||||
}
|
||||
|
||||
static double calcDist3D( const Point3f* points, int count, float *_line, float *dist )
|
||||
{
|
||||
int j;
|
||||
float px = _line[3], py = _line[4], pz = _line[5];
|
||||
float vx = _line[0], vy = _line[1], vz = _line[2];
|
||||
double sum_dist = 0.;
|
||||
|
||||
for( j = 0; j < count; j++ )
|
||||
{
|
||||
float x, y, z;
|
||||
double p1, p2, p3;
|
||||
|
||||
x = points[j].x - px;
|
||||
y = points[j].y - py;
|
||||
z = points[j].z - pz;
|
||||
|
||||
p1 = vy * z - vz * y;
|
||||
p2 = vz * x - vx * z;
|
||||
p3 = vx * y - vy * x;
|
||||
|
||||
dist[j] = (float) std::sqrt( p1*p1 + p2*p2 + p3*p3 );
|
||||
sum_dist += dist[j];
|
||||
}
|
||||
|
||||
return sum_dist;
|
||||
}
|
||||
|
||||
static void weightL1( float *d, int count, float *w )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
double t = fabs( (double) d[i] );
|
||||
w[i] = (float)(1. / MAX(t, eps));
|
||||
}
|
||||
}
|
||||
|
||||
static void weightL12( float *d, int count, float *w )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
w[i] = 1.0f / (float) std::sqrt( 1 + (double) (d[i] * d[i] * 0.5) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void weightHuber( float *d, int count, float *w, float _c )
|
||||
{
|
||||
int i;
|
||||
const float c = _c <= 0 ? 1.345f : _c;
|
||||
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
if( d[i] < c )
|
||||
w[i] = 1.0f;
|
||||
else
|
||||
w[i] = c/d[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void weightFair( float *d, int count, float *w, float _c )
|
||||
{
|
||||
int i;
|
||||
const float c = _c == 0 ? 1 / 1.3998f : 1 / _c;
|
||||
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
w[i] = 1 / (1 + d[i] * c);
|
||||
}
|
||||
}
|
||||
|
||||
static void weightWelsch( float *d, int count, float *w, float _c )
|
||||
{
|
||||
int i;
|
||||
const float c = _c == 0 ? 1 / 2.9846f : 1 / _c;
|
||||
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
w[i] = (float) std::exp( -d[i] * d[i] * c * c );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Takes an array of 2D points, type of distance (including user-defined
|
||||
distance specified by callbacks, fills the array of four floats with line
|
||||
parameters A, B, C, D, where (A, B) is the normalized direction vector,
|
||||
(C, D) is the point that belongs to the line. */
|
||||
|
||||
static void fitLine2D( const Point2f * points, int count, int dist,
|
||||
float _param, float reps, float aeps, float *line )
|
||||
{
|
||||
double EPS = count*FLT_EPSILON;
|
||||
void (*calc_weights) (float *, int, float *) = 0;
|
||||
void (*calc_weights_param) (float *, int, float *, float) = 0;
|
||||
int i, j, k;
|
||||
float _line[4], _lineprev[4];
|
||||
float rdelta = reps != 0 ? reps : 1.0f;
|
||||
float adelta = aeps != 0 ? aeps : 0.01f;
|
||||
double min_err = DBL_MAX, err = 0;
|
||||
RNG rng((uint64)-1);
|
||||
|
||||
memset( line, 0, 4*sizeof(line[0]) );
|
||||
|
||||
switch (dist)
|
||||
{
|
||||
case cv::DIST_L2:
|
||||
return fitLine2D_wods( points, count, 0, line );
|
||||
|
||||
case cv::DIST_L1:
|
||||
calc_weights = weightL1;
|
||||
break;
|
||||
|
||||
case cv::DIST_L12:
|
||||
calc_weights = weightL12;
|
||||
break;
|
||||
|
||||
case cv::DIST_FAIR:
|
||||
calc_weights_param = weightFair;
|
||||
break;
|
||||
|
||||
case cv::DIST_WELSCH:
|
||||
calc_weights_param = weightWelsch;
|
||||
break;
|
||||
|
||||
case cv::DIST_HUBER:
|
||||
calc_weights_param = weightHuber;
|
||||
break;
|
||||
|
||||
/*case DIST_USER:
|
||||
calc_weights = (void ( * )(float *, int, float *)) _PFP.fp;
|
||||
break;*/
|
||||
default:
|
||||
CV_Error(cv::Error::StsBadArg, "Unknown distance type");
|
||||
}
|
||||
|
||||
AutoBuffer<float> wr(count*2);
|
||||
float *w = wr.data(), *r = w + count;
|
||||
|
||||
for( k = 0; k < 20; k++ )
|
||||
{
|
||||
int first = 1;
|
||||
for( i = 0; i < count; i++ )
|
||||
w[i] = 0.f;
|
||||
|
||||
for( i = 0; i < MIN(count,10); )
|
||||
{
|
||||
j = rng.uniform(0, count);
|
||||
if( w[j] < FLT_EPSILON )
|
||||
{
|
||||
w[j] = 1.f;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
fitLine2D_wods( points, count, w, _line );
|
||||
for( i = 0; i < 30; i++ )
|
||||
{
|
||||
double sum_w = 0;
|
||||
|
||||
if( first )
|
||||
{
|
||||
first = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double t = _line[0] * _lineprev[0] + _line[1] * _lineprev[1];
|
||||
t = MAX(t,-1.);
|
||||
t = MIN(t,1.);
|
||||
if( fabs(acos(t)) < adelta )
|
||||
{
|
||||
float x, y, d;
|
||||
|
||||
x = (float) fabs( _line[2] - _lineprev[2] );
|
||||
y = (float) fabs( _line[3] - _lineprev[3] );
|
||||
|
||||
d = x > y ? x : y;
|
||||
if( d < rdelta )
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* calculate distances */
|
||||
err = calcDist2D( points, count, _line, r );
|
||||
|
||||
if (err < min_err)
|
||||
{
|
||||
min_err = err;
|
||||
memcpy(line, _line, 4 * sizeof(line[0]));
|
||||
if (err < EPS)
|
||||
break;
|
||||
}
|
||||
|
||||
/* calculate weights */
|
||||
if( calc_weights )
|
||||
calc_weights( r, count, w );
|
||||
else
|
||||
calc_weights_param( r, count, w, _param );
|
||||
|
||||
for( j = 0; j < count; j++ )
|
||||
sum_w += w[j];
|
||||
|
||||
if( fabs(sum_w) > FLT_EPSILON )
|
||||
{
|
||||
sum_w = 1./sum_w;
|
||||
for( j = 0; j < count; j++ )
|
||||
w[j] = (float)(w[j]*sum_w);
|
||||
}
|
||||
else
|
||||
{
|
||||
for( j = 0; j < count; j++ )
|
||||
w[j] = 1.f;
|
||||
}
|
||||
|
||||
/* save the line parameters */
|
||||
memcpy( _lineprev, _line, 4 * sizeof( float ));
|
||||
|
||||
/* Run again... */
|
||||
fitLine2D_wods( points, count, w, _line );
|
||||
}
|
||||
|
||||
if( err < min_err )
|
||||
{
|
||||
min_err = err;
|
||||
memcpy( line, _line, 4 * sizeof(line[0]));
|
||||
if( err < EPS )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Takes an array of 3D points, type of distance (including user-defined
|
||||
distance specified by callbacks, fills the array of four floats with line
|
||||
parameters A, B, C, D, E, F, where (A, B, C) is the normalized direction vector,
|
||||
(D, E, F) is the point that belongs to the line. */
|
||||
static void fitLine3D( Point3f * points, int count, int dist,
|
||||
float _param, float reps, float aeps, float *line )
|
||||
{
|
||||
double EPS = count*FLT_EPSILON;
|
||||
void (*calc_weights) (float *, int, float *) = 0;
|
||||
void (*calc_weights_param) (float *, int, float *, float) = 0;
|
||||
int i, j, k;
|
||||
float _line[6]={0,0,0,0,0,0}, _lineprev[6]={0,0,0,0,0,0};
|
||||
float rdelta = reps != 0 ? reps : 1.0f;
|
||||
float adelta = aeps != 0 ? aeps : 0.01f;
|
||||
double min_err = DBL_MAX, err = 0;
|
||||
RNG rng((uint64)-1);
|
||||
|
||||
switch (dist)
|
||||
{
|
||||
case cv::DIST_L2:
|
||||
return fitLine3D_wods( points, count, 0, line );
|
||||
|
||||
case cv::DIST_L1:
|
||||
calc_weights = weightL1;
|
||||
break;
|
||||
|
||||
case cv::DIST_L12:
|
||||
calc_weights = weightL12;
|
||||
break;
|
||||
|
||||
case cv::DIST_FAIR:
|
||||
calc_weights_param = weightFair;
|
||||
break;
|
||||
|
||||
case cv::DIST_WELSCH:
|
||||
calc_weights_param = weightWelsch;
|
||||
break;
|
||||
|
||||
case cv::DIST_HUBER:
|
||||
calc_weights_param = weightHuber;
|
||||
break;
|
||||
|
||||
default:
|
||||
CV_Error(cv::Error::StsBadArg, "Unknown distance");
|
||||
}
|
||||
|
||||
AutoBuffer<float> buf(count*2);
|
||||
float *w = buf.data(), *r = w + count;
|
||||
|
||||
for( k = 0; k < 20; k++ )
|
||||
{
|
||||
int first = 1;
|
||||
for( i = 0; i < count; i++ )
|
||||
w[i] = 0.f;
|
||||
|
||||
for( i = 0; i < MIN(count,10); )
|
||||
{
|
||||
j = rng.uniform(0, count);
|
||||
if( w[j] < FLT_EPSILON )
|
||||
{
|
||||
w[j] = 1.f;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
fitLine3D_wods( points, count, w, _line );
|
||||
for( i = 0; i < 30; i++ )
|
||||
{
|
||||
double sum_w = 0;
|
||||
|
||||
if( first )
|
||||
{
|
||||
first = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double t = _line[0] * _lineprev[0] + _line[1] * _lineprev[1] + _line[2] * _lineprev[2];
|
||||
t = MAX(t,-1.);
|
||||
t = MIN(t,1.);
|
||||
if( fabs(acos(t)) < adelta )
|
||||
{
|
||||
float x, y, z, ax, ay, az, dx, dy, dz, d;
|
||||
|
||||
x = _line[3] - _lineprev[3];
|
||||
y = _line[4] - _lineprev[4];
|
||||
z = _line[5] - _lineprev[5];
|
||||
ax = _line[0] - _lineprev[0];
|
||||
ay = _line[1] - _lineprev[1];
|
||||
az = _line[2] - _lineprev[2];
|
||||
dx = (float) fabs( y * az - z * ay );
|
||||
dy = (float) fabs( z * ax - x * az );
|
||||
dz = (float) fabs( x * ay - y * ax );
|
||||
|
||||
d = dx > dy ? (dx > dz ? dx : dz) : (dy > dz ? dy : dz);
|
||||
if( d < rdelta )
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* calculate distances */
|
||||
err = calcDist3D( points, count, _line, r );
|
||||
if (err < min_err)
|
||||
{
|
||||
min_err = err;
|
||||
memcpy(line, _line, 6 * sizeof(line[0]));
|
||||
if (err < EPS)
|
||||
break;
|
||||
}
|
||||
|
||||
/* calculate weights */
|
||||
if( calc_weights )
|
||||
calc_weights( r, count, w );
|
||||
else
|
||||
calc_weights_param( r, count, w, _param );
|
||||
|
||||
for( j = 0; j < count; j++ )
|
||||
sum_w += w[j];
|
||||
|
||||
if( fabs(sum_w) > FLT_EPSILON )
|
||||
{
|
||||
sum_w = 1./sum_w;
|
||||
for( j = 0; j < count; j++ )
|
||||
w[j] = (float)(w[j]*sum_w);
|
||||
}
|
||||
else
|
||||
{
|
||||
for( j = 0; j < count; j++ )
|
||||
w[j] = 1.f;
|
||||
}
|
||||
|
||||
/* save the line parameters */
|
||||
memcpy( _lineprev, _line, 6 * sizeof( float ));
|
||||
|
||||
/* Run again... */
|
||||
fitLine3D_wods( points, count, w, _line );
|
||||
}
|
||||
|
||||
if( err < min_err )
|
||||
{
|
||||
min_err = err;
|
||||
memcpy( line, _line, 6 * sizeof(line[0]));
|
||||
if( err < EPS )
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void cv::fitLine( InputArray _points, OutputArray _line, int distType,
|
||||
double param, double reps, double aeps )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat points = _points.getMat();
|
||||
|
||||
float linebuf[6]={0.f};
|
||||
int npoints2 = points.checkVector(2, -1, false);
|
||||
int npoints3 = points.checkVector(3, -1, false);
|
||||
|
||||
CV_Assert( npoints2 >= 0 || npoints3 >= 0 );
|
||||
|
||||
if( points.depth() != CV_32F || !points.isContinuous() )
|
||||
{
|
||||
Mat temp;
|
||||
points.convertTo(temp, CV_32F);
|
||||
points = temp;
|
||||
}
|
||||
|
||||
if( npoints2 >= 0 )
|
||||
fitLine2D( points.ptr<Point2f>(), npoints2, distType,
|
||||
(float)param, (float)reps, (float)aeps, linebuf);
|
||||
else
|
||||
fitLine3D( points.ptr<Point3f>(), npoints3, distType,
|
||||
(float)param, (float)reps, (float)aeps, linebuf);
|
||||
|
||||
Mat(npoints2 >= 0 ? 4 : 6, 1, CV_32F, linebuf).copyTo(_line);
|
||||
}
|
||||
|
||||
/* End of file. */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,173 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
|
||||
|
||||
double cv::matchShapes(InputArray contour1, InputArray contour2, int method, double)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
double ma[7], mb[7];
|
||||
int i, sma, smb;
|
||||
double eps = 1.e-5;
|
||||
double mmm;
|
||||
double result = 0;
|
||||
bool anyA = false, anyB = false;
|
||||
|
||||
HuMoments( moments(contour1), ma );
|
||||
HuMoments( moments(contour2), mb );
|
||||
|
||||
switch (method)
|
||||
{
|
||||
case 1:
|
||||
for( i = 0; i < 7; i++ )
|
||||
{
|
||||
double ama = fabs( ma[i] );
|
||||
double amb = fabs( mb[i] );
|
||||
|
||||
if (ama > 0)
|
||||
anyA = true;
|
||||
if (amb > 0)
|
||||
anyB = true;
|
||||
|
||||
if( ma[i] > 0 )
|
||||
sma = 1;
|
||||
else if( ma[i] < 0 )
|
||||
sma = -1;
|
||||
else
|
||||
sma = 0;
|
||||
if( mb[i] > 0 )
|
||||
smb = 1;
|
||||
else if( mb[i] < 0 )
|
||||
smb = -1;
|
||||
else
|
||||
smb = 0;
|
||||
|
||||
if( ama > eps && amb > eps )
|
||||
{
|
||||
ama = 1. / (sma * log10( ama ));
|
||||
amb = 1. / (smb * log10( amb ));
|
||||
result += fabs( -ama + amb );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
for( i = 0; i < 7; i++ )
|
||||
{
|
||||
double ama = fabs( ma[i] );
|
||||
double amb = fabs( mb[i] );
|
||||
|
||||
if (ama > 0)
|
||||
anyA = true;
|
||||
if (amb > 0)
|
||||
anyB = true;
|
||||
|
||||
if( ma[i] > 0 )
|
||||
sma = 1;
|
||||
else if( ma[i] < 0 )
|
||||
sma = -1;
|
||||
else
|
||||
sma = 0;
|
||||
if( mb[i] > 0 )
|
||||
smb = 1;
|
||||
else if( mb[i] < 0 )
|
||||
smb = -1;
|
||||
else
|
||||
smb = 0;
|
||||
|
||||
if( ama > eps && amb > eps )
|
||||
{
|
||||
ama = sma * log10( ama );
|
||||
amb = smb * log10( amb );
|
||||
result += fabs( -ama + amb );
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
for( i = 0; i < 7; i++ )
|
||||
{
|
||||
double ama = fabs( ma[i] );
|
||||
double amb = fabs( mb[i] );
|
||||
|
||||
if (ama > 0)
|
||||
anyA = true;
|
||||
if (amb > 0)
|
||||
anyB = true;
|
||||
|
||||
if( ma[i] > 0 )
|
||||
sma = 1;
|
||||
else if( ma[i] < 0 )
|
||||
sma = -1;
|
||||
else
|
||||
sma = 0;
|
||||
if( mb[i] > 0 )
|
||||
smb = 1;
|
||||
else if( mb[i] < 0 )
|
||||
smb = -1;
|
||||
else
|
||||
smb = 0;
|
||||
|
||||
if( ama > eps && amb > eps )
|
||||
{
|
||||
ama = sma * log10( ama );
|
||||
amb = smb * log10( amb );
|
||||
mmm = fabs( (ama - amb) / ama );
|
||||
if( result < mmm )
|
||||
result = mmm;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CV_Error( cv::Error::StsBadArg, "Unknown comparison method" );
|
||||
}
|
||||
|
||||
//If anyA and anyB are both true, the result is correct.
|
||||
//If anyA and anyB are both false, the distance is 0, perfect match.
|
||||
//If only one is true, then it's a false 0 and return large error.
|
||||
if (anyA != anyB)
|
||||
result = DBL_MAX;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* End of file. */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,437 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of OpenCV Foundation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the OpenCV Foundation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
struct MinAreaState
|
||||
{
|
||||
int bottom;
|
||||
int left;
|
||||
float height;
|
||||
float width;
|
||||
float base_a;
|
||||
float base_b;
|
||||
};
|
||||
|
||||
enum { CALIPERS_MAXHEIGHT=0, CALIPERS_MINAREARECT=1, CALIPERS_MAXDIST=2 };
|
||||
|
||||
/*F///////////////////////////////////////////////////////////////////////////////////////
|
||||
// Name: rotatingCalipers
|
||||
// Purpose:
|
||||
// Rotating calipers algorithm with some applications
|
||||
//
|
||||
// Context:
|
||||
// Parameters:
|
||||
// points - convex hull vertices ( any orientation )
|
||||
// n - number of vertices
|
||||
// orientation - -1 for clockwise vertices order, 1 for CCW. 0 if unknown.
|
||||
// mode - concrete application of algorithm
|
||||
// can be CV_CALIPERS_MAXDIST or
|
||||
// CV_CALIPERS_MINAREARECT
|
||||
// left, bottom, right, top - indexes of extremal points
|
||||
// out - output info.
|
||||
// In case CV_CALIPERS_MAXDIST it points to float value -
|
||||
// maximal height of polygon.
|
||||
// In case CV_CALIPERS_MINAREARECT
|
||||
// ((CvPoint2D32f*)out)[0] - corner
|
||||
// ((CvPoint2D32f*)out)[1] - vector1
|
||||
// ((CvPoint2D32f*)out)[2] - vector2
|
||||
//
|
||||
// ^
|
||||
// |
|
||||
// vector2 |
|
||||
// |
|
||||
// |____________\
|
||||
// corner /
|
||||
// vector1
|
||||
//
|
||||
// Returns:
|
||||
// Notes:
|
||||
//F*/
|
||||
|
||||
static void rotate90CCW(const cv::Point2f& in, cv::Point2f &out)
|
||||
{
|
||||
out.x = -in.y;
|
||||
out.y = in.x;
|
||||
}
|
||||
|
||||
static void rotate90CW(const cv::Point2f& in, cv::Point2f &out)
|
||||
{
|
||||
out.x = in.y;
|
||||
out.y = -in.x;
|
||||
}
|
||||
|
||||
static void rotate180(const cv::Point2f& in, cv::Point2f &out)
|
||||
{
|
||||
out.x = -in.x;
|
||||
out.y = -in.y;
|
||||
}
|
||||
|
||||
/* return true if first vector is to the right (clockwise) of the second */
|
||||
static bool firstVecIsRight(const cv::Point2f& vec1, const cv::Point2f &vec2)
|
||||
{
|
||||
cv::Point2f tmp;
|
||||
rotate90CW(vec1, tmp);
|
||||
return tmp.x * vec2.x + tmp.y * vec2.y < 0;
|
||||
}
|
||||
|
||||
/* we will use usual cartesian coordinates */
|
||||
static void rotatingCalipers( const Point2f* points, int n, float orientation, int mode, float* out )
|
||||
{
|
||||
float minarea = FLT_MAX;
|
||||
float max_dist = 0;
|
||||
char buffer[32] = {};
|
||||
int i, k;
|
||||
AutoBuffer<float> abuf(n*3);
|
||||
float* inv_vect_length = abuf.data();
|
||||
Point2f* vect = (Point2f*)(inv_vect_length + n);
|
||||
int left = 0, bottom = 0, right = 0, top = 0;
|
||||
int seq[4] = { -1, -1, -1, -1 };
|
||||
Point2f rot_vect[4];
|
||||
|
||||
/* rotating calipers sides will always have coordinates
|
||||
(a,b) (-b,a) (-a,-b) (b, -a)
|
||||
*/
|
||||
/* this is a first base vector (a,b) initialized by (1,0) */
|
||||
float base_a;
|
||||
float base_b = 0;
|
||||
|
||||
float left_x, right_x, top_y, bottom_y;
|
||||
Point2f pt0 = points[0];
|
||||
|
||||
left_x = right_x = pt0.x;
|
||||
top_y = bottom_y = pt0.y;
|
||||
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
double dx, dy;
|
||||
|
||||
if( pt0.x < left_x )
|
||||
left_x = pt0.x, left = i;
|
||||
|
||||
if( pt0.x > right_x )
|
||||
right_x = pt0.x, right = i;
|
||||
|
||||
if( pt0.y > top_y )
|
||||
top_y = pt0.y, top = i;
|
||||
|
||||
if( pt0.y < bottom_y )
|
||||
bottom_y = pt0.y, bottom = i;
|
||||
|
||||
Point2f pt = points[(i+1) & (i+1 < n ? -1 : 0)];
|
||||
|
||||
dx = pt.x - pt0.x;
|
||||
dy = pt.y - pt0.y;
|
||||
|
||||
vect[i].x = (float)dx;
|
||||
vect[i].y = (float)dy;
|
||||
inv_vect_length[i] = (float)(1./std::sqrt(dx*dx + dy*dy));
|
||||
|
||||
pt0 = pt;
|
||||
}
|
||||
|
||||
// find convex hull orientation
|
||||
if (orientation == 0.f)
|
||||
{
|
||||
double ax = vect[n-1].x;
|
||||
double ay = vect[n-1].y;
|
||||
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
double bx = vect[i].x;
|
||||
double by = vect[i].y;
|
||||
|
||||
double convexity = ax * by - ay * bx;
|
||||
|
||||
if( convexity != 0 )
|
||||
{
|
||||
orientation = (convexity > 0) ? 1.f : (-1.f);
|
||||
break;
|
||||
}
|
||||
ax = bx;
|
||||
ay = by;
|
||||
}
|
||||
CV_Assert( orientation != 0 );
|
||||
}
|
||||
base_a = orientation;
|
||||
|
||||
/*****************************************************************************************/
|
||||
/* init calipers position */
|
||||
seq[0] = bottom;
|
||||
seq[1] = right;
|
||||
seq[2] = top;
|
||||
seq[3] = left;
|
||||
/*****************************************************************************************/
|
||||
/* Main loop - evaluate angles and rotate calipers */
|
||||
|
||||
/* all of edges will be checked while rotating calipers by 90 degrees */
|
||||
for( k = 0; k < n; k++ )
|
||||
{
|
||||
/* number of calipers edges, that has minimal angle with edge */
|
||||
int main_element = 0;
|
||||
|
||||
/* choose minimum angle between calipers side and polygon edge by dot product sign */
|
||||
rot_vect[0] = vect[seq[0]];
|
||||
rotate90CW(vect[seq[1]], rot_vect[1]);
|
||||
rotate180(vect[seq[2]], rot_vect[2]);
|
||||
rotate90CCW(vect[seq[3]], rot_vect[3]);
|
||||
for (i = 1; i < 4; i++)
|
||||
{
|
||||
if (firstVecIsRight(rot_vect[i], rot_vect[main_element]))
|
||||
main_element = i;
|
||||
}
|
||||
|
||||
/*rotate calipers*/
|
||||
{
|
||||
//get next base
|
||||
int pindex = seq[main_element];
|
||||
float lead_x = vect[pindex].x*inv_vect_length[pindex];
|
||||
float lead_y = vect[pindex].y*inv_vect_length[pindex];
|
||||
switch( main_element )
|
||||
{
|
||||
case 0:
|
||||
base_a = lead_x;
|
||||
base_b = lead_y;
|
||||
break;
|
||||
case 1:
|
||||
base_a = lead_y;
|
||||
base_b = -lead_x;
|
||||
break;
|
||||
case 2:
|
||||
base_a = -lead_x;
|
||||
base_b = -lead_y;
|
||||
break;
|
||||
case 3:
|
||||
base_a = -lead_y;
|
||||
base_b = lead_x;
|
||||
break;
|
||||
default:
|
||||
CV_Error(cv::Error::StsError, "main_element should be 0, 1, 2 or 3");
|
||||
}
|
||||
}
|
||||
/* change base point of main edge */
|
||||
seq[main_element] += 1;
|
||||
seq[main_element] = (seq[main_element] == n) ? 0 : seq[main_element];
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case CALIPERS_MAXHEIGHT:
|
||||
{
|
||||
/* now main element lies on edge aligned to calipers side */
|
||||
|
||||
/* find opposite element i.e. transform */
|
||||
/* 0->2, 1->3, 2->0, 3->1 */
|
||||
int opposite_el = main_element ^ 2;
|
||||
|
||||
float dx = points[seq[opposite_el]].x - points[seq[main_element]].x;
|
||||
float dy = points[seq[opposite_el]].y - points[seq[main_element]].y;
|
||||
float dist;
|
||||
|
||||
if( main_element & 1 )
|
||||
dist = (float)fabs(dx * base_a + dy * base_b);
|
||||
else
|
||||
dist = (float)fabs(dx * (-base_b) + dy * base_a);
|
||||
|
||||
if( dist > max_dist )
|
||||
max_dist = dist;
|
||||
}
|
||||
break;
|
||||
case CALIPERS_MINAREARECT:
|
||||
/* find area of rectangle */
|
||||
{
|
||||
float height;
|
||||
float area;
|
||||
|
||||
/* find vector left-right */
|
||||
float dx = points[seq[1]].x - points[seq[3]].x;
|
||||
float dy = points[seq[1]].y - points[seq[3]].y;
|
||||
|
||||
/* dotproduct */
|
||||
float width = dx * base_a + dy * base_b;
|
||||
|
||||
/* find vector left-right */
|
||||
dx = points[seq[2]].x - points[seq[0]].x;
|
||||
dy = points[seq[2]].y - points[seq[0]].y;
|
||||
|
||||
/* dotproduct */
|
||||
height = -dx * base_b + dy * base_a;
|
||||
|
||||
area = width * height;
|
||||
if( area <= minarea )
|
||||
{
|
||||
float *buf = (float *) buffer;
|
||||
|
||||
minarea = area;
|
||||
/* leftist point */
|
||||
((int *) buf)[0] = seq[3];
|
||||
buf[1] = base_a;
|
||||
buf[2] = width;
|
||||
buf[3] = base_b;
|
||||
buf[4] = height;
|
||||
/* bottom point */
|
||||
((int *) buf)[5] = seq[0];
|
||||
buf[6] = area;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} /*switch */
|
||||
} /* for */
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case CALIPERS_MINAREARECT:
|
||||
{
|
||||
float *buf = (float *) buffer;
|
||||
|
||||
float A1 = buf[1];
|
||||
float B1 = buf[3];
|
||||
|
||||
float A2 = -buf[3];
|
||||
float B2 = buf[1];
|
||||
|
||||
float C1 = A1 * points[((int *) buf)[0]].x + points[((int *) buf)[0]].y * B1;
|
||||
float C2 = A2 * points[((int *) buf)[5]].x + points[((int *) buf)[5]].y * B2;
|
||||
|
||||
float idet = 1.f / (A1 * B2 - A2 * B1);
|
||||
|
||||
float px = (C1 * B2 - C2 * B1) * idet;
|
||||
float py = (A1 * C2 - A2 * C1) * idet;
|
||||
|
||||
out[0] = px;
|
||||
out[1] = py;
|
||||
|
||||
out[2] = A1 * buf[2];
|
||||
out[3] = B1 * buf[2];
|
||||
|
||||
out[4] = A2 * buf[4];
|
||||
out[5] = B2 * buf[4];
|
||||
}
|
||||
break;
|
||||
case CALIPERS_MAXHEIGHT:
|
||||
{
|
||||
out[0] = max_dist;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
cv::RotatedRect cv::minAreaRect( InputArray _points )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat hull;
|
||||
Point2f out[3];
|
||||
RotatedRect box;
|
||||
double angle = -CV_PI / 2; // default angle for box without rotation and single point
|
||||
|
||||
static const bool clockwise = false;
|
||||
convexHull(_points, hull, clockwise, true);
|
||||
|
||||
if( hull.depth() != CV_32F )
|
||||
{
|
||||
Mat temp;
|
||||
hull.convertTo(temp, CV_32F);
|
||||
hull = temp;
|
||||
}
|
||||
|
||||
int n = hull.checkVector(2);
|
||||
const Point2f* hpoints = hull.ptr<Point2f>();
|
||||
|
||||
if( n > 2 )
|
||||
{
|
||||
rotatingCalipers( hpoints, n, clockwise ? -1.f : 1.f, CALIPERS_MINAREARECT, (float*)out );
|
||||
box.center.x = out[0].x + (out[1].x + out[2].x)*0.5f;
|
||||
box.center.y = out[0].y + (out[1].y + out[2].y)*0.5f;
|
||||
box.size.width = (float)std::sqrt((double)out[2].x*out[2].x + (double)out[2].y*out[2].y);
|
||||
box.size.height = (float)std::sqrt((double)out[1].x*out[1].x + (double)out[1].y*out[1].y);
|
||||
if (out[1].x == 0.f && out[1].y > 0.f)
|
||||
std::swap(box.size.width, box.size.height);
|
||||
else
|
||||
angle = -atan2( (double)out[1].x, (double)out[1].y );
|
||||
}
|
||||
else if( n == 2 )
|
||||
{
|
||||
box.center.x = (hpoints[0].x + hpoints[1].x)*0.5f;
|
||||
box.center.y = (hpoints[0].y + hpoints[1].y)*0.5f;
|
||||
double dx = hpoints[0].x - hpoints[1].x;
|
||||
double dy = hpoints[0].y - hpoints[1].y;
|
||||
box.size.width = 0;
|
||||
box.size.height = (float)std::sqrt(dx*dx + dy*dy);
|
||||
if (dx == 0)
|
||||
{
|
||||
std::swap(box.size.width, box.size.height);
|
||||
}
|
||||
else if (dy < 0)
|
||||
{
|
||||
angle = atan2( dy, dx );
|
||||
std::swap(box.size.width, box.size.height);
|
||||
}
|
||||
else if (dy > 0)
|
||||
{
|
||||
angle = -atan2( dx, dy );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( n == 1 )
|
||||
box.center = hpoints[0];
|
||||
}
|
||||
|
||||
box.angle = (float)(angle*180/CV_PI);
|
||||
CV_DbgCheckGE(box.angle, -90.0f, "");
|
||||
CV_DbgCheckLT(box.angle, 0.0f, "");
|
||||
return box;
|
||||
}
|
||||
|
||||
|
||||
void cv::boxPoints(cv::RotatedRect box, OutputArray _pts)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
_pts.create(4, 2, CV_32F);
|
||||
Mat pts = _pts.getMat();
|
||||
box.points(pts.ptr<Point2f>());
|
||||
}
|
||||
@@ -1,976 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
const float EPS = 1.0e-4f;
|
||||
|
||||
static void findCircle3pts(Point2f *pts, Point2f ¢er, float &radius)
|
||||
{
|
||||
// two edges of the triangle v1, v2
|
||||
Point2f v1 = pts[1] - pts[0];
|
||||
Point2f v2 = pts[2] - pts[0];
|
||||
|
||||
// center is intersection of midperpendicular lines of the two edges v1, v2
|
||||
// a1*x + b1*y = c1 where a1 = v1.x, b1 = v1.y
|
||||
// a2*x + b2*y = c2 where a2 = v2.x, b2 = v2.y
|
||||
Point2f midPoint1 = (pts[0] + pts[1]) / 2.0f;
|
||||
float c1 = midPoint1.x * v1.x + midPoint1.y * v1.y;
|
||||
Point2f midPoint2 = (pts[0] + pts[2]) / 2.0f;
|
||||
float c2 = midPoint2.x * v2.x + midPoint2.y * v2.y;
|
||||
float det = v1.x * v2.y - v1.y * v2.x;
|
||||
if (fabs(det) <= EPS)
|
||||
{
|
||||
// triangle is degenerate, so this is 2-points case
|
||||
// 2-points case should be taken into account in previous step
|
||||
return;
|
||||
}
|
||||
float cx = (c1 * v2.y - c2 * v1.y) / det;
|
||||
float cy = (v1.x * c2 - v2.x * c1) / det;
|
||||
center.x = (float)cx;
|
||||
center.y = (float)cy;
|
||||
cx -= pts[0].x;
|
||||
cy -= pts[0].y;
|
||||
radius = (float)(std::sqrt(cx *cx + cy * cy)) + EPS;
|
||||
}
|
||||
|
||||
template<typename PT>
|
||||
static void findThirdPoint(const PT *pts, int i, int j, Point2f ¢er, float &radius)
|
||||
{
|
||||
center.x = (float)(pts[j].x + pts[i].x) / 2.0f;
|
||||
center.y = (float)(pts[j].y + pts[i].y) / 2.0f;
|
||||
float dx = (float)(pts[j].x - pts[i].x);
|
||||
float dy = (float)(pts[j].y - pts[i].y);
|
||||
radius = (float)norm(Point2f(dx, dy)) / 2.0f + EPS;
|
||||
|
||||
for (int k = 0; k < j; ++k)
|
||||
{
|
||||
dx = center.x - (float)pts[k].x;
|
||||
dy = center.y - (float)pts[k].y;
|
||||
if (norm(Point2f(dx, dy)) < radius)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Point2f ptsf[3];
|
||||
ptsf[0] = (Point2f)pts[i];
|
||||
ptsf[1] = (Point2f)pts[j];
|
||||
ptsf[2] = (Point2f)pts[k];
|
||||
findCircle3pts(ptsf, center, radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename PT>
|
||||
void findSecondPoint(const PT *pts, int i, Point2f ¢er, float &radius)
|
||||
{
|
||||
center.x = (float)(pts[0].x + pts[i].x) / 2.0f;
|
||||
center.y = (float)(pts[0].y + pts[i].y) / 2.0f;
|
||||
float dx = (float)(pts[0].x - pts[i].x);
|
||||
float dy = (float)(pts[0].y - pts[i].y);
|
||||
radius = (float)norm(Point2f(dx, dy)) / 2.0f + EPS;
|
||||
|
||||
for (int j = 1; j < i; ++j)
|
||||
{
|
||||
dx = center.x - (float)pts[j].x;
|
||||
dy = center.y - (float)pts[j].y;
|
||||
if (norm(Point2f(dx, dy)) < radius)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
findThirdPoint(pts, i, j, center, radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename PT>
|
||||
static void findMinEnclosingCircle(const PT *pts_in, int count, Point2f ¢er, float &radius)
|
||||
{
|
||||
// Welzl's algorithm requires random permutation for expected O(n) time.
|
||||
// Without shuffling, sorted inputs trigger O(n^3) worst case.
|
||||
cv::AutoBuffer<PT, 1024> pts_buf(count);
|
||||
std::copy(pts_in, pts_in + count, pts_buf.data());
|
||||
PT* pts = pts_buf.data();
|
||||
if (count > 10)
|
||||
{
|
||||
Cv32suf x0, y0, xn, yn;
|
||||
x0.f = (float)pts[0].x;
|
||||
y0.f = (float)pts[0].y;
|
||||
xn.f = (float)pts[count-1].x;
|
||||
yn.f = (float)pts[count-1].y;
|
||||
|
||||
uint32_t seed = (uint32_t)count ^ x0.u ^ (y0.u << 8) ^ (xn.u << 16) ^ (yn.u << 24);
|
||||
cv::RNG rng(seed);
|
||||
|
||||
for (int i = 1; i < count; ++i)
|
||||
{
|
||||
int j = rng.uniform(0, i + 1);
|
||||
std::swap(pts[i], pts[j]);
|
||||
}
|
||||
}
|
||||
|
||||
center.x = (float)(pts[0].x + pts[1].x) / 2.0f;
|
||||
center.y = (float)(pts[0].y + pts[1].y) / 2.0f;
|
||||
float dx = (float)(pts[0].x - pts[1].x);
|
||||
float dy = (float)(pts[0].y - pts[1].y);
|
||||
radius = (float)norm(Point2f(dx, dy)) / 2.0f + EPS;
|
||||
|
||||
for (int i = 2; i < count; ++i)
|
||||
{
|
||||
dx = (float)pts[i].x - center.x;
|
||||
dy = (float)pts[i].y - center.y;
|
||||
float d = (float)norm(Point2f(dx, dy));
|
||||
if (d < radius)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
findSecondPoint(pts, i, center, radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace cv
|
||||
|
||||
// see Welzl, Emo. Smallest enclosing disks (balls and ellipsoids). Springer Berlin Heidelberg, 1991.
|
||||
void cv::minEnclosingCircle( InputArray _points, Point2f& _center, float& _radius )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat points = _points.getMat();
|
||||
int count = points.checkVector(2);
|
||||
int depth = points.depth();
|
||||
CV_Assert(count >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
|
||||
_center.x = _center.y = 0.f;
|
||||
_radius = 0.f;
|
||||
|
||||
if( count == 0 )
|
||||
return;
|
||||
|
||||
bool is_float = depth == CV_32F;
|
||||
const Point* ptsi = points.ptr<Point>();
|
||||
const Point2f* ptsf = points.ptr<Point2f>();
|
||||
|
||||
if( count == 1 )
|
||||
{
|
||||
_center = (is_float) ? ptsf[0] : Point2f((float)ptsi[0].x, (float)ptsi[0].y);
|
||||
_radius = EPS;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_float)
|
||||
{
|
||||
findMinEnclosingCircle<Point2f>(ptsf, count, _center, _radius);
|
||||
#if 0
|
||||
for (int m = 0; m < count; ++m)
|
||||
{
|
||||
float d = (float)norm(ptsf[m] - _center);
|
||||
if (d > _radius)
|
||||
{
|
||||
printf("error!\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
findMinEnclosingCircle<Point>(ptsi, count, _center, _radius);
|
||||
#if 0
|
||||
for (int m = 0; m < count; ++m)
|
||||
{
|
||||
double dx = ptsi[m].x - _center.x;
|
||||
double dy = ptsi[m].y - _center.y;
|
||||
double d = std::sqrt(dx * dx + dy * dy);
|
||||
if (d > _radius)
|
||||
{
|
||||
printf("error!\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// calculates length of a curve (e.g. contour perimeter)
|
||||
double cv::arcLength( InputArray _curve, bool is_closed )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat curve = _curve.getMat();
|
||||
int count = curve.checkVector(2);
|
||||
int depth = curve.depth();
|
||||
CV_Assert( count >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
double perimeter = 0;
|
||||
|
||||
int i;
|
||||
|
||||
if( count <= 1 )
|
||||
return 0.;
|
||||
|
||||
bool is_float = depth == CV_32F;
|
||||
int last = is_closed ? count-1 : 0;
|
||||
const Point* pti = curve.ptr<Point>();
|
||||
const Point2f* ptf = curve.ptr<Point2f>();
|
||||
|
||||
Point2f prev = is_float ? ptf[last] : Point2f((float)pti[last].x,(float)pti[last].y);
|
||||
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
Point2f p = is_float ? ptf[i] : Point2f((float)pti[i].x,(float)pti[i].y);
|
||||
float dx = p.x - prev.x, dy = p.y - prev.y;
|
||||
perimeter += std::sqrt(dx*dx + dy*dy);
|
||||
|
||||
prev = p;
|
||||
}
|
||||
|
||||
return perimeter;
|
||||
}
|
||||
|
||||
// area of a whole sequence
|
||||
double cv::contourArea( InputArray _contour, bool oriented )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat contour = _contour.getMat();
|
||||
int npoints = contour.checkVector(2);
|
||||
int depth = contour.depth();
|
||||
CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
|
||||
if( npoints == 0 )
|
||||
return 0.;
|
||||
|
||||
double a00 = 0;
|
||||
bool is_float = depth == CV_32F;
|
||||
const Point* ptsi = contour.ptr<Point>();
|
||||
const Point2f* ptsf = contour.ptr<Point2f>();
|
||||
Point2f prev = is_float ? ptsf[npoints-1] : Point2f((float)ptsi[npoints-1].x, (float)ptsi[npoints-1].y);
|
||||
|
||||
for( int i = 0; i < npoints; i++ )
|
||||
{
|
||||
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
|
||||
a00 += (double)prev.x * p.y - (double)prev.y * p.x;
|
||||
prev = p;
|
||||
}
|
||||
|
||||
a00 *= 0.5;
|
||||
if( !oriented )
|
||||
a00 = fabs(a00);
|
||||
|
||||
return a00;
|
||||
}
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
static inline Point2f getOfs(float eps)
|
||||
{
|
||||
RNG& rng = theRNG();
|
||||
return Point2f(rng.uniform(-eps, eps), rng.uniform(-eps, eps));
|
||||
}
|
||||
|
||||
static RotatedRect fitEllipseNoDirect( InputArray _points )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat points = _points.getMat();
|
||||
int i, n = points.checkVector(2);
|
||||
int depth = points.depth();
|
||||
CV_Assert( n >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
|
||||
RotatedRect box;
|
||||
|
||||
if( n < 5 )
|
||||
CV_Error( cv::Error::StsBadSize, "There should be at least 5 points to fit the ellipse" );
|
||||
|
||||
// New fitellipse algorithm, contributed by Dr. Daniel Weiss
|
||||
Point2f c(0,0);
|
||||
double gfp[5] = {0}, rp[5] = {0}, t, vd[25]={0}, wd[5]={0};
|
||||
const double min_eps = 1e-8;
|
||||
bool is_float = depth == CV_32F;
|
||||
|
||||
AutoBuffer<double> _Ad(n*12+n);
|
||||
double *Ad = _Ad.data(), *ud = Ad + n*5, *bd = ud + n*5;
|
||||
Point2f* ptsf_copy = (Point2f*)(bd + n);
|
||||
|
||||
// first fit for parameters A - E
|
||||
Mat A( n, 5, CV_64F, Ad );
|
||||
Mat b( n, 1, CV_64F, bd );
|
||||
Mat x( 5, 1, CV_64F, gfp );
|
||||
Mat u( n, 1, CV_64F, ud );
|
||||
Mat vt( 5, 5, CV_64F, vd );
|
||||
Mat w( 5, 1, CV_64F, wd );
|
||||
|
||||
{
|
||||
const Point* ptsi = points.ptr<Point>();
|
||||
const Point2f* ptsf = points.ptr<Point2f>();
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
|
||||
ptsf_copy[i] = p;
|
||||
c += p;
|
||||
}
|
||||
}
|
||||
c.x /= n;
|
||||
c.y /= n;
|
||||
|
||||
double s = 0;
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
Point2f p = ptsf_copy[i];
|
||||
p -= c;
|
||||
s += fabs(p.x) + fabs(p.y);
|
||||
}
|
||||
double scale = 100./(s > FLT_EPSILON ? s : FLT_EPSILON);
|
||||
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
Point2f p = ptsf_copy[i];
|
||||
p -= c;
|
||||
double px = p.x*scale;
|
||||
double py = p.y*scale;
|
||||
|
||||
bd[i] = 10000.0; // 1.0?
|
||||
Ad[i*5] = -px * px; // A - C signs inverted as proposed by APP
|
||||
Ad[i*5 + 1] = -py * py;
|
||||
Ad[i*5 + 2] = -px * py;
|
||||
Ad[i*5 + 3] = px;
|
||||
Ad[i*5 + 4] = py;
|
||||
}
|
||||
|
||||
SVDecomp(A, w, u, vt);
|
||||
if(wd[0]*FLT_EPSILON > wd[4]) {
|
||||
float eps = (float)(s/(n*2)*1e-3);
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
const Point2f p = ptsf_copy[i] + getOfs(eps);
|
||||
ptsf_copy[i] = p;
|
||||
}
|
||||
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
Point2f p = ptsf_copy[i];
|
||||
p -= c;
|
||||
double px = p.x*scale;
|
||||
double py = p.y*scale;
|
||||
bd[i] = 10000.0; // 1.0?
|
||||
Ad[i*5] = -px * px; // A - C signs inverted as proposed by APP
|
||||
Ad[i*5 + 1] = -py * py;
|
||||
Ad[i*5 + 2] = -px * py;
|
||||
Ad[i*5 + 3] = px;
|
||||
Ad[i*5 + 4] = py;
|
||||
}
|
||||
SVDecomp(A, w, u, vt);
|
||||
}
|
||||
SVBackSubst(w, u, vt, b, x);
|
||||
|
||||
// now use general-form parameters A - E to find the ellipse center:
|
||||
// differentiate general form wrt x/y to get two equations for cx and cy
|
||||
A = Mat( 2, 2, CV_64F, Ad );
|
||||
b = Mat( 2, 1, CV_64F, bd );
|
||||
x = Mat( 2, 1, CV_64F, rp );
|
||||
Ad[0] = 2 * gfp[0];
|
||||
Ad[1] = Ad[2] = gfp[2];
|
||||
Ad[3] = 2 * gfp[1];
|
||||
bd[0] = gfp[3];
|
||||
bd[1] = gfp[4];
|
||||
solve( A, b, x, DECOMP_SVD );
|
||||
|
||||
// re-fit for parameters A - C with those center coordinates
|
||||
A = Mat( n, 3, CV_64F, Ad );
|
||||
b = Mat( n, 1, CV_64F, bd );
|
||||
x = Mat( 3, 1, CV_64F, gfp );
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
Point2f p = ptsf_copy[i];
|
||||
p -= c;
|
||||
double px = p.x*scale;
|
||||
double py = p.y*scale;
|
||||
bd[i] = 1.0;
|
||||
Ad[i * 3] = (px - rp[0]) * (px - rp[0]);
|
||||
Ad[i * 3 + 1] = (py - rp[1]) * (py - rp[1]);
|
||||
Ad[i * 3 + 2] = (px - rp[0]) * (py - rp[1]);
|
||||
}
|
||||
solve(A, b, x, DECOMP_SVD);
|
||||
|
||||
// store angle and radii
|
||||
rp[4] = -0.5 * atan2(gfp[2], gfp[1] - gfp[0]); // convert from APP angle usage
|
||||
if( fabs(gfp[2]) > min_eps )
|
||||
t = gfp[2]/sin(-2.0 * rp[4]);
|
||||
else // ellipse is rotated by an integer multiple of pi/2
|
||||
t = gfp[1] - gfp[0];
|
||||
rp[2] = fabs(gfp[0] + gfp[1] - t);
|
||||
if( rp[2] > min_eps )
|
||||
rp[2] = std::sqrt(2.0 / rp[2]);
|
||||
rp[3] = fabs(gfp[0] + gfp[1] + t);
|
||||
if( rp[3] > min_eps )
|
||||
rp[3] = std::sqrt(2.0 / rp[3]);
|
||||
|
||||
box.center.x = (float)(rp[0]/scale) + c.x;
|
||||
box.center.y = (float)(rp[1]/scale) + c.y;
|
||||
box.size.width = (float)(rp[2]*2/scale);
|
||||
box.size.height = (float)(rp[3]*2/scale);
|
||||
if( box.size.width > box.size.height )
|
||||
{
|
||||
float tmp;
|
||||
CV_SWAP( box.size.width, box.size.height, tmp );
|
||||
box.angle = (float)(90 + rp[4]*180/CV_PI);
|
||||
}
|
||||
if( box.angle < -180 )
|
||||
box.angle += 360;
|
||||
if( box.angle > 360 )
|
||||
box.angle -= 360;
|
||||
|
||||
return box;
|
||||
}
|
||||
}
|
||||
|
||||
cv::RotatedRect cv::fitEllipse( InputArray _points )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat points = _points.getMat();
|
||||
int n = points.checkVector(2);
|
||||
return n == 5 ? fitEllipseDirect(points) : fitEllipseNoDirect(points);
|
||||
}
|
||||
|
||||
cv::RotatedRect cv::fitEllipseAMS( InputArray _points )
|
||||
{
|
||||
Mat points = _points.getMat();
|
||||
int i, n = points.checkVector(2);
|
||||
int depth = points.depth();
|
||||
float eps = 0;
|
||||
CV_Assert( n >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
|
||||
RotatedRect box;
|
||||
|
||||
if( n < 5 )
|
||||
CV_Error( cv::Error::StsBadSize, "There should be at least 5 points to fit the ellipse" );
|
||||
|
||||
Point2f c(0,0);
|
||||
|
||||
bool is_float = depth == CV_32F;
|
||||
const Point* ptsi = points.ptr<Point>();
|
||||
const Point2f* ptsf = points.ptr<Point2f>();
|
||||
|
||||
Mat A( n, 6, CV_64F);
|
||||
Matx<double, 6, 6> DM;
|
||||
Matx<double, 5, 5> M;
|
||||
Matx<double, 5, 1> pVec;
|
||||
Matx<double, 6, 1> coeffs;
|
||||
|
||||
double x0, y0, a, b, theta;
|
||||
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
|
||||
c += p;
|
||||
}
|
||||
c.x /= (float)n;
|
||||
c.y /= (float)n;
|
||||
|
||||
double s = 0;
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
|
||||
s += fabs(p.x - c.x) + fabs(p.y - c.y);
|
||||
}
|
||||
double scale = 100./(s > FLT_EPSILON ? s : (double)FLT_EPSILON);
|
||||
|
||||
// first, try the original pointset.
|
||||
// if it's singular, try to shift the points a bit
|
||||
int iter = 0;
|
||||
for( iter = 0; iter < 2; iter++ )
|
||||
{
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
|
||||
const Point2f delta = getOfs(eps);
|
||||
const double px = (p.x + delta.x - c.x)*scale, py = (p.y + delta.y - c.y)*scale;
|
||||
|
||||
A.at<double>(i,0) = px*px;
|
||||
A.at<double>(i,1) = px*py;
|
||||
A.at<double>(i,2) = py*py;
|
||||
A.at<double>(i,3) = px;
|
||||
A.at<double>(i,4) = py;
|
||||
A.at<double>(i,5) = 1.0;
|
||||
}
|
||||
cv::mulTransposed( A, DM, true, noArray(), 1.0, -1 );
|
||||
DM *= (1.0/n);
|
||||
double dnm = ( DM(2,5)*(DM(0,5) + DM(2,5)) - (DM(1,5)*DM(1,5)) );
|
||||
double ddm = (4.*(DM(0,5) + DM(2,5))*( (DM(0,5)*DM(2,5)) - (DM(1,5)*DM(1,5))));
|
||||
double ddmm = (2.*(DM(0,5) + DM(2,5))*( (DM(0,5)*DM(2,5)) - (DM(1,5)*DM(1,5))));
|
||||
|
||||
M(0,0)=((-DM(0,0) + DM(0,2) + DM(0,5)*DM(0,5))*(DM(1,5)*DM(1,5)) + (-2*DM(0,1)*DM(1,5) + DM(0,5)*(DM(0,0) \
|
||||
- (DM(0,5)*DM(0,5)) + (DM(1,5)*DM(1,5))))*DM(2,5) + (DM(0,0) - (DM(0,5)*DM(0,5)))*(DM(2,5)*DM(2,5))) / ddm;
|
||||
M(0,1)=((DM(1,5)*DM(1,5))*(-DM(0,1) + DM(1,2) + DM(0,5)*DM(1,5)) + (DM(0,1)*DM(0,5) - ((DM(0,5)*DM(0,5)) + 2*DM(1,1))*DM(1,5) + \
|
||||
(DM(1,5)*DM(1,5)*DM(1,5)))*DM(2,5) + (DM(0,1) - DM(0,5)*DM(1,5))*(DM(2,5)*DM(2,5))) / ddm;
|
||||
M(0,2)=(-2*DM(1,2)*DM(1,5)*DM(2,5) - DM(0,5)*(DM(2,5)*DM(2,5))*(DM(0,5) + DM(2,5)) + DM(0,2)*dnm + \
|
||||
(DM(1,5)*DM(1,5))*(DM(2,2) + DM(2,5)*(DM(0,5) + DM(2,5))))/ddm;
|
||||
M(0,3)=(DM(1,5)*(DM(1,5)*DM(2,3) - 2*DM(1,3)*DM(2,5)) + DM(0,3)*dnm) / ddm;
|
||||
M(0,4)=(DM(1,5)*(DM(1,5)*DM(2,4) - 2*DM(1,4)*DM(2,5)) + DM(0,4)*dnm) / ddm;
|
||||
M(1,0)=(-(DM(0,2)*DM(0,5)*DM(1,5)) + (2*DM(0,1)*DM(0,5) - DM(0,0)*DM(1,5))*DM(2,5))/ddmm;
|
||||
M(1,1)=(-(DM(0,1)*DM(1,5)*DM(2,5)) + DM(0,5)*(-(DM(1,2)*DM(1,5)) + 2*DM(1,1)*DM(2,5)))/ddmm;
|
||||
M(1,2)=(-(DM(0,2)*DM(1,5)*DM(2,5)) + DM(0,5)*(-(DM(1,5)*DM(2,2)) + 2*DM(1,2)*DM(2,5)))/ddmm;
|
||||
M(1,3)=(-(DM(0,3)*DM(1,5)*DM(2,5)) + DM(0,5)*(-(DM(1,5)*DM(2,3)) + 2*DM(1,3)*DM(2,5)))/ddmm;
|
||||
M(1,4)=(-(DM(0,4)*DM(1,5)*DM(2,5)) + DM(0,5)*(-(DM(1,5)*DM(2,4)) + 2*DM(1,4)*DM(2,5)))/ddmm;
|
||||
M(2,0)=(-2*DM(0,1)*DM(0,5)*DM(1,5) + (DM(0,0) + (DM(0,5)*DM(0,5)))*(DM(1,5)*DM(1,5)) + DM(0,5)*(-(DM(0,5)*DM(0,5)) \
|
||||
+ (DM(1,5)*DM(1,5)))*DM(2,5) - (DM(0,5)*DM(0,5))*(DM(2,5)*DM(2,5)) + DM(0,2)*(-(DM(1,5)*DM(1,5)) + DM(0,5)*(DM(0,5) + DM(2,5)))) / ddm;
|
||||
M(2,1)=((DM(0,5)*DM(0,5))*(DM(1,2) - DM(1,5)*DM(2,5)) + (DM(1,5)*DM(1,5))*(DM(0,1) - DM(1,2) + DM(1,5)*DM(2,5)) \
|
||||
+ DM(0,5)*(DM(1,2)*DM(2,5) + DM(1,5)*(-2*DM(1,1) + (DM(1,5)*DM(1,5)) - (DM(2,5)*DM(2,5))))) / ddm;
|
||||
M(2,2)=((DM(0,5)*DM(0,5))*(DM(2,2) - (DM(2,5)*DM(2,5))) + (DM(1,5)*DM(1,5))*(DM(0,2) - DM(2,2) + (DM(2,5)*DM(2,5))) + \
|
||||
DM(0,5)*(-2*DM(1,2)*DM(1,5) + DM(2,5)*((DM(1,5)*DM(1,5)) + DM(2,2) - (DM(2,5)*DM(2,5))))) / ddm;
|
||||
M(2,3)=((DM(1,5)*DM(1,5))*(DM(0,3) - DM(2,3)) + (DM(0,5)*DM(0,5))*DM(2,3) + DM(0,5)*(-2*DM(1,3)*DM(1,5) + DM(2,3)*DM(2,5))) / ddm;
|
||||
M(2,4)=((DM(1,5)*DM(1,5))*(DM(0,4) - DM(2,4)) + (DM(0,5)*DM(0,5))*DM(2,4) + DM(0,5)*(-2*DM(1,4)*DM(1,5) + DM(2,4)*DM(2,5))) / ddm;
|
||||
M(3,0)=DM(0,3);
|
||||
M(3,1)=DM(1,3);
|
||||
M(3,2)=DM(2,3);
|
||||
M(3,3)=DM(3,3);
|
||||
M(3,4)=DM(3,4);
|
||||
M(4,0)=DM(0,4);
|
||||
M(4,1)=DM(1,4);
|
||||
M(4,2)=DM(2,4);
|
||||
M(4,3)=DM(3,4);
|
||||
M(4,4)=DM(4,4);
|
||||
|
||||
double npow = (double)n * (double)n;
|
||||
npow = npow * npow * (double)n; // n^5
|
||||
if (fabs(cv::determinant(M)) > 1.0e-10 / npow) {
|
||||
break;
|
||||
}
|
||||
|
||||
eps = (float)(s/(n*2)*1e-2);
|
||||
}
|
||||
|
||||
if (iter < 2) {
|
||||
Mat eVal, eVec;
|
||||
eigenNonSymmetric(M, eVal, eVec);
|
||||
|
||||
// Select the eigen vector {a,b,c,d,e} which has the lowest eigenvalue
|
||||
int minpos = 0;
|
||||
double normi, normEVali, normMinpos, normEValMinpos;
|
||||
normMinpos = sqrt(eVec.at<double>(minpos,0)*eVec.at<double>(minpos,0) + eVec.at<double>(minpos,1)*eVec.at<double>(minpos,1) + \
|
||||
eVec.at<double>(minpos,2)*eVec.at<double>(minpos,2) + eVec.at<double>(minpos,3)*eVec.at<double>(minpos,3) + \
|
||||
eVec.at<double>(minpos,4)*eVec.at<double>(minpos,4) );
|
||||
normEValMinpos = eVal.at<double>(minpos,0) * normMinpos;
|
||||
for (i=1; i<5; i++) {
|
||||
normi = sqrt(eVec.at<double>(i,0)*eVec.at<double>(i,0) + eVec.at<double>(i,1)*eVec.at<double>(i,1) + \
|
||||
eVec.at<double>(i,2)*eVec.at<double>(i,2) + eVec.at<double>(i,3)*eVec.at<double>(i,3) + \
|
||||
eVec.at<double>(i,4)*eVec.at<double>(i,4) );
|
||||
normEVali = eVal.at<double>(i,0) * normi;
|
||||
if (normEVali < normEValMinpos) {
|
||||
minpos = i;
|
||||
normMinpos=normi;
|
||||
normEValMinpos=normEVali;
|
||||
}
|
||||
};
|
||||
|
||||
pVec(0) =eVec.at<double>(minpos,0) / normMinpos;
|
||||
pVec(1) =eVec.at<double>(minpos,1) / normMinpos;
|
||||
pVec(2) =eVec.at<double>(minpos,2) / normMinpos;
|
||||
pVec(3) =eVec.at<double>(minpos,3) / normMinpos;
|
||||
pVec(4) =eVec.at<double>(minpos,4) / normMinpos;
|
||||
|
||||
coeffs(0) =pVec(0) ;
|
||||
coeffs(1) =pVec(1) ;
|
||||
coeffs(2) =pVec(2) ;
|
||||
coeffs(3) =pVec(3) ;
|
||||
coeffs(4) =pVec(4) ;
|
||||
coeffs(5) =-pVec(0) *DM(0,5)-pVec(1) *DM(1,5)-coeffs(2) *DM(2,5);
|
||||
|
||||
// Check that an elliptical solution has been found. AMS sometimes produces Parabolic solutions.
|
||||
bool is_ellipse = (coeffs(0) < 0 && \
|
||||
coeffs(2) < (coeffs(1) *coeffs(1) )/(4.*coeffs(0) ) && \
|
||||
coeffs(5) > (-(coeffs(2) *(coeffs(3) *coeffs(3) )) + coeffs(1) *coeffs(3) *coeffs(4) - coeffs(0) *(coeffs(4) *coeffs(4) )) / \
|
||||
((coeffs(1) *coeffs(1) ) - 4*coeffs(0) *coeffs(2) )) || \
|
||||
(coeffs(0) > 0 && \
|
||||
coeffs(2) > (coeffs(1) *coeffs(1) )/(4.*coeffs(0) ) && \
|
||||
coeffs(5) < (-(coeffs(2) *(coeffs(3) *coeffs(3) )) + coeffs(1) *coeffs(3) *coeffs(4) - coeffs(0) *(coeffs(4) *coeffs(4) )) / \
|
||||
( (coeffs(1) *coeffs(1) ) - 4*coeffs(0) *coeffs(2) ));
|
||||
if (is_ellipse) {
|
||||
double u1 = pVec(2) *pVec(3) *pVec(3) - pVec(1) *pVec(3) *pVec(4) + pVec(0) *pVec(4) *pVec(4) + pVec(1) *pVec(1) *coeffs(5) ;
|
||||
double u2 = pVec(0) *pVec(2) *coeffs(5) ;
|
||||
double l1 = sqrt(pVec(1) *pVec(1) + (pVec(0) - pVec(2) )*(pVec(0) - pVec(2) ));
|
||||
double l2 = pVec(0) + pVec(2) ;
|
||||
double l3 = pVec(1) *pVec(1) - 4.0*pVec(0) *pVec(2) ;
|
||||
double p1 = 2.0*pVec(2) *pVec(3) - pVec(1) *pVec(4) ;
|
||||
double p2 = 2.0*pVec(0) *pVec(4) -(pVec(1) *pVec(3) );
|
||||
|
||||
x0 = p1/l3/scale + c.x;
|
||||
y0 = p2/l3/scale + c.y;
|
||||
a = std::sqrt(2.)*sqrt((u1 - 4.0*u2)/((l1 - l2)*l3))/scale;
|
||||
b = std::sqrt(2.)*sqrt(-1.0*((u1 - 4.0*u2)/((l1 + l2)*l3)))/scale;
|
||||
if (pVec(1) == 0) {
|
||||
if (pVec(0) < pVec(2) ) {
|
||||
theta = 0;
|
||||
} else {
|
||||
theta = CV_PI/2.;
|
||||
}
|
||||
} else {
|
||||
theta = CV_PI/2. + 0.5*std::atan2(pVec(1) , (pVec(0) - pVec(2) ));
|
||||
}
|
||||
|
||||
box.center.x = (float)x0;
|
||||
box.center.y = (float)y0;
|
||||
box.size.width = (float)(2.0*a);
|
||||
box.size.height = (float)(2.0*b);
|
||||
if( box.size.width > box.size.height )
|
||||
{
|
||||
float tmp;
|
||||
CV_SWAP( box.size.width, box.size.height, tmp );
|
||||
box.angle = (float)(90 + theta*180/CV_PI);
|
||||
} else {
|
||||
box.angle = (float)(fmod(theta*180/CV_PI,180.0));
|
||||
};
|
||||
|
||||
|
||||
} else {
|
||||
box = cv::fitEllipseDirect( points );
|
||||
}
|
||||
} else {
|
||||
box = cv::fitEllipseNoDirect( points );
|
||||
}
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
cv::RotatedRect cv::fitEllipseDirect( InputArray _points )
|
||||
{
|
||||
Mat points = _points.getMat();
|
||||
int i, n = points.checkVector(2);
|
||||
int depth = points.depth();
|
||||
float eps = 0;
|
||||
CV_Assert( n >= 0 && (depth == CV_32F || depth == CV_32S));
|
||||
|
||||
RotatedRect box;
|
||||
|
||||
if( n < 5 )
|
||||
CV_Error( cv::Error::StsBadSize, "There should be at least 5 points to fit the ellipse" );
|
||||
|
||||
Point2d c(0., 0.);
|
||||
|
||||
bool is_float = (depth == CV_32F);
|
||||
const Point* ptsi = points.ptr<Point>();
|
||||
const Point2f* ptsf = points.ptr<Point2f>();
|
||||
|
||||
Mat A( n, 6, CV_64F);
|
||||
Matx<double, 6, 6> DM;
|
||||
Matx33d M, TM, Q;
|
||||
Matx<double, 3, 1> pVec;
|
||||
|
||||
double x0, y0, a, b, theta, Ts;
|
||||
Mat eVal, eVec;
|
||||
double cond[3];
|
||||
double s = 0;
|
||||
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
|
||||
c.x += p.x;
|
||||
c.y += p.y;
|
||||
}
|
||||
c.x /= n;
|
||||
c.y /= n;
|
||||
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
|
||||
s += fabs(p.x - c.x) + fabs(p.y - c.y);
|
||||
}
|
||||
double scale = 100./(s > FLT_EPSILON ? s : (double)FLT_EPSILON);
|
||||
|
||||
// first, try the original pointset.
|
||||
// if it's singular, try to shift the points a bit
|
||||
int iter = 0;
|
||||
for( iter = 0; iter < 2; iter++ ) {
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
|
||||
const Point2f delta = getOfs(eps);
|
||||
double px = (p.x + delta.x - c.x)*scale, py = (p.y + delta.y - c.y)*scale;
|
||||
|
||||
A.at<double>(i,0) = px*px;
|
||||
A.at<double>(i,1) = px*py;
|
||||
A.at<double>(i,2) = py*py;
|
||||
A.at<double>(i,3) = px;
|
||||
A.at<double>(i,4) = py;
|
||||
A.at<double>(i,5) = 1.0;
|
||||
}
|
||||
cv::mulTransposed( A, DM, true, noArray(), 1.0, -1 );
|
||||
DM *= (1.0/n);
|
||||
|
||||
TM(0,0) = DM(0,5)*DM(3,5)*DM(4,4) - DM(0,5)*DM(3,4)*DM(4,5) - DM(0,4)*DM(3,5)*DM(5,4) + \
|
||||
DM(0,3)*DM(4,5)*DM(5,4) + DM(0,4)*DM(3,4)*DM(5,5) - DM(0,3)*DM(4,4)*DM(5,5);
|
||||
TM(0,1) = DM(1,5)*DM(3,5)*DM(4,4) - DM(1,5)*DM(3,4)*DM(4,5) - DM(1,4)*DM(3,5)*DM(5,4) + \
|
||||
DM(1,3)*DM(4,5)*DM(5,4) + DM(1,4)*DM(3,4)*DM(5,5) - DM(1,3)*DM(4,4)*DM(5,5);
|
||||
TM(0,2) = DM(2,5)*DM(3,5)*DM(4,4) - DM(2,5)*DM(3,4)*DM(4,5) - DM(2,4)*DM(3,5)*DM(5,4) + \
|
||||
DM(2,3)*DM(4,5)*DM(5,4) + DM(2,4)*DM(3,4)*DM(5,5) - DM(2,3)*DM(4,4)*DM(5,5);
|
||||
TM(1,0) = DM(0,5)*DM(3,3)*DM(4,5) - DM(0,5)*DM(3,5)*DM(4,3) + DM(0,4)*DM(3,5)*DM(5,3) - \
|
||||
DM(0,3)*DM(4,5)*DM(5,3) - DM(0,4)*DM(3,3)*DM(5,5) + DM(0,3)*DM(4,3)*DM(5,5);
|
||||
TM(1,1) = DM(1,5)*DM(3,3)*DM(4,5) - DM(1,5)*DM(3,5)*DM(4,3) + DM(1,4)*DM(3,5)*DM(5,3) - \
|
||||
DM(1,3)*DM(4,5)*DM(5,3) - DM(1,4)*DM(3,3)*DM(5,5) + DM(1,3)*DM(4,3)*DM(5,5);
|
||||
TM(1,2) = DM(2,5)*DM(3,3)*DM(4,5) - DM(2,5)*DM(3,5)*DM(4,3) + DM(2,4)*DM(3,5)*DM(5,3) - \
|
||||
DM(2,3)*DM(4,5)*DM(5,3) - DM(2,4)*DM(3,3)*DM(5,5) + DM(2,3)*DM(4,3)*DM(5,5);
|
||||
TM(2,0) = DM(0,5)*DM(3,4)*DM(4,3) - DM(0,5)*DM(3,3)*DM(4,4) - DM(0,4)*DM(3,4)*DM(5,3) + \
|
||||
DM(0,3)*DM(4,4)*DM(5,3) + DM(0,4)*DM(3,3)*DM(5,4) - DM(0,3)*DM(4,3)*DM(5,4);
|
||||
TM(2,1) = DM(1,5)*DM(3,4)*DM(4,3) - DM(1,5)*DM(3,3)*DM(4,4) - DM(1,4)*DM(3,4)*DM(5,3) + \
|
||||
DM(1,3)*DM(4,4)*DM(5,3) + DM(1,4)*DM(3,3)*DM(5,4) - DM(1,3)*DM(4,3)*DM(5,4);
|
||||
TM(2,2) = DM(2,5)*DM(3,4)*DM(4,3) - DM(2,5)*DM(3,3)*DM(4,4) - DM(2,4)*DM(3,4)*DM(5,3) + \
|
||||
DM(2,3)*DM(4,4)*DM(5,3) + DM(2,4)*DM(3,3)*DM(5,4) - DM(2,3)*DM(4,3)*DM(5,4);
|
||||
|
||||
Ts=(-(DM(3,5)*DM(4,4)*DM(5,3)) + DM(3,4)*DM(4,5)*DM(5,3) + DM(3,5)*DM(4,3)*DM(5,4) - \
|
||||
DM(3,3)*DM(4,5)*DM(5,4) - DM(3,4)*DM(4,3)*DM(5,5) + DM(3,3)*DM(4,4)*DM(5,5));
|
||||
|
||||
if (fabs(Ts) < DBL_EPSILON) {
|
||||
eps = (float)(s/(n*2)*1e-2);
|
||||
continue;
|
||||
}
|
||||
|
||||
M(0,0) = (DM(2,0) + (DM(2,3)*TM(0,0) + DM(2,4)*TM(1,0) + DM(2,5)*TM(2,0))/Ts)/2.;
|
||||
M(0,1) = (DM(2,1) + (DM(2,3)*TM(0,1) + DM(2,4)*TM(1,1) + DM(2,5)*TM(2,1))/Ts)/2.;
|
||||
M(0,2) = (DM(2,2) + (DM(2,3)*TM(0,2) + DM(2,4)*TM(1,2) + DM(2,5)*TM(2,2))/Ts)/2.;
|
||||
M(1,0) = -DM(1,0) - (DM(1,3)*TM(0,0) + DM(1,4)*TM(1,0) + DM(1,5)*TM(2,0))/Ts;
|
||||
M(1,1) = -DM(1,1) - (DM(1,3)*TM(0,1) + DM(1,4)*TM(1,1) + DM(1,5)*TM(2,1))/Ts;
|
||||
M(1,2) = -DM(1,2) - (DM(1,3)*TM(0,2) + DM(1,4)*TM(1,2) + DM(1,5)*TM(2,2))/Ts;
|
||||
M(2,0) = (DM(0,0) + (DM(0,3)*TM(0,0) + DM(0,4)*TM(1,0) + DM(0,5)*TM(2,0))/Ts)/2.;
|
||||
M(2,1) = (DM(0,1) + (DM(0,3)*TM(0,1) + DM(0,4)*TM(1,1) + DM(0,5)*TM(2,1))/Ts)/2.;
|
||||
M(2,2) = (DM(0,2) + (DM(0,3)*TM(0,2) + DM(0,4)*TM(1,2) + DM(0,5)*TM(2,2))/Ts)/2.;
|
||||
|
||||
eigenNonSymmetric(M, eVal, eVec);
|
||||
|
||||
// Select the eigen vector {a,b,c} which satisfies 4ac-b^2 > 0
|
||||
cond[0]=(4.0 * eVec.at<double>(0,0) * eVec.at<double>(0,2) - eVec.at<double>(0,1) * eVec.at<double>(0,1));
|
||||
cond[1]=(4.0 * eVec.at<double>(1,0) * eVec.at<double>(1,2) - eVec.at<double>(1,1) * eVec.at<double>(1,1));
|
||||
cond[2]=(4.0 * eVec.at<double>(2,0) * eVec.at<double>(2,2) - eVec.at<double>(2,1) * eVec.at<double>(2,1));
|
||||
if (cond[0]<cond[1]) {
|
||||
i = (cond[1]<cond[2]) ? 2 : 1;
|
||||
} else {
|
||||
i = (cond[0]<cond[2]) ? 2 : 0;
|
||||
}
|
||||
// Check that the ellipse condition 4ac-b^2 is meaningfully positive
|
||||
// (not just positive due to floating-point noise from complex eigenvalues)
|
||||
{
|
||||
double v0 = eVec.at<double>(i,0), v1 = eVec.at<double>(i,1), v2 = eVec.at<double>(i,2);
|
||||
double vnorm2 = v0*v0 + v1*v1 + v2*v2;
|
||||
if (cond[i] > 1e-6 * vnorm2)
|
||||
break;
|
||||
}
|
||||
eps = (float)(s/(n*2)*1e-2);
|
||||
}
|
||||
|
||||
if( iter < 2 ) {
|
||||
double norm = std::sqrt(eVec.at<double>(i,0)*eVec.at<double>(i,0) + eVec.at<double>(i,1)*eVec.at<double>(i,1) + eVec.at<double>(i,2)*eVec.at<double>(i,2));
|
||||
if (((eVec.at<double>(i,0)<0.0 ? -1 : 1) * (eVec.at<double>(i,1)<0.0 ? -1 : 1) * (eVec.at<double>(i,2)<0.0 ? -1 : 1)) <= 0.0) {
|
||||
norm=-1.0*norm;
|
||||
}
|
||||
pVec(0) =eVec.at<double>(i,0)/norm; pVec(1) =eVec.at<double>(i,1)/norm;pVec(2) =eVec.at<double>(i,2)/norm;
|
||||
|
||||
// Q = (TM . pVec)/Ts;
|
||||
Q(0,0) = (TM(0,0)*pVec(0) +TM(0,1)*pVec(1) +TM(0,2)*pVec(2) )/Ts;
|
||||
Q(0,1) = (TM(1,0)*pVec(0) +TM(1,1)*pVec(1) +TM(1,2)*pVec(2) )/Ts;
|
||||
Q(0,2) = (TM(2,0)*pVec(0) +TM(2,1)*pVec(1) +TM(2,2)*pVec(2) )/Ts;
|
||||
|
||||
// We compute the ellipse properties in the shifted coordinates as doing so improves the numerical accuracy.
|
||||
|
||||
double u1 = pVec(2)*Q(0,0)*Q(0,0) - pVec(1)*Q(0,0)*Q(0,1) + pVec(0)*Q(0,1)*Q(0,1) + pVec(1)*pVec(1)*Q(0,2);
|
||||
double u2 = pVec(0)*pVec(2)*Q(0,2);
|
||||
double l1 = sqrt(pVec(1)*pVec(1) + (pVec(0) - pVec(2))*(pVec(0) - pVec(2)));
|
||||
double l2 = pVec(0) + pVec(2) ;
|
||||
double l3 = pVec(1)*pVec(1) - 4*pVec(0)*pVec(2) ;
|
||||
double p1 = 2*pVec(2)*Q(0,0) - pVec(1)*Q(0,1);
|
||||
double p2 = 2*pVec(0)*Q(0,1) - pVec(1)*Q(0,0);
|
||||
|
||||
x0 = (p1/l3/scale) + c.x;
|
||||
y0 = (p2/l3/scale) + c.y;
|
||||
a = sqrt(2.)*sqrt((u1 - 4.0*u2)/((l1 - l2)*l3))/scale;
|
||||
b = sqrt(2.)*sqrt(-1.0*((u1 - 4.0*u2)/((l1 + l2)*l3)))/scale;
|
||||
if (pVec(1) == 0) {
|
||||
if (pVec(0) < pVec(2) ) {
|
||||
theta = 0;
|
||||
} else {
|
||||
theta = CV_PI/2.;
|
||||
}
|
||||
} else {
|
||||
theta = CV_PI/2. + 0.5*std::atan2(pVec(1) , (pVec(0) - pVec(2) ));
|
||||
}
|
||||
|
||||
box.center.x = (float)x0;
|
||||
box.center.y = (float)y0;
|
||||
box.size.width = (float)(2.0*a);
|
||||
box.size.height = (float)(2.0*b);
|
||||
if( box.size.width > box.size.height )
|
||||
{
|
||||
float tmp;
|
||||
CV_SWAP( box.size.width, box.size.height, tmp );
|
||||
box.angle = (float)(fmod((90 + theta*180/CV_PI),180.0)) ;
|
||||
} else {
|
||||
box.angle = (float)(fmod(theta*180/CV_PI,180.0));
|
||||
};
|
||||
} else {
|
||||
box = cv::fitEllipseNoDirect( points );
|
||||
}
|
||||
return box;
|
||||
}
|
||||
|
||||
namespace cv
|
||||
{
|
||||
// @misc{Chatfield2017,
|
||||
// author = {Chatfield, Carl},
|
||||
// title = {A Simple Method for Distance to Ellipse},
|
||||
// year = {2017},
|
||||
// publisher = {GitHub},
|
||||
// howpublished = {\url{https://blog.chatfield.io/simple-method-for-distance-to-ellipse/}},
|
||||
// }
|
||||
// https://github.com/0xfaded/ellipse_demo/blob/master/ellipse_trig_free.py
|
||||
static void solveFast(float semi_major, float semi_minor, const cv::Point2f& pt, cv::Point2f& closest_pt)
|
||||
{
|
||||
float px = std::abs(pt.x);
|
||||
float py = std::abs(pt.y);
|
||||
|
||||
float tx = 0.707f;
|
||||
float ty = 0.707f;
|
||||
|
||||
float a = semi_major;
|
||||
float b = semi_minor;
|
||||
|
||||
for (int iter = 0; iter < 3; iter++)
|
||||
{
|
||||
float x = a * tx;
|
||||
float y = b * ty;
|
||||
|
||||
float ex = (a*a - b*b) * tx*tx*tx / a;
|
||||
float ey = (b*b - a*a) * ty*ty*ty / b;
|
||||
|
||||
float rx = x - ex;
|
||||
float ry = y - ey;
|
||||
|
||||
float qx = px - ex;
|
||||
float qy = py - ey;
|
||||
|
||||
float r = std::hypotf(rx, ry);
|
||||
float q = std::hypotf(qx, qy);
|
||||
|
||||
tx = std::min(1.0f, std::max(0.0f, (qx * r / q + ex) / a));
|
||||
ty = std::min(1.0f, std::max(0.0f, (qy * r / q + ey) / b));
|
||||
float t = std::hypotf(tx, ty);
|
||||
tx /= t;
|
||||
ty /= t;
|
||||
}
|
||||
|
||||
closest_pt.x = std::copysign(a * tx, pt.x);
|
||||
closest_pt.y = std::copysign(b * ty, pt.y);
|
||||
}
|
||||
} // namespace cv
|
||||
|
||||
void cv::getClosestEllipsePoints( const RotatedRect& ellipse_params, InputArray _points, OutputArray closest_pts )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
Mat points = _points.getMat();
|
||||
int n = points.checkVector(2);
|
||||
int depth = points.depth();
|
||||
CV_Assert(depth == CV_32F || depth == CV_32S);
|
||||
CV_Assert(n > 0);
|
||||
|
||||
bool is_float = (depth == CV_32F);
|
||||
const Point* ptsi = points.ptr<Point>();
|
||||
const Point2f* ptsf = points.ptr<Point2f>();
|
||||
|
||||
float semi_major = ellipse_params.size.width / 2.0f;
|
||||
float semi_minor = ellipse_params.size.height / 2.0f;
|
||||
float angle_deg = ellipse_params.angle;
|
||||
if (semi_major < semi_minor)
|
||||
{
|
||||
std::swap(semi_major, semi_minor);
|
||||
angle_deg += 90;
|
||||
}
|
||||
|
||||
Matx23f align_T_ori_f32;
|
||||
float theta_rad = static_cast<float>(angle_deg * M_PI / 180);
|
||||
float co = std::cos(theta_rad);
|
||||
float si = std::sin(theta_rad);
|
||||
float shift_x = ellipse_params.center.x;
|
||||
float shift_y = ellipse_params.center.y;
|
||||
|
||||
align_T_ori_f32(0,0) = co;
|
||||
align_T_ori_f32(0,1) = si;
|
||||
align_T_ori_f32(0,2) = -co*shift_x - si*shift_y;
|
||||
align_T_ori_f32(1,0) = -si;
|
||||
align_T_ori_f32(1,1) = co;
|
||||
align_T_ori_f32(1,2) = si*shift_x - co*shift_y;
|
||||
|
||||
Matx23f ori_T_align_f32;
|
||||
ori_T_align_f32(0,0) = co;
|
||||
ori_T_align_f32(0,1) = -si;
|
||||
ori_T_align_f32(0,2) = shift_x;
|
||||
ori_T_align_f32(1,0) = si;
|
||||
ori_T_align_f32(1,1) = co;
|
||||
ori_T_align_f32(1,2) = shift_y;
|
||||
|
||||
std::vector<Point2f> closest_pts_list;
|
||||
closest_pts_list.reserve(n);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
|
||||
Matx31f pmat(p.x, p.y, 1);
|
||||
|
||||
Matx21f X_align = align_T_ori_f32 * pmat;
|
||||
Point2f closest_pt;
|
||||
solveFast(semi_major, semi_minor, Point2f(X_align(0,0), X_align(1,0)), closest_pt);
|
||||
|
||||
pmat(0,0) = closest_pt.x;
|
||||
pmat(1,0) = closest_pt.y;
|
||||
Matx21f closest_pt_ori = ori_T_align_f32 * pmat;
|
||||
closest_pts_list.push_back(Point2f(closest_pt_ori(0,0), closest_pt_ori(1,0)));
|
||||
}
|
||||
|
||||
cv::Mat(closest_pts_list).convertTo(closest_pts, CV_32F);
|
||||
}
|
||||
|
||||
/* End of file. */
|
||||
@@ -1,920 +0,0 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// Intel License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of Intel Corporation may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
|
||||
int Subdiv2D::nextEdge(int edge) const
|
||||
{
|
||||
CV_DbgAssert((size_t)(edge >> 2) < qedges.size());
|
||||
return qedges[edge >> 2].next[edge & 3];
|
||||
}
|
||||
|
||||
int Subdiv2D::rotateEdge(int edge, int rotate) const
|
||||
{
|
||||
return (edge & ~3) + ((edge + rotate) & 3);
|
||||
}
|
||||
|
||||
int Subdiv2D::symEdge(int edge) const
|
||||
{
|
||||
return edge ^ 2;
|
||||
}
|
||||
|
||||
int Subdiv2D::getEdge(int edge, int nextEdgeType) const
|
||||
{
|
||||
CV_DbgAssert((size_t)(edge >> 2) < qedges.size());
|
||||
edge = qedges[edge >> 2].next[(edge + nextEdgeType) & 3];
|
||||
return (edge & ~3) + ((edge + (nextEdgeType >> 4)) & 3);
|
||||
}
|
||||
|
||||
int Subdiv2D::edgeOrg(int edge, CV_OUT Point2f* orgpt) const
|
||||
{
|
||||
CV_DbgAssert((size_t)(edge >> 2) < qedges.size());
|
||||
int vidx = qedges[edge >> 2].pt[edge & 3];
|
||||
if( orgpt )
|
||||
{
|
||||
CV_DbgAssert((size_t)vidx < vtx.size());
|
||||
*orgpt = vtx[vidx].pt;
|
||||
}
|
||||
return vidx;
|
||||
}
|
||||
|
||||
int Subdiv2D::edgeDst(int edge, CV_OUT Point2f* dstpt) const
|
||||
{
|
||||
CV_DbgAssert((size_t)(edge >> 2) < qedges.size());
|
||||
int vidx = qedges[edge >> 2].pt[(edge + 2) & 3];
|
||||
if( dstpt )
|
||||
{
|
||||
CV_DbgAssert((size_t)vidx < vtx.size());
|
||||
*dstpt = vtx[vidx].pt;
|
||||
}
|
||||
return vidx;
|
||||
}
|
||||
|
||||
|
||||
Point2f Subdiv2D::getVertex(int vertex, CV_OUT int* firstEdge) const
|
||||
{
|
||||
CV_DbgAssert((size_t)vertex < vtx.size());
|
||||
if( firstEdge )
|
||||
*firstEdge = vtx[vertex].firstEdge;
|
||||
return vtx[vertex].pt;
|
||||
}
|
||||
|
||||
|
||||
Subdiv2D::Subdiv2D()
|
||||
{
|
||||
validGeometry = false;
|
||||
freeQEdge = 0;
|
||||
freePoint = 0;
|
||||
recentEdge = 0;
|
||||
}
|
||||
|
||||
Subdiv2D::Subdiv2D(Rect rect)
|
||||
{
|
||||
validGeometry = false;
|
||||
freeQEdge = 0;
|
||||
freePoint = 0;
|
||||
recentEdge = 0;
|
||||
|
||||
initDelaunay(rect);
|
||||
}
|
||||
|
||||
Subdiv2D::Subdiv2D(Rect2f rect)
|
||||
{
|
||||
validGeometry = false;
|
||||
freeQEdge = 0;
|
||||
freePoint = 0;
|
||||
recentEdge = 0;
|
||||
|
||||
initDelaunay(rect);
|
||||
}
|
||||
|
||||
|
||||
Subdiv2D::QuadEdge::QuadEdge()
|
||||
{
|
||||
next[0] = next[1] = next[2] = next[3] = 0;
|
||||
pt[0] = pt[1] = pt[2] = pt[3] = 0;
|
||||
}
|
||||
|
||||
Subdiv2D::QuadEdge::QuadEdge(int edgeidx)
|
||||
{
|
||||
CV_DbgAssert((edgeidx & 3) == 0);
|
||||
next[0] = edgeidx;
|
||||
next[1] = edgeidx+3;
|
||||
next[2] = edgeidx+2;
|
||||
next[3] = edgeidx+1;
|
||||
|
||||
pt[0] = pt[1] = pt[2] = pt[3] = 0;
|
||||
}
|
||||
|
||||
bool Subdiv2D::QuadEdge::isfree() const
|
||||
{
|
||||
return next[0] <= 0;
|
||||
}
|
||||
|
||||
Subdiv2D::Vertex::Vertex()
|
||||
{
|
||||
firstEdge = 0;
|
||||
type = -1;
|
||||
}
|
||||
|
||||
Subdiv2D::Vertex::Vertex(Point2f _pt, bool _isvirtual, int _firstEdge)
|
||||
{
|
||||
firstEdge = _firstEdge;
|
||||
type = (int)_isvirtual;
|
||||
pt = _pt;
|
||||
}
|
||||
|
||||
bool Subdiv2D::Vertex::isvirtual() const
|
||||
{
|
||||
return type > 0;
|
||||
}
|
||||
|
||||
bool Subdiv2D::Vertex::isfree() const
|
||||
{
|
||||
return type < 0;
|
||||
}
|
||||
|
||||
void Subdiv2D::splice( int edgeA, int edgeB )
|
||||
{
|
||||
int& a_next = qedges[edgeA >> 2].next[edgeA & 3];
|
||||
int& b_next = qedges[edgeB >> 2].next[edgeB & 3];
|
||||
int a_rot = rotateEdge(a_next, 1);
|
||||
int b_rot = rotateEdge(b_next, 1);
|
||||
int& a_rot_next = qedges[a_rot >> 2].next[a_rot & 3];
|
||||
int& b_rot_next = qedges[b_rot >> 2].next[b_rot & 3];
|
||||
std::swap(a_next, b_next);
|
||||
std::swap(a_rot_next, b_rot_next);
|
||||
}
|
||||
|
||||
void Subdiv2D::setEdgePoints(int edge, int orgPt, int dstPt)
|
||||
{
|
||||
qedges[edge >> 2].pt[edge & 3] = orgPt;
|
||||
qedges[edge >> 2].pt[(edge + 2) & 3] = dstPt;
|
||||
vtx[orgPt].firstEdge = edge;
|
||||
vtx[dstPt].firstEdge = edge ^ 2;
|
||||
}
|
||||
|
||||
int Subdiv2D::connectEdges( int edgeA, int edgeB )
|
||||
{
|
||||
int edge = newEdge();
|
||||
|
||||
splice(edge, getEdge(edgeA, NEXT_AROUND_LEFT));
|
||||
splice(symEdge(edge), edgeB);
|
||||
|
||||
setEdgePoints(edge, edgeDst(edgeA), edgeOrg(edgeB));
|
||||
return edge;
|
||||
}
|
||||
|
||||
void Subdiv2D::swapEdges( int edge )
|
||||
{
|
||||
int sedge = symEdge(edge);
|
||||
int a = getEdge(edge, PREV_AROUND_ORG);
|
||||
int b = getEdge(sedge, PREV_AROUND_ORG);
|
||||
|
||||
splice(edge, a);
|
||||
splice(sedge, b);
|
||||
|
||||
setEdgePoints(edge, edgeDst(a), edgeDst(b));
|
||||
|
||||
splice(edge, getEdge(a, NEXT_AROUND_LEFT));
|
||||
splice(sedge, getEdge(b, NEXT_AROUND_LEFT));
|
||||
}
|
||||
|
||||
static double triangleArea( Point2f a, Point2f b, Point2f c )
|
||||
{
|
||||
return ((double)b.x - a.x) * ((double)c.y - a.y) - ((double)b.y - a.y) * ((double)c.x - a.x);
|
||||
}
|
||||
|
||||
int Subdiv2D::isRightOf(Point2f pt, int edge) const
|
||||
{
|
||||
Point2f org, dst;
|
||||
edgeOrg(edge, &org);
|
||||
edgeDst(edge, &dst);
|
||||
double cw_area = triangleArea( pt, dst, org );
|
||||
|
||||
return (cw_area > 0) - (cw_area < 0);
|
||||
}
|
||||
|
||||
int Subdiv2D::newEdge()
|
||||
{
|
||||
if( freeQEdge <= 0 )
|
||||
{
|
||||
qedges.emplace_back();
|
||||
freeQEdge = (int)(qedges.size()-1);
|
||||
}
|
||||
int edge = freeQEdge*4;
|
||||
freeQEdge = qedges[edge >> 2].next[1];
|
||||
qedges[edge >> 2] = QuadEdge(edge);
|
||||
return edge;
|
||||
}
|
||||
|
||||
void Subdiv2D::deleteEdge(int edge)
|
||||
{
|
||||
CV_DbgAssert((size_t)(edge >> 2) < (size_t)qedges.size());
|
||||
splice( edge, getEdge(edge, PREV_AROUND_ORG) );
|
||||
int sedge = symEdge(edge);
|
||||
splice(sedge, getEdge(sedge, PREV_AROUND_ORG) );
|
||||
|
||||
edge >>= 2;
|
||||
qedges[edge].next[0] = 0;
|
||||
qedges[edge].next[1] = freeQEdge;
|
||||
freeQEdge = edge;
|
||||
}
|
||||
|
||||
int Subdiv2D::newPoint(Point2f pt, bool isvirtual, int firstEdge)
|
||||
{
|
||||
if( freePoint == 0 )
|
||||
{
|
||||
vtx.emplace_back();
|
||||
freePoint = (int)(vtx.size()-1);
|
||||
}
|
||||
int vidx = freePoint;
|
||||
freePoint = vtx[vidx].firstEdge;
|
||||
vtx[vidx] = Vertex(pt, isvirtual, firstEdge);
|
||||
|
||||
return vidx;
|
||||
}
|
||||
|
||||
void Subdiv2D::deletePoint(int vidx)
|
||||
{
|
||||
CV_DbgAssert( (size_t)vidx < vtx.size() );
|
||||
vtx[vidx].firstEdge = freePoint;
|
||||
vtx[vidx].type = -1;
|
||||
freePoint = vidx;
|
||||
}
|
||||
|
||||
int Subdiv2D::locate(Point2f pt, int& _edge, int& _vertex)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
int vertex = 0;
|
||||
|
||||
int i, maxEdges = (int)(qedges.size() * 4);
|
||||
|
||||
if( qedges.size() < (size_t)4 )
|
||||
CV_Error( cv::Error::StsError, "Subdivision is empty" );
|
||||
|
||||
if( pt.x < topLeft.x || pt.y < topLeft.y || pt.x >= bottomRight.x || pt.y >= bottomRight.y )
|
||||
CV_Error( cv::Error::StsOutOfRange, "" );
|
||||
|
||||
int edge = recentEdge;
|
||||
CV_Assert(edge > 0);
|
||||
|
||||
int location = PTLOC_ERROR;
|
||||
|
||||
int right_of_curr = isRightOf(pt, edge);
|
||||
if( right_of_curr > 0 )
|
||||
{
|
||||
edge = symEdge(edge);
|
||||
right_of_curr = -right_of_curr;
|
||||
}
|
||||
|
||||
for( i = 0; i < maxEdges; i++ )
|
||||
{
|
||||
int onext_edge = nextEdge( edge );
|
||||
int dprev_edge = getEdge( edge, PREV_AROUND_DST );
|
||||
|
||||
int right_of_onext = isRightOf( pt, onext_edge );
|
||||
int right_of_dprev = isRightOf( pt, dprev_edge );
|
||||
|
||||
if( right_of_dprev > 0 )
|
||||
{
|
||||
if( right_of_onext > 0 || (right_of_onext == 0 && right_of_curr == 0) )
|
||||
{
|
||||
location = PTLOC_INSIDE;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
right_of_curr = right_of_onext;
|
||||
edge = onext_edge;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( right_of_onext > 0 )
|
||||
{
|
||||
if( right_of_dprev == 0 && right_of_curr == 0 )
|
||||
{
|
||||
location = PTLOC_INSIDE;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
right_of_curr = right_of_dprev;
|
||||
edge = dprev_edge;
|
||||
}
|
||||
}
|
||||
else if( right_of_curr == 0 &&
|
||||
isRightOf( vtx[edgeDst(onext_edge)].pt, edge ) >= 0 )
|
||||
{
|
||||
edge = symEdge( edge );
|
||||
}
|
||||
else
|
||||
{
|
||||
right_of_curr = right_of_onext;
|
||||
edge = onext_edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recentEdge = edge;
|
||||
|
||||
if( location == PTLOC_INSIDE )
|
||||
{
|
||||
Point2f org_pt, dst_pt;
|
||||
edgeOrg(edge, &org_pt);
|
||||
edgeDst(edge, &dst_pt);
|
||||
|
||||
double t1 = fabs( pt.x - org_pt.x );
|
||||
t1 += fabs( pt.y - org_pt.y );
|
||||
double t2 = fabs( pt.x - dst_pt.x );
|
||||
t2 += fabs( pt.y - dst_pt.y );
|
||||
double t3 = fabs( org_pt.x - dst_pt.x );
|
||||
t3 += fabs( org_pt.y - dst_pt.y );
|
||||
|
||||
if( t1 < FLT_EPSILON )
|
||||
{
|
||||
location = PTLOC_VERTEX;
|
||||
vertex = edgeOrg( edge );
|
||||
edge = 0;
|
||||
}
|
||||
else if( t2 < FLT_EPSILON )
|
||||
{
|
||||
location = PTLOC_VERTEX;
|
||||
vertex = edgeDst( edge );
|
||||
edge = 0;
|
||||
}
|
||||
else if( (t1 < t3 || t2 < t3) &&
|
||||
fabs( triangleArea( pt, org_pt, dst_pt )) < FLT_EPSILON )
|
||||
{
|
||||
location = PTLOC_ON_EDGE;
|
||||
vertex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if( location == PTLOC_ERROR )
|
||||
{
|
||||
edge = 0;
|
||||
vertex = 0;
|
||||
}
|
||||
|
||||
_edge = edge;
|
||||
_vertex = vertex;
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
|
||||
inline int
|
||||
isPtInCircle3( Point2f pt, Point2f a, Point2f b, Point2f c)
|
||||
{
|
||||
const double eps = FLT_EPSILON*0.125;
|
||||
double val = ((double)a.x * a.x + (double)a.y * a.y) * triangleArea( b, c, pt );
|
||||
val -= ((double)b.x * b.x + (double)b.y * b.y) * triangleArea( a, c, pt );
|
||||
val += ((double)c.x * c.x + (double)c.y * c.y) * triangleArea( a, b, pt );
|
||||
val -= ((double)pt.x * pt.x + (double)pt.y * pt.y) * triangleArea( a, b, c );
|
||||
|
||||
return val > eps ? 1 : val < -eps ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
int Subdiv2D::insert(Point2f pt)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
int curr_point = 0, curr_edge = 0, deleted_edge = 0;
|
||||
int location = locate( pt, curr_edge, curr_point );
|
||||
|
||||
if( location == PTLOC_ERROR )
|
||||
CV_Error( cv::Error::StsBadSize, "" );
|
||||
|
||||
if( location == PTLOC_OUTSIDE_RECT )
|
||||
CV_Error( cv::Error::StsOutOfRange, "" );
|
||||
|
||||
if( location == PTLOC_VERTEX )
|
||||
return curr_point;
|
||||
|
||||
if( location == PTLOC_ON_EDGE )
|
||||
{
|
||||
deleted_edge = curr_edge;
|
||||
recentEdge = curr_edge = getEdge( curr_edge, PREV_AROUND_ORG );
|
||||
deleteEdge(deleted_edge);
|
||||
}
|
||||
else if( location == PTLOC_INSIDE )
|
||||
;
|
||||
else
|
||||
CV_Error_(cv::Error::StsError, ("Subdiv2D::locate returned invalid location = %d", location) );
|
||||
|
||||
CV_Assert( curr_edge != 0 );
|
||||
validGeometry = false;
|
||||
|
||||
curr_point = newPoint(pt, false);
|
||||
int base_edge = newEdge();
|
||||
int first_point = edgeOrg(curr_edge);
|
||||
setEdgePoints(base_edge, first_point, curr_point);
|
||||
splice(base_edge, curr_edge);
|
||||
|
||||
do
|
||||
{
|
||||
base_edge = connectEdges( curr_edge, symEdge(base_edge) );
|
||||
curr_edge = getEdge(base_edge, PREV_AROUND_ORG);
|
||||
}
|
||||
while( edgeDst(curr_edge) != first_point );
|
||||
|
||||
curr_edge = getEdge( base_edge, PREV_AROUND_ORG );
|
||||
|
||||
int i, max_edges = (int)(qedges.size()*4);
|
||||
|
||||
for( i = 0; i < max_edges; i++ )
|
||||
{
|
||||
int temp_dst = 0, curr_org = 0, curr_dst = 0;
|
||||
int temp_edge = getEdge( curr_edge, PREV_AROUND_ORG );
|
||||
|
||||
temp_dst = edgeDst( temp_edge );
|
||||
curr_org = edgeOrg( curr_edge );
|
||||
curr_dst = edgeDst( curr_edge );
|
||||
|
||||
if( isRightOf( vtx[temp_dst].pt, curr_edge ) > 0 &&
|
||||
isPtInCircle3( vtx[curr_org].pt, vtx[temp_dst].pt,
|
||||
vtx[curr_dst].pt, vtx[curr_point].pt ) < 0 )
|
||||
{
|
||||
swapEdges( curr_edge );
|
||||
curr_edge = getEdge( curr_edge, PREV_AROUND_ORG );
|
||||
}
|
||||
else if( curr_org == first_point )
|
||||
break;
|
||||
else
|
||||
curr_edge = getEdge( nextEdge( curr_edge ), PREV_AROUND_LEFT );
|
||||
}
|
||||
|
||||
return curr_point;
|
||||
}
|
||||
|
||||
void Subdiv2D::insert(const std::vector<Point2f>& ptvec)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
for( size_t i = 0; i < ptvec.size(); i++ )
|
||||
insert(ptvec[i]);
|
||||
}
|
||||
|
||||
void Subdiv2D::initDelaunay( Rect rect )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
float big_coord = 6.f * MAX( rect.width, rect.height );
|
||||
float rx = (float)rect.x;
|
||||
float ry = (float)rect.y;
|
||||
|
||||
vtx.clear();
|
||||
qedges.clear();
|
||||
|
||||
recentEdge = 0;
|
||||
validGeometry = false;
|
||||
|
||||
topLeft = Point2f( rx, ry );
|
||||
bottomRight = Point2f( rx + rect.width, ry + rect.height );
|
||||
|
||||
Point2f ppA( rx + big_coord, ry );
|
||||
Point2f ppB( rx, ry + big_coord );
|
||||
Point2f ppC( rx - big_coord, ry - big_coord );
|
||||
|
||||
vtx.emplace_back();
|
||||
qedges.emplace_back();
|
||||
|
||||
freeQEdge = 0;
|
||||
freePoint = 0;
|
||||
|
||||
int pA = newPoint(ppA, false);
|
||||
int pB = newPoint(ppB, false);
|
||||
int pC = newPoint(ppC, false);
|
||||
|
||||
int edge_AB = newEdge();
|
||||
int edge_BC = newEdge();
|
||||
int edge_CA = newEdge();
|
||||
|
||||
setEdgePoints( edge_AB, pA, pB );
|
||||
setEdgePoints( edge_BC, pB, pC );
|
||||
setEdgePoints( edge_CA, pC, pA );
|
||||
|
||||
splice( edge_AB, symEdge( edge_CA ));
|
||||
splice( edge_BC, symEdge( edge_AB ));
|
||||
splice( edge_CA, symEdge( edge_BC ));
|
||||
|
||||
recentEdge = edge_AB;
|
||||
}
|
||||
|
||||
void Subdiv2D::initDelaunay( Rect2f rect )
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
float big_coord = 6.f * MAX( rect.width, rect.height );
|
||||
float rx = rect.x;
|
||||
float ry = rect.y;
|
||||
|
||||
vtx.clear();
|
||||
qedges.clear();
|
||||
|
||||
recentEdge = 0;
|
||||
validGeometry = false;
|
||||
|
||||
topLeft = Point2f( rx, ry );
|
||||
bottomRight = Point2f( rx + rect.width, ry + rect.height );
|
||||
|
||||
Point2f ppA( rx + big_coord, ry );
|
||||
Point2f ppB( rx, ry + big_coord );
|
||||
Point2f ppC( rx - big_coord, ry - big_coord );
|
||||
|
||||
vtx.emplace_back();
|
||||
qedges.emplace_back();
|
||||
|
||||
freeQEdge = 0;
|
||||
freePoint = 0;
|
||||
|
||||
int pA = newPoint(ppA, false);
|
||||
int pB = newPoint(ppB, false);
|
||||
int pC = newPoint(ppC, false);
|
||||
|
||||
int edge_AB = newEdge();
|
||||
int edge_BC = newEdge();
|
||||
int edge_CA = newEdge();
|
||||
|
||||
setEdgePoints( edge_AB, pA, pB );
|
||||
setEdgePoints( edge_BC, pB, pC );
|
||||
setEdgePoints( edge_CA, pC, pA );
|
||||
|
||||
splice( edge_AB, symEdge( edge_CA ));
|
||||
splice( edge_BC, symEdge( edge_AB ));
|
||||
splice( edge_CA, symEdge( edge_BC ));
|
||||
|
||||
recentEdge = edge_AB;
|
||||
}
|
||||
|
||||
|
||||
void Subdiv2D::clearVoronoi()
|
||||
{
|
||||
size_t i, total = qedges.size();
|
||||
|
||||
for( i = 0; i < total; i++ )
|
||||
qedges[i].pt[1] = qedges[i].pt[3] = 0;
|
||||
|
||||
total = vtx.size();
|
||||
for( i = 0; i < total; i++ )
|
||||
{
|
||||
if( vtx[i].isvirtual() )
|
||||
deletePoint((int)i);
|
||||
}
|
||||
|
||||
validGeometry = false;
|
||||
}
|
||||
|
||||
|
||||
static Point2f computeVoronoiPoint(Point2f org0, Point2f dst0, Point2f org1, Point2f dst1)
|
||||
{
|
||||
double a0 = dst0.x - org0.x;
|
||||
double b0 = dst0.y - org0.y;
|
||||
double c0 = -0.5*(a0 * (dst0.x + org0.x) + b0 * (dst0.y + org0.y));
|
||||
|
||||
double a1 = dst1.x - org1.x;
|
||||
double b1 = dst1.y - org1.y;
|
||||
double c1 = -0.5*(a1 * (dst1.x + org1.x) + b1 * (dst1.y + org1.y));
|
||||
|
||||
double det = a0 * b1 - a1 * b0;
|
||||
|
||||
if( det != 0 )
|
||||
{
|
||||
det = 1. / det;
|
||||
return Point2f((float) ((b0 * c1 - b1 * c0) * det),
|
||||
(float) ((a1 * c0 - a0 * c1) * det));
|
||||
}
|
||||
|
||||
return Point2f(FLT_MAX, FLT_MAX);
|
||||
}
|
||||
|
||||
|
||||
void Subdiv2D::calcVoronoi()
|
||||
{
|
||||
// check if it is already calculated
|
||||
if( validGeometry )
|
||||
return;
|
||||
|
||||
clearVoronoi();
|
||||
int i, total = (int)qedges.size();
|
||||
|
||||
// loop through all quad-edges, except for the first 3 (#1, #2, #3 - 0 is reserved for "NULL" pointer)
|
||||
for( i = 4; i < total; i++ )
|
||||
{
|
||||
QuadEdge& quadedge = qedges[i];
|
||||
|
||||
if( quadedge.isfree() )
|
||||
continue;
|
||||
|
||||
int edge0 = (int)(i*4);
|
||||
Point2f org0, dst0, org1, dst1;
|
||||
|
||||
if( !quadedge.pt[3] )
|
||||
{
|
||||
int edge1 = getEdge( edge0, NEXT_AROUND_LEFT );
|
||||
int edge2 = getEdge( edge1, NEXT_AROUND_LEFT );
|
||||
|
||||
edgeOrg(edge0, &org0);
|
||||
edgeDst(edge0, &dst0);
|
||||
edgeOrg(edge1, &org1);
|
||||
edgeDst(edge1, &dst1);
|
||||
|
||||
Point2f virt_point = computeVoronoiPoint(org0, dst0, org1, dst1);
|
||||
|
||||
if( fabs( virt_point.x ) < FLT_MAX * 0.5 &&
|
||||
fabs( virt_point.y ) < FLT_MAX * 0.5 )
|
||||
{
|
||||
quadedge.pt[3] = qedges[edge1 >> 2].pt[3 - (edge1 & 2)] =
|
||||
qedges[edge2 >> 2].pt[3 - (edge2 & 2)] = newPoint(virt_point, true);
|
||||
}
|
||||
}
|
||||
|
||||
if( !quadedge.pt[1] )
|
||||
{
|
||||
int edge1 = getEdge( edge0, NEXT_AROUND_RIGHT );
|
||||
int edge2 = getEdge( edge1, NEXT_AROUND_RIGHT );
|
||||
|
||||
edgeOrg(edge0, &org0);
|
||||
edgeDst(edge0, &dst0);
|
||||
edgeOrg(edge1, &org1);
|
||||
edgeDst(edge1, &dst1);
|
||||
|
||||
Point2f virt_point = computeVoronoiPoint(org0, dst0, org1, dst1);
|
||||
|
||||
if( fabs( virt_point.x ) < FLT_MAX * 0.5 &&
|
||||
fabs( virt_point.y ) < FLT_MAX * 0.5 )
|
||||
{
|
||||
quadedge.pt[1] = qedges[edge1 >> 2].pt[1 + (edge1 & 2)] =
|
||||
qedges[edge2 >> 2].pt[1 + (edge2 & 2)] = newPoint(virt_point, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
validGeometry = true;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
isRightOf2( const Point2f& pt, const Point2f& org, const Point2f& diff )
|
||||
{
|
||||
double cw_area = ((double)org.x - pt.x)*diff.y - ((double)org.y - pt.y)*diff.x;
|
||||
return (cw_area > 0) - (cw_area < 0);
|
||||
}
|
||||
|
||||
|
||||
int Subdiv2D::findNearest(Point2f pt, Point2f* nearestPt)
|
||||
{
|
||||
CV_INSTRUMENT_REGION();
|
||||
|
||||
if( !validGeometry )
|
||||
calcVoronoi();
|
||||
|
||||
int vertex = 0, edge = 0;
|
||||
int loc = locate( pt, edge, vertex );
|
||||
|
||||
if( loc != PTLOC_ON_EDGE && loc != PTLOC_INSIDE )
|
||||
return vertex;
|
||||
|
||||
vertex = 0;
|
||||
|
||||
Point2f start;
|
||||
edgeOrg(edge, &start);
|
||||
Point2f diff = pt - start;
|
||||
|
||||
edge = rotateEdge(edge, 1);
|
||||
|
||||
int i, total = (int)vtx.size();
|
||||
|
||||
for( i = 0; i < total; i++ )
|
||||
{
|
||||
Point2f t;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
CV_Assert( edgeDst(edge, &t) > 0 );
|
||||
if( isRightOf2( t, start, diff ) >= 0 )
|
||||
break;
|
||||
|
||||
edge = getEdge( edge, NEXT_AROUND_LEFT );
|
||||
}
|
||||
|
||||
for(;;)
|
||||
{
|
||||
CV_Assert( edgeOrg( edge, &t ) > 0 );
|
||||
|
||||
if( isRightOf2( t, start, diff ) < 0 )
|
||||
break;
|
||||
|
||||
edge = getEdge( edge, PREV_AROUND_LEFT );
|
||||
}
|
||||
|
||||
Point2f tempDiff;
|
||||
edgeDst(edge, &tempDiff);
|
||||
edgeOrg(edge, &t);
|
||||
tempDiff -= t;
|
||||
|
||||
if( isRightOf2( pt, t, tempDiff ) >= 0 )
|
||||
{
|
||||
vertex = edgeOrg(rotateEdge( edge, 3 ));
|
||||
break;
|
||||
}
|
||||
|
||||
edge = symEdge( edge );
|
||||
}
|
||||
|
||||
if( nearestPt && vertex > 0 )
|
||||
*nearestPt = vtx[vertex].pt;
|
||||
|
||||
return vertex;
|
||||
}
|
||||
|
||||
void Subdiv2D::getEdgeList(std::vector<Vec4f>& edgeList) const
|
||||
{
|
||||
edgeList.clear();
|
||||
|
||||
for( size_t i = 4; i < qedges.size(); i++ )
|
||||
{
|
||||
if( qedges[i].isfree() )
|
||||
continue;
|
||||
if( qedges[i].pt[0] > 0 && qedges[i].pt[2] > 0 )
|
||||
{
|
||||
Point2f org = vtx[qedges[i].pt[0]].pt;
|
||||
Point2f dst = vtx[qedges[i].pt[2]].pt;
|
||||
edgeList.emplace_back(org.x, org.y, dst.x, dst.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Subdiv2D::getLeadingEdgeList(std::vector<int>& leadingEdgeList) const
|
||||
{
|
||||
leadingEdgeList.clear();
|
||||
int i, total = (int)(qedges.size()*4);
|
||||
std::vector<bool> edgemask(total, false);
|
||||
|
||||
for( i = 4; i < total; i += 2 )
|
||||
{
|
||||
if( edgemask[i] )
|
||||
continue;
|
||||
int edge = i;
|
||||
edgemask[edge] = true;
|
||||
edge = getEdge(edge, NEXT_AROUND_LEFT);
|
||||
edgemask[edge] = true;
|
||||
edge = getEdge(edge, NEXT_AROUND_LEFT);
|
||||
edgemask[edge] = true;
|
||||
leadingEdgeList.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Subdiv2D::getTriangleList(std::vector<Vec6f>& triangleList) const
|
||||
{
|
||||
triangleList.clear();
|
||||
int i, total = (int)(qedges.size()*4);
|
||||
std::vector<bool> edgemask(total, false);
|
||||
Rect2f rect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x, bottomRight.y - topLeft.y);
|
||||
|
||||
for( i = 4; i < total; i += 2 )
|
||||
{
|
||||
if( edgemask[i] )
|
||||
continue;
|
||||
Point2f a, b, c;
|
||||
int edge_a = i;
|
||||
edgeOrg(edge_a, &a);
|
||||
if ( !rect.contains(a) )
|
||||
continue;
|
||||
int edge_b = getEdge(edge_a, NEXT_AROUND_LEFT);
|
||||
edgeOrg(edge_b, &b);
|
||||
if ( !rect.contains(b) )
|
||||
continue;
|
||||
int edge_c = getEdge(edge_b, NEXT_AROUND_LEFT);
|
||||
edgeOrg(edge_c, &c);
|
||||
if ( !rect.contains(c) )
|
||||
continue;
|
||||
edgemask[edge_a] = true;
|
||||
edgemask[edge_b] = true;
|
||||
edgemask[edge_c] = true;
|
||||
triangleList.emplace_back(a.x, a.y, b.x, b.y, c.x, c.y);
|
||||
}
|
||||
}
|
||||
|
||||
void Subdiv2D::getVoronoiFacetList(const std::vector<int>& idx,
|
||||
CV_OUT std::vector<std::vector<Point2f> >& facetList,
|
||||
CV_OUT std::vector<Point2f>& facetCenters)
|
||||
{
|
||||
calcVoronoi();
|
||||
facetList.clear();
|
||||
facetCenters.clear();
|
||||
|
||||
std::vector<Point2f> buf;
|
||||
|
||||
size_t i, total;
|
||||
if( idx.empty() )
|
||||
i = 4, total = vtx.size();
|
||||
else
|
||||
i = 0, total = idx.size();
|
||||
|
||||
for( ; i < total; i++ )
|
||||
{
|
||||
int k = idx.empty() ? (int)i : idx[i];
|
||||
|
||||
if( vtx[k].isfree() || vtx[k].isvirtual() )
|
||||
continue;
|
||||
int edge = rotateEdge(vtx[k].firstEdge, 1), t = edge;
|
||||
|
||||
// gather points
|
||||
buf.clear();
|
||||
do
|
||||
{
|
||||
buf.push_back(vtx[edgeOrg(t)].pt);
|
||||
t = getEdge( t, NEXT_AROUND_LEFT );
|
||||
}
|
||||
while( t != edge );
|
||||
|
||||
facetList.push_back(buf);
|
||||
facetCenters.push_back(vtx[k].pt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Subdiv2D::checkSubdiv() const
|
||||
{
|
||||
int i, j, total = (int)qedges.size();
|
||||
|
||||
for( i = 0; i < total; i++ )
|
||||
{
|
||||
const QuadEdge& qe = qedges[i];
|
||||
|
||||
if( qe.isfree() )
|
||||
continue;
|
||||
|
||||
for( j = 0; j < 4; j++ )
|
||||
{
|
||||
int e = (int)(i*4 + j);
|
||||
int o_next = nextEdge(e);
|
||||
int o_prev = getEdge(e, PREV_AROUND_ORG );
|
||||
int d_prev = getEdge(e, PREV_AROUND_DST );
|
||||
int d_next = getEdge(e, NEXT_AROUND_DST );
|
||||
|
||||
// check points
|
||||
CV_Assert( edgeOrg(e) == edgeOrg(o_next));
|
||||
CV_Assert( edgeOrg(e) == edgeOrg(o_prev));
|
||||
CV_Assert( edgeDst(e) == edgeDst(d_next));
|
||||
CV_Assert( edgeDst(e) == edgeDst(d_prev));
|
||||
|
||||
if( j % 2 == 0 )
|
||||
{
|
||||
CV_Assert( edgeDst(o_next) == edgeOrg(d_prev));
|
||||
CV_Assert( edgeDst(o_prev) == edgeOrg(d_next));
|
||||
CV_Assert( getEdge(getEdge(getEdge(e,NEXT_AROUND_LEFT),NEXT_AROUND_LEFT),NEXT_AROUND_LEFT) == e );
|
||||
CV_Assert( getEdge(getEdge(getEdge(e,NEXT_AROUND_RIGHT),NEXT_AROUND_RIGHT),NEXT_AROUND_RIGHT) == e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* End of file. */
|
||||
Reference in New Issue
Block a user