磨皮功能 C++/C的OpenCV 实现

磨皮功能 C++/C的OpenCV 实现

前提条件

  1. OpenCV 安装: 你需要正确安装 OpenCV 库。
  2. C++ 编译器: 如 G++。

C++ 代码

cpp 复制代码
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>

// 使用标准命名空间
using namespace std;
using namespace cv;

/**
 * @brief 对图像进行简单的磨皮处理 (使用双边滤波器)
 *
 * @param inputImage 输入的彩色图像
 * @param d 滤波过程中每个像素邻域的直径。如果此值为非正数,则从 sigmaSpace 计算得到。
 * @param sigmaColor 颜色空间滤波器的 sigma 值。这个参数的值越大,就意味着在邻域内有越宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
 * @param sigmaSpace 坐标空间滤波器的 sigma 值。这个参数的值越大,就意味着越远的像素会相互影响,从而使更大的区域中足够相似的颜色获取相同的颜色。当 d>0 时,d 指定了邻域大小且与 sigmaSpace 无关。否则,d 正比于 sigmaSpace。
 * @return Mat 返回磨皮后的图像
 */
Mat simpleSkinSmoothing(const Mat& inputImage, int d = 15, double sigmaColor = 150, double sigmaSpace = 15) {
    if (inputImage.empty()) {
        cerr << "错误: 输入图像为空!" << endl;
        return inputImage;
    }

    Mat smoothedImage;
    bilateralFilter(inputImage, smoothedImage, d, sigmaColor, sigmaSpace);

    return smoothedImage;
}

int main(int argc, char** argv) {
    // --- 1. 加载图像 ---
    string image_path = "portrait.jpg"; // 修改为你的肖像图片路径
    if (argc > 1) {
        image_path = argv[1]; // 或者从命令行参数获取图片路径
    }

    Mat originalImage = imread(image_path, IMREAD_COLOR);
    if (originalImage.empty()) {
        cout << "无法加载图像: " << image_path << endl;
        return -1;
    }
    cout << "图像加载成功: " << image_path << endl;

    // --- 2. 应用磨皮处理 ---
    // 调整这些参数以获得不同的磨皮效果
    int diameter = 15;          // 邻域直径。可以尝试 5, 10, 15, 20, 25 等
    double sigma_color = 75;   // 颜色标准差。可以尝试 50, 75, 100, 150 等
    double sigma_space = 75;   // 空间标准差。可以尝试 50, 75, 100, 150 等

    cout << "应用磨皮效果: d=" << diameter << ", sigmaColor=" << sigma_color << ", sigmaSpace=" << sigma_space << endl;
    Mat smoothedImage = simpleSkinSmoothing(originalImage, diameter, sigma_color, sigma_space);

    if (smoothedImage.empty()) {
        cout << "磨皮处理失败。" << endl;
        return -1;
    }

    // --- 3. 显示结果 ---
    namedWindow("原始图像", WINDOW_AUTOSIZE);
    imshow("原始图像", originalImage);

    namedWindow("简单磨皮效果", WINDOW_AUTOSIZE);
    imshow("简单磨皮效果", smoothedImage);

    cout << "按任意键退出..." << endl;
    waitKey(0); // 等待按键

    destroyAllWindows(); // 关闭所有窗口

    return 0;
}

如何编译

  1. 保存代码: 将上述代码保存为 skin_smoother.cpp

  2. 准备图片: 准备一张名为 portrait.jpg (或你在代码中指定的其他名称,或通过命令行传入) 的肖像图片,并将其放在与 skin_smoother.cpp 相同的目录下或指定正确路径。

  3. 编译:

    打开终端,使用以下命令编译 (假设你使用的是 g++,并且 OpenCV 已正确安装):

    bash 复制代码
    g++ skin_smoother.cpp -o skin_smoother $(pkg-config --cflags --libs opencv4)
    • 注意: opencv4 可能需要根据你的 OpenCV 版本调整 (例如 opencvopencv3)。你可以通过运行 pkg-config --libs opencv4 来检查正确的链接标志。

如何运行

bash 复制代码
./skin_smoother

或者,如果你想指定图片路径:

bash 复制代码
./skin_smoother /path/to/your/portrait_image.jpg

程序会显示原始图像和磨皮后的图像。


代码解释

  1. 包含头文件

    cpp 复制代码
    #include <opencv2/opencv.hpp> // OpenCV 主要头文件
    #include <iostream>           // 标准输入输出
    #include <string>             // STL string
  2. simpleSkinSmoothing 函数

    cpp 复制代码
    Mat simpleSkinSmoothing(const Mat& inputImage, int d = 15, double sigmaColor = 150, double sigmaSpace = 15) {
        if (inputImage.empty()) { /* ... 错误处理 ... */ }
    
        Mat smoothedImage;
        // 应用双边滤波器
        bilateralFilter(inputImage, smoothedImage, d, sigmaColor, sigmaSpace);
    
        return smoothedImage;
    }
    • 这是核心的磨皮函数。它接收一个输入图像和双边滤波器的三个主要参数。
    • cv::bilateralFilter(): 这是 OpenCV 中实现双边滤波的函数。
      • inputImage: 输入图像。
      • smoothedImage: 输出的滤波后图像。
      • d: 滤波过程中每个像素邻域的直径。值越大,计算越慢,但平滑区域会更大。
      • sigmaColor: 颜色空间的标准差。值越大,意味着颜色差异更大的像素也会被一起平滑。
      • sigmaSpace: 坐标空间的标准差。值越大,意味着距离更远的像素也会相互影响(如果它们的颜色相似的话)。
  3. main 函数

    • 加载图像 (imread): 加载用户指定的或默认的图像。

    • 调用磨皮函数 :

      cpp 复制代码
      int diameter = 15;
      double sigma_color = 75;
      double sigma_space = 75;
      Mat smoothedImage = simpleSkinSmoothing(originalImage, diameter, sigma_color, sigma_space);

      这里设置了双边滤波器的参数。你可以调整这些值来观察不同的磨皮效果。

    • 显示图像 (imshow, waitKey): 显示原始图像和处理后的图像,并等待用户按键关闭。


参数调整技巧

  • d (直径):
    • 较小的值 (如 5-10):平滑效果较弱,保留更多细节,计算速度快。
    • 较大的值 (如 15-25 或更高):平滑效果更强,可能导致一些细节丢失,计算速度慢。对于高分辨率图像,可能需要更大的 d
  • sigmaColor (颜色标准差):
    • 较小的值 (如 25-50):只有颜色非常相近的像素才会被平滑,保留更多纹理。
    • 较大的值 (如 75-150 或更高):颜色差异较大的像素也会被平滑,导致更强的"涂抹"感,皮肤看起来更"干净"。
  • sigmaSpace (空间标准差):
    • 较小的值 (如 25-50):只有空间上非常接近的像素才会被一起考虑,效果更局部。
    • 较大的值 (如 75-150 或更高):空间上较远的像素也会参与计算(如果颜色相似),平滑范围更广。

经验法则:

  • 通常 sigmaColorsigmaSpace 会取相似的值。
  • 可以从 d=15, sigmaColor=75, sigmaSpace=75 开始尝试。
  • 如果希望保留更多细节,减小这些值。
  • 如果希望获得更强的平滑效果,增大这些值。
  • 过度增大会导致图像看起来不自然,像塑料娃娃。

局限性

  • 简单性: 这种方法非常基础,对于复杂的皮肤问题(如严重的痘痘、斑点或光照不均)可能效果有限。
  • 全局性: 双边滤波器是全局应用的。如果只想对特定区域(如脸部皮肤)进行磨皮,需要先进行人脸检测和皮肤区域分割。
  • 细节保留: 虽然双边滤波器比高斯模糊能更好地保留边缘,但非常强的磨皮参数仍然可能导致一些期望的细节(如痣、雀斑,如果想保留的话)模糊。
  • 性能: 对于大图像和较大的 d 值,双边滤波计算可能会比较慢。

对于更高级的磨皮效果,可能需要结合频率分离、表面模糊、更复杂的皮肤检测和遮罩技术,甚至深度学习方法。但对于快速实现一个简单的磨皮效果,双边滤波器是一个很好的起点。

相关推荐
s1533520 分钟前
3.RV1126-OPENCV 图像叠加
人工智能·opencv·计算机视觉
AgilityBaby41 分钟前
UE5打包项目设置Project Settings(打包widows exe安装包)
c++·3d·ue5·游戏引擎·unreal engine
让我们一起加油好吗3 小时前
【基础算法】高精度(加、减、乘、除)
c++·算法·高精度·洛谷
鑫鑫向栄3 小时前
[蓝桥杯]缩位求和
数据结构·c++·算法·职场和发展·蓝桥杯
stormsha3 小时前
MCP架构全解析:从核心原理到企业级实践
服务器·c++·架构
梁下轻语的秋缘3 小时前
每日c/c++题 备战蓝桥杯(P1204 [USACO1.2] 挤牛奶 Milking Cows)
c语言·c++·蓝桥杯
鑫鑫向栄3 小时前
[蓝桥杯]外卖店优先级
数据结构·c++·算法·职场和发展·蓝桥杯
Zfox_3 小时前
【C++项目】:仿 muduo 库 One-Thread-One-Loop 式并发服务器
linux·服务器·c++·muduo库
零K沁雪4 小时前
Linux C 优雅的执行命令
linux·c语言
wangyuxuan10294 小时前
AtCoder Beginner Contest 399题目翻译
开发语言·c++·算法