Python中快速RGB阈值处理(可能有一些智能OpenCV代码?)

4 投票
4 回答
16314 浏览
提问于 2025-04-17 04:04

我需要对大量图片进行快速的阈值处理,每个RGB通道都有特定的范围。也就是说,要把所有不在[100;110]范围内的红色值变成黑色,把所有不在[80;85]范围内的绿色值变成黑色,把所有不在[120;140]范围内的蓝色值变成黑色。

我使用Python的OpenCV库来进行快速的阈值处理,但这样处理后,所有三个RGB通道都被阈值化为一个单一的值:

cv.Threshold(cv_im,cv_im,threshold+5, 100,cv.CV_THRESH_TOZERO_INV)
cv.Threshold(cv_im,cv_im,threshold-5, 100,cv.CV_THRESH_TOZERO)

另外,我尝试手动将图片从PIL格式转换为numpy格式:

arr=np.array(np.asarray(Image.open(filename).convert('RGB')).astype('float'))
for x in range(img.size[1]):
    for y in range(img.size[0]):
        bla = 0
        for j in range(3):
            if arr[x,y][j] > threshold2[j] - 5 and arr[x,y][j] < threshold2[j] + 5 :
                bla += 1
        if bla == 3:
            arr[x,y][0] = arr[x,y][1] = arr[x,y][2] = 200
        else:
            arr[x,y][0] = arr[x,y][1] = arr[x,y][2] = 0

虽然这样做达到了预期效果,但速度非常慢!

有没有什么办法可以让我快速实现这个功能呢?

非常感谢, Bjarke

4 个回答

2

PIL的point函数需要一个包含256个值的表,这些值对应图像的每个颜色通道,然后它会用这个表来进行映射。这个过程应该是相当快的。下面是如何在这种情况下应用它的示例:

def mask(low, high):
    return [x if low <= x <= high else 0 for x in range(0, 256)]

img = img.point(mask(100,110)+mask(80,85)+mask(120,140))

编辑:上面的内容和你的numpy示例输出不一样;我跟着描述走的,而不是代码。这里有个更新:

def mask(low, high):
    return [255 if low <= x <= high else 0 for x in range(0, 256)]

img = img.point(mask(100,110)+mask(80,85)+mask(120,140)).convert('L').point([0]*255+[200]).convert('RGB')

这个过程会对图像进行几次转换,同时会生成一些副本,但它的速度应该还是比逐个像素操作要快。

6

如果你不使用循环,可以用numpy以快的方式来实现。

这是我想到的办法:

def better_way():
    img = Image.open("rainbow.jpg").convert('RGB')
    arr = np.array(np.asarray(img))

    R = [(90,130),(60,150),(50,210)]
    red_range = np.logical_and(R[0][0] < arr[:,:,0], arr[:,:,0] < R[0][1])
    green_range = np.logical_and(R[1][0] < arr[:,:,0], arr[:,:,0] < R[1][1])
    blue_range = np.logical_and(R[2][0] < arr[:,:,0], arr[:,:,0] < R[2][1])
    valid_range = np.logical_and(red_range, green_range, blue_range)

    arr[valid_range] = 200
    arr[np.logical_not(valid_range)] = 0

    outim = Image.fromarray(arr)
    outim.save("rainbowout.jpg")


import timeit
t = timeit.Timer("your_way()", "from __main__ import your_way")
print t.timeit(number=1)

t = timeit.Timer("better_way()", "from __main__ import better_way")
print t.timeit(number=1)

省略的your_way函数是你上面代码的一个稍微修改版。这个方法运行得快多了:

$ python pyrgbrange.py 
10.8999910355
0.0717720985413

时间对比是10.9秒和0.07秒。

6

我觉得你可能对 inRange 这个opencv的方法感兴趣。这个方法可以让你同时设置多个阈值。

所以,针对你的例子,你可以使用

# Remember -> OpenCV stores things in BGR order
lowerBound = cv.Scalar(120, 80, 100);
upperBound = cv.Scalar(140, 85, 110);

# this gives you the mask for those in the ranges you specified,
# but you want the inverse, so we'll add bitwise_not...
cv.InRange(cv_im, lowerBound, upperBound, cv_rgb_thresh);
cv.Not(cv_rgb_thresh, cv_rgb_thresh);

希望这能帮到你!

撰写回答