文章目录
- 前言
- 一、图像是什么?
- 二、OpenCV中数据类型和常用数据类型对应
- 三、图像的操作
- 1)imread函数:利用imread函数读取图像
- 3.1.1、利用imread实现读取的代码块:
- 3.1.2、imread函数在opencv里的定义:
- 2)imwrite函数:利用imwrite函数写入图像,即保存图像
- 3.2.1、利用imwrite实现写入的代码块:
- 3.2.2、imwrite函数在opencv里的定义:
- 3)遍历图像
- 3.3.1、利用at函数实现单通道图像遍历,对图像像素点进行操作:
- 3.3.2、利用at实现多通道图像遍历:
- 3.3.3、利用at函数以及随机函数生成一张图片:
- 3.3.4、ptr指针遍历图像:
- 3.3.5、iterator迭代器遍历图像:
- 3.3.6、iterator迭代器遍历图像:
- 4)ROI区域
- 3.4.1、ROI区域的定义:
- 3.4.2、规则的ROI区域选取:
- (A) 使用Rect函数,截取ROI区域:
- (B) 使用Range函数,截取ROI区域:
- 3.4.3、不规则的ROI区域选取:
- 5)图像的混合操作:
- 3.5.1、图像的二进制运算操作:
- 3.5.2、图像的线性混合操作:
- 四、其他图像操作函数:
- 1)waitKey()函数:
- 2)cvtColor()函数:
- 3)Filp()函数:
- 总结
前言
Opencv图像操作与混合,用opencv实现对图像的修改
一、图像是什么?
在这一篇文章中,我们对图像已经有了一定的了解
二、OpenCV中数据类型和常用数据类型对应
数据类型 | opencv数据类型对应 |
Mat<uchar> | CV_8U |
Mat<char> | CV_8S |
Mat<ushort> | CV_16U |
Mar<short> | CV_16S |
Mat<int> | CV_32S |
Mat<float> | CV_32F |
Mat<double> | CV_16F |
三、图像的操作
1)imread函数:利用imread函数读取图像
imread是image read的缩写:即负责图像读取
3.1.1、利用imread实现读取的代码块:
Mat src, gray_src;
src = imread("C:\\Users\\ASUS\\Desktop\\1.jpg");
3.1.2、imread函数在opencv里的定义:
CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR );
/** @brief Loads a multi-page image from a file.
The function imreadmulti loads a multi-page image from the specified file into a vector of Mat objects.
@param filename Name of file to be loaded.
@param flags Flag that can take values of cv::ImreadModes, default with cv::IMREAD_ANYCOLOR.
@param mats A vector of Mat objects holding each page, if more than one.
@sa cv::imread*/
2)imwrite函数:利用imwrite函数写入图像,即保存图像
imwrite是image write的缩写:即负责图像写入
3.2.1、利用imwrite实现写入的代码块:
imwrite("C:\\Users\\ASUS\\Desktop\\imwrite.jpg", src);
3.2.2、imwrite函数在opencv里的定义:
CV_EXPORTS_W bool imwrite( const String& filename, InputArray img,
const std::vector<int>& params = std::vector<int>());
/// @overload multi-image overload for bindings
//filename:需要写入的文件名,会自己创建(像imwrite("imwrite.jpg",src);支持JPEG,PNG,PPM,PGM,PBM,TIFF等格式
//img:要保存的图像
//params:表示为特定格式保存的参数编码
3)遍历图像
3.3.1、利用at函数实现单通道图像遍历,对图像像素点进行操作:
代码块
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main() {
Mat src, gray_src;
src = imread("C:\\Users\\ASUS\\Desktop\\1.jpg");
if (!src.data) {
cout << "could not load your image!";
return 0;
}
namedWindow("input_image", WINDOW_AUTOSIZE);
imshow("input_image", src);
namedWindow("output_image", WINDOW_AUTOSIZE);
cvtColor(src, gray_src, COLOR_BGR2GRAY); //利用色彩空间转换函数,转化为灰度图像,灰度图像只有单通道
int height = gray_src.rows;
int width = gray_src.cols;
//单通道
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int gray = gray_src.at<uchar>(row, col);
gray_src.at<uchar>(row, col) = 255 - gray;
}
}
imshow("gray_invert", gray_src);
waitKey(0);
return 0;
}
利用at函数,单通道遍历图像后的运行结果:
3.3.2、利用at实现多通道图像遍历:
代码块:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main() {
Mat src, dst;
src = imread("C:\\Users\\ASUS\\Desktop\\1.jpg");
if (!src.data) {
cout << "could not load your image!";
return 0;
}
namedWindow("input_image", WINDOW_AUTOSIZE);
imshow("input_image", src);
namedWindow("output_image", WINDOW_AUTOSIZE);
int height = src.rows;
int width = src.cols;
dst.create(src.size(), src.type());
height = src.rows;
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 One = src.at<uchar>(row, col);
src.at<uchar>(row, col) = 255 - One;
}
else if (nc == 3) { //多通道
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;
}//Vec3b放着bgr的像素,每个像素的读法采用Vec3b;Vec3f是浮点型的像素
}
}
imshow("output_image", dst);
waitKey(0);
return 0;
}
利用at函数,多通道遍历图像后的运行结果:
3.3.3、利用at函数以及随机函数生成一张图片:
代码块:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat src;
src = Mat(666, 666, CV_8UC3);
int height = src.rows;
int weight = src.cols;
for (int row = 0; row < height; row++){
for (int col = 0; col < weight; col++) {
src.at<Vec3b>(row, col)[0] = rand() * 255 + 1;//让随机数处于0-255之间,也可以rand() % 255
src.at<Vec3b>(row, col)[1] = rand() * 255 + 1;
src.at<Vec3b>(row, col)[2] = rand() * 255 + 1;
}
}
namedWindow("my_random_image", WINDOW_AUTOSIZE);
imshow("my_random_image", src);
waitKey(6000); //图像停滞6秒后消失;
return 0;
}
运行结果:
3.3.4、ptr指针遍历图像:
代码块:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat src;
src = imread("C:\\Users\\ASUS\\Desktop\\2.jpg");
if (!src.data) {
cout << "could not load image..." << endl;
return 0;
}
namedWindow("init_image", WINDOW_AUTOSIZE);
imshow("init_image", src);
int height = src.rows;
int weight = src.cols;
int chan = src.channels();
for (int row = 0; row < height; row++) {
uchar* row_Pointer = src.ptr<uchar>(row);
for (int col = 0; col < weight * chan; col++) {
//图像是三通道的,所以要*3
row_Pointer[col] = saturate_cast<uchar>(row_Pointer[col] + 66);
//在原有图像的基础上,每个像素点加66
}
}
namedWindow("changed_image", WINDOW_AUTOSIZE);
imshow("changed_image", src);
waitKey(6666); //图像停滞6.666秒后消失;
return 0;
}
运行结果:
3.3.5、iterator迭代器遍历图像:
代码块:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat src;
src = imread("C:\\Users\\ASUS\\Desktop\\2.jpg");
if (!src.data) {
cout << "could not load image..." << endl;
return 0;
}
namedWindow("init_image", WINDOW_AUTOSIZE);
imshow("init_image", src);
for (Mat_<Vec3b>::iterator iter = src.begin<Vec3b>(); iter != src.end<Vec3b>(); iter++) {
//利用迭代器遍历图像,对三通道依次进行修改
(*iter)[0] = saturate_cast<uchar>((*iter)[0] + 1);
(*iter)[1] = saturate_cast<uchar>((*iter)[1] + 111);
(*iter)[2] = saturate_cast<uchar>((*iter)[2] + 1);
//使用saturate_cast<uchar>()函数,防止溢出
}
namedWindow("changed_image", WINDOW_AUTOSIZE);
imshow("changed_image", src);
waitKey(6666); //图像停滞6.666秒后消失;
return 0;
}
运行结果:
3.3.6、iterator迭代器遍历图像:
4)ROI区域
3.4.1、ROI区域的定义:
- ROI全称region of interest. 在图像处理领域,感兴趣区域(ROI) 是从图像中选择一块图像区域,该区域是图像分析所关注的重点。
- 圈定该区域以便进行进一步处理。
- 使用ROI圈定你想读的目标,可以减少处理时间,增加精度。
3.4.2、规则的ROI区域选取:
(A) 使用Rect函数,截取ROI区域:
程序代码:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat src;
src = imread("C:\\Users\\ASUS\\Desktop\\2.jpg");
if (src.empty()) {
cout << "could not load your image...";
return 0;
}
namedWindow("init_image", WINDOW_AUTOSIZE);
imshow("init_image", src);
Mat dst = src(Rect(150, 50, 150, 150));
//Rect 四个参数分别为x,y,截取多长的x,截取多长的y
namedWindow("rect_image", WINDOW_AUTOSIZE);
imshow("rect_image", dst);
waitKey(0);
return 0;
}
运行结果:
(B) 使用Range函数,截取ROI区域:
解析:
- Mat(Range(定位x,宽度), Range(定义y,长度));
- 用Range::all()静态方法来获取所有的行或列
程序代码:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat src;
src = imread("C:\\Users\\ASUS\\Desktop\\1.jpg");
if (src.empty()) {
cout << "could not load your image...";
return 0;
}
namedWindow("init_image", WINDOW_AUTOSIZE);
imshow("init_image", src);
Mat dst = src(Range(150, 350), Range(100,400));
/*Mat::operator()( Range _rowRange, Range _colRange ) const{
return Mat(*this, _rowRange, _colRange);}
利用Mat 的方法提取ROI区域
*/
namedWindow("rect_image", WINDOW_AUTOSIZE);
imshow("rect_image", dst);
waitKey(0);
return 0;
}
运行结果:
3.4.3、不规则的ROI区域选取:
程序代码:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat src, dst, mask;
src = imread("C:\\Users\\ASUS\\Desktop\\3.png");
if (src.empty()) {
cout << "could not load your image...";
return 0;
}
//原图,为了简单,自己手绘了一个色彩分明的图像
namedWindow("init_image", WINDOW_AUTOSIZE);
imshow("init_image", src);
//生成mask区域
inRange(src, Scalar(5, 30, 0), Scalar(100, 255, 100), mask);
//inRange()无返回值,因为是3通道的,中间参数采用向量
namedWindow("mask_image", WINDOW_AUTOSIZE);
imshow("mask_image", mask);
//生成ROI区域
bitwise_and(src, src, dst, mask);
//用混合方式,将mask以及原图混合,得出ROI不规则区域
namedWindow("output_image", WINDOW_AUTOSIZE);
imshow("output_image", dst);
//简而言之,就是2张图像的结合,得出结果。
waitKey(0);
return 0;
}
运行结果:
5)图像的混合操作:
3.5.1、图像的二进制运算操作:
说到二进制运算,我们总能想到&,|,!,^;
在opencv图像处理中,二进制操作又是哪些呢?
二进制函数 | 解析 |
bitwise_and | void bitwise_and(InputArray src1, InputArray src2,OutputArray dst, InputArray mask=noArray()); 对图像每个像素值进行二进制“与”操作;简而言之,计算两个数组或一个数组和一个标量的逐元素逐位析取(或真或假,但不能既真又假)。 |
bitwise_or | void bitwise_or(InputArray src1, InputArray src2,OutputArray dst, InputArray mask = noArray()); 对图像每个像素值进行二进制“或”操作;简而言之,对两个元素计算按位“异或”运算数组或数组和标量。 |
bitwise_not | void bitwise_not(InputArray src, OutputArray dst,InputArray mask = noArray()); 对图像每个像素值进行二进制“取反”操作;简而言之,计算两个数组或数组与标量之间的每元素绝对差 |
bitwise_xor | void bitwise_xor(InputArray src1, InputArray src2,OutputArray dst, InputArray mask = noArray()); 对图像每个像素值进行二进制“异或”操作;简而言之,反转数组的每一位 |
bitwise_or简单操作:
程序代码:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat src, dst, add_src_dst;
src = imread("C:\\Users\\ASUS\\Desktop\\1.jpg");
dst = imread("C:\\Users\\ASUS\\Desktop\\3.jpg");
if (src.empty() || dst.empty()) {
cout << "could not load your image...";
return 0;
}
namedWindow("init_image1", WINDOW_AUTOSIZE);
imshow("init_image1", src);
namedWindow("init_image2", WINDOW_AUTOSIZE);
imshow("init_image2", dst);
//进行或操作
bitwise_or(src, dst, add_src_dst);
//也可以采用与等,看自己需要
namedWindow("add_src_dst", WINDOW_AUTOSIZE);
imshow("add_src_dst", add_src_dst);
waitKey(0);
return 0;
}
运行结果:
3.5.2、图像的线性混合操作:
前提:两张图像的大小(size)、类型(type)必须一模一样。
a, 即alpha(alpha channels)的取值在[0,1]
程序代码:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main() {
Mat src, dst, add_src_dst;
src = imread("C:\\Users\\ASUS\\Desktop\\1.jpg");
dst = imread("C:\\Users\\ASUS\\Desktop\\3.jpg");
if (src.empty() || dst.empty()) {
cout << "could not load your image...";
return 0;
}
namedWindow("init_image1", WINDOW_AUTOSIZE);
imshow("init_image1", src);
namedWindow("init_image2", WINDOW_AUTOSIZE);
imshow("init_image2", dst);
if (src.type() == dst.type() && src.rows == dst.rows && src.cols == dst.cols) {
double alpha = 0.45;
addWeighted(src, alpha, dst, (1.0 - alpha), 0.0, add_src_dst);
//这说明第一张图权重占比45%,第二张图权重占比55%
//add(src, dst, add_src_dst, Mat());
//两个元素级的加运算
//multiply(src, dst, add_src_dst, 1.0);
//两张图相乘
namedWindow("add_src_dst", WINDOW_AUTOSIZE);
imshow("add_src_dst", add_src_dst);
}
else {
cout << "could not blend image, the size of images is not same...\n";
return 0;
}
waitKey(0);
return 0;
}
运行结果:
四、其他图像操作函数:
1)waitKey()函数:
调用 waitKey() 会进入一个消息循环,来等待运行窗口上的按键动作命令。
2)cvtColor()函数:
将图像从一个颜色空间转换为另一个颜色空间,其中源图像存储在两个平面中。
3)Filp()函数:
选定轴翻转,0为x轴,即垂直翻转;1为y轴,即水平翻转;
写法:flip(src, dst, 0);
还有很多函数,等你来使用,这里不一一赘述了!
总结
本文讲述了图像的遍历,图像怎么选取ROI区域,图像的混合操作,以及其他小知识点!
如有错误,敬请指教!