Color Discrepancy Between OpenCV and Photoshop Hue Adjustments

82 views Asked by At

1

Problem:
I'm encountering an issue with color while trying to apply a selected hue value into some image data using OpenCV. After applying the hue value +50, the output color doesn't resemble what I see in Photoshop when I check its +50 result.

  • The first screenshot shows the original image without applying the hue value.
  • The second screenshot displays the image with a +50 hue value in Photoshop.
  • The third screenshot shows the image with a +50 hue value using the OpenCV code.
    (as displayed in: ImGui).

Question:
How can I adjust the code to make the image appear similar to the one adjusted with a +50 hue value in Photoshop?

In the code below, currentImage contains the image data in CV::Mat format, and hueFactor represents the applied hue value, which is +50.

I've implemented the following function:

cv::Mat apply_hue_for_data(cv::Mat &currentImage, float hueFactor) { 

// Create the output image with the same size and type as the current 
image cv::Mat outputImage = currentImage.clone();

// Separate the alpha channel
std::vector<cv::Mat> channels(4);
cv::split(outputImage, channels);
cv::Mat alpha = channels[3];

// Convert the image from BGR to HSV
cv::cvtColor(outputImage, outputImage, cv::COLOR_BGRA2BGR);
cv::cvtColor(outputImage, outputImage, cv::COLOR_BGR2HSV);

// Loop through the rows
for (int y = 0; y < outputImage.rows; ++y) {
    // Loop through the columns
    for (int x = 0; x < outputImage.cols; ++x) {
        // Retrieve the pixel value
        cv::Vec3b &pixelValue = outputImage.at<cv::Vec3b>(y, x);

        // Adjust the hue value
        int hue = static_cast<int>(pixelValue[0]) + static_cast<int>(hueFactor * 179.0f);
        
        // Ensure the hue value wraps around correctly within the 0-179 range
        hue = hue % 180;
        if (hue < 0) {
            hue += 180;
        }
        pixelValue[0] = static_cast<uchar>(hue);
    }
}

// Convert the image back to BGR
cv::cvtColor(outputImage, outputImage, cv::COLOR_HSV2BGR);

// Merge the alpha channel back
std::vector<cv::Mat> bgrChannels;
cv::split(outputImage, bgrChannels);
std::vector<cv::Mat> bgraChannels = {bgrChannels[0], bgrChannels[1], bgrChannels[2], alpha};
cv::merge(bgraChannels, outputImage);

// Return the modified image data
return outputImage;

}

Any help please?
I have tried applying the hue value for the image data by using the above code, but after applying the hue value, the color doesn't resemble what I see in Photoshop.

1

There are 1 answers

0
VC.One On

"...After applying the hue value, the color doesn't resemble what I see in Photoshop."

Your code assumes +50 hue value to mean jumping near to 120 degrees (gives Greens), but Photoshop only goes half that distance to land near 60 degrees (gives Yellows).

You can see that on an R-G-B colour wheel, Red always starts at 0 degrees.

Looking at an example of the "Reds" in your input image, you can see that the model's hair and jacket have a hue of: (Red) 0 degrees, now your code does a +50 hue shift (or colour wheel rotation) which rotates the red dot to be now positioned at 120 degrees, so the old hue Red is now replaced by a new hue of: (Green) 120.

PS:
Your Greens have a strong hint of Yellow so I suspect you are actually landing in the 90 to 100 degree area.

Whats going on here? Let's investigate the docs...

COLOR_BGR2HSV
Convert RGB/BGR into HSV (hue saturation value)
with H range 0..180 if 8 bit image.

Son of a... So they really mapped 360 degrees into 180 levels? Yes, crazy but true.

( 360 / 180 ) * 50 hue factor = 100 degrees on the colour wheel

(note: There is a 360/255 option called COLOR_BGR2HSV_FULL so they are trying to be helpful within the limits).

Two Possible Solution(s):

(1) Fix at the input reading side:

You can counter the above equation with:

( 180 / 360 ) * 50 hue factor = 25 degrees on the colour wheel

Where: In color, the code's resulting 25 degrees should match the Photoshop result at its +50 hue offsetting.

(untested example, no C++ compiler here):

int hue = 0; //# hue of input pixel
int hueFactor = 50; //# factor for adding

//# using either Float or Int to store a "difference" offset between Photoshop and OpenCV values
int hue_coeff_PShop = 2; //# is "divide by 2 to match Photoshop" value for same point in colour wheel.
float hue_coeff_PShop = 0.5f; //# is ( OpenCV_180 / PShop_360 ) == using 0.5 as multiplication co-effecient

//# (1) Loop through the rows
for (int y = 0; y < outputImage.rows; ++y) 
{
    //# Loop through the columns
    for (int x = 0; x < outputImage.cols; ++x) 
    {
        //# Retrieve the current pixel and extract its current hue value
        cv::Vec3b &pixelValue = outputImage.at<cv::Vec3b>(y,x);
        hue = static_cast<int>( pixelValue[0] );

        //# Adjust the hue value using ADD with hueFactor (then doing MINUS is another option)
        hue += static_cast<int>( hueFactor * hue_coeff_PShop ); //# same as... ( hueFactor * 0.5f );
        
        //# Ensure the hue value wraps around correctly within the 0-179 range
        if (hue > 180) { hue -= 180; }
        
        pixelValue[0] = static_cast<uchar>(hue);
    }
}

(2) Or Else.. Fix at the output writing side:

So you have green output, how can you still match Photoshop with these green pixels?

You can try ADDing the two images together. This means:

You need two arrays (or vectors) each of length 3, that is one to hold the Input's R-G-B and the other holds the Output's R-G-B.

Add 50% from each side's related component.
The new result should match (or be close) to the Photoshop output.

Some pseudo-code:

//# add each side together (do reds, greens, blues separately): 
new_Red = ( ( in_R * 0.5f ) + ( out_R * 0.5f) ); //# example for red
//new_Grn = ( ( in_G * 0.5f ) + ( out_G * 0.5f) ); //# other example for green

//# then clamp the sum if it is above 255:
if( new_Red > 255 ) { new_Red = 255; } //# example for red
//if( new_Grn > 255 ) { new_Grn = 255; } //# other example for green