【问题标题】:Count different colour pixels - Python计算不同颜色的像素 - Python
【发布时间】:2018-05-26 17:07:54
【问题描述】:

我在这里找到了这段代码,但它所做的只是计算黑色和红色,这仅适用于黑色和红色图像。

from PIL import Image
im = Image.open('oh.png')

black = 0
red = 0

for pixel in im.getdata():
    if pixel == (0, 0, 0, 255): # if your image is RGB (if RGBA, (0, 0, 0, 255) or so
        black += 1
    else:
        red += 1
print('black=' + str(black)+', red='+str(red))

我如何能够检查不同的颜色而不使其如此精确,例如黑色可能是 (0, 0, 0) 到 (40,40,40)。

这可能问得太多了,请告诉我。 谢谢:)

【问题讨论】:

  • 你到底想做什么?一张图片可能有 1600 万种颜色...
  • 我希望它查看一系列选项,例如在查找黑色时,它会查找 (0, 0 ,0) 和 (40, 40, 40) 之间的像素。在python中做起来可能太复杂了我不确定
  • 可以用 Python 完成,我只是想了解您打算如何处理列表。例如,是否有 32 种不同的颜色让您感兴趣,并且您想知道它们中的哪一种?或者您想找到图像中的统计变化?或者你是想判断是否存在某种音调?
  • 将有几组数据,其中已经包含示例数据,从那时起收集的数据将根据最相似的内容分类到正确的组中。将其用于机器学习。它将处于非常基本的水平,例如猜测水果
  • 所以您想将每个新图像中的每个像素映射到某个预先存在的 "reference" 图像中最接近的颜色?或者至少计算到最近的预先存在的颜色的距离?

标签: python image python-imaging-library pillow


【解决方案1】:

这是一种使用 KDTree 进行高效最近颜色查找的方法。请注意,虽然 KDTrees 可能非常先进,但使用它们实际上非常简单。

import numpy as np
from matplotlib import colors
from scipy.spatial import cKDTree as KDTree
from scipy.misc import face

REDUCED_COLOR_SPACE = True

# borrow a list of named colors from matplotlib
if REDUCED_COLOR_SPACE:
    use_colors = {k: colors.cnames[k] for k in ['red', 'green', 'blue', 'black', 'yellow', 'purple']}
else:
    use_colors = colors.cnames

# translate hexstring to RGB tuple
named_colors = {k: tuple(map(int, (v[1:3], v[3:5], v[5:7]), 3*(16,)))
                for k, v in use_colors.items()}
ncol = len(named_colors)

if REDUCED_COLOR_SPACE:
    ncol -= 1
    no_match = named_colors.pop('purple')
else:
    no_match = named_colors['purple']

# make an array containing the RGB values 
color_tuples = list(named_colors.values())
color_tuples.append(no_match)
color_tuples = np.array(color_tuples)

color_names = list(named_colors)
color_names.append('no match')

# get example picture
img = face()

# build tree
tree = KDTree(color_tuples[:-1])
# tolerance for color match `inf` means use best match no matter how
# bad it may be
tolerance = np.inf
# find closest color in tree for each pixel in picture
dist, idx = tree.query(img, distance_upper_bound=tolerance)
# count and reattach names
counts = dict(zip(color_names, np.bincount(idx.ravel(), None, ncol+1)))

print(counts)

import pylab

pylab.imshow(img)
pylab.savefig('orig.png')
pylab.clf()
pylab.imshow(color_tuples[idx])
pylab.savefig('minimal.png' if REDUCED_COLOR_SPACE else 'reduced.png')

带有完整 matplotlib 命名颜色空间的输出:

{'aliceblue': 315, 'antiquewhite': 0, 'aqua': 0, 'aquamarine': 0, 'azure': 0, 'beige': 27, 'bisque': 0, 'black': 88584, 'blanchedalmond': 0, 'blue': 0, 'blueviolet': 0, 'brown': 0, 'burlywood': 76, 'cadetblue': 0, 'chartreuse': 0, 'chocolate': 0, “珊瑚”:0,“矢车菊蓝”:0,“玉米丝”:0,“深红色”:0,“青色”:0,“深蓝色”:0,“深青色”:0,“深金色”:0,“深灰色” ':0,'darkgreen':4148,'darkgrey':71985,'darkkhaki':32907,'darkmagenta':0,'darkolivegreen':90899,'darkorange':0,'darkorchid':0,'darkred': 0,“darksalmon”:0,“darkseagreen”:30171,“darkslateblue”:134,“darkslategray”:108608,“darkslategrey”:0,“darkturquoise”:0,“darkviolet”:0,“deeppink”:0, 'deepskyblue':0,'dimgray':0,'dimgrey':108318,'dodgerblue':0,'firebrick':0,'floralwhite':0,'forestgreen':1,'紫红色':0,'gainsboro ':10438,'ghostwhite':736,'gold':0,'goldenrod':0,'gray':0,'green':0,'greenyellow':0,'grey':79835,'honeydew': 0, 'hotpink': 0, 'indianred': 0, 'in digo':0,'象牙':0,'卡其色':1056,'lavender':4650,'lavenderblush':46,'lawngreen':0,'lemonchiffon':0,'lightblue':3,'lightcoral' :0,'lightcyan':0,'lightgoldenrodyellow':0,'lightgray':11905,'lightgreen':2323,'lightgrey':0,'lightpink':0,'lightsalmon':0,'lightseagreen':0 ,'lightskyblue':0,'lightslategray':0,'lightslategrey':31920,'lightsteelblue':3590,'lightyellow':0,'lime':0,'limegreen':0,'linen':46,'洋红色':0,'栗色':0,'mediumaquamarine':0,'mediumblue':0,'mediumorchid':0,'mediumpurple':15,'mediumseagreen':0,'mediumslateblue':0,'mediumspringgreen' :0,'mediumturquoise':0,'mediumvioletred':0,'midnightblue':54,'mintcream':0,'mistyrose':19,'莫卡辛':0,'navajowhite':0,'navy':0 ,'oldlace':0,'橄榄':0,'olivedrab':30828,'橙色':0,'橙色':0,'兰花':0,'palegoldenrod':1499,'palegreen':285,'绿松石':0,'palevioletred':0,'番木瓜':0,'peachpuff':0,'秘鲁':21,'粉红色':0,'李子':0,'粉rblue':0,'purple':0,'rebeccapurple':0,'red':0,'rosybrown':2831,'royalblue':0,'saddlebrown':0,'salmon':0,'sandybrown' :0,'seagreen':0,'seashell':0,'sienna':5,'silver':35951,'skyblue':0,'slateblue':0,'slategray':7836,'slategrey':0 , 'snow': 18, 'springgreen': 0, 'steelblue': 0, 'tan': 3925, 'teal': 0, 'thistle': 10274, 'tomato': 0, 'turquoise': 0, '紫罗兰色':0,'小麦':21,'白色':3,'whitesmoke':834,'黄色':0,'黄绿色':9292,'不匹配':0}

只有基本颜色的输出:

{'red': 0, 'green': 403561, 'blue': 3262, 'black': 153782, 'yellow': 225827, 'no match': 0}

原图:

减色版:

基本色版:

【讨论】:

  • @MiniMiloe 你的 Python 版本是什么?
  • 这是 3.4.3 我认为最新的 3.6 有问题
  • @MiniMiloe 好的,您能否尝试将(*map(int, (v[1:3], v[3:5], v[5:7]), 3*(16,)),) 替换为tuple(map(int, (v[1:3], v[3:5], v[5:7]), 3*(16,)))
  • 无效的语法,该行现在显示:named_colors = {k: (*map(int, (v[1:3], v[3:5], v[5:7]), 3*(16,)), 元组(map(int, (v[1:3], v[3:5], v[5:7]), 3*(16,))) 正确吗?跨度>
  • @MiniMiloe 不,从(* 到包括“with”在内的所有内容都应该在那里。
【解决方案2】:

Paul 的回答更优雅,但基本上我认为您可以通过定义一个函数来解决它,该函数为您提供任意两种 RGB 颜色之间的 “距离”,如下所示:

def distance(col1, col2):
    (r1,g1,b1) = col1
    (r2,g2,b2) = col2
    return (r1 - r2)**2 + (g1 - g2) ** 2 + (b1 - b2) **2

现在,您需要做的(在伪代码中)是:

load your pre-existing reference colours into a list
load your new image
for each pixel in new image
    # Calculate distance from this pixel to first one in reference list
    mindist=distance(this pixel, first pixel in reference colours)
    nearest=first pixel in reference colours
    # Now see if any other colour in reference list is closer
    for index=1 to number of colours in reference list
        d=distance(this pixel, reference list[index])
        if d < mindist:
           mindist=d
           nearest=reference list[index]
    end
    replace pixel with nearest one from reference list as determined above
end

我还在学习 Python,所以我对上述内容的翻译可能不是最佳的,但它确实有效!

#!/usr/local/bin/python3
from PIL import Image
import numpy as np

# Calculate distance in RGB space between two RGB pixels
def distance(col1, col2):
    r1,g1,b1 = col1
    r2,g2,b2 = col2
    return (r1 - r2)**2 + (g1 - g2) ** 2 + (b1 - b2) **2

# All colours in the image will be forced to nearest one in this list
refColours=(
      [[255,   0,   0],    # red
       [  0, 255,   0],    # green
       [  0,   0, 255],    # blue
       [255, 255,   0],    # yellow
       [  0, 255, 255],    # cyan
       [255,   0, 255],    # magenta
       [  0,   0,   0],    # black
       [255, 255, 255]])   # white

# Load your new image and note its width and height
im = Image.open('colorwheel.png')
imrgb = im.convert("RGB")
(w,h)=im.size[0],im.size[1]

# Make a buffer for our output pixels
px=np.zeros(w*h,dtype=np.uint8)
idx=0

for pixel in list(imrgb.getdata()):
   # Calculate distance from this pixel to first one in reference list
   mindist = distance([pixel[0],pixel[1],pixel[2]],refColours[0])
   nearest = 0
   # Now see if any other colour in reference list is closer
   for index in range(1,len(refColours)):
       d=distance([pixel[0],pixel[1],pixel[2]],refColours[index])
       if d < mindist:
          mindist=d
          nearest=index
   # Set output pixel to nearest
   px[idx]=nearest
   idx += 1

# Reshape our output pixels to match input image
px=px.reshape(w,h)
# Make output image from our pixels
outimg=Image.fromarray(px).convert('P')
# Put our palette of favourite colours into the output image
palette=[item for sublist in refColours for item in sublist]
outimg.putpalette(palette)
outimg.save("result.png")

所以,我从 colorwheel.png 开始:

最后是这样的:


当然,像我在 cmets 中建议的那样,更简单的解决方案是使用 ImageMagick 之类的工具将新图像中的颜色重新映射到“参考”图像中的颜色,您可以这样做像这样在命令行上:

convert colorwheel.png +dither -remap colormap.png result.png

my other answer here所示。因此,在 Python 中,您可以使用 system() 调用或使用 subprocess 模块:

cmd="https://stackoverflow.com/a/38328879/2836621"
system(cmd)

【讨论】:

    【解决方案3】:

    如果只做红黑,你可以只检查红色值是否小于等于40。

    if pixel[0] &lt;= 40:

    编辑:

    colors = {}
    
    for pixel in im.getdata():
        r = pixel[0]
        g = pixel[1]
        b = pixel[2]
    
        color = ''
        brightness = ''
        avg = (r + g + b) / 3
    
        if avg < 40 then brightness = 'black'
        else if avg < 80 then brightness = 'dark'
        else if avg > 220 then brightness = 'white'
        else if avg > 150 then brightness = 'light'
    
        if avg / r > 0.9 then hue = 'red'
        else if avg / r > 0.8 and avg / g > 0.6 then hue = 'orange'
        else if avg / r > 0.7 and avg / g > 0.7 then hue = 'yellow'
        else if avg / g > 0.8 and avg / r > 0.6 then hue = 'lime'
        else if avg / g > 0.9 then hue = 'green'
        else if avg / g > 0.7 and avg / b > 0.7 then hue = 'cyan'
        else if avg / b > 0.9 then hue = 'blue'
        else if avg / b > 0.8 and avg / r > 0.6 then hue = 'indigo'
        else if avg / b > 0.7 and avg / r > 0.7 then hue = 'magenta'
    
        color = brightness + hue
        if color not in colors:
            colors[color] = 1
        else
            colors[color] = colors[color] + 1
    

    【讨论】:

    • 我希望它可以查看所有颜色,但不知道如何添加它
    【解决方案4】:

    您必须定义您的颜色桶。我建议使用hsv 色彩空间。

    from PIL import Image
    import colorsys
    im = Image.open('download.png')
    
    NUM_BUCKETS = 6 # Just for example
    colour_counts = [0] * NUM_BUCKETS
    
    for pixel in im.getdata():
        hue, saturation, value = colorsys.hsv_to_rgb(pixel[0], pixel[1], pixel[2])
        hue_bucket = hue * NUM_BUCKETS // 255 # Using python3 to get an int
        colour_counts[hue_bucket] += 1
    
    colour_names = ["red", "yellow", "green", "cyan", "blue", "magenta"]
    for name, count in [x for x in zip(colour_names, colour_counts)]:
        print("{n} = {c}".format(n=name, c=count))
    

    所以,这只是将颜色空间划分为 6 个,但您可以使用任何您想要的数字(您只需想好所有数字的名称)。黑色和白色效果不佳,因为我们正在研究色调。要捕获黑白以及使用“值”和“饱和度”,即:

    for pixel in im.getdata():
            hue, saturation, value = colorsys.hsv_to_rgb(pixel[0], pixel[1], pixel[2])
            if value < 32: # It's very dark
                blacks += 1
            elif saturation < 32 and value > 224: # it's basically white
                whites += 1
            else: # if it fails both of those, we can call it a colour
                hue_bucket = hue * NUM_BUCKETS // 255 # Using python3 to get an int
                colour_counts[hue_bucket] += 1
    

    黑色的特点是低value。白色有一个高value 和一个低saturation。低saturation 颜色为灰色。我通常发现hsvrgb 更容易理解。

    【讨论】:

    • 在 print("{n} = "{c}".format(n=name, c=count") 处出现语法错误
    • 开枪,你说得对,我加了一个额外的引号
    • 它说列表索引超出范围,第 14 行
    • 对我来说很好,确定你复制/粘贴准确吗?
    • 是的,刚刚重试。我也需要底部代码块吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-29
    • 2015-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多