Article directory
Theoretical basis
Linear interpolation: Two points determine a straight line, and the values of unknown points on the line can be determined by the equation of the straight line.
In two-dimensional space, linear difference needs to be extended to bilinear interpolation.
Let the pixel values of A, B, C, and D be A, B, C, and D respectively.
The pixel value of P1 is P 1 = 1 / row × ( ( row − a ) A + a B ) P1 = 1/row \times ((row-a)A + aB)P1 _=1/row×((row−a)A+a B )
The pixel value of P2 isP 2 = 1 / row × ( ( row − a ) C + a D ) P2 = 1/row \times ((row-a)C + aD)P2 _=1/row×((row−a)C+a D )
The pixel value of P isP = 1 / col × ( ( col − b ) P 1 + b P 2 ) P = 1/col \times ((col-b)P1 + bP2)P=1/col×((col−b)P1+bP2)
将P1、P2代入得
P = 1 c o l × r o w × [ ( r o w − a ) ( c l o − b ) A + a ( c o l − b ) B ) + a ( r o w − a ) C + a b D ] P = \frac{1}{col \times row} \times \left[ (row-a)(clo-b)A + a(col-b)B) + a(row-a)C + abD\right] P=col×row1×[(row−a)(clo−b)A+a(col−b)B)+a(row−a)C+ab D ]
Note: If point P is projected to the original image for interpolation, the row and col values are 1 and 1.
Coding implementation (Python)
# coding=utf-8
import cv2
import numpy as np
def Bilinear(img, up_height, up_width, channels):
bilinear_img = np.zeros(shape=(up_height, up_width, channels), dtype=np.uint8)
img_height, img_width, img_channels = img.shape
for i in range(up_height):
for j in range(up_width):
row_up = int(i * img_height/up_height) # 原图像左像素点
col_left = int(j * img_width/up_width) # 原图像上像素点
row_bottom = row_up + 1
col_right = col_left + 1
if row_bottom == img_height:
row_bottom -= 1
if col_right == img_width:
col_right -= 1
a = j * img_width / up_width - col_left
b = i * img_height / up_height - row_up
# 这里定义bilinear_img已经强制了数据类型\
bilinear_img[i][j] = ( \
(1 - a) * (1 - b) * img[row_up][col_left] + \
a * (1 - b) * img[row_up][col_right] + \
(1 - a) * b * img[row_bottom][col_left] + \
a * b * img[row_bottom][col_right] \
)
return bilinear_img
if __name__ == "__main__":
# 无法处理透明度通道(压根就没读进来)
img_source = cv2.imread(r"Image_restoration\source\1.png")
img_height, img_width, img_channels = img_source.shape
print("height {}, width {}, channels {}".format( img_height, img_width, img_channels))
times = 3 # 放大3倍
up_height = int(img_height * times)
up_width = int(img_width * times)
print("up_height {}, up_width {}".format(up_height, up_width))
bilinear_img = Bilinear(img_source, up_height, up_width, img_channels)
cv2.imshow("img_source", img_source)
cv2.imshow("bilinear_img", bilinear_img)
cv2.imwrite(r"Image_restoration\result\1_bilinear.png", bilinear_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Show results
- After enlarging the image using bilinear interpolation, the color blocks are reduced, but the clarity effect is still very limited.
Other weights
The bilinear interpolation formula designs a weight for the four points near point P.
We can also use other forms of weighting. like:
- Gaussian distribution
If the distance is r, then use Gaussian formula to calculate its weight:
ω ( r ) = e − ( ε r ) 2 \bf \omega(r) = e^{-(\varepsilon r)^2}ω ( r )=e−(εr)2
Note: Because the weights need to be normalized, the coefficients that will be omitted are directly discarded here.
In the following two-dimensional space.
The pixel value of point P is
P = 1 ω ( a ) + ω ( b ) + ω ( c ) + ω ( d ) [ ω ( a ) × A + ω ( b ) × B + ω ( c ) × C + ω ( d ) × D ] P = \frac{1}{\omega(a)+\omega(b)+\omega(c)+\omega(d)}[\omega(a)\times A+\omega( b)\times B+\omega(c)\times C+\omega(d)\times D]P=o ( a )+o ( b )+o ( c )+o ( d )1[ o ( a )×A+o ( b )×B+o ( c )×C+o ( d )×D]
The effect produced by the blogger is average.