Search code examples
c++opencvvisual-studio-2019photo

How to increase the saturation values of an image using HSV (in OpenCV using C++)?


I was looking for a way to increase the saturation of some of my images using code and found the strategy of splitting a material with HSV and then increasing the S channel by a factor. However, I ran into some issues where the split channels were still in BGR (I think) because the output was just a greener tinted version of the original.

    //Save original image to material
    Mat orgImg = imread("sunset.jpg");
    //Resize the image to be smaller
    resize(orgImg, orgImg, Size(500, 500));
    //Display the original image for comparison
    imshow("Original Image", orgImg);
    
    Mat g = Mat::zeros(Size(orgImg.cols, orgImg.rows), CV_8UC1);

    Mat convertedHSV;
    orgImg.convertTo(convertedHSV, COLOR_BGR2HSV);

    Mat saturatedImg;
    
    Mat HSVChannels[3];
    split(convertedHSV, HSVChannels);

    imshow("H", HSVChannels[0]);
    imshow("S", HSVChannels[1]);
    imshow("V", HSVChannels[2]);

    HSVChannels[1] *= saturation;
    merge(HSVChannels, 3, saturatedImg);

    //Saturate the original image and save it to a new material.
    
    //Display the new, saturated image.
    imshow("Saturated", saturatedImg);

    waitKey(0);
    return 0;

This is my code and nothing I do makes it actually edit the saturation, all the outputs are just green tinted photos. Note saturation is a public double that is usually set to around 1.5 or whatever you want.


Solution

  • Do not use cv::convertTo() here. It changes the bitdepth (and representation, int vs. float) of the image, not what you are trying to achieve, the color space.

    Using it like that does not throw a warning or error though, because both type indicators (CV_8U, ...) and the colorspace indicators (COLOR_BGR2HSV,...) can be resolved as integers, one is a #define, the other a old style enum.

    Following the example here, it is possible to do with cv::cvtColor(). Don't forget to revert back before showing the image, imshow() and imwrite() both expect an BGR format.

    // Convert image from BGR -> HSV:
    // orgImg.convertTo(convertedHSV, COLOR_BGR2HSV); // <- this wrong, do not use
    cvtColor(orgImg, convertedHSV, COLOR_BGR2HSV); // <- this does the trick instead
    
    // to the split, multiplication, merge
    // [...]
    
    
    // Convert image back HSV -> BGR:
    cvtColor(saturatedImg, saturatedImg, COLOR_HSV2BGR);
    
    //Display the new, saturated image.
    imshow("Saturated", saturatedImg);
    
    

    Note that oCV does not care about color representation when working with a 3 channel Mat: Could be RGB, HSV or anything else. Only for displaying (or saving to an image format) does the given color space matter.