近期需要做两幅图像的融合,网上查了很多资料,讲述最多效果最好的是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/
我们先来看看[1]https://www.cs.jhu.edu/~misha/Fall07/Papers/Perez03.pdf 这篇文章的介绍。
introduction部分先介绍了文章的核心,也就是Dirichlet边界约束下的偏微分方程,这是一个两段式(twofold)的算法。第一,由于拉普拉斯算子抑制的区域梯度较小,可以将其不用处理的直接叠加在图像上;第二,约束域的标量函数由其边界值和拉普拉斯算子唯一确定,因此对应的poisson方程有着唯一解。
我们假定S表示图像的定义域,它是二维空间下的子集;Ω为S的子集,它的边界为∂Ω;设f*是已知的标量函数,它是由图像S减去子集Ω得到的;设f是未知的标量的函数,它由Ω的内部所定义;最后v是矢量场,由Ω获得。
以下的所有代码网上都有现成的,简单实现的代码可以参考下面这篇:
[6]OCR -- seamlessClone泊松融合
opencv有相关的函数可以直接调用,参考:https://docs.opencv.org/master/df/da0/group__photo__clone.html
前面的博客里看了很多融合效果非常好的例子,下面来说一下这个算法的问题。
现在自己有一张图,图上有一只羊,比如:
然后我可以通过目标检测的方式检测出这只羊,得到一个mask:
最后我想把这只羊放到另一群羊当中,背景图像为:
这里用了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
这样做好之后的误差很大,很多地方并没有计算正确:
颜色虽然正常了很多,不过效果并不好。
这里简单给出代码:
def method2():
im = cv2.imread("background.jpg") # background image
obj = cv2.imread("goat.jpg") # object image
mask = cv2.imread("mask.png")
mask[mask > 254] = 1
mask[mask != 1] = 0
width, 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里面的一个方法,目前来看效果能稍微好点:
颜色也稍微正常了一些,不过现在的问题就是明显这只羊的色彩过渡不自然。
先暂时给出代码:
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图:
然后我用的是beyasian matting进行抠图,获得alpha图:
最后再叠加两张图可以得到:
这种方法保留了一点点边缘,效果算是比之前的方法要好一些,后面有空我会详细讲讲这个方法。
下面给出部分关键代码:
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)