zoukankan      html  css  js  c++  java
  • 【C++】双边滤波器(bilateral filter)

    Bilateral Filtering for Gray and Color Images

    双边滤波器:保留边界的平滑滤波器。 在局部上,就是在灰度值差异不大的区域平滑,在灰度值差异比较大的边界地区保留边界。所以双边滤波器作用于每个像素的同时,必然会受到领域像素点的距离、灰度值差的权重影响。

    已知低通滤波可以表示为:

    range filter可以表示为:(range filter 试选定一个数值范围,再做滤波的一个操作)

    所以,双边滤波器的定义是:

    其中,k(x)是归一化(normalize)函数,

    ( f 表示原图像,h 表示处理后的图像 x 表示 h 中某个像素点位置,ξ 表示 f 中x位置像素点的邻域像素,f(ξ)表示该像素点的灰度值,c表示低通滤波, s表示range filter)

    其中,

    //Filters.h
    
    #ifndef FILTERS_H
    #define FILTERS_H
    
    #include "opencv2/imgproc.hpp"
    #include "opencv2/highgui.hpp"
    #include "opencv2/core.hpp"
    
    #include <iostream>
    #include <cmath>
    
    //Bilateral Filtering
    //sigmaD == sigmaSpace, sigmaR == sigmaColor
    cv::Mat BilateralFilter(cv::Mat inputImg, int filterSize, double sigmaD, double sigmaR);
    
    cv::Mat fastBilateralFilter(cv::Mat inputImg, int filterSize, double sigmaD, double sigmaR);
    
    #endif // ! FILTERS_H
    //Filters.cpp
    
    #include "Filters.h"
    
    double SpaceFactor(int x1, int y1, int x2, int y2, double sigmaD) {
        double absX = pow(abs(x1 - x2), 2);
        double absY = pow(abs(y1 - y2), 2);
    
        return exp(-(absX + absY) / (2 * pow(sigmaD, 2)));
    }
    
    double ColorFactor(int x, int y, double sigmaR) {
        double distance = abs(x - y) / sigmaR;
        return exp(-0.5 * pow(distance, 2));
    }
    
    cv::Mat BilateralFilter(cv::Mat inputImg, int filterSize, double sigmaD, double sigmaR) {
        int len; //must be odd number
        cv::Mat gray; // must be 1-channel image
        cv::Mat LabImage; // if channels == 3
    
        if (filterSize % 2 != 1 || filterSize <= 0) {
            std::cerr << "Filter Size must be a positive odd number!" << std::endl;
            return inputImg;
        }
        len = filterSize / 2;
    
        if (inputImg.channels() >= 3) {
            cv::cvtColor(inputImg, LabImage, cv::COLOR_BGR2Lab);
            gray = cv::Mat::zeros(LabImage.size(), CV_8UC1);
            for (int i = 0; i < LabImage.rows; i++) {
                for (int j = 0; j < LabImage.cols; j++) {
                    gray.ptr<uchar>(i)[j] = LabImage.ptr<uchar>(i, j)[0];
                }
            }
        }
        else if(inputImg.channels() == 1){
            inputImg.copyTo(gray);
        }
        else {
            std::cerr << "the count of input image's channel can not be 2!" << std::endl;
            return inputImg;
        }
    
        cv::Mat resultGrayImg = cv::Mat::zeros(gray.size(), CV_8UC1);
        for (int i = 0; i < gray.rows; i++) {
            for (int j = 0; j < gray.cols; j++) {
                double k = 0;
                double f = 0;
                for (int r = i - len; r <= i + len; r++) {
                    for (int c = j - len; c <= j + len; c++) {
                        if (r < 0 || c < 0 || r >= gray.rows || c >= gray.cols)
                            continue;
                        f = f + gray.ptr<uchar>(r)[c] * SpaceFactor(i, j, r, c, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(r)[c], sigmaD);
                        k += SpaceFactor(i, j, r, c, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(r)[c], sigmaD);
                    }
                }
                int value = f / k;
                if (value < 0) value = 0;
                else if (value > 255) value = 255;
    
                resultGrayImg.ptr<uchar>(i)[j] = (uchar)value;
            }
        }
    
        cv::Mat resultImg;
        if (inputImg.channels() >= 3) {
            for (int i = 0; i < LabImage.rows; i++) {
                for (int j = 0; j < LabImage.cols; j++) {
                    LabImage.ptr<uchar>(i, j)[0] = resultGrayImg.ptr<uchar>(i)[j];
                }
            }
            cv::cvtColor(LabImage, resultImg, cv::COLOR_Lab2BGR);
        }
        else {
            resultGrayImg.copyTo(resultImg);
        }
    
        return resultImg;
    }
    
    cv::Mat fastBilateralFilter(cv::Mat inputImg, int filterSize, double sigmaD, double sigmaR) {
        int len; //must be odd number
        cv::Mat gray; // must be 1-channel image
        cv::Mat LabImage; // if channels == 3
    
        if (filterSize % 2 != 1 || filterSize <= 0) {
            std::cerr << "Filter Size must be a positive odd number!" << std::endl;
            return inputImg;
        }
        len = filterSize / 2;
    
        if (inputImg.channels() >= 3) {
            cv::cvtColor(inputImg, LabImage, cv::COLOR_BGR2Lab);
            gray = cv::Mat::zeros(LabImage.size(), CV_8UC1);
            for (int i = 0; i < LabImage.rows; i++) {
                for (int j = 0; j < LabImage.cols; j++) {
                    gray.ptr<uchar>(i)[j] = LabImage.ptr<uchar>(i, j)[0];
                }
            }
        }
        else if (inputImg.channels() == 1) {
            inputImg.copyTo(gray);
        }
        else {
            std::cerr << "the count of input image's channel can not be 2!" << std::endl;
            return inputImg;
        }
    
        cv::Mat resultGrayImg = cv::Mat::zeros(gray.size(), CV_8UC1);
        for (int i = 0; i < gray.rows; i++) {
            for (int j = 0; j < gray.cols; j++) {
                double k = 0;
                double f = 0;
                double sum = 0;
                for (int r = i - len; r <= i + len; r++) {
                    if (r < 0 || r >= gray.rows)
                        continue;
                    f = f + gray.ptr<uchar>(r)[j] * SpaceFactor(i, j, r, j, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(r)[j], sigmaD);
                    k += SpaceFactor(i, j, r, j, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(r)[j], sigmaD);
                }
                sum = f / k;
                f = k = 0.0;
                for (int c = j - len; c <= j + len; c++) {
                    if (c < 0 || c >= gray.cols)
                        continue;
                    f = f + gray.ptr<uchar>(i)[c] * SpaceFactor(i, j, i, c, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(i)[c], sigmaD);
                    k += SpaceFactor(i, j, i, c, sigmaD) * ColorFactor(gray.ptr<uchar>(i)[j], gray.ptr<uchar>(i)[c], sigmaD);
                }
                int value = (sum + f / k) / 2;
                if (value < 0) value = 0;
                else if (value > 255) value = 255;
    
                resultGrayImg.ptr<uchar>(i)[j] = (uchar)value;
            }
        }
    
        cv::Mat resultImg;
        if (inputImg.channels() >= 3) {
            for (int i = 0; i < LabImage.rows; i++) {
                for (int j = 0; j < LabImage.cols; j++) {
                    LabImage.ptr<uchar>(i, j)[0] = resultGrayImg.ptr<uchar>(i)[j];
                }
            }
            cv::cvtColor(LabImage, resultImg, cv::COLOR_Lab2BGR);
        }
        else {
            resultGrayImg.copyTo(resultImg);
        }
    
        return resultImg;
    }
    //main.cpp
    
    #include <iostream>
    #include <time.h>
    
    #include "Filters.h"
    
    using namespace std;
    
    int main() {
        cv::Mat img = cv::imread("Capture.jpg", cv::IMREAD_UNCHANGED);
        clock_t begin_time = clock();
        cv::Mat result = BilateralFilter(img, 15, 12.5, 50);
        std::cout << float(clock() - begin_time) / CLOCKS_PER_SEC << std:: endl;
        cv::imshow("original", result);
        cv::waitKey(0);
        cv::imwrite("original.jpg", result);
    
        begin_time = clock();
        result = fastBilateralFilter(img, 15, 12.5, 50);
        std::cout << float(clock() - begin_time) / CLOCKS_PER_SEC << std::endl;
        cv::imshow("fast", result);
        cv::waitKey(0);
        cv::imwrite("fast.jpg", result);
    
        begin_time = clock();
        cv::bilateralFilter(img, result, 15, 50, 12.5);
        std::cout << float(clock() - begin_time) / CLOCKS_PER_SEC << std::endl;
        cv::imshow("opencv", result);
        cv::waitKey(0);
        cv::imwrite("opencv.jpg", result);
    
        system("pause");
        return 0;
    }

    运行结果:

    46.889s  5.694s  0.202s

    二维算子降成两个一维算子之后,速度加快了一些,但是还是不如opencv的快,效果也比它差一些(No more reinventing the wheel...)

    从左至右:“小雀斑”帅气原图、BilateralFilter处理结果、fastBilateralFilter处理结果、opencv接口处理结果

       

  • 相关阅读:
    第九次训练赛
    什么是 Catalan 数列以及其应用
    Python pip 安装与使用
    HDU 1179:Ollivanders: Makers of Fine Wands since 382 BC.
    身份证信息
    流量暴增,掌门教育如何基于 Spring Cloud Alibaba 构建微服务体系?
    从零入门 Serverless | 函数计算的可观测性
    如何管理越来越多的 operator?OLM 给你答案
    Fluid: 让大数据和 AI 拥抱云原生的一块重要拼图
    SpringCloud 应用在 Kubernetes 上的最佳实践 — 线上发布(可监控)
  • 原文地址:https://www.cnblogs.com/cheermyang/p/6637186.html
Copyright © 2011-2022 走看看