zoukankan      html  css  js  c++  java
  • OpenCV学习(9.2)

    离散傅里叶变换的运行速度和图片的尺寸有很大关系。图像尺寸是 2,3,5的整数倍时计算速度最快。因此为了加快速度,往往通过添凑新的边缘像素的方法获得最佳图像尺寸。
    但毕竟计算速度加快的同时需要计算的像素也增多了,所以最优的图像尺寸往往是利用getOptimalDFTSize()函数获得,再用copyMakeBorder()填充边缘像素。

    傅里叶变化的结果是复数,所以对于每个原图像值会有两个图像值。此外,频域值范围远远超过空间值,因此至少要储存在float格式中,所以我们将输入图像转换成浮点类型,多加一个额外通道来储存复数部分。

    将复数转换为幅值。复数包含实数部分和虚数部分,对应的幅度可以表示为M=√Re(DET(I))^2+Im(DFT(I))^2;

    傅里叶变换的幅度值范围大到不适合在屏幕上显示。为了在屏幕上凸显出高低变化的连续性,用对数尺度来替换线性尺度。公式如下:
    M1=log(1+M);

    因为扩大了图像,所以需要将新像素剔除。

    归一化,将幅度变化到可显示范围。

    程序代码:
    // 离散傅里叶变换.cpp : 定义控制台*应用程序的入口点。
    //
    #include "stdafx.h"

    using namespace std;
    using namespace cv;

    int main()
    {
    Mat srcImage = imread("1.jpg", 0);//以灰度模式读取图像并显示
    if (!srcImage.data) {
    printf("读取图片错误,图片可能不存在! ");
    return 0;
    }
    imshow("原始图像", srcImage);

    //扩充到最佳尺寸,用0填充
    int m = getOptimalDFTSize(I.rows);
    int n = getOptimalDFTSize(I.cols);
    //将添加的像素初始化为0
    Mat padded;
    copyMakeBorder(I, padded, 0, m - I.rows, n - I.cols, BORDER_CONSTANT);
    Scalar::all((0));

    Mat planes[] = { Mat_<float>(padded), Mat::zeros(padded.size()),CV_32F };
    Mat complexI;
    merge(planes, 2, complexI);

    //进行离散傅里叶变换
    dft(complexI, complexI);

    //复数转化为幅值
    split(complexI, planes);//分成单通道
    planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
    magnitude(planes[0], planse[1], planes[0]);
    Mat magnitudeImage = planes[0];

    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));
    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);

    normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX);

    imshow("频谱幅值", magnitudeImage);
    )


    return 0;
    }

    文件输入输出(XML,YAML)
    XML,即eXtensible Markup Language,翻译成中文为“可扩展标识语言”。
    同时,XML是一种元标记语言,开发者可以根据自身需要定义自己的标记。任何满足XML命名规则的名称都可以标记。此外,XML是一种语义/结构化语言,描述了文档的结构和语义。
    YAML以数据为中心,而不是以置标语言为重点。可读性高,用来表达资料序列。

    FileStorage类操作文件的使用引导。
    写入或者读取数据的过程:
    (1)实例化一个FileStorage类的对象,
    (2)使用流操作符<<进行文件写入操作,或者>>进行文件读取操作。
    (3)使用FileStorage::release()函数析构FileStorage
    1.打开XML/YAML文件
    准备写入:
    XML和YAML文件的存储类是FileStorage,其中封装了所有相关的信息。它是OpenCV从文件中读取或写入数据必须使用的。
    构造方法:
    FileStorage::FileStorage()
    FileStorage::FileStorage(const string& source, int flags, const string& encoding=string())
    构造函数在实际使用中方法一般有两种:
    1)对于第一种不带参数的构造函数,使用其成员函数FileStorage::open进行写操作:
    FileStorage fs;
    fs.open("abc.xml",FileStorage::WRITE);
    2)第二种带参数的构造函数:
    FileStorage fs("abc.xml",FileStorage:WRITE);

    准备读入:
    读操作使用FileStorage::READ标识符即可,有两种方式。
    1)FileStorage fs("abc.xml",FileStorage::READ);
    2)FileStorage fs;
    fs.open("abc.xml",FileStorage::READ);

    对YAML文件,把xml换成yaml即可。

    2.进行文件读写操作
    1)文本和数字的输入输出
    定义好FileStorage类后,写入文件使用“<<”运算符,例如:
    fs << "iterationNr" << 100;
    读取文件使用“>>”运算符,例如:
    int itNr;
    fs["iterationNr"] >> itNr;
    itNr = (int) fs{"iterationNr"};
    2)数据结构的输入和输出
    和基本的C++形式相同。
    Mat R = Mat_<uchar >::eye (3,3),
    Mat T = Mat_<double>::zeors(3,1);
    //Mat中写入数据
    fs << "R" << R;
    fs << "T" << T;
    //Mat中读取数据
    fs["R"] >> R;
    fs["T"] >> T;

    vector(arrays)和maps的输入和输出
    对于vector结构的输入和输出,要注意在第一个元素前加上“[”,在最后一个元素前加上“]”。例如:
    fs << "strings" << "[";//开始读入string文本序列
    fs << "image1.jpg" << "Awesomeness" << "baboon.jpg";
    fs << "]";//关闭序列

    而对于map结构的操作,使用的符号是“{”“}”,例如:
    fs << "Mapping";
    fs << "{" << "One" << 1;
    fs << "Two" << 2 << "}";
    读取这些结构时会用到FileNode和FileNodeIterator数据结构。对FileStorage类的“[”“]”操作符会返回FileNode数据类型:对于一连串的node,可以使用FileNodeIterator结构,例如:
    FileNode n = fs["strings"];//读取字符串序列以得到节点
    if (n.type() != FileNode::SEQ)
    {
    cerr << "字符串不是一个序列!" << endl;
    return 1;
    }

    FileNodeIterator it = n.begin(), it_end = n.end(); //遍历节点
    for (; it != it_end; ++it)
    cout << (string)*it << endl;

    文件关闭
    文件关闭会在FileStorage类销毁时自动进行,但我们也可显式调用其析构函数FileStorage::release()实现。
    调用过程:
    fs.release();

    // 文件输入输出.cpp : 定义控制台应用程序的入口点。
    //

    #include "stdafx.h"

    using namespace std;
    using namespace cv;

    int main()
    {
    FileStorage fs("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) & i);
    fs << "]" << "}";
    }
    fs << "]";
    fs.release();

    //开始读取操作
    system("color 6F");

    FileStorage fs2("test.yaml", FileStorage::READ);

    //第一种方法,对FileNode操作
    int frameCount = (int)fs2 { "frameCount" };

    std::string date;
    //第二种方法,使用FileNode运算符>>
    fs2{ "calibrationDate" } >> date;

    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;
    std::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();

    return 0;
    }

    线性滤波:方框滤波、均值滤波、高斯滤波
    平滑处理:也称模糊处理,是一种简单且使用频率很高的图像处理方法。用途广泛,最常见的是用来减少图像上的噪点或者失真。涉及到降低图像分辨率时,平滑处理是非常好用的方法。

    图像滤波与滤波器
    图像滤波指在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制。
    消除图像中的噪声成分叫作图像的平滑化或滤波操作。信号或图像的能量大部分集中在幅度谱的低频和中频段,而较高频段有用的信息经常被噪声掩盖,所以一个能降低高频成分幅度的滤波器就能够减弱噪声的影响。
    图像滤波的目的:抽出对象的特征作为图像识别的特征模式;为适应图像处理的要求,消除图像数字化时所混入的噪声。
    滤波处理的要求:不能损坏图像的轮廓及边缘等重要信息;使图像清晰视觉效果好。
    平滑滤波是低频增强的空间域滤波技术。目的是模糊或消除噪音。

    空间域的平滑滤波一般采用简单平均法进行,求邻近像元点的平均亮度值。邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好;但邻域过大会让平滑后损失的边缘信息损失越大,使输出的图像变得模糊。

    新版本的OpenCV提供了如下5种常用的图像平滑处理操作方法,分别被封装在单独函数中,使用起来相当方便。
    方框滤波:BoxBlur
    均值滤波:Blur
    高斯滤波:GaussianBlur
    中值滤波:medianBlur
    双边滤波:bilateralFilter

    线性滤波器:用于剔除输入信号中不想要的频率或者从许多频率中选择一个想要的频率。
    低通滤波器:允许低频率通过;
    高通滤波器:允许高频率通过;
    带通滤波器:允许一定范围频率通过;
    带阻滤波器:阻止一定范围频率通过并允许其他频率通过;
    全通滤波器:允许所有频率通过,仅仅改变相位关系;
    陷波滤波器:阻止一个狭窄频率范围通过,是一种特殊带阻滤波器。

  • 相关阅读:
    剑指offer(链表)
    设计模式
    谷歌Colab使用(深度学习)
    Consul与python API注册与注销
    【日志收集】之Loki
    【消息队列】之 RabbitMQ安装
    【消息队列】之NSQ安装
    Docker基础
    Python3 , Mysql5.7 , Smb 安装
    SkyWalking部署
  • 原文地址:https://www.cnblogs.com/Shymuel/p/9576875.html
Copyright © 2011-2022 走看看