【opencv】教程代码 —photo

  • 将彩色图像转换为去色图像(灰度图像)和 颜色增强图像

  • HDR 图像合成,并同时执行色调映射和曝光融合

  • 非真实感渲染(NPR)

  • 正常克隆、混合克隆、单色传递、局部颜色改变、局部照明改变和纹理平滑

1. decolorization将彩色图像转换为去色图像(灰度图像)和** 颜色增强图像**

本段代码的功能是使用OpenCV的去色模块将输入的彩色图像转换为灰度图像和色彩提升图像,并显示这三种图像。程序首先解析命令行参数获取图像文件名,然后读取彩色图像。如果读取成功,则使用decolor函数处理图像。最后,使用imshow函数分别显示原始彩色图像、生成的灰度图像和色彩提升图像,并等待用户操作来结束程序

cpp 复制代码
// 本教程展示了如何使用 OpenCV 去色模块。
//
// 输入:
// 彩色图像
//
// 输出:
// 1) 灰度图像
// 2) 色彩提升图像
//


#include "opencv2/photo.hpp"      // 包含OpenCV中处理照片的相关功能的头文件
#include "opencv2/imgproc.hpp"    // 包含OpenCV中图像处理的相关功能的头文件
#include "opencv2/highgui.hpp"    // 包含OpenCV中高层GUI(图形用户界面)的相关功能的头文件
#include "opencv2/core.hpp"       // 包含OpenCV中核心功能的头文件
#include <iostream>               // 包含标准输入输出流库


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


int main( int argc, char *argv[] ) // 主函数,程序入口
{
    // 解析命令行参数
    CommandLineParser parser( argc, argv, "{@input | HappyFish.jpg | input image}" );
    // 读取输入图像,使用命令行参数指定的文件名
    Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
    if ( src.empty() ) // 如果图像为空,即无法打开或找到图像
    {
        cout << "Could not open or find the image!\n" << endl; // 输出错误信息
        cout << "Usage: " << argv[0] << " <Input image>" << endl; // 输出正确用法
        return EXIT_FAILURE; // 返回失败状态码
    }


    Mat gray, color_boost;     // 创建两个Mat对象,用于存放结果图像
    decolor( src, gray, color_boost ); // 使用去色函数处理图像,得到灰度图像和色彩强化图像
    imshow( "Source Image", src ); // 显示原始彩色图像
    imshow( "grayscale", gray ); // 显示灰度图像
    imshow( "color_boost", color_boost ); // 显示色彩提升图像
    waitKey(0); // 等待用户按键
}

2. hdr_imaging.cppHDR 图像合成,并同时执行色调映射和曝光融合****

memorial.png

fusion.png

ldr.png

该段代码是一个完整的程序,用于 HDR 图像合成,并同时执行色调映射和曝光融合。程序首先通过命令行参数加载一系列不同曝光时间的图像,使用Debevec方法估算相机响应,然后生成HDR图像,对HDR图像进行色调映射以生成LDR图像,最后执行曝光融合来生成一个动态范围较宽的显示图像。程序把所有生成的结果以图像文件的形式写入到磁盘中。

cpp 复制代码
#include "opencv2/photo.hpp" // 引入OpenCV中的photo模块,主要用于高动态范围和图像去噪
#include "opencv2/imgcodecs.hpp" // 引入OpenCV中的imgcodecs模块,提供图像文件读取和写入功能
#include "opencv2/highgui.hpp" // 引入OpenCV中的highgui模块,用于图像显示和简单用户界面


#include <vector> // 引入标准模板库中的vector容器
#include <iostream> // 引入标准输入输出流库
#include <fstream> // 引入文件流库


using namespace cv; // 使用OpenCV命名空间,方便后面直接调用其下的函数和类
using namespace std; // 使用标准命名空间


// 声明一个用于加载曝光时间序列的函数
void loadExposureSeq(String, vector<Mat>&, vector<float>&);


// 程序入口点
int main(int argc, char**argv)
{
    // 解析命令行参数
    CommandLineParser parser( argc, argv, "{@input | | Input directory that contains images and exposure times. }" );


    // [加载图像和曝光时间]
    vector<Mat> images; // 存储图像的向量
    vector<float> times; // 存储曝光时间的向量
    // 调用自定义函数loadExposureSeq加载图像和曝光时间
    loadExposureSeq(parser.get<String>( "@input" ), images, times);
    // [加载图像和曝光时间结束]


    // [估算相机响应]
    Mat response; // 创建一个Mat对象用于存放相机响应结果
    // 创建Debevec的相机校正对象
    Ptr<CalibrateDebevec> calibrate = createCalibrateDebevec(); 
    // 使用calibrate对象的process方法处理图像和曝光时间,得到响应曲线
    calibrate->process(images, response, times);
    // [估算相机响应结束]


    // [生成HDR图像]
    Mat hdr; // 创建一个HDR图像的Mat对象
     // 创建Debevec的HDR融合对象
    Ptr<MergeDebevec> merge_debevec = createMergeDebevec();
    // 使用merge_debevec对象的process方法合成HDR图像
    merge_debevec->process(images, hdr, times, response);
    // [生成HDR图像结束]


    // [对HDR图像进行色调映射]
    Mat ldr; // 创建一个LDR图像的Mat对象
    Ptr<Tonemap> tonemap = createTonemap(2.2f); // 创建色调映射对象,gamma值为2.2
    // 使用tonemap对象的process方法对HDR图像进行色调映射,得到LDR图像
    tonemap->process(hdr, ldr);
    // [对HDR图像进行色调映射结束]


    // [执行曝光融合]
    Mat fusion; // 创建一个融合结果的Mat对象
    Ptr<MergeMertens> merge_mertens = createMergeMertens(); // 创建Mertens的曝光融合对象
    // 使用merge_mertens对象的process方法对图像执行曝光融合
    merge_mertens->process(images, fusion);
    // [执行曝光融合结束]


    // [写出结果]
    imwrite("fusion.png", fusion * 255); // 将曝光融合的结果乘以255后写入文件fusion.png
    imwrite("ldr.png", ldr * 255); // 将色调映射的结果乘以255后写入文件ldr.png
    imwrite("hdr.hdr", hdr); // 将HDR图像写入文件hdr.hdr
    // [写出结果结束]


    // 程序正常退出
    return 0;
}


// 定义之前声明的加载曝光时间序列函数
void loadExposureSeq(String path, vector<Mat>& images, vector<float>& times)
{
    // 在路径后面添加斜杠
    path = path + "/";
    // 使用文件流打开path路径下的list.txt文件
    ifstream list_file((path + "list.txt").c_str());
    string name;
    float val;
    // 循环读取图像文件名和曝光值,直到文件结尾
    while(list_file >> name >> val) {
        // 读取图像并将其存入images向量
        Mat img = imread(path + name);
        images.push_back(img);
        // 计算曝光时间并将其存入times向量
        times.push_back(1 / val);
    }
    // 关闭文件
    list_file.close();
}

3. npr_demo.cpp非真实感渲染(NPR)

Using Normalized convolution Filter

Using Recursive Filter

Detail Enhancement

Pencil sketch/Color Pencil Drawing

Stylization

  • cv::edgePreservingFilter():用于边缘保留平滑处理的函数,输入参数为源图像和目标图像,以及处理方式。

  • cv::detailEnhance():用于细节增强的函数,输入参数为源图像和目标图像。

  • cv::pencilSketch():用于铅笔素描处理的函数,输入参数包括源图像、两个目标图像和其他效果参数。

  • cv::stylization():用于风格化处理的函数,输入参数为源图像和目标图像。

该代码是一个使用OpenCV库进行非真实感渲染(NPR)的示例程序,包括以下几种处理方式:

  1. 边缘保留平滑:基于标准化卷积滤波器或递归滤波器。

  2. 细节增强:强调图像中的细节。

  3. 铅笔素描/彩色铅笔画:将图像转化为铅笔素描或彩色铅笔画样式。

  4. 风格化:对图像进行风格化处理。

    用户可以通过输入对应的数字来选择相应的处理方式。

cpp 复制代码
/*
* npr_demo.cpp
*
* 作者:
* Siddharth Kherada <siddharthkherada27[at]gmail[dot]com>
*
* 这个教程展示了如何使用OpenCV非真实感渲染模块。
* 1)边缘保留平滑
*    -> 使用标准化卷积过滤器
*    -> 使用递归过滤器
* 2)细节增强
* 3)铅笔素描/彩色铅笔画
* 4)风格化
*
*/


#include "opencv2/photo.hpp"     // 包含opencv2的photo模块
#include "opencv2/imgproc.hpp"   // 包含opencv2的imgproc模块
#include "opencv2/highgui.hpp"   // 包含opencv2的highgui模块
#include <iostream>              // 包含输入输出流


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


int main(int argc, char* argv[])  // 主函数,程序执行的入口
{
    int num,type;                 // 定义整型变量num和type
    // 命令行解析器,获取输入图片
    CommandLineParser parser(argc, argv, "{@input | lena.jpg | input image}");
    // 读取图片并保存到Mat类型的src变量中
    Mat src = imread( samples::findFile( parser.get<String>("@input") ), IMREAD_COLOR);


    // 如果没有得到图片,输出错误信息并退出程序
    if(src.empty())
    {
        cout << "Could not open or find the image!\n" << endl;
        cout << "Usage: " << argv[0] << " <Input image>" << endl;
        exit(0);
    }


    // 输出各种方法的介绍
    cout << endl;
    cout << " Edge Preserve Filter" << endl;
    cout << "----------------------" << endl;
    cout << "Options: " << endl;
    cout << "1) Edge Preserve Smoothing" << endl;
    cout << "   -> Using Normalized convolution Filter" << endl;
    cout << "   -> Using Recursive Filter" << endl;
    cout << "2) Detail Enhancement" << endl;
    cout << "3) Pencil sketch/Color Pencil Drawing" << endl;
    cout << "4) Stylization" << endl;
    cout << endl;


    cout << "Press number 1-4 to choose from above techniques: ";


    cin >> num;   // 输入选择的方法编号


    Mat img;      // 定义Mat类型的img,用于保存处理后的图片


    // 根据选择的方法编号进行相应的处理
    if(num == 1)
    {
        cout << "Press 1 for Normalized Convolution Filter and 2 for Recursive Filter: ";


        cin >> type;


        edgePreservingFilter(src,img,type);   // 调用边缘保留滤波函数,进行图片处理
        imshow("Edge Preserve Smoothing",img);    // 显示处理后的图片


    }
    else if(num == 2)
    {
        detailEnhance(src,img);              // 调用细节增强函数,进行图片处理
        imshow("Detail Enhanced",img);       // 显示处理后的图片
    }
    else if(num == 3)
    {
        Mat img1;
        pencilSketch(src,img1, img, 10 , 0.1f, 0.03f);    // 调用铅笔素描函数,进行图片处理
        imshow("Pencil Sketch",img1);                      // 显示处理后的图片
        imshow("Color Pencil Sketch",img);                 // 显示处理后的彩色铅笔素描图片
    }
    else if(num == 4)
    {
        stylization(src,img);              // 调用风格化函数,进行图片处理
        imshow("Stylization",img);         // 显示处理后的图片
    }
    waitKey(0);   // 等待用户输入,0表示无限期等待
}

4. cloning_demo.cpp 正常克隆、混合克隆、单色传递、局部颜色改变、局部照明改变和纹理平滑

cpp 复制代码
/*
* cloning_demo.cpp
*
* 作者:
* Siddharth Kherada <siddharthkherada27[at]gmail[dot]com>
*
* 本教程展示了如何在没有图形用户界面的情况下使用OpenCV无缝克隆模块。
*
* 1- 正常克隆
* 2- 混合克隆
* 3- 单色传递
* 4- 颜色改变
* 5- 照明改变
* 6- 纹理平滑
*
* 程序输入一个源图像和一个目标图像(对于前三种方法),输出克隆的图像。
*
* 从github上的opencv_extra文件夹下载测试图片。
*
*/


// 包含OpenCV库的相关头文件
#include "opencv2/photo.hpp" // 包含了OpenCV photo模块的函数定义
#include "opencv2/imgproc.hpp" // 图像处理模块
#include "opencv2/highgui.hpp" // 高层图形界面模块
#include "opencv2/core.hpp" // OpenCV的核心功能模块
#include <iostream>
#include <stdlib.h>


// 使用命名空间std和cv,减少代码中的前缀
using namespace std;
using namespace cv;


// 程序主函数入口
int main()
{
    // 输出选项信息到标准输出
    cout << endl;
    cout << "Cloning Module" << endl;
    cout << "---------------" << endl;
    cout << "Options: " << endl;
    cout << endl;
    cout << "1) Normal Cloning " << endl;
    cout << "2) Mixed Cloning " << endl;
    cout << "3) Monochrome Transfer " << endl;
    cout << "4) Local Color Change " << endl;
    cout << "5) Local Illumination Change " << endl;
    cout << "6) Texture Flattening " << endl;
    cout << endl;
    cout << "Press number 1-6 to choose from above techniques: ";
    int num = 1; // 定义且初始化选项变量
    cin >> num; // 从标准输入读取用户选择
    cout << endl;


    // 通过用户输入的数字选择不同的克隆技术
    if(num == 1) // 如果用户选择1,进行Normal Cloning(正常克隆)
    {
        // 定义图片的路径
        string folder =  "cloning/Normal_Cloning/";
        string original_path1 = folder + "source1.png";
        string original_path2 = folder + "destination1.png";
        string original_path3 = folder + "mask.png";


        // 读取源图像、目标图像和遮罩图像
        Mat source = imread(original_path1, IMREAD_COLOR);
        Mat destination = imread(original_path2, IMREAD_COLOR);
        Mat mask = imread(original_path3, IMREAD_COLOR);


        // 检查图像是否被正确加载
        if(source.empty())
        {
            cout << "Could not load source image " << original_path1 << endl;
            exit(0);
        }
        if(destination.empty())
        {
            cout << "Could not load destination image " << original_path2 << endl;
            exit(0);
        }
        if(mask.empty())
        {
            cout << "Could not load mask image " << original_path3 << endl;
            exit(0);
        }


        Mat result; // 定义结果图像变量
        Point p; // 定义克隆位置的点
        p.x = 400;
        p.y = 100;


        // 执行无缝克隆操作,此处使用正常克隆方法
        seamlessClone(source, destination, mask, p, result, 1);


        // 显示和保存结果图像
        imshow("Output",result);
        imwrite(folder + "cloned.png", result);
    }
    // ... 这以下逻辑类似,分别对应不同的克隆技术选项 ...
    // 具体每个分支逻辑与上述解释相同,这里不再赘述
    else if(num == 2)
    {
        // 如果用户选择2,则进行混合克隆(Mixed Cloning)
        string folder = "cloning/Mixed_Cloning/"; // 定义混合克隆图像的文件夹路径
        string original_path1 = folder + "source1.png"; // 定义源图像路径
        string original_path2 = folder + "destination1.png"; // 定义目标图像路径
        string original_path3 = folder + "mask.png"; // 定义遮罩图像路径
    
        // 读取图像
        Mat source = imread(original_path1, IMREAD_COLOR);
        Mat destination = imread(original_path2, IMREAD_COLOR);
        Mat mask = imread(original_path3, IMREAD_COLOR);
    
        // 检查图像是否成功加载
        if(source.empty())
        {
            cout << "Could not load source image " << original_path1 << endl;
            exit(0); // 如果源图像加载失败,则退出程序
        }
        if(destination.empty())
        {
            cout << "Could not load destination image " << original_path2 << endl;
            exit(0); // 如果目标图像加载失败,则退出程序
        }
        if(mask.empty())
        {
            cout << "Could not load mask image " << original_path3 << endl;
            exit(0); // 如果遮罩图像加载失败,则退出程序
        }
    
        Mat result; // 定义结果图像变量
        Point p; // 定义一个点p,将用作克隆的中心点
        // 设置克隆的中心点为目标图像的中心
        p.x = destination.size().width/2;
        p.y = destination.size().height/2;
    
        // 执行无缝克隆操作,此处使用混合克隆方法
        seamlessClone(source, destination, mask, p, result, 2);
    
        imshow("Output",result); // 在屏幕上显示结果
        imwrite(folder + "cloned.png", result); // 将结果图像写入文件
    }
    else if(num == 3)
    {
        // 如果用户选择3,执行单色传递(Monochrome Transfer)
        string folder = "cloning/Monochrome_Transfer/";  // 单色传递所使用的目录路径
        string original_path1 = folder + "source1.png"; // 源图像的文件路径
        string original_path2 = folder + "destination1.png"; // 目标图像的文件路径
        string original_path3 = folder + "mask.png"; // 遮罩图像的文件路径
    
        // 读取源图像、目标图像和遮罩图像
        Mat source = imread(original_path1, IMREAD_COLOR);
        Mat destination = imread(original_path2, IMREAD_COLOR);
        Mat mask = imread(original_path3, IMREAD_COLOR);
    
        // 检查图像是否成功加载
        if(source.empty())
        {
            cout << "Could not load source image " << original_path1 << endl;
            exit(0); // 如果源图像加载失败,则退出程序
        }
        if(destination.empty())
        {
            cout << "Could not load destination image " << original_path2 << endl;
            exit(0); // 如果目标图像加载失败,则退出程序
        }
        if(mask.empty())
        {
            cout << "Could not load mask image " << original_path3 << endl;
            exit(0); // 如果遮罩图像加载失败,则退出程序
        }
    
        Mat result; // 定义一个用于存放结果的Mat对象
        Point p; // 定义一个点,决定将源图像克隆到目标图像的什么位置
        // 将点设置在目标图像的中心
        p.x = destination.size().width/2;
        p.y = destination.size().height/2;
    
        // 执行无缝克隆操作,此处的方法参数为3,表示单色传递
        seamlessClone(source, destination, mask, p, result, 3);
    
        imshow("Output",result); // 在窗口中显示结果图像
        imwrite(folder + "cloned.png", result); // 将结果图像写入文件中
    }
    else if(num == 4)
    {
        // 如果用户选择4,执行颜色改变(Color Change)
        string folder = "cloning/Color_Change/"; // 颜色改变的目录路径
        string original_path1 = folder + "source1.png"; // 源图像的文件路径
        string original_path2 = folder + "mask.png"; // 遮罩图像的文件路径
    
        Mat source = imread(original_path1, IMREAD_COLOR); // 读取源图像
        Mat mask = imread(original_path2, IMREAD_COLOR); // 读取遮罩图像
    
        // 检查图像是否成功加载
        if(source.empty())
        {
            cout << "Could not load source image " << original_path1 << endl;
            exit(0); // 如果源图像加载失败,则退出程序
        }
        if(mask.empty())
        {
            cout << "Could not load mask image " << original_path2 << endl;
            exit(0); // 如果遮罩图像加载失败,则退出程序
        }
    
        Mat result; // 定义一个用于存放结果的Mat对象
    
        // 执行颜色改变操作
        colorChange(source, mask, result, 1.5, .5, .5);
    
        imshow("Output",result); // 在窗口中显示结果图像
        imwrite(folder + "cloned.png", result); // 将结果图像写入文件中
    }
    else if(num == 5)
    {
        // 如果用户选择5,则进行照明变化(Illumination Change)
        string folder = "cloning/Illumination_Change/"; // 定义照明变化图像的路径
        string original_path1 = folder + "source1.png"; // 源图像的路径
        string original_path2 = folder + "mask.png"; // 遮罩图像的路径
    
        // 读取图像
        Mat source = imread(original_path1, IMREAD_COLOR);
        Mat mask = imread(original_path2, IMREAD_COLOR);
    
        // 检查图像是否成功加载
        if(source.empty())
        {
            cout << "Could not load source image " << original_path1 << endl;
            exit(0); // 如果源图像加载失败,则退出程序
        }
        if(mask.empty())
        {
            cout << "Could not load mask image " << original_path2 << endl;
            exit(0); // 如果遮罩图像加载失败,则退出程序
        }
    
        Mat result; // 定义结果图像
    
        // 对源图像进行照明变化操作
        illuminationChange(source, mask, result, 0.2f, 0.4f);
    
        imshow("Output",result); // 在窗口展示结果图像
        imwrite(folder + "cloned.png", result); // 将结果图像写入文件
    }
    else if(num == 6)
    {
        // 如果用户选择6,进行纹理平滑(Texture Flattening)
        string folder = "cloning/Texture_Flattening/"; // 纹理平滑图像的路径
        string original_path1 = folder + "source1.png"; // 源图像的路径
        string original_path2 = folder + "mask.png"; // 遮罩图像的路径
    
        // 读取图像
        Mat source = imread(original_path1, IMREAD_COLOR);
        Mat mask = imread(original_path2, IMREAD_COLOR);
    
        // 检查图像是否成功加载
        if(source.empty())
        {
            cout << "Could not load source image " << original_path1 << endl;
            exit(0); // 如果源图像加载失败,则退出程序
        }
        if(mask.empty())
        {
            cout << "Could not load mask image " << original_path2 << endl;
            exit(0); // 如果遮罩图像加载失败,则退出程序
        }
    
        Mat result; // 定义结果图像
    
        // 对源图像进行纹理平滑操作
        textureFlattening(source, mask, result, 30, 45, 3);
    
        imshow("Output",result); // 在窗口展示结果图像
        imwrite(folder + "cloned.png", result); // 将结果图像写入文件
    }
    waitKey(0); // 等待用户按键,然后退出
}

这段代码是一个使用OpenCV库中的无缝克隆功能的C++程序,它提供了六种不同的图像处理技术,包括正常克隆、混合克隆、单色传递、局部颜色改变、局部照明改变和纹理平滑。用户可以选择一种方法,程序将对用户指定的源图像和目标图像进行处理,并输出克隆后的图像。程序中还包含了错误处理部分,确保加载的图像是有效的。最后,它会展示处理结果,并将其保存到指定的文件夹中。

5. cloning_gui.cpp

cpp 复制代码
/* 
* cloning.cpp 
* 
* 这个文件主要实现了图像无缝克隆的相关功能.包含以下几种模式:
* 1- 常规克隆
* 2- 混合克隆
* 3- 单色传递
* 4- 颜色变更
* 5- 光照变更
* 6- 纹理平滑
*
* 克隆过程主要分为两步:
* 第一步:选择源图像的兴趣区域
* 第二步:在目标图像中选择将兴趣区域放置的位置
* 结果:显示处理后的图像
*/
// 包含信号处理、OpenCV包含的头文件和C++标准输入输出库
#include <signal.h>
#include "opencv2/photo.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/core.hpp"
#include <iostream>
#include <stdlib.h>


// 避免命名空间std中的beta和C++17中的std::beta发生冲突
using std::cin;
using std::cout;
using std::endl;
using std::string;


// 使用OpenCV命名空间
using namespace cv;


// 声明全局的Mat类型变量和图像处理所需的变量
Mat img0, img1, img2, res, res1, final, final1, blend;
Point point;
int drag = 0;
int destx, desty;
int numpts = 100;
Point* pts = new Point[100];
Point* pts2 = new Point[100];
Point* pts_diff = new Point[100];


// 使用的辅助变量
int var = 0;
int flag = 0, flag1 = 0, flag4 = 0;


// 用于存储兴趣区域的范围
int minx, miny, maxx, maxy, lenx, leny;
int minxd, minyd, maxxd, maxyd, lenxd, lenyd;


// 用户输入参数
int channel, num, kernel_size;
float alpha, beta;
float red, green, blue;
float low_t, high_t;


// 声明函数原型
void source(int, int, int, int, void*);
void destination(int, int, int, int, void*);
void checkfile(char*);


// source函数定义:处理原图的鼠标事件
// 例如:选择ROI区域,绘制ROI多边形轮廓
void source(int event, int x, int y, int, void*)
{
    // 鼠标左键按下事件,开始拖动
    if (event == EVENT_LBUTTONDOWN && !drag)
    {
        // 如果还没有选择ROI区域,开始映射点
        if (flag1 == 0)
        {
            if (var == 0)
                img1 = img0.clone(); // 克隆原图
            point = Point(x, y); // 记录点击点
            circle(img1, point, 2, Scalar(0, 0, 255), -1, 8, 0); // 在图上标出点击点
            pts[var] = point; // 将其添加到点集合
            var++; // 点数量加一
            drag = 1; // 标记正在拖动
            //如果是连续的点,绘制线段连接
            if (var > 1)
                line(img1, pts[var - 2], point, Scalar(0, 0, 255), 2, 8, 0);


            cv::imshow("Source", img1); // 显示绘制变化后的原图
        }
    }
    // 鼠标左键释放,结束拖动
    if (event == EVENT_LBUTTONUP && drag)
    {
        imshow("Source", img1);


        drag = 0;
    }
    // 处理鼠标右键按下事件
    if (event == EVENT_RBUTTONDOWN) // 如果鼠标右键被按下
    {
        flag1 = 1; // 设置标识,表示区域选择已经开始或完成
        img1 = img0.clone(); // 克隆原始图像,img1作为显示的副本
        for (int i = var; i < numpts; i++) // 将多边形未闭合的点设置为最后一个点
            pts[i] = point;


        // 如果var不为0,即已选择多个点,则连接它们
        if (var != 0)
        {
            const Point* pts3[1] = { &pts[0] }; // 创建包含点的数组
            polylines(img1, pts3, &numpts, 1, 1, Scalar(0, 0, 0), 2, 8, 0); // 在img1上绘制多边形,闭合区域
        }


        // 遍历所有点以计算最大最小的X和Y值,用于确定选择区域的范围
        for (int i = 0; i < var; i++)
        {
            minx = min(minx, pts[i].x);
            maxx = max(maxx, pts[i].x);
            miny = min(miny, pts[i].y);
            maxy = max(maxy, pts[i].y);
        }
        // 计算长度和宽度
        lenx = maxx - minx;
        leny = maxy - miny;


        // 计算中心点
        int mid_pointx = minx + lenx / 2;
        int mid_pointy = miny + leny / 2;


        // 修正每个点相对于中心点的位置
        for (int i = 0; i < var; i++)
        {
            pts_diff[i].x = pts[i].x - mid_pointx;
            pts_diff[i].y = pts[i].y - mid_pointy;
        }


        // 显示原图img1
        imshow("Source", img1);
    }


    // 处理鼠标右键释放事件
    if (event == EVENT_RBUTTONUP) // 如果鼠标右键释放
    {
        flag = var; // 设置标识表示区域已选完


        // 创建黑色背景的图像和掩模
        final = Mat::zeros(img0.size(), CV_8UC3);
        res1 = Mat::zeros(img0.size(), CV_8UC1);
        const Point* pts4[1] = { &pts[0] }; // 创建点数组


        // 利用已选择的点填充掩模
        fillPoly(res1, pts4, &numpts, 1, Scalar(255, 255, 255), 8, 0);
        // 使用掩模提取原图对应的区域
        bitwise_and(img0, img0, final, res1); // 应用掩码


        // 显示更新后的原图img1
        imshow("Source", img1);


        // 如果当前选项为4,则执行颜色变更转换
        if (num == 4)
        {
            colorChange(img0, res1, blend, red, green, blue); // 颜色变更
            imshow("Color Change Image", blend); // 显示颜色变更后的图像
            waitKey(0); // 等待键盘输入
        }
        // 如果当前选项为5,则执行光照变更转换
        else if (num == 5)
        {
            illuminationChange(img0, res1, blend, alpha, beta); // 光照变更
            imshow("Illum Change Image", blend); // 显示光照变更后的图像
            waitKey(0); // 等待键盘输入
        }
        // 如果当前选项为6,则执行纹理平滑转换
        else if (num == 6)
        {
            textureFlattening(img0, res1, blend, low_t, high_t, kernel_size); // 纹理平滑
            imshow("Texture Flattened", blend); // 显示纹理平滑后的图像
            waitKey(0); // 等待键盘输入
        }
    }
    // 处理鼠标中键按下事件,重置所有参数并显示原始图像
    if (event == EVENT_MBUTTONDOWN) // 如果鼠标中键被按下
    {
        // 重置所有点
        for (int i = 0; i < numpts; i++)
        {
            pts[i].x = 0;
            pts[i].y = 0;
        }
        var = 0; // 重置点计数器
        flag1 = 0; // 重置ROI选择标识
        // 重置最大最小值
        minx = INT_MAX; miny = INT_MAX; maxx = INT_MIN; maxy = INT_MIN;
        // 显示原始图像
        imshow("Source", img0);
        // 如果是1、2、3号选项,则还需要显示目标图像
        if (num == 1 || num == 2 || num == 3)
            imshow("Destination", img2);
        // 重置拖拽标识
        drag = 0;
    }
}


// 目标图像窗口的鼠标事件处理函数
void destination(int event, int x, int y, int, void*)
{
    Mat im1; // 定义中间变量im1用于图像操作
    // 初始化目标图像的最小和最大x、y坐标值
    minxd = INT_MAX; minyd = INT_MAX; maxxd = INT_MIN; maxyd = INT_MIN;
    im1 = img2.clone(); // 克隆目标图像img2到im1


    // 当鼠标左键按下时
    if (event == EVENT_LBUTTONDOWN)
    {
        flag4 = 1; // 设置已选取目标区域标志
        if (flag1 == 1) // 如果源图像上的选区已经完成
        {
            point = Point(x, y); // 设置新的点


            // 对于选择好的每一个源点,根据目标中的点击点,平移相应距离
            for (int i = 0; i < var; i++)
            {
                pts2[i].x = point.x + pts_diff[i].x;
                pts2[i].y = point.y + pts_diff[i].y;
            }


            // 未设置的目标点也赋予一个基准位置
            for (int i = var; i < numpts; i++)
            {
                pts2[i].x = point.x + pts_diff[0].x;
                pts2[i].y = point.y + pts_diff[0].y;
            }


            // 配置点集用于绘制多边形
            const Point* pts5[1] = { &pts2[0] };
            polylines(im1, pts5, &numpts, 1, 1, Scalar(0, 0, 255), 2, 8, 0); // 在目标图像im1上绘制多边形


            // 更新目标点的位置
            destx = x;
            desty = y;


            // 显示目标图像
            imshow("Destination", im1);
        }
    }


    // 鼠标右键释放,完成对选区图像的传递
    if (event == EVENT_RBUTTONUP)
    {
        // 计算目标图像上选择区域的边界
        for (int i = 0; i < flag; i++)
        {
            minxd = min(minxd, pts2[i].x);
            maxxd = max(maxxd, pts2[i].x);
            minyd = min(minyd, pts2[i].y);
            maxyd = max(maxyd, pts2[i].y);
        }


        // 确保目标区域不超出目标图像边界
        if (maxxd > im1.size().width || maxyd > im1.size().height || minxd < 0 || minyd < 0)
        {
            cout << "Index out of range" << endl; // 打印错误信息
            exit(0); // 异常退出
        }


        // 创建一个黑色背景的Mat变量
        final1 = Mat::zeros(img2.size(), CV_8UC3);
        res = Mat::zeros(img2.size(), CV_8UC1); // 创建掩码


        // 复制源图像选区到目标图像对应位置
        for (int i = miny, k = minyd; i < (miny + leny); i++, k++)
            for (int j = minx, l = minxd; j < (minx + lenx); j++, l++)
            {
                for (int c = 0; c < channel; c++)
                {
                    final1.at<uchar>(k, l * channel + c) = final.at<uchar>(i, j * channel + c); // 使用.channels取通道数
                }
            }


        // 标记目标图像已选取区域
        const Point* pts6[1] = { &pts2[0] };
        fillPoly(res, pts6, &numpts, 1, Scalar(255, 255, 255), 8, 0);


        // 如果是用于无缝克隆的场合
        if (num == 1 || num == 2 || num == 3)
        {
            seamlessClone(img0, img2, res1, point, blend, num); // 执行无缝克隆
            imshow("Cloned Image", blend); // 显示克隆后的目标图像
            imwrite("cloned.png", blend); // 将克隆结果图像写入文件
            waitKey(0); // 等待用户响应
        }


        // 重置目标图像多边形的点集
        for (int i = 0; i < flag; i++)
        {
            pts2[i].x = 0;
            pts2[i].y = 0;
        }


        // 重新初始化目标图像多边形的参数
        minxd = INT_MAX; minyd = INT_MAX; maxxd = INT_MIN; maxyd = INT_MIN;
    }


    im1.release(); // 释放im1内存
}




int main()
{
    // 主函数入口
    cout << endl;
    cout << "Cloning Module" << endl; // 输出克隆模块的标题
    cout << "---------------" << endl;
    cout << "Step 1:" << endl;  // 第一步的说明
    cout << " -> In the source image, select the region of interest by left click mouse button. A Polygon ROI will be created by left clicking mouse button." << endl;
    // 输出如何在源图片中选择兴趣区域(鼠标左键单击)
    cout << " -> To set the Polygon ROI, click the right mouse button or use 'd' key" << endl;
    // 输出如何设置多边形的兴趣区域(鼠标右键单击或按'd'键)
    cout << " -> To reset the region selected, click the middle mouse button or use 'r' key." << endl;
    // 输出如何重置已选区域(鼠标中键单击或按'r'键)


    cout << "Step 2:" << endl;  // 第二步的说明
    cout << " -> In the destination image, select the point where you want to place the ROI in the image by left clicking mouse button." << endl;
    // 输出如何在目标图像中通过左键设置ROI的放置点
    cout << " -> To get the cloned result, click the right mouse button or use 'c' key." << endl;
    // 输出如何获取克隆结果(鼠标右键单击或按'c' 键)
    cout << " -> To quit the program, use 'q' key." << endl;  // 输出如何退出程序(按'q'键)
    cout << endl;
    cout << "Options: " << endl;  // 提供克隆操作的选项
    cout << endl;
    cout << "1) Normal Cloning " << endl;  // 常规克隆
    cout << "2) Mixed Cloning " << endl;  // 混合克隆
    cout << "3) Monochrome Transfer " << endl;  // 单色传递
    cout << "4) Local Color Change " << endl;  // 局部颜色变更
    cout << "5) Local Illumination Change " << endl;  // 局部光照变更
    cout << "6) Texture Flattening " << endl;  // 纹理平滑


    cout << endl;


    cout << "Press number 1-6 to choose from above techniques: ";
    cin >> num;  // 从用户那里获取用户想尝试的克隆技术的编号
    cout << endl;


    // 初始化各种变量
    minx = INT_MAX; miny = INT_MAX; maxx = INT_MIN; maxy = INT_MIN;


    minxd = INT_MAX; minyd = INT_MAX; maxxd = INT_MIN; maxyd = INT_MIN;


    int flag3 = 0;  // 控制流程的标志变量


    if (num == 1 || num == 2 || num == 3)
    {
        // 如果用户选择了1、2或3号技术
        string src, dest;
        cout << "Enter Source Image: ";
        cin >> src;  // 输入源图像路径


        cout << "Enter Destination Image: ";
        cin >> dest;  // 输入目标图像路径


        img0 = imread(src);  // 读取源图像
        img2 = imread(dest);  // 读取目标图像


        // 如果读取图像失败,则退出
        if (img0.empty())
        {
            cout << "Source Image does not exist" << endl;
            exit(0);
        }
        if (img2.empty())
        {
            cout << "Destination Image does not exist" << endl;
            exit(0);
        }


        channel = img0.channels();  // 获取图像通道数


        // 初始化各种图像
        res = Mat::zeros(img2.size(), CV_8UC1);
        res1 = Mat::zeros(img0.size(), CV_8UC1);
        final = Mat::zeros(img0.size(), CV_8UC3);
        final1 = Mat::zeros(img2.size(), CV_8UC3);
         source image ///


        namedWindow("Source", 1);  // 创建一个名为"Source"的窗口
        setMouseCallback("Source", source, NULL);  // 设置该窗口的鼠标回调函数
        imshow("Source", img0);  // 显示源图像


        /// destination image ///


        namedWindow("Destination", 1);  // 创建一个名为"Destination"的窗口
        setMouseCallback("Destination", destination, NULL);  // 设置该窗口的鼠标回调函数
        imshow("Destination", img2);  // 显示目标图像
    }
    else if (num == 4)
    {
        // 如果选中的是第4个选项(Local Color Change),则进入此代码块
        string src;
        cout << "Enter Source Image: "; // 请求输入源图像的路径
        cin >> src; // 读取源图像路径


        cout << "Enter RGB values: " << endl; // 请求输入RGB值
        cout << "Red: ";
        cin >> red; // 读取红色通道的值


        cout << "Green: ";
        cin >> green; // 读取绿色通道的值


        cout << "Blue: ";
        cin >> blue; // 读取蓝色通道的值


        img0 = imread(src); // 读取源图像


        if (img0.empty()) // 检查源图像是否读取成功
        {
            cout << "Source Image does not exist" << endl; // 如果源图像不存在,则输出错误信息并退出程序
            exit(0);
        }


        res1 = Mat::zeros(img0.size(), CV_8UC1); // 创建与源图像同尺寸的矩阵,并初始化为0
        final = Mat::zeros(img0.size(), CV_8UC3); // 创建与源图像同尺寸的3通道矩阵,并初始化为0


        // 初始化源图像窗口并设置回调函数
        namedWindow("Source", 1); // 创建用于选择区域的源图像窗口
        setMouseCallback("Source", source, NULL); // 设置鼠标回调函数用于选择区域
        imshow("Source", img0); // 显示源图像
    }
    else if (num == 5)
    {
        // 如果选中的是第5个选项(Local Illumination Change),则进入此代码块
        string src;
        cout << "Enter Source Image: "; // 请求输入源图像的路径
        cin >> src; // 读取源图像路径


        cout << "alpha: "; // 请求输入alpha值
        cin >> alpha; // 读取alpha(影响光照的参数)


        cout << "beta: "; // 请求输入beta值
        cin >> beta; // 读取beta(影响对比度的参数)


        img0 = imread(src); // 读取源图像


        if (img0.empty()) // 检查源图像是否读取成功
        {
            cout << "Source Image does not exist" << endl; // 如果源图像不存在,则输出错误信息并退出程序
            exit(0);
        }


        res1 = Mat::zeros(img0.size(), CV_8UC1); // 创建与源图像同尺寸的矩阵,并初始化为0
        final = Mat::zeros(img0.size(), CV_8UC3); // 创建与源图像同尺寸的3通道矩阵,并初始化为0


        // 初始化源图像窗口并设置回调函数
        namedWindow("Source", 1); // 创建用于选择区域的源图像窗口
        setMouseCallback("Source", source, NULL); // 设置鼠标回调函数用于选择区域
        imshow("Source", img0); // 显示源图像
    }
    else if (num == 6)
    {
        // 如果选中的是第6个选项(Texture Flattening),则进入此代码块
        string src;
        cout << "Enter Source Image: "; // 请求输入源图像的路径
        cin >> src; // 读取源图像路径


        cout << "low_threshold: "; // 请求输入low_threshold值
        cin >> low_t; // 读取low_threshold


        cout << "high_threshold: "; // 请求输入high_threshold值
        cin >> high_t; // 读取high_threshold


        cout << "kernel_size: "; // 请求输入kernel_size值
        cin >> kernel_size; // 读取kernel_size


        img0 = imread(src); // 读取源图像


        if (img0.empty()) // 检查源图像是否读取成功
        {
            cout << "Source Image does not exist" << endl; // 如果源图像不存在,则输出错误信息并退出程序
            exit(0);
        }


        res1 = Mat::zeros(img0.size(), CV_8UC1); // 创建与源图像同尺寸的矩阵,并初始化为0
        final = Mat::zeros(img0.size(), CV_8UC3); // 创建与源图像同尺寸的3通道矩阵,并初始化为0


        // 初始化源图像窗口并设置回调函数
        namedWindow("Source", 1); // 创建用于选择区域的源图像窗口
        setMouseCallback("Source", source, NULL); // 设置鼠标回调函数用于选择区域
        imshow("Source", img0); // 显示源图像
    }
    else
    {
        cout << "Wrong Option Chosen" << endl;
        exit(0);
    }


    for (;;) // 无限循环,等待用户按键输入
    {
        char key = (char)waitKey(0); // 等待用户按键


        if (key == 'd' && flag3 == 0) // 如果用户按了'd'键并且flag3等于0
        {
            flag1 = 1; // 设置标志变量flag1为1
            flag3 = 1; // 设置标志变量flag3为1
            img1 = img0.clone(); // 克隆原始图像img0到img1
            for (int i = var; i < numpts; i++) // 将未完成的多边形点设置为当前点
                pts[i] = point;


            if (var != 0) // 如果var不等于0,即点数量不为零
            {
                const Point* pts3[1] = { &pts[0] };
                polylines(img1, pts3, &numpts, 1, 1, Scalar(0, 0, 0), 2, 8, 0);
                // 在img1上绘制多边形线条
            }


            for (int i = 0; i < var; i++) // 遍历所有点,计算出最小和最大的x,y值
            {
                minx = min(minx, pts[i].x);
                maxx = max(maxx, pts[i].x);
                miny = min(miny, pts[i].y);
                maxy = max(maxy, pts[i].y);
            }
            lenx = maxx - minx; // 计算x方向长度
            leny = maxy - miny; // 计算y方向长度


            int mid_pointx = minx + lenx / 2; // 计算中心点x坐标
            int mid_pointy = miny + leny / 2; // 计算中心点y坐标


            for (int i = 0; i < var; i++)
            {
                pts_diff[i].x = pts[i].x - mid_pointx; // 计算点与中心点的x差值
                pts_diff[i].y = pts[i].y - mid_pointy; // 计算点与中心点的y差值
            }


            flag = var; // 更新flag


            final = Mat::zeros(img0.size(), CV_8UC3); // 初始化final图像
            res1 = Mat::zeros(img0.size(), CV_8UC1); // 初始化res1图像
            const Point* pts4[1] = { &pts[0] };


            fillPoly(res1, pts4, &numpts, 1, Scalar(255, 255, 255), 8, 0); // 在res1上填充多边形
            bitwise_and(img0, img0, final, res1); // 使用res1作为掩码对img0进行位与操作


            imshow("Source", img1); // 显示更新后的源图像
        }
        else if (key == 'r') // 如果用户按了'r'键
        {
            // 重置所有预选的点
            for (int i = 0; i < numpts; i++)
            {
                pts[i].x = 0;
                pts[i].y = 0;
            }
            // 重置相关变量和标志位
            var = 0;
            flag1 = 0;
            flag3 = 0;
            flag4 = 0;
            minx = INT_MAX; miny = INT_MAX; maxx = INT_MIN; maxy = INT_MIN;
            // 显示原始图片
            imshow("Source", img0);
            if (num == 1 || num == 2 || num == 3)
                imshow("Destination", img2); // 如果是1、2、3号技术,也显示目的图片
            drag = 0;
        }
        else if ((num == 1 || num == 2 || num == 3) && key == 'c' && flag1 == 1 && flag4 == 1)
        {
            // 对于1、2、3号克隆技术,当按下'c'键时进行无缝克隆操作
            seamlessClone(img0, img2, res1, point, blend, num); // 无缝克隆操作
            imshow("Cloned Image", blend); // 显示克隆后的图片
            imwrite("cloned.png", blend); // 将克隆后的图片保存到文件
        }
        else if (num == 4 && key == 'c' && flag1 == 1)
        {
            // 对于4号技术,颜色变换
            colorChange(img0, res1, blend, red, green, blue); // 颜色变换操作
            imshow("Color Change Image", blend); // 显示颜色变换后的图片
            imwrite("cloned.png", blend); // 将颜色变换后的图片保存到文件
        }
        else if (num == 5 && key == 'c' && flag1 == 1)
        {
            // 对于5号技术,光照变换
            illuminationChange(img0, res1, blend, alpha, beta); // 光照变换操作
            imshow("Illum Change Image", blend); // 显示光照变换后的图片
            imwrite("cloned.png", blend); // 将光照变换后的图片保存到文件
        }
        else if (num == 6 && key == 'c' && flag1 == 1)
        {
            // 对于6号技术,纹理平滑
            textureFlattening(img0, res1, blend, low_t, high_t, kernel_size); // 纹理平滑操作
            imshow("Texture Flattened", blend); // 显示纹理平滑后的图片
            imwrite("cloned.png", blend); // 将纹理平滑后的图片保存到文件
        }
        else if (key == 'q') // 如果用户按了'q'键
            exit(0); // 退出程序
    }
}
相关推荐
吃个糖糖11 分钟前
36 Opencv SURF 关键点检测
人工智能·opencv·计算机视觉
AI慧聚堂23 分钟前
自动化 + 人工智能:投标行业的未来是什么样的?
运维·人工智能·自动化
盛世隐者24 分钟前
【pytorch】循环神经网络
人工智能·pytorch
cdut_suye37 分钟前
Linux工具使用指南:从apt管理、gcc编译到makefile构建与gdb调试
java·linux·运维·服务器·c++·人工智能·python
开发者每周简报1 小时前
微软的AI转型故事
人工智能·microsoft
古希腊掌管学习的神1 小时前
[机器学习]sklearn入门指南(1)
人工智能·python·算法·机器学习·sklearn
普密斯科技1 小时前
手机外观边框缺陷视觉检测智慧方案
人工智能·计算机视觉·智能手机·自动化·视觉检测·集成测试
四口鲸鱼爱吃盐2 小时前
Pytorch | 利用AI-FGTM针对CIFAR10上的ResNet分类器进行对抗攻击
人工智能·pytorch·python
lishanlu1362 小时前
Pytorch分布式训练
人工智能·ddp·pytorch并行训练
日出等日落2 小时前
从零开始使用MaxKB打造本地大语言模型智能问答系统与远程交互
人工智能·语言模型·自然语言处理