Merge pull request #13869 from chacha21:LineVirtualIterator

* LineVirtualIterator

Proposal of LineVirtualIterator, an alternative to "LineIterator not attached to any mat".
This is basically the same implementation, replacing the address difference by a single "offset" variable. elemsize becomes irrelevant and considered to be 1. "step" is thus equal to size.width since no stride is expected.

* Update drawing.cpp

fixed warning

* improvement of LineVirtualIterator

instead of being too conservative, the new implementation gets rid of "offset/step" and only keeps a "Point currentPos" up to date.

left_to_right is renamed to forceLeftToRight as suggested (even for the old LineIterator)

assert() replaced by CV_Assert() (even for the old LineIterator)

* fixed implementation

+fixed last commit so that LineVirtualIterator gives at least the same results as LineIterator

+added a new constructor that does not require any Size, so that no clipping is done and iteration occurs from pt1 to pt2. This is done by adding a spatial offset to pt1 and pt2 so that the same implementation is used, the size being in that case the spatial size between pt1 and pt2

* Update imgproc.hpp

fixed warnings

* Update drawing.cpp

fixed whitespace

* Update drawing.cpp

trailing whitespace

* Update imgproc.hpp

+added a new constructor that takes a Rect rather than a Size. It computes the line pt1->pt2 that clips that rect.
Yet again, this is still based on the same implementation, thanks to the Size and the currentPosOffset that can artifically consider the origin of the rect at (0,0)

* revert changes

revert changes on  original LineIterator implementation, that will be superseded by the new LineVirtualIterator anyway

* added test of LineVirtualIterator

* More tests

* refactoring

Use C++11 chained constructors
Improved code style

* improve test

Added offset as random test data.

* fixed order of initialization

* merged LineIterator and VirtualLineIterator

* merged LineIterator & VirtualLineIterator

* merged LineIterator & VirtualLineIterator

* merged LineIterator & VirtualLineIterator

* made LineIterator::operator ++() more efficient

added one perfectly predictable check; in theory, since ptmode is set in the end of the constructor in the header file, the compiler can figure out that it's always true/false and eliminate the check from the inline `LineIterator::operator++()` completely

* optimized Line() function

in the most common case (CV_8UC3) eliminated the check from the loop

Co-authored-by: Vadim Pisarevsky <vadim.pisarevsky@gmail.com>
This commit is contained in:
Pierre Chatelier 2020-04-13 08:59:31 +02:00 committed by GitHub
parent 0556450801
commit f351653589
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 180 additions and 70 deletions

View File

@ -4725,10 +4725,39 @@ public:
the line is 8-connected or 4-connected
If leftToRight=true, then the iteration is always done
from the left-most point to the right most,
not to depend on the ordering of pt1 and pt2 parameters
not to depend on the ordering of pt1 and pt2 parameters;
*/
LineIterator( const Mat& img, Point pt1, Point pt2,
int connectivity = 8, bool leftToRight = false );
int connectivity = 8, bool leftToRight = false )
{
init(&img, Rect(0, 0, img.cols, img.rows), pt1, pt2, connectivity, leftToRight);
ptmode = false;
}
LineIterator( Point pt1, Point pt2,
int connectivity = 8, bool leftToRight = false )
{
init(0, Rect(std::min(pt1.x, pt2.x),
std::min(pt1.y, pt2.y),
std::max(pt1.x, pt2.x) - std::min(pt1.x, pt2.x) + 1,
std::max(pt1.y, pt2.y) - std::min(pt1.y, pt2.y) + 1),
pt1, pt2, connectivity, leftToRight);
ptmode = true;
}
LineIterator( Size boundingAreaSize, Point pt1, Point pt2,
int connectivity = 8, bool leftToRight = false )
{
init(0, Rect(0, 0, boundingAreaSize.width, boundingAreaSize.height),
pt1, pt2, connectivity, leftToRight);
ptmode = true;
}
LineIterator( Rect boundingAreaRect, Point pt1, Point pt2,
int connectivity = 8, bool leftToRight = false )
{
init(0, boundingAreaRect, pt1, pt2, connectivity, leftToRight);
ptmode = true;
}
void init(const Mat* img, Rect boundingAreaRect, Point pt1, Point pt2, int connectivity, bool leftToRight);
/** @brief returns pointer to the current pixel
*/
uchar* operator *();
@ -4748,6 +4777,9 @@ public:
int err, count;
int minusDelta, plusDelta;
int minusStep, plusStep;
int minusShift, plusShift;
Point p;
bool ptmode;
};
//! @cond IGNORED
@ -4757,7 +4789,7 @@ public:
inline
uchar* LineIterator::operator *()
{
return ptr;
return ptmode ? 0 : ptr;
}
inline
@ -4765,7 +4797,15 @@ LineIterator& LineIterator::operator ++()
{
int mask = err < 0 ? -1 : 0;
err += minusDelta + (plusDelta & mask);
ptr += minusStep + (plusStep & mask);
if(!ptmode)
{
ptr += minusStep + (plusStep & mask);
}
else
{
p.x += minusShift + (plusShift & mask);
p.y += minusStep + (plusStep & mask);
}
return *this;
}
@ -4780,9 +4820,13 @@ LineIterator LineIterator::operator ++(int)
inline
Point LineIterator::pos() const
{
Point p;
p.y = (int)((ptr - ptr0)/step);
p.x = (int)(((ptr - ptr0) - p.y*step)/elemSize);
if(!ptmode)
{
size_t offset = (size_t)(ptr - ptr0);
int y = (int)(offset/step);
int x = (int)((offset - (size_t)y*step)/elemSize);
return Point(x, y);
}
return p;
}

View File

@ -156,96 +156,107 @@ bool clipLine( Rect img_rect, Point& pt1, Point& pt2 )
return inside;
}
/*
Initializes line iterator.
Returns number of points on the line or negative number if error.
*/
LineIterator::LineIterator(const Mat& img, Point pt1, Point pt2,
int connectivity, bool left_to_right)
void LineIterator::init( const Mat* img, Rect rect, Point pt1_, Point pt2_, int connectivity, bool leftToRight )
{
count = -1;
CV_Assert( connectivity == 8 || connectivity == 4 );
if( (unsigned)pt1.x >= (unsigned)(img.cols) ||
(unsigned)pt2.x >= (unsigned)(img.cols) ||
(unsigned)pt1.y >= (unsigned)(img.rows) ||
(unsigned)pt2.y >= (unsigned)(img.rows) )
count = -1;
p = Point(0, 0);
ptr0 = ptr = 0;
step = elemSize = 0;
ptmode = !img;
Point pt1 = pt1_ - rect.tl();
Point pt2 = pt2_ - rect.tl();
if( (unsigned)pt1.x >= (unsigned)(rect.width) ||
(unsigned)pt2.x >= (unsigned)(rect.width) ||
(unsigned)pt1.y >= (unsigned)(rect.height) ||
(unsigned)pt2.y >= (unsigned)(rect.height) )
{
if( !clipLine( img.size(), pt1, pt2 ) )
if( !clipLine(Size(rect.width, rect.height), pt1, pt2) )
{
ptr = img.data;
err = plusDelta = minusDelta = plusStep = minusStep = count = 0;
ptr0 = 0;
step = 0;
elemSize = 0;
err = plusDelta = minusDelta = plusStep = minusStep = plusShift = minusShift = count = 0;
return;
}
}
size_t bt_pix0 = img.elemSize(), bt_pix = bt_pix0;
size_t istep = img.step;
pt1 += rect.tl();
pt2 += rect.tl();
int delta_x = 1, delta_y = 1;
int dx = pt2.x - pt1.x;
int dy = pt2.y - pt1.y;
int s = dx < 0 ? -1 : 0;
if( left_to_right )
if( dx < 0 )
{
dx = (dx ^ s) - s;
dy = (dy ^ s) - s;
pt1.x ^= (pt1.x ^ pt2.x) & s;
pt1.y ^= (pt1.y ^ pt2.y) & s;
}
else
{
dx = (dx ^ s) - s;
bt_pix = (bt_pix ^ s) - s;
if( leftToRight )
{
dx = -dx;
dy = -dy;
pt1 = pt2;
}
else
{
dx = -dx;
delta_x = -1;
}
}
ptr = (uchar*)(img.data + pt1.y * istep + pt1.x * bt_pix0);
if( dy < 0 )
{
dy = -dy;
delta_y = -1;
}
s = dy < 0 ? -1 : 0;
dy = (dy ^ s) - s;
istep = (istep ^ s) - s;
bool vert = dy > dx;
if( vert )
{
std::swap(dx, dy);
std::swap(delta_x, delta_y);
}
s = dy > dx ? -1 : 0;
/* conditional swaps */
dx ^= dy & s;
dy ^= dx & s;
dx ^= dy & s;
bt_pix ^= istep & s;
istep ^= bt_pix & s;
bt_pix ^= istep & s;
CV_Assert( dx >= 0 && dy >= 0 );
if( connectivity == 8 )
{
assert( dx >= 0 && dy >= 0 );
err = dx - (dy + dy);
plusDelta = dx + dx;
minusDelta = -(dy + dy);
plusStep = (int)istep;
minusStep = (int)bt_pix;
minusShift = delta_x;
plusShift = 0;
minusStep = 0;
plusStep = delta_y;
count = dx + 1;
}
else /* connectivity == 4 */
{
assert( dx >= 0 && dy >= 0 );
err = 0;
plusDelta = (dx + dx) + (dy + dy);
minusDelta = -(dy + dy);
plusStep = (int)(istep - bt_pix);
minusStep = (int)bt_pix;
minusShift = delta_x;
plusShift = -delta_x;
minusStep = 0;
plusStep = delta_y;
count = dx + dy + 1;
}
this->ptr0 = img.ptr();
this->step = (int)img.step;
this->elemSize = (int)bt_pix0;
if( vert )
{
std::swap(plusStep, plusShift);
std::swap(minusStep, minusShift);
}
p = pt1;
if( !ptmode )
{
ptr0 = img->ptr();
step = (int)img->step;
elemSize = (int)img->elemSize();
ptr = (uchar*)ptr0 + (size_t)p.y*step + (size_t)p.x*elemSize;
plusStep = plusStep*step + plusShift*elemSize;
minusStep = minusStep*step + minusShift*elemSize;
}
}
static void
@ -262,19 +273,26 @@ Line( Mat& img, Point pt1, Point pt2,
int pix_size = (int)img.elemSize();
const uchar* color = (const uchar*)_color;
for( i = 0; i < count; i++, ++iterator )
if( pix_size == 3 )
{
uchar* ptr = *iterator;
if( pix_size == 1 )
ptr[0] = color[0];
else if( pix_size == 3 )
for( i = 0; i < count; i++, ++iterator )
{
uchar* ptr = *iterator;
ptr[0] = color[0];
ptr[1] = color[1];
ptr[2] = color[2];
}
else
memcpy( *iterator, color, pix_size );
}
else
{
for( i = 0; i < count; i++, ++iterator )
{
uchar* ptr = *iterator;
if( pix_size == 1 )
ptr[0] = color[0];
else
memcpy( *iterator, color, pix_size );
}
}
}

View File

@ -54,6 +54,7 @@ protected:
void run( int );
virtual void draw( Mat& img ) = 0;
virtual int checkLineIterator( Mat& img) = 0;
virtual int checkLineVirtualIterator() = 0;
};
void CV_DrawingTest::run( int )
@ -93,6 +94,7 @@ void CV_DrawingTest::run( int )
ts->set_failed_test_info(checkLineIterator( testImg ));
}
}
ts->set_failed_test_info(checkLineVirtualIterator());
ts->set_failed_test_info(cvtest::TS::OK);
}
@ -103,6 +105,7 @@ public:
protected:
virtual void draw( Mat& img );
virtual int checkLineIterator( Mat& img);
virtual int checkLineVirtualIterator();
};
void CV_DrawingTest_CPP::draw( Mat& img )
@ -245,6 +248,51 @@ int CV_DrawingTest_CPP::checkLineIterator( Mat& img )
return 0;
}
int CV_DrawingTest_CPP::checkLineVirtualIterator( )
{
RNG randomGenerator(1);
for (size_t test = 0; test < 10000; ++test)
{
int width = randomGenerator.uniform(0, 512+1);
int height = randomGenerator.uniform(0, 512+1);
int x1 = randomGenerator.uniform(-512, 1024+1);
int y1 = randomGenerator.uniform(-512, 1024+1);
int x2 = randomGenerator.uniform(-512, 1024+1);
int y2 = randomGenerator.uniform(-512, 1024+1);
int x3 = randomGenerator.uniform(-512, 1024+1);
int y3 = randomGenerator.uniform(-512, 1024+1);
int channels = randomGenerator.uniform(1, 3+1);
Mat m(cv::Size(width, height), CV_MAKETYPE(8U, channels));
Point p1(x1, y1);
Point p2(x2, y2);
Point offset(x3, y3);
LineIterator it( m, p1, p2 );
LineIterator vit(Rect(offset.x, offset.y, width, height), p1 + offset, p2 + offset);
if (it.count != vit.count)
{
ts->printf( ts->LOG, "virtual LineIterator works incorrectly" );
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
break;
}
else
{
for(int i = 0; i < it.count; ++it, ++vit, i++ )
{
Point pIt = it.pos();
Point pVit = vit.pos() - offset;
if (pIt != pVit)
{
ts->printf( ts->LOG, "virtual LineIterator works incorrectly" );
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
break;
}
}
}
}
ts->set_failed_test_info(cvtest::TS::OK);
return 0;
}
class CV_DrawingTest_Far : public CV_DrawingTest_CPP
{
public: