【问题标题】:PIL Best Way To Replace Color?PIL 更换颜色的最佳方法?
【发布时间】:2009-10-24 02:42:12
【问题描述】:

我正在尝试从我的图像中删除某种颜色,但它的效果不如我希望的那样。我尝试做与这里看到的相同的事情Using PIL to make all white pixels transparent? 但是图像质量有点有损,所以它在被移除的地方留下了一些奇怪的彩色像素的幽灵。如果所有三个值都低于 100,我尝试执行更改像素之类的操作,但由于图像质量较差,因此周围的像素甚至都不是黑色的。

有谁知道在 Python 中使用 PIL 替换颜色及其周围的任何东西的更好方法?这可能是我能想到的完全移除物体的唯一可靠的方法,但是我想不出一种方法来做到这一点。

图片背景为白色,文字为黑色。假设我想完全从图像中删除文本而不留下任何伪影。

非常感谢有人的帮助!谢谢

【问题讨论】:

    标签: python image replace colors python-imaging-library


    【解决方案1】:

    使用 numpy 和 PIL:

    这会将图像加载到形状为(W,H,3) 的numpy 数组中,其中W 是 宽度和H 是高度。数组的第三个轴代表3种颜色 频道,R,G,B

    import Image
    import numpy as np
    
    orig_color = (255,255,255)
    replacement_color = (0,0,0)
    img = Image.open(filename).convert('RGB')
    data = np.array(img)
    data[(data == orig_color).all(axis = -1)] = replacement_color
    img2 = Image.fromarray(data, mode='RGB')
    img2.show()
    

    因为 orig_color 是一个长度为 3 的元组,而 data 有 形状(W,H,3),NumPy broadcasts orig_color 到形状数组 (W,H,3) 以执行比较 data == orig_color。结果为形状为(W,H,3) 的布尔数组。

    (data == orig_color).all(axis = -1) 是一个形状为 (W,H) 的布尔数组,其中 只要 data 中的 RGB 颜色为 original_color,则为 True。

    【讨论】:

      【解决方案2】:

      最好的方法是使用Gimp 中使用的“颜色到alpha”算法来替换颜色。它会在您的情况下完美运行。我使用 PIL 为开源 python 照片处理器 ​​phatch 重新实现了这个算法。你可以找到完整的实现here。这是一个纯粹的 PIL 实现,它没有其他依赖项。您可以复制功能代码并使用它。这是一个使用 Gimp 的示例:

      您可以在使用黑色作为颜色的图像上应用color_to_alpha 函数。然后将图像粘贴到不同的背景颜色上进行替换。

      顺便说一下,这个实现使用了 PIL 中的 ImageMath 模块。它比使用 getdata 访问像素要高效得多。

      编辑:这是完整的代码:

      from PIL import Image, ImageMath
      
      def difference1(source, color):
          """When source is bigger than color"""
          return (source - color) / (255.0 - color)
      
      def difference2(source, color):
          """When color is bigger than source"""
          return (color - source) / color
      
      
      def color_to_alpha(image, color=None):
          image = image.convert('RGBA')
          width, height = image.size
      
          color = map(float, color)
          img_bands = [band.convert("F") for band in image.split()]
      
          # Find the maximum difference rate between source and color. I had to use two
          # difference functions because ImageMath.eval only evaluates the expression
          # once.
          alpha = ImageMath.eval(
              """float(
                  max(
                      max(
                          max(
                              difference1(red_band, cred_band),
                              difference1(green_band, cgreen_band)
                          ),
                          difference1(blue_band, cblue_band)
                      ),
                      max(
                          max(
                              difference2(red_band, cred_band),
                              difference2(green_band, cgreen_band)
                          ),
                          difference2(blue_band, cblue_band)
                      )
                  )
              )""",
              difference1=difference1,
              difference2=difference2,
              red_band = img_bands[0],
              green_band = img_bands[1],
              blue_band = img_bands[2],
              cred_band = color[0],
              cgreen_band = color[1],
              cblue_band = color[2]
          )
      
          # Calculate the new image colors after the removal of the selected color
          new_bands = [
              ImageMath.eval(
                  "convert((image - color) / alpha + color, 'L')",
                  image = img_bands[i],
                  color = color[i],
                  alpha = alpha
              )
              for i in xrange(3)
          ]
      
          # Add the new alpha band
          new_bands.append(ImageMath.eval(
              "convert(alpha_band * alpha, 'L')",
              alpha = alpha,
              alpha_band = img_bands[3]
          ))
      
          return Image.merge('RGBA', new_bands)
      
      image = color_to_alpha(image, (0, 0, 0, 255))
      background = Image.new('RGB', image.size, (255, 255, 255))
      background.paste(image.convert('RGB'), mask=image)
      

      【讨论】:

      • 我试图让它工作,但它说没有名为 core 的模块和类似的东西,它只是一团糟。我可能是个白痴,但我就是无法让它工作。无论如何,我相信您的回答会对其他人有所帮助。
      • 您不应尝试运行整个文件。只需复制 color_to_alpha 函数本身。无论如何,我很高兴您找到了适合您的解决方案。如果您需要更有效的解决方案,您知道该去哪里寻找;)
      • 我做了,它首先说全局名称'OPTIONS'没有定义,所以我复制了那个部分然后它说_t没有定义,但它是一个我没有的模块。这就是我所说的混乱,我试图让它工作但不能,下面建议的方法对我有用,但如果你的函数真的可以取出图像中的所有背景像素,那就太好了。还有一些让 tesseract 感到困惑。
      • 我用完整的答案更新了解决方案。您可以更改最后 3 行中的颜色以匹配您想要的颜色。
      • 谢谢,它可以工作,只是它仍然会留下像素的幽灵,这与下面的解决方案不同。基本上我有一个纯色的深色文本,它是黑色/蓝色的,因为质量很差。我想保留实体对象并删除它周围的所有像素,这些像素有点像阴影。像这样:img36.imageshack.us/img36/6963/expuq.jpg我会玩一下,看看我能不能做点什么。再次感谢!
      【解决方案3】:

      这是我的代码的一部分,结果如下: source

      target

      import os
      import struct
      from PIL import Image
      def changePNGColor(sourceFile, fromRgb, toRgb, deltaRank = 10):
          fromRgb = fromRgb.replace('#', '')
          toRgb = toRgb.replace('#', '')
      
          fromColor = struct.unpack('BBB', bytes.fromhex(fromRgb))
          toColor = struct.unpack('BBB', bytes.fromhex(toRgb))
      
          img = Image.open(sourceFile)
          img = img.convert("RGBA")
          pixdata = img.load()
      
          for x in range(0, img.size[0]):
              for y in range(0, img.size[1]):
                  rdelta = pixdata[x, y][0] - fromColor[0]
                  gdelta = pixdata[x, y][0] - fromColor[0]
                  bdelta = pixdata[x, y][0] - fromColor[0]
                  if abs(rdelta) <= deltaRank and abs(gdelta) <= deltaRank and abs(bdelta) <= deltaRank:
                      pixdata[x, y] = (toColor[0] + rdelta, toColor[1] + gdelta, toColor[2] + bdelta, pixdata[x, y][3])
      
          img.save(os.path.dirname(sourceFile) + os.sep + "changeColor" + os.path.splitext(sourceFile)[1])
      
      if __name__ == '__main__':
          changePNGColor("./ok_1.png", "#000000", "#ff0000")
      

      【讨论】:

        【解决方案4】:

        您需要将图像表示为二维数组。这意味着要么制作一个像素列表列表,要么通过一些巧妙的数学将一维数组视为二维数组。然后,对于每个目标像素,您需要找到所有周围的像素。您可以使用 python 生成器来执行此操作:

        def targets(x,y):
            yield (x,y) # Center
            yield (x+1,y) # Left
            yield (x-1,y) # Right
            yield (x,y+1) # Above
            yield (x,y-1) # Below
            yield (x+1,y+1) # Above and to the right
            yield (x+1,y-1) # Below and to the right
            yield (x-1,y+1) # Above and to the left
            yield (x-1,y-1) # Below and to the left
        

        所以,你可以这样使用它:

        for x in range(width):
            for y in range(height):
                px = pixels[x][y]
                if px[0] == 255 and px[1] == 255 and px[2] == 255:
                    for i,j in targets(x,y):
                        newpixels[i][j] = replacementColor
        

        【讨论】:

          【解决方案5】:

          如果像素不容易识别,例如您说 (r

          最好的方法是识别一个区域并用您想要的颜色填充它,您可以手动识别该区域,也可以通过边缘检测,例如http://bitecode.co.uk/2008/07/edge-detection-in-python/

          或更复杂的方法是使用诸如 opencv (http://opencv.willowgarage.com/wiki/) 之类的库来识别对象。

          【讨论】:

            【解决方案6】:
            #!/usr/bin/python
            from PIL import Image
            import sys
            
            img = Image.open(sys.argv[1])
            img = img.convert("RGBA")
            
            pixdata = img.load()
            
            # Clean the background noise, if color != white, then set to black.
            # change with your color
            for y in xrange(img.size[1]):
                for x in xrange(img.size[0]):
                    if pixdata[x, y] == (255, 255, 255, 255):
                        pixdata[x, y] = (0, 0, 0, 255)
            

            【讨论】:

              猜你喜欢
              • 2011-02-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-05-16
              • 2016-01-13
              • 2023-03-16
              • 2014-03-09
              相关资源
              最近更新 更多