当前位置: 首页 > news >正文

OpenCV 像素操作—证件照换底色详细原理 C++纯手写实现

文章目录

    • 总体步骤
      • 1.RGB转HSV
      • 2.找出要换的底色
      • 3.取反,黑白颠倒
      • 4.将原图像的非背景部分复制到新背景上
    • 完整代码
      • 1.C++纯手写版
      • 2.官方API版本

总体步骤

在这里插入图片描述

1.RGB转HSV

为什么一定要转为HSV 颜色空间?

将图像从BGR颜色空间转换为HSV颜色空间是因为HSV颜色空间更适合进行颜色分割和提取操作。这是因为HSV颜色空间将颜色表示为色相(Hue)、饱和度(Saturation)和明度(Value),这使得颜色的分离更加直观和有效。具体原因如下:

色相(Hue)分离

  • 在HSV颜色空间中,色相(H)直接表示颜色的种类,例如红色、绿色、蓝色等。通过指定色相范围,可以非常容易地分离出特定的颜色。例如,**绿色的色相范围通常集中在一个较小的区间内,这使得提取绿色对象变得简单而精确。**这也是为什么特效制作的时候需要使用绿色幕布,更加方便提取图像。

饱和度(Saturation)和明度(Value)独立

  • 饱和度表示颜色的纯度,明度表示颜色的亮度。在BGR颜色空间中,这些属性是混合在一起的,不容易分离。但是在HSV颜色空间中,饱和度和明度是独立的,可以单独进行调节和阈值化,从而实现更好的颜色分割效果。

处理光照变化

  • HSV颜色空间对于光照变化更为鲁棒。由于明度和饱和度是独立的,即使在光照发生变化时,色相也相对稳定,使得颜色分割在不同光照条件下表现更好。

HSV范围取色表

在这里插入图片描述

例如在H:35 -77 S:43-255 V:46-255 之间的可以认为是绿色。 有了这张图参考,我们能够更容易地过滤掉某种颜色。

2.找出要换的底色

以将一张蓝底的证件照换成红底证件照为例:

在这里插入图片描述

转为HSV颜色空间之后,我们要找出蓝色的底色所在区域。可以通过遍历像素的方式实现

// 根据范围创建 掩码图像 二值化图像
void customInRange(const Mat& src, Scalar lowerBound, Scalar upperBound, Mat& dst) {// 创建与源图像大小相同的掩码图像dst = Mat::zeros(src.size(), CV_8UC1);// 遍历图像的每个像素for (int y = 0; y < src.rows; y++) {for (int x = 0; x < src.cols; x++) {Vec3b pixel = src.at<Vec3b>(y, x);bool inRange = true;// 检查像素值是否在指定范围内 通过HSV范围表来指定for (int c = 0; c < 3; c++) {if (pixel[c] < lowerBound[c] || pixel[c] > upperBound[c]) {inRange = false;break;}}// 如果在范围内,设置掩码图像中的对应位置为255if (inRange) {dst.at<uchar>(y, x) = 255;}}}
}

这里涉及到一个新概念 掩码图像

掩码(mask)在计算机视觉和图像处理领域中,是一种用于选择、过滤或操作图像中特定部分的工具。掩码本质上是一张与原图像大小相同的二值图像,其中每个像素要么是0,要么是1(在OpenCV中通常用0和255来表示)。掩码图像中值为1(或255)的部分表示感兴趣的区域(ROI,Region of Interest),而值为0的部分则表示不感兴趣的区域。

掩码的具体作用可以包括以下几种:

  1. 区域选择
    • 通过掩码,可以选择图像中的某个特定区域进行操作,而忽略其他部分。例如,只对图像中的某个颜色范围进行处理。
  2. 图像分割
    • 掩码可以用于将图像分割成前景和背景。前景是感兴趣的部分,背景则是其他部分。
  3. 图像修复
    • 掩码可以用于图像修复,指定需要修复的区域。
  4. 遮罩操作
    • 在图像合成时,掩码可以用作遮罩,控制哪些部分需要叠加或混合。

上述代码通过遍历可以找到蓝色底色的区域,并置为白色。其余的全部设置为黑色。

代码运行效果:(中间的白点,因为胖虎穿的偏蓝色领带,因此过滤出了一部分)

在这里插入图片描述

此时,基本上已经找出原来底色的区域,已经全部置成了白色。

3.取反,黑白颠倒

主要目的是找到除了背景色的其他元素,这一步是选择 非背景色的元素 置为白色,因为除了背景色,其他像素均要移到新的背景色上去, 非背景色成为了ROI(感兴趣区域),背景色置为黑色,代表不再关注原来的背景色。遍历每个像素,用255减去原来的像素便可反转颜色,黑白颠倒。

//    bitwise_not(mask,mask);for (int y = 0; y < mask.rows; ++y) {for (int x = 0; x < mask.cols; ++x) {//对每个像素进行取反mask.at<uchar>(y, x) = 255 - mask.at<uchar>(y, x);}}

效果如图:

在这里插入图片描述

4.将原图像的非背景部分复制到新背景上

//将非背景色像素移到事先准备好的背景上
void customCopyTo(const Mat& src, Mat& dst, const Mat& mask) {// 确保源图像和掩码图像的大小一致if (src.size() != dst.size() || src.size() != mask.size()) {throw std::invalid_argument("Size of src, dst, and mask must be the same");}// 遍历图像的每个像素for (int y = 0; y < src.rows; y++) {for (int x = 0; x < src.cols; x++) {// 如果掩码中的值为非零(通常是255),则复制源图像的像素到目标图if (mask.at<uchar>(y, x) != 0) {dst.at<Vec3b>(y, x) = src.at<Vec3b>(y, x);}}}
}

遍历像素,与掩码图像对比,如果不是黑色,说明是ROI区域,将像素替换到准备好的纯色背景上面。就完成了背景换色

效果如下:

在这里插入图片描述

完整代码

1.C++纯手写版

//将非背景色像素移到事先准备好的背景上
void customCopyTo(const Mat& src, Mat& dst, const Mat& mask) {// 确保源图像和掩码图像的大小一致if (src.size() != dst.size() || src.size() != mask.size()) {throw std::invalid_argument("Size of src, dst, and mask must be the same");}// 遍历图像的每个像素for (int y = 0; y < src.rows; y++) {for (int x = 0; x < src.cols; x++) {// 如果掩码中的值为非零(通常是255),则复制源图像的像素到目标图像if (mask.at<uchar>(y, x) != 0) {dst.at<Vec3b>(y, x) = src.at<Vec3b>(y, x);}}}
}
// 根据范围创建 掩码图像 二值化图像
void customInRange(const Mat& src, Scalar lowerBound, Scalar upperBound, Mat& dst) {// 创建与源图像大小相同的掩码图像dst = Mat::zeros(src.size(), CV_8UC1);// 遍历图像的每个像素for (int y = 0; y < src.rows; y++) {for (int x = 0; x < src.cols; x++) {Vec3b pixel = src.at<Vec3b>(y, x);bool inRange = true;// 检查像素值是否在指定范围内for (int c = 0; c < 3; c++) {if (pixel[c] < lowerBound[c] || pixel[c] > upperBound[c]) {inRange = false;break;}}// 如果在范围内,设置掩码图像中的对应位置为255if (inRange) {dst.at<uchar>(y, x) = 255;}}}
}void inRange_dmeo(Mat &image){Mat hsv;//先将图片转为HSVcvtColor(image,hsv,COLOR_BGR2HSV);// 提取左上角像素的HSV值 ,也就是对应底色的值 有利于更好控制提取Vec3b hsvValue = hsv.at<Vec3b>(0, 0);cout<<hsvValue<<endl;Mat mask;//设置 下限 和上限根据设定的HSV颜色范围  生成一个二值化的掩码(mask),掩码中白色部分表示图像中符合设定颜色范围的部分,黑色部分表示不符合的部分。customInRange(hsv,Scalar(100,43,46),Scalar(115,225,227),mask);imshow("mask",mask);//准备好一个背景颜色 以红色为例Mat red_background = Mat::zeros(image.size(),image.type());red_background = Scalar (40,40,200);//对掩码取反, 黑白颠倒,选择除了背景色的区域 ,也就是欲复制的区域
//    bitwise_not(mask,mask);for (int y = 0; y < mask.rows; ++y) {for (int x = 0; x < mask.cols; ++x) {//对每个像素进行取反mask.at<uchar>(y, x) = 255 - mask.at<uchar>(y, x);}}imshow("bitwise_not mask",mask);//使用掩码(mask)将输入图像中符合条件的部分复制到红色背景图像上customCopyTo(image,red_background,mask);imshow("roi",red_background);
}

2.官方API版本

OpenCV提供了生成掩码,掩码取反,复制非背景色元素到新背景的API 如下,

inRange 根据HSV范围 生成掩码

bitwise_not 掩码取反

copyTo 根据掩码复制到新背景色

void inRange_dmeo(Mat &image){Mat hsv;//先将图片转为HSVcvtColor(image,hsv,COLOR_BGR2HSV);// 提取左上角像素的HSV值 ,也就是对应底色的值 有利于更好控制提取Vec3b hsvValue = hsv.at<Vec3b>(0, 0);cout<<hsvValue<<endl;Mat mask;//设置 下限 和上限根据设定的HSV颜色范围  生成一个二值化的掩码(mask),掩码中白色部分表示图像中符合设定颜色范围的部分,黑色部分表示不符合的部分。inRange(hsv,Scalar(100,43,46),Scalar(115,225,227),mask);imshow("mask",mask);//准备好一个背景颜色 以红色为例Mat red_background = Mat::zeros(image.size(),image.type());red_background = Scalar (40,40,200);//对掩码取反, 黑白颠倒,选择除了背景色的区域 ,也就是欲复制的区域bitwise_not(mask,mask);imshow("bitwise_not mask",mask);//使用掩码(mask)将输入图像中符合条件的部分复制到红色背景图像上image.copyTo(red_background,mask);imshow("roi",red_background);
}

相关文章:

OpenCV 像素操作—证件照换底色详细原理 C++纯手写实现

文章目录 总体步骤1.RGB转HSV2.找出要换的底色3.取反&#xff0c;黑白颠倒4.将原图像的非背景部分复制到新背景上 完整代码1.C纯手写版2.官方API版本 总体步骤 1.RGB转HSV 为什么一定要转为HSV 颜色空间&#xff1f; 将图像从BGR颜色空间转换为HSV颜色空间是因为HSV颜色空间更…...

tinygrad框架简介;MLX框架简介

目录 tinygrad框架简介 MLX框架简介 LLaMA​编辑 Stable Diffusion​编辑 tinygrad框架简介 极简主义与易扩展性 tinygrad 的设计理念是极简主义。与 XLA 类比,如果 XLA 是复杂指令集计算 (CISC),那么 tinygrad 就是精简指令集计算 (RISC)。这种简约的设计使得它成为添加…...

服务器重启了之后就卡在某个页面了,花屏,如何解决??

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…...

Hospital 14.6.0全开源医院管理预约系统源码

InfyHMS 具有 60 种功能和 9 种不同类型的用户类型&#xff0c; 他们可以登录系统并根据他们的角色访问他们的数据。 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89580674 更多资源下载&#xff1a;关注我。...

C/C++樱花树代码

目录 写在前面 系列文章 C简介 完整代码 代码分析 写在后面 写在前面 C实现精美的樱花树&#xff0c;只需这100行代码&#xff01; 系列文章 序号目录直达链接1爱心代码https://want595.blog.csdn.net/article/details/1363606842李峋同款跳动的爱心https://want595.b…...

sklearn基础学习

1. 简介 1.1 什么是sklearn sklearn&#xff0c;或者更正式地称为scikit-learn&#xff0c;是一个基于Python的开源机器学习库。它建立在NumPy、SciPy和matplotlib之上&#xff0c;提供了简单而有效的工具用于数据挖掘和数据分析。sklearn支持监督学习和无监督学习算法&#…...

SpringBoot 自动配置原理

一、Condition Condition 是在 Spring 4.0 增加的条件判断功能&#xff0c;通过这个可以功能可以实现选择性的创建 Bean 操 作。 思考&#xff1a; SpringBoot 是如何知道要创建哪个 Bean 的&#xff1f;比如 SpringBoot 是如何知道要创建 RedisTemplate 的&#xff1f; …...

Redisson中RQueue的使用场景附一个异步的例子

RQueue 是一个基于 Redis 的分布式作业队列系统&#xff0c;它允许开发者在 Ruby 应用程序中实现异步任务处理和计划任务调度。由于 Redis 提供了高性能的内存数据结构存储&#xff0c;RQueue 可以快速地存储和检索队列中的任务&#xff0c;这使得它非常适合于高并发和低延迟的…...

SpringMVC 控制层框架-下

五、SpringMVC其他扩展 1. 异常处理机制 1.1 异常处理概念 开发过程中是不可避免地会出现各种异常情况&#xff0c;例如网络连接异常、数据格式异常、空指针异常等等。异常的出现可能导致程序的运行出现问题&#xff0c;甚至直接导致程序崩溃。因此&#xff0c;在开发过程中&a…...

(四)js前端开发中设计模式之工厂方法模式

工厂方法模式,通过对产品类的抽象&#xff0c;使其创建业务主要用于负责创建多类产品的实例 const Java function (content) {this.content content;(function () {let oDiv document.createElement(div)oDiv.innerHTML contentoDiv.style.color greendocument.getElement…...

新版GPT-4omini上线!快!真TM快!

大半夜&#xff0c;OpenAI突然推出了GPT-4o mini版本。 当我看到这条消息时&#xff0c;正准备去睡觉。mini版本质上是GPT-4o模型的精简版本&#xff0c;没有什么革命性的创新&#xff0c;因此我并没有太在意。 结果今天早上一觉醒来发现伴随GPT-4o mini上线&#xff0c;官网和…...

【Unity】RPG2D龙城纷争(十七)敌方常规AI(Normal)的实现

更新日期:2024年7月24日。 项目源码:第五章发布(正式开始游戏逻辑的章节) 索引 简介一、AI_Normal类二、AI调遣策略第一阶段:收集1.提供战场数据收集方法2.收集战场数据三、AI调遣策略第二阶段:评估四、AI调遣策略第三阶段:行动简介 AI_Normal定位为框架自带的最基础的…...

Tracy 小笔记:微信小程序 mpx 雷达图的实现

使用文档&#xff1a; https://www.kancloud.cn/xchhhh/wx-chart/399337 https://github.com/xiaolin3303/wx-charts https://gitee.com/mirrors/wx-charts/#wx-charts 参数说明&#xff1a; https://github.com/xiaolin3303/wx-charts/issues/56 下载 dist 里的 wx-charts-…...

Unity UGUI 之 Input Field

本文仅作学习笔记与交流&#xff0c;不作任何商业用途 本文包括但不限于unity官方手册&#xff0c;唐老狮&#xff0c;麦扣教程知识&#xff0c;引用会标记&#xff0c;如有不足还请斧正 1.Input Field是什么&#xff1f; 给玩家提供输入的输入框 2.重要参数 中英文对照着看…...

SpringBoot接入mongodb例子,并有增删改查功能

1&#xff0c;首先&#xff0c;在pom.xml中添加依赖&#xff1a; <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency><!--上面这…...

类和对象(三)

目录 一. 构造函数初始化列表 二. 类型转换 三. static成员 四. 友元 五. 内部类 六. 匿名对象 七. 对象拷贝时的编译器优化 一. 构造函数初始化列表 1. 之前我们实现构造函数时&#xff0c;初始化成员变量主要使用函数体内赋值&#xff0c;构造函数初始化还有一种方式&…...

Android SurfaceFlinger——GraphicBuffer初始化(二十九)

在 SurfaceFlinger 中,GraphicBuffer 是一个关键的数据结构,用于封装和管理图形数据的内存缓冲区。它不仅在 SurfaceFlinger 内部使用,也被其他组件如 GPU 驱动、摄像头服务、视频解码器等广泛利用,以实现高效的数据交换和图形渲染。 一、概述 GraphicBuffer 对象封装了一…...

pytest:4种方法实现 - 重复执行用例 - 展示迭代次数

简介&#xff1a;在软件测试中&#xff0c;我们经常需要重复执行测试用例&#xff0c;以确保代码的稳定性和可靠性。在本文中&#xff0c;我们将介绍四种方法来实现重复执行测试用例&#xff0c;并显示当前迭代次数和剩余执行次数。这些方法将帮助你更好地追踪测试执行过程&…...

一文入门SpringSecurity 5

目录 提示 Apache Shiro和Spring Security 认证和授权 RBAC Demo 环境 Controller 引入Spring Security 初探Security原理 认证授权图示​编辑 图中涉及的类和接口 流程总结 提示 Spring Security源码的接口名和方法名都很长&#xff0c;看源码的时候要见名知意&am…...

IPython的HTML魔法:%%html_header命令全解析

IPython的HTML魔法&#xff1a;%%html_header命令全解析 在IPython和Jupyter Notebook中&#xff0c;%%html_header是一个魔术命令&#xff0c;它允许用户在Notebook的单元格中添加HTML头部&#xff08;head&#xff09;内容。这个功能特别有用&#xff0c;当你需要定制Notebo…...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...