用opencv把小照片合成大照片

这效果实在是不知道该怎么描述…

放一张著名的海报(楚门的世界),大概就这意思。

楚门的世界海报
放大看细节:
在这里插入图片描述

正好大计课上提到了 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)
   
  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值