好的,这是一个相当复杂的问题。我会尝试将其拆分成不同部分并逐个回答。
问题一
blockproc
可以使用 BorderSize
和 TrimBorder
参数在滑动窗口上实现我的函数。
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
像素,其中
M
和
N
是您想要的水平和垂直边框大小。 这将使我们捕获更多的像素,即在原始块上方和下方分别增加
M
个像素,并且在原始块左侧和右侧分别增加
N
个像素。
因此,对于A
中的值1,如果块大小为1 x 1,则表示该元素仅包含1,并且如果我们的BorderSize
为1 x 1。 这意味着我们的最终块将为:
0 0 0
0 1 6
0 2 7
因为我们的块大小是1,下一个块将位于6的中心,我们将获得一个3x3像素的网格,依此类推。同时,重要的是设置
TrimBorder
为
false
,以便保留最初扩展块时捕获的那些像素。默认设置为
true
。最后,
PadPartialBlocks
是
true
,以确保所有块的大小相同。运行上述代码时,我们得到的结果是:
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 = 5
且floor(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
我进行了一些时间测试,似乎在此环境中使用的blockproc
比nlfilter
更快。
问题#2
第二个是im2col
。im2col(A,[m n],block_type)
将块分成m乘n个块并将它们排列在列中,因此每个列都是一个块?如果是这样,那么重叠如何控制?如果每个块都是一列,那我能成功地对每一列应用dct2
函数吗?因为我怀疑它会将向量作为输入?
您说的im2col
确实将每个像素邻域或块转换为单个列,并且这些列的串联形成输出矩阵。可以通过block_type
参数控制块是否重叠或不同。使用m
和n
可以控制每个邻域的大小。
但是,如果您的目标是对im2col
的输出应用dct2
,则您将无法得到所需的结果。具体而言,dct2
考虑了2D数据中每个数据点的空间位置,并用作变换的一部分。通过将每个像素邻域转换为单个列,原始的每个块中存在的2D空间关系现在已经消失了。 dct2
期望2D空间数据,但您将指定1D数据。因此,im2col
可能不是您要寻找的东西。如果我正确理解您的需求,您应该使用blockproc
。
希望这可以帮助你!
blockproc
的说法是相矛盾的...因为经过一些实验后,您肯定可以...只是您需要调整一下参数。这是一个非常有趣的问题,我也从中学到了一些东西。同时,感谢您提出这个问题! - rayryeng