首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >平均图像压缩

平均图像压缩
EN

Stack Overflow用户
提问于 2015-12-20 06:32:24
回答 1查看 252关注 0票数 2

我一直在阅读有关使用k x k框进行图像压缩的内容,其中指出:

我们将图像分割成k x k框,并通过获取每个块上的平均值形成一个新的图像。这样,我们就可以将文件大小从MN减少到MN/k^2像素。

我正在努力想象如何编写我的算法。我是否必须先在k像素的x方向上循环,然后在y方向上循环,然后在x方向上选择3 x 3,然后在点上放置一个平均值?

这听起来像解决方案吗?如果有人能写出一个伪算法,我会很高兴的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-12-20 07:25:47

让我感到惊讶的是,文件大小从MN框减少到MN/k^2框。这意味着您要选择k x k 不同的框,查找每个块的平均值,然后将每个块的中心设置为平均值。

让我们从一个小的数值例子开始。假设我有下面的6x6灰度图像,让我们制作k = 3,这意味着您需要3 x 3不同的框:

代码语言:javascript
复制
 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

代码语言:javascript
复制
 7     5     4
 3    10     8
 3     7     5

方框2

代码语言:javascript
复制
 8     7     1
 2     9     2
 2     8     5

方框3

代码语言:javascript
复制
 6     5     1
 8     4     4
 1     3     4

方框4

代码语言:javascript
复制
 6     7    10
 6     8     9
 7     8     7

若要计算输出图像,请查找每个块的平均值,然后将平均值写入输出图像中的新位置。6 x 6图像现在简化为6 / 3 x 6 / 3 = 2 x 2,其中每个位置都查找每个不同图像块的平均值。中心地点标明如下:

代码语言:javascript
复制
 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

我们现在找出每个街区的平均值。对于第一个街区的平均值,我们得到:

代码语言:javascript
复制
(7 + 5 + 4 + 3 + 10 + 8 + 3 + 7 + 5) / 9 = 5.7778

如果对其余的块重复此操作,我们将得到以下输出映像:

代码语言:javascript
复制
5.7778    4.8889
4.0000    7.5556

既然我们已经建立了基本原理,就有很多方法可以做到这一点。最典型的方式就是你提到的。查看每个不同的块,找到平均值并将其写入输出映像中的写入位置。假设您的图像存储在A中,这样的东西可能就是您要寻找的

代码语言:javascript
复制
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返回到期望的输出矩阵中。

像这样的事情在脑海里浮现。请注意,大多数代码与安装所需的代码相同:

代码语言:javascript
复制
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完成这一任务。

代码语言:javascript
复制
B = blockproc(Apad, [3 3], @(x) mean(mean(x.data,2),1));

总之,有很多方法你可以尝试。只是做个实验!

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34378553

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档