欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于OpenCV的中值滤波代码仿真

程序员文章站 2024-03-25 22:54:52
...

中值滤波主要是用来除去椒盐噪声的,基本操作如下:
1. 抠出区域
2. 排序
3. 中值
还是老规矩,此代码仅仅是用来学习,暂时不用管它速度。
这一份代码主要做了以下工作:
1. 自定义了一份新的中值滤波函数
2. 调用OpenCV的中值滤波函数
3. 比较两个函数的处理结果,如果所对应的元素值小于一定范围,则表示两个元素值是一样的,显示黑色,反之,则显示白色

备注:因为在实际代码中处理会有精度损失,所以只能通过两个值小于一定阈值来判定两个元素是否一样;另外,边界没有处理,opencv的边界处理时用copyMakeBorder来做。
为了给技术小白提供方便,提供了完整的代码,项目文件和测试图片。点此下载
下面附上代码:

/**
中值滤波步骤:
1. 抠出区域
2. 排序
3. 中值

本程序做了以下操作:
1. 调用自己设计中值滤波代码
2. 调用opencv中值滤波代码
3. 比较两个函数的结果。
若比较的结果图某部分是白色,说明此部分是不一样。如果是黑色区域,表示的是数据一样

程序运行信息:
开发环境: VS2015 + OpenCV 3.4.1 + Windows 10
硬件环境: CPU i7-8750H; RAM 8G; Gpu: GTX1050Ti
*/
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include "opencv2/opencv.hpp"

/** @brief 将unsigned char的单通道Mat转换成float的vector类型

@param img: 单通道unsigned char类型
@param dst: 元素为float的vector类型
*/
static void MatConvertToVector(const cv::Mat& img, std::vector<float>& dst)
{
    CV_Assert(img.type() == CV_8UC1);

    cv::Mat tmp;
    img.convertTo(tmp, CV_8U);
    dst = (std::vector<float>)(tmp.reshape(1, 1));
}

/** @brief 将图片转换成vector数组, 并将其排序获得中位数的值

@param src 单通道图像矩阵
@return 中值
*/
static float CalcMedianValue(const cv::Mat& src)
{
    CV_Assert(src.type() == CV_8UC1);
    std::vector<float> values;
    MatConvertToVector(src, values);
    std::sort(values.begin(), values.end());

    int index = values.size() / 2;
    return values[index];
}

/** @brief 比较两个矩阵是否一致, 不一致则在结果矩阵中显示255

@param src0 矩阵0
@param src1 矩阵1
@param dst 比较结果
@param eps 两个矩阵对应元素值得差异, 大于此值则显示255,反之显示0
*/
static void CompareTwoMatrix(const cv::Mat& src0, const cv::Mat& src1, cv::Mat& dst, float eps)
{
    if ((src0.rows != src1.rows) || (src0.cols != src1.cols)) return;

    dst = cv::Mat::zeros(src0.rows, src0.cols, CV_8UC1);
    cv::Mat s0;
    cv::Mat s1;
    src0.convertTo(s0, CV_32F);
    src1.convertTo(s1, CV_32F);

    for (int y = 0; y < src0.rows; ++y)
    {
        for (int x = 0; x < src0.cols; ++x)
        {
            float v0 = s0.at<float>(y, x);
            float v1 = s1.at<float>(y, x);

            // 当差异非常大了, 才显示255
            if (std::abs(v0 - v1) >= eps)
            {
                dst.at<uchar>(y, x) = 255;
            }
        }
    }

}

/** @brief 仿写中值模糊算法

本仿写算法不处理边界情况

@param src 单通道灰度图
@param dst 处理后的矩阵, CV_32FC1类型
@param sz kernel的长和宽, 必须为奇数. cv的源码是sz.width = sz.height。本代码不要求一定相等
*/
static void MedianBlur(const cv::Mat& src, cv::Mat& dst, cv::Size sz)
{
    CV_Assert(src.type() == CV_8UC1);
    CV_Assert((sz.width % 2 == 1) && (sz.height % 2 == 1));

    const int xn = sz.width / 2;
    const int yn = sz.height / 2;

    dst = cv::Mat::zeros(src.rows, src.cols, CV_32FC1);

    for (int y = yn; y < src.rows - yn; ++y)
    {
        for (int x = xn; x < src.cols - xn; ++x)
        {
            cv::Rect rect = cv::Rect(x - xn, y - yn, sz.width, sz.height);
            cv::Mat roi = src(rect);
            float val = CalcMedianValue(roi);
            dst.at<float>(y, x) = val;
        }
    }

}

void test_median_blur()
{
    std::cout << "Demoe" << std::endl;
    std::string path = "../Resources/QQ图片20180614112723.jpg";
    cv::Mat img = cv::imread(path, cv::ImreadModes::IMREAD_GRAYSCALE);
    cv::imshow("原图", img);

    cv::Mat dst;
    MedianBlur(img, dst, cv::Size(5, 5));

    //cv
    cv::Mat cvDst;
    cv::medianBlur(img, cvDst, 5);

    cv::Mat cmpResult;
    CompareTwoMatrix(dst, cvDst, cmpResult, 0.5/*eps*/);

    cv::imshow("cv处理函数", cvDst);
    dst.convertTo(dst, CV_8U);
    cv::imshow("手动撸函数", dst);
    cv::imshow("比较结果", cmpResult);
}

效果图镇楼:
基于OpenCV的中值滤波代码仿真

相关标签: opencv