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

news2024/9/20 9:31:57

【测试环境】

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2036612.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

笔记:在WPF中OverridesDefaultStyle属性如何使用

一、目的&#xff1a;介绍下在WPF中OverridesDefaultStyle属性如何使用 OverridesDefaultStyle 属性在 WPF 中用于控制控件是否使用默认的主题样式。将其设置为 True 时&#xff0c;控件将不会应用默认的主题样式&#xff0c;而是完全依赖于你在 Style 中定义的样式。以下是如何…

代码随想录算法训练营day39||动态规划07:多重背包+打家劫舍

多重背包理论 描述&#xff1a; 有N种物品和一个容量为V 的背包。 第i种物品最多有Mi件可用&#xff0c;每件耗费的空间是Ci &#xff0c;价值是Wi 。 求解将哪些物品装入背包可使这些物品的耗费的空间 总和不超过背包容量&#xff0c;且价值总和最大。 本质&#xff1a; …

图论------迪杰斯特拉(Dijkstra)算法求单源最短路径。

编程要求 在图的应用中&#xff0c;有一个很重要的需求&#xff1a;我们需要知道从某一个点开始&#xff0c;到其他所有点的最短路径。这其中&#xff0c;Dijkstra 算法是典型的最短路径算法。 本关的编程任务是补全右侧代码片段中 Begin 至 End 中间的代码&#xff0c;实现 …

543 二叉树的直径

解题思路&#xff1a; \qquad 如果某一个点&#xff08;非叶子节点&#xff09;在最长路径上&#xff0c;那么应该有两种情况&#xff1a; \qquad 情况一&#xff1a;该节点为非转折点&#xff0c;最长路径经过其一个子节点 父节点&#xff1b; \qquad 情况二&#xff1a;该…

Rancher 使用 Minio 备份 Longhorn 数据卷

0. 概述 Longhorn 支持备份到 NFS 或者 S3, 而 MinIO 就是符合 S3 的对象存储服务。通过 docker 部署 minio 服务&#xff0c;然后在 Longhorn UI 中配置备份服务即可。 1. MinIO 部署 1.1 创建备份目录 mkdir -p /home/longhorn-backup/minio/data mkdir -p /home/longhor…

24经济师报名照上传技巧,无需下载照片工具

24经济师报名照上传技巧&#xff0c;无需下载照片工具 #中级经济师 #经济师 #高级经济师 #经济师报名照片 #中级经济师报名照片 #经济师考试

SPI通讯协议示例

目录 0x01 SPI通讯特点0x01 硬件SPI示例0x02 软件SPI示例 0x01 SPI通讯特点 SPI在接线方面会拥有片选引脚、时钟引脚、数据引脚&#xff0c;其中数据引脚又分为 MISO和MOSI&#xff0c;分别对应的是 “Master IN Slave Out”(主机输入从机输出) 和 “Master Out Slave IN”(主…

机械学习—零基础学习日志(如何理解线性代数)

零基础为了学人工智能&#xff0c;正在快乐学习&#xff0c;每天都长脑子 如何理解线性代数&#xff1f; 线性代数的本质是代数——代替数字。有时数学里有很多的规律&#xff0c;不以数字形式存在&#xff0c;可以用字幕替代。用一个通用的等式替代我们发现的规律。 代数研…

在VB.net中,CDbl、Double.Parse与Double.TryParse有什么区别

标题 在VB.net中&#xff0c;CDbl、Double.Parse与Double.TryParse有什么区别 正文 在VB.NET中&#xff0c;CDbl、Double.Parse和Double.TryParse都是用于将不同类型的值&#xff08;主要是字符串&#xff09;转换为Double类型的方法&#xff0c;但它们之间在用法、性能、错误处…

django学习入门系列之第七点《案例 添加页面》

文章目录 7.6 前端整合标准引入格式案例 添加页面 往期回顾 7.6 前端整合 HTMLCSSjavaScript、jQueryBootStrap&#xff08;动态效果依赖于jQuery&#xff09; 标准引入格式 css在上面js动态效果放下面bootstrap依赖于jQuery&#xff0c;所以先要有jQuery&#xff0c;再有bo…

汽车精密设计、无人机外形优化总是遇难题?CFD参数优化详解2来袭

数值仿真的参数优化 在上期文章中&#xff0c;我们给大家带来了机翼多学科优化、拟合试验曲线、一维CFD模型参数的DOE和回归分析三个参数优化案例&#xff0c;本期文章将继续为各位讲解多个 Altair CFD 参数优化案例&#xff0c;一起来看看吧。 案例&#xff1a;汽车排气管形状…

Jenkins链接Gitlab(HttpSSH方式)

文章目录 前言一、安装必要插件1、安装git2、安装Jenkins插件 二、配置git1、http方式&#xff08;1&#xff09;基础配置&#xff08;http方式配置凭证&#xff09;&#xff08;2&#xff09;测试 2、SSH方式配置凭证 总结 前言 为避免汉化导致的显示差异&#xff0c;以下操作…

通过Go示例理解函数式编程思维

一个孩子要尝试10次、20次才肯接受一种新的食物&#xff0c;我们接受一种新的范式&#xff0c;大概不会比这个简单。-- 郭晓刚 《函数式编程思维》译者 函数式编程(Functional Programming, 简称fp)是一种编程范式&#xff0c;与命令式编程(Imperative Programming)、面向对象编…

xlua使用

1. 安装 到 github 移动三个文件夹过去即可 Assets -》Plugins Assets -》Xlua Tools 移动到 unity里面的Assets目录即可 会在工具栏出现Xlua即安装成功 2. 引入基础类 ABMgr.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using Un…

生成式人工智能(大语言模型)上线备案材料

材料总体一览 生成式人工智能&#xff08;大语言模型&#xff09;上线备案&#xff0c;除申请表外还需要提交五份材料&#xff1a; 《生成式人工智能 &#xff08;大语言模型&#xff09;上线备案申请表》 《附件1&#xff1a;安全自评估报告》 《附件2&#xff1a;模型服务协议…

django学习入门系列之第七点《案例 点击删除文本》

文章目录 前置回顾案例 点击删除文本总结往期回顾 前置回顾 HTML结构&#xff1a; 页面使用<!DOCTYPE html>声明为HTML5文档。<html>标签定义了页面的根元素&#xff0c;并且设置了lang"en"属性&#xff0c;表示页面内容使用英语。<head>部分包含…

统计回归与Matlab软件实现上(一元多元线性回归模型)

引言 关于数学建模的基本方法 机理驱动 由于客观事物内部规律的复杂及人们认识程度的限制&#xff0c;无法得到内在因果关系&#xff0c;建立合乎机理规律的数学模型数据驱动 直接从数据出发&#xff0c;找到隐含在数据背后的最佳模型&#xff0c;是数学模型建立的另一大思路…

数据结构 - 位图 | 布隆过滤器

文章目录 一、位图1、位图概念2、实现一个简略的位3、位图的优缺点4、位图的应用场景 二、布隆过滤器1、提出2、概念3、布隆过滤器的实现 三、海量数据处理1、哈希切割2、面试题 一、位图 1、位图概念 位图&#xff08;Bitmap&#xff09;是一种非常高效的数据结构&#xff0c…

【ocr识别003】flask+paddleocr+bootstrap搭建OCR文本推理WEB服务

1.欢迎点赞、关注、批评、指正&#xff0c;互三走起来&#xff0c;小手动起来&#xff01; 2.了解、学习OCR相关技术知识领域&#xff0c;结合日常的场景进行测试、总结。如本文总结的flaskpaddleocrbootstrap搭建OCR文本推理WEB服务应用示例场景。 文章目录 1.代码结构2.效果演…

【算法】梯度下降

一、引言 梯度下降算法&#xff08;Gradient Descent&#xff09;是一种一阶迭代优化算法&#xff0c;用于求解最小化目标函数的问题&#xff0c;广泛应用于机器学习和人工智能中的参数优化。 用于优化问题的迭代算法&#xff0c;尤其在机器学习和深度学习中广泛用于最小化损失…