CV—双线性插值算法,python实现,opencv(resize)源码分析⽂章⽬录
CV — 双线性插值算法
⼀、概念
双线性插值:
双线性插值,⼜称为双线性内插。在数学上,双线性插值是对线性插值在⼆维直⾓⽹格上的扩展,⽤于对双变量函数(例如 x 和y)进⾏插值。其核⼼思想是在两个⽅向分别进⾏⼀次线性插值。⽰例:
附:–双线性插值:
我们已知函数 f 在 Q = (x1,y1),Q = (x1,y2),Q = (x2,y1),Q = (x2,y2) 四个点的值,我们想得到未知函数 f 在点 P = (x,y) 的值。
步骤如下:
⾸先在 X ⽅向进⾏线性插值,得到:
然后在 y ⽅向进⾏线性插值,得到:
以上便是采⽤双线性插值,得到的 P (x,y) 的值 f(x,y)。
注意此处如果先在 y ⽅向插值、再在 x ⽅向插值,其结果与按照上述顺序双线性插值的结果是⼀样的。
⼆、双线性插值函数
插值法是⼀种根据原图(source)图⽚信息构造⽬标图像(destination)的⽅法。⽽其中的双线性插值法是⼀种⽬前使⽤较多的插值⽅法,他在精度和计算量之间有较好的权衡,所以使⽤较为⼴泛。
1.最近邻插值法
11122122
给定⼀个图⽚像素的矩阵,将1个3∗33∗3尺⼨的矩阵使⽤最近邻插值法到4∗4的矩阵
⟹
原始图⽚像素矩阵(src)和缩放后图⽚像素矩阵(dst)的对应对应关系公式如下:src_x = dst_x * (src_width / dst_width )src_y = dst_y * (src_heght / dst_height )
dst[0][0] 对应的src位置如下:src_x = 0 * (3 / 4) = 0src_y = 0 * (3 / 4) = 0
所以dst[0][0] = src[src_x][src_y] = src[0][0] = 1dst[0][1] 对应的src位置如下:src_x = 0 * (3 / 4) = 0src_y = 1 * (3 / 4) = 0.75
所以dst[0][0] = src[src_x][src_y] = src[0][0.75]
由于图⽚像素最⼩单位是1,所以四舍五⼊dst[0][1] = src[0][1] = 2以此类推,最终结果如下:
这是⼀种最基本、最简单的图像缩放算法,效果也是最不好的,放⼤后的图像有很严重的马赛克,缩⼩后的图像有很严重的失真;效果不好的根源就是其简单的最近邻插值⽅法引⼊了严重的图像失真,⽐如,当由⽬标图的坐标反推得到的源图的的坐标是⼀个的时候,采⽤了的⽅法,直接采⽤了和这个浮点数最接近的象素的值,这种⽅法是很不科学的,当推得坐标值为 0.75的时候,不应该就简单的取为1,既然是0.75,⽐1要⼩0.25 ,⽐0要⼤0.75 ,那么⽬标象素值其实应该根据这个源图中虚拟的点四周的四个真实的点来按照⼀定的规律计算出来的,这样才能达到更好的缩放效果。
2. 双线性插值法
双线型内插值算法就是⼀种⽐较好的图像缩放算法,它充分的利⽤了源图中虚拟点四周的四个真实存在像素点的值(灰度值或者RGB 值)来共同决定⽬标图中的⼀个像素点的值(灰度值或者RGB值),因此缩放效果⽐简单的最邻近插值要好很多,缩放后图像质量⾼,不会出现值(灰度值或者RGB值)不连续的情况。
2.1 双线性内插值算法核⼼
⎣⎡147258369⎦
⎤(src)
⎣⎢⎢⎡
????????
????⎦
⎥⎥⎤(dst)
(博客第三部分会对此公式进⾏优化)
⎣⎢⎢⎡1477
25883699
3699⎦
⎥⎥⎤(dst)
浮点数四舍五⼊
对于⼀个⽬的像素点dst[x][y],通过反向变换得到的源图像中 浮点坐标为 src[i + u][j + v] (其中i、j均为浮点坐标的整数部分,u、v为浮点坐标的⼩数部分,是取值[0,1)区间的浮点数),则这个像素点的值 f(i+u,j+v) 可由原图像中坐标为 (i,j)、(i+1,j)、(i,j+1)、(i+1,j+1)所对应的决定,即:f(i+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(i,j)表⽰源图像(i,j)处的的值(灰度值或者RGB值),以此类推。
2.2 ⽰例
resize函数vba 假如⽬标图的象素坐标为 (1,1),反推得到的对应于源图的坐标是(0.75 , 0.75), 这其实只是⼀个概念上的虚拟象素,实际在源图中并不存在这样⼀个象素,那么⽬标图的象素(1,1)的取值不能够由这个虚拟象素来决定,⽽只能由源图的这四个象素共同决定:(0,0)(0,1)(1,0)(1,1),⽽由于(0.75,0.75)离(1,1)要更近⼀些,那么(1,1)所起的决定作⽤更⼤⼀些,这从公式1中的系数uv=0.75×0.75就可以体现出来,⽽(0.75,0.75)离(0,0)最远,所以(0,0)所起的决定作⽤就要⼩⼀些,公式中系数为(1-u)(1-v)=0.25×0.25也体现出了这⼀特点。
2.3 算法完整步骤
假设原始图像⼤⼩为size=m×n,其中m与n分别是原始图像的⾏数与列数。
若图像的缩放因⼦是t(t>0),则⽬标图像的⼤⼩size=t×m×t×n。对于⽬标图像的某个像素点P(x,y)通过P*1/t可得到对应的原始图像坐标P’( x1,y1),其中x1=x/t,y1=y/t,由于x1,y1都不是整数所以并不存在这样的点,这样可以出与它相邻的四个像素点的值(灰度值或者RGB值)、f2、f3、f4,使⽤双线性插值算法就可以得到这个像素点P’(x1,y1)的值(灰度值或者RGB值),也就是像素点P(x,y)的值(灰度值或者RGB值)。⼀个完整的双线性插值算法可描述如下:
1. 通过原始图像和⽐例因⼦得到新图像的⼤⼩,并⽤零矩阵初始化新图像。
2. 由新图像的某个像素点(x,y)映射到原始图像(x’,y’)处。
3. 对x’,y’取整得到(xx,yy)并得到(xx,yy)、(xx+1,yy)、(xx,yy+1)和(xx+1,yy+1)的值。
4. 利⽤双线性插值得到像素点(x,y)的值并写回新图像。重复步骤2,3 直到新图像的所有像素点写完。
三、opencv (resize )源码分析
如果采⽤以上的公式进⾏编程实现,会发现计算出来的结果会和使⽤openCV对应的resize()函数得到的结果完全不⼀样,⽽且从直观上观察,图⽚缩放越⼩,差异越明显,上⾯的算法会使缩放之后的图⽚失真。
3.1 那么opencv 到底是做了哪些优化呢?
主要原因: openCV 的 resize 函数对与这个公式进⾏了改进
src_x = dst_x * (src_width / dst_width )src_y = dst_y * (src_heght / dst_height )
改为如下:
src_x = (j + 0.5) * float (org_w / dst_w ) - 0.5src_y = (i + 0.5) * float (org_h / dst_h ) - 0.5
3.2 参考资料
:博客:
双线性插值算法:
opencv处理放缩前后图⽚中⼼重合问题:
四、python 代码实现
#!/usr/bin/env python
最相邻四个像素点的值(灰度值或者RGB 值)即将最近的四个点采⽤加权平均,离得越近的点的权值越⼤。
#!/usr/bin/env python
# encoding: utf-8
'''
@Author : pentiumCM
@Email:****************
@Software: PyCharm
@File : bilinear_interpolation.py
@Time : 2020/3/7 11:01
@desc : 双线性插值,来调整图像的尺⼨
'''
import numpy as np
import cv2
import math
def bilinear(src_img, dst_shape):
"""
双线性插值法,来调整图⽚尺⼨
:param org_img: 原始图⽚
:param dst_shape: 调整后的⽬标图⽚的尺⼨
:return: 返回调整尺⼨后的图⽚矩阵信息
"""
dst_img = np.zeros((dst_shape[0], dst_shape[1],3), np.uint8)
dst_h, dst_w = dst_shape
src_h = src_img.shape[0]
src_w = src_img.shape[1]
# i:纵坐标y,j:横坐标x
# 缩放因⼦,dw,dh
scale_w = src_w / dst_w
scale_h = src_h / dst_h
for i in range(dst_h):
for j in range(dst_w):
src_x =float((j +0.5)* scale_w -0.5)
src_y =float((i +0.5)* scale_h -0.5)
# 向下取整,代表靠近源点的左上⾓的那⼀点的⾏列号
src_x_int = math.floor(src_x)
src_y_int = math.floor(src_y)
# 取出⼩数部分,⽤于构造权值
src_x_float = src_x - src_x_int
src_y_float = src_y - src_y_int
if src_x_int +1== src_w or src_y_int +1== src_h:
dst_img[i, j,:]= src_img[src_y_int, src_x_int,:]
continue
dst_img[i, j,:]=(1.- src_y_float)*(1.- src_x_float)* src_img[src_y_int, src_x_int,:]+ \
(1.- src_y_float)* src_x_float * src_img[src_y_int, src_x_int +1,:]+ \
src_y_float *(1.- src_x_float)* src_img[src_y_int +1, src_x_int,:]+ \
src_y_float * src_x_float * src_img[src_y_int +1, src_x_int +1,:]
return dst_img
if __name__ =='__main__':
img_path ='F:/experiment/image_identification/License_plate_recognition/Img/1.jpg'
src = cv2.imread(img_path, cv2.IMREAD_COLOR)
# ⾼337 - 宽500
src_shape =(src.shape[0], src.shape[1])
# 定义图⽚缩放后的尺⼨
dst_shape =(100,400)
# 图像放缩均采⽤双线性插值法
# opencv的放缩图像函数
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论