在进行像素分割时,我有 256 个不同的 colorID 与某些 RGB 值集匹配,并且需要编写一个脚本来检索分割的 RGB 图片中每个像素的 colorID。将会有很多高分辨率图片,所以我需要一种有效的方法在 python 中实现这一点。我有一个 256x1 png,其中包含 rgb 值作为每个像素的颜色,并且像素从左到右的索引对应于 colorID 值。
我有一个有效的代码,但正如你所看到的,它有一些缺陷。这个想法是将每个图像解构为单个像素并检索每个像素的 RGB 值。然后,这些 RGB 值可以用作索引或键,以使用基于 256x1 png 的转换器检索 colorID。
这是生成从 rgb 到 colorID 的转换器的代码:
def generate_rgb_to_colorID_converter(self):
color_palette = Image.open(path_to_image)
rgb_to_colorID_converter = np.zeros((256, 256, 256), dtype=np.uint8) # rgb values will be used as index and the corresponding colorID will be the elements
for x_coord in range(256):
pixel_coord = (x_coord, 0)
rgb = color_palette.getpixel(pixel_coord) # Returns decimal rgb value at coordinate in a tuple
rgb_to_colorID_converter[rgb[0], rgb[1], rgb[2]] = x_coord # Sets the element at given index in the 3D array to the colorID value
return rgb_to_colorID_converter
这是将 RGB 值转换为其相应的 colorID 的函数:
def rgb_to_colorID(self, rgb_array, rgb_to_colorID_converter):
"""rgb_array[:, :, 0] returns the r-channel for every pixel while the indexes 1 and 2 does the same for g and b
[rgb_array[:, :, 0], rgb_array[:, :, 1], rgb_array[:, :, 2]] then returns the rgb-value at every pixel which is used as index in rgb_to_colorID_converter"""
mapped_image = rgb_to_colorID_converter[rgb_array[:, :, 0], rgb_array[:, :, 1], rgb_array[:, :, 2]]
return mapped_image
您可能会看到,这段代码的问题在于 rgb_to_colorID_converter 包含 256^3 个元素,而其中只有 256 个实际上保存 colorID 值(其余为零)。使用tracemalloc,这个数组似乎使用了大约16 MB的内存,这只是浪费,因为它只用于256个uint8值。在我的完整系统中运行此代码实现平均需要 0.017 秒才能将 1920x1080 图片从 RGB 转换为具有 colorID 值的灰度。
为了消除这种浪费的内存使用,我通过以下方式重写了函数:
def generate_rgb_to_colorID_converter(self):
color_palette = Image.open(path_to_image)
rgb_to_colorID_converter = {}
for x_coord in range(256):
pixel_coord = (x_coord, 0)
rgb = color_palette.getpixel(pixel_coord) # Returns decimal rgb value at coordinate in a tuple
rgb_to_colorID_converter[(rgb[0], rgb[1], rgb[2])] = np.uint8(x_coord) # Sets the element at given index in the 3D array to the colorID value
return rgb_to_colorID_converter
def rgb_to_colorID(self, rgb_array, rgb_to_colorID_converter):
columns = len(rgb_array[0]) # columns av rows of the original array is needed to reshape the colorID array back to the right shape
rows = len(rgb_array)
rgb_array = rgb_array.reshape(-1,3) # Turns the 3D array into a 2D array where each element of the 2D array is a single array containing the rgb values for a single pixel. Makes it easier to loop through the array
colorID_array = np.empty(len(rgb_array), dtype=np.uint8) # will be filled with the colorID values for each pixel
for pixel_index, pixel in enumerate(rgb_array):
colorID_array[pixel_index] = rgb_to_colorID_converter[tuple(pixel)] # indexes in the rgb and colorID arrays correspond. tuple(index) correponds to the key of the dictionary
colorID_array = colorID_array.reshape(rows, columns) # reshapes it back, but its now a 2D array since each pixel only has a single color channel and not 3
return colorID_array
词典的内存占用非常低。但现在的问题是从rgb到colorID数组的转换速度。在整个系统中,for 循环遍历每个像素并将其从 RGB 更改为 colorID 平均需要 8.1 秒。这根本不能用。考虑到这些统计数据,第一个实施是绝对的赢家。
我真正想问的是如何在第一个实现中缩小 rgb_to_colorID_converter 的数组大小或在第二个实现中加速/替换 for 循环。
假设性能问题出在元组索引的字典中,您可以用一些较低级别的实现来替换它。
例如,使用 numpy 数组实现哈希表。
您可能想要生成随机哈希函数,直到找到一个完美的;不确定这个想法有多有用。
以上都是对性能瓶颈在哪里的大胆猜测。如果您不想相信我(您真的不应该)并且想要系统化它,那么您应该使用分析器。在这种情况下,代码非常小,您特别需要一个line profiler来将问题精确定位到代码中的特定位置。