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

时间秒杀一切 提交于 2020-01-25 08:11:38

安装

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插值
反向变换公式

假设srcsrc为源图,dstdst为目标图,widthwidth指对应宽度,heightheight指对应高度,xxyy为对应坐标值横纵坐标,则:
{srcx=dstx×srcwidthdstwidthsrcy=dsty×srcheightdstheight \begin{cases} src_x=dst_x\times\frac{src_{width}}{dst_{width}}\\ src_y=dst_y\times\frac{src_{height}}{dst_{height}} \end{cases}

举个栗子:

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

srcwidth=1920,srcheight=1080 src_{width}=1920,src_{height}=1080
dstwidth=1280,dstheight=720 dst_{width}=1280,dst_{height}=720

计算目标图坐标(1,2)所对应的源图坐标值为:
srcx=1×192012801.5 src_x=1\times\frac{1920}{1280}\approx1.5
srcy=2×10807203 src_y=2\times\frac{1080}{720}\approx3

INTER_NEAREST最近邻插值
  1. 根据反向变换公式,计算出目标图坐标值(dstx,dsty)(dst_x,dst_y)所对应的源图坐标浮点值(srcx,srcy)(src_x,src_y)
  2. (srcx,srcy)(src_x,src_y)取整即可得到对应的源图坐标值,取整方法包括四舍五入、去尾法等。
INTER_LINEAR双线性插值
  1. 根据反向变换公式,计算出目标图坐标值(dstx,dsty)(dst_x,dst_y)所对应的源图坐标浮点值(srcx,srcy)(src_x,src_y)
  2. srcx=i+u,srcy=j+vsrc_x=i+u,src_y=j+v,其中iijj均为浮点坐标的整数部分,uuvv为浮点坐标的小数部分,是取值[0,1)区间的浮点数
  3. 这个像素的值f(i+u,j+v)f(i+u,j+v) 可由原图像中坐标为 (i,j)(i,j)(i+1,j)(i+1,j)(i,j+1)(i,j+1)(i+1,j+1)(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) 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().

dst=src1×alpha+src2×beta+gamma;dst = src1 \times alpha + src2 \times beta + gamma;

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

融合效果

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

局部融合

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

{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 \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}
weight=fA(i,j)alpha255x0,y0A 其中:weight=\frac{f_A(i,j)_{alpha}}{255},x_0,y_0为图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:
    picA
  • 背景图:
    BG
  • 融合效果
    result

微信公众号二维码

微信扫一扫关注公众号
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!