将彩色图像转换为去色图像(灰度图像)和 颜色增强图像
HDR 图像合成,并同时执行色调映射和曝光融合
非真实感渲染(NPR)
正常克隆、混合克隆、单色传递、局部颜色改变、局部照明改变和纹理平滑
1. decolorization将彩色图像转换为去色图像(灰度图像)和 颜色增强图像
本段代码的功能是使用OpenCV的去色模块将输入的彩色图像转换为灰度图像和色彩提升图像,并显示这三种图像。程序首先解析命令行参数获取图像文件名,然后读取彩色图像。如果读取成功,则使用decolor
函数处理图像。最后,使用imshow
函数分别显示原始彩色图像、生成的灰度图像和色彩提升图像,并等待用户操作来结束程序
// 本教程展示了如何使用 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.cpp HDR 图像合成,并同时执行色调映射和曝光融合
memorial.png
fusion.png
ldr.png
该段代码是一个完整的程序,用于 HDR 图像合成,并同时执行色调映射和曝光融合。程序首先通过命令行参数加载一系列不同曝光时间的图像,使用Debevec方法估算相机响应,然后生成HDR图像,对HDR图像进行色调映射以生成LDR图像,最后执行曝光融合来生成一个动态范围较宽的显示图像。程序把所有生成的结果以图像文件的形式写入到磁盘中。
#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.pngimwrite("ldr.png", ldr * 255); // 将色调映射的结果乘以255后写入文件ldr.pngimwrite("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)的示例程序,包括以下几种处理方式:
边缘保留平滑:基于标准化卷积滤波器或递归滤波器。
细节增强:强调图像中的细节。
铅笔素描/彩色铅笔画:将图像转化为铅笔素描或彩色铅笔画样式。
风格化:对图像进行风格化处理。
用户可以通过输入对应的数字来选择相应的处理方式。
/*
* 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 正常克隆、混合克隆、单色传递、局部颜色改变、局部照明改变和纹理平滑
/*
* 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
/*
* 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;}// 显示原图img1imshow("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); // 应用掩码// 显示更新后的原图img1imshow("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); // 创建与源图像同尺寸的矩阵,并初始化为0final = 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); // 创建与源图像同尺寸的矩阵,并初始化为0final = 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_thresholdcout << "high_threshold: "; // 请求输入high_threshold值cin >> high_t; // 读取high_thresholdcout << "kernel_size: "; // 请求输入kernel_size值cin >> kernel_size; // 读取kernel_sizeimg0 = imread(src); // 读取源图像if (img0.empty()) // 检查源图像是否读取成功{cout << "Source Image does not exist" << endl; // 如果源图像不存在,则输出错误信息并退出程序exit(0);}res1 = Mat::zeros(img0.size(), CV_8UC1); // 创建与源图像同尺寸的矩阵,并初始化为0final = 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为1flag3 = 1; // 设置标志变量flag3为1img1 = img0.clone(); // 克隆原始图像img0到img1for (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; // 更新flagfinal = 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); // 退出程序}
}