如何对 python 图像(使用 numpy、opencv 或 PIL)的每个 PIXEL(不是每个 RGB 组件!)应用操作?

gui*_*ges 3 python opencv numpy image-processing python-imaging-library

我试图以有效的方式对图像的所有像素应用一个函数(在我的具体情况下,我想对每个像素进行一些颜色近似,但我认为这与问题无关)。问题是,我找到了不同的方法来做到这一点,但它们都在像素的每个组件上应用一个函数,而我想做的是应用一个接收像素(而不是像素组件)的函数,这是 3 个 RGB 组件(我猜是一个元组,但只要我的函数中将这 3 个组件作为参数,我就不关心格式)。

如果您对我所拥有的感兴趣,这是解决我的问题的低效解决方案(工作正常,但太慢):

def closest_colour(pixel, colours):
    closest_colours = sorted(colours, key=lambda colour: colours_distance(colour, pixel))
    return closest_colours[0]

# reduces the colours of the image based on the results of KMean
# img is image open with opencv.imread()
# colours is an array of colours
def image_color_reduction(img, colours):
    start = time.time()
    print("Color reduction...")
    reduced_img = img.copy()[...,::-1]
    width = reduced_img.shape[0]
    height = reduced_img.shape[1]
    
    for x in range(width):
        for y in range(height):
            reduced_img[x,y] = closest_colour(reduced_img[x,y], colours)
    
    end = time.time()
    print(f"Successfully reduced in {end-start} seconds")
    return reduced_img
Run Code Online (Sandbox Code Playgroud)

我关注了这篇文章: PIL - 对每个看起来非常清晰且与我的问题一致的像素应用相同的操作。我尝试过使用任何类型的图像格式,我尝试过多线程(都使用 pool.map 和 pool.imap),我尝试过 numpy.apply_along_axis 最后尝试了 PIL.point(),我认为是与我正在寻找的最相似的解决方案。事实上,如果你看一下他们的官方文档:.point(),它准确地说:该函数为每个可能的像素值调用一次。我发现这确实具有误导性,因为在尝试之后,我意识到 这种情况下的像素值并不是指 RGB 元组,而是指 3 个 RGB 组件中的每一个(说真的,在哪个世界?)。

如果有人能分享一些他们的经验并给我一些关于这个问题的启发,我将非常感激。先感谢您!!

(更新)

根据您的要求,我添加有关我正在解决的具体问题的更多信息:

给定

  • 尺寸为 1022*1080 的图像 M
  • 颜色数组 N,大小为 1 < |N| < 16

通过用 N 中最相似的颜色替换每个像素的颜色来减少 M 的颜色(感谢您的回答,我知道这被定义为最近邻颜色量化)

这是缺少的 colors_distance 实现:

def colours_distance(c1, c2):
    (r1,g1,b1) = c1
    (r2,g2,b2) = c2
    return math.sqrt((r1 - r2)**2 + (g1 - g2) ** 2 + (b1 - b2) **2)
Run Code Online (Sandbox Code Playgroud)

这是运行此代码所需的导入:

import cv2
import time
import math
Run Code Online (Sandbox Code Playgroud)

我的问题中显示的解决方案平均在略小于 40 秒的时间内解决了所描述的问题。

Mad*_*ist 7

假设你的图像是一个(M, N, 3)numpy 数组,你的颜色表是(K, 3),并且你将颜色距离测量为一些合理的向量范数,你可以使用 scipy 的cKDTree(或只是KDTree)来优化和向量化查找。

首先从颜色表中创建一棵树:

colors = ... # K, 3 array
color_tree = cKDTree(colors)
Run Code Online (Sandbox Code Playgroud)

现在你可以query直接树来获取输出图像:

_, output = color_tree.query(img)
Run Code Online (Sandbox Code Playgroud)

output将是一个(M, N)索引数组color_table。重要的是 KD 树经过优化以执行O(log K)每个像素的查找,而不是O(K)O(K log K)像您当前的实现那样。由于循环是用 C 语言实现的,因此您也会从中获得很大的提升。