【问题标题】:Fast Way to slice image into overlapping patches and merge patches to image将图像切成重叠的补丁并将补丁合并到图像的快速方法
【发布时间】:2013-05-22 08:49:23
【问题描述】:

尝试将大小为 100x100 的灰度图像分割成大小为 39x39 的重叠块,步幅大小为 1。这意味着下一个从右侧/或下方开始一个像素的块仅与另一列/或行中的上一个补丁。

代码粗略概述:首先计算每个补丁的索引,以便能够 从图像构造二维补丁数组,并能够从补丁构造图像:

patches = imgFlat[ind]

'patches' 是一个二维数组,每列包含一个向量形式的补丁。

处理这些补丁,每个补丁单独并随后再次合并到图像中,并使用预先计算的索引。

img = np.sum(patchesWithColFlat[ind],axis=2)

由于补丁重叠,最后需要将 img 与预先计算的权重相乘:

imgOut = weights*imgOut

我的代码真的很慢,速度是一个关键问题,因为这应该在 ca. 10^8 个补丁。

函数 get_indices_for_un_patchify 和 weights_unpatchify 可以预先计算一次,因此速度只是 patchify 和 unpatchify 的问题。

感谢任何提示。

卡洛斯

import numpy as np
import scipy
import collections
import random as rand


def get_indices_for_un_patchify(sImg,sP,step):
    ''' creates indices for fast patchifying and unpatchifying

    INPUTS:
      sx    image size
      sp    patch size
      step  offset between two patches (default == [1,1])

      OUTPUTS:
       patchInd             collection with indices
       patchInd.img2patch   patchifying indices
                            patch = img(patchInd.img2patch);
       patchInd.patch2img   unpatchifying indices

    NOTE: * for unpatchifying necessary to add a 0 column to the patch matrix
          * matrices are constructed row by row, as normally there are less rows than columns in the 
            patchMtx
     '''
    lImg = np.prod(sImg)
    indImg = np.reshape(range(lImg), sImg)

    # no. of patches which fit into the image
    sB = (sImg - sP + step) / step

    lb              = np.prod(sB)
    lp              = np.prod(sP)
    indImg2Patch    = np.zeros([lp, lb])
    indPatch        = np.reshape(range(lp*lb), [lp, lb])

    indPatch2Img = np.ones([sImg[0],sImg[1],lp])*(lp*lb+1)

    # default value should be last column
    iRow   = 0;
    for jCol in range(sP[1]):
        for jRow in range(sP[0]):
            tmp1 = np.array(range(0, sImg[0]-sP[0]+1, step[0]))
            tmp2 = np.array(range(0, sImg[1]-sP[1]+1, step[1]))
            sel1                    = jRow  + tmp1
            sel2                    = jCol  + tmp2
            tmpIndImg2Patch = indImg[sel1,:]          
            # do not know how to combine following 2 lines in python
            tmpIndImg2Patch = tmpIndImg2Patch[:,sel2]
            indImg2Patch[iRow, :]   = tmpIndImg2Patch.flatten()

            # next line not nice, but do not know how to implement it better
            indPatch2Img[min(sel1):max(sel1)+1, min(sel2):max(sel2)+1, iRow] = np.reshape(indPatch[iRow, :, np.newaxis], sB)
            iRow                    += 1

    pInd = collections.namedtuple
    pInd.patch2img = indPatch2Img
    pInd.img2patch = indImg2Patch

    return pInd

def weights_unpatchify(sImg,pInd):
    weights = 1./unpatchify(patchify(np.ones(sImg), pInd), pInd)
    return weights

# @profile
def patchify(img,pInd):
    imgFlat = img.flat
   # imgFlat = img.flatten()
    ind = pInd.img2patch.tolist()
    patches = imgFlat[ind]

    return patches

# @profile
def unpatchify(patches,pInd):
    # add a row of zeros to the patches matrix    
    h,w = patches.shape
    patchesWithCol = np.zeros([h+1,w])
    patchesWithCol[:-1,:] = patches
    patchesWithColFlat = patchesWithCol.flat
   #  patchesWithColFlat = patchesWithCol.flatten()
    ind = pInd.patch2img.tolist()
    img = np.sum(patchesWithColFlat[ind],axis=2)
    return img

我在这里调用这些函数,例如随机图片

if __name__ =='__main__':
    img = np.random.randint(255,size=[100,100])
    sImg = img.shape
    sP = np.array([39,39])  # size of patch
    step = np.array([1,1])  # sliding window step size
    pInd = get_indices_for_un_patchify(sImg,sP,step)
    patches = patchify(img,pInd)
    imgOut = unpatchify(patches,pInd)
    weights = weights_unpatchify(sImg,pInd)
    imgOut = weights*imgOut

    print 'Difference of img and imgOut = %.7f' %sum(img.flatten() - imgOut.flatten())

【问题讨论】:

  • 补丁需要做什么?也许整个事情可以作为卷积来完成。您的patchify 函数几乎可以免费完成,但我不确定它是否相反。
  • 补丁用于训练神经网络。100x100 的图像被分割成 39x39 的补丁。这些补丁被输入神经网络。然后将神经网络的输出(也是补丁)合并在一起以再次获得 100x100(或更小)的图像。这必须使用不同的 100x100 图像多次完成。所以我真的很感兴趣你认为如何加速 patchify 功能,“免费”做?
  • 你为什么不使用 scikit 的“从 2D 函数中提取补丁”?它只给出重叠的补丁。对于重建,有“从补丁 2d 重建”功能。

标签: python numpy slice


【解决方案1】:

一个有效的“patchify”数组的方法,也就是得到一个windows数组到原来的数组是用自定义strides创建一个视图,跳转到后面元素的字节数。将 numpy 数组视为(美化的)内存块可能会有所帮助,然后 strides 是将索引映射到内存地址的一种方式。

例如,在

a = np.arange(10).reshape(2, 5)

a.itemsize 等于 4(即每个元素 4 个字节或 32 位),a.strides(20, 4)(5 个元素,1 个元素),因此 a[1,2] 指的是 1*20 + 2*4 字节的元素(或1*5 + 2 元素)在第一个之后:

0 1 2 3 4
5 6 7 x x

实际上,元素是一个接一个地放在内存中的,0 1 2 3 4 5 6 7 x x 但是跨步让我们将其索引为二维数组。

基于这个概念,我们可以重写patchify如下

def patchify(img, patch_shape):
    img = np.ascontiguousarray(img)  # won't make a copy if not needed
    X, Y = img.shape
    x, y = patch_shape
    shape = ((X-x+1), (Y-y+1), x, y) # number of patches, patch_shape
    # The right strides can be thought by:
    # 1) Thinking of `img` as a chunk of memory in C order
    # 2) Asking how many items through that chunk of memory are needed when indices
    #    i,j,k,l are incremented by one
    strides = img.itemsize*np.array([Y, 1, Y, 1])
    return np.lib.stride_tricks.as_strided(img, shape=shape, strides=strides)

这个函数返回一个img 的视图,所以没有分配内存,它只运行了几十微秒。输出的形状并不是你想要的,实际上它必须被复制才能得到那个形状。

在处理比基本数组大得多的数组视图时必须小心,因为操作会触发需要分配大量内存的副本。在你的情况下,由于数组不是太大并且没有那么多补丁,应该没问题。

最后,我们可以稍微梳理一下补丁数组:

patches = patchify(img, (39,39))
contiguous_patches = np.ascontiguousarray(patches)
contiguous_patches.shape = (-1, 39**2)

这不会重现您的 patchify 函数的输出,因为您是按 Fortran 顺序开发补丁的。我建议你改用这个,因为

  1. 它会导致以后更自然的索引(即,第一个补丁是您的解决方案的补丁[0] 而不是补丁[:, 0])。

  2. 在 numpy 中使用 C 排序也更容易,因为您需要更少的输入(避免使用 order='F' 之类的东西,数组默认按 C 顺序创建...)。

“提示”以防你坚持:strides = img.itemsize * np.array([1, Y, Y, 1]),在contiguous_patches 上使用.reshape(..., order='F'),最后转置.T

【讨论】:

  • 非常感谢。从那个关于python的答案中学到了很多东西。 @joregca:真的超级快。我会做功课:) 并用 Fortran 顺序发布解决方案。对于那些不立即理解发布的答案的人(我没有),请查看 help(np.lib.stride_tricks.as_strided) 和 another question at stackoverflow 以了解有关 stride_tricks 的更多信息。
  • 但是任何人都知道如何使 unpatchify 功能更快?
  • @user2425142 我扩展了我的答案,我希望现在更清楚了。不过,对于逆问题,我没有类似的解决方案。我不确定将两个问题放在一起是否更好,或者您应该将它们分开。
  • 感谢扩展。我在stackoverflow上发布了另一个问题,仅关于合并部分。希望有人知道一种快速的方法。然后我将编辑这个问题并包含快速合并部分并关闭这两个问题。
  • @user2425142 太好了。我认为最好有针对性的问题。顺便说一句,欢迎来到stackoverflow。您可以为您认为有用的问题和答案投票,也可以accept answers 回答您的问题。
猜你喜欢
  • 2021-02-01
  • 2019-10-22
  • 2012-05-10
  • 2020-09-29
  • 2016-10-07
  • 2016-10-20
  • 1970-01-01
  • 2020-04-07
  • 2016-11-16
相关资源
最近更新 更多