【问题标题】:Numpy array - Two unknown dimensions - png filesNumpy 数组 - 两个未知维度 - png 文件
【发布时间】:2020-07-07 01:59:49
【问题描述】:

我有一个数据集,其中包含一堆关于它们的高度和宽度大小不同的 png 文件。

我使用以下代码读入此文件以获取 numpy 数组。在这种情况下,它是 2D。但实际上我想得到一个存在图像数量 n、图像高度 h 和宽度 w 的 3D 数组。

import os.path
import glob
import numpy as np

def open_images(images_directory):
    pattern_to_match = os.path.join(images_directory, "*.png")
    png_files = (x for x in glob.iglob(pattern_to_match)
                 if os.path.isfile(x))
    for current_png_filename in png_files:
        print("Opening file", current_png_filename)
        with open(current_png_filename, "rb") as current_png_file:
            data = current_png_file.read()
            return np.frombuffer(data, dtype=np.uint8, offset=16)\
            .reshape(-1, 3)\
            .astype(np.float32)
            pass

directory_to_search = r"C:\Users\tobis\OneDrive\Desktop\Masterarbeit\data\2017-IWT4S-HDR_LP-dataset\crop_h1"
open_images(directory_to_search)

目前,我得到一个带有 shpae 的数组,如下所示: (21559, 3)。我认为第一个数字是宽度和高度的组合,最后一个是 RGB 值。我想得到一个看起来像这样的数组:(n, h, w)。

有没有办法得到这样的数组?不幸的是,我有两个未知的维度。好像是这个问题……

【问题讨论】:

  • 这:不同尺寸的高度和宽度将是最大的问题。 numpy 数组是统一的,因此您要么必须识别最大的图像,然后对其余图像进行零填充,要么选择另一种结构来存储它们
  • 这不是宽度 * 高度,你很幸运你有 12 个字节的倍数。 PNG 不是没有元数据的平面文件。使用 PIL、scipy、matplotlib、cv2、libpng 或其他一些库来正确读取文件(或实施您自己的本土解决方案,但要正确执行)。
  • 21559 是一个素数,因此它不太可能代表图像。

标签: python arrays image numpy image-processing


【解决方案1】:

如果您有一个展平的图像并希望恢复原始行和列尺寸,您可以应用启发式方法来测试各种可能的组合并检查图像沿行轴的“平滑度”。例如,可以通过连续行的均方误差来检查这种平滑度。这假设原始图像具有某种结构,沿行轴也是如此,因此与其他可能的形状相比,原始图像中连续像素的变化相对较小。

例如,假设原始图像为155 x 79 像素,它已被展平为155 * 79 == 12245 的数组。其主要分解是5, 31, 79。所以可能的行尺寸都是这些主要因素的唯一组合,即5, 31, 79, 155, 395, 2449。现在,这些可能的行维度(以下称为 estimates)可以分为两个不同的类别:

  1. 作为原始行维度除数的估计值:531155。这实际上意味着原始图像的多个跳过行的副本彼此相邻堆叠。因此生成的图像将保留原始列分组。由于相似的列保持在一起,因此堆栈的每个元素将具有大致相同的平滑度。例如,如果估计为31,这意味着原始形状31 x 5 , 79 被转换为31 , 5 x 79,即仅考虑原始图像的每5 行,并且五个这样的副本彼此相邻堆叠。对于原始图像(即155 的估计值),考虑长度-1 相关性(即比较每对连续行),而对于31 的估计值,考虑长度-5 相关性(即比较行对它们之间还有 4 行)。由于原始图像预计具有一些平滑结构,因此当比较较长的范围时,平滑度应该会降低。当跳跃范围增加时,平滑度的下降会更大,但如果图像沿行轴包含某种程度的周期性,它也可能完全消失。
  2. 所有其他估计:79, 155, 395, 2449。对于这个类别的估计,原始图像的不同列混合在对应于估计的测试图像中。例如,如果估计为79,我们有155 % 79 == 76,即测试图像中的每一新行相对于前一行将原始列移动3。假设原始图像沿列维度变化,这些变化将为出现的连续行引入越来越大的偏差。由于该列移位逐行增加,因此行平滑度的下降应该很大,除非行数很少。但是,如果原始图像是列周期性的且具有估计的移位数,则这可能会导致完美的一致性。

总而言之,如果我们计算所有行维度估计的平滑度,我们预计错误估计的平滑度会降低,如果估计属于 (1) 类别,则下降幅度较小,如果属于 (1) 类别,则下降幅度更大。 2).

重要提示:如果图像在行或列维度上是周期性的,则可能导致错误估计。

实施需要涵盖以下步骤:

  1. 计算平面图像长度的素数分解。
  2. 根据主要因素的组合计算所有唯一行维度估计值。
  3. 为每个估计计算生成的测试图像的行平滑度。例如使用连续行的均方误差(实际上这将是一个非平滑度分数)。
  4. 从分数中找到最佳估计值。

下面是一些实现的示例代码:

import itertools as it
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image


image = np.array(Image.open('example.jpg'))
original_shape = image.shape[:2]
image = image.reshape(-1, 3)


def compute_prime_factors(n):
    i = 2
    while i <= n:
        if n % i == 0:
            n //= i
            yield i
        else:
            i += 1

prime_factors = list(compute_prime_factors(len(image)))
combinations = it.chain.from_iterable(it.combinations(prime_factors, r=i) for i in range(1, len(prime_factors)))
row_dims = sorted({np.prod(x) for x in combinations})


def test_row_dim(r):
    c = len(image) // r
    test = image.reshape(r, c, 3)
    return np.mean((test[1:] - test[:-1])**2)


scores = [test_row_dim(r) for r in row_dims]
best_estimate = row_dims[np.argmin(scores)]

fig, ax = plt.subplots()
ax.set(xlabel='row dimension', ylabel='score')
ax.set_xscale('log')
ax.plot(row_dims, scores, '-o', label='Estimations')
ax.plot([best_estimate], [np.min(scores)], '*', ms=12, label=f'Best Estimate ({best_estimate})')
ax.axvline(original_shape[0], label=f'Actual Dim ({original_shape[0]})', color='#2ca02c', zorder=-100, lw=1.5, ls='--')
ax.legend()

plt.figure()
plt.imshow(image.reshape(205, -1, 3))  # second best score

plt.show()

让我们在一些图片上测试一下 (H x W: 410 x 640):


Cameron Venti 在 Unsplash 上的照片

这会产生以下估计分数:

最佳估计左侧的峰值是类别 (1) 的估计,其具有最小的跳行。 410640 的素数分解分别是 2*5*412**7 * 5。所以最接近原始行维度的类别 (1) 估计是2058241(侧峰从右到左)。减少的估计意味着增加的行跳过范围,因此增加的 MSE 分数。最佳估计左侧的峰值对应于205 的估计,即每第二行被跳过,因此两个这样的跳过行的版本彼此相邻堆叠:

您可以想象,通过跳过每隔一行,图像不会发生太大变化,并且两个并排版本的变化是相同的。因此与原始图像的 MSE 分数相差很小。

【讨论】:

  • 引人入胜的阅读。这种方法是您开发的并且出于某种原因有理由经常使用,还是源自您可以披露的其他地方?我很想听听你能告诉我的更多信息……我的名字是 Mark,我的域名是 theSetchells.com,所以你可以很容易地推断出我的电子邮件。作为交换,请参阅此处的第 420-460 行,了解 libTiff 如何执行此操作...gitlab.com/libtiff/libtiff/-/blob/master/tools/raw2tiff.c
  • @MarkSetchell 我以前从未使用过此代码,我只是想出它来回答这个问题(虽然这似乎与一个完全不同的问题有关)。我会在更广泛的图像上对其进行测试,但我不知道任何包含不同尺寸图像的图像数据集。
  • 好的,谢谢您的解释。我经常在 Stack Overflow 上遇到一些人,他们不知道他们的数据的尺寸、每像素字节数或通道顺序,以及发现这些参数的算法/启发式方法让我感兴趣。
【解决方案2】:

您不能只读取这样的图像文件。您需要使用库来读取它并解释高度、宽度、色彩空间、每像素位数、日期、GPS 数据、相机品牌和型号以及所有压缩、编码的像素。

例如,使用 PIL/Pillow:

from PIL import Image 
import numpy as np 

# Open image and make sure it is RGB - not palette 
im = Image.open('image.png').convert('RGB') 

# Make into Numpy array 
na = np.array(im) 

# Check shape
print(na.shape)          # prints (480,640,3) for height, width, channels

【讨论】:

  • 是的,这适用于一张图片!谢谢!但我不明白如何打开多个不同大小的图像。我的目标是获得一个可以用来训练神经网络的数组或数据框。到目前为止,我发现的所有解决方案都是关于具有两个固定尺寸而不是变化尺寸的图像的解决方案......
  • 我对神经网络做的不多,所以我不知道。我猜你的选择可能是用黑色填充(可能使用ImageOps.expand())到数据集中最大项目的大小,或者调整到一个常见的大小。我建议您提出一个新问题以获得最佳答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-07
  • 2021-05-02
  • 2021-08-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多