/* 本教程演示如何在没有 GUI 的情况下使用 OpenCV 无缝克隆模块。
*
* 1- 正常克隆
* 2- 混合克隆
* 3- 单色转移
* 4-颜色变化
* 5- 照明变化
* 6-纹理展平
* 程序将源图像和目标图像(用于 1-3 种方法)作为输入,并输出克隆图像。
*
* 从 opencv_extra 存储库下载测试图像。
*/
#include "opencv2/photo.hpp" //计算摄影
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/core.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
cout << endl;
cout << "Note: specify OPENCV_SAMPLES_DATA_PATH_HINT=<opencv_extra>/testdata/cv" << endl << 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:按数字 1-6 从上述技术中进行选择 ";
int num = 1;
cin >> num;
cout << endl;
if(num == 1)//正常克隆
{ //准备原始图片路径
string folder = "cloning/Normal_Cloning/";
string original_path1 = samples::findFile(folder + "source1.png");
string original_path2 = samples::findFile(folder + "destination1.png");
string original_path3 = samples::findFile(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;
//输入source截取目标的大图,输入destination待粘贴融合的目标背景图标,mask目标掩码区域图像,
seamlessClone(source, destination, mask, p, result, 1);//p 对象被放置在目标图像dst中的位置
imshow("Output",result);
imwrite("cloned.png", result);
}
else if(num == 2)
{
string folder = "cloning/Mixed_Cloning/";
string original_path1 = samples::findFile(folder + "source1.png");
string original_path2 = samples::findFile(folder + "destination1.png");
string original_path3 = samples::findFile(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 = destination.size().width/2;//中心
p.y = destination.size().height/2;
seamlessClone(source, destination, mask, p, result, 2);//混合克隆
imshow("Output",result);
imwrite("cloned.png", result);
}
else if(num == 3)
{
string folder = "cloning/Monochrome_Transfer/";
string original_path1 = samples::findFile(folder + "source1.png");
string original_path2 = samples::findFile(folder + "destination1.png");
string original_path3 = samples::findFile(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 = destination.size().width/2;
p.y = destination.size().height/2;
seamlessClone(source, destination, mask, p, result, 3);//单色转移
imshow("Output",result);
imwrite("cloned.png", result);
}
else if(num == 4)
{
string folder = "cloning/color_change/";
string original_path1 = samples::findFile(folder + "source1.png");
string original_path2 = samples::findFile(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;
////mask定位source中的roi区域,调整该区域颜色r,g,b
colorChange(source, mask, result, 1.5, .5, .5);//局部颜色变化
imshow("Output",result);
imwrite("cloned.png", result);
}
else if(num == 5)
{
string folder = "cloning/Illumination_Change/";
string original_path1 = samples::findFile(folder + "source1.png");
string original_path2 = samples::findFile(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;
//lpha,beta两个参数共同决定消除高光后图像的模糊程度(范围0~2,0比较清晰,2比较模糊)
illuminationChange(source, mask, result, 0.2f, 0.4f);////消除source中mask锁定的高亮区域,后两个参数0-2调整
imshow("Output",result);
imwrite("cloned.png", result);
}
else if(num == 6)
{
string folder = "cloning/Texture_Flattening/";
string original_path1 = samples::findFile(folder + "source1.png");
string original_path2 = samples::findFile(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);//对mask锁定的source中的区域进行纹理扁平化,低阈值,高阈值,核尺寸
imshow("Output",result);
imwrite("cloned.png", result);
}
else
{
cerr << "Invalid selection: " << num << endl;
exit(1);
}
waitKey(0);
}
参考:
#include <opencv2/photo.hpp>
//一、 OpenCV图像无缝融合-seamlessClone介绍与使用(Python/C++源码)
https://cloud.tencent.com/developer/article/1799058
https://docs.opencv.org/4.x/df/da0/group__photo__clone.html
使用seamlessClone实现图像无缝融合效果。
seamlessClone是OpenCV3后添加的函数,使用此函数可以轻松将一幅图像中的指定目标复制后粘贴到另一幅图像中,并自然的融合。函数说明:
seamlessClone()
void cv::seamlessClone ( InputArray src,
InputArray dst,
InputArray mask,
Point p,
OutputArray blend,
int flags
)
Python:
cv.seamlessClone( src, dst, mask, p, flags[, blend] ) -> blend
Parameters
src Input 8-bit 3-channel image.
dst Input 8-bit 3-channel image.
mask Input 8-bit 1 or 3-channel image.
p Point in dst image where object is placed.
blend Output image with the same size and type as dst.
flags Cloning method that could be cv::NORMAL_CLONE, cv::MIXED_CLONE or cv::MONOCHROME_TRANSFER
Examples:
samples/cpp/tutorial_code/photo/seamless_cloning/cloning_demo.cpp.
src
输入8位3通道图像(截取目标的大图)
dst
输入8位3通道图像(待粘贴融合的目标背景图标)
mask
输入8位1或3通道图像(目标掩码区域图像)
p
对象被放置在目标图像dst中的位置
blend
输出图像,与dst具有相同大小和类型。
flags
克隆方法可以是cv :: NORMAL_CLONE,cv :: MIXED_CLONE或cv :: MONOCHROME_TRANSFER
-NORMAL_CLONE: 不保留dst 图像的texture细节。目标区域的梯度只由源图像决定。
-MIXED_CLONE: 保留dest图像的texture 细节。目标区域的梯度是由原图像和目的图像的组合计算出来(计算dominat gradient)。
-MONOCHROME_TRANSFER: 不保留src图像的颜色细节,只有src图像的质地,颜色和目标图像一样,可以用来进行皮肤质地填充。
图像编辑任务涉及全局变化(颜色/强度校正、过滤器、变形)或与选择有关的局部变化。 在这里,我们有兴趣以无缝且轻松的方式实现局部更改,这些更改仅限于手动选择的区域 (ROI)。 变化的程度从轻微的扭曲到完全被新颖的内容替代[195]。
//二、 colorChange
colorChange()
void cv::colorChange ( InputArray src,
InputArray mask,
OutputArray dst,
float red_mul = 1.0f,
float green_mul = 1.0f,
float blue_mul = 1.0f
)
Python:
cv.colorChange( src, mask[, dst[, red_mul[, green_mul[, blue_mul]]]] ) -> dst
给定一个原始彩色图像,可以无缝混合该图像的两个不同颜色版本。
Parameters
src Input 8-bit 3-channel image.
mask Input 8-bit 1 or 3-channel image.
dst Output image with the same size and type as src .
red_mul R-channel multiply factor.
green_mul G-channel multiply factor.
blue_mul B-channel multiply factor.
无缝融合seamlessClone(),调试颜色colorChange(),消除高亮illuminationChange(),纹理扁平化textureFlattening()(OpenCV案例源码cloning_demo.cpp解读)
https://www.cnblogs.com/xixixing/p/12335317.html
//三、 OpenCV无缝融合应用(三)--局部区域亮度调整(附C++源码)
https://cloud.tencent.com/developer/article/1806438
◆ illuminationChange()
void cv::illuminationChange ( InputArray src,
InputArray mask,
OutputArray dst,
float alpha = 0.2f,
float beta = 0.4f
)
Python:
cv.illuminationChange( src, mask[, dst[, alpha[, beta]]] ) -> dst
#include <opencv2/photo.hpp>
https://docs.opencv.org/4.5.5/df/da0/group__photo__clone.html#gac5025767cf2febd8029d474278e886c7
对选区内部的梯度场应用适当的非线性变换,然后用泊松求解器积分,局部修改图像的表观照明。
参数:
src
输入8位3通道图像(截取目标的大图)
mask
输入8位1或3通道图像(待改变颜色目标掩码区域图像)
dst
输出结果图(要求和src相同的大小和类型)
alpha
算法中的α值(建议取值范围0.0~2.0)
beta
算法中的β值(建议取值范围0.0~2.0)
效果展示
分别实现指定区域亮度变暗(减少橙子表面因镜面反射造成的曝光过度)和变亮(突出曝光不足的前景对象-人):
实现步骤与源码
程序实现步骤:
(1) 使用selectROI函数框选指定目标;
(2) 使用;两个滑动条动态改变alpha和beta参数的值;
(3) 滑动条回调函数中使用illuminationChange函数完成亮度改变。
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
Mat src;
int alpha = 20, beta = 40;
Rect rect;
void illuminationChange_Callback(int, void*)
{
Mat dst;
Mat mask = Mat::zeros(src.size(), src.type());
rectangle(mask, rect, Scalar::all(255), -1);
illuminationChange(src, mask, dst, alpha / 100.0, beta / 100.0);
imshow("illuminationChange", dst);
}
int main()
{
src = imread("1.png");
rect = selectROI(src, true, false);
namedWindow("illuminationChange", WINDOW_NORMAL);
createTrackbar("alpha", "illuminationChange", &alpha, 300, illuminationChange_Callback);
createTrackbar("beta", "illuminationChange", &beta, 300, illuminationChange_Callback);
illuminationChange_Callback(0, 0);
waitKey();
return 0;
}
//四、 OpenCV无缝融合应用(四)--纹理平滑(附C++源码)
https://cloud.tencent.com/developer/article/1806440
实现指定区域纹理平滑(全局人像处理有点类似油画效果):
https://docs.opencv.org/4.5.5/df/da0/group__photo__clone.html#gad55df6aa53797365fa7cc23959a54004
textureFlattening()
void cv::textureFlattening ( InputArray src,
InputArray mask,
OutputArray dst,
float low_threshold = 30,
float high_threshold = 45,
int kernel_size = 3
)
Python:
cv.textureFlattening( src, mask[, dst[, low_threshold[, high_threshold[, kernel_size]]]] ) -> dst
通过仅保留边缘位置的梯度,在与泊松求解器集成之前,可以洗掉所选区域的纹理,使其内容具有平坦的外观。 这里使用 Canny 边缘检测器。
Parameters
src Input 8-bit 3-channel image.
mask Input 8-bit 1 or 3-channel image.
dst Output image with the same size and type as src.
low_threshold Range from 0 to 100.
high_threshold Value > 100.
kernel_size The size of the Sobel kernel to be used.
参数
src 输入 8 位 3 通道图像。
mask 输入 8 位 1 或 3 通道图像。
dst 输出与 src 大小和类型相同的图像。
low_threshold 范围从 0 到 100。
high_threshold 值 > 100。
kernel_size 要使用的 Sobel 内核的大小。
该算法假设源图像的颜色接近目标图像的颜色。 这个假设意味着当颜色不匹配时,源图像颜色会向目标图像的颜色着色。
实现步骤与源码
程序实现步骤:
(1) 使用鼠标滑动绘制指定目标并生成mask;
(2) 使用;两个滑动条动态改变low_threshold和high_threshold参数的值;
(3) 滑动条回调函数中使用textureFlattening函数完成纹理平滑。
// SeamlessCloning.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int low_thres = 30, high_thres = 45;
Mat src, temp, mask, result;
Rect rect;
Point pt;
bool flag = false;
void OnChange(int, void*)
{
//Mat mask = Mat::zeros(src.size(), src.type());
//rectangle(mask, rect, Scalar::all(255), -1);
//illuminationChange(src, mask, result, alpha / 100.0, beta / 100.0);
textureFlattening(src, mask, result, low_thres, high_thres, 3);
imshow("textureFlattening", result);
}
///鼠标消息回调函数
void OnMouse(int event, int x, int y, int flag, void* param)
{
switch (event)
{
case EVENT_LBUTTONDOWN: //鼠标左键按下
//cout<<"left button down"<<endl;
flag = true;
pt.x = x;
pt.y = y;
break;
case EVENT_MOUSEMOVE: //鼠标移动
//cout<<"mouse move"<<endl;
if (flag)
{
circle(temp, Point(x, y), 5, Scalar(0, 0, 255), -1, 8);
circle(mask, Point(x, y), 5, Scalar(255, 255, 255), -1, 8);
}
break;
case EVENT_LBUTTONUP: //鼠标左键弹起
//cout<<"left button up"<<endl;
flag = false;
break;
default:
break;
}
}
int main()
{
src = imread("B.jpg");
temp = src.clone();
mask = Mat::zeros(src.size(), CV_8UC1);
namedWindow("src", WINDOW_NORMAL);
imshow("src", src);
namedWindow("Mouse", WINDOW_NORMAL);
setMouseCallback("Mouse", OnMouse, 0); //设置鼠标回调函数
while (1)
{
imshow("Mouse", temp);
if (27 == waitKey(10)) //Esc跳出循环
break;
}
vector<vector<Point>> contours;
vector<Vec4i> hierarcy;
findContours(mask, contours, hierarcy, RETR_EXTERNAL, CHAIN_APPROX_NONE);
drawContours(mask, contours, -1, Scalar::all(255), -1, 8);
namedWindow("textureFlattening", WINDOW_NORMAL);
createTrackbar("low_thres", "textureFlattening", &low_thres, 500, OnChange);
createTrackbar("high_thres", "textureFlattening", &high_thres, 500, OnChange);
OnChange(0, 0);
waitKey();
return 0;
}