2021-07-10暑期专题培训(opencv1-6)

本文详细介绍了OpenCV中图像处理的基础知识,包括载入与保存图片、旋转、遍历像素、高效处理像素的方法,如灰度图、彩色图的处理,以及存取像素值。讲解了迭代器、指针遍历、高效遍历连续图像等技巧,并探讨了图像的加减乘除运算、颜色空间转换、直方图统计和形态学运算等图像变换。此外,还讨论了设计模式在图像处理中的应用,如策略模式、控制器通信、单件模式和MVC架构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

opencv 学习

第一章:

  1. 载入图片,保存,旋转。

第二章:

2.1目标:
一,学会如何遍历一张图像并处理其像素
二,高效的处理方法
灰度图:像素由8位无符号数来表示;
彩色图:由三个这样的8位无符号数来表示三个颜色通道,0黑色;255白色;
opencv允许创建不同像素类型的矩阵或图像:如整形(CV_8U),浮点型(CV_32F)。
它们在一些图像处理过程中,用来保存中间值这样的内容很有用。大多数矩阵可以用于任意类型的矩阵,但有些运算对数据类型或矩阵的通道数有要求。

2.2.存取像素值:高效的遍历数组。椒盐噪点,即随机设置为黑白,黑白1通道,彩色3通道。Mat类,at方法。
需要在代码中指定元素所在的行(row)和列(col),函数会返回相应的元素。单通道返回单个数值;多通道返回一组向量(Vector)。

//2021.6.27 图像椒盐,加噪点。
#include<opencv2/opencv.hpp>
#include<iostream>

using namespace cv;
void salt(Mat& image, int n) {
   
   
//第一个参数是一张输入图像,使用传引用的参数传递方式(值,指针,传引用三种方式);第二个参数是要该改变的噪点个数
	for (int k = 0; k < n; k++)
	{
   
   
		int i = rand() % image.cols;//随机获取行和列        //col,row;是了类cv::Mat的公有成员变量,能给出图像的宽高;
		int j = rand() % image.rows;
		if (image.channels() == 1) {
   
   //灰度为1
			image.at<uchar>(j, i) = 255;//255白色,0为黑色   //成员函数at(int y,int x)用来存取图像的元素;但是必须要知道图像的数据类型。cv::Mat可以存放任意类型的元素。
		}
		else if (image.channels() == 3)//彩色为3
		{
   
   
			image.at<Vec3b>(j, i)[0] = 255;               //分清类型
			image.at<Vec3b>(j, i)[1] = 255;               // 确保指定的数据类型和矩阵的数据类型
			image.at<Vec3b>(j, i)[2] = 255;               //相符,at方法本身不会经行数据类型转换
		}
	}
}

int main() {
   
   
	Mat src;
	src = imread("D:/opencvcode/img/13.png");
	if (!src.data) {
   
   
		printf("not img!");
		return -1;
	}
	salt(src, 3000);
	namedWindow("inputimg");
	imshow("inputimg", src);
	waitKey(0);
	return 0;
}

拓展阅读:cv::Mat_是cv::Mat的一个模板子类。

2.3 用指针遍历:rows,cows。
将要遍历图像的所有像素时,像素个数很多,所以高效的遍历很重要。以下两种方法,第一种是指针算术;
每个像素的每个通道,将其值除以N,整除。再乘以N。

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

using namespace cv;
//颜色缩减的其中一种方法,(函数)
void colorreduce(Mat& image, int div = 64) {
   
   
	int n1 = image.rows;
	int nc = image.cols * image.channels();        //获取每行像素值
	for (int j = 0; j < n1; j++) {
   
   
		uchar* data = image.ptr<uchar>(j);//ptr函数得到图像任意首地址,ptr返回第J行的首地址
		for (int i = 0; i < nc; i++) {
   
   
			data[i] = data[i] / div * div + div / 2;//使用指针从一列移到下一列(颜色缩减函数公式1)
		//*data++ = *data / div * div + div/2;//颜色缩减函数公式2
		//data[i] = data[i] - data[i] % div + div / 2; //颜色缩减函数公式3,这个方法比较慢

		////用位运算(非常高效),限制缩减因子为2^n,(运用掩模???)
		//uchar mask = OxFF << n;//e.g.for div=16,mask=OxFo;
		//data[i] = (data[i] & mask) + div / 2;

		}
	}
}

////result.creatr(image.rows, image.cows, image.tye());    //创建一个与输入图像的尺寸和类型相同的矩阵,create 函数创建的内存是连续的,不会对图像进行填补。内存大小为total()*elemSize();

//	for (int j = 0; j < n1; j++) {
   
   
//		const uchar* data_in = image.ptr<uchar>(j);
//		uchar* data_out = result.ptr<uchar>(j);
//		for (int i = 0; i < nc; i++) {
   
   
//			data_out[i] = data_in[i] / div * div + div / 2;
//		}
//	}

int main() {
   
   
	Mat img;
	img = imread("D:/opencvcode/img/13.png");

	colorreduce(img);//调用颜色缩减函数
	namedWindow("input");
	imshow("input", img);
                                          // 额外的复制操作,法1
	Mat imageClone = img.clone();//clone函数的使用//处理克隆的图像
	colorreduce(imageClone);//原始图像不变
                                         //额外的复制操作,法2
	void colorReduce(const Mat & image,//输入图像,常量引用传递,不能被函数修改
		Mat & resul,                  //输出图像
		int div = 64);
	//colorreduce(img,img);//in-place处理方式时,可以将输入输出指定为同一个变量。不能用,mat->int

	//////另一个实列,不然就要提供另外一个cv::Mat的实列
	//cv::Mat result;
	//colorreduce(img,result);//必须检查输入输出图像的大小,元素类型是否一致。cv::Mat的create成员函数有内置检查操作。

	void colorreduce(Mat & img, int div = 64) {
   
       //利用图像的连续性,把整个处理过程用一个循环完成,颜色缩减函数重写为:
		int n1 = img.rows;
		int nc = img.cols * img.channels();
		if (img.isContinuous())//函数
		{
   
   
			nc = nc * n1;
			n1 = 1;
		}
		for (int j = 0; j < n1; j++) {
   
   
			uchar* data = img.ptr<uchar>(j);
			data[i] = data[i] / div * div + div / 2;
		}
	}
//也可以用reshape方法来重写这段函数:
	if (img.isContinuous())
	{
   
   
		img.reshape(1,img.cols * img.rows);//reshpape不用内存拷贝或重新分配就能改变矩阵维度。两个参数为新的通道数和新的行数。矩阵的列数可根据通道数和行数来自适应。
	}
	int n1 = img.rows;
	int nc = img.cols * img.channels();

	/*namedWindow("output");
	imshow("output", imageClone);*/


	namedWindow("input");
	imshow("input", img);
	waitKey(0);
	return 0;
}

cols代表宽度,rows代表高度,step代表以字节为单位的图像有效宽度。elemSize函数能得到像素大小,channels方法得到图像通道数,total函数返回矩阵像素个数。
in-place变换:直接在输入的图像上进行操作,不然就是新创建一个图像 ,“深拷贝”的方式是调用clone函数。

3.高效的遍历连续图像
若不对图像行尾经行填补的时候,图像视为一个长WxH的一位数组。通过cv::Mat的一个成员函数isContinuous方法来判断是否经行了填补,真,没有填补。

4.底层指针运算
容易出错,还不能定义感兴趣区域,不好用。

2.4 迭代器遍历图像
面向对象中常用迭代器编历数据集合,迭代器是一种特殊的类。不管数据类型是什么,都可以用相似的方式遍历集合,STL标准模板库为每一个容器类提供了迭代器,cv::Mat提供与STL迭代器兼容的迭代器。

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
//Mat Iterator_<Vec3b>it;//创建一个迭代器特化版本1
//Mat_<Vec3b>::iterator it;//创建一个迭代器特化版本2
//第二种实现方式:
void colorreduce(Mat& img, int div = 64) {
   
   
//迭代器初始化完成(法1),再开始循环遍历
	Mat_<Vec3b>::iterator it = img.begin<Vec3b>();//处理彩色图像3b,用begin方法获得初始位置,得到左上角位置的迭代器
	//img.begin<Vec3b>() + img.rows;//从图像的第二行开始
	Mat_<Vec3b>::iterator itend = img.end<Vec3b>();//终止迭代的运算
	//img.end<Vec3b>() - img.rows;//希望在迭代过程的图像最后一行之前停止
	//循环方法1
	for (; it != itend; ++it) {
   
   
	//处理每一个像素
		(*it)[0] = (*it)[0] / div * div + div / 2;
		(*it)[1] = (*it)[1] / div * div + div / 2;
		(*it)[2] = (*it)[2] / div * div + div / 2;
		//处理像素完毕
	}
	//循环方法2
	/*
	while (it!=item){
	//处理每一个像素
	……
	//处理完成
	++it;//移动迭代器。可随意改步长,it+=10;
    }
   */

}
int main() {
   
   
	Mat img = imread("D:/opencvcode/img/13.png");
	if (img.empty()) {
   
   
		printf("not img!");
		return -1;
	}
	colorreduce(img);//函数调用
	namedWindow("input");
	imshow("input", img);

	waitKey(0);
	return 0;
}


在循环体内部,使用解引用操作符*来读写当前元素。读,element=*it,写,*it=element。
若操作对象是const cv::Mat或者强调当前循环不会对cv::Mat的实列进行修改,要创建常量迭代器。
创建方法1:cv::Mat ConstIterator_cv::Vec3bit;
方法2:cv::Mat_cv::Vec3b::const_iterator it;

迭代器初始化(法2)

cv::Mat_<cv::Vec3b>cimage=image;
cv::Mat_<cv::Vec3b>::iterator it =cimage.begin();
cv::Mat_<cv::Vec3b>::iteratoe itend=cimage.end();

面向对象的迭代器概念,阅读STL中迭代器相关的入门文章。关键字:STL Iterator

2.5. 高效的图像遍历循环

 分析效率,提高运行效率。

opencv中, cv:: getTickCount()函数用来检测一段代码的运行时间,计算从上次开机算起的时钟周期数。我们需要计算某个代码段的运行毫秒数,用cv::getTickFrequency(),此函数返回每秒内的时钟周期数。

//统计函数耗费时间的方法
double  duration;
duration =static_cast<double>(cv::getTickCount());
colorreduce(image);//被测试的函数
duration =static_cast<double>(cv::getTickCount())-duration;
duration/=cv::getTickFrequency();//运行时间,以ms为单位

at方法实现:

//at方法实现
for(int j=0;j<nl;j++){
   
   
	for(int i=0;i<nc;i++){
   
   
	//process ench pixel------
	image.at<cv::Vec3b>(j,i)[0]=image.at<cv::Vec3b>(j,i)[0]/div*div+div/2;
	image.at<cv::Vec3b>(j,i)[1]=image.at<cv::Vec3b>(j,i
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阳杨羊

你的鼓励是我继续创作的动力~~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值