[C++][opencv]基于opencv实现photoshop算法可选颜色调整

【测试环境】

vs2019

opencv==4.8.0

【效果演示】

【核心实现代码】

SelectiveColor.hpp

#ifndef OPENCV2_PS_SELECTIVECOLOR_HPP_
#define OPENCV2_PS_SELECTIVECOLOR_HPP_

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

namespace cv {

typedef enum select_color {
	SELECT_RED,
	SELECT_YELLOW,
	SELECT_GREEN,
	SELECT_CYAN,
	SELECT_BLUE,
	SELECT_MAGENTA,
	SELECT_WHITE,
	SELECT_MIDDLE,
	SELECT_BLACK
}  select_color_t;

/**
 * Class of Adjustment for One Select Color
 */
class SelectiveColorAdjust{
public:
	float cyan;       //青色调整值,取值范围: [-1, 1]
	float magenta;    //取值范围: [-1, 1]
	float yellow;     //取值范围: [-1, 1]
	float black;      //取值范围: [-1, 1]


	bool  defined;

	SelectiveColorAdjust();
	virtual ~SelectiveColorAdjust();
	void   calcDefined();
};

/**
 * Class of Selective Color
 */
class SelectiveColor {
public:
	bool isAbsolute;  //是否采用绝对方法
	SelectiveColorAdjust colors[ SELECT_BLACK + 1 ]; //9种可选颜色通道

	SelectiveColor();
	virtual ~SelectiveColor();

	int adjust(InputArray src, OutputArray dst); //实施可选颜色调整
};

} /* namespace cv */

#endif /* OPENCV2_PS_SELECTIVECOLOR_HPP_ */

SelectiveColor.cpp

#include "SelectiveColor.hpp"


#define DEBUG

#ifndef DEBUG

#define DEBUG_PRINT(a)
#define PRINT_VAR(var)
#define PRINT_VAR1(var)
#define PRINT_VARF(var)

#else

#include <iostream>
using namespace std;
#define DEBUG_PRINT(a)  cout << (a) << endl
#define PRINT_VAR(var)  cout << #var << " = " << (var) <<  endl
#define PRINT_VAR1(var) if ( nn == 0 ) cout << #var << " = " << int(var) <<  endl
#define PRINT_VARF(var) if ( nn == 0 ) cout << #var << " = " << double(var) <<  endl

#endif


#define SWAP(a, b, t)  do { t = a; a = b; b = t; } while(0)
#define CLIP_RANGE(value, min, max)  ( (value) > (max) ? (max) : (((value) < (min)) ? (min) : (value)) )
#define COLOR_RANGE(value)  CLIP_RANGE(value, 0, 255)

#define MAX2(a, b) ( (a) > (b) ) ?  (a) : (b)
#define MAX3(a,b,c) ( ( a > b ) ?  MAX2(a,c) : MAX2(b,c) )
#define BLUE  0
#define GREEN 1
#define RED   2


namespace cv {


SelectiveColorAdjust:: SelectiveColorAdjust()
{
	cyan = 1.0;
	magenta = 1.0;
	yellow = 1.0;
	black = 1.0;
	defined = false;
}

SelectiveColorAdjust::~SelectiveColorAdjust()
{

}

void SelectiveColorAdjust::calcDefined()
{
	if (cyan != 1.0 || magenta != 1.0 || yellow != 1.0 || black != 1.0 ) {
		defined =  true;
		return;
	}
	defined = false;
}

//=========================================================
// SelectiveColor

SelectiveColor::SelectiveColor() {
	isAbsolute = false;
}

SelectiveColor::~SelectiveColor() {
}

int SelectiveColor::adjust(InputArray src, OutputArray dst)
{

	Mat input = src.getMat();
	if( input.empty() ) {
		return -1;
	}

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

	//calculate color adjustment is defined
	for(int i = 0; i < 9; i++ ) {
		colors[i].calcDefined();
	}

	const uchar *in;
	uchar *out;
	int width = input.cols;
	int height = input.rows;
	int channels = input.channels();
	int nn = 0;
	uchar t;  //temp
	uchar sorted[3];      //RGB value sorted
	uchar c[SELECT_BLACK + 1];
	float delta[3];  //delta of RGB value
	float ratio[3],  ratio_positive[3], ratio_negative[3];

	for (int h = 0; h < height; h ++, nn++) {
		in = input.ptr<uchar>(h);  //pointer to input image data
		out = output.ptr<uchar>(h); //pointer to output image data

		for (int w = 0; w < width; w ++) {
			//Sort RGB values:  sorted[0] is biggest, sorted[1] is middle, sorted[2] is smallest
			memcpy(sorted, in, 3);
			if (sorted[1] > sorted[0]) SWAP(sorted[0], sorted[1], t);
			if (sorted[2] > sorted[1]) SWAP(sorted[1], sorted[2], t);
			if (sorted[1] > sorted[0]) SWAP(sorted[0], sorted[1], t);

			//calculation c[] array
			memset(c, sizeof(c), 0);
			c[SELECT_BLUE] = in[BLUE];
			c[SELECT_GREEN] = in[GREEN];
			c[SELECT_RED] = in[RED];

			//subtract the smallest value from the RGB
			c[SELECT_BLUE]  -= sorted[2];
			c[SELECT_GREEN] -= sorted[2];
			c[SELECT_RED]   -= sorted[2];

			//calculate WHIT, MIDDLE, BLACK
			if ( sorted[2] <= 127 ) {
				c[SELECT_WHITE] = 0;
				c[SELECT_MIDDLE] = sorted[2] * 2;
				c[SELECT_BLACK]  = 255 - sorted[2] * 2;
			} else  {
				c[SELECT_WHITE]  = sorted[2] * 2 - 255;
				c[SELECT_MIDDLE] = 255 - (  sorted[2] - 127.5 ) * 2;
				c[SELECT_BLACK] = 0;
			}

			//calculate YELLOW
			if ( c[SELECT_RED] > 0 && c[SELECT_GREEN] > 0 ) {
				c[SELECT_YELLOW] = ( c[SELECT_RED] > c[SELECT_GREEN] ) ?  c[SELECT_GREEN] : c[SELECT_RED];
				c[SELECT_GREEN] -= c[SELECT_YELLOW];
				c[SELECT_RED]   -= c[SELECT_YELLOW];
			};

			//calculate CYAN
			if ( c[SELECT_BLUE] > 0 && c[SELECT_GREEN] > 0 ) {
				c[SELECT_CYAN] = ( c[SELECT_BLUE] > c[SELECT_GREEN] ) ?  c[SELECT_GREEN] : c[SELECT_BLUE];
				c[SELECT_GREEN] -= c[SELECT_CYAN];
				c[SELECT_BLUE]  -= c[SELECT_CYAN];
			};

			//calculate MAGENTA
			if ( c[SELECT_BLUE] > 0 && c[SELECT_RED] > 0 ) {
				c[SELECT_MAGENTA] = ( c[SELECT_BLUE] > c[SELECT_RED] ) ?  c[SELECT_RED] : c[SELECT_BLUE];
				c[SELECT_RED]   -= c[SELECT_MAGENTA];
				c[SELECT_BLUE]  -= c[SELECT_MAGENTA];
			};

			//initialize delta[]
			delta[BLUE] = delta[GREEN] = delta[RED] = 0;

			//initialize ratios
			for(int i = 0; i < 3 ; i++ ) {
				ratio_positive[i] = in[i] / 255.0;
				ratio_negative[i] = ratio_positive[i] - 1 ;
			}

			//calculate each selective color
			for (int j = 0; j <= SELECT_BLACK; j++ ) {
				if ( colors[j].defined && (c[j] > 0) ) {
					if ( isAbsolute ) {
						ratio[RED] = colors[j].cyan + colors[j].black;
						ratio[RED] = CLIP_RANGE(ratio[RED], ratio_negative[RED], ratio_positive[RED]);
						ratio[GREEN] = colors[j].magenta + colors[j].black;
						ratio[GREEN] = CLIP_RANGE(ratio[GREEN], ratio_negative[GREEN], ratio_positive[GREEN]);
						ratio[BLUE] = colors[j].yellow + colors[j].black;
						ratio[BLUE] = CLIP_RANGE(ratio[BLUE], ratio_negative[BLUE], ratio_positive[BLUE]);
					} else {
						ratio[RED] = colors[j].cyan + ::abs(colors[j].cyan + 1) * colors[j].black;
						ratio[RED] = CLIP_RANGE(ratio[RED], -1, 1);
						ratio[RED] = ( ratio[RED] > 0 ) ? ratio[RED] * ratio_positive[RED] : - ratio[RED] * ratio_negative[RED];

						ratio[GREEN] = colors[j].magenta + ::abs(colors[j].magenta + 1) * colors[j].black;
						ratio[GREEN] = CLIP_RANGE(ratio[GREEN], -1, 1);
						ratio[GREEN] = ( ratio[GREEN] > 0 ) ? ratio[GREEN] * ratio_positive[GREEN] : - ratio[GREEN] * ratio_negative[GREEN];

						ratio[BLUE] = colors[j].yellow + ::abs(colors[j].yellow + 1) * colors[j].black;
						ratio[BLUE] = CLIP_RANGE(ratio[BLUE], -1, 1);
						ratio[BLUE] = ( ratio[BLUE] > 0 ) ? ratio[BLUE] * ratio_positive[BLUE] : - ratio[BLUE] * ratio_negative[BLUE];
					}
					delta[RED] -=  c[j] * ratio[RED];
					delta[GREEN] -=  c[j] * ratio[GREEN];
					delta[BLUE] -=  c[j] * ratio[BLUE];
				}
			}

			//save to output
			out[BLUE]  = COLOR_RANGE( in[BLUE] + delta[BLUE] );
			out[GREEN] = COLOR_RANGE( in[GREEN] + delta[GREEN] );
			out[RED]   = COLOR_RANGE( in[RED] + delta[RED] );

			//move pointer forward
			in += 3;
			out += 3;
			for (int j = 0; j < channels - 3; j++) {
				*out++ = *in++;
			}
		}
	}

	return 0;
}


} /* namespace cv */

【完整演示代码下载地址】

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

相关推荐
闻缺陷则喜何志丹33 分钟前
【C++数论 因数分解】829. 连续整数求和|1694
c++·数学·算法·力扣··因数分解·组数
初九之潜龙勿用35 分钟前
C# 读取多条数据记录导出到 Word标签模板之图片输出改造
开发语言·c#·word
抓哇能手36 分钟前
机器学习基础
人工智能·opencv·算法·机器学习·计算机视觉·机器视觉
zhangpz_40 分钟前
c ++零基础可视化——字符串
数据结构·c++·算法
迷迭所归处2 小时前
C++ —— 以真我之名 如飞花般绚丽 - 智能指针
开发语言·c++
dvlinker2 小时前
内存不足引发C++程序闪退崩溃问题的分析与总结
c++·内存泄漏·内存不足·malloc返回null·new抛出异常·abort强制终止进程·排查c++软件异常常用方法
沥川同学2 小时前
跨平台应用开发框架(1)----Qt(组件篇)
c++·qt·udp·线程·tcp·qt5·qt6.3
Code哈哈笑2 小时前
【Java 学习】构造器、static静态变量、static静态方法、static构造器、
java·开发语言·学习
是老余2 小时前
Java三大特性:封装、继承、多态【详解】
java·开发语言
小馒头学python2 小时前
【Python爬虫五十个小案例】爬取豆瓣电影Top250
开发语言·爬虫·python