Sreejith Sasidharan
Sreejith Sasidharan

Reputation: 1368

convert RGB values to equivalent HSV values using python

I want to convert RGB values to HSV using python. I got some code samples, which gave the result with the S and V values greater than 100. (example : http://code.activestate.com/recipes/576554-covert-color-space-from-hsv-to-rgb-and-rgb-to-hsv/ ) . anybody got a better code which convert RGB to HSV and vice versa

thanks

Upvotes: 26

Views: 70801

Answers (8)

quine9997
quine9997

Reputation: 834

1.) This is a complete script. It convert RGB(40, 32, 28) to HSV(20, 30, 26)

2.) Usage.

Run this script, in particular the function convert_rgb_to_hsv()

You can edit the variable red, green, blue.

import colorsys

#usage.
#run this script, in particular the function convert_rgb_to_hsv()
#edit red, green, blue



def convert_rgb_to_hsv():
    #rgb normal: range (0-255, 0-255, 0.255)
    red=40
    green=32
    blue=28

    
    
    #get rgb percentage: range (0-1, 0-1, 0-1 )
    red_percentage= red / float(255)
    green_percentage= green/ float(255)
    blue_percentage=blue / float(255)

    
    #get hsv percentage: range (0-1, 0-1, 0-1)
    color_hsv_percentage=colorsys.rgb_to_hsv(red_percentage, green_percentage, blue_percentage) 
    print('color_hsv_percentage: ', color_hsv_percentage)

    
    
    #get normal hsv: range (0-360, 0-255, 0-255)
    color_h=round(360*color_hsv_percentage[0])
    color_s=round(255*color_hsv_percentage[1])
    color_v=round(255*color_hsv_percentage[2])
    color_hsv=(color_h, color_s, color_h)

    print('color_hsv: ', color_hsv)



#INVOCATE MAIN FUNCTION
convert_rgb_to_hsv()

Upvotes: 1

Mark Loyman
Mark Loyman

Reputation: 2170

If you need to convert an image that is saved as numpy, you should either use:

  1. opencv see answer by George Pipis
  2. skimage:
from skimage.color import rgb2hsv
hsv_img = rgb2hsv(rgb_img)

Upvotes: 2

Ismael Harun
Ismael Harun

Reputation: 147

I've looked at the various libraries from opencv, colorsys and PIL.

colorsys is not very accurate but gets within 15 degrees. Most of the time within 3 degress. FOr the others there seems to be minor dependencies over a few degrees, but who is more correct? I don't know. But here is my numpyfied rgb to hsv code. I think it's pretty good but I am sure it could be better optimized somehow. The most obvious would be to combine the H, s, and v. Yes that is there also. Mine's more accurate then colorsys for sure, but you definitely see some small rounding errors. But hey it does the whole array in several steps at least. Sorry no invert code. Maybe another day I will write it. Ok so last thing is I don't assume scale. You have to add your own post scaling for the separate hue, sat, and val. the optional s argument is input scaling to 1, the dhue, dsat and dval are out scaling. And yes this is for 2dim arrays. it shouldn't be hard to fix it to work on while, or just reshape your stuff to 2dim and then back again.

def rgb_hue(rgb, s=1):
  nrgb = rgb.astype(np.float64) / float(s)
  nmax = np.max(nrgb,1)
  ndelta = nmax - np.min(nrgb,1)
  return (np.where(ndelta == 0, 0
      , np.where(nmax == nrgb[:,0], nrgb[:,1]-nrgb[:,2]
      , np.where(nmax == nrgb[:,1], (nrgb[:,2]-nrgb[:,0])+2
      , (nrgb[:,0]-nrgb[:,1])+4))) / 6.) % 1.

def rgb_saturation(rgb, s=1):
  nrgb = rgb.astype(np.float64) / float(s)
  nmax = np.max(nrgb,1)
  ndelta = nmax - np.min(nrgb,1)
  return np.where(nmax == 0, 0, ndelta / nmax)

def rgb_value(rgb, s=1):
  nrgb = rgb.astype(np.float64) / float(s)
  nmax = np.max(nrgb,1)
  return nmax

def rgb_hsv(rgb, s=1, dhue=1, dsat=1, dval=1):
  nrgb = rgb.astype(np.float64) / float(s)
  nmax = np.max(nrgb,1)
  ndelta = nmax - np.min(nrgb,1)
  hue = (np.where(ndelta == 0, 0
      , np.where(nmax == nrgb[:,0], nrgb[:,1]-nrgb[:,2]
      , np.where(nmax == nrgb[:,1], (nrgb[:,2]-nrgb[:,0])+2
      , (nrgb[:,0]-nrgb[:,1])+4))) / 6.) % 1.
  sat = np.where(nmax == 0, 0, ndelta / nmax)
  val = nmax
  return np.column_stack((hue*dhue, sat*dsat, val*dval))

Upvotes: -2

hahnec
hahnec

Reputation: 652

Based on array indexing and slicing in numpy, this is my approach for the forward:

import numpy as np

def rgb2hsv(rgb):
    """ convert RGB to HSV color space

    :param rgb: np.ndarray
    :return: np.ndarray
    """

    rgb = rgb.astype('float')
    maxv = np.amax(rgb, axis=2)
    maxc = np.argmax(rgb, axis=2)
    minv = np.amin(rgb, axis=2)
    minc = np.argmin(rgb, axis=2)

    hsv = np.zeros(rgb.shape, dtype='float')
    hsv[maxc == minc, 0] = np.zeros(hsv[maxc == minc, 0].shape)
    hsv[maxc == 0, 0] = (((rgb[..., 1] - rgb[..., 2]) * 60.0 / (maxv - minv + np.spacing(1))) % 360.0)[maxc == 0]
    hsv[maxc == 1, 0] = (((rgb[..., 2] - rgb[..., 0]) * 60.0 / (maxv - minv + np.spacing(1))) + 120.0)[maxc == 1]
    hsv[maxc == 2, 0] = (((rgb[..., 0] - rgb[..., 1]) * 60.0 / (maxv - minv + np.spacing(1))) + 240.0)[maxc == 2]
    hsv[maxv == 0, 1] = np.zeros(hsv[maxv == 0, 1].shape)
    hsv[maxv != 0, 1] = (1 - minv / (maxv + np.spacing(1)))[maxv != 0]
    hsv[..., 2] = maxv

    return hsv

and backward color space conversion:

def hsv2rgb(hsv):
    """ convert HSV to RGB color space

    :param hsv: np.ndarray
    :return: np.ndarray
    """

    hi = np.floor(hsv[..., 0] / 60.0) % 6
    hi = hi.astype('uint8')
    v = hsv[..., 2].astype('float')
    f = (hsv[..., 0] / 60.0) - np.floor(hsv[..., 0] / 60.0)
    p = v * (1.0 - hsv[..., 1])
    q = v * (1.0 - (f * hsv[..., 1]))
    t = v * (1.0 - ((1.0 - f) * hsv[..., 1]))

    rgb = np.zeros(hsv.shape)
    rgb[hi == 0, :] = np.dstack((v, t, p))[hi == 0, :]
    rgb[hi == 1, :] = np.dstack((q, v, p))[hi == 1, :]
    rgb[hi == 2, :] = np.dstack((p, v, t))[hi == 2, :]
    rgb[hi == 3, :] = np.dstack((p, q, v))[hi == 3, :]
    rgb[hi == 4, :] = np.dstack((t, p, v))[hi == 4, :]
    rgb[hi == 5, :] = np.dstack((v, p, q))[hi == 5, :]

    return rgb

I was motivated to write these lines as I wasn't convinced by a pixel-wise conversion due to the computational overload and also did not want to rely on another library such as OpenCV.

Feel free to suggest a modification to make this solution more elegant and generic.

Upvotes: 10

George Pipis
George Pipis

Reputation: 1822

I suggest to work with OpenCV

import cv2

# Read the image - Notice that OpenCV reads the images as BRG instead of RGB
img = cv2.imread('myimage.jpg')

# Convert the BRG image to RGB
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# Convert the RGB image to HSV
img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)

Upvotes: 7

K3---rnc
K3---rnc

Reputation: 7050

If using PIL, with a recent copy of Pillow, one should probably use

def rgb2hsv(image):
    return image.convert('HSV')

Upvotes: 10

Brooks Moses
Brooks Moses

Reputation: 9527

What values of R, G, and B did you put in, and what values of H, S, and V that look erroneous did you get out?

The code you link to, like colorsys, expects float values between 0.0 and 1.0. If you call it with integer values in the 0-to-255 range, you'll get bogus results. It should work fine if you give it the input values it expects.

(It is a distinct failure of that code sample that it does not actually document what sorts of inputs it expects.)

Upvotes: 0

Eli Bendersky
Eli Bendersky

Reputation: 273366

Did you try using the colorsys library?

The colorsys module defines bidirectional conversions of color values between colors expressed in the RGB (Red Green Blue) color space used in computer monitors and three other coordinate systems: YIQ, HLS (Hue Lightness Saturation) and HSV (Hue Saturation Value)

Example (taken from the above link):

>>> import colorsys
>>> colorsys.rgb_to_hsv(.3, .4, .2)
(0.25, 0.5, 0.4)
>>> colorsys.hsv_to_rgb(0.25, 0.5, 0.4)
(0.3, 0.4, 0.2)

Upvotes: 44

Related Questions