文章目录
- 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 调整图像的对比度
对比度是图像处理中一个非常重要的概念,它描述了图像中明暗区域之间的差异程度。具体来说,对比度反映了图像中最亮和最暗部分的亮度差异。对比度越高,图像中的明暗差异越明显;对比度越低,图像看起来越平淡、灰暗。通俗的讲,让亮的地方更亮,暗的地方更暗,使得差异更大。
在技术上,对比度可以定义为图像中像素值的范围。对于灰度图像,对比度可以用最大亮度和最小亮度之差来表示:
对比度=最大亮度−最小亮度
在彩色图像中,对比度通常是三个颜色通道(红、绿、蓝)的平均值。
对比度对于图像的视觉效果和信息传达有很大影响。适当的对比度可以使图像更加清晰、细节更加突出,增强视觉吸引力;而不合适的对比度可能会导致图像细节丢失或难以辨认。
对比度可以通过多种方法进行调整,常见的方法包括:
-
线性变换:
-
对图像中的每个像素值进行线性变换,使得亮度值范围变大或变小。
-
公式:
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
是亮度偏移量。 -
-
直方图均衡化:
- 通过调整图像的灰度直方图,使得像素值分布更加均匀,从而增强对比度。
-
伽马校正:
- 伽马矫正(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_8U
、CV_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):对缩放后的每个元素加上一个偏移量。
效果如图: