在图像中查找 RGB 颜色的边界框
Posted
技术标签:
【中文标题】在图像中查找 RGB 颜色的边界框【英文标题】:Finding bounding boxes of RGB colors in image 【发布时间】:2013-01-10 09:11:05 【问题描述】:我正在使用页面分割算法。代码的输出写入图像,每个区域的像素都分配了唯一的颜色。我想处理图像以找到区域的边界框。我需要找到所有颜色,然后找到该颜色的所有像素,然后找到它们的边界框。
以下是示例图片。
我目前从 R、G、B 通道的直方图开始。直方图告诉我数据位置。
img = Image.open(imgfilename)
img.load()
r,g,b = img.split()
ra,ga,ba = [ np.asarray(p,dtype="uint8") for p in (r,g,b) ]
rhist,edges = np.histogram(ra,bins=256)
ghist,edges = np.histogram(ga,bins=256)
bhist,edges = np.histogram(ba,bins=256)
print np.nonzero(rhist)
print np.nonzero(ghist)
print np.nonzero(bhist)
输出: (数组([ 0, 1, 128, 205, 255]),) (数组([ 0, 20, 128, 186, 255]),) (数组([ 0, 128, 147, 150, 255]),)
在这一点上我有点困惑。通过目测,我有颜色(0,0,0),(1,0,0),(0,20,0),(128,128,128)等。我应该如何将非零输出置换为 np.where() 的像素值?
我正在考虑将 3,row,col narray 展平为 24 位压缩 RGB 值 (r
【问题讨论】:
这么多令人难以置信的建议! 【参考方案1】:没有理由将其视为 RGB 彩色图像,它只是其他人所做的分割的可视化。您可以轻松地将其视为灰度图像,而对于这些特定颜色,您无需自己进行任何其他操作。
import sys
import numpy
from PIL import Image
img = Image.open(sys.argv[1]).convert('L')
im = numpy.array(img)
colors = set(numpy.unique(im))
colors.remove(255)
for color in colors:
py, px = numpy.where(im == color)
print(px.min(), py.min(), px.max(), py.max())
如果您不能依赖convert('L')
提供独特的颜色(即,您正在使用给定图像中的颜色之外的其他颜色),您可以打包您的图像并获得独特的颜色:
...
im = numpy.array(img, dtype=int)
packed = im[:,:,0]<<16 | im[:,:,1]<<8 | im[:,:,2]
colors = set(numpy.unique(packed.ravel()))
colors.remove(255<<16 | 255<<8 | 255)
for color in colors:
py, px = numpy.where(packed == color)
print(px.min(), py.min(), px.max(), py.max())
顺便说一下,我还建议在找到边界框之前移除小的连接组件。
【讨论】:
+1 表示np.unique
,比直方图好得多。其余的……他肯定需要减少他的搜索空间,因为每张支票都非常昂贵。不确定亮度是否可行,因为它可能会使不同颜色的文本混淆。可能不会,但可能会。我认为最好将整个图像除以 16 以将相似的颜色混合在一起。
@Jaime 图像已经被分割,如果没有,那么在 RGB 中这样做将是一件非常糟糕的事情。使用亮度只是一种“技巧”来映射选择的颜色来表示分割的不同区域,使用其他颜色肯定会失败。在这种情况下,最简单的做法是 colors = set(img.getdata())
并为那里的每个项目选择一个标签。
我讨厌将 RGB 转换为灰度。我这样做是为了一篇论文,准确性是我最关心的问题。第二个建议(包含独特的 24 位)效果很好!我确实需要对输入图像进行更多预处理。
@DavidPoole 根据我回答的最初部分,您没有真正的“RGB”图像。无论分割例程做什么,它都可以返回值为 1 的第一个区域,返回值为 2 的第二个区域,依此类推。那将与您的图像一样“RGB”。转换为灰度工作/在展示的示例中/因为它只是将您在该特定图像中显示的颜色映射到 /distinct/ 值,就是这样。如果它没有映射到不同的值(即至少有两种颜色映射到相同的灰度值),我什至不会考虑显示它。
@mmgp RGB->L 中发生冲突的可能性非常小。但是我有 100 张图像要测试,所以零机会更好(与 24 位解决方案一样)。我不确定这个分段代码(XYCut)是如何分配颜色的。我有源,所以下一步是修改它以直接输出区域。感谢您的帮助!【参考方案2】:
编辑使用您发布的图片将所有内容整合到一个工作程序中:
from __future__ import division
import numpy as np
import itertools
from PIL import Image
img = np.array(Image.open('test_img.png'))
def bounding_boxes(img) :
r, g, b = [np.unique(img[..., j]) for j in (0, 1, 2)]
bounding_boxes =
for r0, g0, b0 in itertools.product(r, g, b) :
rows, cols = np.where((img[..., 0] == r0) &
(img[..., 1] == g0) &
(img[..., 2] == b0))
if len(rows) :
bounding_boxes[(r0, g0, b0)] = (np.min(rows), np.max(rows),
np.min(cols), np.max(cols))
return bounding_boxes
In [2]: %timeit bounding_boxes(img)
1 loops, best of 3: 30.3 s per loop
In [3]: bounding_boxes(img)
Out[3]:
(0, 0, 255): (3011, 3176, 755, 2546),
(0, 128, 0): (10, 2612, 0, 561),
(0, 128, 128): (1929, 1972, 985, 1438),
(0, 255, 0): (10, 166, 562, 868),
(0, 255, 255): (2938, 2938, 680, 682),
(1, 0, 0): (10, 357, 987, 2591),
(128, 0, 128): (417, 1873, 984, 2496),
(205, 186, 150): (11, 56, 869, 1752),
(255, 0, 0): (3214, 3223, 570, 583),
(255, 20, 147): (2020, 2615, 956, 2371),
(255, 255, 0): (3007, 3013, 600, 752),
(255, 255, 255): (0, 3299, 0, 2591)
不是很快,即使实际检查的颜色数量很少......
您可以找到颜色的边界框r0
、g0
、b0
,类似于
rows, cols = np.where((ra == r0) & (ga == g0) & (ba == b0))
top, bottom = np.min(rows), np.max(rows)
left, right = np.min(cols), np.max(cols)
您可以仅使用非零直方图箱的笛卡尔积来大大减少搜索空间,而不是遍历所有 2**24
RGB 颜色组合:
for r0, g0, b0 in itertools.product(np.nonzero(rhist),
np.nonzero(ghist),
np.nonzero(bhist)) :
您将有不存在的组合泄漏,您可以过滤掉检查 rows
和 cols
不是空元组。但在您的示例中,您会将 2**24
combinations 的搜索空间减少到仅 125 个。
【讨论】:
【参考方案3】:这只是我想到的一个解决方案。您可以从左上角到右下角遍历图像中的像素,并为每种颜色保存 top
、bottom
、left
和 right
值。对于给定的颜色,top
值将是您看到该颜色的第一行,bottom
将是最后一个原始值,left
值将是该颜色像素的最小列值,@987654328 @ 是您找到的最大列值。
然后,对于每种颜色,您可以在top-left
到bottom-right
之间以所需颜色绘制一个矩形。
我不知道这算不算一个好的边界框算法,但我觉得还可以。
【讨论】:
以上是关于在图像中查找 RGB 颜色的边界框的主要内容,如果未能解决你的问题,请参考以下文章