《OpenCV3编程入门》学习笔记五:core组件进阶
来源:互联网 发布:网店数据分析毕业论文 编辑:程序博客网 时间:2024/06/03 09:27
一:内容介绍
本节主要介绍OpenCV的core模块基础部分:
1. 访问像素的一些方法
2. 图像混合的方法
3. 图像对比度、亮度调整
4. 离散傅里叶变换
5. XML和YAML文件的读取写入
二:学习笔记
- 本章仍是介绍了一些core模块的一些比较简单的应用。
- 在本章的大部分内容中,都透着用数字看待图像的观点。面对一副图像,别人看到的是美丽的画面及展示的内容,你还应该清醒的看到图像背后隐藏的数字,并且看的越深刻越好。本章中,在第三块源码及解析的提示部分大多添加了这些图像变换针对于每个像素的数学公式。都比较简单。
- 个人在示例7图像对比度亮度调整中加入了CLAHE算法的使用示例。CLAHE算法(Contrast Limited Adaptive Histogram Equalization,自适应直方图均衡化)在OpenCV中的使用。关于直方图均衡化和规定化这一块可参见:OpenCV2学习笔记(二):图像的直方图http://blog.csdn.net/liyuefeilong/article/details/43709359 ,《数字图像处理与机器视觉》3.7节直方图均衡化 3.8节直方图规定化(匹配) 和《数字图像处理原理与实践》2.5节灰度均衡和2.6节直方图规定化。
三:相关源码及解析
本章示例较多,示例列表:
1.用指针访问像素
2.用迭代器访问像素
3.用动态地址计算配合at访问像素
4.遍历图像像素的14种方法
5.初级图像混合
6.多通道图像混合
7.图像对比度、亮度值调整
8.离散傅里叶变换
9.XML和YAML文件写入
10.XML和YAML文件读取
1. 用指针访问图像中的每个像素,缩小颜色空间(即减少图像中颜色的种类)
源码:
#include<opencv2/opencv.hpp>#include<iostream>using namespace cv;using namespace std;//颜色空间缩减函数void colorReduce(Mat& inputImage, Mat& outputImage, int div);int main(){ Mat srcImage = imread("poster_tower.jpg"); imshow("【原始图像】", srcImage); Mat dstImage; dstImage.create(srcImage.rows, srcImage.cols, srcImage.type()); double time0 = static_cast<double>(getTickCount()); colorReduce(srcImage, dstImage, 64); time0 = ((double)getTickCount()-time0)/getTickFrequency(); cout << "此方法运行时间为:" << time0 << "秒" << endl; imshow("【效果图】", dstImage); waitKey(6000); return 0;}void colorReduce(Mat& inputImage, Mat& outputImage, int div){ outputImage = inputImage.clone(); int rowNumber = outputImage.rows; int colNumber = outputImage.cols*outputImage.channels(); //每一行元素的个数 for (int i = 0; i < rowNumber; i++) { uchar* data = outputImage.ptr<uchar>(i); for (int j = 0; j < colNumber; j++) { data[j] = data[j] / div * div + div / 2; } }}
素材:
效果图:
提示:
对应数学表达公式:I(x,y)= I(x,y)/div *div + div/2;
2 . 用迭代器访问像素,缩小颜色空间(即减少图像中颜色的种类)
源码:
#include<opencv2/opencv.hpp>#include<iostream>using namespace cv;using namespace std;void colorReduce(Mat& inputImage, Mat& outputImage, int div);int main(){ Mat srcImage = imread("poster_tower.jpg"); imshow("【原始图像】", srcImage); Mat dstImage; dstImage.create(srcImage.rows, srcImage.cols, srcImage.type()); double time0 = static_cast<double>(getTickCount()); colorReduce(srcImage, dstImage, 64); time0 = ((double)getTickCount() - time0) / getTickFrequency(); cout << "此方法运行时间为:" << time0 << "秒" << endl; imshow("【效果图】", dstImage); waitKey(6000); return 0;}//缩减颜色空间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; }}
素材:
同1
效果图:
同1
提示:
对应数学表达公式:I(x,y)= I(x,y)/div *div + div/2;
3 . 用动态地址计算配合at访问像素,缩小颜色空间(即减少图像中颜色的种类)/font>
源码:
#include<opencv2/opencv.hpp>#include<iostream>using namespace cv;using namespace std;void colorReduce(Mat& inputImage, Mat& outputImage, int div);int main(){ Mat srcImage = imread("poster_tower.jpg"); imshow("【原始图像】", srcImage); Mat dstImage; dstImage.create(srcImage.rows, srcImage.cols, srcImage.type()); double time0 = static_cast<double>(getTickCount()); colorReduce(srcImage, dstImage, 64); time0 = ((double)getTickCount() - time0) / getTickFrequency(); cout << "此方法运行时间为:" << time0 << "秒" << endl; imshow("【效果图】", dstImage); waitKey(6000); return 0;}//缩减颜色空间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++) { 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; //红色通道 } }}
素材:
同1
效果图:
同1
提示:
对应数学表达公式:I(x,y)= I(x,y)/div *div + div/2;
4 . 遍历图像像素的14种方法
源码:
素材:
效果图:
提示:
5 . 初级图像混合
源码:
#include<opencv2/opencv.hpp>#include<iostream>using namespace cv;using namespace std;int main() { Mat srcImage = imread("poster_dota.jpg"); imshow("【原图】", srcImage); Mat logoImage = imread("poster_dota_logo.jpg"); Mat imageROI = srcImage(Rect(200, 250, logoImage.cols, logoImage.rows));// Mat mask = Mat::zeros(logoImage.cols, logoImage.rows, CV_8UC1); //模板为单通道即可 Mat mask = Mat::ones(logoImage.cols, logoImage.rows, CV_8UC1); logoImage.copyTo(imageROI, mask); imshow("【利用ROI实现图像叠加示例窗口】", srcImage); waitKey(6000); return 0;}
素材:
效果图:
提示:
copyto()函数,mask中元素值为非0时才copy
6 . 多通道图像混合
源码:
#include<opencv2/opencv.hpp>#include<iostream>using namespace cv;using namespace std;int main(){ Mat srcImage, logoImage; vector<Mat> channels; //多通道混合——蓝色分量 Mat imageBlueChannel; logoImage = imread("poster_dota_logo.jpg", IMREAD_GRAYSCALE); //载入灰度图 srcImage = imread("poster_dota.jpg"); split(srcImage, channels); imageBlueChannel = channels.at(0); //注意此处是引用,修改其中一个另一个跟着变 addWeighted(imageBlueChannel(Rect(500, 250, logoImage.cols, logoImage.rows) ), 1.0, logoImage, 0.5, 0, imageBlueChannel(Rect(500, 250, logoImage.cols, logoImage.rows) ) ); merge(channels, srcImage); imshow("【游戏原画+蓝色通道logo】", srcImage); //多通道混合——绿色分量 Mat imageGreenChannel; logoImage = imread("poster_dota_logo.jpg", IMREAD_GRAYSCALE); //载入灰度图 srcImage = imread("poster_dota.jpg"); split(srcImage, channels); imageGreenChannel = channels.at(1); //注意此处是引用,修改其中一个另一个跟着变 addWeighted(imageGreenChannel(Rect(500, 250, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0, imageGreenChannel(Rect(500, 250, logoImage.cols, logoImage.rows))); merge(channels, srcImage); imshow("【游戏原画+绿色通道logo】", srcImage); //多通道混合——红色分量 Mat imageRedChannel; logoImage = imread("poster_dota_logo.jpg", IMREAD_GRAYSCALE); //载入灰度图 srcImage = imread("poster_dota.jpg"); split(srcImage, channels); imageRedChannel = channels.at(2); //注意此处是引用,修改其中一个另一个跟着变 addWeighted(imageRedChannel(Rect(500, 250, logoImage.cols, logoImage.rows)), 1.0, logoImage, 0.5, 0, imageRedChannel(Rect(500, 250, logoImage.cols, logoImage.rows))); merge(channels, srcImage); imshow("【游戏原画+红色通道logo】", srcImage); waitKey(16000); return 0;}
素材:
同5
效果图:
提示:
5中时直接将像素叠加上去,此例程中先用split()分离通道,然后在单通道上使用addWeighted()叠加,最后再使用merge()将各通道合并。
7 . 图像对比度、亮度值调整
源码:
#include<opencv2/opencv.hpp>#include<iostream>using namespace cv;using namespace std;static void on_ContrastAndBright(int, void* );void useCLAHE();int g_nContrastValue=80; //对比度值int g_nBrightValue=80; //亮度值Mat g_srcImage, g_dstImage;int main(){ g_srcImage = imread("poster_girl_1.jpg"); useCLAHE(); g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type()); namedWindow("【效果图窗口】"); createTrackbar("对比度:", "【效果图窗口】", &g_nContrastValue, 300, on_ContrastAndBright); createTrackbar("亮度:", "【效果图窗口】", &g_nBrightValue, 200, on_ContrastAndBright); on_ContrastAndBright(0, 0); //进入中断一次 while (char(waitKey(1)) != 'q'); return 0;}//此函数传入的参数k即为g_nContrastValue或g_nBrightValuestatic void on_ContrastAndBright(int k, void*) { 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);}//Contrast Limited Adaptive histgram equalization/CLAHEvoid useCLAHE() { Mat grayImage; cvtColor(g_srcImage, grayImage, COLOR_RGB2GRAY); imshow("【灰度图】", grayImage); Ptr<CLAHE> clahe = createCLAHE(); clahe->setClipLimit(4); Mat dst; clahe->apply(grayImage, dst); imshow("【CLAHE效果图】", dst);}
素材:
效果图:
提示:
改变图像亮度和对比度的数学表达式:I(x, y) = ContrastValue * I(x, y) + BrightValue;
本示例中在书本例程的基础上添加了CLAHE算法(Contrast Limited Adaptive Histogram Equalization,自适应直方图均衡化)在OpenCV中的使用。关于直方图均衡化和规定化这一块可参见:《数字图像处理与机器视觉》3.7节直方图均衡化 3.8节直方图规定化(匹配) 和《数字图像处理原理与实践》2.5节灰度均衡和2.6节直方图规定化。
8 . 散傅里叶变换
源码:
#include<opencv2/opencv.hpp>#include<iostream>using namespace std;using namespace cv;int main(){ //读取灰度图 Mat srcImage = imread("poster_meal.jpg", IMREAD_GRAYSCALE); imshow("【原始图像】", srcImage); //将输入图像延扩到最佳尺寸,边界用0补充 int m = getOptimalDFTSize(srcImage.rows); int n = getOptimalDFTSize(srcImage.cols); Mat padded; copyMakeBorder(srcImage, padded, 0, m - srcImage.rows, 0, n - srcImage.cols, BORDER_CONSTANT, Scalar::all(0)); //为傅里叶变换的结果(实部和虚部)分配存储空间, 将planes数组组合合并成一个多通道的数组complesI Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F) }; Mat complexI; merge(planes, 2, complexI); //进行就地离散傅里叶变换 dft(complexI, complexI); //将复数转换为幅值,即=>log(1+sqrt(Re(DFT(I))^2+Im(DFT(I))^2)) split(complexI, planes);//planes[0]=Re(DFT(I)), planes[1]=Im(DFT(I)) magnitude(planes[0], planes[1], planes[0]); //planes[0]=magnitude Mat magnitudeImage = planes[0]; //进行对数尺度(logarithm scale)缩放 magnitudeImage += Scalar::all(1); log(magnitudeImage, magnitudeImage); //求自然对数 //剪切(若有奇数行或奇数列,进行频谱裁剪)和重分布幅度图像限 magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols&-2, magnitudeImage.rows&-2)); int cx = magnitudeImage.cols / 2;//重新排列傅里叶图像中的象限,使得原点位于图像中心 int cy = magnitudeImage.rows / 2; Mat q0(magnitudeImage, Rect(0, 0, cx, cy)); //ROI区域的左上 Mat q1(magnitudeImage, Rect(cx, 0, cx, cy)); //右上 Mat q2(magnitudeImage, Rect(0, cy, cx, cy)); //左下 Mat q3(magnitudeImage, Rect(cx, cy, cx, cy)); //右下 Mat tmp; //交换象限 q0.copyTo(tmp); q3.copyTo(q0); tmp.copyTo(q3); q1.copyTo(tmp); q2.copyTo(q1); tmp.copyTo(q2); //归一化,用0和1之间的浮点值将矩阵变换为可视的图像格式 normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX); //显示效果图 imshow("【频谱幅值】", magnitudeImage); waitKey(); return 0;}
素材:
效果图:
提示:
傅里叶变换,难啃的一块骨头。
9 . XML和YAML文件写入
源码:
#include<opencv2/opencv.hpp>#include<iostream>#include<time.h>using namespace std;using namespace cv;int main(){ FileStorage fs("yaml_test.yaml", FileStorage::WRITE); //初始化 //开始写入文件 fs << "frameCount" << 5; time_t rawtime; time(&rawtime); fs << "CalibrationDate" << asctime(localtime(&rawtime)); Mat cameraMatrix = (Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1); Mat distCoeffs = (Mat_<double>(5, 1)<<0.1, 0.01, -0.001, 0, 0); fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs; fs << "features" << "["; for (int i = 0; i < 3; i++) { int x = rand() % 640; int y = rand() % 480; uchar lbp = rand() % 256; fs << "{:" << "x" << x << "y" << y << "lbp" << "[:"; for (int j = 0; j < 8; j++) fs << ((lbp>>j)&1); fs << "]" << "}"; } fs << "]"; fs.release(); cout << "文件读写完毕,请在工程目录下查看生成的文件" << endl; waitKey(); return 0;}
素材:
无
效果图:
无
提示:
YAML用的还是挺广泛的。
10 . XML和YAML文件读取
源码:
#include<opencv2/opencv.hpp>#include<iostream>using namespace std;using namespace cv;int main(){ FileStorage fs2("yaml_test.yaml", FileStorage::READ); int frameCount = (int)fs2["frameCount"]; //第一种方法,对FileNode操作 string date; fs2["calibrationDate"] >> date; //第二种方法,使用FileNode运算符>> Mat cameraMatrix2, distCoeffs2; fs2["cameraMatrix"] >> cameraMatrix2; fs2["distCoeffs"] >> distCoeffs2; cout << "frameCount: " << frameCount << endl << "calibration date: " << date << endl << "camera matrix: " << cameraMatrix2 << endl << "distortion coeffs: " << distCoeffs2 << endl; FileNode features = fs2["features"]; FileNodeIterator it = features.begin(), it_end = features.end(); int idx = 0; vector<uchar> lbpval; //使用FileNodeIterator遍历序列 for (; it != it_end; ++it, idx++) { cout << "feature #" << idx << ": "; cout << "x=" << (int)(*it)["x"] << ", y=" << (int)(*it)["y"] << ", lbp: ("; (*it)["lbp"] >> lbpval; for (int i = 0; i < (int)lbpval.size(); i++) cout << " " << (int)lbpval[i]; cout << ")" << endl; } fs2.release(); cout << "文件读取完毕,请输入任意键结束程序" << endl; return 0;}
素材:
无
效果图:
无
提示:
YAML用的还是挺广泛的。
- 《OpenCV3编程入门》学习笔记五:core组件进阶
- 《OpenCV3编程入门》第五章core组件进阶学习笔记part2
- 《OpenCV3编程入门》学习笔记四:初探core组件
- 《OpenCV编程入门》第五章core组件进阶学习笔记part 1
- OpenCV3编程入门学习笔记
- OpenCV3——core组件进阶
- opencv3学习笔记(三)——core组件
- Opencv3编程入门学习笔记(一)
- Opencv3编程入门学习笔记(五)之通道分离(split)与合并(merge)
- 图像识别(4)——《OpenCV3编程入门-毛星云》第二部分 初探core组件
- 【OpenCV】OpenCV3的第四天——core组件进阶
- 《OpenCV3编程入门》学习笔记之官方例程学习
- C++学习笔记(五)opencv在win下的使用 —参考浅墨opencv3编程入门
- 【openCV入门之四】 Core组件进阶
- opencv3学习笔记(三)——core组件之模块operations on array
- 《OpenCV3编程入门》学习笔记一:邂逅OpenCV
- 《OpenCV3编程入门》学习笔记二:快速上手OpenCV
- 《OpenCV3编程入门》学习笔记三:HighGUI图形用户界面
- 宏定义详解
- UML类图几种关系的总结
- 深入分析Android native exception框架---native编译
- 练手项目的小问题
- Linux用户管理
- 《OpenCV3编程入门》学习笔记五:core组件进阶
- Android四大基本组件介绍与生命周期
- WPF学习笔记:(一)数据绑定与DataContext
- Leetcode之Longest Valid Parentheses
- Symfony2一级目录结构
- C++每日一练(多重继承)
- C++输入日期判断是星期几
- 「枫桥夜泊」一诗
- 基于snowflake的ID生成器