4

我需要对大量图像进行一些快速阈值处理,每个 RGB 通道都有一个特定范围,即删除(使黑色)不在 [100;110] 中的所有 R 值,不在 [80; 中的所有 G 值; 85] 和所有不在 [120;140] 中的 B 值

使用 python 绑定到 OpenCV 给了我一个快速的阈值,但它将所有三个 RGP 通道阈值设置为一个值:

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

4 回答 4

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 秒。

于 2011-10-11T18:07:28.567 回答
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);

希望有帮助!

于 2011-10-11T13:12:48.407 回答
2

PIL 点函数为图像的每个波段获取一个包含 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')

这会对图像进行一些转换,在此过程中进行复制,但它仍然应该比对单个像素进行操作更快。

于 2011-10-11T13:37:29.523 回答
0

如果您坚持使用 OpenCV,则只需cv.Split将图像先放入多个通道,然后再将cv.Threshold每个通道单独放入。我会使用这样的东西(未经测试):

# Temporary images for each color channel
b = cv.CreateImage(cv.GetSize(orig), orig.depth, 1)
g = cv.CloneImage(b)
r = cv.CloneImage(b)
cv.Split(orig, b, g, r, None)

# Threshold each channel using individual lo and hi thresholds
channels = [ b, g, r ]
thresh = [ (B_LO, B_HI), (G_LO, G_HI), (R_LO, R_HI) ]
for c, (lo, hi) in zip(channels, thresh):
    cv.Threshold(ch, ch, hi, 100, cv.CV_THRESH_TOZERO_INV)
    cv.Threshold(ch, ch, lo, 100, cv.CV_THRESH_TOZERO)

# Compose a new RGB image from the thresholded channels (if you need it)
dst = cv.CloneImage(orig)
cv.Merge(b, g, r, None, dst)

如果您的图像大小相同,则可以重复使用创建的图像以节省时间。

于 2011-10-11T15:22:02.877 回答