阅读(3090) (15)

OpenCV图像金字塔

2017-09-05 10:05:34 更新

目标

在本教程中,您将学习如何:

理论

注意
下面的解释属于Bradski和Kaehler 的“ 学习OpenCV ”一书。
  • 通常我们需要将图像转换成与原始图像不同的大小。为此,有两个可能的选择:升高图像(放大)或缩小(缩小)。
  • 虽然有一个几何变换中的OpenCV函数-literally-调整图像大小(CV ::调整大小,我们将在以后的教程显示),在该部分中,我们分析第一使用图像金字塔,这是在广泛应用的视觉范围广泛的应用。

图像金字塔

  • 图像金字塔是由单个原始图像产生的图像的集合,它们被连续下采样,直到达到一些所需的停止点。
  • 有两种常见的图像金字塔:高斯金字塔:用于缩减图像拉普拉斯金字塔:用于从金字塔中较低的图像重建上采样图像(分辨率较低)
  • 在本教程中,我们将使用高斯金字塔。

高斯金字塔

  • 想象一下金字塔是一组层,层数越高,尺寸越小。

OpenCV图像金字塔

  • 每个层从底部到顶部编号,因此层(i+1)(表示为Gi+1)小于层i(Gi)。
  • 为了在高斯金字塔中产生层(i+1),我们做如下:

将Gi与高斯内核进行卷积:

OpenCV图像金字塔

删除每个偶数行和列。

  • 您可以轻松注意到,所得到的图像将是其前身的四分之一。在输入图像(原始图像)上迭代该过程产生整个金字塔。G0
  • 上面的过程对于缩小图像是有用的。如果我们想使它更大,怎么办?:填充零的列()0
  1. 首先,将图像大小增加到每个维度的两倍,即新的偶数行和
  2. 用与上述相同的内核(乘以4)执行卷积近似“丢失像素”的值
  • 这两个过程(如上所述的下采样和上采样)由OpenCV函数cv :: pyrUpcv :: pyrDown实现,我们将在下面的代码示例中看到:
注意
当我们减小图像的大小时,我们实际上丢失了图像的信息。

Code

本教程代码如下所示。您也可以从这里下载


#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
Mat src, dst, tmp;
const char* window_name = "Pyramids Demo";
int main( void )
{
  printf( "n Zoom In-Out demo  n " );
  printf( "------------------ n" );
  printf( " * [u] -> Zoom in  n" );
  printf( " * [d] -> Zoom out n" );
  printf( " * [ESC] -> Close program n n" );
  src = imread( "../data/chicky_512.png" ); // Loads the test image
  if( src.empty() )
    { printf(" No data! -- Exiting the program n");
      return -1; }
  tmp = src;
  dst = tmp;
  imshow( window_name, dst );
  for(;;)
  {
    char c = (char)waitKey(0);
    if( c == 27 )
      { break; }
    if( c == 'u' )
      { pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 ) );
        printf( "** Zoom In: Image x 2 n" );
      }
    else if( c == 'd' )
      { pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 ) );
        printf( "** Zoom Out: Image / 2 n" );
      }
    imshow( window_name, dst );
    tmp = dst;
   }
   return 0;
}

说明

我们来看一下程序的一般结构:

  • 加载图像(在这种情况下,它在程序中定义,用户不必将其作为参数输入)
  src = imread( "../data/chicky_512.png" ); // Loads the test image
  if( src.empty() )
    { printf(" No data! -- Exiting the program n");
      return -1; }
  • 创建一个Mat对象以存储操作的结果(dst),另一个用于保存时间结果(tmp)。
Mat src,dst,tmp;
/ * ... * /
tmp = src;
dst = tmp;
  • 创建一个窗口来显示结果
  imshow(window_name,dst);

执行无限循环等待用户输入。

  for(;;)
  {
    char c = (char)waitKey(0);
    if( c == 27 )
      { break; }
    if( c == 'u' )
      { pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 ) );
        printf( "** Zoom In: Image x 2 n" );
      }
    else if( c == 'd' )
      { pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 ) );
        printf( "** Zoom Out: Image / 2 n" );
      }
    imshow( window_name, dst );
    tmp = dst;
   }

如果用户按ESC键退出我们的程序。此外,它有两个选择:

  • 执行上采样(按'u')
        if( c == 'u' )
          { pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 ) );
            printf( "** Zoom In: Image x 2 n" );
          }
  • 我们使用函数cv :: pyrUp与三个参数:
    • tmp:当前图像,它是用src原始图像初始化的。
    • dst:目标图像(要显示在屏幕上,据说是输入图像的两倍)
    • *大小(tmp.cols * 2,tmp.rows * 2)*:目的地大小。由于我们是上采样,cv :: pyrUp的大小要比输入图像大一倍(在这种情况下是tmp)。
  • 执行下采样(按'd'后)
        else if( c == 'd' )
          { pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 ) );
            printf( "** Zoom Out: Image / 2 n" );
          }
  • 类似于cv :: pyrUp,我们使用三个参数的函数cv :: pyrDown
    • tmp:当前图像,它是用src原始图像初始化的。
    • dst:目标图像(要显示在屏幕上,据说是输入图像的一半)
    • 大小(tmp.cols / 2,tmp.rows / 2):目的地大小。由于我们是上采样,cv :: pyrDown预期输入图像的大小(在这种情况下为tmp)的一半。
  • 请注意,输入图像的重要性可以除以二分之一(两个维度)。否则,将显示错误。
  • 最后,我们使用显示的当前图像更新输入图像tmp,因此执行后续操作。
    tmp = dst;

结果

  • 在编译上面的代码之后,我们可以测试一下。该程序调用图像chicky_512.jpg自带的样品/数据文件夹中。请注意,此图像为512 × 512,因此下采样不会产生任何错误(512 = 29)。原始图像如下所示:

OpenCV图像金字塔

  • 首先我们通过按“d” 应用两个连续的cv :: pyrDown操作。我们的产出是:

OpenCV图像金字塔

  • 请注意,由于我们正在减少图像的大小,我们应该已经失去了一些解决方案。这是很明显的,我们应用cv :: pyrUp两次(通过按'u')。我们的产品现在是:

OpenCV图像金字塔