Python - 图像部分的平均颜色
Python - average color of part of an image
我需要找到图像指定部分的平均颜色。具体来说,我需要知道里面的颜色是红色、绿色、棕色、黑色还是蓝色。
图片是640x640,我需要分析的区域是像素x1=20 y1=600和像素x2=620 y2=640之间的矩形。 (其中 x0y0 是左上角)
我找到了几个示例,例如这个 How to find the average colour of an image in Python with OpenCV?,但它们都处理整个图像。
如何获取指定区域的平均颜色?
必须是它必须尽可能快,低于 5 毫秒。
我的方法是遍历范围内的每个像素并进行数学运算,但我感觉开放式 CV 或类似库已经可以做到这一点。
感谢任何帮助。
这是 Python/OpenCV/Numpy 中的一种方法。
- 读取输入
- 使用 cv2.inRange()
的区域颜色阈值
- 可选地,应用形态学来清理小区域
- 可选地获取最大的轮廓并将其绘制为黑色背景上的白色填充作为最终蒙版
- 使用掩码计算区域的 BGR 分量平均值
- 将颜色名称定义为 select 测试颜色作为 Numpy 数组
- 定义颜色数组的数组
- 遍历数组数组中的每个数组并分离出颜色的 BGR 颜色分量
- 计算平均颜色和测试颜色之间的 RMSE
- 搜索最小RMSE及其对应的颜色名称
- 打印结果
输入:
黄色区域测试:
import cv2
import numpy as np
import math
# load image
img = cv2.imread("sailboat.png")
hh, ww = img.shape[:2]
# threshold
lower = (0,200,200)
upper = (50,255,255)
thresh = cv2.inRange(img, lower, upper)
# apply open morphology
#kernel = np.ones((5,5), np.uint8)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
# get bounding box coordinates from largest external contour
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
# draw white filled contour on black background
mask = np.zeros((hh,ww), dtype=np.uint8)
cv2.drawContours(mask, [big_contour], 0, 255, cv2.FILLED)
# compute mean color for region
mean = cv2.mean(img, mask=mask)[0:3]
blue_mean = mean[0]
green_mean = mean[1]
red_mean = mean[2]
print("region_mean_color:", mean)
print("")
# define colors
red = np.array(["red",(0,0,255)],dtype=object)
green = np.array(["green",(0,255,0)],dtype=object)
brown = np.array(["brown",(20,70,140)],dtype=object)
black = np.array(["black",(0,0,0)],dtype=object)
blue = np.array(["blue",(255,0,0)],dtype=object)
yellow = np.array(["yellow",(0,255,255)],dtype=object)
min_rmse = 1000000
colors = np.array([red, green, brown, black, blue, yellow])
print("colorname", "rmse")
for color in colors:
bb = color[1][0]
gg = color[1][1]
rr = color[1][2]
rmse = math.sqrt( ( (red_mean-rr)*(red_mean-rr) + (green_mean-gg)*(green_mean-gg) + (blue_mean-bb)*(blue_mean-bb) )/3 )
colorname = color[0]
print(colorname,rmse)
if rmse < min_rmse:
min_rmse = rmse
match_color = color[0]
print("")
print("match_color:", match_color)
print("rmse:", min_rmse)
# write result to disk
cv2.imwrite("sailboat_thresh.jpg", thresh)
cv2.imwrite("sailboat_morph.jpg", morph)
cv2.imwrite("sailboat_mask.jpg", mask)
# display results
cv2.imshow("THRESH", thresh)
cv2.imshow("MORPH", morph)
cv2.imshow("MASK", mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
蒙版图片:
文字信息:
region_mean_color: (0.0, 254.65158457426497, 254.65158457426497)
colorname rmse
red 147.02329851590747
green 147.02329851590747
brown 126.01745055239607
black 207.92214813271502
blue 254.7677759924176
yellow 0.2844800038551275
match_color: yellow
rmse: 0.2844800038551275
由于您的感兴趣区域(ROI)只是一个简单的矩形,我想您只是想使用 Numpy 切片来识别它。
所以,我制作了一张测试图像,您要测量的地方是绿色的:
那么代码应该是这样的:
import cv2
import numpy as np
# Load the image
im = cv2.imread('start.png')
# Calculate mean of green area
A = np.mean(im[600:640, 20:620], axis=(0,1))
得到绿色,不足为奇:
array([ 0., 255., 0.])
现在包括绿色上方的一些黑色区域以降低平均值“绿色度”
B = np.mean(im[500:640, 20:620], axis=(0,1))
这让... “少一点绿色”:
aarray([ 0. , 72.85714286, 0. ])
在我的Mac上对绿色区域的每个像素进行全采样需要214微秒,如下:
IIn [5]: %timeit A = np.mean(im[600:640, 20:620], axis=(0,1))
214 µs ± 150 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
请注意,您几乎可以肯定地在 50.6 微秒内如下所示每隔 4 个像素向下采样和每隔 4 个像素采样一次,并且仍然会得到一个非常具有指示性的结果:
In [11]: %timeit A = np.mean(im[500:640:4, 20:620:4], axis=(0,1))
50.6 µs ± 29.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
您可以像这样将要采样的每个像素都变成一个红点 - 仔细看:
im[600:640:4, 20:620:4] = [255,0,0]
根据 Fred (@fmw42) 的建议,如果将 np.mean()
替换为 cv2.mean()
会更快:
因此,cv2.mean()
为 11.4 微秒,而 np.mean()
为 214 微秒:
In [22]: %timeit cv2.mean(im[600:640, 20:620])
11.4 µs ± 11.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
如果每 4 个像素采样一次,cv2.mean()
为 7.85 微秒,而 np.mean()
为 50.6 微秒:
In [23]: %timeit cv2.mean(im[600:640:4, 20:620:4])
7.85 µs ± 6.42 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
我需要找到图像指定部分的平均颜色。具体来说,我需要知道里面的颜色是红色、绿色、棕色、黑色还是蓝色。
图片是640x640,我需要分析的区域是像素x1=20 y1=600和像素x2=620 y2=640之间的矩形。 (其中 x0y0 是左上角)
我找到了几个示例,例如这个 How to find the average colour of an image in Python with OpenCV?,但它们都处理整个图像。
如何获取指定区域的平均颜色?
必须是它必须尽可能快,低于 5 毫秒。
我的方法是遍历范围内的每个像素并进行数学运算,但我感觉开放式 CV 或类似库已经可以做到这一点。
感谢任何帮助。
这是 Python/OpenCV/Numpy 中的一种方法。
- 读取输入
- 使用 cv2.inRange() 的区域颜色阈值
- 可选地,应用形态学来清理小区域
- 可选地获取最大的轮廓并将其绘制为黑色背景上的白色填充作为最终蒙版
- 使用掩码计算区域的 BGR 分量平均值
- 将颜色名称定义为 select 测试颜色作为 Numpy 数组
- 定义颜色数组的数组
- 遍历数组数组中的每个数组并分离出颜色的 BGR 颜色分量
- 计算平均颜色和测试颜色之间的 RMSE
- 搜索最小RMSE及其对应的颜色名称
- 打印结果
输入:
黄色区域测试:
import cv2
import numpy as np
import math
# load image
img = cv2.imread("sailboat.png")
hh, ww = img.shape[:2]
# threshold
lower = (0,200,200)
upper = (50,255,255)
thresh = cv2.inRange(img, lower, upper)
# apply open morphology
#kernel = np.ones((5,5), np.uint8)
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
# get bounding box coordinates from largest external contour
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
# draw white filled contour on black background
mask = np.zeros((hh,ww), dtype=np.uint8)
cv2.drawContours(mask, [big_contour], 0, 255, cv2.FILLED)
# compute mean color for region
mean = cv2.mean(img, mask=mask)[0:3]
blue_mean = mean[0]
green_mean = mean[1]
red_mean = mean[2]
print("region_mean_color:", mean)
print("")
# define colors
red = np.array(["red",(0,0,255)],dtype=object)
green = np.array(["green",(0,255,0)],dtype=object)
brown = np.array(["brown",(20,70,140)],dtype=object)
black = np.array(["black",(0,0,0)],dtype=object)
blue = np.array(["blue",(255,0,0)],dtype=object)
yellow = np.array(["yellow",(0,255,255)],dtype=object)
min_rmse = 1000000
colors = np.array([red, green, brown, black, blue, yellow])
print("colorname", "rmse")
for color in colors:
bb = color[1][0]
gg = color[1][1]
rr = color[1][2]
rmse = math.sqrt( ( (red_mean-rr)*(red_mean-rr) + (green_mean-gg)*(green_mean-gg) + (blue_mean-bb)*(blue_mean-bb) )/3 )
colorname = color[0]
print(colorname,rmse)
if rmse < min_rmse:
min_rmse = rmse
match_color = color[0]
print("")
print("match_color:", match_color)
print("rmse:", min_rmse)
# write result to disk
cv2.imwrite("sailboat_thresh.jpg", thresh)
cv2.imwrite("sailboat_morph.jpg", morph)
cv2.imwrite("sailboat_mask.jpg", mask)
# display results
cv2.imshow("THRESH", thresh)
cv2.imshow("MORPH", morph)
cv2.imshow("MASK", mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
蒙版图片:
文字信息:
region_mean_color: (0.0, 254.65158457426497, 254.65158457426497)
colorname rmse
red 147.02329851590747
green 147.02329851590747
brown 126.01745055239607
black 207.92214813271502
blue 254.7677759924176
yellow 0.2844800038551275
match_color: yellow
rmse: 0.2844800038551275
由于您的感兴趣区域(ROI)只是一个简单的矩形,我想您只是想使用 Numpy 切片来识别它。
所以,我制作了一张测试图像,您要测量的地方是绿色的:
那么代码应该是这样的:
import cv2
import numpy as np
# Load the image
im = cv2.imread('start.png')
# Calculate mean of green area
A = np.mean(im[600:640, 20:620], axis=(0,1))
得到绿色,不足为奇:
array([ 0., 255., 0.])
现在包括绿色上方的一些黑色区域以降低平均值“绿色度”
B = np.mean(im[500:640, 20:620], axis=(0,1))
这让... “少一点绿色”:
aarray([ 0. , 72.85714286, 0. ])
在我的Mac上对绿色区域的每个像素进行全采样需要214微秒,如下:
IIn [5]: %timeit A = np.mean(im[600:640, 20:620], axis=(0,1))
214 µs ± 150 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
请注意,您几乎可以肯定地在 50.6 微秒内如下所示每隔 4 个像素向下采样和每隔 4 个像素采样一次,并且仍然会得到一个非常具有指示性的结果:
In [11]: %timeit A = np.mean(im[500:640:4, 20:620:4], axis=(0,1))
50.6 µs ± 29.3 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
您可以像这样将要采样的每个像素都变成一个红点 - 仔细看:
im[600:640:4, 20:620:4] = [255,0,0]
根据 Fred (@fmw42) 的建议,如果将 np.mean()
替换为 cv2.mean()
会更快:
因此,cv2.mean()
为 11.4 微秒,而 np.mean()
为 214 微秒:
In [22]: %timeit cv2.mean(im[600:640, 20:620])
11.4 µs ± 11.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
如果每 4 个像素采样一次,cv2.mean()
为 7.85 微秒,而 np.mean()
为 50.6 微秒:
In [23]: %timeit cv2.mean(im[600:640:4, 20:620:4])
7.85 µs ± 6.42 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)