【opencv】教程代码 —photo

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

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

  • 非真实感渲染(NPR)

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

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

4ad518bbcd4403e24e60cac476622b1e.png

本段代码的功能是使用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); // 等待用户按键
}

c47895f37b894a2efce223c9437a0fdb.png

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

cb15b034cfbc1dbb7fb0e7161d23a338.png

memorial.png

20dbeaeb3dd317b9856376ce5ddf85ba.png

fusion.png

0935854728f730737c2ba092fe67fad0.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();
}

14d1114ba69644fad9b4935aa8e71b9b.png

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

c7b996966b413d59b7034f7a84d71057.png

 Using Normalized convolution Filter

1d198a0c6eeaf6ab08263e61a4e0f47d.png

Using Recursive Filter

9cb63bda6a7d9d8c99fff42c0ed61590.png

Detail Enhancement

d8585bcfe33c87ca993aa5e1e4fd7e36.png

Pencil sketch/Color Pencil Drawing

f139f7dc3bf3cc2c97387d781d1dec76.png

 Stylization

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

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

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

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

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

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

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

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

  4. 风格化:对图像进行风格化处理。
    用户可以通过输入对应的数字来选择相应的处理方式。

/*
* 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表示无限期等待
}

d9f51c53b4b08f85e452e454bea69092.png

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

3d53de28affb230207ac520d5860de2a.png

/*
* 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

f59129b5cf197923a04fa8d2268bd13d.png

/* 
* 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); // 退出程序}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/792863.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序 电影院售票选座票务系统5w7l6

uni-app框架&#xff1a;使用Vue.js开发跨平台应用的前端框架&#xff0c;编写一套代码&#xff0c;可编译到Android、小程序等平台。 框架支持:springboot/Ssm/thinkphp/django/flask/express均支持 前端开发:vue.js 可选语言&#xff1a;pythonjavanode.jsphp均支持 运行软件…

竞赛 Yolov安全帽佩戴检测 危险区域进入检测 - 深度学习 opencv

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; Yolov安全帽佩戴检测 危险区域进入检测 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;4分 该项目较为新颖&am…

Flutter仿Boss-4.短信验证码界面

效果 简述 在移动应用开发中&#xff0c;处理短信验证码是确保用户身份验证和安全性的重要步骤。本文将介绍如何使用Flutter构建一个短信验证码界面&#xff0c;让用户输入通过短信发送到他们手机的四位验证码。 依赖项 在这个项目中&#xff0c;我们将使用以下依赖项&#…

vue快速入门(六)v-else和v-else-if

注释很详细&#xff0c;直接上代码 上一篇 新增内容 v-else-if用法v-else用法 源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-s…

Excel、PowerQuery 和 ChatGPT 终极手册(上)

原文&#xff1a;Ultimate ChatGPT Handbook for Enterprises 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 序言 在不断发展的数据管理和分析领域中&#xff0c;掌握 Excel 的查找功能不仅是一种技能&#xff0c;更是高效数据处理的基石。《使用 Power Query 和 Ch…

论文阅读:Walk These Ways: 通过行为多样性调整机器人控制以实现泛化

Walk These Ways: 通过行为多样性调整机器人控制以实现泛化 摘要&#xff1a; 通过学习得到的运动策略可以迅速适应与训练期间经历的类似环境&#xff0c;但在面对分布外测试环境失败时缺乏快速调整的机制。这就需要一个缓慢且迭代的奖励和环境重新设计周期来在新任务上达成良…

Django模板层——三种自定义模板simple_tag、inclusion_tag、filter的用法

目录 1. 前言 2. 前置操作 3. simple_tag 3.1 注意点 4. inclusion_tag 5. filter 6. 结尾 1. 前言 在前后端不分离的模式中&#xff0c;Django的模板语法尤为重要&#xff0c;我们可以动态传入变量&#xff0c;并在前端HTML中进行展示。在变量展示时&#xff0c;会有一…

JavaWeb中的Servlet是什么?怎么使用?

文章目录 一、什么是Servlet二、Servlet的基本内容1、Servlet的作用2、Servlet接口3、Servlet接口实现类4、Servlet接口实现类开发步骤5、Servlet对象生命周期6、HttpServletResquest接口7、HttpServletResponse接口8、请求对象和响应对象流程图9、请求对象和响应对象生命周期1…

vulhub中Apache Solr 远程命令执行漏洞复现(CVE-2019-0193)

Apache Solr 是一个开源的搜索服务器。Solr 使用 Java 语言开发&#xff0c;主要基于 HTTP 和 Apache Lucene 实现。此次漏洞出现在Apache Solr的DataImportHandler&#xff0c;该模块是一个可选但常用的模块&#xff0c;用于从数据库和其他源中提取数据。它具有一个功能&#…

vue给input密码框设置眼睛睁开闭合对于密码显示与隐藏

<template><div class"login-container"><el-inputv-model"pwd":type"type"class"pwd-input"placeholder"请输入密码"><islot"suffix"class"icon-style":class"elIcon"…

三子棋(C游戏)

文章目录 三子棋的描述思路关键代码运行代码 三子棋的描述 三子棋是一种民间传统游戏&#xff0c;又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏分为双方对战&#xff0c;双方依次在9宫格棋盘上摆放棋子&#xff0c;率先将自己的三个棋子走成一条线就视为胜利&#xff0c;…

实现 select 中嵌套 tree 外加搜索

实现 select 中嵌套 tree 外加搜索 参考地址实现地址 代码 <el-form-item label"考核人员" prop"userIdArr" v-if"title 发起考核"><el-popover v-model"popoverVisible" placement"bottom" trigger"cli…

论文笔记:Teaching Large Language Models to Self-Debug

ICLR 2024 REVIEWER打分 6666 1 论文介绍 论文提出了一种名为 Self-Debugging 的方法&#xff0c;通过执行生成的代码并基于代码和执行结果生成反馈信息&#xff0c;来引导模型进行调试不同于需要额外训练/微调模型的方法&#xff0c;Self-Debugging 通过代码解释来指导模型识…

Windows Server 2008 (限制用户登录时间、为客户机设置统一的桌面背景、管理用户统一安装软件、隐藏用户C盘)

一、限制用户登陆时间 Server&#xff1a; Client&#xff1a; Server&#xff1a; 将新建的用户移动到group1下 限制用户登陆时间 二、为客户机设置统一的桌面背景 Server&#xff1a; 将jpg图片放到abc文件夹中&#xff0c;并且设置文件夹共享模式 三、管理用户统一安装软件…

DFS:深搜+回溯+剪枝解决矩阵搜索问题

创作不易&#xff0c;感谢三连&#xff01;&#xff01; 一、N皇后 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:vector<vector<string>> ret;vector<string> path;bool checkcol[9];bool checkdig1[18];bool checkdig2[18];int n…

WordPress主题–Applay v3.7.1 开心版下载

Applay是一款功能强大的多用途WordPress主题&#xff0c;专为应用展示、应用商店、商业和购物等Woocommerce网站而设计。它配备了拖曳式页面编辑功能&#xff0c;类似于Elementor&#xff0c;让您能够轻松构建和定制您的网站。无论您有什么需求&#xff0c;都可以尝试下这个主题…

Flutter 画笔(Paint)、绘制直线(drawLine)

override bool shouldRepaint(CustomPainter oldDelegate) > true; } class MyPainter extends CustomPainter { override void paint(Canvas canvas, Size size) { //画背景 var paint Paint() …isAntiAlias false …strokeWidth30.0 …color Colors.red; c…

金融贷款批准预测项目

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 在金融服务行业&#xff0c;贷款审批是一项关键任务&#xff0c;它不仅关系到资金的安全&#xff0c;还直接影响到金融机构的运营效率和风险管理…

8000预算可以购买阿里云服务器配置整理

一个月8000元预算如何选择阿里云服务器配置&#xff1f;八千预算可选的阿里云服务器配置相当高了&#xff0c;这个预算可以购买阿里云企业级独享型云服务器&#xff0c;至少8核以上的配置&#xff0c;这个预算可以支持复杂、高负载或大规模的业务需求。阿里云服务器网整理8000元…

微信小程序python+uniapp高校图书馆图书借阅管理系统ljr9i

根据日常实际需要&#xff0c;一方面需要在系统中实现基础信息的管理&#xff0c;同时还需要结合实际情况的需要&#xff0c;提供图书信息管理功能&#xff0c;方便图书管理工作的展开&#xff0c;综合考虑&#xff0c;本套系统应该满足如下要求&#xff1a; 首先&#xff0c;在…