我有一個 RGB 位元組的 NumPy 影像,假設它是這個 2x3 影像:
img = np.array([[[ 0, 255, 0], [255, 255, 255]],
[[255, 0, 255], [ 0, 255, 255]],
[[255, 0, 255], [ 0, 0, 0]]])
我還有一個調色板,涵蓋了影像中使用的每種顏色。假設它是這個調色板:
palette = np.array([[255, 0, 255],
[ 0, 255, 0],
[ 0, 255, 255],
[ 0, 0, 0],
[255, 255, 255]])
是否有一些將影像索引到調色板的組合(反之亦然)會給我一個與此等效的調色板影像?
img_p = np.array([[1, 4],
[0, 2],
[0, 3]])
為了比較,我知道反過來很簡單。 palette[img_p]將給出等效于 的結果img。我試圖弄清楚是否有相反方向的類似方法可以讓 NumPy 完成所有繁重的作業。
我知道我可以單獨迭代所有影像像素并構建我自己的調色板影像。我希望有一個更優雅的選擇。
好的,所以我實作了下面的各種解決方案,并在一個中等的測驗集上運行它們:20 張影像,每張 2000x2000 像素,帶有 32 元素的三位元組顏色調色板。像素被賦予隨機調色板索引。所有演算法都在相同的影像上運行。
計時結果:
- 大部分為空的查找陣列- 0.89 秒
- np.searchsorted 方法- 3.20 秒
- Pandas 查找,單個整數- 38.7 秒
- 使用 == 然后聚合布爾結果- 66.4 秒
- 將調色板反轉為字典并使用 np.apply_along_axis() - 大約 500 秒,基于較小的測驗集
- 使用 MultiIndex 進行 Pandas 查找- 大約 3000 秒,基于較小的測驗集
鑒于查找陣列具有顯著的記憶體損失(如果有 alpha 通道,則會受到禁止),我將采用這種np.searchsorted方法。如果您想在其上花費 RAM,則查找陣列的速度要快得多。
uj5u.com熱心網友回復:
編輯這里是使用np.searchsorted.
def rev_lookup_by_sort(img, palette):
M = (1 palette.max())**np.arange(3)
p1d, ix = np.unique(palette @ M, return_index=True)
return ix[np.searchsorted(p1d, img @ M)]
正確性(等同rev_lookup_by_dict()于下面的原始答案):
np.array_equal(
rev_lookup_by_sort(img, palette),
rev_lookup_by_dict(img, palette),
)
加速(對于 1000 x 1000 影像和 1000 調色板):
orig = %timeit -o rev_lookup_by_dict(img, palette)
# 2.47 s ± 10.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
v2 = %timeit -o rev_lookup_by_sort(img, palette)
# 71.8 ms ± 93.7 μs per loop (mean ± std. dev. of 7 runs, 10 loops each)
>>> orig.average / v2.average
34.46
因此,np.searchsorted在該尺寸下,使用的答案要快 30 倍。
原始答案
最初的鏡頭給出了一個緩慢的版本(希望我們可以做得更好)。它使用 a dict,其中鍵是元組的顏色。
def rev_lookup_by_dict(img, palette):
d = {tuple(v): k for k, v in enumerate(palette)}
def func(pix):
return d.get(tuple(pix), -1)
return np.apply_along_axis(func, -1, img)
img_p = rev_lookup_by_dict(img, palette)
請注意,“未找到顏色”表示-1為img_p。
在您的(修改后的)資料上:
>>> img_p
array([[1, 4],
[0, 2],
[0, 3]])
更大的例子:
# setup
from math import isqrt
w, h = 1000, 1000
s = isqrt(w * h)
palette = np.random.randint(0, 256, (s, 3))
img = palette[np.random.randint(0, s, (w, h))]
測驗:
img_p = rev_lookup_by_dict(img, palette)
>>> np.array_equal(palette[img_p], img)
True
定時:
%timeit rev_lookup_by_dict(img, palette)
# 2.48 s ± 16.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
這很糟糕,但希望我們能做得更好。
uj5u.com熱心網友回復:
比字典快,但具有 64 MB 的查找陣列。
d = np.zeros((256,256,256), np.int32) # 64 MB!
d[tuple(palette.T)] = np.arange(len(palette))
img_p = d[tuple(img.reshape(-1,3).T)].reshape(*img.shape[:2])
# %%timeit 10 loops, best of 5: 25.8 ms per loop (1000 x 1000)
np.testing.assert_equal(img, palette[img_p])
uj5u.com熱心網友回復:
如果除了 NumPy 之外還可以使用 Pandas,則可以使用 Pandas MultiIndex 作為一種稀疏陣列:
inverse_palette = pd.Series(np.arange(len(palette)),
index=pd.MultiIndex.from_arrays(palette.T)).sort_index()
img_p = np.apply_along_axis(lambda px: inverse_palette[tuple(px)], 2, img)
不過,這真的很慢。您可以通過先將顏色轉換為整數來做得更好:
def collapse_bytes(array):
result = np.zeros(array.shape[:-1], np.uint32)
for i in range(array.shape[-1]):
result = result * 256 array[...,i]
return result
inverse_palette = pd.Series(np.arange(len(palette)),
index=collapse_bytes(palette)).sort_index()
img_p = inverse_palette[collapse_bytes(img).flat].to_numpy()\
.reshape(img.shape[:-1])
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/468201.html
上一篇:np.diag不包括陣列中的0
