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

news2024/9/22 5:31:07

文章目录

    • 总体步骤
      • 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;
                }
            }
            // 如果在范围内,设置掩码图像中的对应位置为255
            if (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;
                }
            }
            // 如果在范围内,设置掩码图像中的对应位置为255
            if (inRange) {
                dst.at<uchar>(y, x) = 255;
            }
        }
    }
}

void inRange_dmeo(Mat &image){
    Mat hsv;
    //先将图片转为HSV
    cvtColor(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;
    //先将图片转为HSV
    cvtColor(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);
}

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

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

相关文章

vscode 文件颜色变绿色

解决&#xff1a;关闭git功能 在设置中搜索Git:Enabled&#xff0c;取消Decorations: Enabled的勾选

内网渗透隧道构建,使用github项目联动msf绕uac,使用简单的spp来进行操作icmp隧道

在我们需要木马上线的时候&#xff0c;发现上线不了&#xff0c;我们一般就想到建立隧道&#xff0c;来解决问题&#xff0c;或者是说我们直接还一种连接的操作来进行上线。比如说我们正向连接上不了&#xff0c;我们可以还成反向连接的操作。或者我们使用隧道直接硬刚waf来进行…

计算机毕业设计-程序论文文档-基于SSM的驾校管理系统

本系统开发采用技术为JSP、Bootstrap、Ajax、SSM、Java、Tomcat、Maven 此文章为本人亲自指导加编写&#xff0c;禁止任何人抄袭以及各类盈利性传播&#xff0c; 相关的代码部署论文ppt代码讲解答辩指导文件都有可私要 项目源码&#xff0c;请关注❥点赞收藏并私信博主&#x…

代码随想录 day 18 二叉树

第六章 二叉树part06 详细布置 530.二叉搜索树的最小绝对差 需要领悟一下二叉树遍历上双指针操作&#xff0c;优先掌握递归 题目链接/文章讲解&#xff1a;https://programmercarl.com/0530.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E6%9C%80%E5%B0%8F%E7%B…

鸿蒙OpenHarmony Native API【HiLog】

HiLog Overview Description: HiLog模块实现日志打印功能。 开发者可以通过使用这些接口实现日志相关功能&#xff0c;输出日志时可以指定日志类型、所属业务领域、日志TAG标识、日志级别等。 syscap SystemCapability.HiviewDFX.HiLog Since: 8 Summary Files File …

甄选范文“论企业集成平台的理解与应用”,软考高级论文,系统架构设计师论文

论文真题 企业集成平台(Enterprise Imtcgation Plaform,EIP)是支特企业信息集成的像环境,其主要功能是为企业中的数据、系统和应用等多种对象的协同行提供各种公共服务及运行时的支撑环境。企业集成平台能够根据业务模型的变化快速地进行信息系统的配置和调整,保证不同系统…

HarmonyOS应用开发者高级认证,Next版本发布后最新题库 - 单选题序号3

基础认证题库请移步&#xff1a;HarmonyOS应用开发者基础认证题库 注&#xff1a;有读者反馈&#xff0c;题库的代码块比较多&#xff0c;打开文章时会卡死。所以笔者将题库拆分&#xff0c;单选题20个为一组&#xff0c;多选题10个为一组&#xff0c;题库目录如下&#xff0c;…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 堆内存申请(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线…

爬虫自己做的

1.urllib 1.1基本使用 1.2 下载&#xff08;图片&#xff0c;页面&#xff0c;视频&#xff09; 1.3 get 1.3.1 quote 中文变成对应uncode编码 当url 的wd中文时 quote是将中文变成对应uncode编码 然后拼接成完整的url 1.3.2urlencode方法 wd有多个参数 1.3.3ajas get实例 …

科普文:银行信贷系统概叙

信贷业务流程 资金需求者提交申请&#xff1a;资金需求者通过不同渠道&#xff08;如APP、网站、门店等&#xff09;提交贷款申请。 系统交互完成审批&#xff1a;系统通过自动化和人工相结合的方式&#xff0c;对贷款申请进行初步筛选和审批。 系统交互完成策略判断&#xf…

java学习---小项目---租房系统

package com.project.House_rental.HouseApp;import com.project.House_rental.HouseView.HouseView; //主界面 public class HouseApp {public static void main(String[] args) {new HouseView().List_();System.out.println("------已退出----------");} }package…

5G网络近海覆盖和融合应用5G网关5G路由器CPE

5G移动超远高速率CPE 5G移动超远距离高速率海上CPE是根据海上特殊场景需求&#xff0c;研发的5G天通电话&#xff0c;短信&#xff0c;北斗短报文&#xff0c;远航道宽带接入系统&#xff0c;专门为海洋急救&#xff0c;海洋作业、海洋施工定制的船用5G高速上网终端&#xff0c…

合并区间 1

合并区间 思路&#xff1a; 感觉就是一个个vector遍历&#xff1f; 两两对比。若第一个的第二个元素大于下一个的第一个元素&#xff0c;则合并&#xff0c;存入答案数组即可。 nonono 新思路 思路打开&#xff01; 对于每一个区间&#xff0c;判断他能覆盖的最大区间在哪里…

哈希 -- 简单实现

在STL库中&#xff0c;有map和set两个关联式容器&#xff0c;这两个容器的底层都是以红黑树为底层。但是在后续的发展过程中&#xff0c;我们发现有些场景的数据不适合用红黑树进行存储&#xff0c;所以有人就发明了底层为哈希表的map和set,称为unordered_map 和 unordered_set…

SAP Fiori 实战课程(二):新建页面

课程回顾 上一课中,利用Visual studio Code 新建、并运行了一个Demo工程。可以实现对项目的启动,启动后进入一个List清单。 那么本次课程的目前就是在上一节Demo的基础上,从零开始新建一个完整的页面。实现从首页清单,选择行后,鼠标点击,进入下一个页面。 准备工作 在开…

pcie数据传输

一 数据传输通道总体设计 在上传数据时首先将 FPGA 中数据缓存到 DDR3 存储器&#xff0c;然后上位机请求后把数据从DDR3 存储器中取出并通过 PCIE 总线将数据传输到上位机&#xff1b;在下传数据时上位机中的数据首先通过 PCIE 总线下传至 FPGA&#xff0c;FPGA 读取这些数据并…

展厅中控系统有哪些优势呢

格芬科技的展厅中控系统具有多方面的优势&#xff0c;主要体现在以下几个方面&#xff1a; 一、高度集成与灵活控制 全终端网络可编程&#xff1a;格芬科技的展厅中控系统采用全终端网络可编程技术&#xff0c;能够实现对展厅内各种设备的集中控制和管理&#xff0c;包括电脑…

关于对CSDN的谴责

关于对CSDN的谴责 如果不是心血来潮登了一次旧帐号我是万万不会想到&#xff0c;所有的文章都被设置成了仅VIP可见。 CSDN你的VIP有多不受人待见您不知道吗&#xff1f;为什么要把我用于你开通VIP刷绩效的工具&#xff1f; 这种东西不应该首先经过同意再开启吗&#xff1f;默认…

yum不可用

当使用yum命令安装xx时&#xff0c;出现“Cannot find a vaild baseurl for repo&#xff1a;base”的错误&#xff0c;通常意为着yum源配置不正确或网络连接问题。 一、普通方法 1、检查网络连接 使用ping命令&#xff0c;测试是否可以ping通公网&#xff0c;如百度&#xf…

数据结构第一讲:复杂度

数据结构第一讲&#xff1a;复杂度 1.数据结构前言1.1什么是数据结构1.2算法 2.算法效率2.1复杂度的概念 3.时间复杂度3.1案例13.2案例23.3案例33.4案例43.5案例53.6案例63.7案例7 4.空间复杂度4.1案例14.2案例2 5.常见复杂度对比6.轮转数组题目分析6.1优化16.2优化2 博客简介&…