OpenCV基础--以图像融合为例

安装

pip install opencv-python

图像读取

三通道读取BGR

img_jpg = cv2.imread(path+"bg.jpg")

四通道读取BGRA

img_png = cv2.imread(path+"bg.png",cv2.IMREAD_UNCHANGED)

图像大小

# 输出: 高,宽,通道数
img_jpg.shape
# -> (1280, 720, 3)

img_png.shape
# -> (1280, 720, 4)

图像保存

# cv2.imwrite(filename, img)
cv2.imwrite("bg.jpg", img_jpg)

为三通道图像添加alpha通道, 设置为不透明

通道切分
b_channel, g_channel, r_channel = cv2.split(img_jpg)
b_channel, g_channel, r_channel, a_channel = cv2.split(img_png)
alpha通道生成
alpha_channel = np.ones(b_channel.shape, dtype=b_channel.dtype) * 255
通道合成
bg_alpha = cv2.merge((b_channel, g_channel, r_channel, alpha_channel))

图像缩放

cv2.resize(src, dsize, dst=None, fx=None, fy=None, interpolation=None)
# src: 图像
# dsize: 输出尺寸,当输入为0时,fx、fy皆不可为0,dsize = Size(round(fxsrc.cols),round(fysrc.rows))->(宽, 高)
# dst:  输出图,形态和输入图相同,当dsize不等于0,输出图尺寸会和dsize相同,当dsize等于0,输出图尺寸会由输入图尺寸、fx、fy计算而得
# fx:  水平缩放比例,当输入为0时,fx=(double)dsize.width/src.cols
# fy: 垂直缩放比例,当输入为0时,fy=(double)dsize.height/src.rows
# interpolation: 插值方法
interpolation插值方法
interpolation插值方法备注
INTER_NEAREST最近邻插值
INTER_LINEAR双线性插值(默认设置)放大时计算速度快, 且效果还可以
INTER_AREA使用像素区域关系进行重采样。缩小时最佳
INTER_CUBIC4x4像素邻域的双三次插值放大时效果最佳, 但计算速度慢
INTER_LANCZOS48x8像素邻域的Lanczos插值
反向变换公式

假设 s r c src src为源图, d s t dst dst为目标图, w i d t h width width指对应宽度, h e i g h t height height指对应高度, x x x y y y为对应坐标值横纵坐标,则:
{ s r c x = d s t x × s r c w i d t h d s t w i d t h s r c y = d s t y × s r c h e i g h t d s t h e i g h t \begin{cases} src_x=dst_x\times\frac{src_{width}}{dst_{width}}\\ src_y=dst_y\times\frac{src_{height}}{dst_{height}} \end{cases} {srcx=dstx×dstwidthsrcwidthsrcy=dsty×dstheightsrcheight

举个栗子:

假设源图大小是 1920 × 1080 1920\times1080 1920×1080,目标图大小是 1280 × 720 1280\times720 1280×720,则有:

s r c w i d t h = 1920 , s r c h e i g h t = 1080 src_{width}=1920,src_{height}=1080 srcwidth=1920,srcheight=1080
d s t w i d t h = 1280 , d s t h e i g h t = 720 dst_{width}=1280,dst_{height}=720 dstwidth=1280,dstheight=720

计算目标图坐标(1,2)所对应的源图坐标值为:
s r c x = 1 × 1920 1280 ≈ 1.5 src_x=1\times\frac{1920}{1280}\approx1.5 srcx=1×128019201.5
s r c y = 2 × 1080 720 ≈ 3 src_y=2\times\frac{1080}{720}\approx3 srcy=2×72010803

INTER_NEAREST最近邻插值
  1. 根据反向变换公式,计算出目标图坐标值 ( d s t x , d s t y ) (dst_x,dst_y) (dstx,dsty)所对应的源图坐标浮点值 ( s r c x , s r c y ) (src_x,src_y) (srcx,srcy)
  2. ( s r c x , s r c y ) (src_x,src_y) (srcx,srcy)取整即可得到对应的源图坐标值,取整方法包括四舍五入、去尾法等。
INTER_LINEAR双线性插值
  1. 根据反向变换公式,计算出目标图坐标值 ( d s t x , d s t y ) (dst_x,dst_y) (dstx,dsty)所对应的源图坐标浮点值 ( s r c x , s r c y ) (src_x,src_y) (srcx,srcy)
  2. s r c x = i + u , s r c y = j + v src_x=i+u,src_y=j+v srcx=i+u,srcy=j+v,其中 i i i j j j均为浮点坐标的整数部分, u u u v v v为浮点坐标的小数部分,是取值[0,1)区间的浮点数
  3. 这个像素的值 f ( i + u , j + v ) f(i+u,j+v) f(i+u,j+v) 可由原图像中坐标为 ( i , j ) (i,j) (i,j) ( i + 1 , j ) (i+1,j) (i+1,j) ( i , j + 1 ) (i,j+1) (i,j+1) ( i + 1 , j + 1 ) (i+1,j+1) (i+1,j+1)所对应的周围四个像素的值决定:
    f ( + u , j + v ) = ( 1 − u ) ( 1 − v ) f ( i , j ) + ( 1 − u ) v f ( i , j + 1 ) + u ( 1 − v ) f ( i + 1 , j ) + u v f ( i + 1 , j + 1 ) f(+u,j+v)=(1-u)(1-v)f(i,j)+(1-u)vf(i,j+1)+u(1-v)f(i+1,j)+uvf(i+1,j+1) f(+u,j+v)=(1u)(1v)f(i,j)+(1u)vf(i,j+1)+u(1v)f(i+1,j)+uvf(i+1,j+1)
INTER_AREA

使用像素区域关系进行重采样。它可能是图像抽取的首选方法,因为它会产生无云纹理的结果。但是当图像缩放时,它类似于INTER_NEAREST方法。

具体原理解释可参考OpenCV里的INTER_AREA究竟是在做啥?

图像融合

两图像大小尺寸一致,可使用OpenCV中的addWeighted方法进行图像融合:

cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]]) → dst

参数说明
src1 – first input array.
alpha – weight of the first array elements.
src2 – second input array of the same size and channel number as src1.
beta – weight of the second array elements.
dst – output array that has the same size and number of channels as the input arrays.
gamma – scalar added to each sum.
dtype – optional depth of the output array; when both input arrays have the same depth, dtype can be set to -1, which will be equivalent to src1.depth().

d s t = s r c 1 × a l p h a + s r c 2 × b e t a + g a m m a ; dst = src1 \times alpha + src2 \times beta + gamma; dst=src1×alpha+src2×beta+gamma;

由参数说明可以看出,被叠加的两幅图像必须是尺寸相同、类型相同的。

融合效果

  • 图A:
    A
  • 图B:
    B
  • 效果图:
    result

局部融合

若两图像尺寸不一致,希望把图像A局部融合于图像bg(背景图)的某个区域,可根据图A的alpha通道数值计算透明度,将透明度作为权重weight进行加权融合:

{ f b g ( x 0 + i , y 0 + j ) k = w e i g h t ⋅ f A ( i , j ) k + ( 1 − w e i g h t ) ⋅ f b g ( x 0 + i , y 0 + j ) k , k = r , g , b f b g ( x 0 + i , y 0 + j ) a l p h a = 255 \begin{cases} f_{bg}(x_0+i,y_0+j)_k=weight\cdot f_A(i,j)_k+(1-weight)\cdot f_{bg}(x_0+i,y_0+j)_k, k=r,g,b\\ f_{bg}(x_0+i,y_0+j)_{alpha}=255 \end{cases} {fbg(x0+i,y0+j)k=weightfA(i,j)k+(1weight)fbg(x0+i,y0+j)k,k=r,g,bfbg(x0+i,y0+j)alpha=255
其 中 : w e i g h t = f A ( i , j ) a l p h a 255 , x 0 , y 0 为 图 A 放 置 于 背 景 图 的 起 始 位 置 点 坐 标 。 其中:weight=\frac{f_A(i,j)_{alpha}}{255},x_0,y_0为图A放置于背景图的起始位置点坐标。 weight=255fA(i,j)alphax0,y0A

此处采用向量化编程思想,代码如下:

import cv2
import os
import numpy as np

# 获取当前路径
path = os.getcwd() + "/"

# 读取背景图
bg = cv2.imread(path+"bg_air.jpg")
# 加上cv2.IMREAD_UNCHANGED保留png的alpha通道
plane = cv2.imread(path+"plane.png", cv2.IMREAD_UNCHANGED)

# 设置融合起始点坐标
x0 = 150
y0 = 500

# 构造与背景图同样大小的图A, 源图以外部分alpha通道数值为0, 表示全透明
b_channel, g_channel, r_channel = cv2.split(bg)
_channel = np.zeros(b_channel.shape, dtype=b_channel.dtype)
picA = cv2.merge((_channel, _channel, _channel, _channel))
height = plane.shape[0]
width = plane.shape[1]
picA[x0:height + x0, y0: width+y0] = plane[:, :]

# 若背景图为jpg, 则加上alpha通道, 数值为255, 表示不透明
alpha_channel = np.ones(b_channel.shape, dtype=b_channel.dtype) * 255
bg_alpha = cv2.merge((b_channel, g_channel, r_channel, alpha_channel))

# 对图片进行加权融合
_weight = picA[:, :, 3] / 255
weight = cv2.merge((_weight, _weight, _weight, _weight))
result = weight * picA + (1-weight) * bg_alpha
cv2.imwrite(path+"result.jpg", result)

融合效果

  • 图A:
    picA
  • 背景图:
    BG
  • 融合效果
    result
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值