图像金字塔及尺寸缩放
对图像尺度变换方法:resize函数可以直接。pyrUp()(拉普拉斯) ,pyrUDown()函数(高斯)(图像金字塔)。
图像金字塔:主要用于图像分割,是一种以多分辨率来解释图像的有效但概念简单的结构。
是一系列以金字塔形状排列的,分辨率逐渐降低接来源于同一张原始图的图像集合。通过梯次向下采样获得,直到达到某个终止条件才停止采样。
金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。
金字塔有两种:
1、高斯金字塔—————-用来向下采样(主要)。
2、拉普拉斯金字塔———————用来从金字塔低层图像重建上层未采样图像即最大程度的还原图像,配合高斯金字塔使用。
高斯金字塔原理:从金字塔i层生成i+1层(Gi+1),先用高斯核对Gi进行卷积,然后删除所有偶数行和偶数列,所得图像面积会变成原图的1/4。
当图像金字塔向上移动时,尺寸和分辨率会降低。向下采样,向上就是图像尺寸加倍,向下就是图像尺寸减半。
高斯金字塔:
它是通过高斯平滑核亚采样获得一系列采样图像即第K层高斯金字塔通过平滑、亚采样可获得K+1层高斯图像。高斯金字塔包含了一系列低通滤波器,其截止频率从上一层到下一层一因子2逐渐增加,so高斯金字塔可以跨越很大的频率范围。
1、向下取样(缩小图像)
对图像Gi进行高斯核卷积且去除偶数行和列。
2、向上取样(放大图像)
将图像在每个方向扩大为原来的两倍,新增的行和列以0补充。使用先前同样的内核(乘以4)与放大后的图像卷积,获得“新增像素”的近似值。得到的图像极为放大的图像,但与原来的图像相比会发现比较模糊(丢失部分信息)采用拉普拉斯金字塔。
注意:上面所说的向上(拉普拉斯图像重建—加倍,倒金字塔)向下(高斯图像缩减—减半,正金字塔,)是针对图像尺寸的(和金字塔的方向相反)。
void cv::pyrUp( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType )
void cv::pyrUp( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType )
{
CV_Assert(borderType == BORDER_DEFAULT);
CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(),
ocl_pyrUp(_src, _dst, _dsz, borderType))
Mat src = _src.getMat();
Size dsz = _dsz.area() == 0 ? Size(src.cols*2, src.rows*2) : _dsz;
_dst.create( dsz, src.type() );
Mat dst = _dst.getMat();
int depth = src.depth();
#ifdef HAVE_TEGRA_OPTIMIZATION
if(borderType == BORDER_DEFAULT && tegra::useTegra() && tegra::pyrUp(src, dst))
return;
#endif
#if IPP_VERSION_X100 >= 801 && 0
CV_IPP_CHECK()
{
bool isolated = (borderType & BORDER_ISOLATED) != 0;
int borderTypeNI = borderType & ~BORDER_ISOLATED;
if (borderTypeNI == BORDER_DEFAULT && (!src.isSubmatrix() || isolated) && dsz == Size(src.cols*2, src.rows*2))
{
typedef IppStatus (CV_STDCALL * ippiPyrUp)(const void* pSrc, int srcStep, void* pDst, int dstStep, IppiSize srcRoi, Ipp8u* buffer);
int type = src.type();
CV_SUPPRESS_DEPRECATED_START
ippiPyrUp pyrUpFunc = type == CV_8UC1 ? (ippiPyrUp) ippiPyrUp_Gauss5x5_8u_C1R :
type == CV_8UC3 ? (ippiPyrUp) ippiPyrUp_Gauss5x5_8u_C3R :
type == CV_32FC1 ? (ippiPyrUp) ippiPyrUp_Gauss5x5_32f_C1R :
type == CV_32FC3 ? (ippiPyrUp) ippiPyrUp_Gauss5x5_32f_C3R : 0;
CV_SUPPRESS_DEPRECATED_END
if (pyrUpFunc)
{
int bufferSize;
IppiSize srcRoi = { src.cols, src.rows };
IppDataType dataType = depth == CV_8U ? ipp8u : ipp32f;
CV_SUPPRESS_DEPRECATED_START
IppStatus ok = ippiPyrUpGetBufSize_Gauss5x5(srcRoi.width, dataType, src.channels(), &bufferSize);
CV_SUPPRESS_DEPRECATED_END
if (ok >= 0)
{
Ipp8u* buffer = ippsMalloc_8u(bufferSize);
ok = pyrUpFunc(src.data, (int) src.step, dst.data, (int) dst.step, srcRoi, buffer);
ippsFree(buffer);
if (ok >= 0)
{
CV_IMPL_ADD(CV_IMPL_IPP);
return;
}
setIppErrorStatus();
}
}
}
}
#endif
PyrFunc func = 0;
if( depth == CV_8U )
func = pyrUp_<FixPtCast<uchar, 6>, PyrUpVec_32s8u >;
else if( depth == CV_16S )
func = pyrUp_<FixPtCast<short, 6>, PyrUpVec_32s16s >;
else if( depth == CV_16U )
func = pyrUp_<FixPtCast<ushort, 6>, PyrUpVec_32s16u >;
else if( depth == CV_32F )
func = pyrUp_<FltCast<float, 6>, PyrUpVec_32f >;
else if( depth == CV_64F )
func = pyrUp_<FltCast<double, 6>, PyrUpNoVec<double, double> >;
else
CV_Error( CV_StsUnsupportedFormat, "" );
func( src, dst, borderType );
}
拉普拉斯金字塔:
数学定义:Li=Gi-UP(Gi+1)\*g
将源图像中位置(x,y)的像素映射到目标图像的(2x+1,2y+1)位置,即在进行向上取样。拉普拉斯金字塔是通过源图像减去先缩小后放大的图像的一系列图像构成。
void cv::pyrDown( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType )
void cv::pyrDown( InputArray _src, OutputArray _dst, const Size& _dsz, int borderType )
{
CV_Assert(borderType != BORDER_CONSTANT);
CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat(),
ocl_pyrDown(_src, _dst, _dsz, borderType))
Mat src = _src.getMat();
Size dsz = _dsz.area() == 0 ? Size((src.cols + 1)/2, (src.rows + 1)/2) : _dsz;
_dst.create( dsz, src.type() );
Mat dst = _dst.getMat();
int depth = src.depth();
#ifdef HAVE_TEGRA_OPTIMIZATION
if(borderType == BORDER_DEFAULT && tegra::useTegra() && tegra::pyrDown(src, dst))
return;
#endif
#if IPP_VERSION_X100 >= 801 && 0
CV_IPP_CHECK()
{
bool isolated = (borderType & BORDER_ISOLATED) != 0;
int borderTypeNI = borderType & ~BORDER_ISOLATED;
if (borderTypeNI == BORDER_DEFAULT && (!src.isSubmatrix() || isolated) && dsz == Size((src.cols + 1)/2, (src.rows + 1)/2))
{
typedef IppStatus (CV_STDCALL * ippiPyrDown)(const void* pSrc, int srcStep, void* pDst, int dstStep, IppiSize srcRoi, Ipp8u* buffer);
int type = src.type();
CV_SUPPRESS_DEPRECATED_START
ippiPyrDown pyrDownFunc = type == CV_8UC1 ? (ippiPyrDown) ippiPyrDown_Gauss5x5_8u_C1R :
type == CV_8UC3 ? (ippiPyrDown) ippiPyrDown_Gauss5x5_8u_C3R :
type == CV_32FC1 ? (ippiPyrDown) ippiPyrDown_Gauss5x5_32f_C1R :
type == CV_32FC3 ? (ippiPyrDown) ippiPyrDown_Gauss5x5_32f_C3R : 0;
CV_SUPPRESS_DEPRECATED_END
if (pyrDownFunc)
{
int bufferSize;
IppiSize srcRoi = { src.cols, src.rows };
IppDataType dataType = depth == CV_8U ? ipp8u : ipp32f;
CV_SUPPRESS_DEPRECATED_START
IppStatus ok = ippiPyrDownGetBufSize_Gauss5x5(srcRoi.width, dataType, src.channels(), &bufferSize);
CV_SUPPRESS_DEPRECATED_END
if (ok >= 0)
{
Ipp8u* buffer = ippsMalloc_8u(bufferSize);
ok = pyrDownFunc(src.data, (int) src.step, dst.data, (int) dst.step, srcRoi, buffer);
ippsFree(buffer);
if (ok >= 0)
{
CV_IMPL_ADD(CV_IMPL_IPP);
return;
}
setIppErrorStatus();
}
}
}
}
#endif
PyrFunc func = 0;
if( depth == CV_8U )
func = pyrDown_<FixPtCast<uchar, 8>, PyrDownVec_32s8u>;
else if( depth == CV_16S )
func = pyrDown_<FixPtCast<short, 8>, PyrDownVec_32s16s >;
else if( depth == CV_16U )
func = pyrDown_<FixPtCast<ushort, 8>, PyrDownVec_32s16u >;
else if( depth == CV_32F )
func = pyrDown_<FltCast<float, 8>, PyrDownVec_32f>;
else if( depth == CV_64F )
func = pyrDown_<FltCast<double, 8>, PyrDownNoVec<double, double> >;
else
CV_Error( CV_StsUnsupportedFormat, "" );
func( src, dst, borderType );
}
可以理解拉普拉斯金字塔是高斯金字塔的逆行式。
尺寸调整:resize()函数
void resize( InputArray src, OutputArray dst, Size dsize, double fx = 0, double fy = 0, int interpolation = INTER_LINEAR );
void cv::resize( InputArray _src, OutputArray _dst, Size dsize,
double inv_scale_x, double inv_scale_y, int interpolation )
{
static ResizeFunc linear_tab[] =
{
resizeGeneric_<
HResizeLinear<uchar, int, short,
INTER_RESIZE_COEF_SCALE,
HResizeLinearVec_8u32s>,
VResizeLinear<uchar, int, short,
FixedPtCast<int, uchar, INTER_RESIZE_COEF_BITS*2>,
VResizeLinearVec_32s8u> >,
0,
resizeGeneric_<
HResizeLinear<ushort, float, float, 1,
HResizeLinearVec_16u32f>,
VResizeLinear<ushort, float, float, Cast<float, ushort>,
VResizeLinearVec_32f16u> >,
resizeGeneric_<
HResizeLinear<short, float, float, 1,
HResizeLinearVec_16s32f>,
VResizeLinear<short, float, float, Cast<float, short>,
VResizeLinearVec_32f16s> >,
0,
resizeGeneric_<
HResizeLinear<float, float, float, 1,
HResizeLinearVec_32f>,
VResizeLinear<float, float, float, Cast<float, float>,
VResizeLinearVec_32f> >,
resizeGeneric_<
HResizeLinear<double, double, float, 1,
HResizeNoVec>,
VResizeLinear<double, double, float, Cast<double, double>,
VResizeNoVec> >,
0
};
static ResizeFunc cubic_tab[] =
{
resizeGeneric_<
HResizeCubic<uchar, int, short>,
VResizeCubic<uchar, int, short,
FixedPtCast<int, uchar, INTER_RESIZE_COEF_BITS*2>,
VResizeCubicVec_32s8u> >,
0,
resizeGeneric_<
HResizeCubic<ushort, float, float>,
VResizeCubic<ushort, float, float, Cast<float, ushort>,
VResizeCubicVec_32f16u> >,
resizeGeneric_<
HResizeCubic<short, float, float>,
VResizeCubic<short, float, float, Cast<float, short>,
VResizeCubicVec_32f16s> >,
0,
resizeGeneric_<
HResizeCubic<float, float, float>,
VResizeCubic<float, float, float, Cast<float, float>,
VResizeCubicVec_32f> >,
resizeGeneric_<
HResizeCubic<double, double, float>,
VResizeCubic<double, double, float, Cast<double, double>,
VResizeNoVec> >,
0
};
static ResizeFunc lanczos4_tab[] =
{
resizeGeneric_<HResizeLanczos4<uchar, int, short>,
VResizeLanczos4<uchar, int, short,
FixedPtCast<int, uchar, INTER_RESIZE_COEF_BITS*2>,
VResizeNoVec> >,
0,
resizeGeneric_<HResizeLanczos4<ushort, float, float>,
VResizeLanczos4<ushort, float, float, Cast<float, ushort>,
VResizeLanczos4Vec_32f16u> >,
resizeGeneric_<HResizeLanczos4<short, float, float>,
VResizeLanczos4<short, float, float, Cast<float, short>,
VResizeLanczos4Vec_32f16s> >,
0,
resizeGeneric_<HResizeLanczos4<float, float, float>,
VResizeLanczos4<float, float, float, Cast<float, float>,
VResizeLanczos4Vec_32f> >,
resizeGeneric_<HResizeLanczos4<double, double, float>,
VResizeLanczos4<double, double, float, Cast<double, double>,
VResizeNoVec> >,
0
};
static ResizeAreaFastFunc areafast_tab[] =
{
resizeAreaFast_<uchar, int, ResizeAreaFastVec<uchar, ResizeAreaFastVec_SIMD_8u> >,
0,
resizeAreaFast_<ushort, float, ResizeAreaFastVec<ushort, ResizeAreaFastVec_SIMD_16u> >,
resizeAreaFast_<short, float, ResizeAreaFastVec<short, ResizeAreaFastVec_SIMD_16s> >,
0,
resizeAreaFast_<float, float, ResizeAreaFastVec_SIMD_32f>,
resizeAreaFast_<double, double, ResizeAreaFastNoVec<double, double> >,
0
};
static ResizeAreaFunc area_tab[] =
{
resizeArea_<uchar, float>, 0, resizeArea_<ushort, float>,
resizeArea_<short, float>, 0, resizeArea_<float, float>,
resizeArea_<double, double>, 0
};
Size ssize = _src.size();
CV_Assert( ssize.area() > 0 );
CV_Assert( dsize.area() > 0 || (inv_scale_x > 0 && inv_scale_y > 0) );
if( dsize.area() == 0 )
{
dsize = Size(saturate_cast<int>(ssize.width*inv_scale_x),
saturate_cast<int>(ssize.height*inv_scale_y));
CV_Assert( dsize.area() > 0 );
}
else
{
inv_scale_x = (double)dsize.width/ssize.width;
inv_scale_y = (double)dsize.height/ssize.height;
}
CV_OCL_RUN(_src.dims() <= 2 && _dst.isUMat() && _src.cols() > 10 && _src.rows() > 10,
ocl_resize(_src, _dst, dsize, inv_scale_x, inv_scale_y, interpolation))
Mat src = _src.getMat();
_dst.create(dsize, src.type());
Mat dst = _dst.getMat();
#ifdef HAVE_TEGRA_OPTIMIZATION
if (tegra::useTegra() && tegra::resize(src, dst, (float)inv_scale_x, (float)inv_scale_y, interpolation))
return;
#endif
int type = src.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type);
double scale_x = 1./inv_scale_x, scale_y = 1./inv_scale_y;
int k, sx, sy, dx, dy;
int iscale_x = saturate_cast<int>(scale_x);
int iscale_y = saturate_cast<int>(scale_y);
bool is_area_fast = std::abs(scale_x - iscale_x) < DBL_EPSILON &&
std::abs(scale_y - iscale_y) < DBL_EPSILON;
#if IPP_VERSION_X100 >= 701
CV_IPP_CHECK()
{
#define IPP_RESIZE_EPS 1e-10
double ex = fabs((double)dsize.width / src.cols - inv_scale_x) / inv_scale_x;
double ey = fabs((double)dsize.height / src.rows - inv_scale_y) / inv_scale_y;
if ( ((ex < IPP_RESIZE_EPS && ey < IPP_RESIZE_EPS && depth != CV_64F) || (ex == 0 && ey == 0 && depth == CV_64F)) &&
(interpolation == INTER_LINEAR || interpolation == INTER_CUBIC) &&
!(interpolation == INTER_LINEAR && is_area_fast && iscale_x == 2 && iscale_y == 2 && depth == CV_8U))
{
int mode = -1;
if (interpolation == INTER_LINEAR && src.rows >= 2 && src.cols >= 2)
mode = ippLinear;
else if (interpolation == INTER_CUBIC && src.rows >= 4 && src.cols >= 4)
mode = ippCubic;
if( mode >= 0 && (cn == 1 || cn == 3 || cn == 4) &&
(depth == CV_16U || depth == CV_16S || depth == CV_32F ||
(depth == CV_64F && mode == ippLinear)))
{
bool ok = true;
Range range(0, src.rows);
IPPresizeInvoker invoker(src, dst, inv_scale_x, inv_scale_y, mode, &ok);
parallel_for_(range, invoker, dst.total()/(double)(1<<16));
if( ok )
{
CV_IMPL_ADD(CV_IMPL_IPP|CV_IMPL_MT);
return;
}
setIppErrorStatus();
}
}
#undef IPP_RESIZE_EPS
}
#endif
if( interpolation == INTER_NEAREST )
{
resizeNN( src, dst, inv_scale_x, inv_scale_y );
return;
}
{
// in case of scale_x && scale_y is equal to 2
// INTER_AREA (fast) also is equal to INTER_LINEAR
if( interpolation == INTER_LINEAR && is_area_fast && iscale_x == 2 && iscale_y == 2 )
interpolation = INTER_AREA;
// true "area" interpolation is only implemented for the case (scale_x <= 1 && scale_y <= 1).
// In other cases it is emulated using some variant of bilinear interpolation
if( interpolation == INTER_AREA && scale_x >= 1 && scale_y >= 1 )
{
if( is_area_fast )
{
int area = iscale_x*iscale_y;
size_t srcstep = src.step / src.elemSize1();
AutoBuffer<int> _ofs(area + dsize.width*cn);
int* ofs = _ofs;
int* xofs = ofs + area;
ResizeAreaFastFunc func = areafast_tab[depth];
CV_Assert( func != 0 );
for( sy = 0, k = 0; sy < iscale_y; sy++ )
for( sx = 0; sx < iscale_x; sx++ )
ofs[k++] = (int)(sy*srcstep + sx*cn);
for( dx = 0; dx < dsize.width; dx++ )
{
int j = dx * cn;
sx = iscale_x * j;
for( k = 0; k < cn; k++ )
xofs[j + k] = sx + k;
}
func( src, dst, ofs, xofs, iscale_x, iscale_y );
return;
}
ResizeAreaFunc func = area_tab[depth];
CV_Assert( func != 0 && cn <= 4 );
AutoBuffer<DecimateAlpha> _xytab((ssize.width + ssize.height)*2);
DecimateAlpha* xtab = _xytab, *ytab = xtab + ssize.width*2;
int xtab_size = computeResizeAreaTab(ssize.width, dsize.width, cn, scale_x, xtab);
int ytab_size = computeResizeAreaTab(ssize.height, dsize.height, 1, scale_y, ytab);
AutoBuffer<int> _tabofs(dsize.height + 1);
int* tabofs = _tabofs;
for( k = 0, dy = 0; k < ytab_size; k++ )
{
if( k == 0 || ytab[k].di != ytab[k-1].di )
{
assert( ytab[k].di == dy );
tabofs[dy++] = k;
}
}
tabofs[dy] = ytab_size;
func( src, dst, xtab, xtab_size, ytab, ytab_size, tabofs );
return;
}
}
int xmin = 0, xmax = dsize.width, width = dsize.width*cn;
bool area_mode = interpolation == INTER_AREA;
bool fixpt = depth == CV_8U;
float fx, fy;
ResizeFunc func=0;
int ksize=0, ksize2;
if( interpolation == INTER_CUBIC )
ksize = 4, func = cubic_tab[depth];
else if( interpolation == INTER_LANCZOS4 )
ksize = 8, func = lanczos4_tab[depth];
else if( interpolation == INTER_LINEAR || interpolation == INTER_AREA )
ksize = 2, func = linear_tab[depth];
else
CV_Error( CV_StsBadArg, "Unknown interpolation method" );
ksize2 = ksize/2;
CV_Assert( func != 0 );
AutoBuffer<uchar> _buffer((width + dsize.height)*(sizeof(int) + sizeof(float)*ksize));
int* xofs = (int*)(uchar*)_buffer;
int* yofs = xofs + width;
float* alpha = (float*)(yofs + dsize.height);
short* ialpha = (short*)alpha;
float* beta = alpha + width*ksize;
short* ibeta = ialpha + width*ksize;
float cbuf[MAX_ESIZE];
for( dx = 0; dx < dsize.width; dx++ )
{
if( !area_mode )
{
fx = (float)((dx+0.5)*scale_x - 0.5);
sx = cvFloor(fx);
fx -= sx;
}
else
{
sx = cvFloor(dx*scale_x);
fx = (float)((dx+1) - (sx+1)*inv_scale_x);
fx = fx <= 0 ? 0.f : fx - cvFloor(fx);
}
if( sx < ksize2-1 )
{
xmin = dx+1;
if( sx < 0 && (interpolation != INTER_CUBIC && interpolation != INTER_LANCZOS4))
fx = 0, sx = 0;
}
if( sx + ksize2 >= ssize.width )
{
xmax = std::min( xmax, dx );
if( sx >= ssize.width-1 && (interpolation != INTER_CUBIC && interpolation != INTER_LANCZOS4))
fx = 0, sx = ssize.width-1;
}
for( k = 0, sx *= cn; k < cn; k++ )
xofs[dx*cn + k] = sx + k;
if( interpolation == INTER_CUBIC )
interpolateCubic( fx, cbuf );
else if( interpolation == INTER_LANCZOS4 )
interpolateLanczos4( fx, cbuf );
else
{
cbuf[0] = 1.f - fx;
cbuf[1] = fx;
}
if( fixpt )
{
for( k = 0; k < ksize; k++ )
ialpha[dx*cn*ksize + k] = saturate_cast<short>(cbuf[k]*INTER_RESIZE_COEF_SCALE);
for( ; k < cn*ksize; k++ )
ialpha[dx*cn*ksize + k] = ialpha[dx*cn*ksize + k - ksize];
}
else
{
for( k = 0; k < ksize; k++ )
alpha[dx*cn*ksize + k] = cbuf[k];
for( ; k < cn*ksize; k++ )
alpha[dx*cn*ksize + k] = alpha[dx*cn*ksize + k - ksize];
}
}
for( dy = 0; dy < dsize.height; dy++ )
{
if( !area_mode )
{
fy = (float)((dy+0.5)*scale_y - 0.5);
sy = cvFloor(fy);
fy -= sy;
}
else
{
sy = cvFloor(dy*scale_y);
fy = (float)((dy+1) - (sy+1)*inv_scale_y);
fy = fy <= 0 ? 0.f : fy - cvFloor(fy);
}
yofs[dy] = sy;
if( interpolation == INTER_CUBIC )
interpolateCubic( fy, cbuf );
else if( interpolation == INTER_LANCZOS4 )
interpolateLanczos4( fy, cbuf );
else
{
cbuf[0] = 1.f - fy;
cbuf[1] = fy;
}
if( fixpt )
{
for( k = 0; k < ksize; k++ )
ibeta[dy*ksize + k] = saturate_cast<short>(cbuf[k]*INTER_RESIZE_COEF_SCALE);
}
else
{
for( k = 0; k < ksize; k++ )
beta[dy*ksize + k] = cbuf[k];
}
}
func( src, dst, xofs, fixpt ? (void*)ialpha : (void*)alpha, yofs,
fixpt ? (void*)ibeta : (void*)beta, xmin, xmax, ksize );
}
还没有评论,来说两句吧...