先上两张图:
图一
图二
把两张图融合在一起,实现如下图效果:
图三
API解释
#python
output = cv2.seamlessClone(src, dst, mask, center, flags)
#c++
seamlessClone(Mat src, Mat dst, Mat mask, Point center, Mat output, int flags)
src 目标影像,在本次给出的示例中是云彩。
dst 背景图像,在本次示例中是山。
mask 目标影像上的mask,表示目标影像上那些区域是感兴趣区域。如果只对云彩感兴趣,那么mask上就只有云彩所在的区域。
center 目标影像的中心在背景图像上的坐标!注意是目标影像的中心!
flags 选择融合的方式,目前有NORMAL_CLONE、MIXED_CLONE和MONOCHROME_TRANSFER三种方法。
output 输出图像
大体思路是
- 先对图二进行背景分割,分割出感兴趣区域——天空和湖水。
- 对两个区域求出最小外接矩形。
- 用2中得到的两个最小外接矩形对图二进行二值化后的图像截取出掩模图像mask1,mask2。
- 使用seamlessClone进行图像融合,融合前记得resize图一,使图一尺寸与掩模图像大小一致。
在第1步时,图像分割环节费了很大功夫。使用了如下方法KMeans聚类、meanshift均值漂移、灰度值阈值分割、转化到hsv空间,再对h值使用inRange(hsv, Scalar(100, 43, 46), Scalar(124, 255, 255), mask)(注:蓝色对应的h值范围在100~124之间)进行分割。但想把下图A、B区域同时分割出来,均取得不了很好地效果。
图四
最后采用的方法是基于边缘的阈值分割:
- 先使用canny函数寻找图二边界。
- 找到的图二中山的外部边界很零碎,这个时候需要对图像进行膨胀操作,把零碎的边界连接起来。
- 提取膨胀后图像的边缘轮廓。
- 找出外部的大轮廓,进行填充。
- 这个时候山就从背景中被提取出来了,对填充后的图像寻找面积最大的前两个轮廓区域——天空和湖水。
- 对5中的两个轮廓寻找最小外接矩形。
c++代码如下:
#include<iostream>
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;
void findMaxContour(Mat input);
vector<vector<Point>> contours;
Mat EnhanceSaturation(Mat temp);
Mat src;
Mat GuassImage;
Mat edges;
Mat gray;
Mat dst;
Mat enhanced;
Mat mask;
Mat open;
Mat temp;
Mat cloud;
Rect MaxRect;
Rect SecRect;
int SecIndex = 0;
int MaxIndex = 0;
int main()
{
src = imread("H:/cv/src2.jpg");
if (!src.data)
{
printf("couldn't load the picture!");
return -1;
}
imshow("src", src);
cloud = imread("H:/cv/cloud.jpg");
if (!cloud.data)
{
printf("couldn't load the picture!");
return -1;
}
`
//用边缘检测分割图像,先canny,检测边缘,再膨胀,找到大轮廓,填充,再腐蚀回去,可达到分割效果。
GaussianBlur(src, GuassImage,Size(3,3),(0,0));
Canny(GuassImage, edges, 50, 150, 3);
Mat kennel1 = getStructuringElement(MORPH_RECT, Size(10, 10));
dilate(edges, edges, kennel1, Point(-1, -1));
//rectangle(edges, Point(0,0),Point(src.cols-1,src.rows-1), Scalar(255, 0, 0), 1, 8);
imshow("edges", edges);
findMaxContour(edges);
Mat draw(src.size(), CV_8UC1,Scalar(255));
drawContours(draw, contours, MaxIndex, Scalar(0), -1);
drawContours(draw, contours, SecIndex, Scalar(0), -1);
dilate(draw, draw, kennel1, Point(-1, -1));
imshow("draw", draw);
//找到前两个最大轮廓,并求出外接矩形。
findMaxContour(draw);
//截取需要填充的RIO区域
Mat mask1 = draw(MaxRect);
Mat mask2 = draw(SecRect);
temp.create(src.size(), CV_8UC3);
resize(cloud, cloud, mask2.size());
Point center2 = Point((SecRect.x + static_cast<float>(0.5)*SecRect.width), (SecRect.y + SecRect.height / 2));//注(1/2)*MaxRect.width会造成越界,注意强制类型转换
seamlessClone(cloud, src, mask2, center2, temp, NORMAL_CLONE);
resize(cloud, cloud, mask1.size());
//用重映射翻转图像
Mat splitImg, map_x, map_y;
splitImg.create(cloud.size(), cloud.type());
map_x.create(cloud.size(), CV_32FC1);
map_y.create(cloud.size(), CV_32FC1);
for (int i = 0; i < map_x.rows; i++)
{
for (int j = 0; j < map_y.cols; j++)
{
map_x.at<float>(i, j) = j;
map_y.at<float>(i, j) = map_y.rows - i;
}
}
remap(cloud, splitImg, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));
Point center1 = Point((MaxRect.x + MaxRect.width / 2), (MaxRect.y + MaxRect.height / 2));
seamlessClone(splitImg, temp, mask1, center1, temp, NORMAL_CLONE);
imshow("temp", temp);
//美化图像,增强效果。
cvtColor(temp, temp, COLOR_BGR2YCrCb);
vector<Mat> planes;
split(temp, planes);
equalizeHist(planes[0], planes[0]);
merge(planes, src);
cvtColor(src, src, COLOR_YCrCb2BGR);
imshow("src", src);
enhanced = EnhanceSaturation(src);
imshow("enhanced", enhanced);
imwrite("H:/cv/融合后.jpg",src);
imwrite("H:/cv/提升饱和度后.jpg",enhanced);
waitKey(0);
}
void findMaxContour(Mat input)
{
Mat edges;
Mat herachy;
int MaxArea = 0;
int SecArea = 0;
int Area = 0;
//Canny(input, edges, 50, 150, 3);不用canny提取边缘,直接从二值图像寻找白色区域轮廓
//imshow("edges", edges);
findContours(input, contours, herachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
for (int i = 0; i < contours.size(); i++)
{
Area = contourArea(contours[i]);
if (MaxArea < Area)
{
MaxArea = Area;
MaxIndex = i;
}
}
for (int i = 0; i < contours.size(); i++)
{
Area = contourArea(contours[i]);
if (SecArea < Area && Area != MaxArea)
{
SecArea = Area;
SecIndex = i;
}
}
MaxRect = boundingRect(contours[MaxIndex]);
SecRect = boundingRect(contours[SecIndex]);
Mat srcCopy = src.clone();
rectangle(srcCopy, MaxRect, Scalar(255, 0, 0), 3, 8);
rectangle(srcCopy, SecRect, Scalar(0, 255, 0), 3, 8);
imshow("input", srcCopy);
}
Mat EnhanceSaturation(Mat temp)
{
Mat matDst;
Mat Img_out(temp.size(), CV_32FC3);
temp.convertTo(Img_out, CV_32FC3);
Mat Img_in(temp.size(), CV_32FC3);
temp.convertTo(Img_in, CV_32FC3);
// define the iterator of the input image
MatIterator_<Vec3f> inp_begin, inp_end;
inp_begin = Img_in.begin<Vec3f>();
inp_end = Img_in.end<Vec3f>();
// define the iterator of the output image
MatIterator_<Vec3f> out_begin, out_end;
out_begin = Img_out.begin<Vec3f>();
out_end = Img_out.end<Vec3f>();
// increment (-100.0, 100.0)
float Increment = 50.0 / 100.0; //饱和度参数调整
float delta = 0;
float minVal, maxVal;
float t1, t2, t3;
float L, S;
float alpha;
for (; inp_begin != inp_end; inp_begin++, out_begin++)
{
t1 = (*inp_begin)[0];
t2 = (*inp_begin)[1];
t3 = (*inp_begin)[2];
minVal = std::min(std::min(t1, t2), t3);
maxVal = std::max(std::max(t1, t2), t3);
delta = (maxVal - minVal) / 255.0;
L = 0.5*(maxVal + minVal) / 255.0;
S = std::max(0.5*delta / L, 0.5*delta / (1 - L));
if (Increment>0)
{
alpha = max(S, 1 - Increment);
alpha = 1.0 / alpha - 1;
(*out_begin)[0] = (*inp_begin)[0] + ((*inp_begin)[0] - L*255.0)*alpha;
(*out_begin)[1] = (*inp_begin)[1] + ((*inp_begin)[1] - L*255.0)*alpha;
(*out_begin)[2] = (*inp_begin)[2] + ((*inp_begin)[2] - L*255.0)*alpha;
}
else
{
alpha = Increment;
(*out_begin)[0] = L*255.0 + ((*inp_begin)[0] - L*255.0)*(1 + alpha);
(*out_begin)[1] = L*255.0 + ((*inp_begin)[1] - L*255.0)*(1 + alpha);
(*out_begin)[2] = L*255.0 + ((*inp_begin)[2] - L*255.0)*(1 + alpha);
}
}
Img_out /= 255;
Img_out.convertTo(matDst, CV_8UC3, 255);
return matDst;
}
void findThdContour(Mat input)
{
Mat edges;
Mat herachy;
int MaxArea = 0;
int SecArea = 0;
int ThdArea = 0;
int Area = 0;
//Canny(input, edges, 50, 150, 3);不用canny提取边缘,直接从二值图像寻找白色区域轮廓
//imshow("edges", edges);
findContours(input, contours, herachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
for (int i = 0; i < contours.size(); i++)
{
Area = contourArea(contours[i]);
if (MaxArea < Area)
{
MaxArea = Area;
MaxIndex = i;
}
}
for (int i = 0; i < contours.size(); i++)
{
Area = contourArea(contours[i]);
if (SecArea < Area && Area != MaxArea)
{
SecArea = Area;
SecIndex = i;
}
}
}
最后得到提升饱和度的图像: