【发布时间】:2011-08-04 14:08:16
【问题描述】:
我正在尝试优化超大图像的旋转,最小的是 4096x4096 或约 1600 万像素。
旋转总是围绕图像的中心,图像不一定总是正方形,但总是 2 的幂。
我可以访问 MKL/TBB,其中 MKL 是针对我的目标平台优化的 BLAS。我不熟悉这个操作是否在 BLAS 中。
到目前为止,对于 4096x4096 图像,我的最佳尝试大约是 17 到 25 毫秒(对于相同的图像大小非常不一致,这意味着我可能会在整个缓存中踩踏)。矩阵是 16 字节对齐的。
现在,无法调整目标大小。因此,剪裁应该并且可以发生。例如,一个旋转 45 度的方阵肯定会在角落处裁剪,并且该位置的值应该为零。
目前,我最好的尝试是使用平铺方法 - 还没有优雅地融入平铺尺寸,也没有循环展开。
这是我使用 TBB 的算法 - http://threadingbuildingblocks.org/:
//- cosa = cos of the angle
//- sina = sin of angle
//- for those unfamiliar with TBB, this is giving me blocks of rows or cols that
//- are unique per thread
void operator() ( const tbb::blocked_range2d<size_t, size_t> r ) const
{
double xOffset;
double yOffset;
int lineOffset;
int srcX;
int srcY;
for ( size_t row = r.rows().begin(); row != r.rows().end(); ++row )
{
const size_t colBegin = r.cols().begin();
xOffset = -(row * sina) + xHelper + (cosa * colBegin);
yOffset = (row * cosa) + yHelper + (sina * colBegin);
lineOffset = ( row * rowSpan ); //- all col values are offsets of this row
for( size_t col = colBegin; col != r.cols().end(); ++col, xOffset += cosa, yOffset += sina )
{
srcX = xOffset;
srcY = yOffset;
if( srcX >= 0 && srcX < colSpan && srcY >= 0 && srcY < rowSpan )
{
destData[col + lineOffset] = srcData[srcX + ( srcY * rowSpan )];
}
}
}
}
我这样调用这个函数:
double sina = sin(angle);
double cosa = cos(angle);
double centerX = (colSpan) / 2;
double centerY = (rowSpan) / 2;
//- Adding .5 for rounding
const double xHelper = centerX - (centerX * cosa) + (centerY * sina) + .5;
const double yHelper = centerY - (centerX * sina) - (centerY * cosa) + .5;
tbb::parallel_for( tbb::blocked_range2d<size_t, size_t>( 0, rowSpan, 0, colSpan ), DoRotate( sina, cosa, xHelper, yHelper, rowSpan, colSpan, (fcomplex *)pDestData, (fcomplex *)pSrcData ) );
fcomplex 只是复数的内部表示。定义为:
struct fcomplex
{
float real;
float imag;
};
所以,对于非常大的图像,我想尽可能快地以任意角度围绕其中心旋转复数值矩阵。
更新:
根据出色的反馈,我已更新为:大约增加了 40%。我想知道是否可以做其他事情:
void operator() ( const tbb::blocked_range2d<size_t, size_t> r ) const
{
float xOffset;
float yOffset;
int lineOffset;
__m128i srcXints;
__m128i srcYints;
__m128 dupXOffset;
__m128 dupYOffset;
for ( size_t row = r.rows().begin(); row != r.rows().end(); ++row )
{
const size_t colBegin = r.cols().begin();
xOffset = -(row * sina) + xHelper + (cosa * colBegin);
yOffset = (row * cosa) + yHelper + (sina * colBegin);
lineOffset = ( row * rowSpan ); //- all col values are offsets of this row
for( size_t col = colBegin; col != r.cols().end(); col+=4, xOffset += dupOffsetsX.m128_f32[3], yOffset += dupOffsetsY.m128_f32[3] )
{
dupXOffset = _mm_load1_ps(&xOffset); //- duplicate the x offset 4 times into a 4 float field
dupYOffset = _mm_load1_ps(&yOffset); //- duplicate the y offset 4 times into a 4 float field
srcXints = _mm_cvtps_epi32( _mm_add_ps( dupOffsetsX, dupXOffset ) );
srcYints = _mm_cvtps_epi32( _mm_add_ps( dupOffsetsY, dupYOffset ) );
if( srcXints.m128i_i32[0] >= 0 && srcXints.m128i_i32[0] < colSpan && srcYints.m128i_i32[0] >= 0 && srcYints.m128i_i32[0] < rowSpan )
{
destData[col + lineOffset] = srcData[srcXints.m128i_i32[0] + ( srcYints.m128i_i32[0] * rowSpan )];
}
if( srcXints.m128i_i32[1] >= 0 && srcXints.m128i_i32[1] < colSpan && srcYints.m128i_i32[1] >= 0 && srcYints.m128i_i32[1] < rowSpan )
{
destData[col + 1 + lineOffset] = srcData[srcXints.m128i_i32[1] + ( srcYints.m128i_i32[1] * rowSpan )];
}
if( srcXints.m128i_i32[2] >= 0 && srcXints.m128i_i32[2] < colSpan && srcYints.m128i_i32[2] >= 0 && srcYints.m128i_i32[2] < rowSpan )
{
destData[col + 2 + lineOffset] = srcData[srcXints.m128i_i32[2] + ( srcYints.m128i_i32[2] * rowSpan )];
}
if( srcXints.m128i_i32[3] >= 0 && srcXints.m128i_i32[3] < colSpan && srcYints.m128i_i32[3] >= 0 && srcYints.m128i_i32[3] < rowSpan )
{
destData[col + 3 + lineOffset] = srcData[srcXints.m128i_i32[3] + ( srcYints.m128i_i32[3] * rowSpan )];
}
}
}
}
更新 2: 我在下面提出了一个解决方案,考虑了我作为答案得到的建议以及在旋转矩形时修复了一个错误。
【问题讨论】:
-
我edited out the clutter 第二次了。
-
这为 GPU 计算而尖叫。也许这是你的一个选择。
-
我记得以前使用Bresenham's algorithm 来避免在这种情况下进行浮点运算。这可能是一个想法,因为您的 xOffset 和 yOffset 修改似乎是 Bresenham 的不错选择。
-
@Axel Gneiting 不幸的是,我还不能把它带到 OpenCL 或 CUDA 世界。我们不保证客户的硬件。但是,我已经获得了下一次迭代的许可,可以为具有兼容硬件的客户编写 gpu 版本。到那时!
标签: c++ optimization parallel-processing blas intel-mkl