精确的图像颜色量化以最小化调色板

2024-09-28 23:07:10 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图量化一幅图像,使所有的原色保持在适当的位置,并删除所有的副色,如“抗锯齿”边框。 例如,下面的图像最终应量化为3种颜色,而原始图像中的实际颜色数量超过30种。所有“抗锯齿”边框颜色都应视为次要颜色,并在量化时消除,以及“jpeg伪影”,由于过度优化,这些伪影会为图像添加更多颜色。 注意:源图像可以是png或jpeg

3 color example

对于量化本身,我使用PIL.quantize(...)和K作为要离开的颜色数。它工作得相当好,并保持调色板与原作完美匹配

def color_quantize(path, K):
    image = cv2.imread(path, cv2.IMREAD_UNCHANGED)
    img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    im_pil = Image.fromarray(np.uint8(img))
    im_pil = im_pil.quantize(K, None, 0, None)
    return cv2.cvtColor(np.array(im_pil.convert("RGB")), cv2.COLOR_RGB2BGR) 

因此,如果我事先知道“K”(原色的数量),那么我会将其用于im_pil.quantize(...)。基本上,我需要一种方法来得到那个“K”数字。 有没有办法确定原色的数量

顺便说一句,关于“jpeg伪影”的删除,我目前正在使用img = cv2.bilateralFilter(img, 9, 75, 75),这非常有效


Tags: path图像imageimg数量pil颜色cv2
3条回答

您可以使用K-Means Clustering来确定带有^{}的图像中的主色调。我不确定是否有办法确定原色的“K”数,因为这通常是用户定义的值。此方法提供主色调和百分比分布


输入图像

enter image description here

结果

对于n_clusters=4,以下是最主要的颜色和百分比分布

[ 28.59165576 114.71245821 197.21921791] 4.30%
[ 25.54783639 197.94045711 147.22737091] 17.94%
[197.75739373  25.78053504 143.31173478] 19.04%
[254.75762607 254.77925368 254.85116121] 58.72%

每个颜色簇的可视化(白色不可见)

enter image description here

import cv2, numpy as np
from sklearn.cluster import KMeans

def visualize_colors(cluster, centroids):
    # Get the number of different clusters, create histogram, and normalize
    labels = np.arange(0, len(np.unique(cluster.labels_)) + 1)
    (hist, _) = np.histogram(cluster.labels_, bins = labels)
    hist = hist.astype("float")
    hist /= hist.sum()

    # Create frequency rect and iterate through each cluster's color and percentage
    rect = np.zeros((50, 300, 3), dtype=np.uint8)
    colors = sorted([(percent, color) for (percent, color) in zip(hist, centroids)])
    start = 0
    for (percent, color) in colors:
        print(color, "{:0.2f}%".format(percent * 100))
        end = start + (percent * 300)
        cv2.rectangle(rect, (int(start), 0), (int(end), 50), \
                      color.astype("uint8").tolist(), -1)
        start = end
    return rect

# Load image and convert to a list of pixels
image = cv2.imread('1.png')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
reshape = image.reshape((image.shape[0] * image.shape[1], 3))

# Find and display most dominant colors
cluster = KMeans(n_clusters=4).fit(reshape)
visualize = visualize_colors(cluster, cluster.cluster_centers_)
visualize = cv2.cvtColor(visualize, cv2.COLOR_RGB2BGR)
cv2.imshow('visualize', visualize)
cv2.waitKey()

我使用以下函数来确定主色调的数量:

def get_dominant_color_number(img, threshold):
    # remove significant artifacts
    img = cv2.bilateralFilter(img, 9, 75, 75) 

    # resize image to make the process more efficient on 250x250 (without antialiasing to reduce color space)
    thumbnail = cv2.resize(img, (250, 250), None)

    # convert to HSV color space 
    imghsv = cv2.cvtColor(thumbnail, cv2.COLOR_BGR2HSV).astype("float32")
    (h, s, v) = cv2.split(imghsv)

    # quantize saturation and value to merge close colors
    v = (v // 30) * 30
    s = (s // 30) * 30

    imghsv = cv2.merge([h,s,v])
    thumbnail = cv2.cvtColor(imghsv.astype("uint8"), cv2.COLOR_HSV2BGR)

    (unique, counts) = np.unique(thumbnail.reshape(-1, thumbnail.shape[2]), return_counts=True, axis = 0)

    # calculate frequence of each color and sort them 
    freq = counts.astype("float")
    freq /= freq.sum()
    count_sort_ind = np.argsort(-counts)
    
    # get frequent colors above the specified threshold
    n = 0
    dominant_colors = []
    
    for (c) in count_sort_ind:
        n += 1;
        dominant_colors.append(unique[c])
        if (freq[c] <= threshold):
           break

    return (dominant_colors, n)

#                              -

img = cv2.imread("File.png", cv2.IMREAD_UNCHANGED)
channels = img.shape[2]
if channels == 4:
   trans_mask = img[:,:,3] == 0
   img[trans_mask] = [254, 253, 254, 255]
   img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)

(dom_colors, dom_color_num) = get_dominant_color_number(img, .0045)

对于阈值“.0045”,它给出了一个可接受的结果。然而,它看起来仍然有点“人造”

您可能想尝试分析RGB通道的直方图,以找出它们有多少个峰值,希望您有几个大峰值和一些非常小的峰值,然后大峰值的数量应该是您的K

相关问题 更多 >