颜色空间缩减

利用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错误\n");
return 0;
}
if (!logoImage.data)
{
printf("读取logoImage错误\n");
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 << "\n运行成功,得出了需要的图像";
}
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错误\n");
return false;
}
if (!srcImage.data)
{
printf("读取srcImage错误\n");
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错误\n");
return false;
}
if (!srcImage.data)
{
printf("读取srcImage错误\n");
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错误\n");
return false;
}
if (!srcImage.data)
{
printf("读取srcImage错误\n");
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;

OpenCV之Core组件进阶的更多相关文章

  1. opencv 3 core组件进阶(3 离散傅里叶变换;输入输出XML和YAML文件)

    离散傅里叶变换 #include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" ...

  2. opencv 3 core组件进阶(2 ROI区域图像叠加&图像混合;分离颜色通道、多通道图像混合;图像对比度,亮度值调整)

    ROI区域图像叠加&图像混合 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp&g ...

  3. opencv 3 core组件进阶(1 访问图像中的像素)

    访问图像像素的三类方法 ·方法一 指针访问:C操作符[ ]; ·方法二 迭代器iterator; ·方法三 动态地址计算. #include <opencv2/core/core.hpp> ...

  4. opencv core组件进阶

    1.图像在内存中存储方式,图像矩阵的大小取决于颜色模型,取决于所有的通道数:还有重要的颜色空间缩减的概念:因为如果是RGB的话,使用uchar的话,就有256^3的结合方法.所以要用到颜色缩减的方法, ...

  5. core组件进阶

    访问图像像素 存储方式 BGR连续存储有助于提升图像扫描速度. isContinuous()判断是否是连续存储. 颜色空间缩减 仅用这些颜色中具有代表性的很小的部分,就足以达到同样的效果. 将现有颜色 ...

  6. .Net Core组件化视图(部分视图)

    .Net Core组件化视图(部分视图) 1.背景 1.以前我们使用.Net的时候使用部分视图的方式在,.Net Core 中能在单独处理逻辑的部分视图没有了,但是我们还是想使用现在的.Net Cor ...

  7. Vue基础二之全局API、实例属性和全局配置,以及组件进阶(mixins)的详细教程(案列实现,详细图解,附源码)

    本篇文章主要是写Vue.directive().Vue.use()等常用全局API的使用,vm.$props.vm.$options.vm.$slots等实例属性的使用,以及Vue全局配置.组件的mi ...

  8. Django之forms组件进阶

    Django Form表单组件   Form介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要 ...

  9. opencv:联通组件扫描

    #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace st ...

随机推荐

  1. Web_0005:阿里云服务器OSS权限的配置开通

    创建用户 1,创建子用户 2,点击新建用户 3,设置账号类型,可以同时选 设置权限 1,设置对阿里云模块的控制权限,如 oss  ecs 等的访问控制权限 2,点击所需的权限 3,获取账号的Acess ...

  2. Java虚拟内存(栈、堆)

    一.java虚拟的内存可以分为几种 1. 第一种 栈(stack) 栈的特点 1.1 栈描述的是方法执行的内存模型,每个方法都被调用都会创建一个栈(存储局部变量.操作数. 方法出口等) 1.2 JVM ...

  3. Minion 主机同步失败问题,全过程

    如果出现以下状态 token也有了 这个是salt-api  说明你salt-api没问题 点击同步主机 查看你产品线管理那里,添加了你这个salt-api没? 配置参考文档 https://gith ...

  4. maven依赖包无法更新下载

    在IDEA工程中导入已存在的module时,按默认设置,直到完成导入,结果所有的外部依赖包都无法更新下载,即使是更新了setting.xml配置文件信息,依旧是不能更新下载依赖包,现将具体的操作过程和 ...

  5. vs2008 asp.net “无法连接到ASP.NET Development server”

    这是因为该网站启动ASP.NET Development server时使用的端口被占用而导致的. 解决方法: 1.在“解决方案资源管理器”中选中网站项目名称,然后切换到“属性”窗口 2.在“属性”窗 ...

  6. 提取 Microsoft.ReportViewer等dll

    ReportViewer 在开发环境没问题 发布以后可能会提示找不到 Microsoft.ReportViewer 下的几个dll 可以用用下面脚本在开发服务器上提取 相应的dll @SET dest ...

  7. SQL语句中设置字段值取反操作

    1.对布尔值取反,使用 ~. 如 update set status=~status where id=2; status的值为true || false. 2.对0.1 数值取反,使用abs() 取 ...

  8. 字符串积累ing

    明天就要上网课拉拉啦啦! 数据库先在手机端登录然后转战客户端试之! 操作系统在客户端登录试一试! 马原用学习通试试啦! 首先,介绍一下strlen,strcpy,strcmp函数! 参考:https: ...

  9. 0级搭建类005-Oracle Solaris Unix安装 (11.4) 公开

    项目文档引子系列是根据项目原型,制作的测试实验文档,目的是为了提升项目过程中的实际动手能力,打造精品文档AskScuti. 项目文档引子系列目前不对外发布,仅作为博客记录.如学员在实际工作过程中需提前 ...

  10. 2017-9-15Opencv 杂

    Mat::at()的具体含义.指的是三通道.(0),(1),(2)分别表示BGR: Vector<Mat>结构的使用.将Mat类型的数据转化成了具有多个单通道的容器? 灰度图的具体含义.和 ...