Python 3.x 矢量化自定义RGB->灰度转换
如标题所述,我想做一个从RGB到灰度的非常具体的转换 我有一堆图像可能看起来像这样: 我想把它们转换成这样的图像 现在你可能想知道为什么我不只是使用opencv的内置函数。原因是我需要将RGB图像的每种颜色映射到灰度中的特定强度值,这并不困难,因为我只有六种颜色Python 3.x 矢量化自定义RGB->灰度转换,python-3.x,performance,numpy,opencv,Python 3.x,Performance,Numpy,Opencv,如标题所述,我想做一个从RGB到灰度的非常具体的转换 我有一堆图像可能看起来像这样: 我想把它们转换成这样的图像 现在你可能想知道为什么我不只是使用opencv的内置函数。原因是我需要将RGB图像的每种颜色映射到灰度中的特定强度值,这并不困难,因为我只有六种颜色 Red, rgb(255,0,0) -> 25 Brown, rgb(165,42,42) -> 120 Light Blue, rgb(0,255,255) -> 127 G
Red, rgb(255,0,0) -> 25
Brown, rgb(165,42,42) -> 120
Light Blue, rgb(0,255,255) -> 127
Green, rgb(127,255,0) -> 50
Yellow, rgb(255,255,255) -> 159
Purple, rgb(128, 0, 128) -> 90
现在我已经用一些包含这些映射的对象创建了一个数组,我只是在我的图像上迭代来分配新的颜色代码。然而,这是非常缓慢的,我希望在这之前,所有的图像完成一个宏伟的胡须,我也想知道这是为了学习的目的。这是我目前运行速度非常慢的代码和映射对象:
colorMapping = [ColorMapping(RGB=[255, 0, 0], Grayscale=25),
ColorMapping(RGB=[165, 42, 42], Grayscale=120), ... ]
def RGBtoGray(RGBimg, colorMapping):
RGBimg = cv2.cvtColor(RGBimg, cv2.COLOR_BGR2RGB)
row = RGBimg.shape[0]
col = RGBimg.shape[1]
GRAYimg = np.zeros((row, col))
for x in range(0,row):
for y in range(0,col):
pixel = RGBimg[x,y,:]
for cm in colorMapping:
if np.array_equal(pixel, np.array(cm.RGB)):
GRAYimg[x,y] = cm.Grayscale
return GRAYimg
对于使用内置库或改进此代码计算的任何建议,我都很高兴。
颜色映射是从json文件读取的,这是一个自动化步骤,因为我必须至少对两批不同编码的图像执行此操作。这可能与其他任何操作一样简单。它确实会在你的图像上传递6次,所以一些聪明的裸体人可能知道一个更好的方法,但它会比你的循环快得多
#!/usr/bin/env python3
import numpy as np
import cv2
# Load image
im = cv2.imread('blobs.png')
# Make output image
res = np.zeros_like(im[:,:,0])
res[np.all(im == (0, 0, 255), axis=-1)] = 25
res[np.all(im == (42,42,165), axis=-1)] = 120
res[np.all(im == (255,255,0), axis=-1)] = 127
res[np.all(im == (0,255,127), axis=-1)] = 50
res[np.all(im == (255,255,255), axis=-1)] = 159
res[np.all(im == (128,0,128), axis=-1)] = 90
# Write image of just palette indices
cv2.imwrite('indices.png', res)
受answer的启发,您可以将每个RGB三元组转换为单个24位数字,从而使其在5毫秒和30毫秒内运行
这可能和其他任何事情一样简单。它确实会在你的图像上传递6次,所以一些聪明的裸体人可能知道一个更好的方法,但它会比你的循环快得多
#!/usr/bin/env python3
import numpy as np
import cv2
# Load image
im = cv2.imread('blobs.png')
# Make output image
res = np.zeros_like(im[:,:,0])
res[np.all(im == (0, 0, 255), axis=-1)] = 25
res[np.all(im == (42,42,165), axis=-1)] = 120
res[np.all(im == (255,255,0), axis=-1)] = 127
res[np.all(im == (0,255,127), axis=-1)] = 50
res[np.all(im == (255,255,255), axis=-1)] = 159
res[np.all(im == (128,0,128), axis=-1)] = 90
# Write image of just palette indices
cv2.imwrite('indices.png', res)
受answer的启发,您可以将每个RGB三元组转换为单个24位数字,从而使其在5毫秒和30毫秒内运行
方法1
这是一个基于1D变换+np的矢量化搜索排序,灵感来自-
样本运行-
# Mapping colors array
In [197]: colors
Out[197]:
array([[255, 0, 0],
[165, 42, 42],
[ 0, 255, 255],
[127, 255, 0],
[255, 255, 255],
[128, 0, 128]])
# Mapping values array
In [198]: vals
Out[198]: array([ 25, 120, 127, 50, 155, 90])
# Input 3D image array
In [199]: img
Out[199]:
array([[[255, 255, 255],
[128, 0, 128],
[255, 0, 0],
[127, 255, 0]],
[[127, 255, 0],
[127, 255, 0],
[165, 42, 42],
[ 0, 0, 0]]]) # <= one color absent in mappings
# Output
In [200]: map_colors(img, colors, vals, invalid_val=0)
Out[200]:
array([[155, 90, 25, 50],
[ 50, 50, 120, 0]])
方法2
我们可以使用一个映射数组,当通过1D变换颜色索引时,它将直接引导我们到最终的灰度图像,如下所示-
def map_colors_with_mappingar_solution(img):
# Edit the custom colors and values here
colors = np.array([
[ 0, 0, 255],
[ 42, 42, 165],
[255, 255, 0],
[ 0, 255, 127],
[255, 255, 255],
[128, 0, 128]], dtype=np.uint8) # BGR format
vals = np.array([25, 120, 127, 50, 155, 90], dtype=np.uint8)
return map_colors_with_mappingar(img, colors, vals, 0)
def map_colors_with_mappingar(img, colors, vals, invalid_val=0):
s = 256**np.arange(3)
img1D = img.reshape(-1,img.shape[2]).dot(s)
colors1D = colors.reshape(-1,img.shape[2]).dot(s)
N = colors1D.max()+1
mapar = np.empty(N, dtype=np.uint8)
mapar[colors1D] = vals
mask = np.zeros(N, dtype=bool)
mask[colors1D] = True
valid = img1D < N
valid &= mask[img1D]
out = np.full(len(img1D), invalid_val, dtype=np.uint8)
out[valid] = mapar[img1D[valid]]
return out.reshape(img.shape[:2])
进一步表现。促进
再进一步,我们可以以更高性能的方式进行1D缩减,从而实现更高的性能。像这样-
# https://stackoverflow.com/a/57236217/ @tstanisl
def scalarize(x):
# compute x[...,2]*65536+x[...,1]*256+x[...,0] in efficient way
y = x[...,2].astype('u4')
y <<= 8
y +=x[...,1]
y <<= 8
y += x[...,0]
return y
def map_colors_with_mappingar(img, colors, vals, invalid_val=0):
img1D = scalarize(img)
colors1D = scalarize(colors)
N = colors1D.max()+1
mapar = np.empty(N, dtype=np.uint8)
mapar[colors1D] = vals
mask = np.zeros(N, dtype=bool)
mask[colors1D] = True
valid = img1D < N
valid &= mask[img1D]
out = np.full(img1D.shape, invalid_val, dtype=np.uint8)
out[valid] = mapar[img1D[valid]]
return out
# On given sample image
In [10]: %timeit map_colors_with_mappingar_solution(im)
5.45 ms ± 143 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
方法1
这是一个基于1D变换+np的矢量化搜索排序,灵感来自-
样本运行-
# Mapping colors array
In [197]: colors
Out[197]:
array([[255, 0, 0],
[165, 42, 42],
[ 0, 255, 255],
[127, 255, 0],
[255, 255, 255],
[128, 0, 128]])
# Mapping values array
In [198]: vals
Out[198]: array([ 25, 120, 127, 50, 155, 90])
# Input 3D image array
In [199]: img
Out[199]:
array([[[255, 255, 255],
[128, 0, 128],
[255, 0, 0],
[127, 255, 0]],
[[127, 255, 0],
[127, 255, 0],
[165, 42, 42],
[ 0, 0, 0]]]) # <= one color absent in mappings
# Output
In [200]: map_colors(img, colors, vals, invalid_val=0)
Out[200]:
array([[155, 90, 25, 50],
[ 50, 50, 120, 0]])
方法2
我们可以使用一个映射数组,当通过1D变换颜色索引时,它将直接引导我们到最终的灰度图像,如下所示-
def map_colors_with_mappingar_solution(img):
# Edit the custom colors and values here
colors = np.array([
[ 0, 0, 255],
[ 42, 42, 165],
[255, 255, 0],
[ 0, 255, 127],
[255, 255, 255],
[128, 0, 128]], dtype=np.uint8) # BGR format
vals = np.array([25, 120, 127, 50, 155, 90], dtype=np.uint8)
return map_colors_with_mappingar(img, colors, vals, 0)
def map_colors_with_mappingar(img, colors, vals, invalid_val=0):
s = 256**np.arange(3)
img1D = img.reshape(-1,img.shape[2]).dot(s)
colors1D = colors.reshape(-1,img.shape[2]).dot(s)
N = colors1D.max()+1
mapar = np.empty(N, dtype=np.uint8)
mapar[colors1D] = vals
mask = np.zeros(N, dtype=bool)
mask[colors1D] = True
valid = img1D < N
valid &= mask[img1D]
out = np.full(len(img1D), invalid_val, dtype=np.uint8)
out[valid] = mapar[img1D[valid]]
return out.reshape(img.shape[:2])
进一步表现。促进
再进一步,我们可以以更高性能的方式进行1D缩减,从而实现更高的性能。像这样-
# https://stackoverflow.com/a/57236217/ @tstanisl
def scalarize(x):
# compute x[...,2]*65536+x[...,1]*256+x[...,0] in efficient way
y = x[...,2].astype('u4')
y <<= 8
y +=x[...,1]
y <<= 8
y += x[...,0]
return y
def map_colors_with_mappingar(img, colors, vals, invalid_val=0):
img1D = scalarize(img)
colors1D = scalarize(colors)
N = colors1D.max()+1
mapar = np.empty(N, dtype=np.uint8)
mapar[colors1D] = vals
mask = np.zeros(N, dtype=bool)
mask[colors1D] = True
valid = img1D < N
valid &= mask[img1D]
out = np.full(img1D.shape, invalid_val, dtype=np.uint8)
out[valid] = mapar[img1D[valid]]
return out
# On given sample image
In [10]: %timeit map_colors_with_mappingar_solution(im)
5.45 ms ± 143 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
转换为灰度后,6种RGB颜色的强度不同:76、79179188255和53。由于它们是唯一的,我们可以使用2个已经优化的步骤。首先,转换RGB2GRAY,然后应用LUT重新映射强度。@DanMašek是的,这也是我最初的想法:-但OP有其他可能不那么友好的贴图。@MarkSetchell很好。转换为灰度后,6种RGB颜色的强度不同:76,79179188255和53。由于它们是唯一的,我们可以使用2个已经优化的步骤。首先,转换RGB2GRAY,然后应用LUT重新映射强度。@DanMašek是的,这也是我最初的想法:-但OP有其他可能不那么友好的贴图。@MarkSetchell说得好。我尝试了方法一,它比以前快了很多!我试了100次方法一,结果比以前快了很多!大概100次吧