Reputation: 18188
assume that I need to search inside a matrix over rows and cols to find min values, what is the best way to do this?
What I can think of it at the moment is to have two nested vector as follow:
std::vector<std::vector<float>> myData;
and search it in this way:
// row search on row say 10
int index= std::min_element(myData[10].begin(), myData[10].end()) - myData[10].begin();
but for searching on cols, I need to write a for loop to do the search.
// col search say on col 20
float min_value=10000000; / assuming values in table are less than this value
int min_index=-1;
for(int i=0;i<myData.size();++i)
{
if(myData[i][20] <min_value)
{
min_value=myData[i][20];
min_index=i;
}
}
Is there any better way to do this? I also have access to OpenCV.
Upvotes: 3
Views: 2062
Reputation: 16811
If you have an OpenCV Mat, you can use minMaxLoc:
void minMaxLoc(InputArray src, double* minVal, double* maxVal=0,
Point* minLoc=0, Point* maxLoc=0, InputArray mask=noArray())
The return values minVal/maxVal
contain the actual values while minLoc/maxLoc
are the coordinates of the min/max (the first occurrence if there are multiple).
Obviously, if you pass the entire matrix you'll get the global min/max, but you can also just pass a single row or column.
For a matrix C
you can find the min/max for column n
using Mat::col
minMaxLoc(C.col(n), &minVal, &maxVal, &minLoc, &maxLoc);
or for row m
using Mat::row
minMaxLoc(C.row(m), &minVal, &maxVal, &minLoc, &maxLoc);
Both Mat::col
and Mat::row
are O(1)
operations in that they don't copy any data, but I haven't done any benchmarks to determine how fast their column iteration is.
Upvotes: 2
Reputation: 11221
Instead of using minMaxLoc
with range/roi, we can use cv::reduce
(1) for row/column wise minimum/maximum. Sample below.
unsigned char data[4][2] = { 1,2,3,4,5,6,7,8 };
Mat img(4, 2, CV_8UC1, data) ;
Mat rowMinImg;
int singleCoumnResult = 1;
cv::reduce(img, rowMinImg, singleCoumnResult, CV_REDUCE_MIN );
//Mat colMinImg;
//int singleRowResult = 0;
//cv::reduce(img, colMinImg, singleRowResult, CV_REDUCE_MIN );
A quick look into implementation of cv::reduce(2), shows it is a simple for loop to find min/max values. So, if you have your data already in an OpenCV Mat
, I think this is the way to go.
Upvotes: 1
Reputation: 2534
If I understood you correctly, you want to be able to find minimum and maximum values (and locations) of a given matrix:
Then the following MWE may help you. Let's first look at the output:
mat =
[75, 97, 66, 95, 15, 22;
24, 21, 71, 72, 34, 66;
21, 69, 88, 72, 64, 1;
26, 47, 26, 40, 95, 24;
70, 37, 9, 83, 16, 83]
global min = 1 @ [5, 2]
global max = 97 @ [1, 0]
Row 0 min = 15 @ [4, 0] max = 97 @ [1, 0]
Row 1 min = 21 @ [1, 1] max = 72 @ [3, 1]
Row 2 min = 1 @ [5, 2] max = 88 @ [2, 2]
Row 3 min = 24 @ [5, 3] max = 95 @ [4, 3]
Row 4 min = 9 @ [2, 4] max = 83 @ [3, 4]
Col 0 min = 21 @ [0, 2] max = 75 @ [0, 0]
Col 1 min = 21 @ [1, 1] max = 97 @ [1, 0]
Col 2 min = 9 @ [2, 4] max = 88 @ [2, 2]
Col 3 min = 40 @ [3, 3] max = 95 @ [3, 0]
Col 4 min = 15 @ [4, 0] max = 95 @ [4, 3]
Col 5 min = 1 @ [5, 2] max = 83 @ [5, 4]
And the corresponding code (which you said is OK to use OpenCV):
#include <opencv2/core/core.hpp>
#include <algorithm>
#include <iostream>
#include <iomanip>
namespace {
template <typename T>
std::pair< cv::Point, cv::Point > findMinMaxLoc( cv::Mat_<T> & mat )
{
double ignored1, ignored2;
std::pair< cv::Point, cv::Point > mmloc;
cv::minMaxLoc( mat, &ignored1, &ignored2, &(mmloc.first), &(mmloc.second) );
return mmloc;
}
template <typename T>
std::pair< cv::Point, cv::Point > minMaxLocRow( cv::Mat_<T> & mat, int row )
{
cv::Rect roi( 0, row, mat.size().width, 1 );
cv::Mat_<T> matRow = mat( roi );
std::pair< cv::Point, cv::Point > mmloc = findMinMaxLoc( matRow );
mmloc.first.y = row;
mmloc.second.y = row;
return mmloc;
}
template <typename T>
std::pair< cv::Point, cv::Point > minMaxLocCol( cv::Mat_<T> & mat, int col )
{
cv::Rect roi( col, 0, 1, mat.size().height );
cv::Mat_<T> matCol = mat( roi );
std::pair< cv::Point, cv::Point > mmloc = findMinMaxLoc( matCol );
mmloc.first.x = col;
mmloc.second.x = col;
return mmloc;
}
} // namespace
int main( int argc, char ** argv )
{
// Generate a matrix filled with random data.
cv::Size size( 6, 5 );
cv::Mat1i mat( size ); // Or cv::Mat1b, cv::Mat3f, etc.
cv::RNG rng( cv::getCPUTickCount() );
rng.fill( mat, cv::RNG::UNIFORM, 0, 100 );
std::cout << "mat = " << std::endl << mat << std::endl << std::endl;
// Find the global minimum and maximum.
std::pair< cv::Point, cv::Point > mmloc = findMinMaxLoc( mat );
std::cout << "global min = " << std::setw( 3 ) << std::setfill( ' ' );
std::cout << mat( mmloc.first ) << " @ " << mmloc.first << std::endl;
std::cout << "global max = " << std::setw( 3 ) << std::setfill( ' ' );
std::cout << mat( mmloc.second ) << " @ " << mmloc.second << std::endl << std::endl;
// Row-wise extrema.
for ( int row = 0; row < size.height; ++row )
{
std::pair< cv::Point, cv::Point > mmloc = minMaxLocRow( mat, row );
std::cout << "Row " << row;
std::cout << " min = " << std::setw( 3 ) << std::setfill( ' ' );
std::cout << mat( mmloc.first ) << " @ " << mmloc.first;
std::cout << " max = " << std::setw( 3 ) << std::setfill( ' ' );
std::cout << mat( mmloc.second ) << " @ " << mmloc.second << std::endl;
}
std::cout << std::endl;
// Column-wise extrema.
for ( int col = 0; col < size.width; ++col )
{
std::pair< cv::Point, cv::Point > mmloc = minMaxLocCol( mat, col );
std::cout << "Col " << col;
std::cout << " min = " << std::setw( 3 ) << std::setfill( ' ' );
std::cout << mat( mmloc.first ) << " @ " << mmloc.first;
std::cout << " max = " << std::setw( 3 ) << std::setfill( ' ' );
std::cout << mat( mmloc.second ) << " @ " << mmloc.second << std::endl;
}
return 0;
}
Maybe my proposed solution isn't best way (which you explicitly asked for), but it's a simple one.
Upvotes: 0
Reputation: 101
While this may be more complicated than you would like, it could be possible to subclass the iterator base class. There is a simple example there. You would redefine the operators to traverse the column instead. That way, you could pass them into min_element just like the row iterators.
Upvotes: 0
Reputation: 70999
As the elements of your structure are going to be changing, the best option you have is to implement Range minimum query(RMQ) using binary index trees(a.k.a Fenwick trees). I suggest you keep one such tree for each row and for each column. You can also implement a tree of such trees if you want to support a query for a submatrix of the original matrix. The solution I propose will require O(N * M )
additional memory where N and M are the dimensions of the matrix. It will also support query and update with complexity O(log(N) + log(M))
.
Upvotes: 2