OpenCvSharp学习笔记7--离散傅立叶变换

只是用OpenCvSharp将官网源码跑一遍,关于傅立叶变换的理论,还是自己百度吧,反正,本人是还没搞明白!

相关函数

CopyMakeBorder边界外推

原文中使用了CopyMakeBorder函数,就顺便了解下BorderType参数各值时的效果,具体见OpenCvSharp函数:CopyMakeBorder边框外推_图南堂的博客-CSDN博客

GetOptimalDFTSize返回适合DFT的最佳大小

返回一个大于等于原值,且满足N=2^p * 3^q * 5^r(p,q,r为大于等于0的整数)的最小整数。

Dft离散傅立叶变换

对一张图像使用傅立叶变换就是将它分解成正弦和余弦两部分。也就是将图像从空间域(spatial domain)转换到频域(frequency domain)。 这一转换的理论基础来自于以下事实:任一函数都可以表示成无数个正弦和余弦函数的和的形式

Merge图像合并

将多个矩阵合并为一个矩阵

Normalize归一化

将图像归一化,便于显示

图像示例

        应用示例,用于判断文本的方向

 源码示例

public void Run(ParamBase paramBase) {
    //var imgFile = ImagePath.Lena;
    //var imgFile = ImagePath.ImageTextN;
    var imgFile = ImagePath.ImageTextR;

    using var mat = Cv2.ImRead(imgFile);
    if (mat.Empty()) throw new Exception($"图像打开有误:{imgFile}");
    //用CopyMakeBorder演示BorderTypes各值的不同效果
    TestBorderTypes(mat);


    using var I = mat.CvtColor(ColorConversionCodes.BGR2GRAY);
    if (I.Empty()) throw new Exception($"图像打开有误:{imgFile}");


    //扩展为大小最适合做离散傅立叶转换的矩阵
    using var padded = new Mat();
    //大于等下原值,且满足N = 2^p * 3^q * 5^r(p,q,r为大于等于0的整数)
    int rows = Cv2.GetOptimalDFTSize(I.Rows);
    int cols = Cv2.GetOptimalDFTSize(I.Cols);

    //用0扩展图像边框
    Cv2.CopyMakeBorder(I, padded, 0, rows - I.Rows, 0, cols - I.Cols, BorderTypes.Constant, Scalar.All(0));

    //结果需要用浮点型存储
    padded.ConvertTo(padded, MatType.CV_32F);
    var planes = new Mat[] { padded, Mat.Zeros(padded.Size(), MatType.CV_32F) };

    using var complexSrc = new Mat();
    Cv2.Merge(planes, complexSrc);

    //离散傅立叶变换
    Cv2.Dft(complexSrc, complexSrc);

    //分割实数与复数部分 planes[0]为实数部分,planes[1]为复数部分
    Cv2.Split(complexSrc,out planes);

    //将结果转为幅度
    //dst(I) = Math.Sqrt(x(I)^2 + y(I)^2)
    Cv2.Magnitude(planes[0], planes[1], planes[0]);
    var magSrc = planes[0];

    magSrc += Scalar.All(1);
    //由于幅度结果太大,无法显示,使用对数尺度替换线性尺度
    Cv2.Log(magSrc, magSrc);

    //行、列都为偶数
    magSrc= magSrc[new Rect(0,0, magSrc.Cols & -2, magSrc.Rows & -2)];

    int cx = magSrc.Cols / 2;
    int cy = magSrc.Rows / 2;

    Mat q0 = new Mat(magSrc, new Rect(0, 0, cx, cy));
    Mat q1 = new Mat(magSrc, new Rect(cx, 0, cx, cy));
    Mat q2 = new Mat(magSrc, new Rect(0, cy, cx, cy));
    Mat q3 = new Mat(magSrc, new Rect(cx, cy, cx, cy));

    //左上角与右下角对换
    Mat tmp=new Mat();
    q0.CopyTo(tmp);
    q3.CopyTo(q0);
    tmp.CopyTo(q3);

    //右上角与左下角对换
    q1.CopyTo(tmp);
    q2.CopyTo(q1);
    tmp.CopyTo(q2);

    //原幅度值仍更不便于显示,归一化后方便可视
    Cv2.Normalize(magSrc, magSrc, 0, 1, NormTypes.MinMax);

    Cv2.ImShow("Input Image", I);
    Cv2.ImShow("spectrum magnitude", magSrc);
    Cv2.WaitKey();
    Cv2.DestroyAllWindows();
}

/// <summary>
/// 测试BorderTypes
/// </summary>
/// <param name="src"></param>
private void TestBorderTypes(Mat src) {
    var bBorderTypes = Enum.GetValues(typeof(BorderTypes));
    foreach(BorderTypes b in bBorderTypes) {
        try {
            //外推宽度
            var extend = 100;
            using var dst = new Mat();
            Cv2.CopyMakeBorder(src, dst, extend, extend, extend, extend, b, Scalar.All(127));
            Cv2.Rectangle(dst, new Rect(extend, extend, src.Width, src.Height), Scalar.Red);
            Cv2.ImShow($"{b.ToString()}", dst);                    
        }
        catch (Exception ex) {
            //不支持 Transparent
        }
    }
    Cv2.WaitKey();
    Cv2.DestroyAllWindows();
}

参考

OpenCV: Discrete Fourier Transform

猜你喜欢

转载自blog.csdn.net/TyroneKing/article/details/129788567
今日推荐