-
将彩色图像转换为去色图像(灰度图像)和 颜色增强图像
-
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)的示例程序,包括以下几种处理方式:
-
边缘保留平滑:基于标准化卷积滤波器或递归滤波器。
-
细节增强:强调图像中的细节。
-
铅笔素描/彩色铅笔画:将图像转化为铅笔素描或彩色铅笔画样式。
-
风格化:对图像进行风格化处理。
用户可以通过输入对应的数字来选择相应的处理方式。
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); // 退出程序
}
}