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

Posted

技术标签:

【中文标题】图像的准确颜色量化以最小化调色板【英文标题】:Accurate color quantization of image to minimize color palette 【发布时间】:2021-11-11 18:37:08 【问题描述】:

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

对于量化本身,我使用 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 artifacts”的删除,我目前正在使用img = cv2.bilateralFilter(img, 9, 75, 75),效果很好。

【问题讨论】:

量化基本上是一种聚类技术。有些聚类方法不需要参数k,它们会找到一组“自然”的聚类。例如 DBSCAN:en.wikipedia.org/wiki/DBSCAN 【参考方案1】:

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

【讨论】:

【参考方案2】:

我最终得到了以下函数来确定主色的数量:

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”,它给出了可接受的结果。然而,它看起来还是有点“人造”。

【讨论】:

以上是关于图像的准确颜色量化以最小化调色板的主要内容,如果未能解决你的问题,请参考以下文章

需要对用于迭代颜色量化的调色板数据结构的建议;特别是KD堆的任何经验?

如何使用 OpenCV 减少图像中的颜色数量?

使用 NQUANT 的图像量化误差

在 ImageSharp 中使用调色板/索引图像

bmp图像反色的原理及实现

当调色板发散时,如何使用最小值和最大值设置热图颜色?