# Adjust hue and saturation like Photoshop

Hello everybody, In Photoshop there are sliders that allow users to adjust hue and saturation values.

The range of hue is [-180, 180] and the range of saturation is [-100, 100].

How photoshop works when I change the values of the sliders?

For example, if I set hue to -60 and saturation to 100, how it changes the value of the pixels? And how can I do the same with opencv?

edit retag close merge delete

why not just use photoshop, instead of abusing a computer-vision library for this ?

( 2017-11-20 12:29:34 -0600 )edit

Because I need a preprocessing step before analize the picture and the preprocessing consists on adjusting hue and saturation

( 2017-11-20 12:33:49 -0600 )edit
1

Here is a simple code to shift the hue on an image. There may be bugs:

Mat frame = imread("puppets.png");

Mat hsv;
cvtColor(frame, hsv, CV_BGR2HSV);

const unsigned char hue_shift = 20;

for (int j = 0; j < frame.rows; j++)
{
for (int i = 0; i < frame.cols; i++)
{
// Get hue.
// Saturation is hsv.at<Vec3b>(j, i)[1], and
// Value is hsv.at<Vec3b>(j, i)[2].
unsigned char h = hsv.at<Vec3b>(j, i)[0];

if (h + hue_shift > 180)
h = (h + hue_shift) - 180;
else
h = h + hue_shift;

// Set hue.
hsv.at<Vec3b>(j, i)[0] = h;
}
}

cvtColor(hsv, frame, CV_HSV2BGR);

( 2017-11-20 13:24:40 -0600 )edit

Note that the inner loop body can be replaced with this simpler, likely slower version:

hsv.at<Vec3b>(j, i)[0] = (hsv.at<Vec3b>(j, i)[0] + hue_shift) % 180;

( 2017-11-20 15:36:49 -0600 )edit

Thank you for the answer. I have two more questions:

1. What happens when the shift is negative and the result value is < 0? Do I have to set the hue to zero?
2. For the saturation is the same thing? I just have to change 180 in 256?
( 2017-11-20 17:59:46 -0600 )edit

You're welcome.

1. My code doesn't handle that case. It's up to you to write a more general code. You should post it as answer once you've got it figured out. :)
2. Saturation is from 0 to 255. Likewise with Value. I got these ranges by googling for "opencv hsv values".

Edit: Try this inner loop body to handle negative hue shift:

        signed short h = hsv.at<Vec3b>(j, i)[0];
signed short h_plus_shift = h;
h_plus_shift += hue_shift;

if (h_plus_shift < 0)
h = 180 + h_plus_shift;
else if (h_plus_shift > 180)
h = h_plus_shift - 180;
else
h = h_plus_shift;

hsv.at<Vec3b>(j, i)[0] = static_cast<unsigned char>(h);

( 2017-11-20 18:08:34 -0600 )edit

Yes! Thank you! It definitely works great!

( 2017-11-22 04:11:02 -0600 )edit

Sort by ยป oldest newest most voted

can anybody provide python equivalent code. Thank you in advance.

more

Upvote my answer first, and then I'll work on the Python code for you.

( 2017-12-19 08:32:55 -0600 )edit
1

i have less than 5 points so i cant vote you. i will vote if i earn points. I am new to this community.

( 2017-12-19 22:10:30 -0600 )edit

OK. You should have more than 5 karma. Do you have anycode to start from? I found a trackbar example in Python: https://docs.opencv.org/3.0-beta/doc/... and another one: https://github.com/kyatou/python-open...

( 2017-12-20 09:45:27 -0600 )edit

I converted the code to Python, but it doesn't work very well. Maybe you can experiment with it to see if you can get it to work:

https://github.com/sjhalayka/hue

Oh, and it runs at a glacial speed. :( See kbami's comments in my answer for optimization ideas.

( 2017-12-20 15:28:39 -0600 )edit

ok thankx sjhalayka i am working on it. I will post working code soon.

( 2017-12-22 23:45:55 -0600 )edit

After many attempts, the following code seems to do what you want in terms of shifting the hue, saturation, and value:

#include <opencv2/opencv.hpp>
using namespace cv;
#pragma comment(lib, "opencv_world331.lib")

#include <iostream>
using namespace std;

int int_hue = 180;
int int_saturation = 100;
int int_value = 100;

static void on_trackbar(int, void*)
{

Mat hsv;
cvtColor(frame, hsv, CV_BGR2HSV);
signed short hue_shift = (int_hue - 180) / 2;

for (int j = 0; j < frame.rows; j++)
{
for (int i = 0; i < frame.cols; i++)
{
signed short h = hsv.at<Vec3b>(j, i)[0];
signed short h_plus_shift = h;
h_plus_shift += hue_shift;

if (h_plus_shift < 0)
h = 180 + h_plus_shift;
else if (h_plus_shift > 180)
h = h_plus_shift - 180;
else
h = h_plus_shift;

hsv.at<Vec3b>(j, i)[0] = static_cast<unsigned char>(h);

double s = hsv.at<Vec3b>(j, i)[1];
double s_shift = (int_saturation - 100) / 100.0;
double s_plus_shift = s + 255.0*s_shift;

if (s_plus_shift < 0)
s_plus_shift = 0;
else if (s_plus_shift > 255)
s_plus_shift = 255;

hsv.at<Vec3b>(j, i)[1] = static_cast<unsigned char>(s_plus_shift);

double v = hsv.at<Vec3b>(j, i)[2];
double v_shift = (int_value - 100) / 100.0;
double v_plus_shift = v + 255.0 * v_shift;

if (v_plus_shift < 0)
v_plus_shift = 0;
else if (v_plus_shift > 255)
v_plus_shift = 255;

hsv.at<Vec3b>(j, i)[2] = static_cast<unsigned char>(v_plus_shift);
}
}

cvtColor(hsv, frame, CV_HSV2BGR);

imshow("frame", frame);
}

int main(void)
{
imshow("frame", frame);

createTrackbar("Hue", "frame", &int_hue, 360, on_trackbar);
createTrackbar("Saturation", "frame", &int_saturation, 200, on_trackbar);
createTrackbar("Value", "frame", &int_value, 200, on_trackbar);
on_trackbar(0, 0);

waitKey(0);

destroyAllWindows();

return 0;
}

more

Some suggestions:

• use short values instead of double
• calculate s_shift and v_shift before the loop
• use line pointer access
• Simplify Hue calculation:

short s_shift=(short)(s_val*2.55);
short v_shift=(short)(s_val*2.55);
uchar h,s,v;
short h_new,s_new,v_new;
if(hue_shift<0) hue_shift+=180;
for (int j = 0; j < frame.rows; j++){
Vec3b *p=(Vec3b*)frame.ptr(j);
for(int i=0;i<frame.cols;i++){
h=p[i][0];
h_new=(h + hue_shift) % 180;
p[i][0]= static_cast<unsigned char>(h_new);

s=p[i][1];
s_new=s+s_shift;
if(s_new<0)s_new=0;
if(s_new>255)s_new=255;
p[i][1]= static_cast<unsigned char>(s_new);

( 2017-11-23 04:54:47 -0600 )edit

Thanks for the optimization ideas.

( 2017-11-23 10:26:37 -0600 )edit

Official site

GitHub

Wiki

Documentation