首页 > 其他分享 >【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。

时间:2024-01-22 12:33:05浏览次数:51  
标签:泊松 Width 拼贴 Height Src2 Stride Src1 单幅 Scan0

泊松融合还可以创建一些很有意思的图片,比如一张图片任意规格平铺,使用泊松融合后,平铺的边界处过渡的很自然,另外,对于两张图片,由于局部亮度等等的影响,导致拼接在一起时不自然,也可以使用泊松融合予以解决。

  在【快速阅读二】从OpenCv的代码中扣取泊松融合算子(Poisson Image Editing)并稍作优化 一文的最后,我曾经提到有个使用泊松融合来来实现Seamless Tiling的效果,我自己尝试去实现,暂时没有获取正确的结果,论文里给出的效果如下: 

   

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_Image

                              

  一开始我没怎么看这个tinling的意思,总是以为算法的目的是左图通过泊松融合的处理,能够处理成右图的效果,所以怎么测试也打不到真确的结果。

  后面又看了几篇文章,原来他并不是这个意思,注意到上面左图里上下共有2*3个相同的块,如下图所示:  

                                 

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_ide_02

  他的意思是通过修改某种边界条件对这个图使用泊松融合,得到后的结果图,再进行拼接就可以得到那种无缝的效果了,即先泊松,再拼接,而不是先拼接,后泊松。我们去看看Tiling这个单词的意思也明白这个道理了。 

we have chosen fnorth = fsouth = 0:5(gnorth + gsouth), and similarly for the eastand west borders.就可以了。

  后面我在泊松图像编辑(Possion Image Edit)原理、实现与应用 这位仁兄的博客的尾部也看到了关于这个过程的一个较为详细的注释,直接复制他博客里的图吧(谢谢)。

 

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_Mask_03

  即用原图的梯度场,用修改后边界后的图作为融合的前景图进行融合,这样得到的融合的结果图,一个简单的代码如下所示:

void IM_SeamlessTiling(unsigned char* Src, unsigned char* Dest, int Width, int Height, int Stride)
{
    int Channel = Stride / Width;
    unsigned char* Fore = (unsigned char*)malloc(Height * Stride * sizeof(unsigned char));
    unsigned char* Mask = (unsigned char*)calloc(Height * Width, sizeof(unsigned char));
    memcpy(Fore, Src, Height * Stride);
    //    上下边界和左右边界设为原图像的边界和的一半。
    for (int X = 0; X < Width * Channel; X++)
    {
        Fore[X] = (Src[X] + Src[(Height - 1) * Stride + X]) / 2;
        Fore[(Height - 1) * Stride + X] = Fore[X];
    }
    for (int Y = 1; Y < Height - 1; Y++)
    {
        for (int C = 0; C < Channel; C++)
        {
            Fore[Y * Stride + C] = (Src[Y * Stride + C] + Src[Y * Stride + (Width - 1) * Channel + C]) / 2;
            Fore[Y * Stride + (Width - 1) * Channel + C] = Fore[Y * Stride + C];
        }
    }
    memset(Mask , 255, Width * Height);
    //    然后引导向量场仍然使用原图像的梯度场。这样得到的结果就可以用来无缝拼接
    //    最后一个参数为0,倒数第二个参数也为,Mask设置为全255,则就是直接使用Src的梯度场了
    IM_NormalClone(Fore, Src, Mask, Dest, Width, Height, Stride, 0, 0);
    free(Fore);
    free(Mask);
}

  在原始的opencv代码里,对于蒙版在内部有个如下的操作:

Mat Kernel(Size(3, 3), CV_8UC1);
    Kernel.setTo(Scalar(1));
    erode(binaryMask, binaryMask, Kernel, Point(-1,-1), 3);

  我感觉我在上一篇文章里说到他的作用是错误的,按理说不应该需要这个的,但是实际测试就是用了这个对于非规则的图还是有一定好处的,他融合的更为自然,但是在这里的Tiling的应用中,就不应该需要了。

  这个简单的操作的效果确实还是蛮好玩的,除去论文里那个图,我们有弄了几副测试图,效果如下:

   

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_Mask_04

  

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_Mask_05

                

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_Mask_06

                              

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_Image_07

 

                                                 

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_Image_08

                                                                                                                  

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_ide_09

   

                  直接拼贴的效果                                           泊松融合后拼接的效果

  以下是拼接过程中使用到的小图。

         

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_ide_10

             

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_ide_11

                 

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_Mask_12

     可见进行泊松融合后的图,弥合的非常自然。

  还是在同一篇博客里,作者还对这个想法进行了扩展,因为前面的融合是对单幅图四个边进行融合,另外一个更为常见的情形是对两个不同的图片在某一条边进行融合,这个情况再图像拼接, 多福图像合成等等方面也有着较为常见的应用。

  比如两幅图在水平方向进行拼接,这个时候就可以用如下的思路来解决问题(也是直接拷贝的作者的原图,谢谢):

       

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_Image_13

   这个图的思路也是很简单的,左右两幅图拼接,则在左侧图的最右侧那一列以及右侧图最左侧那一列,都用两幅图的平均值代替,然后分别用两幅图各自的梯度场进行泊松融合,得到的两幅图再进行拼接,就显得自然了。

   一个简单的代码如下所示:

void IM_InnerSeamlessStitching(HBitmap Src1, HBitmap Src2, HBitmap Dest1, HBitmap Dest2, int Direction)
{
    int Channel = Src1.Stride / Src1.Width;
    if (Src1.PixelFormat != Src1.PixelFormat)                return;
    if ((Direction == 0) && (Src1.Height != Src2.Height))    return;
    if ((Direction == 1) && (Src1.Width != Src2.Width))        return;

    unsigned char* Fore1 = (unsigned char*)malloc(Src1.Height * Src1.Stride * sizeof(unsigned char));
    unsigned char* Fore2 = (unsigned char*)malloc(Src2.Height * Src2.Stride * sizeof(unsigned char));
    unsigned char* Mask1 = (unsigned char*)calloc(Src1.Height * Src1.Width, sizeof(unsigned char));
    unsigned char* Mask2 = (unsigned char*)calloc(Src2.Height * Src2.Width, sizeof(unsigned char));

    memcpy(Fore1, Src1.Scan0, Src1.Height * Src1.Stride);
    memcpy(Fore2, Src2.Scan0, Src2.Height * Src2.Stride);

    //    水平方向融合
    if (Direction == 0)
    {
        for (int Y = 0; Y < Src1.Height; Y++)
        {
            if (Channel == 1)
            {
                int Avg = (Src1.Scan0[Y * Src1.Stride + Src1.Width - 1] + Src2.Scan0[Y * Src2.Stride]) / 2;
                Fore1[Y * Src1.Stride + Src1.Width - 1] = Avg;
                Fore2[Y * Src2.Stride] = Avg;
            }
            else
            {
                int AvgB = (Src1.Scan0[Y * Src1.Stride + (Src1.Width - 1) * 3 + 0] + Src2.Scan0[Y * Src2.Stride + 0]) / 2;
                int AvgG = (Src1.Scan0[Y * Src1.Stride + (Src1.Width - 1) * 3 + 1] + Src2.Scan0[Y * Src2.Stride + 1]) / 2;
                int AvgR = (Src1.Scan0[Y * Src1.Stride + (Src1.Width - 1) * 3 + 2] + Src2.Scan0[Y * Src2.Stride + 2]) / 2;
                Fore1[Y * Src1.Stride + (Src1.Width - 1) * 3 + 0] = AvgB;
                Fore1[Y * Src1.Stride + (Src1.Width - 1) * 3 + 1] = AvgG;
                Fore1[Y * Src1.Stride + (Src1.Width - 1) * 3 + 2] = AvgR;
                Fore2[Y * Src2.Stride + 0] = AvgB;
                Fore2[Y * Src2.Stride + 1] = AvgG;
                Fore2[Y * Src2.Stride + 2] = AvgR;
            }
        }
    }
    else if (Direction == 1)
    {
        for (int X = 0; X < Src1.Width * Channel; X++)
        {
            //    上下两个可以采用不一样的值,但是做点融合也是不错的,实测还是有点小问题
            //    Fore1[(Src1.Height - 1) * Src1.Stride + X] = (Src1.Scan0[(Src1.Height - 1) * Src1.Stride + X] * 77 + Src2.Scan0[X] * 51) / 128;
            //    Fore2[X] = (Src1.Scan0[(Src1.Height - 1) * Src1.Stride + X] * 51 + Src2.Scan0[X] * 77) / 128;*/
            
            int Avg = (Src1.Scan0[(Src1.Height - 1) * Src1.Stride + X] + Src2.Scan0[X]) / 2;
            Fore1[(Src1.Height - 1) * Src1.Stride + X] = Avg;
            Fore2[X] = Avg;
        }
    }
    memset(Mask1, 255, Src1.Width * Src1.Height);
    memset(Mask2, 255, Src2.Width * Src2.Height);
    //    然后引导向量场仍然使用原图像的梯度场。这样得到的结果就可以用来无缝拼接
    //    最后一个参数为0,倒数第二个参数也为,Mask设置为全255,则就是直接使用Src的梯度场了
    IM_NormalClone(Fore1, Src1.Scan0, Mask1, Dest1.Scan0, Src1.Width, Src1.Height, Src1.Stride, 0, 0);
    IM_NormalClone(Fore2, Src2.Scan0, Mask2, Dest2.Scan0, Src2.Width, Src2.Height, Src2.Stride, 0, 0);

    free(Fore1);
    free(Fore2);
    free(Mask1);
    free(Mask2);
}

   我手上没有合适的图片,就自己随意处理了结果图,感觉还有那么一点用处:

   

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_ide_14

  

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_ide_15

   

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_ide_16

  

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_Mask_17

  可以看到,使用泊松融合后,已经没有明显的分解线了。

  这个算法有一个很好的用法就是,对于某些大图像,需要分块进行处理,但是分块后必然会出现块于块之间出现过渡不自然的情况,直接拼接在一起有明显的痕迹,用上面的泊松融合则可以解决这个问题。

  另外, 实际中可能还会遇到分界线不是垂直或者水平的现象,这种,个人感觉也是可以通过泊松融合来解决,只要能确定相应的分解mask,在分解线处求平均值,就一样能把边缘值的影响平均到图像内部,这个暂时还没有去研究。可以留待后续继续处理。

  另外,提一个算法优化方面的问题,如果图像比较大,直接处理会打来很大的算法效率瓶颈,那么在这里其实是没有必要全图处理的,可以在相邻的区域取一定范围的像素进行融合就可以了,而且这个一定范围也可以做些固化,比如我们知道在OpenCV里不同长度的FFT其运算效率是不同的,对于有些数据,效率很高,因此,我们可以取这些值来做运算。、


       如果想时刻关注本人的最新文章,也可关注公众号

                             

【快速阅读三】使用泊松融合实现单幅图的无缝拼贴及消除两幅图片直接的拼接缝隙。_Image_18


标签:泊松,Width,拼贴,Height,Src2,Stride,Src1,单幅,Scan0
From: https://blog.51cto.com/u_15341345/9364427

相关文章

  • 【快速阅读二】从OpenCv的代码中扣取泊松融合算子(Poisson Image Editing)并稍作优化
    泊松融合是一种非常不错多图融合算法,在OpenCv的相关版本中也包含了该算子模块,作者尝试着从OpenCv的大仓库中扣取出该算子的全部代码,并分享了一些在扣取代码中的心得和收获。泊松融合我自己写的第一版程序大概是2016年在某个小房间里折腾出来的,当时是用......
  • 【快速阅读二】从OpenCv的代码中扣取泊松融合算子(Poisson Image Editing)并稍作优化
    泊松融合我自己写的第一版程序大概是2016年在某个小房间里折腾出来的,当时是用的迭代的方式,记得似乎效果不怎么样,没有达到论文的效果。前段时间又有网友问我有没有这方面的程序,我说Opencv已经有了,可以直接使用,他说opencv的框架太大,不想为了一个功能的需求而背上这么一座大山,看......
  • 泊松分布与计算生物学的关系
    1.背景介绍泊松分布是一种概率分布,用于描述一段时间或空间中事件发生的频率。在计算生物学中,泊松分布被广泛应用于分析基因表达量、DNA序列中的单核苷酸变异以及蛋白质结构中的互补基因组。本文将详细介绍泊松分布的核心概念、算法原理、应用实例以及未来发展趋势。1.1泊松分布的......
  • 泊松融合-无缝融合
    最近,在构思一篇文章,要用fakeimage的方法,突然想到了以前学到的泊松融合方法。参考文献:https://zhuanlan.zhihu.com/p/96777721?utm_id=0https://blog.csdn.net/TracelessLe/article/details/113270697该方法就是在泊松边界(目标图像f与合成图像f*的边界像素值相等)条件下,保持原图......
  • R语言贝叶斯Metropolis-Hastings Gibbs 吉布斯采样器估计变点指数分布分析泊松过程车
    原文链接:http://tecdat.cn/?p=26578 原文出处:拓端数据部落公众号最近我们被客户要求撰写关于吉布斯采样器的研究报告,包括一些图形和统计输出。指数分布是泊松过程中事件之间时间的概率分布,因此它用于预测到下一个事件的等待时间,例如,您需要在公共汽车站等待的时间,直到下一班车......
  • 无涯教程-Python - 泊松分布
    泊松分布是显示事件在预定时间段内可能发生的次数的分布,它用于独立事件,这些事件在给定的时间间隔内以恒定的速率发生,泊松分布是一个离散函数,意味着该事件只能按发生或不发生的方式进行度量,这意味着该变量只能按整数进行度量。无涯教程使用具有内置功能的seabornpython库来创建......
  • R语言贝叶斯Metropolis-Hastings Gibbs 吉布斯采样器估计变点指数分布分析泊松过程车
    最近我们被客户要求撰写关于吉布斯采样器的研究报告,包括一些图形和统计输出。指数分布是泊松过程中事件之间时间的概率分布,因此它用于预测到下一个事件的等待时间,例如,您需要在公共汽车站等待的时间,直到下一班车到了。在本文中,我们将使用指数分布,假设它的参数λ,即事件之间的平均......
  • R语言贝叶斯Metropolis-Hastings Gibbs 吉布斯采样器估计变点指数分布分析泊松过程车
    原文链接:http://tecdat.cn/?p=26578 原文出处:拓端数据部落公众号最近我们被客户要求撰写关于吉布斯采样器的研究报告,包括一些图形和统计输出。指数分布是泊松过程中事件之间时间的概率分布,因此它用于预测到下一个事件的等待时间,例如,您需要在公共汽车站等待的时间,直到下一班车......
  • 数据统计分析 — 泊松分布
    在一指定时间范围内或在指定的面积或体积内某一事件出现的次数的分布,他们对应的随机变量的概率服从的分布叫做泊松分布,泊松分布是二项分布的极限例如:某企业中每月某设备出现故障的次数单位时间内到达某一服务台需要服务的顾客人数举个例子小王的婶婶新开了一个包子铺,生意......
  • R语言和Python用泊松过程扩展:霍克斯过程Hawkes Processes分析比特币交易数据订单到达
    全文下载链接:http://tecdat.cn/?p=25880 最近我们被客户要求撰写关于泊松过程的研究报告,包括一些图形和统计输出。本文描述了一个模型,该模型解释了交易的聚集到达,并展示了如何将其应用于比特币交易数据。这是很有趣的,原因很多。例如,对于交易来说,能够预测在短期内是否有更多的买......