I have written an adapter class that allows iteration over the rows of a Mat
object from OpenCV. For those interested, here is the Mat documentation, but the salient points are as follows:
Mat
is a reference-counted header to a shared data buffer.Mat::row()
returns anotherMat
, which provides a view of a single row of the largerMat
.Mat_<T>
is a templated version ofMat
offering the same interface but better type safety.Mat_<T>
andMat
can be converted to one another.
This is my first attempt at writing custom iterators, so I am interested in feedback on correctness and general points to improve. It seems like my use of mutable
is suspect, but I don't know if I can avoid it.
Here are the class definitions:
RowRange.hpp
#ifndef CV_ADAPTERS_ROWRANGE_HPP
#define CV_ADAPTERS_ROWRANGE_HPP
#include <iterator>
#include <opencv2/core/core.hpp>
namespace cv
{
template <typename T>
class RowRangeConstIterator : public std::iterator<std::forward_iterator_tag, T>
{
public:
RowRangeConstIterator()
: data()
, row()
, position()
{}
RowRangeConstIterator(const cv::Mat_<T>& m, int index)
: data(m)
, row()
, position(index)
{
CV_DbgAssert(position >= 0 && position <= data.rows);
}
// Dereference
const cv::Mat_<T>& operator*() const
{
setRow();
return row;
}
const cv::Mat_<T>* operator->() const
{
setRow();
return &row;
}
// Logical comparison
bool operator==(const RowRangeConstIterator& that) const
{
return this->position == that.position;
}
bool operator!=(const RowRangeConstIterator& that) const
{
return !(*this == that);
}
bool operator<(const RowRangeConstIterator& that) const
{
return this->position < that.position;
}
bool operator>(const RowRangeConstIterator& that) const
{
return this->position > that.position;
}
bool operator<=(const RowRangeConstIterator& that) const
{
return !(*this > that);
}
bool operator>=(const RowRangeConstIterator& that) const
{
return !(*this < that);
}
// Increment
RowRangeConstIterator& operator++()
{
++position;
return *this;
}
RowRangeConstIterator operator++(int) const
{
RowRangeConstIterator tmp(*this);
++(*this);
return tmp;
}
protected:
void setRow() const
{
row = data.row(position);
}
cv::Mat_<T> data;
mutable cv::Mat_<T> row;
int position;
};
template <typename T>
class RowRangeIterator : public RowRangeConstIterator<T>
{
public:
RowRangeIterator()
: RowRangeConstIterator<T>()
{}
RowRangeIterator(const cv::Mat_<T>& m, int index)
: RowRangeConstIterator<T>(m, index)
{}
// Dereference
cv::Mat_<T>& operator*() const
{
this->setRow();
return this->row;
}
cv::Mat_<T>* operator->() const
{
this->setRow();
return &this->row;
}
};
template <typename T>
class RowRange
{
public:
typedef RowRangeConstIterator<T> const_iterator;
typedef RowRangeIterator<T> iterator;
RowRange(cv::Mat m)
: data(m)
{
CV_Assert(m.type() == cv::DataType<T>::type);
}
RowRange(cv::Mat_<T> m)
: data(m) {}
const_iterator begin() const
{
return const_iterator(data, 0);
}
iterator begin()
{
return iterator(data, 0);
}
const_iterator end() const
{
return const_iterator(data, data.rows);
}
iterator end()
{
return iterator(data, data.rows);
}
const_iterator cbegin() const
{
return begin();
}
const_iterator cend() const
{
return end();
}
private:
cv::Mat_<T> data;
};
template <typename T>
RowRange<T> make_RowRange(cv::Mat_<T> m)
{
return RowRange<T>(m);
}
} // namespace cv
#endif /* CV_ADAPTERS_ROWRANGE_HPP */
And an example usage (More examples can be found here):
main.cpp
#include <opencv2/opencv.hpp>
#include "RowRange.hpp"
int main()
{
cv::Mat m2 = cv::Mat::zeros(3, 3, CV_8UC1);
for (auto row : cv::RowRange<uchar>(m2))
{
row(1) = 255;
}
}
that
and gothis
vsthat
. \$\endgroup\$data
is not the best name for acv::Mat_
member, since it is the same as the name of the data pointer withincv::Mat
; I was confused first. \$\endgroup\$