注:以下是根据链接https://blog.youkuaiyun.com/Cream_Cicilian/article/details/105427752 的小白学习过程。
1加载、修改、保存图像
1.1加载图像
1.1.1加载图像cv::imread
用于从文件中读取图像数据并将其存储到一个 cv::Mat 对象中,其中第一个参数表示图像文件名称
第二个参数,表示加载的图像是什么类型
以下是三个参数值展示效果
(1)当使用 IMREAD_UNCHANGED 时
IMREAD_UNCHANGED (<0) 表示加载原图,不做任何改变
#include <opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
cv::Mat image = cv::imread("D:\\vscodestore\\zxc.jpg", cv::IMREAD_UNCHANGED);
//Mat src = imread("D:\\vscodestore\\zxc.jpg");
if (image.empty()) {
printf("could not find the image!\n");
return -1;
}
imshow("ImputImage", image);
waitKey(0);
system("path");
getchar();
return 0;
}
(2)当使用IMREAD_GRAYSCALE 时
IMREAD_GRAYSCALE ( 0)表示把原图作为灰度图像加载进来
#include <opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
cv::Mat grayImage = cv::imread("D:\\vscodestore\\cartoon.jpg", cv::IMREAD_GRAYSCALE);
if (grayImage.empty()) {
printf("could not find the image!\n");
return -1;
}
imshow("ImputImage", grayImage);
waitKey(0);
system("path");
getchar();
return 0;
}
用人像实验灰度图像有点怪,这里用卡通人物纪念童年
(3)当使用IMREAD_COLOR时
IMREAD_COLOR (>0) 表示把原图作为RGB图像加载进来
#include <opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
cv::Mat rbgImage = cv::imread("D:\\vscodestore\\zxc.jpg", cv::IMREAD_COLOR);
if (rbgImage.empty()) {
printf("could not find the image!\n");
return -1;
}
imshow("ImputImage", rbgImage);
waitKey(0);
system("path");
getchar();
return 0;
}
使用 IMREAD_COLOR 和 IMREAD_UNCHANGED 的主要区别在于理论上的处理意图——前者明确要求以彩色无透明度信息的方式读取,后者理论上想要保持所有可能的原始信息,但在 JPG 的实际应用场景中这一点并不适用,因为 JPG 不携带 alpha 通道信息。因此,在 JPG 图像的上下文中,这两个模式的实际效果是等价的。
1.1.2显示图像(cv::namedWindos 与cv::imshow)
namedWindow 是 OpenCV 库中的一个函数,用于创建一个窗口并为其指定一个名称。使用 namedWindow 创建窗口后,可以在这个窗口中显示图像,进行交互,比如调整窗口大小、移动窗口位置等。
imshow根据窗口名称显示图像到指定的窗口上去,第一个参数是窗口名称,第二参数是Mat对象
以下是参数值展示效果:
- WINDOW_AUTOSIZE会自动根据图像大小,显示窗口大小,不能人为改变窗口大小
- WINDOW_NORMAL,跟QT集成的时候会使用,允许修改窗口大小。
#include <opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
cv::Mat rbgImage = cv::imread("D:\\vscodestore\\zxc.jpg", cv::IMREAD_COLOR);
if (rbgImage.empty()) {
printf("could not find the image!\n");
return -1;
}
namedWindow("exampleWindow", WINDOW_NORMAL);
imshow("exampleWindow", rbgImage);
waitKey(0);
destroyAllWindows();
return 0;
}
你会发现图片小了好多,本来还在想怎么输入代码调整窗口大小,后来搜索发现就是在显示的图片窗口简单粗暴的拉动窗口😂
这里WINDOW_AUTOSIZE用法就不演示了,就是代码换一下就行。
namedWindow("exampleWindow", WINDOW_AUTOSIZE);
1.2修改图像 (cv::cvtColor)
cvtColor的功能是把图像从一个彩色空间转换到另外一个色彩空间,有三个参数,第一个参数表示源图像、第二参数表示色彩空间转换之后的图像、第三个参数表示源和目标色彩空间如:COLOR_BGR2HLS 、COLOR_BGR2GRAY 等
cvtColor(image, output_image, COLOR_BGR2HLS);
#include <opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
cv::Mat image = cv::imread("D:\\vscodestore\\cartoon.jpg", cv::IMREAD_COLOR);
if (image.empty()) {
printf("could not find the image!\n");
return -1;
}
namedWindow("exampleWindow1", WINDOW_NORMAL);//创建窗口
imshow("exampleWindow1", image);//显示默认窗口
Mat output_image;
cvtColor(image, output_image, COLOR_BGR2HLS);
namedWindow("exampleWindow2", WINDOW_NORMAL);//创建窗口
imshow("exampleWindow2", output_image);//显示创建窗口
waitKey(0);
system("path");
getchar();
return 0;
}
cvtColor( image, gray_image, COLOR_BGR2GRAY );
这里替换一下就好
1.3保存图像(cv::imwrite)
保存图像文件到指定目录路径
只有8位、16位的PNG、JPG、Tiff文件格式而且是单通道或者三通道的RGB的图像才可以通过这种方式保存
保存PNG格式的时候可以保存透明通道的图片
可以指定压缩参数
#include <opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
cv::Mat image = cv::imread("D:\\vscodestore\\cartoon.jpg", cv::IMREAD_COLOR);
if (image.empty()) {
printf("could not find the image!\n");
return -1;
}
namedWindow("exampleWindow1", WINDOW_NORMAL);//创建窗口
imshow("exampleWindow1", image);//显示默认窗口
Mat output_image;
cvtColor(image, output_image, COLOR_BGR2GRAY);
namedWindow("exampleWindow2", WINDOW_NORMAL);//创建窗口
imshow("exampleWindow2", output_image);//显示创建窗口
imwrite("D:\\vscodestore\\copycartoon.jpg", output_image);
waitKey(0);
system("path");
getchar();
return 0;
}
2矩阵的掩膜操作
掩膜操作可实现图像对比度调整;矩阵的掩膜操作是根据掩膜来重新计算每个像素的像素值,掩膜(mask 也被称为 Kernel)
参考链接:https://blog.youkuaiyun.com/wangyuankl123/article/details/104726702
红色是中心像素,从上到下,从左到右对每个像素都做同样的处理操作,得到最终结果就是对比度提高之后的输出图像
计算公式:
I(i,j) = 5 * I(i,j) - [I(i-1,j) + I(i+1,j) + I(i,j-1) + I(i,j+1)]
#include <opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main() {
cv::Mat src = cv::imread("D:\\vscodestore\\zxc.jpg", cv::IMREAD_COLOR);
if (src.empty()) {
printf("could not find the image!\n");
return -1;
}
namedWindow("exampleWindow1", WINDOW_NORMAL);//创建窗口
imshow("exampleWindow1", src);//显示默认窗口
Mat output_image;
int cols = (src.cols - 1)*src.channels();
//计算处理的有效列数。src.cols 是源图像的总列数,减去1是因为算法中通常会避免处理边缘像素以防止数组越界(这里假设处理时忽略了边缘像素)。src.channels() 返回图像的通道数,通常是3(对于RGB图像)。乘以通道数是为了在遍历时正确地跳过通道,确保处理的是完整的像素而不是单个通道。
int offsetx = src.channels();
//定义了一个偏移量,用于在遍历图像像素时跳过到下一个像素的第一个通道。对于多通道图像(如RGB图像),每个像素占据连续的内存位置,通道数就是需要跳过的字节数以到达下一个像素的起始位置。
int rows = src.rows;
//获取了源图像的行数,存储在变量 rows 中,用于后续遍历图像的行
output_image = Mat::zeros(src.size(), src.type());
//用全零初始化了 output_image。Mat::zeros 是一个静态工厂方法,用于创建一个给定尺寸和类型的矩阵,并将其所有元素初始化为0。src.size() 保证了输出图像与源图像具有相同的尺寸(高度和宽度),而 src.type() 确保了输出图像的通道数和数据类型与源图像相同,这样处理后的图像能与原图兼容。
for (int row = 1; row < (rows - 1); row++) {
//循环从第二行开始到倒数第二行结束,避免了图像的边界(第一行和最后一行),因为在计算时需要用到上下相邻的像素。
const uchar*previous = src.ptr<uchar>(row - 1);
const uchar*current = src.ptr<uchar>(row);
const uchar*next = src.ptr<uchar>(row + 1);
//分别为上一行(previous)、当前行(current)、下一行(next)获取指向像素数据的指针。ptr<uchar>方法用于获取特定行的首地址,uchar表示无符号字符类型,通常用于表示单个图像像素的一个通道值。
uchar*output = output_image.ptr<uchar>(row);
//获取输出图像中对应行的指针,以便直接修改输出图像的像素值。
for (int col = offsetx; col < cols; col++) {
//从每行的第二个像素开始遍历到倒数第二个像素,避免了边界效应,因为算法中涉及到对当前像素周围像素的访问。
output[col] = saturate_cast<uchar>(5 * current[col] - (current[col - offsetx] + current[col + offsetx] + previous[col] + next[col]));
//核心处理逻辑(带入公式),对每个像素应用一个算子来增强对比度。公式实质上是当前像素值乘以5,然后减去其上下左右相邻像素值之和。这样做可以增强边缘和对比度,因为中心像素与其邻域的差异被放大了。saturate_cast<uchar>() 是一个安全类型转换,确保计算结果在uchar类型(0 - 255)的范围内,防止溢出
}
}
namedWindow("contrastImage", WINDOW_NORMAL);
imshow("contrastImage", output_image);
waitKey(0);
system("path");
getchar();
return 0;
}
如果是24位图像,那么每个像素点取值是0到2^24。
RGB是基本三原色,如果用8位代表一种颜色,那么每种颜色最大是255,这样每个像素点的颜色值就是(0-255,0-255,0-255)。这样图像通道是3。
RGB存储模型:
因此在计算时需要*通道数
视频讲解链接:https://www.bilibili.com/video/av29600072/?p=3&vd_source=a44194be6b6be2dd036a04576e8d1559
3 Mat对象
3.1 Mat对象与IplImage对象
cv::Mat 是OpenCV后来引入的一个更加现代化的图像容器,旨在替代IplImage,提供了更高效的内存管理和更面向对象的接口。
IplImage
IplImage是从2001年OpenCV发布之后就一直存在,是C语言风格的数据结构,需要开发者自己分配与管理内存,对大的程序使用它容易导致内存泄漏问题(现在一般不用)
Mat
- 内存管理: Mat采用了引用计数机制,自动管理内存,减少了内存泄漏的风险。当不再有引用指向一个Mat对象时,其内存会被自动释放。
- 灵活性:Mat设计得更加灵活,不仅能够处理图像数据,还能处理任意维度的数组,非常适合进行多维数组的数学运算。
- 高效性:Mat在内部使用了连续内存分配策略,对于连续内存块的访问更加高效,有利于CPU缓存利用,提升了处理速度。
- 易用性:提供了丰富的成员函数和操作符重载,使得图像处理和数组操作更为便捷和直观,支持更多的C++特性。
3.2 Mat 对象使用与常用方法
参考链接:https://blog.youkuaiyun.com/danwuxie/article/details/81395650
https://blog.youkuaiyun.com/wangyuankl123/article/details/104732460
3.2.1 Mat 对象构造函数
- Mat()
含义: 默认构造函数,创建一个空的Mat对象。它不分配内存,可用于之后的赋值或初始化。 - Mat(int rows, int cols,int type)
含义:创建一个具有指定行数、列数和数据类型的矩阵。type参数是一个包含数据类型和通道数信息的编码值,如CV_8UC3表示8位无符号整型,3通道(通常用于RGB图像)。 - Mat(Size size, int type)
含义: 类似于上一个构造函数,但是使用Size对象来同时指定宽度和高度。Size(size.width,size.height)等效于(cols, rows)。 - Mat(int rows, int cols, int type, const Scalar &s)
含义: 创建一个指定大小、类型且所有元素初始化为给定标量值s的矩阵。这常用于创建具有特定初始值的图像或矩阵。
例:Mat M(2,2,CV_8UC3, Scalar(0,0,255)) - Mat(Size size,int type, const Scalar &s)
含义: 与上一个相似,但使用Size来指定尺寸,所有元素初始化为标量s。 - *Mat(int ndims, const int sizes, int type)
含义:创建一个具有多维度(ndims大于2)的数组或矩阵,其中sizes是一个指针,指向一个包含每个维度大小的整数数组。这对于创建多维数组或特殊结构的图像非常有用。 - *Mat(int ndims, const int sizes, int type, const Scalar &s)
含义: 类似于上一个,但是多维数组的所有元素也初始化为标量值s。
例:
cv::Mat::create
int sz[3] = {2,2,2};
Mat L(3,sz, CV_8UC1, Scalar::all(0));
参考链接:https://blog.youkuaiyun.com/wangyuankl123/article/details/104732460
#include <opencv2/core/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main(int argc, char** args) {
//声明并初始化Mat对象
Mat M;
M.create(4, 3, CV_8UC2);
//矩阵元素赋值
M = Scalar(127, 127);
//打印矩阵
cout << "M = " << endl << " " << M << endl << endl;
//访问矩阵的第一行数据
uchar* firstRow = M.ptr<uchar>(0);
printf("%d\n", *firstRow);
//使用ptr<uchar>(0)获取矩阵M第一行的指针,并通过解引用*firstRow打印出第一行第一个元素的值。
//初始化并打印另一个类型的Mat对象
Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
//这里使用了OpenCV的内联矩阵初始化语法,创建了一个3x3的双精度浮点数矩阵C,并直接在括号内赋予初值
waitKey(0);
return 0;
}
3.2.2 Mat 对象常用方法
3.2.2.1 Scalar()
Scalar() 构造函数是创建一个默认初始化(通常是全0)的多通道数值对象的方法
#include <opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("D:\\vscodestore\\zxc2.png",IMREAD_COLOR);
Mat dst;
namedWindow("Window Title", WINDOW_AUTOSIZE);
imshow("Window Title", src);
dst = Mat(src.size(), src.type());
dst = Scalar(0, 0, 0);
//代表了一个具有三个通道的标量对象,每个通道的值分别被初始化为0。
namedWindow("Window dst", WINDOW_AUTOSIZE);
imshow("Window dst", dst);
waitKey(0);
return 0;
}
生成了一幅尺寸和类型与原图像相同的每个元素都是 (0, 0, 0)黑色的图像
3.2.2.2 clone()
用于创建原始矩阵的一个深拷贝。这意味着 clone() 会创建一个新的 cv::Mat 对象,这个新对象拥有与原矩阵相同的数据内容,但是位于内存中的不同位置。对克隆后的新矩阵所做的任何修改都不会影响到原始矩阵。
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("D:\\vscodestore\\zxc2.png",IMREAD_COLOR);
Mat dst;
namedWindow("WindowTitle", WINDOW_AUTOSIZE);
imshow("WindowTitle", src);
dst = src.clone();
namedWindow("WindowDst", WINDOW_AUTOSIZE);
imshow("WindowDst", dst);
waitKey(0);
return 0;
}
这里修改一个像素点的数值,以(0,0)(0,1)(1,0)为例。(代码里只写了一个像素点,另两个同理)像素点太小,其他地方找不到,会看到复制后的左上角变红了,仔细瞅😎
```cpp
#include <opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src = imread("D:\\vscodestore\\zxc2.png",IMREAD_COLOR);
Mat dst = src.clone();
// 修改克隆图像的一个像素值,例如将(0, 0)位置的像素设为红色(假设是BGR格式)
if (dst.channels() == 3) {
dst.at<Vec3b>(0, 0)[0] = 0;
dst.at<Vec3b>(0, 0)[1] = 0;
dst.at<Vec3b>(0, 0)[2] = 255;
}
else {
cout << "Image is not in BGR format." << endl;
}
namedWindow("WindowTitle", WINDOW_AUTOSIZE);
imshow("WindowTitle", src);
namedWindow("WindowDst", WINDOW_AUTOSIZE);
imshow("WindowDst", dst);
waitKey(0);
destroyAllWindows();
return 0;
}
3.2.2.3 void copyTo(Mat mat)
用于将当前矩阵的内容复制到另一个矩阵中。这个方法不仅复制矩阵的数据,还会考虑掩码(mask),如果有提供的话,只复制那些掩码中标记为非零的元素。如果没有提供掩码,它就相当于一个普通的深拷贝操作。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main() {
// 创建一个简单的图像(例如,一个蓝色背景上的红色矩形)
Mat src(200, 300, CV_8UC3, Scalar(255, 0, 0)); // 蓝色背景
rectangle(src, Rect(50, 50, 100, 100), Scalar(0, 0, 255), -1); // 红色矩形
// 创建一个全零的掩码图像,与src尺寸相同
Mat mask(src.size(), CV_8U, Scalar(0));
// 仅在图像中心区域设置掩码为255,表示这部分区域是我们感兴趣的
rectangle(mask, Rect(75, 75, 50, 50), Scalar(255), -1);
// 创建一个目标图像用于复制
Mat dst1, dst2;
// 不使用掩码的复制
src.copyTo(dst1);
// 使用掩码的复制
src.copyTo(dst2, mask);
// 显示图像
imshow("Source Image", src);
imshow("Copy Without Mask", dst1);
imshow("Copy With Mask", dst2);
// 等待按键后关闭窗口
waitKey(0);
destroyAllWindows();
return 0;
}
3.2.2.4 cvtColor()
cvCvtColor是Opencv里的颜色空间转换函数,可以实现RGB颜色向HSV,HSI等颜色空间的转换
可以看上面1.3保存图像代码展示效果
3.2.2.5 channels()
用于查看图像的通道数。
可以参考2掩膜里面的用法
3.2.3 Mat对象使用
部分复制:创建一个新的Mat对象B,但它只是共享了A的内存数据,即头信息(包括尺寸、类型等)和数据指针被复制了,但底层的实际图像数据并未被复制。这意味着,修改B的数据会影响到A
Mat A= imread(image);
Mat B(A) // 只复制
完全复制:创建一个与原Mat对象完全独立的副本,包括其数据部分
Mat F = A.clone();
//或
Mat G; A.copyTo(G);
4 图像操作
4.1 读写图像
imread()
cv::imread(const String& filename, int flags = IMREAD_COLOR) 函数用于从文件中读取图像数据到内存中。可以参考1.1.1中内容
例:
Mat img = imread("input.jpg", IMREAD_COLOR);
imwrite()
cv::imwrite(const String& filename, InputArray img, const std::vector& params = std::vector()) 函数用于将图像数据写入文件。
例:
bool success = imwrite("output_high_quality.jpg", image, params);
#include <opencv2/opencv.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
int main() {
Mat image = imread("input.jpg"); // 读取图像
if(image.empty()) {
cout << "Error loading image" << endl;
return -1;
}
// 设置JPEG质量参数,范围通常为0-100,其中100表示最高质量
vector<int> params;
params.push_back(IMWRITE_JPEG_QUALITY);
params.push_back(95); // 以较高的质量保存图像
bool success = imwrite("output_high_quality.jpg", image, params); // 使用指定参数保存图像
if(success) {
cout << "Image saved with high quality!" << endl;
} else {
cout << "Failed to save image with specified parameters" << endl;
}
return 0;
}
vector params;:这行代码声明并创建了一个空的整数向量(vector),用于存储整数值。在这个上下文中,它将用于存储图像写入参数。
params.push_back(IMWRITE_JPEG_QUALITY);:push_back是向vector末尾添加元素的方法。这里添加的元素IMWRITE_JPEG_QUALITY是一个常量,用于指示接下来要设置的参数是关于JPEG图像的质量控制。
params.push_back(95);:紧接着,再次使用push_back方法向params中添加了一个具体的值95,这个值代表了要保存的JPEG图像的质量。在OpenCV中,JPEG质量的取值范围通常是0到100,其中100表示最高质量。
4.2 单通道图片像素反差操作
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
int main() {
Mat src = imread("D:\\vscodestore\\cartoon1.png");
CV_Assert(src.depth() == CV_8U);
//CV_Assert: 是OpenCV库提供的一个宏,用于在运行时检查条件。如果条件为假(false),则程序会抛出一个错误并停止执行。
if (src.empty()) {
printf("could not find the image!\n");
return -1;
}
imshow("ImputImage", src);
//显示原图
//单通道图像操作
Mat grayImg;
Mat gray2;
cvtColor(src, grayImg, COLOR_BGR2GRAY);
gray2 = Mat(grayImg.size(), grayImg.type());
int height = grayImg.rows;
int width = grayImg.cols;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int gray = grayImg.at<uchar>(row, col);
gray2.at<uchar>(row, col) = 255 - gray;
//通过双层循环遍历grayImg的每个像素,将每个像素值取反(即255减去当前像素值),并将结果存储到gray2中。
}
}
namedWindow("grayImage", WINDOW_AUTOSIZE);
imshow("grayImage", grayImg);
//显示灰度图像
namedWindow("grayImage2", WINDOW_AUTOSIZE);
imshow("grayImage2", gray2);
//显示反转后灰度图像
waitKey(0);
system("path");
getchar();
return 0;
}
4.3 多通道图片像素反差操作
如果是单通道图像(灰度图),则直接取像素值进行反转;如果是三通道图像(如RGB),则分别对每个颜色通道(BGR顺序)进行值的反转。
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
int main() {
Mat src = imread("D:\\vscodestore\\cartoon1.png");
CV_Assert(src.depth() == CV_8U);
//CV_Assert: 是OpenCV库提供的一个宏,用于在运行时检查条件。如果条件为假(false),则程序会抛出一个错误并停止执行。
if (src.empty()) {
printf("could not find the image!\n");
return -1;
}
imshow("ImputImage", src);//显示默认窗口
Mat dst;
dst.create(src.size(), src.type());
int height = src.rows;
int width = src.cols;
int nc = src.channels();
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
if (nc == 1) {
int gray = src.at<uchar>(row, col);
dst.at<uchar>(row, col) = 255 - gray;
}
else {
int b = src.at<Vec3b>(row, col)[0];
int g = src.at<Vec3b>(row, col)[1];
int r = src.at<Vec3b>(row, col)[2];
dst.at<Vec3b>(row, col)[0] = 255 - b;
dst.at<Vec3b>(row, col)[1] = 255 - g;
dst.at<Vec3b>(row, col)[2] = 255 - r;
}
}
}
namedWindow("grayImage", WINDOW_AUTOSIZE);
imshow("grayImage", dst);
waitKey(0);
system("path");
getchar();
return 0;
}
4.4 修改像素值
灰度图像
img.at(y, x) = 128;
RGB三通道图像
img.at(y,x)[0]=128; // blue
img.at(y,x)[1]=128; // green
img.at(y,x)[2]=128; // red
空白图像赋值
img = Scalar(0);
ROI选择
指在图像处理、计算机视觉或机器学习等领域中,从一个较大的图像或数据集中选出一个感兴趣的特定区域进行分析或处理的过程
Rect r(10, 10, 100, 100);
Mat smallImg = img(r);
定义了一个矩形区域(Rect)对象r,它的构造函数接受四个参数,分别是矩形左上角的x坐标、y坐标,以及矩形的宽度和高度。矩形的左上角位于(10, 10),宽度为100,高度也为100。
Mat是OpenCV中用于存储图像的数据结构。这条语句表示从原始图像img中根据之前定义的矩形区域r提取出一个子图像,并将其赋值给新的Mat对象smallImg。
Vec3b与Vec3F
Vec3b:代表一个包含三个8位无符号整数(uchar)的向量,通常用于表示BGR(蓝色、绿色、红色)通道的颜色信息
Vec3f:代表一个包含三个单精度浮点数(float)的向量,用于更精确的颜色表示或在需要更高精度的图像处理算法中。
把CV_8U转换到CV_32F实现如下:
Mat src_gray; // 假设src_gray是一个CV_8UC1类型的图像
Mat dst_float;
src_gray.convertTo(dst_float, CV_32F, 1.0 / 255.0); // 转换到CV_32FC1,并进行归一化到[0,1]区间
5 图像混合
5.1 理论-线性混合操作
g(x)=(1-α)f0(x)+αf1(x)
- g(x):表示在 𝑥 处结合了 𝑓0(𝑥) 和 𝑓1(𝑥)特性后得到的新函数值。
- 𝑓0(𝑥):第一个基础函数,在 𝑥点的值。
- 𝑓1(𝑥):第二个基础函数,在 𝑥点的值。
当 0<𝛼<1 时,𝑔(𝑥)是 𝑓0(𝑥)和 𝑓1(𝑥)的加权平均,𝛼 决定了两者之间的相对重要性。𝛼接近0时更偏向 𝑓0(𝑥),接近1时更偏向 𝑓1(𝑥)。
公式提供了一种平滑地从一个函数过渡到另一个函数的方法。
5.2 相关API(addWeighted)
加权公式:dst=src1∗α+src2∗β+γ。
void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype=-1)
- src1: 第一个输入图像或矩阵,类型为cv::Mat或相似的容器。
- alpha: 第一个图像的权重,一个浮点数,通常介于0和1之间。
- src2: 第二个输入图像或矩阵,与src1有相同的尺寸和类型。
- beta:第二个图像的权重,一个浮点数,通常介于0和1之间,且alpha + beta不应超过1(除非使用了gamma进行偏置调整)。
- gamma:一个标量值,会被加到两个图像乘以权重求和的结果上,可以用作亮度调整。
- dst: 输出图像或矩阵,存储结果。
- dtype: 输出图像的数据类型,如果设置为默认值-1,则输出图像的数据类型与输入图像一致。
注:两张图像的大小和类型必须一致
这里用之前使用的原图和保存的灰度图像混合
#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
int main()
{
Mat src1 = imread("D:\\vscodestore\\cartoon1.png"); //读取图片(使用图片的绝对路径)
Mat src2 = imread("D:\\vscodestore\\cartoon2.png");
Mat dst;
dst.create(src1.size(), src1.type());
if (src1.empty() || src2.empty()) {
printf("could not find the image!\n");
return -1;
}
double alpha = 0.5;
if (src1.rows == src2.rows&&src1.cols == src1.cols&&src1.type() == src2.type()) {
addWeighted(src1, alpha, src2, (1.0 - alpha), 0.0, dst);
imshow("ImputImage", src1);//显示默认窗口
namedWindow("gray2", WINDOW_AUTOSIZE);
imshow("gray2", src2);
namedWindow("gray2m", WINDOW_AUTOSIZE);
imshow("gray2m", dst);
}
else {
printf("could not do it!\n");
return -1;
}
waitKey(0);//不加此语句图片会一闪而过
system("path");
getchar();
return 0;
}
6 调整图像亮度与对比度
6.1 理论-图像变换
图像变换可以看作如下:
- 像素变换 – 点操作
- 邻域操作 – 区域
调整图像亮度和对比度属于像素变换-点操作
公式:g(i,j)=α*f(i,j)+β
其中α>0,β是增益变量
6.2 重要的API
Mat new_image = Mat::zeros( image.size(), image.type() ); 创建一张跟原图像大小和类型一致的空白图像、像素值初始化为0
saturate_cast(value)确保值大小范围为0~255之间
Mat.at(y,x)[index]=value 给每个像素点每个通道赋值
#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
int main()
{
Mat src1 = imread("D:\\vscodestore\\cartoon1.png"); //读取图片(使用图片的绝对路径)
Mat dst;
dst.create(src1.size(), src1.type());
if (src1.empty()) {
printf("could not find the image!\n");
return -1;
}
char input_win[] = "input image";
namedWindow(input_win, WINDOW_AUTOSIZE);
imshow(input_win, src1);
int height = src1.rows;
int width = src1.cols;
int nc = src1.channels();
dst = Mat::zeros(src1.size(), src1.type());
float alpha = 1.2;
float beta = 30;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
if (nc == 1) {
float t = src1.at<uchar>(row, col);
dst.at<uchar>(row, col) = saturate_cast<uchar>(t*alpha + beta);
}
else {
float b = src1.at<Vec3b>(row, col)[0];
float g = src1.at<Vec3b>(row, col)[1];
float r = src1.at<Vec3b>(row, col)[2];
dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b*alpha + beta);
dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g*alpha + beta);
dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r*alpha + beta);
}
}
}
namedWindow("output", WINDOW_AUTOSIZE);
imshow("output", dst);
waitKey(0);//不加此语句图片会一闪而过
system("path");
getchar();
return 0;
}