Opencv图像操作——图像遍历、图像ROI选取、图像混合

前言

Opencv图像操作与混合,用opencv实现对图像的修改

一、图像是什么?

在这一篇文章中,我们对图像已经有了一定的了解

https://blog.csdn.net/ivan_9/article/details/113059961

二、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区域,图像的混合操作,以及其他小知识点!

如有错误,敬请指教!

猜你喜欢

转载自blog.csdn.net/ivan_9/article/details/113177480