阅读 .PNG,您如何识别颜色簇并重写图像文件,以便每个簇都有唯一的 RGB 代码?

Reading .PNGs, how do you identify clusters of color and rewrite the image file so that every cluster has a unique RGB code?

接此问题:How could you rewrite a list of lists so that "islands" of values are unique from one another?

简要说明:您将如何解析图像,例如:

以这种方式识别不同像素的几个簇并重写文件,以便每个簇都有唯一的颜色,例如:

以下是我在包括 Whosebug 用户@Rabinzel 在内的一些来源的帮助下尝试实现它的方法:(详细推理在主要代码块下方)

    from scipy import ndimage
    import numpy as np
    from PIL import Image
    
    #set the file path to wherever your provinces.png is located
    im = Image.open(r"C:\Users\scoop\Desktop\prov_test.png")
    
    print('-------------------------------------------')
    #DEBUGGING: simply prints the format, size, and mode of your file
    print(im.format, im.size, im.mode)
    #saves the width and depth of the file
    im_xsize = im.size[0]
    im_ysize = im.size[1]
    #DEBUGGING: prints it
    print(im_xsize, im_ysize)
    #DEBUGGNG: prints data bands, should be R, G, B
    print(im.getbands())
    
    #DEBUGGING: prints RGB value of pixel of choice
    print(im.getpixel((0,0)))
    print('-------------------------------------------')
    
    #creates array for pixel RGBs
    rgb_array = [[None] * im_ysize for length in range(0,im_xsize)]
    
    #fills pixel RGB array
    for x in range(0,im_xsize):
        for y in range(0,im_ysize):
            rgb_array[x][y] = im.getpixel((x,y))
       
    #find unique clusters of identical RGB codes
    def find_clusters(array):
        clustered = np.empty_like(array)
        unique_vals = np.unique(array)
        cluster_count = 0
        for val in unique_vals:
            labelling, label_count = ndimage.label(array == val)
            for k in range(1, label_count + 1):
                clustered[labelling == k] = cluster_count
                cluster_count += 1
        return clustered, cluster_count
    
    clusters, cluster_count = find_clusters(rgb_array)
    print("Found {} clusters:".format(cluster_count))
    #print(clusters)
    
    #defining a list of unique colors
    province_color_list = [[0] * 3 for length in range(0,cluster_count)] 
    
    #DEBUGGING
    print('province count...', cluster_count)
    #variables
    r = 255
    g = 0
    b = 0
    count = 0
    
    #generating colors
for length in range(0,cluster_count):
    province_color_list[length][0] = r
    province_color_list[length][1] = g
    province_color_list[length][2] = b
    g += 25
    b += 25
    count += 1
    if count >= 11:
        r -= 1
        g = 0
        b = 0
        count = 0

#DEBUGGING
print('# of colors... ', len(province_color_list))
print(province_color_list)
print('-------------------------------------------')

#writing colors to pixels
for x in range(0,im_xsize):
    for y in range(0,im_ysize):
        #places province color based on which province current pixel is assigned to
        im.putpixel((x,y),   (province_color_list[0][0],   province_color_list[0][1],   province_color_list[0][2]))
         
#im.save(r"C:\Users\scoop\Desktop\prov_test.png", im.format)

我使用 PIL 加载图像:

im = Image.open(r"C:\Users\scoop\Desktop\prov_test.png")

我创建了一个数组以更轻松地(?)访问图像数组,该数组将每个像素的颜色存储为元组形式的 RGB 颜色代码。然后这个方法识别相关的像素簇。

rgb_array = [[None] * im_ysize for length in range(0,im_xsize)]

#fills pixel RGB array
for x in range(0,im_xsize):
    for y in range(0,im_ysize):
        rgb_array[x][y] = im.getpixel((x,y))
   
#find unique clusters of identical RGB codes
def find_clusters(array):
    clustered = np.empty_like(array)
    unique_vals = np.unique(array)
    cluster_count = 0
    for val in unique_vals:
        labelling, label_count = ndimage.label(array == val)
        for k in range(1, label_count + 1):
            clustered[labelling == k] = cluster_count
            cluster_count += 1
    return clustered, cluster_count

clusters, cluster_count = find_clusters(rgb_array)

然后我创建了一个独特的 RGB 代码列表,其长度为存在的像素簇的数量。

province_color_list = [[0] * 3 for length in range(0,cluster_count)] 

#DEBUGGING
print('province count...', cluster_count)
#variables
r = 255
g = 0
b = 0
count = 0

#generating colors
for length in range(0,cluster_count):
    province_color_list[length][0] = r
    province_color_list[length][1] = g
    province_color_list[length][2] = b
    g += 25
    b += 25
    count += 1
    if count >= 11:
        r -= 1
        g = 0
        b = 0
        count = 0

最后,我用与之前唯一簇关联的新 RGB 代码重写每个像素(并保存图像)。

#writing colors to pixels
for x in range(0,im_xsize):
    for y in range(0,im_ysize):
        #places province color based on which province current pixel is assigned to
        im.putpixel((x,y),   (province_color_list[clusters[x][y]][0],   province_color_list[clusters[x][y]][1],   province_color_list[clusters[x][y]][2]))
         
#im.save(r"C:\Users\scoop\Desktop\prov_test.png", im.format)

不幸的是,此脚本存在多个问题,我感觉它已退化为一些废话。主要问题似乎是访问 .PNG 图像 class 的 RGB 元组并将它们更改为整数以正确识别它们以及区分不同的簇而不仅仅是不同的颜色。到目前为止,我什至无法让脚本将图像写成纯色。

作为参考,我希望能够将其放大以处理这样的图像:

并为这些小簇中的每一个赋予独特的颜色。任何帮助表示赞赏。

好的,让我们看看这是否适合您。如果我理解你想要达到的目标是正确的,这是我的(初学者)解决方案。

基本上我在 3D 数组中获取图像,找到图片中所有独特的颜色并将它们替换为整数(函数:arr_to_int)。然后找到函数为find_clusters的所有簇。用新颜色创建一个字典,颜色的数量与簇的数量一样多(因此每个簇的每个 int 都会再次被一种颜色替换)。 最后再次将所有int替换为colors并保存图片。

这是我开始使用的图像:

这是我输出的新图片:

如果您更改如何将它们应用到您想要使用的特定颜色的过程中,我认为我非常接近您想要实现的目标(希望如此:))

import numpy as np
import cv2
from scipy import ndimage

# array of GBR colors to single int
def arr_to_int(arr, col_mask):
    out = np.ndarray(shape=arr.shape[:2], dtype=int)
    out[:,:] = -1
    for rgb, idx in col_mask.items():
        out[(arr==rgb).all(2)] = idx
    return out

# find unique clusters of identical RGB codes
def find_clusters(array):
    clustered = np.empty_like(array)
    unique_vals = np.unique(array)
    cluster_count = 0
    for val in unique_vals:
        labelling, label_count = ndimage.label(array == val)
        for k in range(1, label_count + 1):
            clustered[labelling == k] = cluster_count
            cluster_count += 1
    return clustered, cluster_count
# Load image
im = cv2.imread("prov_test.png")
#im = cv2.resize(im, (2, 3)) #resize for debugging
#print('original image: \n', im, '\n')

#find all unique colors in image (cv2 presents in BGR format!!!)
unique_col_BGR = list(set(tuple(v) for m2d in im for v in m2d))
print('unique values: ', unique_col_BGR, '\n')

#create dict with GBR_colors as keys and unique integers as value
mask_GBR_int = {color:idx for idx,color in enumerate(unique_col_BGR)}
print('mask dict: ', mask_GBR_int, '\n')

#change all color values in im to a single int (mask)
im_with_ints = arr_to_int(im, mask_GBR_int)
#print('pic with mask values: \n', im_with_ints, '\n')

# due to replacing array of 3 values to a single int, new array has one dimension less
print('orig pic resized shape', im.shape)
print('Mask int pic shape', im_with_ints.shape, '\n')

clusters, cluster_count = find_clusters(im_with_ints)
print(f'Found {cluster_count} clusters', '\n')
#print(clusters)

#create dict with length equal to number of clusters and choose color of list_of_colors (random from the internet)
list_of_colors = [[192,192,192],[128,128,128],[128,0,0],[128,128,0],[0,128,0],[128,0,128],[0,128,128],[0,0,128],[255,0,0],[0,255,0],[0,0,255],[255,255,0],[0,255,255],[255,0,255]]
new_color_dict = {idx:val for idx,val in enumerate(list_of_colors[:cluster_count])}
print('new_color_dict: ', new_color_dict,'\n')

#change arr with int to colors again
res = np.array([*new_color_dict.values()])[clusters]
#print('image array with new colors: \n', res)

cv2.imwrite("prov_test_output.png", res)


Output:

unique values:  [(0, 255, 0), (255, 0, 0), (0, 0, 255), (0, 255, 255)] 

mask dict:  {(0, 255, 0): 0, (255, 0, 0): 1, (0, 0, 255): 2, (0, 255, 255): 3} 

orig pic resized shape (100, 100, 3)
Mask int pic shape (100, 100) 

Found 9 clusters 

new_color_dict:  {0: [192, 192, 192], 1: [128, 128, 128], 2: [128, 0, 0], 3: [128, 128, 0], 4: [0, 128, 0], 5: [128, 0, 128], 6: [0, 128, 128], 7: [0, 0, 128], 8: [255, 0, 0]}