OpenCV架构设计:核心(Core)+ 插件(图像处理、视频处理、机器学习等等…)设计
核心组件->Core
问题:我们如何访问图像中的像素?
1、了解图像在内存中存储方式
第一种:单通道->灰度图片
[图片上传失败...(image-d7de16-1593442557387)]
第二种:多通道->彩色图片
每一个像素点:可能是2种、3种
顺序:R->G->B,B->G->R
图像在内存中存储方式-多通道
[图片上传失败...(image-33000d-1593442557387)]
OpenCV里面图像存储方式连续不断像素(好处:提高图片扫描效率)->isContinuous()函数判定图片存储方式在内存是否是连续不断的
2、图像颜色空间缩减问题?
为什么讨论一个这样的问题?
在图像学里面
原始范围
RGB颜色取值范围:0-255
R大小:0-255->256种颜色
G大小:0-255->256种颜色
B大小:0-255->256种颜色
RGB大小 = R * B * G = 256 * 256 * 256 = 167万
缩减范围
因为:在开发中,其实颜色用不到这么多(例如:图像处理的时候,知道图片颜色种类1000种,然而我们在进行图片处理的时候,我们依然选择167万种这样处理方式,也就会带来问题,图像处理效率问题)
目的:为了提高图像处理效率(性能优化)
在OpenCV框架里面->缩减规则
0-9->对应->0
10-19->对应->10
20-29->对应->20
30->39->对应->30
…
250-255->对应->250
R大小:26种
G大小:26种
B大小:26种
缩减之后:RGB = 26 * 26 * 26 = 1.7万
这样的图像处理方式,比原来处理缩减了100倍(效率提高了)
3、访问图像像素点?(操作图像像素点有哪些方式?)
无数种方式
总结3大类->主流方式
第一类:指针访问(直接通过指针操作)(这种效率最高)
案例:处理一张图片,对比处理性能
第二类:迭代器访问
第三类:动态地址计算
案例:
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
// 第一类:指针访问
// 耗时0.003235秒 = 3.235毫秒
void colorSpaceReduce1(Mat &mat_image_in, Mat &mat_image_out, int div){ // div:缩减级别
mat_image_out = mat_image_in.clone();
int rows = mat_image_out.rows;// 行数
int cols = mat_image_out.cols;// 列数
// 通道数量->2种颜色,3种颜色(每一个像素点,有多少个颜色)
int channels = mat_image_out.channels();
// 获取每一行元素个数->RGB个数,每个像素点24位:8*3=24,8:每个RGB中一个有8位,3:R、G、B
int colsSize = cols * channels;
// 循环缩减颜色通道
for (int i = 0; i < rows; i++) {
// 获取第i行->指针访问像素点
uchar* data = mat_image_out.ptr<uchar>(i);
for (int j = 0; j < colsSize; j++) {
// 指针位移
// data[j]->表示一个颜色值
// data[j] = 100
// div = 24
// data[j] = 100 / 24 * 24 = 96
// data[j] = 96 + 24 / 2 = 108
// 这只是一个公式而已(随便你改)->想怎么玩就怎么玩->随心所欲
data[j] = data[j] / div * div;
}
}
}
//第二类:迭代器访问->封装东西比较多(根据场景)
//耗时0.004958秒 = 4.958毫秒
void colorSpaceReduce2(Mat &mat_image_in, Mat &mat_image_out, int div){
mat_image_out = mat_image_in.clone();
Mat_<Vec3b>::iterator it_start = mat_image_out.begin<Vec3b>();
Mat_<Vec3b>::iterator it_end = mat_image_out.end<Vec3b>();
//循环缩减颜色通道
for (; it_start != it_end; it_start++) {
//(*it_start)像素点
//(*it_start)[0]:表示获取像素点值->蓝色->B值
(*it_start)[0] = (*it_start)[0] / div * div;
(*it_start)[1] = (*it_start)[1] / div * div;
(*it_start)[2] = (*it_start)[2] / div * div;
}
}
//第三类:动态地址计算
//耗时0.003782秒 = 3.782毫秒
void colorSpaceReduce3(Mat &mat_image_in, Mat &mat_image_out, int div){
mat_image_out = mat_image_in.clone();
int rows = mat_image_out.rows;
int cols = mat_image_out.cols;
for (int i = 0; i < rows; i ++) {
for (int j = 0; j < cols; j++) {
mat_image_out.at<Vec3b>(i, j)[0] = mat_image_out.at<Vec3b>(i, j)[0] / div * div;
mat_image_out.at<Vec3b>(i, j)[1] = mat_image_out.at<Vec3b>(i, j)[1] / div * div;
mat_image_out.at<Vec3b>(i, j)[2] = mat_image_out.at<Vec3b>(i, j)[2] / div * div;
}
}
}
int main(int argc, const char * argv[]) {
//第一步:创建一张图片->空白图片
Mat mat_image_src = imread("/Users/alange/Desktop/OpenCV/Images/scenery.png");
//第二步:按照原是图片创建一张新的图片
Mat mat_image_dst;
mat_image_dst.create(mat_image_src.rows,
mat_image_src.cols,
mat_image_src.type());
//记录开始时间
//getTickCount()函数表示:返回CPU自某个事件以来走过的时间周期数(纳秒,非常小的时间单位)
//例如:电脑启动、电脑休眠等等...
double time_start = static_cast<double>(getTickCount());
//第三步:颜色空间缩减
//第一类:指针访问
colorSpaceReduce1(mat_image_src, mat_image_dst, 24);
//第二类:迭代器访问->封装东西比较多(根据场景)
// colorSpaceReduce2(mat_image_src, mat_image_dst, 24);
//第三类:动态地址计算
// colorSpaceReduce3(mat_image_src, mat_image_dst, 24);
//记录结束时间
double time_end = static_cast<double>(getTickCount());
//getTickFrequency()函数表示:返回CPU一秒钟所有的时间周期数,这样就能够按照秒单位计算出耗时
double time = (time_end - time_start) / getTickFrequency();
printf("耗时%f秒\n", time);
//第三步:显示图片
namedWindow("图片");
imshow("图片", mat_image_dst);
waitKey();
return 0;
}
4、ROI区域图片叠加?->感兴趣区域
POI(搜索点:感兴趣)
ROI(区域点:感兴趣)
上一次用的函数
换一种方式来玩图片叠加
5、ROI区域图像和图片混合?
1、ROI感兴趣区域?->方式一
图片叠加
2、计算数组加权和?->方式二
案例
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace cv;
using namespace std;
// ROI感兴趣区域
int main(int argc, const char * argv[]) {
// 第一步:创建一张图片->空白图片
Mat mat_image_src = imread("/Users/alange/Desktop/OpenCV/Images/scenery.png");
Mat mat_image_logo = imread("/Users/alange/Desktop/OpenCV/Images/strawberry.png");
// 第二步:灰度处理->加载一张灰度图片
Mat mat_image_mask = imread("/Users/alange/Desktop/OpenCV/Images/strawberry.png");
// 第三步:定义ROI区域
// 根据原始图片->得到src图片一块内存地址(指针)
Mat mat_image_roi = mat_image_src(Rect2i(0, 0, mat_image_logo.cols, mat_image_logo.rows));
// 第四步:拷贝到ROI区域
// 参数一:ROI区域
// 参数二:需要填充图片(水印)
mat_image_logo.copyTo(mat_image_roi, mat_image_mask);
// 第五步:显示图片
namedWindow("图片");
imshow("图片", mat_image_src);
waitKey();
return 0;
}
6、分离颜色通道和多通道图像混合?
说白了:对像素点->包含颜色进行分离(RGB,分离出来,单独R、G、B通道)->由原来多通道->单通道图片
1、颜色通道分离->split()函数?
2、颜色通道合并->merge()函数?
3、多通道图像混合?
4、图像对比度和亮度值调整?
案例:
颜色通道分离和合并
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
// 1、颜色通道分离->split()函数?
void colorChannelSeparation() {
//第一步:加载一张图片
Mat mat_image = imread("/Users/alange/Desktop/OpenCV/Images/strawberry.png");
//第二步:分离通道->字符串分隔
//参数一:原始图片->RGB彩色图片->多通道
//参数二:目标图片->数组
vector<Mat> channels;
split(mat_image, channels);
//获取蓝色通道->单通道->灰度图片(图片亮度不一样,灰度层度不一样)
Mat mat_image_blue = channels.at(0);
//获取绿色通道
Mat mat_image_green = channels.at(1);
//获取红色通道
Mat mat_image_red = channels.at(2);
namedWindow("颜色通道分离");
imshow("颜色通道分离", mat_image_red);
}
//2、颜色通道合并->merge()函数?
void colorChannelMerge() {
//第一步:加载一张图片
Mat mat_image = imread("/Users/alange/Desktop/OpenCV/Images/strawberry.png");
//第二步:分离通道->字符串分隔
//参数一:原始图片->RGB彩色图片->多通道
//参数二:目标图片->数组
vector<Mat> channels;
split(mat_image, channels);
//获取蓝色通道->单通道->灰度图片(图片亮度不一样,灰度层度不一样)
Mat mat_image_blue = channels.at(0);
//获取绿色通道
Mat mat_image_green = channels.at(1);
//获取红色通道
Mat mat_image_red = channels.at(2);
//通道合并
//参数一:原始数组->通道数组
//参数二:合并出来之后新的图片->目标图片
Mat mat_image_dst;
merge(channels, mat_image_dst);
namedWindow("颜色通道合并");
imshow("颜色通道合并", mat_image_dst);
}
int main(int argc, const char * argv[]) {
// 1、颜色通道分离->split()函数?
// colorChannelSeparation();
//2、颜色通道合并->merge()函数?
colorChannelMerge();
waitKey();
return 0;
}
多通道图像混合
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
// 3、多通道图像混合?
// 对两种图片进行叠加
// 图片A->彩色图片
// 图片B->彩色图片
// 叠加->图片B是->图片B的某一个通道进行叠加
void multichannelImageBlending(Mat& mat_image_src, Mat& mat_image_logo, int channelIndex){
vector<Mat> channels;
split(mat_image_src, channels);
// 获取指定通道
// 注意:是一个引用,mat_image_channel发生改变的时候,那么我们集合中元素也会发现改变
Mat mat_image_channel = channels.at(channelIndex);
// 获取叠加区域->ROI->感兴趣区域
Mat mat_image_roi = mat_image_channel(Rect2i(0, 0, mat_image_logo.cols, mat_image_logo.rows));
// 通过加权函数进行叠加图片
addWeighted(mat_image_roi, 0, mat_image_logo, 1, 0, mat_image_roi);
// 合并通道
merge(channels, mat_image_src);
}
int main(int argc, const char * argv[]) {
// 第一步:加载一张图片
Mat mat_image_src = imread("/Users/alange/Desktop/OpenCV/Images/scenery.png");
// 参数二:单通道
Mat mat_image_logo = imread("/Users/alange/Desktop/OpenCV/Images/strawberry.png", 0);
multichannelImageBlending(mat_image_src, mat_image_logo, 2);// 最后一个表示颜色:0、1、2
namedWindow("多通道图像混合");
imshow("多通道图像混合", mat_image_src);
waitKey();
return 0;
}
图像对比度和亮度值调整
#include <opencv2/core/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
// 4、图像对比度和亮度值调整?
// 4.1 什么是对比度?
//答案:对比度指的是一幅图像中明暗区域最亮的白和最暗的黑之间不同亮度层级的测量,差异范围越大代表对比越大,差异范围越小代表对比越小,好的对比率120:1就可容易地显示生动、丰富的色彩,当对比率高达300:1时,便可支持各阶的颜色。但对比率遭受和亮度相同的困境,现今尚无一套有效又公正的标准来衡量对比率,所以最好的辨识方式还是依靠使用者眼睛。
// 4.2 如何计算对比度和亮度结果图片?
// 公式:g(x) = a * f(x) + b;->标准对比度和亮度计算公式
// 每一个像素点,每一个颜色值都需要按照这个公式进行计算
// g(x)->表示输出图片->结果图片(调整对比度和亮度之后的图片)
// a->表示对比度
// f(x)->表示原始图片
// b->表示亮度
// 根据以上算法->推导出一个新的算法结构->获取像素点
// g(i, j) = a * f(i, j) + b;
// i表示第几行
// j表示第几列
// 获取像素点
// 实现功能
int main(int argc, const char * argv[]) {
// 第一步:加载一张图片
Mat mat_image_src = imread("/Users/alange/Desktop/OpenCV/Images/strawberry.png");
int rows = mat_image_src.rows;
int cols = mat_image_src.cols;
// 访问像素点->获取颜色值
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
// g(i, j) = a * f(i, j) + b;
// mat_image_src.at<Vec3b>(i, j)[0]->相当于g(i, j)
//saturate_cast保证颜色值范围
mat_image_src.at<Vec3b>(i, j)[0] = saturate_cast<uchar>(20 * 0.01 * mat_image_src.at<Vec3b>(i, j)[0] + 50);
mat_image_src.at<Vec3b>(i, j)[1] = saturate_cast<uchar>(20 * 0.01 * mat_image_src.at<Vec3b>(i, j)[1] + 50);
mat_image_src.at<Vec3b>(i, j)[2] = saturate_cast<uchar>(20 * 0.01 * mat_image_src.at<Vec3b>(i, j)[2] + 50);
}
}
namedWindow("图片显示");
imshow("图片显示", mat_image_src);
waitKey();
return 0;
}
网友评论