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

中值滤波主要是用来除去椒盐噪声的,基本操作如下:
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);
}

效果图镇楼:
这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值