【问题标题】:Average image compression平均图像压缩
【发布时间】:2015-12-20 06:32:24
【问题描述】:

我一直在阅读有关使用 k x k 框的图像压缩的信息:

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

我正在尝试想象如何编写我的算法。我是否必须先在x 方向上循环k 像素,然后在y 方向上循环,比如说在x 方向上选择3 x 3 并将平均值放在该位置?

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

【问题讨论】:

    标签: matlab image-processing signal-processing


    【解决方案1】:

    让我知道的是将文件大小从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));
    

    总而言之,您可以尝试多种方法。只是实验!

    【讨论】:

    • 很好的解释!但结果证明只有带有经典 lenna 图像的白色。
    • @thirdknife 那是因为我忘记将图像转换回uint8。我需要编辑我的帖子。稍等片刻。
    猜你喜欢
    • 2011-03-09
    • 2013-11-26
    • 2020-02-29
    • 1970-01-01
    • 2013-08-07
    • 1970-01-01
    • 2017-02-19
    • 1970-01-01
    相关资源
    最近更新 更多