2013-08-27 09:53:20 taoyanbian1022 阅读数 2717
  • 学习OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频教程

    OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频培训课程概况:教程中会讲解到基于opencv视频和摄像机录制、播放和播放进度控制,多视频图像合并、多视频图像融合、剪切、视频亮度、对比度、尺寸(近邻插值(手动实现),双线性插值,图像金字塔)、颜色格式(灰度图,二值化(阈值)),旋转镜像,视频裁剪(ROI),视频水印(ROI+weight),导出处理后的视频(包含音频,使用ffmpeg工具对音频进行抽取、剪切和终于opencv处理的视频合并)。

    19567 人正在学习 去看看 夏曹俊

以多分辨率来解释图像的一种有效但概念简单的结构就是图像金字塔。一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低的图像集合。金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。当向金字塔的上层移动时,尺寸和分辨率就降低。
对于一幅尺寸为2^J*2^J的图像,则其图像金字塔中间级的尺寸是2^j*2^j,其中j的理论取值返回是0,1,2,...,(J-1),J。但是由于当j很小时,图像将丢失大部分信息,所以一般限制只用P+1级来减少原来图像近似值的尺寸,也就是j的实际取值是(J-P),(J-P+1),...(J-1),J。
而一般处理这种分辨率变化,简单的方法就是隔行取值来生成低分辨率的图像,复杂点的方法就涉及到图像的内插和过滤等步骤了,当然复杂点的方法使得生成的低分辨率下的图像更加平滑和真实。对某幅原始图像生成了一系列的低分辨率图像之后,这些图像和原始图像一起就形成了图像金字塔。
例如,对于512*512的图像,该图像的4级图像金字塔就是:

  1. 金字塔顶层   64*64
  2.     |     128*128
  3.     |     256*256
  4. 金字塔底层 512*512

当然,有时候也会对原图像进行扩展,生成分辨率更高的图像,然后把这个图像放到金字塔的更底层。这时候就需要更多处理,使得生成的图像保持真实性,避免色块和噪声等的产生。

在matlab里面,函数impyramid专门用于生成图像金字塔,直接调用就可以了。
格式:B = impyramid(A, direction)
作用:对A进行Gaussian金字塔变换,生成的图像是B。direction为“reduce”和“expand”,分别对应着分解和扩张。
注:
1.m*n的A“reduce”和“expand”后的维数分别对应ceil(M/2)*ceil(N/2)和(2*M-1)*(2*N-1)。
2.impyramid只对前两维有效。

2017-12-31 18:51:52 u013162035 阅读数 808
  • 学习OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频教程

    OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频培训课程概况:教程中会讲解到基于opencv视频和摄像机录制、播放和播放进度控制,多视频图像合并、多视频图像融合、剪切、视频亮度、对比度、尺寸(近邻插值(手动实现),双线性插值,图像金字塔)、颜色格式(灰度图,二值化(阈值)),旋转镜像,视频裁剪(ROI),视频水印(ROI+weight),导出处理后的视频(包含音频,使用ffmpeg工具对音频进行抽取、剪切和终于opencv处理的视频合并)。

    19567 人正在学习 去看看 夏曹俊

1.6图像金字塔

1.6.1关于图像金字塔

图像金字塔是图像中多尺度表达的一种,最主要用于图像的分割,是一种以多分辨率来解释图像的有效但概念简单的结构。
图像金字塔最初用于机器视觉和图像压缩,一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。
金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。

这里写图片描述

图1

一般情况下有两种类型的图像金字塔常常出现在文献和以及实际运用中。他们分别是:
•高斯金字塔(Gaussianpyramid): 用来向下采样,主要的图像金字塔
•拉普拉斯金字塔(Laplacianpyramid): 用来从金字塔低层图像重建上层未采样图像,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用。

两者的简要区别:高斯金字塔用来向下降采样图像,而拉普拉斯金字塔则用来从金字塔底层图像中向上采样重建一个图像。
要从金字塔第i层生成第i+1层(我们表示第i+1层为G_i+1),我们先要用高斯核对G_1进行卷积,然后删除所有偶数行和偶数列。当然的是,新得到图像面积会变为源图像的四分之一。按上述过程对输入图像G_0执行操作就可产生出整个金字塔。
当图像向金字塔的上层移动时,尺寸和分辨率就降低。OpenCV中,从金字塔中上一级图像生成下一级图像的可以用PryDown。而通过PryUp将现有的图像在每个维度都放大两遍。
图像金字塔中的向上和向下采样分别通过OpenCV函数 pyrUp 和 pyrDown 实现,概括起来就是:
对图像向上采样:pyrUp函数
对图像向下采样:pyrDown函数
这里的向下与向上采样,是对图像的尺寸而言的(和金字塔的方向相反),向上就是图像尺寸加倍,向下就是图像尺寸减半。而如果我们按上图中演示的金字塔方向来理解,金字塔向上图像其实在缩小,这样刚好是反过来了。
但需要注意的是,PryUp和PryDown不是互逆的,即PryUp不是降采样的逆操作。这种情况下,图像首先在每个维度上扩大为原来的两倍,新增的行(偶数行)以0填充。然后给指定的滤波器进行卷积(实际上是一个在每个维度都扩大为原来两倍的过滤器)去估计“丢失”像素的近似值。PryDown( )是一个会丢失信息的函数。为了恢复原来更高的分辨率的图像,我们要获得由降采样操作丢失的信息,这些数据就和拉普拉斯金字塔有关系了。

1.6.1.1高斯金字塔

高斯金字塔是通过高斯平滑和亚采样获得一些列下采样图像,也就是说第K层高斯金字塔通过平滑、亚采样就可以获得K+1层高斯图像,高斯金字塔包含了一系列低通滤波器,其截至频率从上一层到下一层是以因子2逐渐增加,所以高斯金字塔可以跨越很大的频率范围。金字塔的图像如下:

这里写图片描述

图2

另外,每一层都按从下到上的次序编号, 层级 G_i+1 (表示为 G_i+1尺寸小于第i层G_i)。
1)对图像的向下取样
为了获取层级为 G_i+1 的金字塔图像,我们采用如下方法:
1>对图像G_i进行高斯内核卷积;
2>将所有偶数行和列去除。
得到的图像即为G_i+1的图像,显而易见,结果图像只有原图的四分之一。通过对输入图像G_i(原始图像)不停迭代以上步骤就会得到整个金字塔。同时我们也可以看到,向下取样会逐渐丢失图像的信息。以上就是对图像的向下取样操作,即缩小图像。
2)对图像的向上取样
如果想放大图像,则需要通过向上取样操作得到,具体做法如下:
1>将图像在每个方向扩大为原来的两倍,新增的行和列以0填充
2>使用先前同样的内核(乘以4)与放大后的图像卷积,获得 “新增像素”的近似值
得到的图像即为放大后的图像,但是与原来的图像相比会发觉比较模糊,因为在缩放的过程中已经丢失了一些信息,如果想在缩小和放大整个过程中减少信息的丢失,这些数据形成了拉普拉斯金字塔。
那么,我们接下来一起看一看拉普拉斯金字塔的概念吧。

1.6.1.2拉普拉斯金字塔

下式是拉普拉斯金字塔第i层的数学定义:

这里写图片描述

式中G_i的表示第i层的图像。而UP()操作是将源图像中位置为(x,y)的像素映射到目标图像的(2x+1,2y+1)位置,即在进行向上取样。 符号表示卷积,g_(5×5)为5x5的高斯内核。
我们下文将要介绍的pryUp,就是在进行上面这个式子的运算。
因此,我们可以直接用OpenCV进行拉普拉斯运算:
Li=GiPyrUp(Gi+1)

也就是说,拉普拉斯金字塔是通过源图像减去先缩小后再放大的图像的一系列图像构成的。个拉普拉斯金字塔运算过程可以通过下图来概括:
这里写图片描述

图3

所以,我们可以将拉普拉斯金字塔理解为高斯金字塔的逆形式。另外再提一点,关于图像金字塔非常重要的一个应用就是实现图像分割。图像分割的话,先要建立一个图像金字塔,然后在G_i和G_i+1的像素直接依照对应的关系,建立起”父与子“关系。而快速初始分割可以先在金字塔高层的低分辨率图像上完成,然后逐层对分割加以优化。

1.6.2 resize( )函数剖析

resize( )为OpenCV中专职调整图像大小的函数。此函数将源图像精确地转换为指定尺寸的目标图像。如果源图像中设置了ROI(Region Of Interest ,感兴趣区域),那么resize( )函数会对源图像的ROI区域进行调整图像尺寸的操作,来输出到目标图像中。若目标图像中已经设置ROI区域,不难理解resize( )将会对源图像进行尺寸调整并填充到目标图像的ROI中。
很多时候,我们并不用考虑第二个参数dst的初始图像尺寸和类型(即直接定义一个Mat类型,不用对其初始化),因为其尺寸和类型可以由“`
src,dsize,fx和fy这其他的几个参数来确定。

C++: void resize(InputArray src,
                           OutputArray dst, 
                           Size dsize, 
                          double fx=0, 
                          double fy=0, 
                          int interpolation=INTER_LINEAR )  

【参数】
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
第二个参数,OutputArray类型的dst,输出图像,当其非零时,有着dsize(第三个参数)的尺寸,或者由src.size()计算出来。
第三个参数,Size类型的dsize,输出图像的大小;如果它等于零,由下式进行计算:
其中,dsize,fx,fy都不能为0。
第四个参数,double类型的fx,沿水平轴的缩放系数,有默认值0,且当其等于0时,由下式进行计算:
第五个参数,double类型的fy,沿垂直轴的缩放系数,有默认值0,且当其等于0时,由下式进行计算:
第六个参数,int类型的interpolation,用于指定插值方式,默认为INTER_LINEAR(线性插值)。
可选的插值方式如下:
INTER_NEAREST - 最近邻插值
INTER_LINEAR - 线性插值(默认值)
INTER_AREA - 区域插值(利用像素区域关系的重采样插值)
INTER_CUBIC –三次样条插值(超过4×4像素邻域内的双三次插值)
INTER_LANCZOS4 -Lanczos插值(超过8×8像素邻域的Lanczos插值)
若要缩小图像,一般情况下最好用CV_INTER_AREA来插值,而若要放大图像,一般情况下最好用CV_INTER_CUBIC(效率不高,慢,不推荐使用)或CV_INTER_LINEAR(效率较高,速度较快,推荐使用)。
关于插值,我们看几张图就能更好地理解。先看原图:

这里写图片描述

图4

当进行6次图像缩小接着6次图像放大操作后,两种不同的插值方式得到的效果图:
这里写图片描述

图5

效果很明显,第一张全是一个个的像素,非常影响美观。另外一张却有雾化的朦胧美感,所以插值方式的选择,对经过多次放大缩小的图片最终得到的效果是有很大影响的。
接着我们来看两种resize的调用范例。
方式一,调用范例:

Mat dst=Mat::zeros(512 ,512, CV_8UC3 );//新建一张512x512尺寸的图片  
Mat src=imread(“1.jpg”);  
//显式指定dsize=dst.size(),那么fx和fy会其计算出来,不用额外指定。  
resize(src, dst, dst.size());  

方式二、调用范例:

Mat dst;  
Mat src=imread(“1.jpg”)  
 //指定fx和fy,让函数计算出目标图像的大小。  
resize(src, dst, Size(), 0.5, 0.5);  

接着我们看看完整的示例程序。
代码参看附件【demo1】,程序运行截图。

这里写图片描述

图6

1.6.3 pyrUp()函数剖析

/*【pyrUp ( )函数源代码】**********************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\imgproc\src\pyramids.cpp
 * @起始行数:1246行   
********************************************************************************/
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 );
}

pyrUp( )函数的作用是向上采样并模糊一张图像,说白了就是放大一张图片。

C++: void pyrUp(InputArray src, 
                OutputArraydst, 
                const Size& dstsize=Size(), 
                int borderType=BORDER_DEFAULT )  

【参数】
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
第二个参数,OutputArray类型的dst,输出图像,和源图片有一样的尺寸和类型。
第三个参数,const Size&类型的dstsize,输出图像的大小;有默认值Size(),即默认情况下,由Size(src.cols*2,src.rows*2)来进行计算,且一直需要满足下列条件:

第四个参数,int类型的borderType,又来了,边界模式,一般我们不用去管它。
pyrUp函数执行高斯金字塔的采样操作,其实它也可以用于拉普拉斯金字塔的。首先,它通过插入可为零的行与列,对源图像进行向上取样操作,然后将结果与pyrDown()乘以4的内核做卷积,就是这样。直接看完整的示例程序。
代码参看附件【demo2】,程序运行截图。

这里写图片描述

图7

1.6.4 pyrDown()函数剖析

/*【pyrDown ( )函数源代码】**********************************************************
 * @Version:OpenCV 3.0.0(Opnencv2和Opnencv3差别不大,Linux和PC的对应版本源码完全一样,均在对应的安装目录下)  
 * @源码路径:…\opencv\sources\modules\imgproc\src\pyramids.cpp
 * @起始行数:1169行   
********************************************************************************/
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 );
}

pyrDown( )函数的作用是向下采样并模糊一张图片,说白了就是缩小一张图片。

C++: void pyrDown(InputArray src,
                  OutputArray dst, 
                  const Size& dstsize=Size(), 
                  int borderType=BORDER_DEFAULT)  

【参数】
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
第二个参数,OutputArray类型的dst,输出图像,和源图片有一样的尺寸和类型。
第三个参数,const Size&类型的dstsize,输出图像的大小;有默认值Size(),即默认情况下,由Size Size((src.cols+1)/2, (src.rows+1)/2)来进行计算,且一直需要满足下列条件:

|dstsize.width2src.cols|2|dstsize.height2src.rows|2

该pyrDown函数执行了高斯金字塔建造的向下采样的步骤。首先,它将源图像与如下内核做卷积运算:

12561464141624164624362464162416414641

接着,它便通过对图像的偶数行和列做插值来进行向下采样操作。依然是看看完整的示例程序。
代码参看【demo3】,程序运行截图。

这里写图片描述

图8

1.6.5图像金字塔综合实例

这个示例程序中,分别演示了用resize,pryUp,pryDown来让源图像进行放大缩小的操作,分别用键盘按键1、2、3、4、A、D、W、S来控制图片的放大与缩小。
代码参看附件【demo4】。

这里写图片描述

图9经过多次按键后的效果

参考:
英文:https://docs.opencv.org/master/d4/d1f/tutorial_pyramids.html
中文:
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/pyramids/pyramids.html#pyramids

本章附件:

请点击参考链接

【注意】博主在附件中的代码只有Linux版本的,如何使用Windows使用该代码请参看博主的另一篇博文
Opencv环境搭建(Visual Studio+Windows)- 请点击
有任何问题请联系博主。

2017-05-10 15:54:29 lzhf1122 阅读数 236
  • 学习OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频教程

    OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频培训课程概况:教程中会讲解到基于opencv视频和摄像机录制、播放和播放进度控制,多视频图像合并、多视频图像融合、剪切、视频亮度、对比度、尺寸(近邻插值(手动实现),双线性插值,图像金字塔)、颜色格式(灰度图,二值化(阈值)),旋转镜像,视频裁剪(ROI),视频水印(ROI+weight),导出处理后的视频(包含音频,使用ffmpeg工具对音频进行抽取、剪切和终于opencv处理的视频合并)。

    19567 人正在学习 去看看 夏曹俊

图像金字塔被广泛用于各种视觉应用中。图像金字塔是一个图像集合,集合中所有的图像都源于同一个原始图像,而且是通过对原始图像连续降采样活得,直到达到某个中止条件才停止降采样。(当然,降为一个像素肯定是中止条件。)


有两种类型的图像金字塔常常出现在文献和应用中:高斯金字塔用来向下降采样图像,而拉普拉斯金字塔则用来从金字塔低层图像中向上采样重建一个图像。


要从金字塔第 i 层生成第i+1 层,我们先要用高斯核对第i 层进行卷积,然后删除所有偶数行和偶数列。当然,新得到的图像面积会变为源图像的四分之一。按上述过程对输入图像循环执行操作就可产生整个金字塔。OpenCV为我们提供了从金字塔中上一级图像生成下一级图像的方法:cvPyrDown。同样,我们可以通过后面相似的函数(但不是降采样的逆操作)将现有的图像在每个维度上都放大两倍:cvPyrUp。在这种情况下,图像首先在每个维度上都扩大为原来的两倍,新增的行(偶数行)以0填充。然后给指定的滤波器进行卷积(实际上是一个在每一维上都扩大为两倍的过滤器)去估计”丢失“像素的近似值。


我们之前注意到函数cvPyrUp并不是函数cvPyrDown的逆操作。之所以这样是因为cvPyrDown是一个会丢失信息的函数。为了恢复原来(更高的分辨率)的图像,我们需要获得由降采样操作丢失的信息。



原图像如下所示:


连续两次向下采样 pyrDown ,结果如图:



由于我们缩小了图像,我们也因此丢失了一些信息。通过连续向上采样两次 pyrUp ,很明显图像有些失真:


2016-01-20 14:55:30 ZJU_fish1996 阅读数 2200
  • 学习OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频教程

    OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频培训课程概况:教程中会讲解到基于opencv视频和摄像机录制、播放和播放进度控制,多视频图像合并、多视频图像融合、剪切、视频亮度、对比度、尺寸(近邻插值(手动实现),双线性插值,图像金字塔)、颜色格式(灰度图,二值化(阈值)),旋转镜像,视频裁剪(ROI),视频水印(ROI+weight),导出处理后的视频(包含音频,使用ffmpeg工具对音频进行抽取、剪切和终于opencv处理的视频合并)。

    19567 人正在学习 去看看 夏曹俊

图像金字塔

     方法一:将图像分割成不同大小的方格,在每个方格提取视觉单词。

         如,第0层为原图像,第1层为2x2等分图像,第2层为4x4等分图像……

        在金字塔的每一层,求得图像XY同一个单元格视觉单词直方图,并计算直方图的交,得到了相似度。

    

       (公式中Hl(i)代表第l层第i个直方图,minHx,Hy)的意思是对直方图的每个元素进行对比,取小的进行求和)

        每一层金字塔取特定的权重。

        总体的相似度为:

       

方法二:下采样

     1.高斯滤波,隔行隔列采样,得到gaussian金字塔

    2.上采样,相同像素两图相减,得到laplacian金字塔

2018-08-22 11:47:17 qq_40962368 阅读数 1600
  • 学习OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频教程

    OpenCV3.2+QT5+ffmpeg实战开发视频编辑器视频培训课程概况:教程中会讲解到基于opencv视频和摄像机录制、播放和播放进度控制,多视频图像合并、多视频图像融合、剪切、视频亮度、对比度、尺寸(近邻插值(手动实现),双线性插值,图像金字塔)、颜色格式(灰度图,二值化(阈值)),旋转镜像,视频裁剪(ROI),视频水印(ROI+weight),导出处理后的视频(包含音频,使用ffmpeg工具对音频进行抽取、剪切和终于opencv处理的视频合并)。

    19567 人正在学习 去看看 夏曹俊

图像金字塔

       图像金字塔是图像多尺度表达的一种,是一种以多分辨率来解释图像的有效但概念简单的结构。图像金字塔最初用于机器视觉和图像压缩,一幅图像的金字塔是一系列以金字塔形状排列的分辨率逐步降低,且来源于同一张原始图的图像集合。其通过梯次向下采样获得,直到达到某个终止条件才停止采样。我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低。

图像金字塔示意图(图片来源百度百科)

        从上面对图像金字塔的定义来看,图像金字塔的功能之一就是对图像尺度尺度的转换,即放大或者缩小图片,在OpenCV中提供了两种方法:

  • cv2.resize()函数,这种方法可直接对图像进行尺度的变换,在我的博客  图像的简单几何变换  中,提供了相应的示例。
  • cv2.pyrUp()、cv2.pyrDown()函数。这是与图像金字塔相关的两个函数,分别是对图像的向上采样、向下采样操作。

 图像金字塔一般有两种类型:

  • 高斯金字塔(Gaussianpyramid):用来向下采样,是主要的图像金字塔。
  • 拉普拉斯金字塔(Laplacianpyramid):用来从金字塔底层图像重建上层未采样图像,在数字图像处理中也就是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用。

两者的简要区别:高斯金字塔用来向下采样图像,而拉普拉斯金字塔则用来从金字塔底层图像中向上采样重建一个图像。 

在OpenCV中:

  • 对图像向上采样:pyrUp()函数
  • 对图像向下采样:pyrDown()函数
img = cv2.imread('luotuo.jpg', 0)  # 读为灰度图
up_img = cv2.pyrUp(img)  # 上采样操作
img_1 = cv2.pyrDown(img)  # 下采样操作
img_2 = cv2.pyrDown(img_1)
cv2.imshow('up_img', up_img)
cv2.imshow('img', img)
cv2.imshow('img_1', img_1)
cv2.imshow('img_2', img_2)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

这里的向下与向上采样是对图像的尺度来说的 ,相当于倒立的金字塔,向上就是图像尺寸加倍,向下就是图像尺寸减半。

 

需要注意的是,pyrUp和pyrDown不是互逆的,即上采样不是下采样的逆操作。

       pyrDown()是一个会丢失信息的函数。为了恢复原来更高分辨率的图像,要获得由于下采样操作所丢失的信息,这些数据就和拉普拉斯金字塔有关了。

图像的拉普拉斯金字塔可以由图像的高斯金字塔得到,转换的公式为:

img = cv2.imread('3.jpg', 0)
img1 = cv2.pyrDown(img)  # 高斯金字塔
_img1 = cv2.pyrDown(img1)
_img = cv2.pyrUp(_img1)
img2 = img1 - _img  # 拉普拉斯金字塔
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.waitKey(0)
cv2.destroyAllWindows()

 拉普拉斯金字塔的图像看起来就像是边界图。经常被用在图像压缩中。

       关于图像的放大和缩小,个人认为还是使用resize()函数比较好,因为他不会使图像变得特别模糊,而且非常简便。这里再对之前的resize函数知识再做一些补充

第六个参数,int类型的interpolation,用于指定插值方式,默认为INTER_LINEAR(线性插值)。

可选的插值方式如下:

  • INTER_NEAREST - 最近邻插值
  • INTER_LINEAR - 线性插值(默认值)
  • INTER_AREA - 区域插值(利用像素区域关系的重采样插值)
  • INTER_CUBIC –三次样条插值(超过4×4像素邻域内的双三次插值)
  • INTER_LANCZOS4 -Lanczos插值(超过8×8像素邻域的Lanczos插值)

若要缩小图像,一般情况下最好用CV_INTER_AREA来插值,

而若要放大图像,一般情况下最好用CV_INTER_CUBIC(效率不高,慢,不推荐使用)或CV_INTER_LINEAR(效率较高,速度较快,推荐使用)。

 

修仙路上不孤单,两位道友的博客,欢迎关注。

https://me.csdn.net/qq_41007606

https://me.csdn.net/weixin_38877564

没有更多推荐了,返回首页