【问题标题】:How to Convert all pixel values of an image to a certain range -python如何将图像的所有像素值转换为一定范围-python
【发布时间】:2017-04-13 14:05:21
【问题描述】:

我有一个具有 12 种不同颜色的 rgb 图像,但我事先不知道颜色(像素值)。我想转换 0 到 11 之间的所有像素值,每个都代表原始 rgb 图像的唯一颜色。

例如所有 [230,100,140] 转换为 [0,0,0] ,所有 [130,90,100] 转换为 [0,0,1] 依此类推...所有 [210,80,50] 转换为 [0,0, 11]。

【问题讨论】:

  • 那么你首先构造一组颜色,然后将它们映射到索引上?
  • @Miki:但这并没有为像素分配索引。在这里您选择 12 种颜色。但我们已经知道图像只包含 12 种颜色。
  • 您想将一个范围转换为另一个范围。检查此链接:stackoverflow.com/questions/929103/…
  • 没有GetPixel()/SetPixel()函数吗?
  • 你不应该为这些颜色三元组之一分配一个标量值,即[230,100,140] 被转换为0 吗?

标签: python python-2.7 opencv numpy


【解决方案1】:

快速而肮脏的应用程序。有很多可以改进的地方,尤其是逐个像素地遍历整个图像不是很numpy也不是很opencv,但是我懒得去确切地记住如何阈值和替换RGB像素..

import cv2
import numpy as np

#finding unique rows
#comes from this answer : http://stackoverflow.com/questions/8560440/removing-duplicate-columns-and-rows-from-a-numpy-2d-array
def unique_rows(a):
    a = np.ascontiguousarray(a)
    unique_a = np.unique(a.view([('', a.dtype)]*a.shape[1]))
    return unique_a.view(a.dtype).reshape((unique_a.shape[0], a.shape[1]))

img=cv2.imread(your_image)

#listing all pixels
pixels=[]
for p in img:
    for k in p:
        pixels.append(k)

#finding all different colors
colors=unique_rows(pixels)

#comparing each color to every pixel
res=np.zeros(img.shape)
cpt=0
for color in colors:
    for i in range(img.shape[0]):
        for j in range(img.shape[1]):
            if (img[i,j,:]==color).all(): #if pixel is this color
                res[i,j,:]=[0,0,cpt] #set the pixel to [0,0,counter]
    cpt+=1

【讨论】:

    【解决方案2】:

    你可以使用 np.unique 有点诡计:

    import numpy as np
    
    def safe_method(image, k):
        # a bit of black magic to make np.unique handle triplets
        out = np.zeros(image.shape[:-1], dtype=np.int32)
        out8 = out.view(np.int8)
        # should really check endianness here
        out8.reshape(image.shape[:-1] + (4,))[..., 1:] = image
        uniq, map_ = np.unique(out, return_inverse=True)
        assert uniq.size == k
        map_.shape = image.shape[:-1]
        # map_ contains the desired result. However, order of colours is most
        # probably different from original
        colours = uniq.view(np.uint8).reshape(-1, 4)[:, 1:]
        return colours, map_
    

    但是,如果像素数远大于颜色数, 以下启发式算法可能会带来巨大的加速。 它试图找到一个廉价的散列函数(例如只查看红色通道),如果成功,它会使用它来创建一个查找表。如果不是,则回退到上述安全方法。

    CHEAP_HASHES = [lambda x: x[..., 0], lambda x: x[..., 1], lambda x: x[..., 2]]
    
    def fast_method(image, k):
        # find all colours
        chunk = int(4 * k * np.log(k)) + 1
        colours = set()
        for chunk_start in range(0, image.size // 3, chunk):
            colours |= set(
                map(tuple, image.reshape(-1,3)[chunk_start:chunk_start+chunk]))
            if len(colours) == k:
                break
        colours = np.array(sorted(colours))
        # find hash method
        for method in CHEAP_HASHES:
            if len(set(method(colours))) == k:
                break
        else:
            safe_method(image, k)
        # create lookup table
        hashed = method(colours)
        # should really provide for unexpected colours here
        lookup = np.empty((hashed.max() + 1,), int)
        lookup[hashed] = np.arange(k)
        return colours, lookup[method(image)]
    

    测试和时间安排:

    from timeit import timeit
    
    def create_image(k, M, N):
        colours = np.random.randint(0, 256, (k, 3)).astype(np.uint8)
        map_ = np.random.randint(0, k, (M, N))
        image = colours[map_, :]
        return colours, map_, image
    
    k, M, N = 12, 1000, 1000
    
    colours, map_, image = create_image(k, M, N)
    
    for f in fast_method, safe_method:
        print('{:16s} {:10.6f} ms'.format(f.__name__, timeit(
            lambda: f(image, k), number=10)*100))
        rec_colours, rec_map_ = f(image, k)
        print('solution correct:', np.all(rec_colours[rec_map_, :] == image))
    

    样本输出(12 色,1000x1000 像素):

    fast_method        3.425885 ms
    solution correct: True
    safe_method       73.622813 ms
    solution correct: True
    

    【讨论】:

    • safe_method() 有效。 fast_method() 在第一个for 循环中抛出错误TypeError: 'numpy.ndarray' object is not callable
    • @FatehSingh 嗯,你没有任何机会隐藏一个内置函数吗?因为据我所知,该循环中唯一的函数调用是range, set, map, tuplelen(我认为我们可以排除image.reshape)。您能否检查其中一个是否是数组。如果是这样,您应该将数组重命名为其他名称。您可以使用import builtins 取回内置函数,然后使用例如map = builtins.map
    • 是的,它有效,是名称冲突导致了问题。谢谢
    猜你喜欢
    • 2018-12-03
    • 2014-11-26
    • 2018-09-13
    • 2018-09-28
    • 2020-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-03
    相关资源
    最近更新 更多