zoukankan      html  css  js  c++  java
  • OpenCV之Core组件进阶

    颜色空间缩减

    利用C++类型转换时向下取整操作,实现定义域内颜色缩减。表达式如下

    Inew = (Iold/10)*10

    简单的颜色空间缩减算法可由以下两步组成:

    (1)遍历图像矩阵的每个元素
    (2)对像应用上述公式

    LUT函数:Look up table操作

    上文提到的Look up table操作,OpenCV官方文档中强烈推荐使用一个原型为operationsOnArrays:LUT()的函数来进行。使用方法如下:

    //首先我们建立一个mat型用于查表
    Mat lookUpTable(1, 256, CV_8U);
    uchar* p = lookUpTable.data;
    for(int i = 0; i < 256; ++i)
      p[i] = table[i];
    
    //然后我们调用函数(I是输入J是输出):
    for(int i = 0; i < times; ++i)
      LUT(I, lookUpTable, J);
    

    计时函数

    • getTickCount()函数返回CPU自时间以来走过的时钟周期数
    • getTickFrequency()函数返回CPU一秒钟所走的时钟周期数。

    访问图像中像素的三类方法

    • 指针访问:C语言操作符[]; (最快)
    • 迭代器iterator; (最安全)
    • 动态地址计算; (最直观)

    示例程序如下

    #include<core.hpp>
    #include<highgui.hpp>
    #include<iostream>
    using namespace std;
    using namespace cv;
    
    void colorReduce1(Mat& inputImage, Mat& outputImage, int div);//用指针访问像素(这种方法最快)
    void colorReduce2(Mat& inputimage, Mat& outputImage, int div);//用迭代器操作像素
    void colorReduce3(Mat& inputImage, Mat& outputImage, int div);//动态地址计算
    
    int main()
    {
    	//1.创建原始图并显示
    	Mat srcImage = imread("..//..//3.jpg");
    	imshow("原始图像", srcImage);
    
    	//2.按原始图的参数规格来创建效果图
    	Mat dstImage;
    	dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());	//效果图的大小、类型与原始图片相同
    
    	//3.记录起始时间
    	double time0 = static_cast<double>(getTickCount());
    
    	//4.调用颜色空间缩减函数
    	colorReduce2(srcImage, dstImage, 32);
    
    	//5.计算运行时间并输出
    	time0 = ((double)getTickCount() - time0) / getTickFrequency();
    	cout << "此方法运行时间为:" << time0 << "秒" << endl;	//输出运行时间
    
    	//6.显示效果图
    	imshow("效果图", dstImage);
    	waitKey(0);
    	return 0;
    }
    
    //用指针访问像素(这种方法最快)
    void colorReduce1(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;
    			//-----处理结束-----
    		}	//行处理结束
    	}
    }
    
    //用迭代器操作像素
    void colorReduce2(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;
    
    		//----处理结束----
    	}
    }
    
    
    //动态地址计算
    void colorReduce3(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++)
    		{
    			//----开始处理每个像素----
    			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; //红色通道
    		}
    	}
    }
    

    感兴趣区域:ROI

    • 使用Rect指定区域
    //定义一个Mat类型并给其设定ROI区域
    Mat imageROI;
    //方法一
    imageROI = image(Rect (500, 250, logo.cols, logo.rows));
    //image 为已载入的图片
    
    • 用Range来定义ROI

    Range 是指从起始索引到终止索引(不包括终止索引)的一连续序列。

    imageROI = image(Range(250, 250+logoImage.rows), Range(200, 200+logoImage.cols));
    // image 为已载入的图片
    

    示例如下

    #include<core.hpp>
    #include<highgui.hpp>
    #include<stdio.h>
    using namespace cv;
    
    int main()
    {
    	//1.input image
    	Mat srcImage1 = imread("..//..//3.jpg");
    	Mat logoImage = imread("..//..//1.jpg");
    	if (!srcImage1.data)
    	{
    		printf("读取srcImage1错误
    ");
    		return 0;
    	}
    	if (!logoImage.data)
    	{
    		printf("读取logoImage错误
    ");
    		return 0;
    	}
    	//2.define ROI
    	Mat imageROI = srcImage1(Rect(100, 150, logoImage.cols, logoImage.rows));
    
    	//3.make mask  (mast be grey value image)
    	Mat mask = imread("..//..//4.img", 0);
    
    	//4.mask to ROI
    	logoImage.copyTo(imageROI, mask);
    
    	//show dstimage
    	namedWindow("<1>利用ROI实现图像叠加示例窗口");
    	imshow("<1>利用ROI实现图像叠加示例窗口", srcImage1);
    	waitKey(0);
    	return 0;
    }
    

    线性混合操作与addWeighted()函数

    • 线性混合理论公式:g(x) = (1-a)fa(x)+ af3(x)
    • addWeighted()函数
      函数原型
    void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype = -1);
    

    参数

    • InputArray 类型的src1,表示需要加权的第一个数组,常常填一个Mat;
    • double类型的alpha,表示第一个数组的权重
    • InputArray 类型的src2,表示第二个数组,它需要和第一个数组拥有相同的尺寸和通道数
    • double 类型的beta,表示第一个数组的权重值;
    • double 类型的gamma,一个加到权重和上的标量值。
    • OutputArray 类型的dst,输出的数组,它和输入的两个数组拥有相同的尺寸和通道数
    • int 类型的dtype,输出阵列的可选深度,默认值-1。当两个输入数组具有相同的深度时,这个参数设置为-1(默认值),即等同于src1.depth()。

    addWeighted 函数作用矩阵的表达式

    dst = src1[I]alpha + src2[I]beta + gamma;

    示例如下

    #include<core.hpp>
    #include<highgui.hpp>
    #include<stdio.h>
    using namespace cv;
    
    int main()
    {
    	//0.定义一些局部变量
    	double alphaValue = 0.5;
    	double betaValue;
    	Mat srcImage2, srcImage3, dstImage;
    
    	//1.读取图像(两幅图像需为同样的类型和尺寸)
    	srcImage2 = imread("..//..//3.jpg");
    	srcImage3 = imread("..//..//4.jpg");
    	if (!srcImage2.data)
    	{
    		printf("读取srcImage2错误");
    	}
    	if (!srcImage3.data)
    	{
    		printf("读取srcImage3错误");
    	}
    
    	//2.做图像混合加权操作
    	betaValue = (1.0 - alphaValue);
    	addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage);
    
    	//3.创建并显示原图窗口
    	namedWindow("<2>线性混合示例窗口【原图】", 1);
    	imshow("<2>线性混合示例窗口【原图】", srcImage2);
    
    	namedWindow("<3>线性混合示例窗口【效果图】");
    	imshow("<3>线性混合示例窗口【效果图】", dstImage);
    	waitKey(0);
    
    	return 0;
    }
    

    颜色通道的分离与混合

    通道分离:split()函数

    函数原型

    void split(const Mat& src, Mat* mvbegin);
    void split(InputArray m, OutputArrayOfArrays mv);
    

    参数

    • InputArray 类型的m或者const Mat&类型的src,填我们需要分离的多通道数组。
    • OutputArrayOfArrays类型的mv,填函数的输出数组或者输出的vector容器

    示例

    vector<Mat> channels;
    Mat imageBlueChannel;
    Mat imageGreenChannel;
    Mat imageRedChannel;
    srcImage = imread(filename);
    //把一个3通道图像转换成3个单通道图像
    split(srcImage, channels);  //分离色彩通道
    imageBlueChannel = channels.at(0);
    imageGreenChannel = channels.at(1);
    imageRedChannel = chan.at(2);
    

    通道合并:merge()函数

    函数原型

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

    参数

    • 填需要被合并的输入矩阵或vector容器的阵列(数组),这个mv参数中所有的矩阵必须有着一样的尺寸和深度。
    • 当mv为一个空白的C语言数组时,代表输入矩阵的个数,这个参数显然必须大于1。
    • dst即输出矩阵,和mv[0]具有一样的尺寸和深度,并且通道的数量是矩阵阵列中的通道的总数。

    示例如下

    #include<iostream>
    #include<highgui.hpp>
    #include<core.hpp>
    using namespace cv;
    using namespace std;
    
    bool MultiChannelBlending();
    
    int main()
    {
    	system("color 9F");
    	if (MultiChannelBlending())
    	{
    		cout << endl << "
    运行成功,得出了需要的图像";
    	}
    	waitKey(0);
    	return 0;
    }
    
    bool MultiChannelBlending()
    {
    	//0.定义相关变量
    	Mat srcImage;
    	Mat logoImage;
    	vector<Mat> channels;
    	Mat imageBlueChannel;
    
    	//多通道混合蓝色通道部分
    	//1.读入图片
    	logoImage = imread("..//..//1.jpg", 0);
    	srcImage = imread("..//..//3.jpg");
    	if (!logoImage.data)
    	{
    		printf("读取logoImage错误
    ");
    		return false;
    	}
    	if (!srcImage.data)
    	{
    		printf("读取srcImage错误
    ");
    		return false;
    	}
    	imshow("srcImage【原图】", srcImage);
    	//2.把一个3通道图像转换成3个单通道图像
    	split(srcImage, channels);	//分离色彩通道
    
    	//3.将原图的蓝色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改一个另一个跟着变
    	imageBlueChannel = channels.at(0);
    	//4.将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageBlueChannel中
    	addWeighted(imageBlueChannel(Rect(100, 100, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0, imageBlueChannel(Rect(100, 100, logoImage.cols, logoImage.rows)));
    	//5.将三个单通道重新合并成一个三通道
    	merge(channels, srcImage);
    	//6.显示效果图
    	namedWindow("<1>游戏原画+logo蓝色通道");
    	imshow("<1>游戏原画+logo蓝色通道", srcImage);
    
    
    	//多通道混合绿色通道部分
    	//0.定义相关变量
    	Mat imageGreenChannel;
    
    	//1.读入图片
    	logoImage = imread("..//..//1.jpg", 0);
    	srcImage = imread("..//..//3.jpg");
    	if (!logoImage.data)
    	{
    		printf("读取logoImage错误
    ");
    		return false;
    	}
    	if (!srcImage.data)
    	{
    		printf("读取srcImage错误
    ");
    		return false;
    	}
    	//2.把一个3通道图像转换成3个单通道图像
    	split(srcImage, channels);	//分离色彩通道
    
    //3.将原图的蓝色通道引用返回给imageGreenChannel,注意是引用,相当于两者等价,修改一个另一个跟着变
    	imageGreenChannel = channels.at(1);
    	//4.将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageGreenChannel中
    	addWeighted(imageGreenChannel(Rect(100, 100, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0, imageGreenChannel(Rect(100, 100, logoImage.cols, logoImage.rows)));
    	//5.将三个单通道重新合并成一个三通道
    	merge(channels, srcImage);
    	//6.显示效果图
    	namedWindow("<2>游戏原画+logo绿色通道");
    	imshow("<2>游戏原画+logo绿色通道", srcImage);
    
    
    	//多通道混合红色通道部分
    	//0.定义相关变量
    	Mat imageRedChannel;
    
    	//1.读入图片
    	logoImage = imread("..//..//1.jpg", 0);
    	srcImage = imread("..//..//3.jpg");
    	if (!logoImage.data)
    	{
    		printf("读取logoImage错误
    ");
    		return false;
    	}
    	if (!srcImage.data)
    	{
    		printf("读取srcImage错误
    ");
    		return false;
    	}
    	//2.把一个3通道图像转换成3个单通道图像
    	split(srcImage, channels);	//分离色彩通道
    
    	//3.将原图的蓝色通道引用返回给imageRedChannel,注意是引用,相当于两者等价,修改一个另一个跟着变
    	imageRedChannel = channels.at(2);
    	//4.将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageRedChannel中
    	addWeighted(imageRedChannel(Rect(100, 100, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0, imageRedChannel(Rect(100, 100, logoImage.cols, logoImage.rows)));
    	//5.将三个单通道重新合并成一个三通道
    	merge(channels, srcImage);
    	//6.显示效果图
    	namedWindow("<3>游戏原画+logo红色通道");
    	imshow("<3>游戏原画+logo红色通道", srcImage);
    
    	return true;
    }
    

    图像对比度、亮度值调整

    理论公式:g(i,j) = a*f(i,j) + b
    其中

    • 参数f(x)表示源图像像素
    • 参数g(x)表示输出图像像素
    • 参数a(需要满足a>0)被称为增益(gain),常常被用来控制图像的对比度。
    • 参数b通常被称为偏置(bias),常常被用来控制图像的亮度。

    示例如下

    #include<core.hpp>
    #include<highgui.hpp>
    #include<iostream>
    using namespace std;
    using namespace cv;
    
    static void on_ContrastAndBright(int, void*);
    
    int g_nContrastValue;	//对比度值
    int g_nBrightValue;		//亮度值
    Mat g_srcImage, g_dstImage;
    
    int main()
    {
    	//1.读取输入图像
    	g_srcImage = imread("..//..//3.jpg");
    	if (!g_srcImage.data)
    	{
    		printf("读取图片错误,请确定目录下是否有该图片");
    		return false;
    	}
    	g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type());
    
    	//2.设定对比度和亮度的初值
    	g_nContrastValue = 80;
    	g_nBrightValue = 80;
    
    	//3.创建效果图窗口
    	namedWindow("【效果图窗口】", 1);
    
    	//4.创建轨迹条
    	createTrackbar("对比度:", "【效果图窗口】", &g_nContrastValue, 300, on_ContrastAndBright);
    	createTrackbar("亮度:", "【效果图窗口】", &g_nBrightValue, 200, on_ContrastAndBright);
    
    	//5.进行回调函数初始化
    	on_ContrastAndBright(g_nContrastValue, 0);
    	on_ContrastAndBright(g_nBrightValue, 0);
    
    	//6.按下"q"键是,程序退出
    	while (char(waitKey(1)) != 'q') {
    
    	}
    	return 0;
    }
    
    static void on_ContrastAndBright(int, void*)
    {
    	//创建窗口
    	namedWindow("【原始图窗口】", 1);
    	//三个for循环,执行运算 g_dstImage(i,j) = a*g_srcImage(i,j) + b
    	for (int y = 0; y < g_srcImage.rows; y++)
    	{
    		for (int x = 0; x < g_srcImage.cols; x++)
    		{
    			for (int c = 0; c < 3; c++)
    			{
    				g_dstImage.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((g_nContrastValue*0.01)*( g_srcImage.at<Vec3b>(y, x)[c] ) + g_nBrightValue);
    
    			}
    		}
    	}
    	//显示图像
    	imshow("【原始图窗口】", g_srcImage);
    	imshow("【效果图窗口】", g_dstImage);
    }
    

    其中saturate_cast是对结果进行转换防止溢出,原理大致如下

    if (data < 0)
      data = 0;
    else if (data > 255)
      data = 255;
    
  • 相关阅读:
    而字歌
    排列组合的学习,基础入门,选修2-3
    金句集(目前9句)
    简析几何叉乘与安培力的内在逻辑
    浅谈参变分离的妙用
    日语
    Tarjan-SCC-NOIP2015message
    WebAPI身份验证
    简单记录在Visual Studio 2013中创建ASP.NET Web API 2
    从两个平方算法到分治算法-java
  • 原文地址:https://www.cnblogs.com/ishero/p/11136325.html
Copyright © 2011-2022 走看看