OpenCV学习笔记(三)

本文详细介绍了OpenCV中Point类的用法,以及色彩空间转换、基本图像绘制、图像像素操作和色彩空间缩减等关键概念,包括Point2i、Point1i等类型,色彩空间转换函数cvtColor和各种绘图函数的示例应用。

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

关于Point类特殊要说的:

typedef Point2i Point1;
typedef Point_<int> Pointi;

typedef Point2f Point2;
typedef Point_<float> Pointf;

这些类型根据其基于的基本数据类型,之间都有等价关系。

Scalar:其本质就是4个元素的数组,在opencv中大量被用于存储像素值,例如RGB元素值。

切记:用几个参数,写几个,别没事找事!

Size:size(int width,int height)构建的时候给定宽高,再没别的用法

Rect:x,y左上点坐标,width,height矩形宽高,area返回面积,contain(Point)判断点是否在矩形里,inSide(Rect)判断矩阵是否在矩阵里,tl()返回左上点左边,br()返回右下角坐标

色彩空间转换:void cvtColor(InputArray src,OutputArray dst,int code,int dstCn = 0);

第三个参数:色彩空间转换的表示符,理解着用,例如BGR转HSV COLOR_BGR2HSV,这个2就是to。

第四个参数:默认值0,表示目标图像获取原图像的通道数。基本上用不到,不输参数,默认值就好了。

基本图像绘制:line直线,ellipse椭圆,rectangle矩形绘制函数,circle圆,fillPoly函数(填充多边形)

具体参数看vs的提示和下面示例测试程序

#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

#define WINDOW_NAME1 "【绘制图1】"        //为窗口标题定义的宏 
#define WINDOW_NAME2 "【绘制图2】"        //为窗口标题定义的宏 
#define WINDOW_WIDTH 600//定义窗口大小的宏

using namespace cv;

void DrawEllipse(Mat img, double angle);//绘制椭圆
void DrawFilledCircle(Mat img, Point center);//绘制圆
void DrawPolygon(Mat img);//绘制多边形
void DrawLine(Mat img, Point start, Point end);//绘制线段

int main(int argc, char** argv) {
	Mat src = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
	double angle = 0.0;
	DrawEllipse(src, angle);
	DrawLine(src, Point(20, 20), Point(100, 100));
	DrawFilledCircle(src, Point(150, 150));
	DrawPolygon(src);
	imshow("画布", src);
	waitKey(0);

}
void DrawEllipse(Mat img, double angle) {
	int thickness = 2;
	int lineType = 8;
	ellipse(img,//画布
		Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2),//圆心
		Size(WINDOW_WIDTH / 4, WINDOW_WIDTH / 16),//椭圆尺寸
		angle,//椭圆旋转角度
		0,//拓展的弧度0-360
		360,
		Scalar(255, 129, 0),//通道颜色,蓝色
		thickness,//线宽
		lineType);//线型
}
void DrawLine(Mat img, Point start, Point end)
{
	int thickness = 2;
	int lineType = 8;
	line(img,//输出图像
		start,//初始点
		end,//结束点
		Scalar(255, 129, 0),//通道颜色。蓝色
		thickness,//线宽
		lineType);//线型
}
void DrawFilledCircle(Mat img, Point center)
{
	//线宽为-1,则会用通道颜色填充圆
	//正整值,则是正常的空心圆
	int thickness = -1;
	int lineType = 8;

	circle(img,
		center,//圆心
		WINDOW_WIDTH / 32,//半径
		Scalar(255, 129, 0),//通道颜色,蓝色
		thickness,//线宽
		lineType);//线型
}
void DrawPolygon(Mat img)
{
	int lineType = 8;

	//创建一些点
	Point rookPoints[1][20];
	rookPoints[0][0] = Point(WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
	rookPoints[0][1] = Point(3 * WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
	rookPoints[0][2] = Point(3 * WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][3] = Point(11 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][4] = Point(19 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][5] = Point(3 * WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][6] = Point(3 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
	rookPoints[0][7] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][8] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][9] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][10] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][11] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][12] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][13] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][14] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][15] = Point(WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
	rookPoints[0][16] = Point(WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][17] = Point(13 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][18] = Point(5 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][19] = Point(WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);

	const Point* ppt[1] = { rookPoints[0] };
	int npt[] = { 20 };

	fillPoly(img,
		ppt,
		npt,
		1,
		Scalar(255, 255, 255),
		lineType);
}

图像在内存中的存储方式:

灰度图像:

多通道图像:

Opencv中的子列存储顺序是反过来的,BGR而不是RGB(了解)

色彩空间缩减:

由于多通道图像,像素值太太太多,决定将现有色彩空间除以某个输入值,以获得较少的颜色数。

0~9取0,10~19取10,类似这样。

LUT函数:批量进行图像元素查找,扫描与操作图像。

函数原型:void LUT(InputArray src, InputArray lut, OutputArray dst);

一参:输入图像(单,多通道都可)

二参:查找表(与输入图像通道数保持一致)

三参:输出图像

具体使用方法:

#include <opencv2/highgui/highgui.hpp>

using namespace cv;

int main(int argc, char** argv) {
	uchar lutData[256];
	for (int i = 0; i < 256; i++) {
		if (i <= 100) {
			lutData[i] = 0;
		}
		if (i > 100 && i <= 200) {
			lutData[i] = 100;
		}
		if (i > 200) {
			lutData[i] = 255;
		}
	}
	Mat lut(1, 256, CV_8UC1, lutData);//1行,256列,像素类型为8字节无符号char类型1通道,通道已定义的uchar类型数组
	Mat src = imread("E://hashiqi.jpg", IMREAD_GRAYSCALE);
	Mat dst;
	LUT(src, lut, dst);
	imshow("///", src);
	imshow("------", dst);
	waitKey(0);


}

其中测试程序的代码不难理解,这个lut查找表的机制原理,暂时没搞懂,之后再说

计时函数(常用):getTickCount():返回某个事件以来走过的时钟周期数

                            getTickFrequency():返回cpu一秒钟所走过的时钟周期数

这样用

访问图片像素的三种方法:

1.指针访问:

mat.ptr<type>(row)[col],没啥好说的,用就完事了

void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
	//参数准备
	outputImage = inputImage.clone();  //拷贝实参到临时变量
	int rowNumber = outputImage.rows;  //行数
	int colNumber = outputImage.cols * outputImage.channels();  //列数 x 通道数=每一行元素的个数

	//双重循环,遍历所有的像素值
	for (int i = 0; i < rowNumber; i++)  //行循环
	{
		uchar* data = outputImage.ptr<uchar>(i);  //获取第i行的首地址
		for (int j = 0; j < colNumber; j++)   //列循环
		{
			// ---------【开始处理每个像素】-------------     
			/*data[j] = data[j] / div * div + div / 2;*/
			std::cout << (int)data[j];
			// ----------【处理结束】---------------------
		}  //行处理结束
	}
}

2.迭代器iterator:

获取图像矩阵的begin和end,然后解指针访问像素内容,遍历跟寻常迭代器一致

void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
	//参数准备
	outputImage = inputImage.clone();  //拷贝实参到临时变量
	//获取迭代器
	Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();  //初始位置的迭代器
	Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();  //终止位置的迭代器

	//存取彩色图像像素
	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;
		std::cout << (*it)[0];
		//多通道的话就再自行发挥语句
		// ------------------------【处理结束】----------------------------
	}
}

3.动态地址计算:

没啥说的,at<像素类型>[通道下标],就可

void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
	//参数准备
	outputImage = inputImage.clone();  //拷贝实参到临时变量
	int rowNumber = outputImage.rows;  //行数
	int colNumber = outputImage.cols;  //列数

	//存取彩色图像像素
	for (int i = 0; i < rowNumber; i++)
	{
		for (int j = 0; j < colNumber; j++)
		{
			// ------------------------【开始处理每个像素】--------------------
			std::cout << outputImage.at<Vec3b>(i, j)[0];
			std::cout << outputImage.at<Vec3b>(i, j)[1];
			std::cout << outputImage.at<Vec3b>(i, j)[2];
			//outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] / div * div + div / 2;  //蓝色通道
			//outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1] / div * div + div / 2;  //绿色通道
			//outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2] / div * div + div / 2;  //红是通道
			// -------------------------【处理结束】----------------------------
		}  // 行处理结束     
	}
}

分离颜色通道函数split,多通道图像混合merge

void split(const Mat &src,Mat *mvbegin)

void split(InputArray m,OutputArrayOfArrays mv);

void merge(const Mat* mv,size_tcount,OutputArray dst)

void merge(InputArrayofArrays mv,OutputArray dst)

split 和 merge 是互逆操作,用法,参数均类似

at方法是取的引用,修改值,原值也会变动

#include <opencv2/core/core.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
	Mat src = imread("E://hashiqi.jpg");
	Mat imageROI,image2_ROI,image3_ROI;
	Mat dst;
	vector<Mat> channels;
	split(src, channels);
	imageROI = channels.at(0);//多通道图像分离通道号,像素值从0~255,理论上来说应该是灰度图像
	image2_ROI = channels.at(1);
	image3_ROI = channels.at(2);
	merge(channels, dst);//合并三通道
	imshow("merge", dst);
	imshow("split", imageROI);
	waitKey(0);
}

点操作(仅通过输入的像素值变化,最多加上些全局信息):亮度调整,对比度调整,颜色校正,变换

f(x):原图像素,g(x):输出图像素,a>0称增益,控制图像对比度,b称偏置,控制图像亮度

下面是使用方法的测试程序:

#include <opencv2/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

int ContrastValue; //对比度值
int BrightValue;  //亮度值

Mat src, dst;//输入图像,输出图像
static void ContrastAndBright(int, void*);

int main(int argc, char** argv) {

	system("color 2F");
	src = imread("E://hashiqi.jpg");
	dst = Mat::zeros(src.size(), src.type());//初始化dst尺寸

	namedWindow("【原始图窗口】",1);
	ContrastValue = 80;
	BrightValue = 80;
	createTrackbar("对比度", "【原始图窗口】", &ContrastValue, 300,ContrastAndBright);//ContrastAndBright实现修改功能的回调函数
	createTrackbar("亮度", "【原始图窗口】", &BrightValue, 200, ContrastAndBright);
	ContrastAndBright(ContrastValue, 0);
	ContrastAndBright(BrightValue, 0);
	waitKey(0);

}
static void ContrastAndBright(int, void*) {

	namedWindow("【原始图窗口】", 1);
	for (int y = 0; y < src.rows; y++)
	{
		for (int x = 0; x < src.cols; x++)
		{
			for (int c = 0; c < 3; c++)
			{
				dst.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((ContrastValue * 0.01) * (src.at<Vec3b>(y, x)[c]) + BrightValue);
			}
		}
	}
	// 显示图像
	imshow("【原始图窗口】", src);
	imshow("【效果图窗口】", dst);
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值