这效果实在是不知道该怎么描述…
放一张著名的海报(楚门的世界),大概就这意思。
放大看细节:
正好大计课上提到了 opencv,顺便搞一下。
假设,我们已经通过一顿操作获得了相同尺寸的背景图(bg)和前景图(fg),那么一个自然的想法就是把图片加权合并起来。
# 合并图片
cv2.addWeighted(fg, alpha, bg, beta, gamma)
# alpha,beta 分别代表权重,gamma 代表合成图片的偏移量(也就是明暗)
但是经过实操,这样做效果并不好。画面的协调性很差,背景和前景对比度都很低,看不清楚。
第二个想法就是用小照片拼前景的时候,根据背景的色调把它放在相应的位置上。那么现在我们需要计算两张子图的“色调差”一类的东西,在 opencv 里可以通过计算和比较直方图(histogram)来完成。灰度图的直方图就是计算在每个灰度区间里的像素个数,RGB 的直方图可以任意选定某一通道。
# 计算直方图
hist = cv2.calcHist([img], [0], None, [64], [0, 256])
# 参数顺次为:图片,通道(灰度即为[0],BGR为[0/1/2]),掩模图像,分组数目,像素值范围
# 注意用中括号括起来
而对于直方图的比较,opencv 提供了三种比较方式:
相关性比较 ,
method = cv.HISTCMP_CORREL
, 值越大,相关度越高,最大值为 1,最小值为 0
卡方比较,method = cv.HISTCMP_CHISQR
,值越小,相关度越高,最大值无上界,最小值 0
巴氏距离比较,method = cv.HISTCMP_BHATTACHARYYA
,值越小,相关度越高,最大值为 1,最小值为 0
但这个相关性比较范围应该是有问题,我跑出来的是 [-1, 1]。至于这几种比较在操作中实际有什么区别我并不清楚,反着我采用了巴氏距离。
# 比较直方图
temp = cv.compareHist(hist1, hist2, cv.HISTCMP_BHATTACHARYYA)
完成了这第二步成片效果还是难以接受。在直接加权的方法下,前景和北京的对比度似乎不能够同时被满足。
通过分析上面的海报,发现它的背景好像不是刻意直接加权上去的,而是主要由前景的主色调自然拼凑成的,类似于马赛克的形式。所以我觉得最重要的区别就是我的图片太少了,不能满足背景对色调的要求。
最后我采用的方法是对背景的每一格进行模糊处理,使之形成大色块,通过抹除细节的方式使前景的呈现更为清晰。
# 均值模糊
img = cv2.blur(img, (a, b))
# a, b 表示取均值的范围
背景图局部效果:
受图片数量的限制,最终效果当然远不及上面的海报,但也只能这样了。
成片就不放了。
完整代码:
import cv2 as cv
import os, random
import numpy as np
size = width, height = 200, 150
row, col = 48, 48
images = []
hists = []
used = []
def initialize():
print("Resizing, please wait...")
readPath = "images0"
savePath = "images1"
filenames = os.listdir(readPath)
for i, filename in enumerate(filenames):
img = cv.imread(readPath + '\\' + filename)
img = cv.resize(img, size)
cv.imwrite(savePath + '\\' + filename, img)
img = cv.imread("background0.jpg")
img = cv.resize(img, (width * col, height * row))
cv.imwrite("background1.jpg", img)
print("All images have been resized!")
def calcHists():
print("Calculating histograms, please wait...")
readPath = "images1"
filenames = os.listdir(readPath)
for i, filename in enumerate(filenames):
img = cv.imread(readPath + '\\' + filename)
images.append(img)
img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
hists.append(cv.calcHist([img], [0], None, [64], [0, 256]))
used.append(0)
print("All histograms have been calculated!")
def solve():
bg = cv.imread("background1.jpg")
newbg = cv.imread("background1.jpg")
for i in range(0, height * row, height):
for j in range(0, width * col, width):
now = cv.cvtColor(bg[i: i + height, j: j + width, 0: 3], cv.COLOR_BGR2GRAY)
nowHist = cv.calcHist([now], [0], None, [64], [0, 256]) # 计算该单元背景的直方图
ttt = bg[i: i + height, j: j + width, 0: 3]
ttt = cv.blur(ttt, (200, 200)) # 模糊化该单元背景
newbg[i: i + height, j: j + width, 0: 3] = ttt
minimum = 1
for k, img in enumerate(images): # 找色调最统一的
if used[k]:
continue
temp = cv.compareHist(hists[k], nowHist, cv.HISTCMP_BHATTACHARYYA)
if temp < minimum + 1e-9:
minimum = temp
index = k
bg[i: i + height, j: j+ width, 0: 3] = images[index]
used[index] = 1
cv.imwrite("final.jpg", bg)
cv.imwrite("newbg.jpg", newbg)
if __name__ == "__main__":
initialize()
calcHists()
solve()
fg = cv.imread("final.jpg")
bg = cv.imread("newbg.jpg")
final = cv.addWeighted(fg, 0.5, bg, 0.5, 0)
cv.imwrite("final3.jpg", final)