理解tf.extract_image_patches用于从图像中提取补丁

33

我在TensorFlow API中找到了以下方法tf.extract_image_patches,但我不清楚其功能。

假设 batch_size = 1,图像大小为 225x225x3,我们想要提取尺寸为 32x32 的补丁。

这个函数的具体行为是什么?具体来说,文档提到输出张量的维度应该是 [batch, out_rows, out_cols, ksize_rows * ksize_cols * depth],但未提及 out_rowsout_cols 是什么。

理想情况下,给定一个尺寸为 1x225x225x3(其中1是批处理大小)的输入图像张量,我希望能够获得 Kx32x32x3 作为输出,其中 K 是补丁的总数,32x32x3 是每个补丁的维度。在TensorFlow中是否已经有实现这个功能的方法?


链接失效.... - SmarthBansal
请注意,此函数现已迁移到https://www.tensorflow.org/api_docs/python/tf/image/extract_patches。 - SmarthBansal
3个回答

55

以下是该方法的工作原理:

  • ksizes 用于决定每个补丁的尺寸,或者换句话说,每个补丁应包含多少像素。

  • strides 表示在原始图像中,从一个补丁的起始位置到下一个连续补丁的起始位置之间的间距长度。

  • rates 是一个数字,实际上意味着我们的补丁在原始图像中应该跳过rates个像素,才会有后续的像素进入我们的补丁。(下面的例子可以帮助说明这一点。)

  • padding 要么是"VALID",这意味着每个补丁必须完全包含在图像中,要么是"SAME",这意味着允许补丁不完整(剩余的像素将被填充为零)。

以下是一些示例代码和输出,以帮助演示其工作原理:

import tensorflow as tf

n = 10
# images is a 1 x 10 x 10 x 1 array that contains the numbers 1 through 100 in order
images = [[[[x * n + y + 1] for y in range(n)] for x in range(n)]]

# We generate four outputs as follows:
# 1. 3x3 patches with stride length 5
# 2. Same as above, but the rate is increased to 2
# 3. 4x4 patches with stride length 7; only one patch should be generated
# 4. Same as above, but with padding set to 'SAME'
with tf.Session() as sess:
  print tf.extract_image_patches(images=images, ksizes=[1, 3, 3, 1], strides=[1, 5, 5, 1], rates=[1, 1, 1, 1], padding='VALID').eval(), '\n\n'
  print tf.extract_image_patches(images=images, ksizes=[1, 3, 3, 1], strides=[1, 5, 5, 1], rates=[1, 2, 2, 1], padding='VALID').eval(), '\n\n'
  print tf.extract_image_patches(images=images, ksizes=[1, 4, 4, 1], strides=[1, 7, 7, 1], rates=[1, 1, 1, 1], padding='VALID').eval(), '\n\n'
  print tf.extract_image_patches(images=images, ksizes=[1, 4, 4, 1], strides=[1, 7, 7, 1], rates=[1, 1, 1, 1], padding='SAME').eval()

输出:

[[[[ 1  2  3 11 12 13 21 22 23]
   [ 6  7  8 16 17 18 26 27 28]]

  [[51 52 53 61 62 63 71 72 73]
   [56 57 58 66 67 68 76 77 78]]]]


[[[[  1   3   5  21  23  25  41  43  45]
   [  6   8  10  26  28  30  46  48  50]]

  [[ 51  53  55  71  73  75  91  93  95]
   [ 56  58  60  76  78  80  96  98 100]]]]


[[[[ 1  2  3  4 11 12 13 14 21 22 23 24 31 32 33 34]]]]


[[[[  1   2   3   4  11  12  13  14  21  22  23  24  31  32  33  34]
   [  8   9  10   0  18  19  20   0  28  29  30   0  38  39  40   0]]

  [[ 71  72  73  74  81  82  83  84  91  92  93  94   0   0   0   0]
   [ 78  79  80   0  88  89  90   0  98  99 100   0   0   0   0   0]]]]

因此,例如,我们的第一个结果看起来像以下内容:

 *  *  *  4  5  *  *  *  9 10 
 *  *  * 14 15  *  *  * 19 20 
 *  *  * 24 25  *  *  * 29 30 
31 32 33 34 35 36 37 38 39 40 
41 42 43 44 45 46 47 48 49 50 
 *  *  * 54 55  *  *  * 59 60 
 *  *  * 64 65  *  *  * 69 70 
 *  *  * 74 75  *  *  * 79 80 
81 82 83 84 85 86 87 88 89 90 
91 92 93 94 95 96 97 98 99 100 

如您所见,我们有2行2列的补丁,这就是out_rowsout_cols的含义。


[[[[1 2 3 11 12 13 21 22 23] [6 7 8 16 17 18 26 27 28]]为什么从第一个示例的11跳到21有很大的差距?我猜测步幅加倍了(5,5)? - Moondra
我在尝试从我的原始图像中提取4个补丁时遇到了问题(链接),你能帮我解决吗? - Luciano Dourado

11

介绍

在这里,我想展示一个相当简单的演示,使用tf.image.extract_patches来处理图像本身。我发现实际应用该方法并进行适当可视化的实现很少,所以在这里呈现。

我们将使用大小为(256, 256, 3)的图像。我们将提取的补丁的形状为(128, 128, 3)。这意味着我们将从图像中检索4个平铺。

使用的数据

我将使用花卉数据集。由于这个答案需要一些数据管道,因此我将在这里链接我的kaggle内核,它讨论了如何使用tf.data.Dataset API消耗数据集。

完成后,我们将浏览以下代码片段。

images, _ = next(iter(train_ds.take(1)))

image = images[0]
plt.imshow(image.numpy().astype("uint8"))

The flower

在这里,我们从图像批次中选择一张图像,并将其直接可视化。

image = tf.expand_dims(image,0) # To create the batch information
patches = tf.image.extract_patches(images=image,
                                   sizes=[1, 128, 128, 1],
                                   strides=[1, 128, 128, 1],
                                   rates=[1, 1, 1, 1],
                                   padding='VALID')

使用这个代码片段,我们从尺寸为(256,256)的图像中提取大小为(128,128)的补丁。这直接意味着我希望将图像分成4个块。

可视化

plt.figure(figsize=(10, 10))
for imgs in patches:
    count = 0
    for r in range(2):
        for c in range(2):
            ax = plt.subplot(2, 2, count+1)
            plt.imshow(tf.reshape(imgs[r,c],shape=(128,128,3)).numpy().astype("uint8"))
            count += 1

Splits of the flower


7

为了进一步说明Neal详细的答案,使用“SAME”时,零填充存在许多微妙之处,因为extract_image_patches会尽可能地将补丁居中于图像。根据步幅,顶部和左侧可能有填充,也可能没有,并且第一个补丁不一定从左上角开始。

例如,扩展先前的示例:

print tf.extract_image_patches(images, [1, 3, 3, 1], [1, n, n, 1], [1, 1, 1, 1], 'SAME').eval()[0]

当步长为n=1时,图像四周都用0填充,第一个补丁从填充开始。其他步幅仅在右侧和底部或根本不填充图像。

当步长为n=10时,单个补丁从元素34(图像中间)开始。

tf.extract_image_patches是由Eigen库实现的,如此答案所述。您可以研究该代码以了解如何计算补丁位置和填充。


该链接已失效。正确的链接可能是:https://github.com/eigenteam/eigen-git-mirror/blob/master/unsupported/Eigen/CXX11/src/Tensor/TensorImagePatch.h#L120 但我在那里没有看到真正的实现。 - SleepProgger
1
@SleepProgger,我已经更新了答案,用代码位置的信息替换了损坏的链接。 - Ken Shirriff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接