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

news2024/9/22 23:25:43

【测试环境】

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

【参考文献】

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

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

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

相关文章

告别繁琐!2024年PDF转CAD工具大推荐

如果你是装修设计师收到的却是被阉割了的PDF&#xff0c;不要破防。现在有不少工具可以直接实现PDF转CAD再也不需要你费心重画哦。今天&#xff0c;我们就来聊聊2024年大家都在用的几款PDF转CAD工具&#xff0c;看看它们是如何改变我们的设计工作的。 1.福昕PDF转换大师 链接…

springboot整合mybatis以及mybatis-plus 开发

一、springboot整合mybatis 1.注解版 1.1 导入坐标 <dependencies><!--mybatis坐标--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</…

39、Python之面向对象:对象属性解析:MRO不够用,补充3个方法

引言 在前面的文章中&#xff0c;我们谈及Python在继承关系&#xff0c;尤其是多继承中&#xff0c;一个对象的属性的查找解析顺序。由于当时的语境聚焦于继承关系&#xff0c;所以只是简要提及了属性解析顺序同方法的解析顺序&#xff0c;而方法的解析顺序&#xff0c;在Pyth…

16.1 深度学习基础

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 工&#x1f497;重&#x1f497;hao&#x1f497;&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题.…

Java 数据结构入门示例

通过我们适合初学者的数据结构指南&#xff08;包含说明性示例&#xff09;深入了解 Java 世界。 理解结构对于软件开发至关重要。Java是学习数据结构的理想选择&#xff0c;因为它的语法简单&#xff0c;应用范围广泛&#xff0c;从移动应用开发到大数据分析。 让我们仔细看看…

Lingo保姆级安装教程及新手快速入门指南

一、软件介绍 Lingo 18.0是一款功能强大的数学建模软件&#xff0c;功能齐全&#xff0c;建模速度快&#xff0c;是求解优化模型的最佳选择&#xff0c;深受广大用户的喜爱。它有一套完整继承的软件包&#xff0c;可以用于构建和编辑问题的完全功能环境&#xff0c;具有建模速…

【前端VUE】npm i 出现版本错误等报错 简单直接解决命令

前端vue npm i 安装时出现 报错原因 在新版本的npm中&#xff0c;默认情况下&#xff0c;npm install遇到冲突的peerDependencies时将失败。 解决办法 使用--force或--legacy-peer-deps可解决这种情况。 --force 会无视冲突&#xff0c;并强制获取远端npm库资源&#xff0…

【C++ 面试 - 基础题】每日 3 题(十五)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

React H5设置企业级v6版本路由的配置

路由配置是项目开发的必要一环&#xff0c;尤其是目前流行SPA&#xff0c;下面看看如何使用v6版本路由进行合理的H5路由配置 一、基本页面结构&#xff08;目录根据开发要求建&#xff0c;下面仅用于展示配置路由&#xff09; 二、具体文件实现 1. index.tsx import React f…

Java | Leetcode Java题解之第335题路径交叉

题目&#xff1a; 题解&#xff1a; class Solution {public boolean isSelfCrossing(int[] distance) {int n distance.length;// 处理第 1 种情况int i 0;while (i < n && (i < 2 || distance[i] > distance[i - 2])) {i;}if (i n) {return false;}// 处…

Adobe Substance 3D Painter v10.0.1 解锁版下载安装教程 (3D绘画软件)

前言 Adobe Substance 3D Painter 简称 Pt&#xff0c;是一款由adobe公司新研发的3D绘画软件。 Substance 3D Painter具有前所未有的功能和工作流程改进&#xff0c;使为 3D 资产创建纹理变得比以往更容易。 一、下载地址 下载链接&#xff1a;分享文件&#xff1a;Adobe Su…

Leetcode JAVA刷刷站(20)有效的括号

一、题目概述 二、思路方向 在Java中&#xff0c;要判断一个仅包含括号&#xff08;(, ), {, }, [, ]&#xff09;的字符串是否有效&#xff0c;你可以使用栈&#xff08;Stack&#xff09;数据结构来实现。栈是一种后进先出&#xff08;LIFO, Last In First Out&#xff09;的…

排序算法之梳排序

title: 梳排序 date: 2024-7-30 14:46:27 0800 categories: 排序算法 tags:排序算法梳排序 description: 梳排序&#xff08;Comb Sort&#xff09;是一种由弗拉基米尔多博舍维奇&#xff08;Wlodzimierz Dobosiewicz&#xff09;于1980年所发明的不稳定排序算法&#xff0c;并…

无字母绕过webshell

目录 代码 payload构造 php7 php5 构造payload 代码 不可以使用大小写字母、数字和$然后实现eval的注入执行 <?php if(isset($_GET[code])){$code $_GET[code];if(strlen($code)>35){die("Long.");}if(preg_match("/[A-Za-z0-9_$]/",$code))…

工业边缘网关软件 NeuronEX 中基于角色的访问控制功能

随着企业数字化转型的不断深入&#xff0c;确保数据和信息安全变得日益重要。传统的访问控制方法往往需要为每个用户单独配置权限&#xff0c;这种方法管理复杂且漏洞百出&#xff0c;难以适应快速变化的业务需求。而基于角色的访问控制&#xff08;RBAC&#xff09;功能通过定…

主任务控制子任务通信流程

#define WM_TAKE_STOCK_CMD 172 //MAIN->TAKE 任务流程#define TAKE_STOCK_CMD_FIND_TRAY 1 // 找托盘 #define WM_TAKE_STOCK_CMD_EXC 173 //TAKE内部使用#define TAKE_STOCK_EXE_OK 0 #define WM_TAKE_STOCK_TASK_MSG 174 //MAIN->TAKE 发送…

数据结构+图的基本应用

一、问题描述 求有向图的简单路径 编写一个程序&#xff0c;设计相关算法完成以下功能。 &#xff08;1&#xff09;输出如图所示的有向图 G 从顶点 5 到顶点 2 的所有简单路径。 &#xff08;2&#xff09;输出如图所示的有向图 G 从顶点 5 到顶点 2 的所有长度为 3 的简单…

Qt编译配置OpenCV+opencv_contrib(使用cmake)

本文使用环境 OpenCV: 4.7.0 cmake: 3.30.2 Qt: 5.12.1一、配置环境变量 安装好OpenCV、Qt、cmake后&#xff0c;应配置好一下环境变量&#xff1a; 二、编译OpenCV 打开cmake&#xff0c;编译的源码路径选择opencv文件夹中的sources目录&#xff0c;在opencv文件夹下新建一…

代码随想录 day 38 动态规划

第九章 动态规划part06 322. 零钱兑换 如果求组合数就是外层for循环遍历物品&#xff0c;内层for遍历背包。 如果求排列数就是外层for遍历背包&#xff0c;内层for循环遍历物品。 这句话结合本题 大家要好好理解。 视频讲解&#xff1a;https://www.bilibili.com/video/BV14K…

【自用】Python爬虫学习(六):通过m3u8文件下载ts文件并合并为.mp4文件

Python爬虫学习&#xff08;六&#xff09; 下载视频&#xff08;简单版&#xff09;的步骤介绍第一步&#xff1a;在网页上找到.m3u8文件第二步&#xff1a;通过.m3u8文件下载对应的.ts视频文件第三步&#xff1a;依据.m3u8文件合并.ts文件为一个.mp4文件 下载视频&#xff08…