[C++][opencv]基于opencv实现photoshop算法色相和饱和度调整

发布于:2024-08-13 ⋅ 阅读:(136) ⋅ 点赞:(0)

【测试环境】

vs2019

opencv==4.8.0

【效果演示】

【核心实现代码】

HSL.hpp

#ifndef OPENCV2_PS_HSL_HPP_
#define OPENCV2_PS_HSL_HPP_

#include "opencv2/core.hpp"
using namespace cv;

namespace cv {

enum HSL_COLOR
{
	HSL_ALL,
	HSL_RED,
	HSL_YELLOW,
	HSL_GREEN,
	HSL_CYAN,
	HSL_BLUE,
	HSL_MAGENTA,
};

/**
 * Class of HSL parameters for one channel
 */
class HSLChannel {
public:
	int hue;          //色度调整值,     数据范围:  [-180, 180]
	int saturation;   //饱和度调整值,数据范围:  [-100, 100]
	int brightness;   //明度调整值,    数据范围:  [-100, 100]

	int   colorIndex;  //color index: 0 = RED, 1 = YELLOW, 2 = GREEN
	float left_left;  //hue range left-left
	float left;       //hue range left
	float right;	  //hue range right
	float right_right;//hue range right-right
	bool defined;     //

	HSLChannel();
	virtual ~HSLChannel();

	void calcDefined();
	void setColorIndex(int index);
	bool match(float hue);
	void adjust(int h, float *delta_hsb);
};

/**
 * Class of HSL
 */
class HSL {
public:
	HSL();
	virtual ~HSL();

	HSLChannel channels[7];

	int adjust(InputArray src, OutputArray dst);
};

} /* namespace cv */

#endif /* OPENCV2_PS_HSL_HPP_ */

 HSL.cpp

#include "HSL.hpp"
#include "ColorSpace.hpp"
#include <math.h>

#define CLIP_RANGE(value, min, max)  ( (value) > (max) ? (max) : (((value) < (min)) ? (min) : (value)) )
#define COLOR_RANGE(value)  CLIP_RANGE(value, 0, 255)

namespace cv {

HSLChannel::HSLChannel()
{
	hue = 0;
	saturation = 0;
	brightness = 0;
	defined = false;

	setColorIndex(0);
}

HSLChannel::~HSLChannel()
{

}

void HSLChannel::setColorIndex(int index)
{
	int data[][4] = {
			{  0,   0, 360, 360},
			{315, 345,  15,  45},
			{ 15,  45,  75, 105},
			{ 75, 105, 135, 165},
			{135, 165, 195, 225},
			{195, 225, 255, 285},
			{255, 285, 315, 345}
	};

	if (index < 0 ) index = 0;
	if (index > 6 ) index = 6;

	colorIndex = index;
	left_left = data[index][0];
	left      = data[index][1];
	right     = data[index][2];
	right_right = data[index][3];
}

void HSLChannel::calcDefined()
{
	if ( hue != 0 || saturation != 0 || brightness != 0 ) {
		defined = true;
		return;
	}
	defined = false;
}


bool  HSLChannel::match(float hue)
{
	if ( left < right ) {
		if ( hue >= left_left && hue <= right_right )
			return true;
	} else {
		if ( hue >=left_left && hue <= 360 )
			return true;
		if ( hue >=0 && hue <= right_right )
			return true;
	}
	return false;
}

void HSLChannel::adjust(int h, float *delta_hsb)
{
	if (colorIndex == 0 ) {
		delta_hsb[0] += hue;
		delta_hsb[1] += saturation;
		delta_hsb[2] += brightness;
		return;
	}

	if ( left < right ) {
		if ( h >= left_left && h <= right_right ) {
			if ( h >=left && h <= right) {
				delta_hsb[0] += hue;
				delta_hsb[1] += saturation;
				delta_hsb[2] += brightness;
				return;
			}

			if ( h >=left_left && h <= left && left > left_left) {
				delta_hsb[0] += hue * (h - left_left) / (left - left_left);
				delta_hsb[1] += saturation * (h - left_left) / (left - left_left);
				delta_hsb[2] += brightness * (h - left_left) / (left - left_left);
				return;
			}

			if ( h >=right && h <= right_right && right_right > right) {
				delta_hsb[0] += hue * (right_right - h) / (right_right - right);
				delta_hsb[1] += saturation * (right_right - h) / (right_right - right);
				delta_hsb[2] += brightness * (right_right - h) / (right_right - right);
				return;
			}
		}

	} else {
		if ( h >=left && h <= 360 ) {
			delta_hsb[0] += hue;
			delta_hsb[1] += saturation;
			delta_hsb[2] += brightness;
			return;
		}

		if ( h >=0 && h <= right ) {
			delta_hsb[0] += hue;
			delta_hsb[1] += saturation;
			delta_hsb[2] += brightness;
			return;
		}

		if ( h >=left_left && h <= left && left > left_left) {
			delta_hsb[0] += hue * (h - left_left) / (left - left_left);
			delta_hsb[1] += saturation * (h - left_left) / (left - left_left);
			delta_hsb[2] += brightness * (h - left_left) / (left - left_left);
			return;
		}

		if ( h >=right && h <= right_right && right_right > right) {
			delta_hsb[0] += hue * (right_right - h) / (right_right - right);
			delta_hsb[1] += saturation * (right_right - h) / (right_right - right);
			delta_hsb[2] += brightness * (right_right - h) / (right_right - right);
			return;
		}
	}
}


//----------------------------------------------------------
//HSL class

HSL::HSL()
{
	for (int i = 0; i < 7; i++)
		channels[i].setColorIndex(i);
}

HSL::~HSL()
{
}

int HSL::adjust(InputArray src, OutputArray dst)
{
	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

	dst.create(src.size(), src.type());
	Mat output = dst.getMat();

	const uchar *in;
	uchar *out;
	int width = input.cols;
	int height = input.rows;
	int channel_count = input.channels();

	float hsb[3];
	float delta_hsb[3];

	//calculate defined
	for (int i = 0; i < 7; i++)
		channels[i].calcDefined();

	//scan pixels of image
	for (int y = 0; y < height; y ++) {
		in = input.ptr<uchar>(y);
		out = output.ptr<uchar>(y);

		for (int x = 0; x < width; x ++) {
			//RGB to HSL conversion
			BGR2HSB(in, hsb);

			//adjust each channel
			delta_hsb[0] = delta_hsb[1] = delta_hsb[2] = 0;
			for (int i = 0; i < 7; i++) {
				if ( channels[i].defined ) {
					 channels[i].adjust(hsb[0], delta_hsb);
				}
			}

			//adjust hue
			hsb[0] = int(hsb[0] + delta_hsb[0]) % 360;
			if ( hsb[0] <  0 ) hsb[0] += 360;

			//adjust saturation
			delta_hsb[1] = CLIP_RANGE(delta_hsb[1], -100, 100);
			if ( delta_hsb[1] < 0) {
				hsb[1] = hsb[1] * (1 + delta_hsb[1] / 100.0);
			} else {
				hsb[1] = hsb[1] + ( 1 - hsb[1] ) * delta_hsb[1] / 100.0; //saturation increase
				hsb[2] = hsb[2] + ( 1 - hsb[2] ) * delta_hsb[1] / 100.0; //brightness increase
			}

			//adjust brightness
			delta_hsb[2] = CLIP_RANGE(delta_hsb[2], -100, 100);
			if ( delta_hsb[2] < 0) {
				hsb[2] = hsb[2] * (1 + delta_hsb[2] / 100.0);
			} else {
				hsb[2] = hsb[2] + ( 1 - hsb[2] ) * delta_hsb[2] / 100.0; //brightness increase
				hsb[1] = hsb[1] - hsb[1]  * delta_hsb[2] / 100.0; //saturation decrease
			}

			//save to output
			HSB2BGR(hsb, out);

			//move to next pixel
			in += 3;
			out += 3;
			for (int c = 0; c < channel_count - 3; c++) {
				*out++ = *in++;
			}
		}
	}

	return 0;
}


} /* namespace cv */

【完整演示源码下载】

https://download.csdn.net/download/FL1623863129/88600796

【参考文献】

https://blog.csdn.net/c80486/article/details/52505546


网站公告

今日签到

点亮在社区的每一天
去签到