实战OpenCV之像素操作

news2024/11/24 12:00:25

基础入门

        在OpenCV中,像素是最基本的操作单位。图像可以视为一个三维数组,其中第三维表示颜色通道。图像数据在内存中以连续或几乎连续的方式存储,对于多通道图像(比如:BGR图像),每个像素的各通道值紧密排列。OpenCV主要使用BGR色彩空间,与常用的RGB顺序不同。因此,在进行像素操作时,需要特别注意色彩通道的顺序。OpenCV中最常见的图像格式是CV_8UC3,表示一个8位无符号整型的三通道图像。

        像素操作通常会涉及到颜色,在OpenCV中,Scalar类型常用来表示颜色。一个Scalar对象可以存储四个元素,分别对应于图像中的四个通道,通常为BGRA色彩空间。当处理彩色图像时,这四个值代表蓝色(B)、绿色(G)、红色(R)和可选的透明度(A,alpha通道)。如果处理的是灰度图像,则通常只使用第一个通道即可。

        如果我们想定义一个红色的颜色,可以参考下面的示例代码。

// BGR格式,故(0, 0, 255)代表红色
cv::Scalar redColor(0, 0, 255);

        对于带有透明度的颜色,可以像下面的示例代码这样,定义一个半透明的红色。

// 最后一个值是alpha通道,范围从0(完全透明)到255(完全不透明)
cv::Scalar semiTransparentColor(0, 0, 255, 128);

实战解析

        在OpenCV中,主要有两种方式来访问和修改像素值:指针访问和at函数访问。

        指针访问是指直接通过计算像素地址来进行操作,这种方式在性能上可能更优,但实现较为复杂,容易出错。在下面的示例代码中,我们创建了一个300 x 400像素的蓝色图像,并通过指针访问方式将图像最中间一行的像素修改为红色。

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
    // 创建一个300 x 400的蓝色图像
    Mat img(300, 400, CV_8UC3, Scalar(255, 0, 0));

    // 访问并修改最中间一行的像素,将其变为红色
    int nMidRow = img.rows / 2;
    uchar* pRow = img.ptr<uchar>(nMidRow);
    for (int nCol = 0; nCol < img.cols; nCol++)
    {
        // B分量
        pRow[nCol * 3] = 0;
        // G分量
        pRow[nCol * 3 + 1] = 0;
        // R分量
        pRow[nCol * 3 + 2] = 255;
    }

    // 显示图像
    imshow("Image", img);
    waitKey(0);
    return 0;
}

        at函数访问提供了一种更安全、更易读的方式来访问和修改像素值,虽然牺牲了一点性能,但代码更加清晰。在下面的示例代码中,我们同样创建了一个300 x 400像素的蓝色图像,并通过at函数访问方式将图像最中间一行的像素修改为红色。

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
    // 创建一个300 x 400的蓝色图像
    Mat img(300, 400, CV_8UC3, Scalar(255, 0, 0));

    // 访问并修改最中间一行的像素,将其变为红色
    int nMidRow = img.rows / 2;
    for (int nCol = 0; nCol < img.cols; nCol++)
    {
        // 通过at函数访问每个像素并修改其颜色分量
        img.at<Vec3b>(nMidRow, nCol)[0] = 0;
        img.at<Vec3b>(nMidRow, nCol)[1] = 0;
        img.at<Vec3b>(nMidRow, nCol)[2] = 255;
    }

    // 显示图像
    imshow("Image", img);
    waitKey(0);
    return 0;
}

        执行上面的示例代码,运行效果可参考下图。

均值计算

        在OpenCV中,计算图像的像素均值,主要利用cv::mean()函数。像素均值被计算出来后,可以作为图像亮度调整的基础。

Mat img;
Scalar avgPixel = cv::mean(img);
cout << avgPixel[0] << ", " << avgPixel[1] << ", " << avgPixel[2] << endl;

像素级逻辑操作

        像素级逻辑操作通常涉及按位与、或、异或等操作,主要用于图像处理中的掩码应用、图像合成等场景。OpenCV提供了一系列函数来执行像素级的逻辑操作,下面逐一进行介绍。

        1、逻辑与。cv::bitwise_and()函数用于对两个图像的对应像素执行逻辑与操作。如果两个像素都为非零,结果像素就是非零;否则,结果像素为零。这对于应用掩码、选取图像的交集区域非常有用。

        2、逻辑或。cv::bitwise_or()函数用于对两个图像的对应像素执行逻辑或操作。只要两个像素中有一个为非零,结果像素就是非零。这可以用来合并图像的区域,或增加特征检测的鲁棒性。

        3、逻辑异或。cv::bitwise_xor()函数用于对两个图像的对应像素执行逻辑异或操作。当两个对应像素不同时,结果像素为非零;否则,结果像素为零。这可以用于突出显示两个图像之间的差异。

        4、逻辑非。cv::bitwise_not()函数用于对图像中的每个像素执行逻辑非操作,即将1变为0,0变为1。这常用于图像的反转,或颜色空间转换前的预处理。

        注意:在使用以上这些函数时,需要确保输入图像的数据类型兼容,并且大小相同。此外,还可以传入一个可选的掩码参数,仅对掩码中非零的像素执行操作,这对于局部处理非常有用。

        接下来,我们通过下面的实战代码来理解cv::bitwise_and()函数。

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main()
{
    Mat srcImage = imread("OpenCV.png");
    if(srcImage.empty())
    {
        cout << "Image not found" << endl;
        return -1;
    }

    // 创建一个矩形的掩码图像,初始化为全黑
    Mat mask = Mat::zeros(srcImage.size(), CV_8UC1);
    // 画一个白色矩形作为掩码
    rectangle(mask, Point(150, 0), Point(450, 250), Scalar(255, 255, 255), -1);

    // 使用bitwise_and函数应用掩码
    Mat destImage;
    bitwise_and(srcImage, srcImage, destImage, mask);

    // 显示原图、掩码和结果
    imshow("Original Image", srcImage);
    imshow("Mask", mask);
    imshow("Result Image", destImage);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

        在上面的示例代码中,我们首先读取了一张图片到srcImage中。接着,我们创建了一个与原图同样大小的单通道灰度图像mask,并用一个白色矩形填充了其中一部分。这个白色矩形就是我们的“感兴趣区域”,其余部分为黑色,代表透明或不需要的部分。

        通过bitwise_and函数,我们将原图srcImage与自身进行了按位与操作,并且指定了掩码mask。这意味着只有掩码中为白色(值为255)的部分,在结果图像中保留了原图的像素值。而掩码中为黑色(值为0)的部分,在结果图像中对应的像素值将被设为0(黑色),从而达到了只显示我们感兴趣区域的效果。

        最后,我们使用imshow显示了原图、掩码、应用掩码后的结果图像,可参考如下。

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

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

相关文章

828华为云征文 | 云上私人数据管家,jMalCloud个人网盘在华为云Flexus的Docker化部署实践

华为云服务器Flexus X实例介绍 华为云Flexus云服务器X实例&#xff0c;是由国家科技进步奖获得者、华为公司Fellow、华为云首席架构师顾炯炯牵头研发。它基于擎天QingTian架构、瑶光云脑、盘古大模型等根技术创新&#xff0c;是业界首款应用驱动的柔性算力云服务器&#xff0c;…

USB Type-C 接口引脚定义,以及 USB 3.1 和 USB 3.2 的区别

USB Type-C 接口引脚定义 USB 3.1 和 USB 3.2 的区别&#xff08;摘自网络&#xff0c;未验证&#xff09;

Matlab对状态机建模的方法

【 线性代数 状态机 】良好的控制系统设计 (根据现有的情况总结出状态转移方程) 状态组件在设计时需要考虑的内容 AI 的逻辑 可以提供一个思路

ICM20948 DMP代码详解(16)

接前一篇文章&#xff1a;ICM20948 DMP代码详解&#xff08;15&#xff09; 上一回讲到了inv_icm20948_initialize函数中的inv_icm20948_initialize_lower_driver函数中的inv_icm20948_read_mems_reg函数中的inv_icm20948_set_chip_power_state函数。再次贴出该函数源码&#x…

[SUCTF 2019]CheckIn

1、文件上传题 2、先上传.user.ini&#xff0c;抓包&#xff0c;然后放包 .user.ini内容为&#xff1a; GIF89a auto_prepend_filetest.jpg 3、接着上传test.jpg&#xff0c;抓包&#xff0c;放包 test.jpg内容&#xff1a; GIF89a? <script languag…

一个基于Spring实现的热更新插件开发框架

前言 对于其他解释性语言来说&#xff0c;热更新根本不是什么事&#xff0c;但对于Java来说是多么的不容易&#xff0c;现在使用Java开发的热更新系统&#xff0c;基本使用JS编写脚本&#xff0c;然后用Java的JavaScript引擎来跑脚本。 spring-hot-plugin 现在有一款开源的S…

PDB自启动设置

1、自启动说明 方案一&#xff1a;oracle12.1只能使用触发器 方案二&#xff1a;oracle12.2可用ALTER pluggable DATABASE ALL save state; 高版本建议优先使用第二种模式 2、触发器模式 CREATE TRIGGER open_all_pdbs AFTER STARTUP ON DATABASE BEGIN EXECUTE IMMEDIA…

通过C# 裁剪PDF页面

在处理PDF文档时&#xff0c;有时需要精确地裁剪页面以适应特定需求&#xff0c;比如去除广告、背景信息或者仅仅是为了简化文档内容。 本文将指导如何使用免费.NET控件通过C#实现裁剪PDF页面。 免费库 Free Spire.PDF for .NET 支持在 .NET (C#, VB.NET, ASP.NET, .NET Core)…

JAVA优化物流生态聚合快递与云洋系统小程序源码

优化物流生态&#xff0c;聚合快递与云洋系统小程序 &#x1f69a; 开篇&#xff1a;物流新生态&#xff0c;从这里启航 在这个快节奏的时代&#xff0c;物流不仅是商品传递的桥梁&#xff0c;更是连接消费者与商家的关键纽带。你是否厌倦了繁琐的物流查询、不稳定的配送时效&…

[Python学习日记-16] 细讲数据类型——元组

[Python学习日记-16] 细讲数据类型——元组 简介 元组的创建和查看 元组的切片 元组的循环 元组的嵌套 简介 有些时候我们的列表数据不想被人修改时怎么办&#xff1f;就可以用元组存放。元组&#xff08;tuple&#xff09;其实是列表的兄弟类型&#xff0c;他们非常的相似…

springboot项目中 前端浏览器访问时遇到跨域请求问题CORS怎么解决?has been blocked by CORS policy

文章目录 现象解决方案1. **全局配置 CORS**2. **使用 CrossOrigin 注解**3. **配置 Spring Security**4. **自定义 CORS 过滤器** Spring Security 6.x 及其后续版本解决方案1. 使用 SecurityFilterChain 配置 CORS2. 重要配置说明3. 在生产环境中的最佳实践 现象 前端浏览器…

【题解】CF2008G

题意翻译 原题链接CF2008G 思路 由于操作次数不限&#xff0c;观察到所有操作都是可逆的&#xff0c;所以可以随便搞。然后观察mex函数&#xff0c;发现让所有数在不重复的情况下尽可能地小是最优的&#xff08;重复就浪费了&#xff09;。      先不考虑重复和 0 0 0&a…

Ali_Yun Port

Ali_Yun Port 云服务器端口

【信创】加装硬盘后如何迁移微信数据到新磁盘 _ 统信 _ 麒麟 _ 方德

原文链接&#xff1a;【信创】Linux加装硬盘后如何迁移微信数据到新磁盘 | 统信 | 麒麟 | 方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于在Linux系统中加装新硬盘后&#xff0c;如何将微信等数据迁移到新磁盘的文章。在使用过程中&#xff0c;随着数据量的…

拱式桥安全结构健康监测解决方案

拱式桥作为一种常见的桥梁结构&#xff0c;其拱形设计不仅美观&#xff0c;还具有较高的承载能力。然而&#xff0c;随着使用年限的增加和环境因素的影响&#xff0c;拱式桥的结构健康和稳定需要持续监测和评估。自动化监测技术的应用&#xff0c;可以提升拱式桥的监测效率和准…

快速使用react 全局状态管理工具--redux

redux Redux 是 JavaScript 应用中管理应用状态的工具&#xff0c;特别适用于复杂的、需要共享状态的中大型应用。Redux 的核心思想是将应用的所有状态存储在一个单一的、不可变的状态树&#xff08;state tree&#xff09;中&#xff0c;状态只能通过触发特定的 action 来更新…

代码随想录训练营 Day58打卡 图论part08 拓扑排序 dijkstra(朴素版)

代码随想录训练营 Day58打卡 图论part08 一、拓扑排序 例题&#xff1a;卡码117. 软件构建 题目描述 某个大型软件项目的构建系统拥有 N 个文件&#xff0c;文件编号从 0 到 N - 1&#xff0c;在这些文件中&#xff0c;某些文件依赖于其他文件的内容&#xff0c;这意味着如果…

用Python实现时间序列模型实战——Day 18: 时间序列中的季节性与周期性预测

一、学习内容 1. 季节性调整与周期性预测 季节性调整 是在时间序列分析中常用的技术&#xff0c;旨在去除数据中因季节性波动导致的周期性变化&#xff0c;使数据更易于解释和预测。通常&#xff0c;我们可以使用季节性分解方法来分离时间序列中的趋势、季节性和随机成分。 …

JAVA实现压缩包解压兼容Windows系统和MacOs

目标&#xff1a;JAVA实现压缩包解压获取图片素材 问题&#xff1a;Windows系统和MacOs压缩出来的zip内容有区别 MacOs会多出来 以及本身一个文件夹 而windows则不会。为了解决这个问题。兼容mac的压缩包增加一层过滤 要知道 ZipInputStream 可以读取 ZIP 文件中的条目&…

KTV 营业明细+员工提成—SAAS本地化及未来之窗行业应用跨平台架构

一、ktv 绩效必要性 1. 激励员工积极性&#xff1a;提成制度能够直接将员工的努力和收入挂钩&#xff0c;促使员工更加积极主动地工作&#xff0c;以获取更高的收入。 2. 提高工作效率和业绩&#xff1a;为了获得更多提成&#xff0c;员工会努力提高工作效率&#xff0c;增加业…