Tips:
1.某个点的积分图反应的是原图中此位置左上角所有像素之和,这里是的累加和是不包括这个点像素本身的,那么,对于原图的第一行和第一列的所有像素,其对应位置的积分图就应该是0, 这样考虑到所有的像素,为了能容纳最后一列和最后一行的情况,最终的积分图就应该是 (W + 1) X (H + 1)大小。
2.优化方式:
原始算法 列优化 行优化
分析:
行优化最优,相比于列优化,更符合图像数据在计算机中的存储顺序,因此并且在计算过程中无需申请额外空间保存各列像素之和,只需一个变量保存行像素之和便可!详见开源软件scantailor里的push函数。
3. 在反汇编中,max和min充分利用了条件按传送指令比如cmovg,cmovl,而一般的if语句很大程度上会调用jmp指令的(当然和常数比较一般编译也会用条件传送替代的,比如和0比较),jmp指令是比较耗时的。
开源软件scantailor里的积分图算法的实现:
(调用的时候先调用beginRow定位行指针,然后循环调用push推入该行的像素值,所有行循环完毕就计算完成,然后可以调用sum计算区域累加和。)
/**
* \brief Provides the sum of values in a sub-rectangle of a 2D array in constant time.
*
* Suppose you have a MxN array of some values. Now if we are not going to
* alter it, but are going to calculate the sum of values in various
* rectangular sub-regions, it's best to use an integral image for this purpose.
* We construct it once, with complexity of O(M*N), and then obtain the sum
* of values in a rectangular sub-region in O(1).
*
* \note Template parameter T must be large enough to hold the sum of all
* values in the array.
*/
template<typename T>
class IntegralImage
{
DECLARE_NON_COPYABLE(IntegralImage)
public:
IntegralImage(int width, int height);
explicit IntegralImage(QSize const& size);
~IntegralImage();
/**
* \brief To be called before pushing new row data.
*/
void beginRow();
/**
* \brief Push a single value to the integral image.
*
* Values must be pushed row by row, starting from row 0, and from
* column to column within each row, starting from column 0.
* At the beginning of a row, a call to beginRow() must be made.
*
* \note Pushing more than width * height values results in undefined
* behavior.
*/
void push(T val);
/**
* \brief Calculate the sum of values in the given rectangle.
*
* \note If the rectangle exceeds the image area, the behaviour is
* undefined.
*/
T sum(QRect const& rect) const;
private:
void init(int width, int height);
T* m_pData;
T* m_pCur;
T* m_pAbove;
T m_lineSum;
int m_width;
int m_height;
};
template<typename T>
IntegralImage<T>::IntegralImage(int const width, int const height)
: m_lineSum() // init with 0 or with default constructor.
{
// The first row and column are fake.
init(width + 1, height + 1);
}
template<typename T>
IntegralImage<T>::IntegralImage(QSize const& size)
: m_lineSum() // init with 0 or with default constructor.
{
// The first row and column are fake.
init(size.width() + 1, size.height() + 1);
}
template<typename T>
IntegralImage<T>::~IntegralImage()
{
delete[] m_pData;
}
template<typename T>
void
IntegralImage<T>::init(int const width, int const height)
{
m_width = width;
m_height = height;
m_pData = new T[width * height];
// Initialize the first (fake) row.
// As for the fake column, we initialize its elements in beginRow().
T* p = m_pData;
for (int i = 0; i < width; ++i, ++p) {
*p = T();
}
m_pAbove = m_pData;
m_pCur = m_pAbove + width; // Skip the first row.
}
template<typename T>
void
IntegralImage<T>::push(T const val)
{
m_lineSum += val;
*m_pCur = *m_pAbove + m_lineSum;
++m_pCur;
++m_pAbove;
}
template<typename T>
void
IntegralImage<T>::beginRow()
{
m_lineSum = T();
// Initialize and skip the fake column.
*m_pCur = T();
++m_pCur;
++m_pAbove;
}
template<typename T>
inline T
IntegralImage<T>::sum(QRect const& rect) const
{
// Keep in mind that row 0 and column 0 are fake.
int const pre_left = rect.left();
int const pre_right = rect.right() + 1; // QRect::right() is inclusive.
int const pre_top = rect.top();
int const pre_bottom = rect.bottom() + 1; // QRect::bottom() is inclusive.
T sum(m_pData[pre_bottom * m_width + pre_right]);
sum -= m_pData[pre_top * m_width + pre_right];
sum += m_pData[pre_top * m_width + pre_left];
sum -= m_pData[pre_bottom * m_width + pre_left];
return sum;
}