OpenCV之Core组件进阶

颜色空间缩减

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

Inew = (Iold/10)*10

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

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

LUT函数:Look up table操作

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

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

计时函数

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

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

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

示例程序如下

1#include<core.hpp> 2#include<highgui.hpp> 3#include<iostream> 4using namespace std; 5using namespace cv; 6 7void colorReduce1(Mat& inputImage, Mat& outputImage, int div);//用指针访问像素(这种方法最快) 8void colorReduce2(Mat& inputimage, Mat& outputImage, int div);//用迭代器操作像素 9void colorReduce3(Mat& inputImage, Mat& outputImage, int div);//动态地址计算 10 11int main() 12{ 13 //1.创建原始图并显示 14 Mat srcImage = imread("..//..//3.jpg"); 15 imshow("原始图像", srcImage); 16 17 //2.按原始图的参数规格来创建效果图 18 Mat dstImage; 19 dstImage.create(srcImage.rows, srcImage.cols, srcImage.type()); //效果图的大小、类型与原始图片相同 20 21 //3.记录起始时间 22 double time0 = static_cast<double>(getTickCount()); 23 24 //4.调用颜色空间缩减函数 25 colorReduce2(srcImage, dstImage, 32); 26 27 //5.计算运行时间并输出 28 time0 = ((double)getTickCount() - time0) / getTickFrequency(); 29 cout << "此方法运行时间为:" << time0 << "秒" << endl; //输出运行时间 30 31 //6.显示效果图 32 imshow("效果图", dstImage); 33 waitKey(0); 34 return 0; 35} 36 37//用指针访问像素(这种方法最快) 38void colorReduce1(Mat& inputImage, Mat& outputImage, int div) 39{ 40 //参数准备 41 outputImage = inputImage.clone(); //复制实参到临时变量 42 int rowNumber = outputImage.rows; //行数 43 int colNumber = outputImage.cols * outputImage.channels(); //列数x通道数=每个元素的个数 44 45 //双重循环,遍历所有的像素值 46 for (int i = 0; i < rowNumber; i++) //行循环 47 { 48 uchar* data = outputImage.ptr<uchar>(i); //获取第i行的首地址 49 for (int j = 0; j < colNumber; j++) //列循环 50 { 51 //-----开始处理每个像素------ 52 data[j] = data[j] / div* div + div / 2; 53 //-----处理结束----- 54 } //行处理结束 55 } 56} 57 58//用迭代器操作像素 59void colorReduce2(Mat& inputimage, Mat& outputImage, int div) 60{ 61 //参数准备 62 outputImage = inputimage.clone(); //复制实参到临时变量 63 //获取迭代器 64 Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>(); //初始位置的迭代器 65 Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>(); //终止位置的迭代器 66 67 //存取彩色图像像素 68 for (; it != itend; ++it) 69 { 70 //----开始处理每个像素---- 71 (*it)[0] = (*it)[0] / div * div + div / 2; 72 (*it)[1] = (*it)[1] / div * div + div / 2; 73 (*it)[2] = (*it)[2] / div * div + div / 2; 74 75 //----处理结束---- 76 } 77} 78 79 80//动态地址计算 81void colorReduce3(Mat& inputImage, Mat& outputImage, int div) 82{ 83 //参数准备 84 outputImage = inputImage.clone(); //复制实参到临时变量 85 int rowNumber = outputImage.rows; //行数 86 int colNumber = outputImage.cols; //列数 87 88 //存取彩色图像像素 89 for (int i = 0; i < rowNumber; i++) 90 { 91 for (int j = 0; j < colNumber; j++) 92 { 93 //----开始处理每个像素---- 94 outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] / div*div + div / 2; //蓝色通道 95 outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1] / div*div + div / 2; //绿色通道 96 outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2] / div*div + div / 2; //红色通道 97 } 98 } 99}

感兴趣区域:ROI

  • 使用Rect指定区域

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

  • 用Range来定义ROI

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

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

示例如下

1#include<core.hpp> 2#include<highgui.hpp> 3#include<stdio.h> 4using namespace cv; 5 6int main() 7{ 8 //1.input image 9 Mat srcImage1 = imread("..//..//3.jpg"); 10 Mat logoImage = imread("..//..//1.jpg"); 11 if (!srcImage1.data) 12 { 13 printf("读取srcImage1错误\n"); 14 return 0; 15 } 16 if (!logoImage.data) 17 { 18 printf("读取logoImage错误\n"); 19 return 0; 20 } 21 //2.define ROI 22 Mat imageROI = srcImage1(Rect(100, 150, logoImage.cols, logoImage.rows)); 23 24 //3.make mask (mast be grey value image) 25 Mat mask = imread("..//..//4.img", 0); 26 27 //4.mask to ROI 28 logoImage.copyTo(imageROI, mask); 29 30 //show dstimage 31 namedWindow("<1>利用ROI实现图像叠加示例窗口"); 32 imshow("<1>利用ROI实现图像叠加示例窗口", srcImage1); 33 waitKey(0); 34 return 0; 35}

线性混合操作与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;

示例如下

1#include<core.hpp> 2#include<highgui.hpp> 3#include<stdio.h> 4using namespace cv; 5 6int main() 7{ 8 //0.定义一些局部变量 9 double alphaValue = 0.5; 10 double betaValue; 11 Mat srcImage2, srcImage3, dstImage; 12 13 //1.读取图像(两幅图像需为同样的类型和尺寸) 14 srcImage2 = imread("..//..//3.jpg"); 15 srcImage3 = imread("..//..//4.jpg"); 16 if (!srcImage2.data) 17 { 18 printf("读取srcImage2错误"); 19 } 20 if (!srcImage3.data) 21 { 22 printf("读取srcImage3错误"); 23 } 24 25 //2.做图像混合加权操作 26 betaValue = (1.0 - alphaValue); 27 addWeighted(srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage); 28 29 //3.创建并显示原图窗口 30 namedWindow("<2>线性混合示例窗口【原图】", 1); 31 imshow("<2>线性混合示例窗口【原图】", srcImage2); 32 33 namedWindow("<3>线性混合示例窗口【效果图】"); 34 imshow("<3>线性混合示例窗口【效果图】", dstImage); 35 waitKey(0); 36 37 return 0; 38}

颜色通道的分离与混合

通道分离:split()函数

函数原型

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

参数

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

示例

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

通道合并:merge()函数

函数原型

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

参数

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

示例如下

1#include<iostream> 2#include<highgui.hpp> 3#include<core.hpp> 4using namespace cv; 5using namespace std; 6 7bool MultiChannelBlending(); 8 9int main() 10{ 11 system("color 9F"); 12 if (MultiChannelBlending()) 13 { 14 cout << endl << "\n运行成功,得出了需要的图像"; 15 } 16 waitKey(0); 17 return 0; 18} 19 20bool MultiChannelBlending() 21{ 22 //0.定义相关变量 23 Mat srcImage; 24 Mat logoImage; 25 vector<Mat> channels; 26 Mat imageBlueChannel; 27 28 //多通道混合蓝色通道部分 29 //1.读入图片 30 logoImage = imread("..//..//1.jpg", 0); 31 srcImage = imread("..//..//3.jpg"); 32 if (!logoImage.data) 33 { 34 printf("读取logoImage错误\n"); 35 return false; 36 } 37 if (!srcImage.data) 38 { 39 printf("读取srcImage错误\n"); 40 return false; 41 } 42 imshow("srcImage【原图】", srcImage); 43 //2.把一个3通道图像转换成3个单通道图像 44 split(srcImage, channels); //分离色彩通道 45 46 //3.将原图的蓝色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改一个另一个跟着变 47 imageBlueChannel = channels.at(0); 48 //4.将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageBlueChannel中 49 addWeighted(imageBlueChannel(Rect(100, 100, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0, imageBlueChannel(Rect(100, 100, logoImage.cols, logoImage.rows))); 50 //5.将三个单通道重新合并成一个三通道 51 merge(channels, srcImage); 52 //6.显示效果图 53 namedWindow("<1>游戏原画+logo蓝色通道"); 54 imshow("<1>游戏原画+logo蓝色通道", srcImage); 55 56 57 //多通道混合绿色通道部分 58 //0.定义相关变量 59 Mat imageGreenChannel; 60 61 //1.读入图片 62 logoImage = imread("..//..//1.jpg", 0); 63 srcImage = imread("..//..//3.jpg"); 64 if (!logoImage.data) 65 { 66 printf("读取logoImage错误\n"); 67 return false; 68 } 69 if (!srcImage.data) 70 { 71 printf("读取srcImage错误\n"); 72 return false; 73 } 74 //2.把一个3通道图像转换成3个单通道图像 75 split(srcImage, channels); //分离色彩通道 76 77//3.将原图的蓝色通道引用返回给imageGreenChannel,注意是引用,相当于两者等价,修改一个另一个跟着变 78 imageGreenChannel = channels.at(1); 79 //4.将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageGreenChannel中 80 addWeighted(imageGreenChannel(Rect(100, 100, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0, imageGreenChannel(Rect(100, 100, logoImage.cols, logoImage.rows))); 81 //5.将三个单通道重新合并成一个三通道 82 merge(channels, srcImage); 83 //6.显示效果图 84 namedWindow("<2>游戏原画+logo绿色通道"); 85 imshow("<2>游戏原画+logo绿色通道", srcImage); 86 87 88 //多通道混合红色通道部分 89 //0.定义相关变量 90 Mat imageRedChannel; 91 92 //1.读入图片 93 logoImage = imread("..//..//1.jpg", 0); 94 srcImage = imread("..//..//3.jpg"); 95 if (!logoImage.data) 96 { 97 printf("读取logoImage错误\n"); 98 return false; 99 } 100 if (!srcImage.data) 101 { 102 printf("读取srcImage错误\n"); 103 return false; 104 } 105 //2.把一个3通道图像转换成3个单通道图像 106 split(srcImage, channels); //分离色彩通道 107 108 //3.将原图的蓝色通道引用返回给imageRedChannel,注意是引用,相当于两者等价,修改一个另一个跟着变 109 imageRedChannel = channels.at(2); 110 //4.将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageRedChannel中 111 addWeighted(imageRedChannel(Rect(100, 100, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0, imageRedChannel(Rect(100, 100, logoImage.cols, logoImage.rows))); 112 //5.将三个单通道重新合并成一个三通道 113 merge(channels, srcImage); 114 //6.显示效果图 115 namedWindow("<3>游戏原画+logo红色通道"); 116 imshow("<3>游戏原画+logo红色通道", srcImage); 117 118 return true; 119}

图像对比度、亮度值调整

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

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

示例如下

1#include<core.hpp> 2#include<highgui.hpp> 3#include<iostream> 4using namespace std; 5using namespace cv; 6 7static void on_ContrastAndBright(int, void*); 8 9int g_nContrastValue; //对比度值 10int g_nBrightValue; //亮度值 11Mat g_srcImage, g_dstImage; 12 13int main() 14{ 15 //1.读取输入图像 16 g_srcImage = imread("..//..//3.jpg"); 17 if (!g_srcImage.data) 18 { 19 printf("读取图片错误,请确定目录下是否有该图片"); 20 return false; 21 } 22 g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type()); 23 24 //2.设定对比度和亮度的初值 25 g_nContrastValue = 80; 26 g_nBrightValue = 80; 27 28 //3.创建效果图窗口 29 namedWindow("【效果图窗口】", 1); 30 31 //4.创建轨迹条 32 createTrackbar("对比度:", "【效果图窗口】", &g_nContrastValue, 300, on_ContrastAndBright); 33 createTrackbar("亮度:", "【效果图窗口】", &g_nBrightValue, 200, on_ContrastAndBright); 34 35 //5.进行回调函数初始化 36 on_ContrastAndBright(g_nContrastValue, 0); 37 on_ContrastAndBright(g_nBrightValue, 0); 38 39 //6.按下"q"键是,程序退出 40 while (char(waitKey(1)) != 'q') { 41 42 } 43 return 0; 44} 45 46static void on_ContrastAndBright(int, void*) 47{ 48 //创建窗口 49 namedWindow("【原始图窗口】", 1); 50 //三个for循环,执行运算 g_dstImage(i,j) = a*g_srcImage(i,j) + b 51 for (int y = 0; y < g_srcImage.rows; y++) 52 { 53 for (int x = 0; x < g_srcImage.cols; x++) 54 { 55 for (int c = 0; c < 3; c++) 56 { 57 g_dstImage.at<Vec3b>(y, x)[c] = saturate_cast<uchar>((g_nContrastValue*0.01)*( g_srcImage.at<Vec3b>(y, x)[c] ) + g_nBrightValue); 58 59 } 60 } 61 } 62 //显示图像 63 imshow("【原始图窗口】", g_srcImage); 64 imshow("【效果图窗口】", g_dstImage); 65}

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

1if (data < 0) 2 data = 0; 3else if (data > 255) 4 data = 255;
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java将前端的json数组字符串转换为列表

记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang

swap空间的增减方法

(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap