# 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

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

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

Official site

GitHub

Wiki

Documentation