安装
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_CUBIC | 4x4像素邻域的双三次插值 | 放大时效果最佳, 但计算速度慢 |
INTER_LANCZOS4 | 8x8像素邻域的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×12801920≈1.5
s
r
c
y
=
2
×
1080
720
≈
3
src_y=2\times\frac{1080}{720}\approx3
srcy=2×7201080≈3
INTER_NEAREST最近邻插值
- 根据反向变换公式,计算出目标图坐标值 ( 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)
- 对 ( s r c x , s r c y ) (src_x,src_y) (srcx,srcy)取整即可得到对应的源图坐标值,取整方法包括四舍五入、去尾法等。
INTER_LINEAR双线性插值
- 根据反向变换公式,计算出目标图坐标值 ( 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)
- 令 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)区间的浮点数
- 这个像素的值
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)=(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)
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:
- 图B:
- 效果图:
局部融合
若两图像尺寸不一致,希望把图像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=weight⋅fA(i,j)k+(1−weight)⋅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)alpha,x0,y0为图A放置于背景图的起始位置点坐标。
此处采用向量化编程思想,代码如下:
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:
- 背景图:
- 融合效果