我一直在阅读有关使用k x k框进行图像压缩的内容,其中指出:
我们将图像分割成
k x k框,并通过获取每个块上的平均值形成一个新的图像。这样,我们就可以将文件大小从MN减少到MN/k^2像素。
我正在努力想象如何编写我的算法。我是否必须先在k像素的x方向上循环,然后在y方向上循环,然后在x方向上选择3 x 3,然后在点上放置一个平均值?
这听起来像解决方案吗?如果有人能写出一个伪算法,我会很高兴的。
发布于 2015-12-20 07:25:47
让我感到惊讶的是,文件大小从MN框减少到MN/k^2框。这意味着您要选择k x k 不同的框,查找每个块的平均值,然后将每个块的中心设置为平均值。
让我们从一个小的数值例子开始。假设我有下面的6x6灰度图像,让我们制作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框。你必须在垂直和水平的循环和收集3x3盒。这意味着你总共会得到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));总之,有很多方法你可以尝试。只是做个实验!
https://stackoverflow.com/questions/34378553
复制相似问题