C# EMGU 3.4.1学习笔记(一)离散傅里叶变换

最近在学习OpenCV3编程入门(毛星云编著)这本书,书中的示例都是用C++写的,而自己用的是C#,配合EMGU 3.4.1版本,网上的资料非常非常少,很多时候遇到问题,都找不到解决方法。为了记录自己的学习过程,也为了后来的学习者能有资料参考,在此分享自己转化后的C# EMGU3.4.1版本的代码。

下面是原书中5.5.8章节的一个示例程序:离散傅里叶变换,我用C#配合EMGU 3.4.1.2976版本,进行了重新书写,过程中遇到很多问题,可能很简单,但苦于网上没有参考资料,费了大量时间去调试。这里分享程序代码供大家参考(注意:我用的是Winform,而不是控制台),一些基本的引用我就不说明了,相信大家书看到这里,肯定都是知道的。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;

namespace FourierTransform
{
    public partial class Form1 : Form
    {
        //以灰度模式读取原始图像
        Mat srcImage = new Mat("meal.jpg", 0);
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            if (srcImage.IsEmpty)
                MessageBox.Show("读取图片错误,请确认目录下是否存在imread函数指定的图片!");
            //显示原始图像
            srcImageBox.Image = srcImage;  
        }

        private void DFT_Btn_Click(object sender, EventArgs e)
        {
            //将输入图像扩展到最佳的尺寸,边界用0补充
            int m = CvInvoke.GetOptimalDFTSize(srcImage.Rows);
            int n = CvInvoke.GetOptimalDFTSize(srcImage.Cols);
            Mat padded = new Mat();
            CvInvoke.CopyMakeBorder(srcImage, padded, 0, m - srcImage.Rows, 0, n - srcImage.Cols, BorderType.Constant);
            padded.ConvertTo(padded, DepthType.Cv32F); //将padded转换为Cv32F类型,此为傅里叶变换的实部
            //创建元素值都为0的zeroMat,此为傅里叶变换的虚部
            Mat zeroMat = Mat.Zeros(padded.Rows, padded.Cols, DepthType.Cv32F, 1);
            //Mat[] planes = { padded, zeroMat };
            //VectorOfMat matVector = new VectorOfMat(planes);
            VectorOfMat matVector = new VectorOfMat(); //创建mat型向量
            matVector.Push(padded); //将padded压入matVector中
            matVector.Push(zeroMat); //将zeroMat压入matVector中
            //创建channel数为2的mat,用于存储傅里叶变换的实部和虚部
            Mat complexI = new Mat(padded.Size, DepthType.Cv32F, 2);
            CvInvoke.Merge(matVector, complexI); //将matVector中存储的2个mat,merge到complexI中
            //创建mat,用于保存傅里叶变换的结果
            Mat fourier = new Mat(complexI.Size, DepthType.Cv32F, 2);
            //调用傅里叶变换函数Dft,进行傅里叶变换
            CvInvoke.Dft(complexI, fourier, DxtType.Forward, complexI.Rows);
            //将复数转换为幅值,即=>log(1+sqrt(Re(DFT(I)^2+Im(DFT(I)^2))
            Mat magnitudeImage = Magnitude(fourier);
            //若有奇数行或奇数列,则进行频谱裁剪
            magnitudeImage = new Mat(magnitudeImage, new Rectangle(0, 0, magnitudeImage.Cols & -2, magnitudeImage.Rows & -2));
            //重新排列傅里叶图像中的象限,使得原点位于图像中心
            SwitchQuadrants(ref magnitudeImage);
            //归一化,用0到1之间的浮点值将矩阵变换为可视的图像格式
            //CvInvoke.Normalize(magnitudeImage, magnitudeImage, 1.0, 0.0, NormType.MinMax, DepthType.Cv32F);
            //CvInvoke.Imshow("Fourier Transform", magnitudeImage);
            //若要在imageBox控件上显示傅里叶变换后的图像,需要按如下方式进行数据规整
            //DepthType的类型一定要是Cv8U,否则图片在imageBox控件中显示为纯黑色图片
            CvInvoke.Normalize(magnitudeImage, magnitudeImage, 0, 255, NormType.MinMax, DepthType.Cv8U);
            dftImageBox.Image = magnitudeImage;
        }

        //将复数转换为幅值,并进行对数尺度缩放
        private Mat Magnitude(Mat fftData)
        {
            //傅里叶变换的实部
            Mat Real = new Mat(fftData.Size, DepthType.Cv32F, 1);
            //傅里叶变换的虚部
            Mat Imaginary = new Mat(fftData.Size, DepthType.Cv32F, 1);
            VectorOfMat channels = new VectorOfMat();
            CvInvoke.Split(fftData, channels); //将多通道mat分离成几个单通道mat
            Real = channels.GetOutputArray().GetMat(0);
            Imaginary = channels.GetOutputArray().GetMat(1);
            CvInvoke.Pow(Real, 2.0, Real);
            CvInvoke.Pow(Imaginary, 2.0, Imaginary);
            CvInvoke.Add(Real, Imaginary, Real);
            CvInvoke.Pow(Real, 0.5, Real);
            Mat onesMat = Mat.Ones(Real.Rows, Real.Cols, DepthType.Cv32F, 1);
            CvInvoke.Add(Real, onesMat, Real);
            CvInvoke.Log(Real,Real); //求自然对数
            return Real;
        }

        //重新排列傅里叶图像中的象限,使得原点位于图像中心
        private void SwitchQuadrants(ref Mat mat)
        {
            int cx = mat.Cols / 2;
            int cy = mat.Rows / 2;
            Mat q0 = new Mat(mat, new Rectangle(0, 0, cx, cy)); //ROI左上区域
            Mat q1 = new Mat(mat, new Rectangle(cx, 0, cx, cy)); //ROI右上区域
            Mat q2 = new Mat(mat, new Rectangle(0, cy, cx, cy)); //ROI左下区域
            Mat q3 = new Mat(mat, new Rectangle(cx, cy, cx, cy)); //ROI右下区域
            Mat temp = new Mat(q0.Size, DepthType.Cv32F, 1);
            //交换象限(左上和右下进行交换)
            q0.CopyTo(temp);
            q3.CopyTo(q0);
            temp.CopyTo(q3);
            //交换象限(右上和左下进行交换)
            q1.CopyTo(temp);
            q2.CopyTo(q1);
            temp.CopyTo(q2);
        }
    }
}

 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值