使用Python查找具有相似色彩盘的图像
假设在一个画廊里有10,000张JPEG和PNG格式的图片,怎么找到和选定的图片颜色搭配相似的所有图片,并按相似度从高到低排序呢?
2 个回答
0
另一种解决方案是对每个调色板进行K均值聚类,以便将颜色分组,然后根据颜色的色调对每个调色板进行排序,最后使用余弦相似度来找到最相似的图像。
下面是一段代码,可以在一堆图像中找到与参考图像最相似的图像:
from PIL import Image
import os
import numpy as np
from sklearn.cluster import MiniBatchKMeans
from numpy.linalg import norm
from tqdm import tqdm
from skimage.color import rgb2hsv
def extract_palette(image, num_colors):
image_array = np.array(image)
pixels = image_array.reshape((-1, 3))
kmeans = MiniBatchKMeans(n_clusters=num_colors, n_init='auto')
kmeans.fit(pixels)
colors = kmeans.cluster_centers_
return colors
def order_vector_by_hue(colors):
hsv_colors = rgb2hsv(np.array([colors]))
ordered_indices = np.argsort(hsv_colors[:, :, 0])
ordered_rgb_colors = colors[ordered_indices]
return ordered_rgb_colors
def cosine_sim(u, v):
return np.dot(u, v) / (norm(u) * norm(v))
if __name__ == "__main__":
ref_image_path = '<your-path>'
folder = '<your-image-folder>'
files = os.listdir(folder)
print('processing ref image')
image = Image.open(image_path)
ref_colors = extract_palette(image, num_colors=32)
ref_colors = order_vector_by_hue(colors).reshape(-1)
print('processing candidate images')
selected_image_path = None
max_similarity = -1 # value for the most dissimilar possible image
for image_path in files:
image = Image.open(image_path)
colors = extract_palette(image, num_colors=32)
colors = order_vector_by_hue(colors).reshape(-1)
similarity = cosine_sim(ref_colors, colors)
if similarity > max_similarity:
max_similarity = similarity
selected_image_path = image_path
print(f'most similar image: {selected_image_path}')
补充:可能还有其他方法可以改进这一切。如果我有时间,我会尝试使用主成分分析(PCA)来压缩调色板,并使用Lab颜色空间来排序(和压缩?)这些向量。这个解决方案对我来说现在已经足够好了。对于一万张图像来说,这可能会比较慢(K均值聚类)。而我的使用场景只有几百张图像。
补充2:一个快速的替代聚类的方法是随机选择我们像素中的num_colors。根据你的使用场景,这可能已经足够了。
def extract_palette(image, num_colors):
image_array = np.array(image)
pixels = image_array.reshape((-1, 3))
selected_colors = pixels[np.random.choice(len(pixels), num_colors, replace=False)]
return selected_colors
11
为每张图片建立一个颜色直方图。然后,当你想把一张图片和其他图片进行匹配时,只需根据它们的直方图与选定图片的直方图的相似度来排序即可。
桶的数量取决于你想要的准确度。用来组成一个桶的数据类型将决定你搜索的优先级。
举个例子,如果你最关心的是色调(颜色的种类),那么你可以定义每个像素应该放入哪个桶,方法是:
def bucket_from_pixel(r, g, b):
hue = hue_from_rgb(r, g, b) # [0, 360)
return (hue * NUM_BUCKETS) / 360
如果你还想要一个更通用的匹配器,那么你可以根据完整的RGB值来选择桶。
使用PIL库,你可以使用内置的histogram
函数。直方图的“相似度”可以用你想要的任何距离度量来计算。例如,L1距离可以表示为:
hist_sel = normalize(sel.histogram())
hist = normalize(o.histogram()) # These normalized histograms should be stored
dist = sum([abs(x) for x in (hist_sel - hist)])
L2距离则可以表示为:
dist = sqrt(sum([x*x for x in (hist_sel - hist)]))
Normalize
的作用是强制直方图的总和等于某个常数值(1.0就很好用)。这很重要,因为这样大图片和小图片才能正确比较。如果你打算使用L1距离,那么在normalize
中也应该使用L1度量;如果是L2,那么就用L2。