user2909415
user2909415

Reputation: 1029

Numpy Image Arrays: How to efficiently switch from RGB to Hex

I have been using nested for loops to turn RGB images into an image array of Hex values, but it is too slow for large images. Does anyone know a quick way and or a library that can help me switch back and from RGB to HEX?

edit: @ragingSloth

This is what I came up with but it is too slow for what I need:

def rgb_to_hex(array):
    (x, y, z) = array.shape
    for v in range(0, x):
        for u in range(0, y):
            array[v, u] = int('%02x%02x%02x' % (array[v, u, 0], array[v, u, 1], array[v, u, 2]))

Upvotes: 3

Views: 2335

Answers (3)

unutbu
unutbu

Reputation: 880677

Using beaker's idea, you can also eliminate the double for-loop:

def tohex(array):
    array = np.asarray(array, dtype='uint32')
    return ((array[:, :, 0]<<16) + (array[:, :, 1]<<8) + array[:, :, 2])

Upvotes: 6

askewchan
askewchan

Reputation: 46578

Not sure if this is much faster, but you could do something like this:

hexarr = np.vectorize('{:02x}'.format)

And then run it on an RGB array:

In [67]: a = (np.random.rand(2,5,3)*255).astype('u1')

In [68]: a
Out[68]:
array([[[149, 145, 203],
        [210, 234, 219],
        [223,  50,  26],
        [166,  34,  65],
        [213,  78, 115]],

       [[191,  54, 168],
        [ 85, 235,  36],
        [180, 140,  96],
        [127,  21,  24],
        [166, 210, 128]]], dtype=uint8)

In [69]: hexarr(a)
Out[69]:
array([[['95', '91', 'cb'],
        ['d2', 'ea', 'db'],
        ['df', '32', '1a'],
        ['a6', '22', '41'],
        ['d5', '4e', '73']],

       [['bf', '36', 'a8'],
        ['55', 'eb', '24'],
        ['b4', '8c', '60'],
        ['7f', '15', '18'],
        ['a6', 'd2', '80']]],
      dtype='|S2')

You can collapse the third dimension with view:

In [71]: hexarr(a).view('S6')
Out[71]:
array([[['9591cb'],
        ['d2eadb'],
        ['df321a'],
        ['a62241'],
        ['d54e73']],

       [['bf36a8'],
        ['55eb24'],
        ['b48c60'],
        ['7f1518'],
        ['a6d280']]],
      dtype='|S6')

Unfortunately, this doesn't seem to be a whole lot faster (just over twice as fast):

In [89]: timeit rgb_to_hex(a)
1 loops, best of 3: 6.83 s per loop

In [90]: timeit hexarr(a).view('S6')
1 loops, best of 3: 2.54 s per loop

Upvotes: 1

beaker
beaker

Reputation: 16811

String operations are probably pretty slow. A direct mathematical approach would be:

array[v, u] = ((array[v, u, 0]<<16) + (array[v, u, 1]<<8) + array[v, u, 2])

This combines the 3 bytes of the RGB representation into a single int:

>>> A = [123, 255, 255]
>>> B = (A[0]<<16) + (A[1]<<8) + A[2]
>>> B
8126463
>>> hex(B)
'0x7bffff'

Upvotes: 5

Related Questions