Skip to content

Instantly share code, notes, and snippets.

@enpe
Last active October 9, 2020 11:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save enpe/369a7a17fd9b3856b544 to your computer and use it in GitHub Desktop.
Save enpe/369a7a17fd9b3856b544 to your computer and use it in GitHub Desktop.
Deleting rows (or columns) from cv::Mat.
// For more details see: http://stackoverflow.com/questions/29696805/what-is-the-best-way-to-remove-a-row-or-col-from-a-cv-mat
#include "Timer.h" // http://www.songho.ca/misc/timer/timer.html
#include <opencv2/core/core.hpp>
#include <algorithm>
#include <numeric>
#include <iostream>
#include <cstring>
// Toggle the line below to test both versions.
#define US_CV_RECT
#ifdef US_CV_RECT
#define METHOD "cv::Rect + cv::Mat::copyTo()"
#else
#define METHOD "std::memcpy and/or for-loop"
#endif // US_CV_RECT
namespace {
template <typename T>
double removeRow( cv::Mat_<T> & matIn, int row )
{
cv::Size size = matIn.size();
cv::Mat_<T> matOut( size.height - 1, size.width );
Timer timer;
timer.start();
#ifdef USE_CV_RECT
if ( row > 0 )
{
cv::Rect rect( 0, 0, size.width, row );
matIn( rect ).copyTo( matOut( rect ) );
}
if ( row < size.height - 1 )
{
cv::Rect rect1( 0, row + 1, size.width, size.height - row - 1 );
cv::Rect rect2( 0, row, size.width, size.height - row - 1 );
matIn( rect1 ).copyTo( matOut( rect2 ) );
}
#else
int rowSizeInBytes = size.width * sizeof( T );
if ( row > 0 )
{
int numRows = row;
int numBytes = rowSizeInBytes * numRows;
std::memcpy( matOut.data, matIn.data, numBytes );
}
if ( row < size.height - 1 )
{
int matOutOffset = rowSizeInBytes * row;
int matInOffset = matOutOffset + rowSizeInBytes;
int numRows = size.height - ( row + 1 );
int numBytes = rowSizeInBytes * numRows;
std::memcpy( matOut.data + matOutOffset , matIn.data + matInOffset, numBytes );
}
#endif
matIn = matOut;
return timer.getElapsedTimeInMicroSec();;
}
template <typename T>
double removeCol( cv::Mat_<T> & matIn, int col )
{
cv::Size size = matIn.size();
cv::Mat_<T> matOut( size.height, size.width - 1 );
Timer timer;
timer.start();
#ifdef USE_CV_RECT
if ( col > 0 )
{
cv::Rect rect( 0, 0, col, size.height );
matIn( rect ).copyTo( matOut( rect ) );
}
if ( col < size.width - 1 )
{
cv::Rect rect1( col + 1, 0, size.width - col - 1, size.height );
cv::Rect rect2( col, 0, size.width - col - 1, size.height );
matIn( rect1 ).copyTo( matOut( rect2 ) );
}
#else
int rowInInBytes = size.width * sizeof( T );
int rowOutInBytes = ( size.width - 1 ) * sizeof( T );
if ( col > 0 )
{
int matInOffset = 0;
int matOutOffset = 0;
int numCols = col;
int numBytes = numCols * sizeof( T );
for ( int y = 0; y < size.height; ++y )
{
std::memcpy( matOut.data + matOutOffset, matIn.data + matInOffset, numBytes );
matInOffset += rowInInBytes;
matOutOffset += rowOutInBytes;
}
}
if ( col < size.width - 1 )
{
int matInOffset = ( col + 1 ) * sizeof( T );
int matOutOffset = col * sizeof( T );
int numCols = size.width - ( col + 1 );
int numBytes = numCols * sizeof( T );
for ( int y = 0; y < size.height; ++y )
{
std::memcpy( matOut.data + matOutOffset, matIn.data + matInOffset, numBytes );
matInOffset += rowInInBytes;
matOutOffset += rowOutInBytes;
}
}
#endif
matIn = matOut;
return timer.getElapsedTimeInMicroSec();;
}
template < typename T >
void test(
double (*remove)( cv::Mat_<T> &, int ),
std::string removeDesc,
cv::Size size = cv::Size( 5, 5 ),
int rowcol = 3 )
{
cv::RNG rng( 0 /*cv::getCPUTickCount()*/ );
cv::Mat_< T > mat( size );
rng.fill( mat, cv::RNG::UNIFORM, 0, 10 );
std::cout << "mat = " << std::endl << mat << std::endl << std::endl;
std::cout << "Removing " << removeDesc << ": " << rowcol << std::endl;
double elapsedTime = remove( mat, rowcol );
std::cout << "mat = " << std::endl << mat << std::endl << std::endl;
}
template < typename T >
void measureTime(
double (*remove)( cv::Mat_<T> &, int ),
std::string removeDesc,
cv::Size size = cv::Size( 500, 500 ),
int count = 10000 )
{
cv::RNG rng( 0 );
std::vector< double > times( count, 0. );
for ( int i = 0; i < count; ++i )
{
cv::Mat_< T > mat = cv::Mat_< T >::eye( size );
int row = rng.uniform( 0, std::min( size.width, size.height ) - 1 );
times[ i ] = remove( mat, row );
}
if ( times.size() > 0 )
{
double numTimes = static_cast< double >( times.size() );
double min = *( std::min_element( times.begin(), times.end() ) );
double max = *( std::max_element( times.begin(), times.end() ) );
double avg = std::accumulate( times.begin(), times.end(), 0. ) / numTimes;
std::sort( times.begin(), times.end() );
double med = times[ times.size() / 2 ];
std::cout << "Removed: " << removeDesc << std::endl;
std::cout << "Method: " << METHOD << std::endl;
std::cout << "Iterations: " << count << std::endl;
std::cout << "Size: " << size << std::endl;
std::cout << "Best time: " << min << "ms" << std::endl;
std::cout << "Worst time: " << max << "ms" << std::endl;
std::cout << "Average time: " << avg << "ms" << std::endl;
std::cout << "Median time: " << med << "ms" << std::endl;
}
}
} // namespace
int main( int argc, char ** argv )
{
std::cout << "--- Removing rows -----------------------------------------------" << std::endl;
test< int >( removeRow, "row" );
measureTime< int >( removeRow, "row" );
std::cout << std::endl;
std::cout << "--- Removing columns --------------------------------------------" << std::endl;
test< int >( removeCol, "column" );
measureTime< int >( removeCol, "column" );
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment