OpenCV 遍历Mat,像素操作,使用TrackBar 调整图像的亮度和对比度 C++实现

news2024/9/21 15:38:50

文章目录

    • 1.使用C++遍历Mat,完成颜色反转
      • 1.1 常规遍历方式
      • 1.2 迭代器遍历方式
      • 1.3指针访问方式遍历(最快)
      • 1.4不同遍历方式的时间对比
    • 2.图像像素操作,提高图像的亮度
    • 3.TrackBar 进度条操作
      • 3.1使用TrackBar 调整图像的亮度
      • 3.2使用TrackBar 调整图像的对比度

1.使用C++遍历Mat,完成颜色反转

首先先看Mat 构成方式,以彩色图像三通道为例,在内存排布是顺序排布,因此可以很高效地遍历Mat的每一个像素点,遍历方式为先遍历行再遍历列

在这里插入图片描述

1.1 常规遍历方式

先遍历行再遍历列,取出的值是一个三通道 无符号整形8位的数据 值的范围代表0-255,OpenCV提供了Vec3b来存储,本质上可以理解为在同一块内存区域,连续存放方式。

for (int j = 0; j < h; ++j) {  // 正确的行(高度)索引
        for (int i = 0; i < w; ++i) {  // 正确的列(宽度)索引
            if (c == 1) {
                // 单通道,灰度图像
                uchar pv = image.at<uchar>(j, i);
                image.at<uchar>(j, i) = 255 - pv;
            } else if (c == 3) {
                // 三通道,BGR 数据 三个值
                Vec3b pv_rgb = image.at<Vec3b>(j, i);
                pv_rgb[0] = 255 - pv_rgb[0];
                pv_rgb[1] = 255 - pv_rgb[1];
                pv_rgb[2] = 255 - pv_rgb[2];
                image.at<Vec3b>(j, i) = pv_rgb;
            }
        }
    }

1.2 迭代器遍历方式

OpenCV 提供了Mat的迭代器,好处是比较安全。迭代器隐藏了内部的行跨距(step size),使得代码更易读。

 for (cv::MatIterator_<cv::Vec3b> it = image.begin<cv::Vec3b>(); it != image.end<cv::Vec3b>(); ++it) {
        cv::Vec3b& pixel = *it;  // 引用当前像素
        // 访问 B, G, R 通道
        uchar blue = pixel[0];
        uchar green = pixel[1];
        uchar red = pixel[2];
        pixel[0] = 255- blue;
        pixel[1] = 255- green;
        pixel[2] = 255- red;
        // 对像素值进行操作
        // pixel[0] = new_blue;
        // pixel[1] = new_green;
        // pixel[2] = new_red;
    }

1.3指针访问方式遍历(最快)

指针无需多言,快就完了

//基于指针的方式 最快
    start =  std::chrono::steady_clock::now();
    // 遍历所有行
    for (int i = 0; i < image.rows; i++) {
        // 获取行指针
        Vec3b* row = image.ptr<Vec3b>(i);
        for (int j = 0; j < image.cols; j++) {
            // 访问像素,row[j] 是 Vec3b 类型,表示一个BGR像素
            row[j][0] = 255 - row[j][0]; // B
            row[j][1] = 255 - row[j][1]; // G
            row[j][2] = 255 - row[j][2]; // R
        }
    }
    end = std::chrono::steady_clock::now();
    nanoseconds = calculateTimeDifferenceInNanoseconds(start, end);
    std::cout << "ptr during:" << nanoseconds << " us." << std::endl;

1.4不同遍历方式的时间对比

在同一张图片在每次遍历完成之后,计算开始时间与结束时间差异可以得到以下时间对比。可以看出指针方式遍历只需3ms左右,常规for循环需要8ms左右,而迭代器迭代的方式需要16ms 左右,最慢

在这里插入图片描述

2.图像像素操作,提高图像的亮度

这一块操作较为简单,OpenCV的Mat类,重载大多数的运算符,使得图像像素加减操作如同整数加减一样简单,简单代码示意

void pixel_operator(Mat &image){
    Mat dst;
    imshow("orig img",image);
    dst = image + Scalar (50,50,50);
    imshow("orig plus img",dst);
    dst = image - Scalar (100,100,100);
    imshow("orig minus img",dst);

    waitKey(0);
    destroyAllWindows();
}

Scalar可以提供RGB三通道数据。 当R G B 三种像素都增大的时候,图像的整体亮度就会得到增强,反之,图像的整体亮度就减弱。

效果示意,左上角为原始图像,右上角为图形亮度减弱的图像,中间的图像为图像亮度增强的效果

在这里插入图片描述

3.TrackBar 进度条操作

3.1使用TrackBar 调整图像的亮度

在OpenCV中,TrackBar是一种在窗口中添加交互式滑动条的工具,可以用来动态调整图像处理参数。使用TrackBar可以方便地调试和调整图像处理算法。

示例代码

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

using namespace cv;
using namespace std;
//全局变量
Mat src, dst;
int lightness;

// TrackBar回调函数
static void on_track(int, void*) {
    Mat m = Scalar(lightness, lightness, lightness);
    add(src, m, dst);  // 基于原始图像 src 进行亮度调整
    imshow("lightness adjust", dst);
}

// TrackBar示例函数
void tracking_bar_demo(Mat& image) {
    namedWindow("lightness adjust", WINDOW_AUTOSIZE);  // 创建窗口
    int max_val = 100;
    lightness = 50;
    src = image.clone();  // 保留原始图像
    dst = Mat::zeros(image.size(), image.type());  // 初始化调整后的图像
    createTrackbar("Value Bar:", "lightness adjust", &lightness, max_val, on_track);
    on_track(lightness, 0);  // 初始化显示
}

int main() {
    // 读取图像
    Mat image = imread("your_image_path.jpg", IMREAD_COLOR);
    if (image.empty()) {
        cout << "Could not open or find the image!" << endl;
        return -1;
    }
    // 调用TrackBar示例函数
    tracking_bar_demo(image);

    // 事件循环
    while (true) {
        int key = waitKey(30);
        if (key == 27) {  // 按下ESC键退出
            break;
        }
    }
    return 0;
}

代码解读

读取和转换图像

  • 使用imread读取图像,并检查图像是否成功加载。

创建窗口和TrackBar

  • 使用namedWindow创建一个窗口并显示初始的灰度图像。
  • 使用createTrackbar在窗口中创建一个TrackBar。参数包括TrackBar的名称、窗口名称、指向变量的指针、最大值和回调函数

回调函数 on_trackbar(int, void\*)

  • 当TrackBar的值发生变化时,OpenCV会调用这个函数。然后会把原图像加上新的变化的像素值,然后在窗口中显示变化的图像

代码改进:

分析TrackBar 的构造函数

CV_EXPORTS int createTrackbar(const String& trackbarname, const String& winname,
                              int* value, int count,
                              TrackbarCallback onChange = 0,
                              void* userdata = 0);

在创建TrackBar的时候,还可以传入的void*的用户数据,回调函数也可以接受变化的值和用户数据

因此,可以直接通过指针传递的方式,不需要再创建全局变量

改进后:直接将图像数据传递进回调函数,无需再拷贝一份图像数据,无需其他变量,修改之后直接进行显示。

//track bar UI改动的回调函数
static void on_track(int val,void* userdata){
    imshow("lightness adjust", *(Mat*)userdata+Scalar (val,val,val));
}
void tracking_bar_demo(Mat &image){
    namedWindow("lightness adjust",WINDOW_AUTOSIZE);
    int max_val = 100;
    int lightness = 50;
    createTrackbar("Value Bar:","lightness adjust",&lightness,max_val,on_track,&image);
    on_track(lightness, &image);  // 初始化值
}

效果如图:

在这里插入图片描述

3.2使用TrackBar 调整图像的对比度

对比度是图像处理中一个非常重要的概念,它描述了图像中明暗区域之间的差异程度。具体来说,对比度反映了图像中最亮和最暗部分的亮度差异。对比度越高,图像中的明暗差异越明显;对比度越低,图像看起来越平淡、灰暗。通俗的讲,让亮的地方更亮,暗的地方更暗,使得差异更大。

在技术上,对比度可以定义为图像中像素值的范围。对于灰度图像,对比度可以用最大亮度和最小亮度之差来表示:

对比度=最大亮度−最小亮度

在彩色图像中,对比度通常是三个颜色通道(红、绿、蓝)的平均值。

对比度对于图像的视觉效果和信息传达有很大影响。适当的对比度可以使图像更加清晰、细节更加突出,增强视觉吸引力;而不合适的对比度可能会导致图像细节丢失或难以辨认。

对比度可以通过多种方法进行调整,常见的方法包括:

  1. 线性变换

    • 对图像中的每个像素值进行线性变换,使得亮度值范围变大或变小。

    • 公式:
      n e w p i x e l = c o n t r a s t × o r i g i n a l p i x e l + b r i g h t n e s s new pixel=contrast×original pixel+brightness newpixel=contrast×originalpixel+brightness

    其中,contrast 是对比度系数,brightness 是亮度偏移量。

  2. 直方图均衡化

    • 通过调整图像的灰度直方图,使得像素值分布更加均匀,从而增强对比度。
  3. 伽马校正

    • 伽马矫正(Gamma Correction)是用来针对影片或影像系统里对于光线的辉度(luminance)或是三色刺激值(tristimulus values)所进行非线性的运算或反运算。其公式一般为幂定律公式

这里使用最简单的方式 线性变换来实现调整对比度

示例代码:

Mat src, dst;
int contrast = 50;
int brightness = 50;

// TrackBar回调函数
static void on_trackbar(int, void*) {
    double alpha = contrast / 50.0; // 对比度系数(范围:0.0 - 2.0)
    int beta = brightness - 50;     // 亮度偏移量(范围:-50 - 50)

    src.convertTo(dst, -1, alpha, beta); // 进行对比度和亮度调整
    imshow("Adjust Contrast and Brightness", dst);
}

// TrackBar示例函数
void tracking_bar_demo(Mat &image) {
    namedWindow("Adjust Contrast and Brightness", WINDOW_AUTOSIZE);
    src = image.clone();
    dst = Mat::zeros(image.size(), image.type());

    // 创建对比度和亮度调节的TrackBar
    createTrackbar("Contrast", "Adjust Contrast and Brightness", &contrast, 100, on_trackbar);
    createTrackbar("Brightness", "Adjust Contrast and Brightness", &brightness, 100, on_trackbar);

    on_trackbar(0, 0); // 初始化显示
}
int main()
{
    string imagePath = "C:\\Users\\Marxist\\Pictures\\coco\\test.jpg";
    Mat image = imread(imagePath);
    if(image.empty()){
        cout<<"file not exist!"<<endl;
        return -1;
    }
    //pixel_operator(image);
    imshow("src image",image);
    tracking_bar_demo(image);
    // 事件循环
    while (true) {
        int key = waitKey(30);
        if (key == 27) {  // 按下ESC键退出
            break;
        }
    }
    return 0;
}

关键函数:convertTo ,用于将一个矩阵转换为另一个矩阵,支持不同的类型和缩放操作。它的主要用途是进行图像类型转换和数值转换,通常用于调整图像的对比度和亮度。

函数定义:

void convertTo(OutputArray m, int rtype, double alpha=1, double beta=0) const;

参数详解

  • OutputArray m: 输出矩阵或图像。可以是与输入矩阵类型相同或不同的类型。
  • int rtype: 输出矩阵的类型(例如 CV_8UCV_32F 等)。如果为 -1,表示输出矩阵类型与输入矩阵相同。
  • double alpha: 可选的缩放因子(乘法因子)。默认为 1。
  • double beta: 可选的偏移量(加法因子)。默认为 0。

该函数执行以下转换:
d s t ( x , y ) = s r c ( x , y ) × α + β dst(x,y)=src(x,y)×α+β dst(x,y)=src(x,y)×α+β

  • 缩放(alpha):对输入矩阵中的每个元素进行缩放。
  • 偏移(beta):对缩放后的每个元素加上一个偏移量。

效果如图:

在这里插入图片描述

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

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

相关文章

【JavaEE进阶】——Spring事务和事务传播机制

目录 &#x1f6a9;事务 &#x1f388;为什么需要事务? &#x1f388;事务的操作 &#x1f6a9;Spring 中事务的实现 &#x1f388;数据准备 &#x1f388;Spring 编程式事务(了解) &#x1f388;Spring 声明式事务 Transactional &#x1f36d;Transactional 详解 &…

2013年全国大学生数学建模竞赛B题碎纸片复原(含word论文和源代码资源)

文章目录 一、部分题目二、部分论文三、部分源代码四、完整word版论文和源代码&#xff08;两种获取方式&#xff09; 一、部分题目 2013高教社杯全国大学生数学建模竞赛题目 B题 碎纸片的拼接复原 破碎文件的拼接在司法物证复原、历史文献修复以及军事情报获取等领域都有着重…

基于术语词典干预的机器翻译挑战赛笔记Task2 #Datawhale AI 夏令营

上回&#xff1a; 基于术语词典干预的机器翻译挑战赛笔记Task1 跑通baseline Datawhale AI 夏令营-CSDN博客文章浏览阅读718次&#xff0c;点赞11次&#xff0c;收藏8次。基于术语词典干预的机器翻译挑战赛笔记Task1 跑通baselinehttps://blog.csdn.net/qq_23311271/article/d…

统计一个页面用到的html,css,js

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>统计html</title><style>* {margin: …

【人工智能】AI音乐创作兴起与AI伦理的新视角

文章目录 &#x1f34a;AI音乐创作&#xff1a;一键生成&#xff0c;打造你的专属乐章&#x1f34a;1 市面上的AI音乐应用1.1 Suno AI1.2 网易天音 &#x1f34a;2 AI音乐创作的流程原理(直接制作可跳到第3点)2.1 AI音乐流派2.2 AI音乐风格2.3 AI音乐的结构顺序2.5 选择AI音乐乐…

基于PSO算法优化PID参数的一些问题

目录 前言 Q1&#xff1a;惯性权重ω如何设置比较好&#xff1f;学习因子C1和C2如何设置&#xff1f; Q2&#xff1a;迭代速度边界设定一定能够遍历&#xff08;/覆盖&#xff09;整个PID参数二维空间范围吗&#xff1f;还是说需要与迭代次数相关&#xff1f;迭代次数越高&a…

阵列信号处理学习笔记(一)--阵列信号处理定义

阵列信号 阵列信号处理学习笔记&#xff08;一&#xff09;–阵列信号处理定义 阵列信号处理学习笔记&#xff08;二&#xff09;–空域滤波基本原理 文章目录 阵列信号前言一、阵列信号处理定义1.1 信号1.2 阵列 二、雷达数据中哪些属于空间采样总结 前言 MOOC 阵列信号处理…

Bubbliiiing 的 Retinaface rknn python推理分析

Bubbliiiing 的 Retinaface rknn python推理分析 项目说明 使用的是Bubbliiiing的深度学习教程-Pytorch 搭建自己的Retinaface人脸检测平台的模型&#xff0c;下面是项目的Bubbliiiing视频讲解地址以及源码地址和博客地址&#xff1b; 作者的项目讲解视频&#xff1a;https:…

数据结构排序合集(笔记)

目录 一.插入排序与希尔排序 二.选择排序与堆排序 三.冒泡排序和快速排序 四.归并排序 五.计数排序 一.插入排序与希尔排序 时间复杂度空间复杂度稳定性插入排序O&#xff08;N^2&#xff09;O(1)稳定希尔排序O(N^1.3)O(1)不稳定 插入排序&#xff1a; 希尔排序&#xff…

css-01-如何实现“文本过长(文本在一行排),超出部分被省略号代替”

目录 需求代码代码解释 需求 最近写一个项目&#xff0c;遇到了一个问题&#xff0c;就是希望实现下面这种文字效果&#xff1a;文字在一行排&#xff0c;超出的部分用省略号代替 代码 <!DOCTYPE html> <html lang"en"><head><meta charset…

OCC 创建点线面体

目录 一、利用封装已有算法实现 1、盒子建模算法封装 2、可视化 二、利用OCC 点线面实现 1、实现过程 2、实现一个面 3、拉伸面生成体 4、旋转面生成体 三、总结 一、利用封装已有算法实现 1、盒子建模算法封装 BRepPrimAPI_MakeBox box(2, 2, 2); 2、可视化 void VTK…

Gateway源码分析:路由Route、断言Predicate、Filter

文章目录 源码总流程图说明GateWayAutoConfigurationDispatcherHandlergetHandler()handleRequestWith()RouteToRequestUrlFilterReactiveLoadBalancerClientFilterNettyRoutingFilter 补充知识适配器模式 详细流程图 源码总流程图 在线总流程图 说明 Gateway的版本使用的是…

在线 PDF 制作者泄露用户上传的文档

两家在线 PDF 制作者泄露了数万份用户文档&#xff0c;包括护照、驾驶执照、证书以及用户上传的其他个人信息。 我们都经历过这样的情况&#xff1a;非常匆忙&#xff0c;努力快速制作 PDF 并提交表单。许多人向在线 PDF 制作者寻求帮助&#xff0c;许多人的祈祷得到了回应。 …

Python学习笔记—100页Opencv详细讲解教程

目录 1 创建和显示窗口... - 4 - 2 加载显示图片... - 6 - 3 保存图片... - 7 - 4 视频采集... - 8 - 5视频录制... - 11 - 6 控制鼠标... - 12 - 7 TrackBar 控件... - 14 - 8.RGB和BGR颜色空间... - 16 - 9.HSV和HSL和YUV.. - 17 - 10 颜色空间的转化... - 18 - …

分页查询与分页条件查询

--------------- 无PageHelper插件分页查询 1.创建PageBean实体类 Data NoArgsConstructor AllArgsConstructor public class PageBean<T> {private Long total;//总条数private List<T> items;//当前页数据集合 }类型安全性 泛型&#xff1a;提供了编译时的类型…

【学长工具库】1.如何快速部署开源框架 | 若依框架保姆级搭建教程

今天学长带来了一款十分适合自学的开源框架-若依框架&#xff0c; 本文会详细的教大家怎么部署这个系统。 文末有所有资料获取方式~ 框架技术栈 前端采用 Vue、Element UI。后端采用 Spring Boot、Spring Security、Redis & Jwt。权限认证使用 Jwt&#xff0c;支持多终端…

【IEEE出版,会议历史良好、论文录用检索快】第四届计算机科学与区块链国际学术会议 (CCSB 2024,9月6-8)

CCSB 2024会议由深圳大学主办&#xff0c;旨在探讨计算机科学的最新发展如何与区块链技术相结合&#xff0c;以及这一结合如何推动金融、供应链管理、数据安全和其他多个行业的革新&#xff0c; 本次会议将提供一个多学科交流的平台&#xff0c;汇集来自相关领域学者的研究和思…

vxe-弹窗初始化激活选中Vxe-Table表格中第一行input输入框

1.实现效果 2.Modal弹窗的渲染过程 一、Vue组件的生命周期 Vue组件从创建到销毁会经历一系列的生命周期钩子&#xff0c;这些钩子为开发者提供了在不同阶段插入自定义逻辑的机会。在Modal弹窗的上下文中&#xff0c;这些生命周期钩子同样适用。 beforeCreate&#xff1a;组件…

解决 Ubuntu 用户登录后的 shell 和功能问题

在使用 Ubuntu 系统管理用户时&#xff0c;可能会遇到一些常见的问题&#xff0c;比如新创建的用户无法使用常见命令&#xff08;如 ll&#xff09;以及输出信息没有颜色。这些问题通常与用户的默认 shell 有关。本文将总结如何解决这些问题&#xff0c;并确保新用户能够正常使…

【linux深入剖析】命名管道 | 匿名管道与命名管道的区别 | system V共享内存

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1. 命名管道2. 创建命名管…