纹理合成 Texture Synthesis 算法的C++实现

  • 理论
    • 生成初始块
    • 计算重叠块相似度
    • 调整边缘
  • 实现
    • 生成初始块
    • 生成重叠块
    • 调整边缘
    • 实现效果图

最近做毕设,需要用到纹理合成Texture Synthesis 的知识,在看了一些论文后,开始使用MATLAB进行实现,但由于本人对MATLAB掌握一般,实现过程出现了各种各样的问题,于是改用C++重新编写,效果较好,现总结如下。

理论

纹理合成的只是主要来源于一篇经典论文Image Quilting for Texture Synthesis and Transfer,这篇论文讲述了纹理合成和传递过程,在此仅讲述纹理合成部分。部分配图来源博客园,如有侵权删除。

生成初始块

在这篇文章中,需要使用的一些变量:块大小Tilesize,重叠大小Overlapsize,合成纹理大小Height,Width。首先我们需要选择一块作为初始块放置在合成纹理的左上角。如下图所示:
纹理合成 Texture Synthesis 算法的C++实现_第1张图片
为了增强随机性,可以使用一个随机函数选择原始图中的区块。

计算重叠块相似度

接下来我们从初始块出发对合成纹理进行扩展。提取出重叠块,即红色框中没有斜线的部分。在原始纹理中遍历,得到距离最近的块。然后将这一块覆盖到这个位置。这时候有三种情况可以作为重叠块:横向,纵向,两者均有。
纹理合成 Texture Synthesis 算法的C++实现_第2张图片

调整边缘

如果直接将选中的块添加到对应位置中,就会出现边缘不平滑的问题。因此我们需要选择一个合适的边缘插入,这样的话看起来总体和谐一些。这时候用到的是贪心算法,以横纵皆有的举例:首先在第一行找到两幅图最接近的像素(1,pos),然后在下一行,只需要寻找(2,pos-1~pos+1)三个像素的差异即可。因此形成两条边缘,合并边缘,以此作为替换的边界。
纹理合成 Texture Synthesis 算法的C++实现_第3张图片

实现

采用的是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);
}

生成重叠块

  1. 先生成第一行的【仅横向重叠】
  2. 然后生成第一列的【仅纵向重叠】
  3. 最后开始生成【均重叠】

相似度的计算直接计算的是像素之间的绝对值差,在这里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:
纹理合成 Texture Synthesis 算法的C++实现_第4张图片
效果2:
纹理合成 Texture Synthesis 算法的C++实现_第5张图片

完整的代码见github地址

如有错误,欢迎指正~

你可能感兴趣的:(日常总结,图形学)