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 秒的时间内解决了所描述的问题。
假设你的图像是一个(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 语言实现的,因此您也会从中获得很大的提升。