如何将RGB颜色转换为最接近的8位颜色?

15

我有一个应用程序,可以索引视频中出现的前16个颜色。

我正在编写另一个应用程序,允许用户选择一种颜色,然后该应用程序会找出所有包含该颜色的视频。

问题在于,因为我每个视频只索引16种颜色,当用户选择一种RGB颜色时,该颜色被索引的概率非常低,因此我的应用程序几乎总是返回无结果。

我想到了一个方法来解决这个问题 - 我可以索引视频中出现的颜色并将它们转换为最接近的8位颜色。

然后当用户选择一个RGB颜色时,我可以将用户选择转换为相同的8位最接近颜色。

这样我就总能找到匹配的结果。

目前唯一的主要问题是如何将RGB颜色转换为最接近的8位颜色?


你使用的是哪种编程语言?也许已经有一些库可以胜任这个任务了。 - Francis P
8位并不足以表示任意颜色。 - Mark Ransom
我唯一想不通的是如何将RGB减少到这个8位调色板。 - bodacydo
抱歉,你是对的,Web 安全调色板是获得近似颜色的好方法。请注意,您提供的链接使用抖动来获取不属于调色板的颜色。 - Mark Ransom
@StefanHaustein 因为我有200,000个视频。我无法循环遍历所有颜色并测量距离。我正在使用(color_id,video_id)查找表。其中color_id是每种Web安全颜色的0-255。然后当用户搜索颜色时,我只需将其输入转换为Web安全颜色,并选择所有与color_id匹配的video_id。 - bodacydo
显示剩余4条评论
7个回答

13

要将颜色转换为web安全调色板,需要将每个RGB分量的取值范围从0-255转换为0-5,并将它们组合起来:

color = (r*6/256)*36 + (g*6/256)*6 + (b*6/256)

6
你需要做的是将RGB转换为HSB(色相、饱和度和亮度)值。 HSB与RGB一样有3个字节,不同之处在于HSB值比RGB更容易比较。
下一步是决定“重要性”加权。例如,如果您只关心“颜色/色相”,而不关心饱和度或亮度,则可以丢弃S和B字节,仅使用颜色字节。
如果我受到8位的限制,我会使用4位颜色信息(16种不同颜色),3位饱和度(8个不同值)和1位亮度信息(浅色或深色)。
本文介绍了如何在Java中进行HSB处理。

http://java.sys-con.com/node/43559

这篇文章的源代码中包含一个Java语言的RGB到HSB色彩空间转换器。

我个人会选择2位饱和度和2位亮度。 - Mark Ransom

3

一种可能的方法是将您的24位颜色简单地缩小到8位颜色空间。如cHao所述,您可以使用RRRGGGBB作为8位数字。然后,每个颜色组件都可以通过简单的缩放算法进行计算,例如:

byte red = (originalColor.red * 8) / 256;
byte green = (originalColor.green * 8) / 256;
byte blue = (originalColor.blue * 4) / 256;

8、4和254分别表示每个颜色分量中可能的值的数量。在您原始的24位颜色中,红色、绿色和蓝色都可以有256种可能的值,因此这是缩放方程式的除数。在8位颜色示例中,红色和绿色各占3位(8种可能的值),而蓝色只占2位(4种可能的值)。

在获取这三个分量后,您可以使用一些简单的位移算术将它们组合起来:

byte eightBitColor = (red << 5) | (green << 2) | blue;

那么你可以简单地比较这些8位颜色。它们极大地降低了分辨率,这可能会对您有所帮助。

或者,您可以像Tyler建议的那样,首先转换为HSB或HSV,仅比较色调(取决于您是否需要亮度和饱和度信息)。根据您的目标,这可能实际上是更理想的解决方案。

编辑:修改缩放算法以修复Mark Ransom指出的缺点。


颜色范围仅有8位,忽略位边界并使用不是2的幂次方的范围更有意义。你的答案有8种可能的红色、8种可能的绿色,但只有4种可能的蓝色;更典型的是6,6,6(如我的答案)或6,7,6。此外,你的缩放有点偏差,因为只有输入255才会给出最大输出,而这些值将被低估。 - Mark Ransom
这确实是真的。我只是想详细说明cHao的评论。我已经编辑了我的答案,以解决缩放问题。 - Michael Calvin
像这样做:rgb[0] & 0b11100000 | rgb[1] & 0b11100000 >> 3 | rgb[2] & 0b11000000 >> 6会更快,并且产生相同的结果。这只是使用位运算符从颜色数字中获取最高有效位,进行移位并执行按位或操作。跳过除法和乘法步骤,这对CPU来说更加昂贵。 - ItsDrike

3

1

这是我几年前发现的非常有用的Python脚本。所有功劳归Micah Elliott

如何使用:

  1. Copy the entire code and create a .py file with the desired name.
  2. The program can convert ( Hex -> RGB ) and ( RGB -> Hex )
  3. Run the program along with arguements like this: python file.py 9CE445 to get the closest 8-bit color value and python file.py 204 to get the exact Hex value.

    """ Convert values between RGB hex codes and xterm-256 color codes.
    Nice long listing of all 256 colors and their codes. Useful for
    developing console color themes, or even script output schemes.
    Resources:
    * http://en.wikipedia.org/wiki/8-bit_color
    * http://en.wikipedia.org/wiki/ANSI_escape_code
    * /usr/share/X11/rgb.txt
    I'm not sure where this script was inspired from. I think I must have
    written it from scratch, though it's been several years now.
    """
    
    __author__    = 'Micah Elliott http://MicahElliott.com'
    __version__   = '0.1'
    __copyright__ = 'Copyright (C) 2011 Micah Elliott.  All rights reserved.'
    __license__   = 'WTFPL http://sam.zoy.org/wtfpl/'
    
    #---------------------------------------------------------------------
    
    import sys, re
    
    CLUT = [  # color look-up table
    #    8-bit, RGB hex
    
        # Primary 3-bit (8 colors). Unique representation!
        ('00',  '000000'),
        ('01',  '800000'),
        ('02',  '008000'),
        ('03',  '808000'),
        ('04',  '000080'),
        ('05',  '800080'),
        ('06',  '008080'),
        ('07',  'c0c0c0'),
    
        # Equivalent "bright" versions of original 8 colors.
        ('08',  '808080'),
        ('09',  'ff0000'),
        ('10',  '00ff00'),
        ('11',  'ffff00'),
        ('12',  '0000ff'),
        ('13',  'ff00ff'),
        ('14',  '00ffff'),
        ('15',  'ffffff'),
    
        # Strictly ascending.
        ('16',  '000000'),
        ('17',  '00005f'),
        ('18',  '000087'),
        ('19',  '0000af'),
        ('20',  '0000d7'),
        ('21',  '0000ff'),
        ('22',  '005f00'),
        ('23',  '005f5f'),
        ('24',  '005f87'),
        ('25',  '005faf'),
        ('26',  '005fd7'),
        ('27',  '005fff'),
        ('28',  '008700'),
        ('29',  '00875f'),
        ('30',  '008787'),
        ('31',  '0087af'),
        ('32',  '0087d7'),
        ('33',  '0087ff'),
        ('34',  '00af00'),
        ('35',  '00af5f'),
        ('36',  '00af87'),
        ('37',  '00afaf'),
        ('38',  '00afd7'),
        ('39',  '00afff'),
        ('40',  '00d700'),
        ('41',  '00d75f'),
        ('42',  '00d787'),
        ('43',  '00d7af'),
        ('44',  '00d7d7'),
        ('45',  '00d7ff'),
        ('46',  '00ff00'),
        ('47',  '00ff5f'),
        ('48',  '00ff87'),
        ('49',  '00ffaf'),
        ('50',  '00ffd7'),
        ('51',  '00ffff'),
        ('52',  '5f0000'),
        ('53',  '5f005f'),
        ('54',  '5f0087'),
        ('55',  '5f00af'),
        ('56',  '5f00d7'),
        ('57',  '5f00ff'),
        ('58',  '5f5f00'),
        ('59',  '5f5f5f'),
        ('60',  '5f5f87'),
        ('61',  '5f5faf'),
        ('62',  '5f5fd7'),
        ('63',  '5f5fff'),
        ('64',  '5f8700'),
        ('65',  '5f875f'),
        ('66',  '5f8787'),
        ('67',  '5f87af'),
        ('68',  '5f87d7'),
        ('69',  '5f87ff'),
        ('70',  '5faf00'),
        ('71',  '5faf5f'),
        ('72',  '5faf87'),
        ('73',  '5fafaf'),
        ('74',  '5fafd7'),
        ('75',  '5fafff'),
        ('76',  '5fd700'),
        ('77',  '5fd75f'),
        ('78',  '5fd787'),
        ('79',  '5fd7af'),
        ('80',  '5fd7d7'),
        ('81',  '5fd7ff'),
        ('82',  '5fff00'),
        ('83',  '5fff5f'),
        ('84',  '5fff87'),
        ('85',  '5fffaf'),
        ('86',  '5fffd7'),
        ('87',  '5fffff'),
        ('88',  '870000'),
        ('89',  '87005f'),
        ('90',  '870087'),
        ('91',  '8700af'),
        ('92',  '8700d7'),
        ('93',  '8700ff'),
        ('94',  '875f00'),
        ('95',  '875f5f'),
        ('96',  '875f87'),
        ('97',  '875faf'),
        ('98',  '875fd7'),
        ('99',  '875fff'),
        ('100', '878700'),
        ('101', '87875f'),
        ('102', '878787'),
        ('103', '8787af'),
        ('104', '8787d7'),
        ('105', '8787ff'),
        ('106', '87af00'),
        ('107', '87af5f'),
        ('108', '87af87'),
        ('109', '87afaf'),
        ('110', '87afd7'),
        ('111', '87afff'),
        ('112', '87d700'),
        ('113', '87d75f'),
        ('114', '87d787'),
        ('115', '87d7af'),
        ('116', '87d7d7'),
        ('117', '87d7ff'),
        ('118', '87ff00'),
        ('119', '87ff5f'),
        ('120', '87ff87'),
        ('121', '87ffaf'),
        ('122', '87ffd7'),
        ('123', '87ffff'),
        ('124', 'af0000'),
        ('125', 'af005f'),
        ('126', 'af0087'),
        ('127', 'af00af'),
        ('128', 'af00d7'),
        ('129', 'af00ff'),
        ('130', 'af5f00'),
        ('131', 'af5f5f'),
        ('132', 'af5f87'),
        ('133', 'af5faf'),
        ('134', 'af5fd7'),
        ('135', 'af5fff'),
        ('136', 'af8700'),
        ('137', 'af875f'),
        ('138', 'af8787'),
        ('139', 'af87af'),
        ('140', 'af87d7'),
        ('141', 'af87ff'),
        ('142', 'afaf00'),
        ('143', 'afaf5f'),
        ('144', 'afaf87'),
        ('145', 'afafaf'),
        ('146', 'afafd7'),
        ('147', 'afafff'),
        ('148', 'afd700'),
        ('149', 'afd75f'),
        ('150', 'afd787'),
        ('151', 'afd7af'),
        ('152', 'afd7d7'),
        ('153', 'afd7ff'),
        ('154', 'afff00'),
        ('155', 'afff5f'),
        ('156', 'afff87'),
        ('157', 'afffaf'),
        ('158', 'afffd7'),
        ('159', 'afffff'),
        ('160', 'd70000'),
        ('161', 'd7005f'),
        ('162', 'd70087'),
        ('163', 'd700af'),
        ('164', 'd700d7'),
        ('165', 'd700ff'),
        ('166', 'd75f00'),
        ('167', 'd75f5f'),
        ('168', 'd75f87'),
        ('169', 'd75faf'),
        ('170', 'd75fd7'),
        ('171', 'd75fff'),
        ('172', 'd78700'),
        ('173', 'd7875f'),
        ('174', 'd78787'),
        ('175', 'd787af'),
        ('176', 'd787d7'),
        ('177', 'd787ff'),
        ('178', 'd7af00'),
        ('179', 'd7af5f'),
        ('180', 'd7af87'),
        ('181', 'd7afaf'),
        ('182', 'd7afd7'),
        ('183', 'd7afff'),
        ('184', 'd7d700'),
        ('185', 'd7d75f'),
        ('186', 'd7d787'),
        ('187', 'd7d7af'),
        ('188', 'd7d7d7'),
        ('189', 'd7d7ff'),
        ('190', 'd7ff00'),
        ('191', 'd7ff5f'),
        ('192', 'd7ff87'),
        ('193', 'd7ffaf'),
        ('194', 'd7ffd7'),
        ('195', 'd7ffff'),
        ('196', 'ff0000'),
        ('197', 'ff005f'),
        ('198', 'ff0087'),
        ('199', 'ff00af'),
        ('200', 'ff00d7'),
        ('201', 'ff00ff'),
        ('202', 'ff5f00'),
        ('203', 'ff5f5f'),
        ('204', 'ff5f87'),
        ('205', 'ff5faf'),
        ('206', 'ff5fd7'),
        ('207', 'ff5fff'),
        ('208', 'ff8700'),
        ('209', 'ff875f'),
        ('210', 'ff8787'),
        ('211', 'ff87af'),
        ('212', 'ff87d7'),
        ('213', 'ff87ff'),
        ('214', 'ffaf00'),
        ('215', 'ffaf5f'),
        ('216', 'ffaf87'),
        ('217', 'ffafaf'),
        ('218', 'ffafd7'),
        ('219', 'ffafff'),
        ('220', 'ffd700'),
        ('221', 'ffd75f'),
        ('222', 'ffd787'),
        ('223', 'ffd7af'),
        ('224', 'ffd7d7'),
        ('225', 'ffd7ff'),
        ('226', 'ffff00'),
        ('227', 'ffff5f'),
        ('228', 'ffff87'),
        ('229', 'ffffaf'),
        ('230', 'ffffd7'),
        ('231', 'ffffff'),
    
        # Gray-scale range.
        ('232', '080808'),
        ('233', '121212'),
        ('234', '1c1c1c'),
        ('235', '262626'),
        ('236', '303030'),
        ('237', '3a3a3a'),
        ('238', '444444'),
        ('239', '4e4e4e'),
        ('240', '585858'),
        ('241', '626262'),
        ('242', '6c6c6c'),
        ('243', '767676'),
        ('244', '808080'),
        ('245', '8a8a8a'),
        ('246', '949494'),
        ('247', '9e9e9e'),
        ('248', 'a8a8a8'),
        ('249', 'b2b2b2'),
        ('250', 'bcbcbc'),
        ('251', 'c6c6c6'),
        ('252', 'd0d0d0'),
        ('253', 'dadada'),
        ('254', 'e4e4e4'),
        ('255', 'eeeeee'),
    ]
    
    def _str2hex(hexstr):
        return int(hexstr, 16)
    
    def _strip_hash(rgb):
        # Strip leading `#` if exists.
        if rgb.startswith('#'):
            rgb = rgb.lstrip('#')
        return rgb
    
    def _create_dicts():
        short2rgb_dict = dict(CLUT)
        rgb2short_dict = {}
        for k, v in short2rgb_dict.items():
            rgb2short_dict[v] = k
        return rgb2short_dict, short2rgb_dict
    
    def short2rgb(short):
        return SHORT2RGB_DICT[short]
    
    def print_all():
        """ Print all 256 xterm color codes.
        """
        for short, rgb in CLUT:
            sys.stdout.write('\033[48;5;%sm%s:%s' % (short, short, rgb))
            sys.stdout.write("\033[0m  ")
            sys.stdout.write('\033[38;5;%sm%s:%s' % (short, short, rgb))
            sys.stdout.write("\033[0m\n")
        print ("Printed all codes.")
        print ("You can translate a hex or 0-255 code by providing an argument.")
    
    def rgb2short(rgb):
        """ Find the closest xterm-256 approximation to the given RGB value.
        @param rgb: Hex code representing an RGB value, eg, 'abcdef'
        @returns: String between 0 and 255, compatible with xterm.
        >>> rgb2short('123456')
        ('23', '005f5f')
        >>> rgb2short('ffffff')
        ('231', 'ffffff')
        >>> rgb2short('0DADD6') # vimeo logo
        ('38', '00afd7')
        >>> rgb2short('3D3D3D')
        ('237', '3a3a3a')
        >>> rgb2short('070707')
        ('232', '080808')
        """
        rgb = _strip_hash(rgb)
        # Break 6-char RGB code into 3 integer vals.
        parts = [ int(h, 16) for h in re.split(r'(..)(..)(..)', rgb)[1:4] ]
    
        incs = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
    
        if parts[0] == parts[1] == parts[2]:
            gs_incs = range(0x08, 0xee, 10)
            incs = sorted(incs + gs_incs + [0xee,])
    
        res = []
        for part in parts:
            i = 0
            while i < len(incs)-1:
                s, b = incs[i], incs[i+1]  # smaller, bigger
                if s <= part <= b:
                    s1 = abs(s - part)
                    b1 = abs(b - part)
                    if s1 < b1: closest = s
                    else: closest = b
                    res.append(closest)
                    break
                i += 1
        #print '***', res
        res = ''.join([ ('%02.x' % i) for i in res ])
        equiv = RGB2SHORT_DICT[ res ]
        #print '***', res, equiv
        return equiv, res
    
    RGB2SHORT_DICT, SHORT2RGB_DICT = _create_dicts()
    
    #---------------------------------------------------------------------
    
    if __name__ == '__main__':
        import doctest
        doctest.testmod()
        if len(sys.argv) == 1:
            print_all()
            raise SystemExit
        arg = sys.argv[1]
        if len(arg) < 4 and int(arg) < 256:
            rgb = short2rgb(arg)
            sys.stdout.write('xterm color \033[38;5;%sm%s\033[0m -> RGB exact \033[38;5;%sm%s\033[0m' % (arg, arg, arg, rgb))
            sys.stdout.write("\033[0m\n")
        else:
            short, rgb = rgb2short(arg)
            sys.stdout.write('RGB %s -> xterm color approx \033[38;5;%sm%s (%s)' % (arg, short, short, rgb))
            sys.stdout.write("\033[0m\n")
    

0

如果您想将24 bpp图像转换为8 bpp图像,请尝试使用此算法:

for y = 0 to ImageHeight - 1
   for x = 0 to ImageWidth - 1
      GetPixel(x,y,red,grn,blu)
      {read RGB data from 24bpp file}
      d0 = red^2 + grn^2 + blu^2
      ColorIndex = 0
      for cl = 0 to 255
         GetPaletteData(p_red,p_gre,p_blu)
         {read RGB data from 8bpp palette}
         d = (red - p_red)^2 + (grn - p_grn)^2 + (blu - p_blu)^2
         if d0 >= d then
            ColorIndex = cl
            d0 = d
         end if
      next cl
        {use ColorIndex to create your 8bpp file}
     next x
   next y

在进行这一步之前,请从维基百科或其他来源了解更多关于8bpp文件的信息。
祝你好运!

0

你熟悉Floyd-Steinberg抖动算法吗?它被用于将高阶颜色转换为低阶颜色,例如将24位RGB转换为3位(8种颜色)RGB或将RGB图像限制为8位(256种颜色)以进行GIF转换。

这个算法在链接的维基百科页面上有描述。


1
这个想法是将单一颜色转换。在这种应用中,抖动是没有意义的。 - Mark Ransom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接