图像融合之Poisson融合及其改进

一、背景 近期需要做两幅图像的融合,网上查了很多资料,讲述最多效果最好的是Poisson融合,网上资料很多,这里就不细讲了。但是自己在做的时候发现目标的颜色几乎都会被改变,因此想就这个问题进行一下改进,目前还在尝试中。
poisson融合(Poisson Image Editing)的原文我们可以查看这里:
[1]https://www.cs.jhu.edu/~misha/Fall07/Papers/Perez03.pdf
不过后来又从网上找到了一篇:http://www.ipol.im/pub/art/2016/163/article_lr.pdf
先给出几个网上的讲解:
[2]泊松融合(这篇讲了很多实现代码)
[3]图像的泊松(Poisson)编辑、泊松融合(这篇讲了很多公式推导)
[4]泊松图像融合(Seamless cloning)的原理 及 API实现(算法的原理及实现)
更多示例可以看这里:
[5]http://cs.brown.edu/courses/cs129/results/proj2/taox/
二、poisson融合算法原理 我们先来看看[1]https://www.cs.jhu.edu/~misha/Fall07/Papers/Perez03.pdf 这篇文章的介绍。
introduction部分先介绍了文章的核心,也就是Dirichlet边界约束下的偏微分方程,这是一个两段式(twofold)的算法。第一,由于拉普拉斯算子抑制的区域梯度较小,可以将其不用处理的直接叠加在图像上;第二,约束域的标量函数由其边界值和拉普拉斯算子唯一确定,因此对应的poisson方程有着唯一解。
图像融合之Poisson融合及其改进
文章图片

我们假定S表示图像的定义域,它是二维空间下的子集;?为S的子集,它的边界为??;设f*是已知的标量函数,它是由图像S减去子集?得到的;设f是未知的标量的函数,它由?的内部所定义;最后v是矢量场,由?获得。

三、实验问题描述 以下的所有代码网上都有现成的,简单实现的代码可以参考下面这篇:
[6]OCR -- seamlessClone泊松融合
opencv有相关的函数可以直接调用,参考:https://docs.opencv.org/master/df/da0/group__photo__clone.html
前面的博客里看了很多融合效果非常好的例子,下面来说一下这个算法的问题。
现在自己有一张图,图上有一只羊,比如:
图像融合之Poisson融合及其改进
文章图片

然后我可以通过目标检测的方式检测出这只羊,得到一个mask:
图像融合之Poisson融合及其改进
文章图片

最后我想把这只羊放到另一群羊当中,背景图像为:
图像融合之Poisson融合及其改进
文章图片

这里用了Poisson融合,不过就出现了新的问题,先来看一下融合效果:
图像融合之Poisson融合及其改进
文章图片

好了,现在的问题出现了,因为周围都是绿色的草地,所以这只羊变成了绿色的,现在在思考有没有什么办法能够较好的融合边缘并且能够保留原有的主要色调。
这里简单给出代码:

def method1(): im = cv2.imread("background.jpg")# background image obj = cv2.imread("goat.jpg")# object image mask = cv2.imread("mask.png")width, height, channels = obj.shape center = (height // 2, width // 2)img = cv2.resize(im, (height, width), interpolation=cv2.INTER_CUBIC)# Seamlessly clone src into dst and put the results in output normal_clone = cv2.seamlessClone(obj, img, mask, center, cv2.NORMAL_CLONE) cv2.imwrite("result.jpg", normal_clone)

第一次更新方法:
其实这次采用了最简单的方法,如果mask是一个0-1二值图的话,那我们需要的图就应该可以表示为:
target_img = mask * object_img + (1-mask) * background_img
这样做好之后的误差很大,很多地方并没有计算正确:
图像融合之Poisson融合及其改进
文章图片

颜色虽然正常了很多,不过效果并不好。
这里简单给出代码:
def method2(): im = cv2.imread("background.jpg")# background image obj = cv2.imread("goat.jpg")# object imagemask = cv2.imread("mask.png") mask[mask > 254] = 1 mask[mask != 1] = 0width, height, channels = obj.shape img = cv2.resize(im, (height, width), interpolation=cv2.INTER_CUBIC)result = cv2.add(mask * obj, (1-mask) * img) cv2.imshow("result", result) cv2.waitKey(0)

第二次更新方法:
这次采用了PIL里面的一个方法,目前来看效果能稍微好点:
图像融合之Poisson融合及其改进
文章图片

颜色也稍微正常了一些,不过现在的问题就是明显这只羊的色彩过渡不自然。
先暂时给出代码:
def method3(): from PIL import Image img = Image.open("goat.jpg") width, height = img.size bg = Image.open("background.jpg") bg = bg.resize((width, height)) mask = Image.open("mask.png")img = img.convert('RGBA') bg = bg.convert('RGBA')img = Image.composite(img, bg, mask) img.show() img.save("blend.png")

第三次更新方法:
这次采用了image matting的思路,拿到原图后,我先做一个trimap,然后再用matting 的方法获得一个alpha图(就是透明度图),最后再根据alpha图叠加两张图即可。
思路比之前的稍微复杂了一点,先来看下trimap图:
图像融合之Poisson融合及其改进
文章图片

然后我用的是beyasian matting进行抠图,获得alpha图:
图像融合之Poisson融合及其改进
文章图片

最后再叠加两张图可以得到:
图像融合之Poisson融合及其改进
文章图片

这种方法保留了一点点边缘,效果算是比之前的方法要好一些,后面有空我会详细讲讲这个方法。
下面给出部分关键代码:
def method4(): import matting alpha = io.imread('test_output/alpha.png') alpha[np.where(alpha < 128)] = 0 alpha[np.where(alpha >= 128)] = 255 trimap_temp = alpha trimap = trimap_make(trimap_temp, alpha) img = io.imread(args.rgb)result = matting.bayesian_matte(img*255, trimap) background_img = io.imread(args.background_image) back_img = transform.resize(background_img, [img.shape[0], img.shape[1]])# 下来需要做的是叠加两张图 final_result = np.zeros(img.shape) for i in range(3): final_result[:, :, i] = img[:, :, i] * result / 255 + back_img[:, :, i] * (1-result) io.imsave("final_result.jpg", final_result)

【图像融合之Poisson融合及其改进】

    推荐阅读