计数彩色色板 - 需要更快的方法
Counting coloured swatches - faster method required
我目前正在尝试找到一种计算彩色色板的方法。它们都是规则的,大小相同,呈棋盘状排列,颜色各不相同。样本的数量从几百到大约 90,000 不等。
在这个例子中,只有三种颜色,我需要它来计算浅绿色方块的数量 = 8
在 Photoshop 中,我可以对每个样本进行采样,其中 x、y 是样本的中心,colArr 是这些样本的所有 RGB 值的数组。
var pointSample = app.activeDocument.colorSamplers.add([x,y]);
// Obtain array of RGB values.
var rgb = [
Math.round(pointSample.color.rgb.red,0),
Math.round(pointSample.color.rgb.green,0),
Math.round(pointSample.color.rgb.blue,0)
];
colArr.push(rgb);
delete_all_colour_samples();
function delete_all_colour_samples()
{
app.activeDocument.colorSamplers.removeAll();
}
如果颜色与先前建立的数组中的颜色匹配,则会被计算在内。
有效!对于小样本来说,这是相当瞬时的。但是,对于超过 3000 个样本的图像,大约需要 25 秒。虽然我很高兴知道图像已经过处理,但我很好奇它是否可以更快地完成。
我的问题是:使用 say Python 可以加快速度吗?
我还不太清楚你所问的细节 - 比如每张图片中的正方形是否总是相同大小,你是否事先知道给定图像中有多少,它们是否总是正方形(您将它们描述为 "regular")以及您是否真的想在每个图像中计算不止一种颜色 - 您的示例是 "light green" 但你似乎有一个数组 "previously established colours" 所以不清楚你是否想知道该数组中每种颜色有多少像素。
无论如何,让我们先尝试一下答案,使用 OpenCV 和基本的 Numpy 索引来获取中心。
import cv2
import numpy as np
# Load an image
im = cv2.imread('Qcza1.png')
# Assume the swatches are 64x64 pixels
sw, sh = 64, 64
# Get colours at each swatch centre, so we start 1/2 a swatch in and step by a whole swatch
centres = im[sh//2::sh, sw//2::sw]
这给了我们这个大小为 [4,6,3] 的数组,因为 Numpy 数组是 [height, width, channels] 并且我们有 4 个垂直样本中心,6 个水平样本中心,每个样本有 3 种颜色。
array([[[141, 244, 159],
[ 85, 202, 105],
[255, 255, 255],
[ 85, 202, 105],
[141, 244, 159],
[ 85, 202, 105]],
[[255, 255, 255],
[141, 244, 159],
[ 85, 202, 105],
[141, 244, 159],
[ 85, 202, 105],
[141, 244, 159]],
[[141, 244, 159],
[ 85, 202, 105],
[255, 255, 255],
[ 85, 202, 105],
[141, 244, 159],
[255, 255, 255]],
[[ 85, 202, 105],
[141, 244, 159],
[ 85, 202, 105],
[255, 255, 255],
[ 85, 202, 105],
[255, 255, 255]]], dtype=uint8)
如果您不熟悉 Numpy 索引,它基本上是:
array[start:end:stride]
所以在上面的代码中,我开始将样本宽度的一半放入数组(以到达第一个样本的中心),然后使用等于样本宽度的步幅到达下一个中心。
如果你现在想要计算具有颜色 [141,244,159] 的样本中心的数量,你可以这样做:
tally = np.sum(np.all(centres==[141,244,159], axis=-1))
得到结果8
.
请注意,您可以使用 Python 成像库 PIL/Pillow 来完成所有这些操作,如果这对您来说更容易安装,则替换为:
import cv2
im = cv2.imread(...)
与:
from PIL import Image
# Open PIL Image and make into Numpy array
im = np.array(Image.open(...).convert('RGB'))
请记住,OpenCV 使用 BGR 排序,而 PIL 使用 RGB,因此您正在寻找的颜色三元组将被反转.
我使用 1,000 种独特的颜色生成了 90,000 个 10x10 像素色板的样本图像,如下所示:
import cv2
import numpy as np
# Define 10 possible values for R, G and B
vals = np.arange(0,255,26)
# Make 90,000 pixel image using approx 10*10*10 = 1000 colours
h, w = 200, 450
np.random.seed(42)
im = np.random.choice(vals, (h,w,3)).astype(np.uint8)
# Scale up to make swatches 10x10
sw, sh = 10, 10
im = cv2.resize(im, (w*sw, h*sh), 0, 0, interpolation=cv2.INTER_NEAREST)
然后我对它计时并得到 1.9 毫秒,它找到了 83 个颜色为 [0, 78, 156] 的色板,这就是你所期望的 90,000 个色板,1,000 种颜色:
%%timeit
centres = im[sh//2::sh, sw//2::sw]
np.sum(np.all(centres==[0,78,156], axis=-1))
1.95 ms ± 30.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
计算具有给定颜色的连续区域的数量:
- 首先找出哪些像素具有该颜色,
- 然后使用连通分量分析来计算连续区域的数量。
大多数图像处理库都具有轻松执行此操作所需的工具。例如使用 DIPlib(披露:我是作者)它将是:
import diplib as dip
img = dip.ImageRead('Qcza1.png')
color = dip.Create0D([159,244,141])
squares = dip.AllTensorElements(img == color)
squares = dip.Label(squares, connectivity=1)
count = dip.Maximum(squares)[0][0]
对于 OpenCV 它将是:
import cv2
img = cv2.imread('Qcza1.png') # Uses BGR ordering!
color = (141,244,159)
squares = cv2.inRange(img, color, color)
count, squares = cv2.connectedComponents(squares, connectivity=4)
count = count - 1 # count included background label
这里有两件事可能会出错:
颜色不准确(光照变化、压缩伪影等)。在这种情况下,您需要使用一系列颜色(dip.InRange(img, color1, color2)
而不是 img == color
,或 cv2.inRange(img, color1, color2)
)。
正方形并没有像示例中那样完全分开。如果连接仅发生在顶点,则在标记之前对二进制 squares
图像进行小幅腐蚀应该可以解决问题。如果方块可以并排放置,则需要一种更复杂的方法:我们会查看大小的直方图,期望在单个方块的大小处看到第一个峰值,然后在该大小的倍数处看到进一步的峰值。从这个直方图中,我们可以计算出有多少个正方形,方法是将接近 2x 大小的 bin 乘以 2,接近 3x 大小的 bin 乘以 3,等等,然后对所有值求和。
我目前正在尝试找到一种计算彩色色板的方法。它们都是规则的,大小相同,呈棋盘状排列,颜色各不相同。样本的数量从几百到大约 90,000 不等。
在这个例子中,只有三种颜色,我需要它来计算浅绿色方块的数量 = 8
在 Photoshop 中,我可以对每个样本进行采样,其中 x、y 是样本的中心,colArr 是这些样本的所有 RGB 值的数组。
var pointSample = app.activeDocument.colorSamplers.add([x,y]);
// Obtain array of RGB values.
var rgb = [
Math.round(pointSample.color.rgb.red,0),
Math.round(pointSample.color.rgb.green,0),
Math.round(pointSample.color.rgb.blue,0)
];
colArr.push(rgb);
delete_all_colour_samples();
function delete_all_colour_samples()
{
app.activeDocument.colorSamplers.removeAll();
}
如果颜色与先前建立的数组中的颜色匹配,则会被计算在内。
有效!对于小样本来说,这是相当瞬时的。但是,对于超过 3000 个样本的图像,大约需要 25 秒。虽然我很高兴知道图像已经过处理,但我很好奇它是否可以更快地完成。
我的问题是:使用 say Python 可以加快速度吗?
我还不太清楚你所问的细节 - 比如每张图片中的正方形是否总是相同大小,你是否事先知道给定图像中有多少,它们是否总是正方形(您将它们描述为 "regular")以及您是否真的想在每个图像中计算不止一种颜色 - 您的示例是 "light green" 但你似乎有一个数组 "previously established colours" 所以不清楚你是否想知道该数组中每种颜色有多少像素。
无论如何,让我们先尝试一下答案,使用 OpenCV 和基本的 Numpy 索引来获取中心。
import cv2
import numpy as np
# Load an image
im = cv2.imread('Qcza1.png')
# Assume the swatches are 64x64 pixels
sw, sh = 64, 64
# Get colours at each swatch centre, so we start 1/2 a swatch in and step by a whole swatch
centres = im[sh//2::sh, sw//2::sw]
这给了我们这个大小为 [4,6,3] 的数组,因为 Numpy 数组是 [height, width, channels] 并且我们有 4 个垂直样本中心,6 个水平样本中心,每个样本有 3 种颜色。
array([[[141, 244, 159],
[ 85, 202, 105],
[255, 255, 255],
[ 85, 202, 105],
[141, 244, 159],
[ 85, 202, 105]],
[[255, 255, 255],
[141, 244, 159],
[ 85, 202, 105],
[141, 244, 159],
[ 85, 202, 105],
[141, 244, 159]],
[[141, 244, 159],
[ 85, 202, 105],
[255, 255, 255],
[ 85, 202, 105],
[141, 244, 159],
[255, 255, 255]],
[[ 85, 202, 105],
[141, 244, 159],
[ 85, 202, 105],
[255, 255, 255],
[ 85, 202, 105],
[255, 255, 255]]], dtype=uint8)
如果您不熟悉 Numpy 索引,它基本上是:
array[start:end:stride]
所以在上面的代码中,我开始将样本宽度的一半放入数组(以到达第一个样本的中心),然后使用等于样本宽度的步幅到达下一个中心。
如果你现在想要计算具有颜色 [141,244,159] 的样本中心的数量,你可以这样做:
tally = np.sum(np.all(centres==[141,244,159], axis=-1))
得到结果8
.
请注意,您可以使用 Python 成像库 PIL/Pillow 来完成所有这些操作,如果这对您来说更容易安装,则替换为:
import cv2
im = cv2.imread(...)
与:
from PIL import Image
# Open PIL Image and make into Numpy array
im = np.array(Image.open(...).convert('RGB'))
请记住,OpenCV 使用 BGR 排序,而 PIL 使用 RGB,因此您正在寻找的颜色三元组将被反转.
我使用 1,000 种独特的颜色生成了 90,000 个 10x10 像素色板的样本图像,如下所示:
import cv2
import numpy as np
# Define 10 possible values for R, G and B
vals = np.arange(0,255,26)
# Make 90,000 pixel image using approx 10*10*10 = 1000 colours
h, w = 200, 450
np.random.seed(42)
im = np.random.choice(vals, (h,w,3)).astype(np.uint8)
# Scale up to make swatches 10x10
sw, sh = 10, 10
im = cv2.resize(im, (w*sw, h*sh), 0, 0, interpolation=cv2.INTER_NEAREST)
然后我对它计时并得到 1.9 毫秒,它找到了 83 个颜色为 [0, 78, 156] 的色板,这就是你所期望的 90,000 个色板,1,000 种颜色:
%%timeit
centres = im[sh//2::sh, sw//2::sw]
np.sum(np.all(centres==[0,78,156], axis=-1))
1.95 ms ± 30.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
计算具有给定颜色的连续区域的数量:
- 首先找出哪些像素具有该颜色,
- 然后使用连通分量分析来计算连续区域的数量。
大多数图像处理库都具有轻松执行此操作所需的工具。例如使用 DIPlib(披露:我是作者)它将是:
import diplib as dip
img = dip.ImageRead('Qcza1.png')
color = dip.Create0D([159,244,141])
squares = dip.AllTensorElements(img == color)
squares = dip.Label(squares, connectivity=1)
count = dip.Maximum(squares)[0][0]
对于 OpenCV 它将是:
import cv2
img = cv2.imread('Qcza1.png') # Uses BGR ordering!
color = (141,244,159)
squares = cv2.inRange(img, color, color)
count, squares = cv2.connectedComponents(squares, connectivity=4)
count = count - 1 # count included background label
这里有两件事可能会出错:
颜色不准确(光照变化、压缩伪影等)。在这种情况下,您需要使用一系列颜色(
dip.InRange(img, color1, color2)
而不是img == color
,或cv2.inRange(img, color1, color2)
)。正方形并没有像示例中那样完全分开。如果连接仅发生在顶点,则在标记之前对二进制
squares
图像进行小幅腐蚀应该可以解决问题。如果方块可以并排放置,则需要一种更复杂的方法:我们会查看大小的直方图,期望在单个方块的大小处看到第一个峰值,然后在该大小的倍数处看到进一步的峰值。从这个直方图中,我们可以计算出有多少个正方形,方法是将接近 2x 大小的 bin 乘以 2,接近 3x 大小的 bin 乘以 3,等等,然后对所有值求和。