让我知道的是将文件大小从MN 框减小到MN/k^2 框。这意味着您正在选择k x k distinct 框,找到每个块的平均值,然后将每个块的中心设置为平均值。
让我们从一个小的数字示例开始。假设我有以下 6 x 6 灰度图像,让我们制作 k = 3,这意味着您想要 3 x 3 不同的框:
7 5 4 8 7 1
3 10 8 2 9 2
3 7 5 2 8 5
6 5 1 6 7 10
8 4 4 6 8 9
1 3 4 7 8 7
我特意选择了以下维度,以便我们可以计算出一个干净的示例。请注意,如果您的图像的行或列没有均匀地划分为k,则必须考虑当您的块不包含所有有效像素时会发生什么。有些人要么用零填充这些值,要么进行某种智能填充,但为了方便您,假设这些值为 0。
您必须将此图像分割成3 x 3 框。您必须垂直和水平循环并收集 3 x 3 个盒子。这意味着您总共将获得 4 个盒子。先水平扫描后垂直扫描,我们得到以下框。
框 #1
7 5 4
3 10 8
3 7 5
框 #2
8 7 1
2 9 2
2 8 5
框 #3
6 5 1
8 4 4
1 3 4
框 #4
6 7 10
6 8 9
7 8 7
要计算输出图像,请找到每个块的平均值,然后将平均值写入输出图像的新位置。 6 x 6 图像现在缩小为 6 / 3 x 6 / 3 = 2 x 2,其中每个位置找到每个不同图像块的平均值。中心位置标记如下:
7 5 4 8 7 1
3 |10| 8 2 |9| 2
3 7 5 2 8 5
6 5 1 6 7 10
8 |4| 4 6 |8| 9
1 3 4 7 8 7
我们现在找到每个块的平均值。对于第一个块的平均值,我们得到:
(7 + 5 + 4 + 3 + 10 + 8 + 3 + 7 + 5) / 9 = 5.7778
如果您对其余的块重复此操作,我们会得到以下输出图像:
5.7778 4.8889
4.0000 7.5556
既然我们已经建立了基础,那么您可以通过多种方式做到这一点。最规范的方式是你提到的。查看每个不同的块,找到平均值并将其写入输出图像中的写入位置。假设您的图像存储在A:
A = imread('...'); %// Read in the image
k = 3; %// Change to whatever suits your needs
rows = size(A,1); cols = size(A,2); %// Get rows and columns of the image
channels = size(A,3); %// Total number of channels
%// Pad the image so that boxes at the end have zeroes
Apad = zeros(ceil(rows/k)*k, ceil(cols/k)*k, channels);
Apad(1:rows, 1:cols, :) = double(A); %// Cast to double for precision
%// Create output image
B = zeros(ceil(rows/k), ceil(cols/k), channels);
%// Find the average of each block
for ii = 1 : size(B,1)
for jj = 1 : size(B,2)
for kk = 1 : size(B,3)
block = Apad((ii-1)*k + 1 : ii*k, (jj-1)*k + 1 : jj*k, kk);
B(ii,jj,kk) = mean(block(:));
end
end
end
%// Convert output image back to original input type
B = cast(B, class(A));
代码非常不言自明。首先,读入图像,选择k的值,然后得到行、列和通道数。然后我们创建一个新的填充图像,以防行和列不能被k 整除。然后我们将原始图像放在这个新的填充图像中,并将图像投射到double 以保持分割精度。
我们还创建了一个大小合适的输出图像,以适应每个输出k x k 块。之后,我们循环并选择每个不同的块并找到平均值。以我们如何索引填充图像以获得正确的块为例。
完成此平均后,将输出图像转换回原始图像类型非常重要。如果你不这样做,那么使用imshow 之类的东西来显示图像会将许多像素渲染为只有黑色和白色,因为imshow 期望动态范围在 0 和 1 之间。
但是,我们可以通过更有效的方式来做到这一点。如果你刚开始,一定要保留上面的代码,但我解决这个问题的一种方法是使用im2col。这里会发生的是像素邻域以列优先格式构建,因此每个像素邻域的列都堆叠成一列。您将获取所有这些堆叠的列并将它们放入 2D 矩阵中。在我们的例子中,行数将是 9(即3 x 3),而我们将拥有与有效图像块一样多的列。
如何获得块再次采用列主要格式。从图像的左上角开始,3 x 3 像素邻域沿着行向下聚集。一旦我们到达矩阵的底部,我们就会移动到下一列,然后再次沿着行向下移动。 im2col 工作方式的这种行为对于这种平均工作至关重要。
一旦我们得到这个矩阵,只需找到每列的平均值,这将产生一个向量,然后reshape 将其返回到所需的输出矩阵。
想到这样的事情。请注意,大部分代码与我们设置所需的代码相同:
A = imread('...'); %// Read in the image
k = 3; %// Change to whatever suits your needs
rows = size(A,1); cols = size(A,2); %// Get rows and columns of the image
channels = size(A,3); %// Total number of channels
%// Pad the image so that boxes at the end have zeroes
Apad = zeros(ceil(rows/k)*k, ceil(cols/k)*k, channels);
Apad(1:rows, 1:cols, :) = double(A); %// Cast to double for precision
%// Create output image
B = zeros(ceil(rows/k), ceil(cols/k), channels);
%// Do the average
for ii = 1 : channels
M = im2col(Apad(:,:,ii), [k k], 'distinct');
B(:,:,ii) = reshape(mean(M,1), [size(B,1), size(B,2)]);
end
%// Convert output image back to original input type
B = cast(B, class(A));
请注意,我仍然需要遍历每个通道,因为 im2col 只接受 2D 矩阵,因此我们必须逐个平面地访问图像。
更短,您可以使用blockproc:
B = blockproc(Apad, [3 3], @(x) mean(mean(x.data,2),1));
总而言之,您可以尝试多种方法。只是实验!