使用blockproc或im2col在图像上进行重叠滑动窗口?

4
我需要将dct2应用于图像的小窗口,最好使用重叠窗口。我发现在Matlab中有两个函数可以实现这一点:blockprocim2col。但我对它们都不太理解,希望能得到一些澄清。使用BorderSizeTrimBorder参数,blockproc可用于在滑动窗口上实现我的函数。
B = blockproc(A,[64,64],fun,'BorderSize',[5,5], 'TrimBorder', 'false');

我知道这会创建一个[64 + 2*5, 64 + 2*5]的块,并在每个块上应用函数@fun。但由于我无法进入我的函数@fun进行调试以验证正确操作,因此我不能确定这是否是我需要的。
我的代码是否正确?我知道我在B中获得了连接的结果,但它应该在重叠滑动块上。这样做能实现我所需的吗?
第二个问题是关于im2colim2col(A,[m n],block_type)将把块分成m×n块并将它们排列在列中,所以每一列都是一个块?如果是这样,如何控制重叠部分?如果每个块都是一列,那么我能成功地在每一列上应用dct2函数吗?因为我怀疑它是否会接受向量作为输入?
希望这些解释对您有所帮助。
1个回答

10

好的,这是一个相当复杂的问题。我会尝试将其拆分成不同部分并逐个回答。

问题一

blockproc 可以使用 BorderSizeTrimBorder 参数在滑动窗口上实现我的函数。

B = blockproc(A,[64,64],fun,'BorderSize',[5,5], 'TrimBorder', 'false');

我知道这会创建一个大小为 [64 + 2*5, 64 + 2*5] 的块,并对每个块应用函数 @fun。但由于我不能进入我的函数 @fun 进行调试以验证其正常运行,所以我不能确定这是否符合我的需求。上面的代码对我需要的内容是否正确?我知道我会得到一个连接后的结果 B,但它应该是基于重叠滑动块的,这样能实现我的要求吗?

经过对 blockproc 进行试验,您可以确实使用它来实现滑动邻域处理。然而,您需要另外一个标志,即 PadPartialBlocks。此标志的目的是,如果您正在提取处于图像外缘的块,无法制作指定大小的块,则会对此部分块进行零填充,以使其符合相同的大小。以下是一个小例子,可以使用滑动窗口来实现这个过程。假设我们有一个矩阵,如下所示:

>> A = reshape(1:25,5,5)

A =

     1     6    11    16    21
     2     7    12    17    22
     3     8    13    18    23
     4     9    14    19    24
     5    10    15    20    25

假设我们想要在上述矩阵中以3 x 3重叠邻域为单位进行平均,并将超出矩阵边界的元素填充为零。您可以使用blockproc来实现:

B = blockproc(A, [1 1], @(x) mean(x.data(:)), 'BorderSize', [1 1], 'TrimBorder', false, 'PadPartialBlocks', true);
重要的是注意,块大小在此处为1 x 1,BorderSize也为1 x 1,与3 x 3块的期望设置不同。 为了解释这个问题,我们需要更深入地了解BorderSize的工作原理。 对于一个给定的块中心,BorderSize允许您捕获超出原始大小块尺寸的值/像素。 对于那些超出矩阵边界的位置,我们将默认填充这些位置为零。 BorderSize允许我们获取2M + 2N像素,其中MN是您想要的水平和垂直边框大小。 这将使我们捕获更多的像素,即在原始块上方和下方分别增加M个像素,并且在原始块左侧和右侧分别增加N个像素。

因此,对于A中的值1,如果块大小为1 x 1,则表示该元素仅包含1,并且如果我们的BorderSize为1 x 1。 这意味着我们的最终块将为:

0  0  0
0  1  6
0  2  7
因为我们的块大小是1,下一个块将位于6的中心,我们将获得一个3x3像素的网格,依此类推。同时,重要的是设置TrimBorderfalse,以便保留最初扩展块时捕获的那些像素。默认设置为true。最后,PadPartialBlockstrue,以确保所有块的大小相同。运行上述代码时,我们得到的结果是:
B =

    1.7778    4.3333    7.6667   11.0000    8.4444
    3.0000    7.0000   12.0000   17.0000   13.0000
    3.6667    8.0000   13.0000   18.0000   13.6667
    4.3333    9.0000   14.0000   19.0000   14.3333
    3.1111    6.3333    9.6667   13.0000    9.7778
你可以使用nlfilter来验证,我们可以对3 x 3滑动邻域应用平均值,并得到相同的结果。
C = nlfilter(A, [3 3], @(x) mean(x(:)))

C =

    1.7778    4.3333    7.6667   11.0000    8.4444
    3.0000    7.0000   12.0000   17.0000   13.0000
    3.6667    8.0000   13.0000   18.0000   13.6667
    4.3333    9.0000   14.0000   19.0000   14.3333
    3.1111    6.3333    9.6667   13.0000    9.7778

因此,如果您想正确使用blockproc进行滑动操作,您需要注意如何分别设置块大小和边框大小。在这种情况下,一般规则是始终将块大小设置为1 x 1,并允许BorderSize指定你想要的每个块的大小。具体而言,对于一个大小为K x K的块,您将分别将BorderSize设置为floor(K/2) x floor(K/2)。如果K是奇数,那么就会更容易。

例如,如果您想要对滑动窗口进行5 x 5均值滤波操作,您将把BorderSize设置为[2 2],因为K = 5floor(K/2) = 2。因此,您将执行以下操作:

B = blockproc(A, [1 1], @(x) mean(x.data(:)), 'BorderSize', [2 2], 'TrimBorder', false, 'PadPartialBlocks', true)

B =

    2.5200    4.5600    7.2000    6.9600    6.1200
    3.6000    6.4000   10.0000    9.6000    8.4000
    4.8000    8.4000   13.0000   12.4000   10.8000
    4.0800    7.0400   10.8000   10.2400    8.8800
    3.2400    5.5200    8.4000    7.9200    6.8400

使用大小为5 x 5的nlfilter进行复制也会得到:

C = nlfilter(A, [5 5], @(x) mean(x(:)))

C =

    2.5200    4.5600    7.2000    6.9600    6.1200
    3.6000    6.4000   10.0000    9.6000    8.4000
    4.8000    8.4000   13.0000   12.4000   10.8000
    4.0800    7.0400   10.8000   10.2400    8.8800
    3.2400    5.5200    8.4000    7.9200    6.8400

我进行了一些时间测试,似乎在此环境中使用的blockprocnlfilter更快。

问题#2

第二个是im2colim2col(A,[m n],block_type)将块分成m乘n个块并将它们排列在列中,因此每个列都是一个块?如果是这样,那么重叠如何控制?如果每个块都是一列,那我能成功地对每一列应用dct2函数吗?因为我怀疑它会将向量作为输入?

您说的im2col确实将每个像素邻域或块转换为单个列,并且这些列的串联形成输出矩阵。可以通过block_type参数控制块是否重叠或不同。使用mn可以控制每个邻域的大小。

但是,如果您的目标是对im2col的输出应用dct2,则您将无法得到所需的结果。具体而言,dct2考虑了2D数据中每个数据点的空间位置,并用作变换的一部分。通过将每个像素邻域转换为单个列,原始的每个块中存在的2D空间关系现在已经消失了。 dct2期望2D空间数据,但您将指定1D数据。因此,im2col可能不是您要寻找的东西。如果我正确理解您的需求,您应该使用blockproc


希望这可以帮助你!


1
非常感谢@rayryeng提供的出色和详细的解释。赏金已经添加并将在回复可用时给予,因为这是如此详细和有帮助的答复 :) - StuckInPhDNoMore
1
@FarazKhan - 噢,非常感谢您的慷慨奖赏!您真的不必这样做!顺便说一下,这与我先前关于无法在滑动窗口方法中使用 blockproc 的说法是相矛盾的...因为经过一些实验后,您肯定可以...只是您需要调整一下参数。这是一个非常有趣的问题,我也从中学到了一些东西。同时,感谢您提出这个问题! - rayryeng
在尝试了上述代码后,我认为“blockproc”生成的输出对我的分类器来说并不理想。每个块应该产生一个大小为“1x200”的向量,但是我得到了一个更小块的“1x1600”的向量。我发现这是“blockproc”的工作方式,因为它将4个块的结果“ul_output”,“ll_output”,“ur_output”,“lr_output”连接起来,所以它将区域分成了更小的块,然后将它们连接在一起,这对我的分类器没有用。因为我需要每个块单独的输出。我试图更改内部函数设置,但失败了。 - StuckInPhDNoMore
@FarazKhan - 你可以发另一个问题并详细说明你正在做什么吗?也许我能帮助你并回答你的问题。 - rayryeng
同意,对于一条评论来说需要讲的东西太多了。我只能开始一个新问题 :) - StuckInPhDNoMore
@rayryeng,我之前发布了一个问题,发现它与你解释的类似。请问我是否犯了同样的错误?https://stackoverflow.com/questions/57407606/blockproc-error-when-including-bordersize-argument - Maxxx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接