Python OpenCV多色阈值

Python OpenCV多色阈值,python,opencv,numpy,Python,Opencv,Numpy,我正在尝试对opencv cv2图像进行多色阈值处理。我试图解决的问题如下: R、 G、B各有一个“有效”列表 如果一个像素的R、G、B都被认为是有效的,那么将该像素设为(0,0,0),否则设为(255、255、255) 比如说 [221,180,50]在R信道中被认为是有效的 [23,18,2]在G通道中被视为有效 [84,22,48]在B通道中被视为有效 然后,如果像素具有以下任意值(RGB顺序) (221、23、84) (221、23、22) (221、23、48) (221、1

我正在尝试对opencv cv2图像进行多色阈值处理。我试图解决的问题如下:

  • R、 G、B各有一个“有效”列表
  • 如果一个像素的R、G、B都被认为是有效的,那么将该像素设为(0,0,0),否则设为(255、255、255)
比如说

  • [221,180,50]在R信道中被认为是有效的
  • [23,18,2]在G通道中被视为有效
  • [84,22,48]在B通道中被视为有效
然后,如果像素具有以下任意值(RGB顺序)

  • (221、23、84)
  • (221、23、22)
  • (221、23、48)
  • (221、18、84)
  • (221、18、22)
  • (221、18、48)
  • (50、2、48)
它将转换为(0,0,0),否则为(255255)

目前,我正在使用嵌套for循环执行此操作:

for x in range(width):
    for y in range(height):
        imcv[y, x] = threshold(imcv[y, x])
其中
threshold
功能执行上述逻辑。请注意,尽管我在原地完成了此操作,但不需要原地转换

我目前使用的方法很有效,但速度很慢。我相信在OpenCV/Numpy中一定有更好的方法。我对这两个框架都很陌生,不知道如何使用

我研究了OpenCV阈值函数,似乎它们只能在单通道灰度图像上工作,而且范围需要是连续范围。我需要的是在离散值上对所有3个通道设置阈值。我想需要一个自定义函数才能传入,但我无法在他们的文档中找到正确的API

我还查找了我可以使用的numpy API,比如
ufunc
。似乎我无法在这里实现我想要的,或者我不知道如何实现

感谢您的帮助

编辑:

多亏了AbidRahmanK和HYRY,这两种解决方案都实现了超过x1500的性能改进

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    1.576    1.576    1.576    1.576 test.py:48(preprocess_cv2_image)
     1    0.000    0.000    0.001    0.001 test.py:79(preprocess_cv2_image3)
     1    0.000    0.000    0.001    0.001 test.py:66(preprocess_cv2_image2)
请试试这个:

z1 = np.dstack([np.in1d(img[...,0],B),np.in1d(img[...,1],G),np.in1d(img[...,2],R)]).reshape(img.shape)
q = np.all(z1,axis=2)
out = np.uint8(q)*255
np.in1d(a,b)
为您提供一个长度与a相同的布尔数组,如果该元素在b中,则为
True
,否则为
False
。它只是Python中方法中的
的向量化对应物。简言之:

np.in1d(a,b)[如果i在b中,则a中的i为真,否则为假]

您可以对所有通道执行此操作,即检查第一个通道B中的有效值,第二个通道G中的有效值,第三个通道R中的有效值

然后使用
np.dstack
沿z方向堆叠它们。为什么是z方向?因为我们需要BGR-BGR-BGR…格式

但请记住,这是1D数组,因此我们使用
X.reformate(img.shape)
方法将其重塑为原始图像形状

现在有了一个布尔掩码,如果有效,则为True,否则为False

这一切都在第一行代码中

现在您想要查看有效的BGR组合。如果所有B、G、R分量均为真,则组合有效。所以在z方向应用np.all()。同样,你会得到一个布尔掩码q

q将是一个布尔掩码,有效颜色为真,其他颜色为假

因此,将转换为整数数据类型,True-->1和False-->0


然后乘以255。如果您想要反转图像,您可以使用
np.按位\u not
您可以为R、G、B创建三个布尔数组,如果该值对R有效,则
R[value]
为真,然后您可以使用
Rm[img[:,:,2]]&Gm[img[:,:,1]&Bm[img[:,:,:,0]
获得结果:

import numpy as np

img = np.random.randint(0, 256, (2000, 2000, 3))

def make_mask(idx):
    b = np.zeros(256, np.bool)
    b[idx] = True
    return b

R = [221, 180, 50]
G = [23, 18, 2]
B = [84, 22, 48]


Rm, Gm, Bm = [make_mask(v) for v in [R, G, B]]
a = Rm[img[:, :, 2]] & Gm[img[:, :, 1]] & Bm[img[:, :, 0]]
最后,要获得结果图像:

v = np.array([[255,255,255], [0,0,0]], np.uint8)
v[a.astype(np.uint8)]

首先,使用
xrange
而不是
range
。后者实际上构建了一个指定大小的完整列表,然后您对其进行迭代,而前者返回一个生成器,它不必预先计算整个列表,只允许您对其进行迭代。您好@SchighSchagh,谢谢您的建议。使用C实现
范围
肯定有帮助。我认为这里的瓶颈是访问和更改python中的每个像素。我想如果有一个api,允许我把它推到numpy或opencv的C实现中,会大大加快速度。[221,18,48]也有效吗?嗨@AbidRahmanK是的。嗨@AbidRahmanK,谢谢你的回答!你能解释一下代码吗?我想让你的代码片段适应我的代码并测试性能,但我无法让它输出cv2图像。现在试试
out=np.uint8(q)*255
。我会很快更新答案。如果答案解决了你的问题,你可以投票并接受答案。谢谢@HYRY,性能很棒。你的方法很容易理解。我给阿比德拉赫曼克的答案打分,因为他是第一个回答的。