前言


  经过前面一节的怎样读取图片,我们可以做一些有趣的图像变换,下面我们首先介绍使用遍历的方法实现,然后我们使用内置的函数实现。

矩阵掩码实现


  矩阵掩码,和卷积神经网络中的卷积类似。一个例子如下:

  现在我们看看怎么实现:

  1. void Sharpen(const Mat& myImage, Mat& Result)
  2. {
  3. CV_Assert(myImage.depth() == CV_8U);
  4.  
  5. Result.create(myImage.size(), myImage.type());
  6. const int nChannels = myImage.channels();
  7.  
  8. for (int j=; j<myImage.rows-; ++j) { // 忽略第一和最后一行,防止数组越界
  9. const uchar * previous = myImage.ptr<uchar>(j-);
  10. const uchar * current = myImage.ptr<uchar>(j);
  11. const uchar * next = myImage.ptr<uchar>(j+);
  12.  
  13. uchar * output = Result.ptr<uchar>(j);
  14.  
  15. // 用连续存储的索引方法,所以每个点有三个uchar值
  16. // saturate_cast溢出保护
  17. for (int i=nChannels; i < nChannels * (myImage.cols-); ++i) {
  18. *output++ = saturate_cast<uchar>( * current[i]
  19. - current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);
  20. }
  21.  
  22. // 四周设置为0
  23. Result.row().setTo(Scalar());
  24. Result.row(Result.rows-).setTo(Scalar());
  25. Result.col().setTo(Scalar());
  26. Result.col(Result.cols-).setTo(Scalar());
  27. }
  28. }

我们看看结果:

因为掩码是增强中间,削弱四周,下面如果我们换掩码,使用内置函数看看效果:

  1. void SharpenUseFilter2D(const Mat& src, Mat& dst) {
  2. Mat kern = (Mat_<char>(, ) << ,-,,
  3. -,-1,5,
  4. ,-,);
  5. filter2D(src, dst, src.depth(), kern);
  6. }

下面是增强右边元素,减弱左边元素的效果(类似浮雕的效果,大家可以换着掩码来玩):

图片混合


  下面是线性混合操作:

  这个可以实现幻灯片的淡入淡出,通过修改alpha值。

  1. resize(src1, src1, cv::Size(, ));
  2. resize(src2, src2, cv::Size(, ));
  3.  
  4. namedWindow("");
  5.  
  6. beta = 1.0 - alpha;
  7. // dst = alpha * src1 + beta * src2 + gamma
  8. // 这里gamma设置为0.0
  9. addWeighted(src1, alpha, src2, beta, 0.0, dst);

  下面看看结果:

  

自己实现的简陋版本,除去错误检查等:

  1. void addWeight(Mat& src1, double w1, Mat& src2, double w2, Mat& dst)
  2. {
  3. dst.create(src1.size(), src2.type());
  4.  
  5. Mat_<Vec3b> _src1 = src1;
  6. Mat_<Vec3b> _src2 = src2;
  7. Mat_<Vec3b> _dst = dst;
  8.  
  9. for (int i=; i<src1.rows; ++i) {
  10. for (int j=; j<src1.cols; ++j) {
  11. for (int c=; c<; ++c)
  12. _dst(i, j)[c] = w1 * _src1(i, j)[c] + w2 * _src2(i, j)[c];
  13. }
  14. }
  15. }

改变图片的对比度和亮度


  1. Mat new_image = Mat::zeros(image.size(), image.type());
  2.  
  3. alpha = 1.2; // 1.0-3.0
  4. beta = ; // 0-100
  5.  
  6. for (int y=; y<image.rows; ++y) {
  7. for (int x=; x<image.cols; ++x) {
  8. for (int c=; c<; ++c)
  9. // Vec3b = [R, G, B]
  10. new_image.at<Vec3b>(y, x)[c] = saturate_cast<uchar>
  11. (alpha * (image.at<Vec3b>(y, x)[c]) + beta);
  12. }
  13. }
  14.  
  15. Mat new_image_2 = Mat::zeros(image.size(), image.type());
  16. // -1 代表输入输出类型一样
  17. image.convertTo(new_image_2, -, alpha, beta);

结果如下:

基本绘图


可以查阅一下网址http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/basic_geometric_drawing/basic_geometric_drawing.html

  • 如何用 Point 在图像中定义 2D 点
  • 如何以及为何使用 Scalar
  • 用OpenCV的函数 line直线
  • 用OpenCV的函数 ellipse椭圆
  • 用OpenCV的函数 rectangle矩形
  • 用OpenCV的函数 circle
  • 用OpenCV的函数 fillPoly填充的多边形

而产生随机数可以使用 RNG rng( 0xFFFFFFFF ); 这样就可以生成符合一定分布的数,例如高斯分布 rng.uniform(1, 10);

快速傅里叶变换


(上图来源:http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/discrete_fourier_transform/discrete_fourier_transform.html)

  1. // 当图片大小是2,3,5的倍数的时候,傅里叶变换表现最高
  2. // 所以先获得最好的尺寸 m,n
  3. // 然后再进行填充
  4. Mat padded;
  5. int m = getOptimalDFTSize(I.rows);
  6. int n = getOptimalDFTSize(I.cols);
  7. copyMakeBorder(I, padded, m-I.rows, , n-I.cols, , BORDER_CONSTANT, Scalar::all());
  8.  
  9. // 对于每个原图,结果是两个图像值
  10. // 因为需要储存复数部分,所以需要添加一个额外通道
  11. // 存到complexI中
  12. Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
  13. Mat complexI;
  14. merge(planes, , complexI);
  15.  
  16. dft(complexI, complexI);
  17.  
  18. // 将复数转化成幅度
  19. split(complexI, planes); // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
  20. magnitude(planes[], planes[], planes[]); // planes[0] = sqrt([0]**2 + [1]**2)
  21. Mat magI = planes[];
  22.  
  23. // 为了使变化可以观察,高低连续变换,需要尺度缩放
  24. magI += Scalar::all();
  25. log(magI, magI);
  26.  
  27. // 剪切和重分布图像象限
  28. magI = magI(Rect(, , magI.cols & -, magI.rows & -));
  29.  
  30. int cx = magI.cols/;
  31. int cy = magI.rows/;
  32.  
  33. Mat q0(magI, Rect(, , cx, cy)); // 上左
  34. Mat q1(magI, Rect(cx, , cx, cy));// 上右
  35. Mat q2(magI, Rect(, cy, cx, cy));// 下左
  36. Mat q3(magI, Rect(cx, cy, cx, cy));// 下右
  37.  
  38. Mat tmp;
  39. q0.copyTo(tmp);
  40. q3.copyTo(q0);
  41. tmp.copyTo(q3);
  42.  
  43. q1.copyTo(tmp);
  44. q2.copyTo(q1);
  45. tmp.copyTo(q2);
  46.  
  47. // 归一化
  48. normalize(magI, magI, , , CV_MINMAX);

输出为XML或者YAML文件


  输出为XML或者YAML需要借助 FileStorage 和 FileNode 。

  首先声明文件名

  1. string filename = "store.xml";

  对于写入:

  1. FileStorage fs(filename, FileStorage::WRITE); // 记得释放, fs.release();

  对于读取:

  1. FileStorage fs;
  2. fs.open(filename, FileStorage::READ);

内置对象的写入读取

  1. // 写入
  2. fs << "iterationNr" << ;
  3.  
  4. //读取
  5. int itNr;
  6. itNr = (int) fs["iterationNr"];

存储效果如下:

序列的写入读取

  1. // 序列写入需要使用[]
  2. fs << "strings" << "[";
  3. fs << "image1.jpg" << "Awesoneness" << "babonn.jpg";
  4. fs << "]";
  5.  
  6. // 读取需要迭代器
  7. FileNode n = fs["strings"];
  8. if (n.type() != FileNode::SEQ) {
  9. cerr << "string is not a sequence!" << endl;
  10. return ;
  11. }
  12. FileNodeIterator it = n.begin(), it_end = n.end();
  13. for (; it != it_end; ++it)
  14. cout << (string)*it << endl;

存储效果:

Map的写入读取

  1. // map的写入需要{}
  2. fs << "Mapping";
  3. fs << "{" << "One" << ;
  4. fs << "Two" << << "}";
  5.  
  6. // 读取
  7. n = fs["Mapping"];
  8. cout << "Two " << (int)(n["Two"]) << ";";
  9. cout << "One " << (int)(n["One"]) << endl << endl;

存储效果:

矩阵的写入读取

  1. Mat R = Mat_<uchar>::eye(, );
  2. fs << "R" << R;
  3.  
  4. Mat R;
  5. fs["R"] >> R;

存储效果:

自定义对象的写入和读取

首先自定义对象:

  1. class MyData
  2. {
  3. public:
  4. MyData(): A(), X(), id() {};
  5.  
  6. explicit MyData(int): A(), X(CV_PI), id("mydata1234") {};
  7.  
  8. void write(FileStorage& fs) const {
  9. fs << "{" << "A" << A << "X" << X <<"id" << id << "}"; // 自定义写入
  10. }
  11.  
  12. void read(const FileNode& node) { // 自定义读取
  13. A = (int)node["A"];
  14. X = (double)node["X"];
  15. id = (string)node["id"];
  16. }
  17. public:
  18. int A;
  19. double X;
  20. string id;
  21. };

然后还要重载全局的读取和写入函数:

  1. static void write(FileStorage& fs, const std::string&, const MyData& x) {
  2. x.write(fs);
  3. }
  4.  
  5. static void read(const FileNode& node, MyData& x, const MyData& default_value = MyData()) {
  6. if (node.empty())
  7. x = default_value;
  8. else
  9. x.read(node);
  10. }

这样就可以写入和读取:

  1. MyData m();
  2. fs << "MyData" << m;
  3.  
  4. fs["MyData"] >> m;

存储效果如下:

和OpenCV1混合

详见http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.html

OpenCv 2.4.9 (二) 核心函数的更多相关文章

  1. OpenCV使用FindContours进行二维码定位

    我使用过FindContours,而且知道有能够直接寻找联通区域的函数.但是我使用的大多只是"最大轮廓"或者"轮廓数目"这些数据.其实轮廓还有另一个很重要的性质 ...

  2. opencv学习笔记(二)寻找轮廓

    opencv学习笔记(二)寻找轮廓 opencv中使用findContours函数来查找轮廓,这个函数的原型为: void findContours(InputOutputArray image, O ...

  3. 【OpenCV入门教程之二】 一览众山小:OpenCV 2.4.8组件结构全解析

    转自: http://blog.csdn.net/poem_qianmo/article/details/19925819 本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接:ht ...

  4. 【OpenGL游戏开发之三】OpenGl核心函数库汇总

    OpenGl核心函数库 glAccum 操作累加缓冲区 glAddSwapHintRectWIN 定义一组被SwapBuffers拷贝的三角形 glAlphaFunc允许设置alpha检测功能 glA ...

  5. zepto学习之路--核心函数$()的实现

    $()可以说是jquery的精华了,为dom操作带来了极大的灵活和方便.zepto号称“移动版的jquery”,那么它是怎么来实现这个核心函数呢?我们来详细探讨下. 1.首先,我们看下zepto中它是 ...

  6. Ext.Js核心函数( 三)

    ExtJs 核心函数简介 1.ExtJs提供的常用函数2.get.fly.getCmp.getDom.getBody.getDoc3.query函数和select函数4.encode函数和decode ...

  7. 【OpenCV入门教程之二】 一览众山小:OpenCV 2.4.8 or OpenCV 2.4.9组件结构全解析

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/19925819 作者:毛星云 ...

  8. 基于Opencv识别,矫正二维码(C++)

    参考链接 [ 基于opencv 识别.定位二维码 (c++版) ](https://www.cnblogs.com/yuanchenhui/p/opencv_qr.html) OpenCV4.0.0二 ...

  9. 解密jQuery内核 DOM操作的核心函数domManip

    domManip是什么 dom即Dom元素,Manip是Manipulate的缩写,连在一起就是Dom操作的意思. .domManip()是jQuery DOM操作的核心函数 对封装的节点操作做了参数 ...

随机推荐

  1. osx c++连接mysql

    最近想尝试一下使用c++连接mysql数据库.使用封装过后的mysql库mysql++访问mysql数据库更加简单,下述讲述的是如何在osx上搭建连接mysql的环境. 首先需要安装mysql++,感 ...

  2. python 自动化之路 day 13

    本节内容参考博客: http://www.cnblogs.com/wupeiqi/articles/5132791.html http://www.cnblogs.com/wupeiqi/articl ...

  3. 上传文件到linux服务器

    可以在SecureCRT下上传 先用使用命令下载一个文件:yum install lrzsz -y 然后在跳转到要保存的目录 最后,拖拽文件到secureCRT中即可

  4. 5分钟了解MySQL5.7的Online DDL雷区

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://suifu.blog.51cto.com/9167728/1855872 Part ...

  5. java 二维码

    在http://www.ostools.net/qr看到了一个生成二维码的工具,于是就产生了一个想法: 为什么自己不做一个二维码的生成和解析工具呢?花了一个多钟的时间,嘿嘿,就做出来啦... 先来看看 ...

  6. easyui datagrid行中点击a标签链接,行被选中,但是获取不到对应的参数

    easyui中使用比较多的就是datagrid了,表格中添加连接,点击跳转,为比较常用的方式;往往在点及标签后调用getSeleted方法会失效; 一.初始代码: {field: 'id',title ...

  7. mySQL内存及虚拟内存优化设置

    为了装mysql环境测试,装上后发现启动后mysql占用了很大的虚拟内存,达8百多兆.网上搜索了一下,得到高人指点my.ini.再也没见再详细的了..只好打开my.ini逐行的啃,虽然英文差了点,不过 ...

  8. Tamper Data 安装与使用

    Tamper Data概览   注意:我将会讲述一些有关Tamper Data的基本常识,包括它的基本功能,如何安装等. Tamper Data是什么?   Tamper Data 的真实含义,即&q ...

  9. emmet学习笔记

    Emment语法使用:按table键的结果1.初始化:(HTML文档需要包含一些固定的标签,比如<html>.<head>.<body>等). html:或! :用 ...

  10. HDU-2573-Typing

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=2573 这题把%s与gets()的输入法搞混了一直感觉没有错,就是找不出哪里错了, 题目思路不是很难. ...