最近做毕设,需要用到纹理合成Texture Synthesis 的知识,在看了一些论文后,开始使用MATLAB进行实现,但由于本人对MATLAB掌握一般,实现过程出现了各种各样的问题,于是改用C++重新编写,效果较好,现总结如下。
纹理合成的只是主要来源于一篇经典论文Image Quilting for Texture Synthesis and Transfer
,这篇论文讲述了纹理合成和传递过程,在此仅讲述纹理合成部分。部分配图来源博客园,如有侵权删除。
在这篇文章中,需要使用的一些变量:块大小Tilesize
,重叠大小Overlapsize
,合成纹理大小Height,Width
。首先我们需要选择一块作为初始块放置在合成纹理的左上角。如下图所示:
为了增强随机性,可以使用一个随机函数选择原始图中的区块。
接下来我们从初始块出发对合成纹理进行扩展。提取出重叠块,即红色框中没有斜线的部分。在原始纹理中遍历,得到距离最近的块。然后将这一块覆盖到这个位置。这时候有三种情况可以作为重叠块:横向,纵向,两者均有。
如果直接将选中的块添加到对应位置中,就会出现边缘不平滑的问题。因此我们需要选择一个合适的边缘插入,这样的话看起来总体和谐一些。这时候用到的是贪心算法,以横纵皆有的举例:首先在第一行找到两幅图最接近的像素(1,pos)
,然后在下一行,只需要寻找(2,pos-1~pos+1)
三个像素的差异即可。因此形成两条边缘,合并边缘,以此作为替换的边界。
采用的是vs 2015
+OpenCV 3.1
进行实现,图片使用的是单通道的。
生成一个随机的位置,这里的拷贝用的是OpenCV中浅拷贝的特点,对于roi
的操作即对于源图像的操作。
void generateInit(Mat &dest, Mat &source)
{
Mat roi_d(dest, Rect(0, 0, TileSize, TileSize));
Mat roi_s(source, Rect(random(DefaultSize - TileSize), random(DefaultSize - TileSize), TileSize, TileSize));
roi_s.copyTo(roi_d);
}
相似度的计算直接计算的是像素之间的绝对值差,在这里OpenCV有提供向量式的运算,非常方便。
absdiff(area, area_t, res); // 将结果存储在res中
auto tmp_dis = sum(res)[0]; // 求和得到的是一个三维向量,但这里只有一个通道
以横纵都有重叠计算:
1. 计算横纵方向的边缘路径
2. 合并两条路径
3. 深度优先搜索掩码【判断每个像素属于哪一个区域,使用的是dfs
,从右下角出发。不得不说,MATLAB实现递归非常的慢】
// dfs代码,tags标记是否搜索过
void solve(set & points, vector<vector<bool>> &mask, int x, int y, vector<vector<bool>> &tags)
{
if (x < 0 || y < 0)
return;
mask[x][y] = true;
tags[x][y] = true;
int xn[][2] = { -1,0,0,-1 };
for (int i = 0; i < 2; ++i)
{
int tmp_x = x + xn[i][0];
int tmp_y = y + xn[i][1];
if (tmp_x < 0 || tmp_y < 0)
continue;
point p(tmp_x, tmp_y);
if (points.count(p) || tags[tmp_x][tmp_y])
continue;
solve(points, mask, tmp_x, tmp_y, tags);
}
}
这里实现的效果图,没有对第一行和第一列进行平滑插入,因此有些生硬。另外每次都是选择最近的作为候选块,如果增加一些随机性则效果会更加平和。
效果1:
效果2:
完整的代码见github地址
如有错误,欢迎指正~