【问题标题】:Replace all pixels of multiple RGB values in OpenCV替换OpenCV中多个RGB值的所有像素
【发布时间】:2021-03-15 02:25:13
【问题描述】:

我对图像处理和 python 还很陌生,所以请耐心等待

我正在尝试拍摄一张大图 (5632x2048),它基本上是一张包含各省的世界地图(从《钢铁之心 4》中提取),每个省都使用不同的 RGB 值着色,并用一组着色颜色,每种颜色对应一个特定的国家。我目前正在使用此代码

import numpy as np
import cv2
import sqlite3

dbPath = 'PATH TO DB'
dirPath = 'PATH TO IMAGE'

con = sqlite3.connect(dbPath)
cur = con.cursor()

im = cv2.imread(dirPath)

cur.execute('SELECT * FROM Provinces ORDER BY id')
provinceTable = cur.fetchall()

for line in provinceTable:
    input_rgb = [line[1], line[2], line[3]]
    if line[7] == None:
        output_rgb = [255,255,255]
    else:
        output_rgb = line[7].replace('[', '').replace(']','').split(',')
    im[np.all(im == (int(input_rgb[0]), int(input_rgb[1]), int(input_rgb[2])), axis=-1)] = (int(output_rgb[0]), int(output_rgb[1]), int(output_rgb[2]))

cv2.imwrite('result.png',im)

我遇到的问题是它非常慢(50 分钟后它还没有完成),因为我通过循环而不是矢量化肯定使用了 numpy 错误(我的一个概念'我还是新手,不知道该怎么做)。 Google 也不是很有帮助。

最好的方法是什么?

编辑:忘了提到我要替换的值的数量非常大(~15000)

【问题讨论】:

  • 您介意分享图片进行测试吗?
  • @Luke 必须上传到 mega,因为它是 33mb mega.nz/file/…
  • 您的图像实际上是 33MB(兆字节)而不是 33MB(毫比特)——只是 80 亿倍。
  • 你有 N 个精确的颜色(不是范围)你想用 N 个其他颜色替换?请问N的范围是多少?
  • @MarkSetchell 抱歉回复晚了,我想用 ~100 种其他颜色替换图像中的 ~13000 种颜色,数据库中的每一行都定义了输入和输出颜色

标签: python numpy opencv


【解决方案1】:

正如我在 cmets 中提到的,我认为您会想要使用 np.take(yourImage, LUT),其中 LUT 是一个查找表。

所以,如果你制作一个与你的形状相同的虚拟图像:

import numpy as np

# Make a dummy image of 5632x2048 RGB values
im = np.random.randint(0,256,(5632,2048,3), np.uint8)

这将是 34MB。现在将其重塑为 RGB 值的高向量:

# Make image into a tall vector, as tall as necessary and 3 RGB values wide
v = im.reshape((-1,3))

这将是 (11534336, 3) 的形状,然后将其展平为 24 位值,而不是使用 np.dot() 的三个 8 位值

# Make into tall vector of shape 11534336x1 rather than 11534336x3
v24 = np.dot(v.astype(np.uint32),[1,256,65536])

您现在将拥有一个 24 位像素值的一维向量,其形状为 (11534336,)

现在创建您的 RGB 查找表(我在这里创建所有 2^24 个 RGB 条目,您可能需要更少):

RGBLUT = np.zeros((2**24,3),np.uint8)

并设置 LUT。因此,假设您要将原始图像中的所有颜色映射到输出图像中的中灰色 (128):

RGBLUT[:] = 128

现在执行np.dot() 的操作,就像我们对图像所做的一样,所以我们得到一个形状为 (224,1) 而不是形状 (224,3) 的 LUT:

LUT24 = np.dot(RGBLUT.astype(np.uint32), [1,256,65536])

然后在表中进行实际查找:

result = np.take(LUT24, v24)

在我的 Mac 上,5632x2048 图像需要 334 毫秒。


然后通过移位和与运算重塑并转换回三个 8 位值,以撤消 np.dot() 的效果。

我目前无法测试重新组装,但它看起来很像这样:

BlueChannel   = result & 0xff         # Blue channel is bottom 8 bits
GreenChannel  = (result>>8)  &0 xff   # Green channel is middle 8 bits
RedChannel    = (result>>16) &0 xff   # Red channel is top 8 bits

现在将这三个单通道组合成一个 3 通道图像:

RGB = np.dstack(RedChannel, GreenChannel, BlueChannel))

然后从高向量重新整形到原始图像的尺寸:

RGB = RGB.reshape(im.shape)

关于设置 LUT,比中灰色更有趣,如果你想将橙色,即 rgb(255,128,0) 映射到洋红色,即 rgb(255,0,255),你可以沿着行:

LUT[np.dot([255,128,0],[1,256,65536])] = [255,0,255]  # map orange to magenta
LUT[np.dot([255,255,255],[1,256,65536])] = [0,0,0]  # map white to black
LUT[np.dot([0,0,0],[1,256,65536])] = [255,255,255]  # map black to white

关键字:Python、图像处理、LUT、RGB LUT 24 位 LUT、查找表。

【讨论】:

  • 非常感谢您的回答。 2 个问题:我是否为要添加到 LUT 的每个 RGB 值运行一个 for 循环?以及如何将图像转换回来?
  • 如果您想将 13,000 个 RGB 值转换为 13,000 个其他值,您需要遍历这 13,000 个值,将每个值放入 LUT,就像我的答案底部一样。然后np.take() 将在 300 毫秒内一次性完成 11534336 次查找。
  • 感谢您的回答。我已将代码更改为:pastebin.com/61XSWgUj 我遇到的问题是图像完全错误(屏幕截图结果粘贴)。我已经尝试了仅灰色的方法,并且效果很好,所以我假设我做错了 LUT 循环部分。
  • 检查RGB.dtype - 它应该是np.uint8。如果没有,请使用RGB = RGB.astype(np.uint8) 进行转换。此外,您似乎正在使用 OpenCV,它(奇怪地)使用 BGR 排序而不是 RGB,因此您需要在所有假定 RGB 的地方重新排序。
  • 或者您可以在使用RGB = cv2.cvtColor(XXX, cv2.COLOR_BGR2RGB) 加载后立即将图像转换为RGB,然后在cv2.imwrite() 之前转换回BGR 顺序。
【解决方案2】:

这是使用 Numpy 和 Python/OpenCV 实现此目的的一种方法。在这里,我将红色变为绿色。

输入:

import cv2
import numpy as np

# load image
img = cv2.imread('test_red.png')

# change color
result = img.copy()
result[np.where((result==[0,0,255]).all(axis=2))] = [0,255,0]

# save output
cv2.imwrite('test_green.png', result)

# Display various images to see the steps
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()

结果:

【讨论】:

    【解决方案3】:

    您可以先创建图像的蒙版并使用它来替换颜色。可能有一种更快的纯 numpy 方法,但我不知道。

    此代码运行大约需要 0.5 秒。您应该预计每次颜色替换大约需要半秒钟。

    import cv2
    import numpy as np
    import time
    
    # make image
    res = (5632, 2048, 3);
    img = np.zeros(res, np.uint8);
    
    # change black to white
    black = (0,0,0);
    white = (255,255,255);
    
    # make a mask
    start_time = time.time();
    mask = cv2.inRange(img, black, black);
    print("Mask Time: " + str(time.time() - start_time));
    
    # replace color
    start_time = time.time();
    img[mask == 255] = white;
    print("Replace Time: " + str(time.time() - start_time));
    

    就您的代码而言,它看起来像这样

    for line in provinceTable:
        input_rgb = [line[1], line[2], line[3]]
        input_rgb = (int(input_rgb[0]), int(input_rgb[1]), int(input_rgb[2]))
        if line[7] == None:
            output_rgb = (255,255,255)
        else:
            output_rgb = line[7].replace('[', '').replace(']','').split(',')
            output_rgb = (int(output_rgb[0]), int(output_rgb[1]), int(output_rgb[2]))
        mask = cv2.inRange(im, input_rgb, input_rgb)
        im[mask == 255] = output_rgb
    

    【讨论】:

    • 比我的解决方案更好,但仍然很慢(大约 10 分钟才能完成)。仅使用 1 个值执行 inRange 似乎也有问题,因为颜色有一半是错误的。还是非常感谢你
    猜你喜欢
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 2020-08-31
    • 2017-06-08
    • 2019-09-14
    • 2015-11-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多