Ask Your Question
1

Adjust hue and saturation like Photoshop

asked 2017-11-20 12:27:05 -0600

macc.n gravatar image

updated 2017-11-20 12:27:26 -0600

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

image description

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?

Thank for your help.

edit retag flag offensive close merge delete

Comments

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

berak gravatar imageberak ( 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

macc.n gravatar imagemacc.n ( 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);
sjhalayka gravatar imagesjhalayka ( 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;
sjhalayka gravatar imagesjhalayka ( 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?
macc.n gravatar imagemacc.n ( 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);
sjhalayka gravatar imagesjhalayka ( 2017-11-20 18:08:34 -0600 )edit

Yes! Thank you! It definitely works great!

macc.n gravatar imagemacc.n ( 2017-11-22 04:11:02 -0600 )edit

2 answers

Sort by ยป oldest newest most voted
-1

answered 2017-11-22 16:12:11 -0600

sjhalayka gravatar image

updated 2017-11-22 20:05:13 -0600

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;

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


static void on_trackbar(int, void*)
{
    frame = imread("puppets.png");

    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;
}
edit flag offensive delete link more

Comments

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);
    
kbarni gravatar imagekbarni ( 2017-11-23 04:54:47 -0600 )edit

Thanks for the optimization ideas.

sjhalayka gravatar imagesjhalayka ( 2017-11-23 10:26:37 -0600 )edit
1

answered 2017-12-18 22:45:43 -0600

pradip gravatar image

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

edit flag offensive delete link more

Comments

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

sjhalayka gravatar imagesjhalayka ( 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.

pradip gravatar imagepradip ( 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...

sjhalayka gravatar imagesjhalayka ( 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.

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

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

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

Question Tools

1 follower

Stats

Asked: 2017-11-20 12:27:05 -0600

Seen: 13,098 times

Last updated: Nov 22 '17