超分辨率技术之插值算法

🌞欢迎莅临我的个人主页👈🏻这里是我专注于深度学习领域、用心分享知识精粹与智慧火花的独特角落!🍉

🌈如果大家喜欢文章,欢迎:关注🍷+点赞👍🏻+评论✍🏻+收藏🌟,如有错误敬请指正!🪐

🍓“请不要相信胜利就像山坡上的蒲公英一样唾手可得,但是请相信生活中总有美好值得我们全力以赴,哪怕粉身碎骨!”🌹

前言

        超分辨率是计算机视觉领域的重要技术,旨在提高低分辨率图像的质量。它通过复杂算法从低分辨率图像中重构出高分辨率图像,在医疗影像、影视娱乐等领域有广泛应用,为图像质量提升带来新突破。

        超分辨率技术分为多种:插值算法、重建技术和深度学习。本文将重点介绍插值算法的原理和应用,并简要说明三种深度学习技术和相关专业软件在超分辨率领域的应用情况!

目录

最近邻插值

双线性插值

双三次插值

Lanczos插值

样条插值

高斯插值

深度学习的SR技术

画质增强专业软件

图像增强效果对比


原理:插值算法通过在已知像素点之间插入新的像素值来提高图像的分辨率。它相对简单直接,能够在一定程度上增加图像的尺寸和细节表现。

最近邻插值

  • 原理:每个新像素的值直接采用脱离其最近的已知像素的值。
  • 优点:计算简单,速度快。
  • 缺点:图像边缘可能会出现锯齿效应和块状效应,图像质量较低。

🍃手写实现

def nearest_neighbor(image, scale_factor):''':param image:    输入图像:param scale_factor:   缩放因子:return: '''img_np = np.array(image)img_np = img_np.astype(np.float32)original_height, original_width = img_np.shape[:2]new_height = int(original_height * scale_factor)new_width = int(original_width * scale_factor)# 创建新的图像矩阵new_img_np = np.zeros((new_height, new_width, img_np.shape[2]), dtype=img_np.dtype)# 遍历新图像的每个像素,进行最近邻插值for i in range(new_height):for j in range(new_width):# 计算对应于原始图像的最近邻像素orig_x = int(i / scale_factor)orig_y = int(j / scale_factor)new_img_np[i, j] = img_np[orig_x, orig_y]new_img = Image.fromarray(np.clip(new_img_np, 0, 255).astype(np.uint8))return new_img

🍂内置函数

image1 = image.resize((height * scale_factor, width * scale_factor), Image.NEAREST)

💥完整代码

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt# -----------------------------------------------------#
#                   最近邻插值
# ------------------------------------------------------#
def nearest_neighbor(image, scale_factor):''':param image:    输入图像:param scale_factor:   缩放因子:return: '''img_np = np.array(image)img_np = img_np.astype(np.float32)original_height, original_width = img_np.shape[:2]new_height = int(original_height * scale_factor)new_width = int(original_width * scale_factor)# 创建新的图像矩阵new_img_np = np.zeros((new_height, new_width, img_np.shape[2]), dtype=img_np.dtype)# 遍历新图像的每个像素,进行最近邻插值for i in range(new_height):for j in range(new_width):# 计算对应于原始图像的最近邻像素orig_x = int(i / scale_factor)orig_y = int(j / scale_factor)new_img_np[i, j] = img_np[orig_x, orig_y]new_img = Image.fromarray(np.clip(new_img_np, 0, 255).astype(np.uint8))return new_imgif __name__ == '__main__':path = r'test/test.jpg'scale_factor = 3image = Image.open(path).convert('RGB')height, width = image.sizeimage0 = nearest_neighbor(image, scale_factor)image1 = image.resize((height * scale_factor, width * scale_factor), Image.NEAREST)plt.subplot(1, 3, 1)plt.imshow(image)plt.title("Original")plt.subplot(1, 3, 2)plt.imshow(image0)plt.title("Nearest")plt.subplot(1, 3, 3)plt.imshow(image1)plt.title("PIL")plt.show()

🍓效果展示

双线性插值

  • 原理:对目标像素点的上下和左右像素点的数值进行线性插值,取其加权托盘。
  • 优点:比最近邻接值更平滑,过渡减弱自然。
  • 缺点:图像的细节和边缘可能会模糊。

🍃手写实现

def bilinear_interpolation(image, scale_factor):''':param image:   输入图像:param scale_factor:    缩放因子:return:'''image = np.array(image)image = image.astype(np.float32)height, width = image.shape[:2]new_height, new_width = int(height * scale_factor), int(width * scale_factor)# 初始化新图像if len(image.shape) == 3:  # 彩色图像new_image = np.zeros((new_height, new_width, image.shape[2]), dtype=image.dtype)else:  # 灰度图像new_image = np.zeros((new_height, new_width), dtype=image.dtype)# 缩放比例row_scale = height / new_heightcol_scale = width / new_widthfor i in range(new_height):for j in range(new_width):# 找到原图像中的对应坐标src_row = i * row_scalesrc_col = j * col_scale# 计算上下和左右的整数像素位置row_top = min(int(np.floor(src_row)), height - 2)row_bottom = row_top + 1col_left = min(int(np.floor(src_col)), width - 2)col_right = col_left + 1# 计算插值的权重row_weight = src_row - row_topcol_weight = src_col - col_left# 进行插值计算if len(image.shape) == 3:  # 彩色图像for c in range(image.shape[2]):top_left = image[row_top, col_left, c]top_right = image[row_top, col_right, c]bottom_left = image[row_bottom, col_left, c]bottom_right = image[row_bottom, col_right, c]top = top_left + (top_right - top_left) * col_weightbottom = bottom_left + (bottom_right - bottom_left) * col_weightnew_image[i, j, c] = top + (bottom - top) * row_weightelse:  # 灰度图像top_left = image[row_top, col_left]top_right = image[row_top, col_right]bottom_left = image[row_bottom, col_left]bottom_right = image[row_bottom, col_right]top = top_left + (top_right - top_left) * col_weightbottom = bottom_left + (bottom_right - bottom_left) * col_weightnew_image[i, j] = top + (bottom - top) * row_weightnew_image = Image.fromarray(np.clip(new_image, 0, 255).astype(np.uint8))return new_image

🍂内置函数

# Scipy
image1 = zoom(image, (scale_factor, scale_factor, 1), order=1)
# PIL
image2 = image.resize((height*scale_factor, width*scale_factor), Image.BILINEAR)

💥完整代码

import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from scipy.ndimage import zoom# -----------------------------------------------------#
#                       双线性插值
# ------------------------------------------------------#
def bilinear_interpolation(image, scale_factor):''':param image:   输入图像:param scale_factor:    缩放因子:return:'''image = np.array(image)image = image.astype(np.float32)height, width = image.shape[:2]new_height, new_width = int(height * scale_factor), int(width * scale_factor)# 初始化新图像if len(image.shape) == 3:  # 彩色图像new_image = np.zeros((new_height, new_width, image.shape[2]), dtype=image.dtype)else:  # 灰度图像new_image = np.zeros((new_height, new_width), dtype=image.dtype)# 缩放比例row_scale = height / new_heightcol_scale = width / new_widthfor i in range(new_height):for j in range(new_width):# 找到原图像中的对应坐标src_row = i * row_scalesrc_col = j * col_scale# 计算上下和左右的整数像素位置row_top = min(int(np.floor(src_row)), height - 2)row_bottom = row_top + 1col_left = min(int(np.floor(src_col)), width - 2)col_right = col_left + 1# 计算插值的权重row_weight = src_row - row_topcol_weight = src_col - col_left# 进行插值计算if len(image.shape) == 3:  # 彩色图像for c in range(image.shape[2]):top_left = image[row_top, col_left, c]top_right = image[row_top, col_right, c]bottom_left = image[row_bottom, col_left, c]bottom_right = image[row_bottom, col_right, c]top = top_left + (top_right - top_left) * col_weightbottom = bottom_left + (bottom_right - bottom_left) * col_weightnew_image[i, j, c] = top + (bottom - top) * row_weightelse:  # 灰度图像top_left = image[row_top, col_left]top_right = image[row_top, col_right]bottom_left = image[row_bottom, col_left]bottom_right = image[row_bottom, col_right]top = top_left + (top_right - top_left) * col_weightbottom = bottom_left + (bottom_right - bottom_left) * col_weightnew_image[i, j] = top + (bottom - top) * row_weightnew_image = Image.fromarray(np.clip(new_image, 0, 255).astype(np.uint8))return new_imageif __name__ == '__main__':path = r'test/test.jpg'scale_factor = 3image = Image.open(path).convert('RGB')height, width = image.sizeimage_bilinear = bilinear_interpolation(image, scale_factor)image1 = zoom(image, (scale_factor, scale_factor, 1), order=1)image2 = image.resize((height*scale_factor, width*scale_factor), Image.BILINEAR)plt.subplot(2, 2, 1)plt.imshow(image)plt.title("Original")plt.subplot(2, 2, 2)plt.imshow(image_bilinear)plt.title("Bilinear")plt.subplot(2, 2, 3)plt.imshow(image1)plt.title("Scipy")plt.subplot(2, 2, 4)plt.imshow(image2)plt.title("PIL")plt.show()

🍓效果展示

双三次插值

  • 原理:基于16个端点像素(距离最近的4x4像素块)进行三次迭代方式插值计算新像素的值。
  • 优点:相比双线性插值,生成的图像更加平滑,边缘细节更好,过渡更自然。
  • 缺点:计算量比双线性插值大,图像可能会有一些模糊。

🍃手写实现

def cubic_weight(t):"""计算三次插值权重的函数"""a = -0.5t = abs(t)if t <= 1:return (a + 2) * (t ** 3) - (a + 3) * (t ** 2) + 1elif t <= 2:return a * (t ** 3) - 5 * a * (t ** 2) + 8 * a * t - 4 * areturn 0def bicubic_interpolation(image, scale_factor):''':param image:   输入图像:param scale_factor: 缩放因子:return: '''img_np = np.array(image)img_np = img_np.astype(np.float32)original_height, original_width = img_np.shape[:2]new_height = int(original_height * scale_factor)new_width = int(original_width * scale_factor)new_img_np = np.zeros((new_height, new_width, img_np.shape[2]), dtype=img_np.dtype)# 遍历新图像的每个像素,进行双三次插值for i in range(new_height):for j in range(new_width):# 计算原始图像中浮点坐标的位置orig_x = i / scale_factororig_y = j / scale_factor# 取整得到插值中心的整数部分和小数部分x_int = int(orig_x)y_int = int(orig_y)dx = orig_x - x_intdy = orig_y - y_int# 双三次插值的核心部分,遍历16个邻近像素for m in range(-1, 3):for n in range(-1, 3):# 获取边界处理后的坐标xm = min(max(x_int + m, 0), original_height - 1)yn = min(max(y_int + n, 0), original_width - 1)# 计算权重weight_x = cubic_weight(m - dx)weight_y = cubic_weight(n - dy)weight = weight_x * weight_y# 插值计算,按通道计算new_img_np[i, j] += img_np[xm, yn] * weightnew_img = Image.fromarray(np.clip(new_img_np, 0, 255).astype(np.uint8))return new_img

🍂内置函数

image1 = image.resize((height * scale_factor, width * scale_factor), Image.BICUBIC)

💥完整代码

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt# -----------------------------------------------------#
#                       双三次插值
# ------------------------------------------------------#
def cubic_weight(t):"""计算三次插值权重的函数"""a = -0.5t = abs(t)if t <= 1:return (a + 2) * (t ** 3) - (a + 3) * (t ** 2) + 1elif t <= 2:return a * (t ** 3) - 5 * a * (t ** 2) + 8 * a * t - 4 * areturn 0def bicubic_interpolation(image, scale_factor):''':param image:   输入图像:param scale_factor: 缩放因子:return: '''img_np = np.array(image)img_np = img_np.astype(np.float32)original_height, original_width = img_np.shape[:2]new_height = int(original_height * scale_factor)new_width = int(original_width * scale_factor)new_img_np = np.zeros((new_height, new_width, img_np.shape[2]), dtype=img_np.dtype)# 遍历新图像的每个像素,进行双三次插值for i in range(new_height):for j in range(new_width):# 计算原始图像中浮点坐标的位置orig_x = i / scale_factororig_y = j / scale_factor# 取整得到插值中心的整数部分和小数部分x_int = int(orig_x)y_int = int(orig_y)dx = orig_x - x_intdy = orig_y - y_int# 双三次插值的核心部分,遍历16个邻近像素for m in range(-1, 3):for n in range(-1, 3):# 获取边界处理后的坐标xm = min(max(x_int + m, 0), original_height - 1)yn = min(max(y_int + n, 0), original_width - 1)# 计算权重weight_x = cubic_weight(m - dx)weight_y = cubic_weight(n - dy)weight = weight_x * weight_y# 插值计算,按通道计算new_img_np[i, j] += img_np[xm, yn] * weightnew_img = Image.fromarray(np.clip(new_img_np, 0, 255).astype(np.uint8))return new_imgif __name__ == '__main__':path = r'test/test.jpg'scale_factor = 3image = Image.open(path).convert('RGB')height, width = image.sizeimage0 = bicubic_interpolation(image, scale_factor)image1 = image.resize((height * scale_factor, width * scale_factor), Image.BICUBIC)plt.subplot(1, 3, 1)plt.imshow(image)plt.title("Original")plt.subplot(1, 3, 2)plt.imshow(image0)plt.title("Bicubic")plt.subplot(1, 3, 3)plt.imshow(image1)plt.title("PIL")plt.show()

🍓效果展示

Lanczos插值

  • 原理:利用Lanczos核函数进行插值,通常在3x3或5x5像素邻域内进行计算。该方法是对双三次插值的进一步改进。
  • 优点:高质量的插值,能够较好地保留图像细节,减少剪辑效果。
  • 缺点:计算复杂度较高,容易产生振铃效应(ringing artifacts)。

🍃手写实现

def lanczos_weight(x, a):"""Lanczos 核函数,计算权重"""if x == 0:return 1elif -a < x < a:return np.sinc(x) * np.sinc(x / a)else:return 0def lanczos_interpolation_custom(image, scale_factor, a=3):''':param image:   输入图像:param scale_factor: 缩放因子:param a:   Lanczos 窗宽,通常为2或3:return:'''img_np = np.array(image)img_np = img_np.astype(np.float32)# 获取原始图像的尺寸original_height, original_width = img_np.shape[:2]# 计算新图像的尺寸new_height = int(original_height * scale_factor)new_width = int(original_width * scale_factor)# 创建新的图像矩阵new_img_np = np.zeros((new_height, new_width, img_np.shape[2]), dtype=img_np.dtype)# 遍历新图像的每个像素,进行 Lanczos 插值for i in range(new_height):for j in range(new_width):# 计算原始图像中浮点坐标的位置orig_x = i / scale_factororig_y = j / scale_factor# 取整得到整数部分和小数部分x_int = int(orig_x)y_int = int(orig_y)dx = orig_x - x_intdy = orig_y - y_int# Lanczos 插值核心部分,考虑 a×a 个邻近像素total_weight = 0for m in range(-a + 1, a):for n in range(-a + 1, a):xm = min(max(x_int + m, 0), original_height - 1)yn = min(max(y_int + n, 0), original_width - 1)weight_x = lanczos_weight(m - dx, a)weight_y = lanczos_weight(n - dy, a)weight = weight_x * weight_ynew_img_np[i, j] += img_np[xm, yn] * weighttotal_weight += weight# 防止权重过小,做归一化if total_weight > 0:new_img_np[i, j] = new_img_np[i, j] / total_weightnew_img = Image.fromarray(np.clip(new_img_np, 0, 255).astype(np.uint8))return new_img

🍂内置函数

image1 = image.resize((height * scale_factor, width * scale_factor), Image.LANCZOS)

💥完整代码

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt# -----------------------------------------------------#
#                   Lanczos插值
# ------------------------------------------------------#
def lanczos_weight(x, a):"""Lanczos 核函数,计算权重"""if x == 0:return 1elif -a < x < a:return np.sinc(x) * np.sinc(x / a)else:return 0def lanczos_interpolation_custom(image, scale_factor, a=3):''':param image:   输入图像:param scale_factor: 缩放因子:param a:   Lanczos 窗宽,通常为2或3:return:'''img_np = np.array(image)img_np = img_np.astype(np.float32)original_height, original_width = img_np.shape[:2]new_height = int(original_height * scale_factor)new_width = int(original_width * scale_factor)# 创建新的图像矩阵new_img_np = np.zeros((new_height, new_width, img_np.shape[2]), dtype=img_np.dtype)# 遍历新图像的每个像素,进行 Lanczos 插值for i in range(new_height):for j in range(new_width):# 计算原始图像中浮点坐标的位置orig_x = i / scale_factororig_y = j / scale_factor# 取整得到整数部分和小数部分x_int = int(orig_x)y_int = int(orig_y)dx = orig_x - x_intdy = orig_y - y_int# Lanczos 插值核心部分,考虑 a×a 个邻近像素total_weight = 0for m in range(-a + 1, a):for n in range(-a + 1, a):xm = min(max(x_int + m, 0), original_height - 1)yn = min(max(y_int + n, 0), original_width - 1)weight_x = lanczos_weight(m - dx, a)weight_y = lanczos_weight(n - dy, a)weight = weight_x * weight_ynew_img_np[i, j] += img_np[xm, yn] * weighttotal_weight += weight# 防止权重过小,做归一化if total_weight > 0:new_img_np[i, j] = new_img_np[i, j] / total_weightnew_img = Image.fromarray(np.clip(new_img_np, 0, 255).astype(np.uint8))return new_imgif __name__ == '__main__':path = r'test/test.jpg'scale_factor = 3image = Image.open(path).convert('RGB')height, width = image.sizeimage0 = lanczos_interpolation_custom(image, scale_factor)image1 = image.resize((height * scale_factor, width * scale_factor), Image.BILINEAR)plt.subplot(1, 3, 1)plt.imshow(image)plt.title("Original")plt.subplot(1, 3, 2)plt.imshow(image0)plt.title("Lanczos")plt.subplot(1, 3, 3)plt.imshow(image1)plt.title("PIL")plt.show()

🍓效果展示

样条插值

  • 原理:通过样条函数(如立方样条函数)进行取值,以平滑曲线形式连接已知像素点,从而计算新的像素值。
  • 优点:生成的图像非常平滑,适合处理曲线轮廓的图像。
  • 缺点:计算复杂,可能导致图像过度平滑,损失细节。

🍃手写实现

def cubic_spline_coefficients(x, y):''':param x: x 坐标点数组(升序排列):param y: y 坐标点数组:return: (a, b, c, d): 分段三次多项式系数'''n = len(x) - 1  # 段数h = np.diff(x)  # 每段的长度# 构造三对角矩阵 AA = np.zeros((n + 1, n + 1))A[0, 0] = 1A[n, n] = 1for i in range(1, n):A[i, i - 1] = h[i - 1]A[i, i] = 2 * (h[i - 1] + h[i])A[i, i + 1] = h[i]# 构造右侧的向量 BB = np.zeros(n + 1)for i in range(1, n):B[i] = 3 * ((y[i + 1] - y[i]) / h[i] - (y[i] - y[i - 1]) / h[i - 1])# 求解二阶导数向量 MM = np.linalg.solve(A, B)# 计算三次样条的系数 a, b, c, da = y[:-1]b = (y[1:] - y[:-1]) / h - h * (2 * M[:-1] + M[1:]) / 3c = M[:-1]d = (M[1:] - M[:-1]) / (3 * h)return a, b, c, ddef cubic_spline_interpolation_1d(x, y, x_new):''':param x:   原始数据的 x 坐标:param y:   原始数据的 y 坐标:param x_new:   要插值的 x 坐标点:return:    插值后对应的 y 值'''a, b, c, d = cubic_spline_coefficients(x, y)n = len(x) - 1y_new = np.zeros_like(x_new)for i, x_i in enumerate(x_new):for j in range(n):if x[j] <= x_i <= x[j + 1]:dx = x_i - x[j]y_new[i] = a[j] + b[j] * dx + c[j] * dx ** 2 + d[j] * dx ** 3breakreturn y_newdef cubic_spline_interpolation_image(image, scale_factor):''':param image: 输入图像:param scale_factor:    缩放因子:return:'''image = np.array(image)image = image.astype(np.float32)height, width = image.shape[:2]# 创建原始图像的网格x = np.arange(width)y = np.arange(height)# 创建新图像的网格new_x = np.linspace(0, width - 1, int(width * scale_factor))new_y = np.linspace(0, height - 1, int(height * scale_factor))# 初始化新图像if len(image.shape) == 3:  # 彩色图像channels = image.shape[2]new_image = np.zeros((len(new_y), len(new_x), channels), dtype=image.dtype)for c in range(channels):# 对每个通道进行行和列的插值temp = np.zeros((height, len(new_x)), dtype=image.dtype)for i in range(height):temp[i, :] = cubic_spline_interpolation_1d(x, image[i, :, c], new_x)for j in range(len(new_x)):new_image[:, j, c] = cubic_spline_interpolation_1d(y, temp[:, j], new_y)else:  # 灰度图像temp = np.zeros((height, len(new_x)), dtype=image.dtype)new_image = np.zeros((len(new_y), len(new_x)), dtype=image.dtype)for i in range(height):temp[i, :] = cubic_spline_interpolation_1d(x, image[i, :], new_x)for j in range(len(new_x)):new_image[:, j] = cubic_spline_interpolation_1d(y, temp[:, j], new_y)new_image = Image.fromarray(np.clip(new_image, 0, 255).astype(np.uint8))return new_image

🍂内置函数

from scipy.interpolate import RectBivariateSplinedef spline_interpolation(image, scale_factor):''':param image: 输入图像:param scale_factor:    缩放因子:return:'''image = np.array(image)image = image.astype(np.float32)height, width = image.shape[:2]# 创建原始图像的网格x = np.arange(width)y = np.arange(height)# 创建新图像的网格new_x = np.linspace(0, width - 1, int(width * scale_factor))new_y = np.linspace(0, height - 1, int(height * scale_factor))# 初始化新图像if len(image.shape) == 3:  # 彩色图像channels = image.shape[2]new_image = np.zeros((len(new_y), len(new_x), channels), dtype=image.dtype)for c in range(channels):# 对每个通道使用 RectBivariateSpline 进行插值spline = RectBivariateSpline(y, x, image[:, :, c])new_image[:, :, c] = spline(new_y, new_x)else:  # 灰度图像spline = RectBivariateSpline(y, x, image)new_image = spline(new_y, new_x)new_image = Image.fromarray(np.clip(new_image, 0, 255).astype(np.uint8))return new_image

💥完整代码

import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import RectBivariateSpline
from PIL import Image# -----------------------------------------------------#
#                   样条插值
# ------------------------------------------------------#
# --------------------手写实现-------------------------#
def cubic_spline_coefficients(x, y):''':param x: x 坐标点数组(升序排列):param y: y 坐标点数组:return: (a, b, c, d): 分段三次多项式系数'''n = len(x) - 1  # 段数h = np.diff(x)  # 每段的长度# 构造三对角矩阵 AA = np.zeros((n + 1, n + 1))A[0, 0] = 1A[n, n] = 1for i in range(1, n):A[i, i - 1] = h[i - 1]A[i, i] = 2 * (h[i - 1] + h[i])A[i, i + 1] = h[i]# 构造右侧的向量 BB = np.zeros(n + 1)for i in range(1, n):B[i] = 3 * ((y[i + 1] - y[i]) / h[i] - (y[i] - y[i - 1]) / h[i - 1])# 求解二阶导数向量 MM = np.linalg.solve(A, B)# 计算三次样条的系数 a, b, c, da = y[:-1]b = (y[1:] - y[:-1]) / h - h * (2 * M[:-1] + M[1:]) / 3c = M[:-1]d = (M[1:] - M[:-1]) / (3 * h)return a, b, c, ddef cubic_spline_interpolation_1d(x, y, x_new):''':param x:   原始数据的 x 坐标:param y:   原始数据的 y 坐标:param x_new:   要插值的 x 坐标点:return:    插值后对应的 y 值'''a, b, c, d = cubic_spline_coefficients(x, y)n = len(x) - 1y_new = np.zeros_like(x_new)for i, x_i in enumerate(x_new):for j in range(n):if x[j] <= x_i <= x[j + 1]:dx = x_i - x[j]y_new[i] = a[j] + b[j] * dx + c[j] * dx ** 2 + d[j] * dx ** 3breakreturn y_newdef cubic_spline_interpolation_image(image, scale_factor):''':param image: 输入图像:param scale_factor:    缩放因子:return:'''image = np.array(image)image = image.astype(np.float32)height, width = image.shape[:2]# 创建原始图像的网格x = np.arange(width)y = np.arange(height)# 创建新图像的网格new_x = np.linspace(0, width - 1, int(width * scale_factor))new_y = np.linspace(0, height - 1, int(height * scale_factor))# 初始化新图像if len(image.shape) == 3:  # 彩色图像channels = image.shape[2]new_image = np.zeros((len(new_y), len(new_x), channels), dtype=image.dtype)for c in range(channels):# 对每个通道进行行和列的插值temp = np.zeros((height, len(new_x)), dtype=image.dtype)for i in range(height):temp[i, :] = cubic_spline_interpolation_1d(x, image[i, :, c], new_x)for j in range(len(new_x)):new_image[:, j, c] = cubic_spline_interpolation_1d(y, temp[:, j], new_y)else:  # 灰度图像temp = np.zeros((height, len(new_x)), dtype=image.dtype)new_image = np.zeros((len(new_y), len(new_x)), dtype=image.dtype)for i in range(height):temp[i, :] = cubic_spline_interpolation_1d(x, image[i, :], new_x)for j in range(len(new_x)):new_image[:, j] = cubic_spline_interpolation_1d(y, temp[:, j], new_y)new_image = Image.fromarray(np.clip(new_image, 0, 255).astype(np.uint8))return new_image# --------------------------内置函数--------------------------------------#def spline_interpolation(image, scale_factor):''':param image: 输入图像:param scale_factor:    缩放因子:return:'''image = np.array(image)image = image.astype(np.float32)height, width = image.shape[:2]# 创建原始图像的网格x = np.arange(width)y = np.arange(height)# 创建新图像的网格new_x = np.linspace(0, width - 1, int(width * scale_factor))new_y = np.linspace(0, height - 1, int(height * scale_factor))# 初始化新图像if len(image.shape) == 3:  # 彩色图像channels = image.shape[2]new_image = np.zeros((len(new_y), len(new_x), channels), dtype=image.dtype)for c in range(channels):# 对每个通道使用 RectBivariateSpline 进行插值spline = RectBivariateSpline(y, x, image[:, :, c])new_image[:, :, c] = spline(new_y, new_x)else:  # 灰度图像spline = RectBivariateSpline(y, x, image)new_image = spline(new_y, new_x)new_image = Image.fromarray(np.clip(new_image, 0, 255).astype(np.uint8))return new_imageif __name__ == '__main__':path = r'test/test.jpg'scale_factor = 3image = Image.open(path).convert('RGB')height, width = image.sizeimage_spline = cubic_spline_interpolation_image(image, scale_factor)image1 = spline_interpolation(image, scale_factor)plt.subplot(1, 3, 1)plt.imshow(image)plt.title("Original")plt.subplot(1, 3, 2)plt.imshow(image_spline)plt.title("Spline")plt.subplot(1, 3, 3)plt.imshow(image1)plt.title("Scipy")plt.show()

🍓效果展示

高斯插值

  • 原理:基于高斯分布的加权平均方法,通过赋予相邻像素不同的权重来计算新的像素值。
  • 优点:平滑并保留一定的细节,减少噪声影响。
  • 缺点:计算复杂度首要。

🍃手写实现

def gaussian_weight(distance, sigma):"""计算距离对应的高斯权重"""return math.exp(-(distance ** 2) / (2 * sigma ** 2))def gaussian_kernel(size, sigma):"""生成高斯核"""kernel = np.zeros((size, size))center = size // 2for i in range(size):for j in range(size):distance = np.sqrt((i - center) ** 2 + (j - center) ** 2)kernel[i, j] = gaussian_weight(distance, sigma)return kernel / np.sum(kernel)  # 归一化核def gaussian_interpolation(image, scale_factor, kernel_size=3, sigma=0.5):"""使用高斯插值对图像进行缩放"""image = np.array(image)image = image.astype(np.float32)height, width = image.shape[:2]kernel = gaussian_kernel(kernel_size, sigma)new_height, new_width = int(height * scale_factor), int(width * scale_factor)if len(image.shape) == 3:  # 彩色图像new_image = np.zeros((new_height, new_width, image.shape[2]), dtype=image.dtype)else:  # 灰度图像new_image = np.zeros((new_height, new_width), dtype=image.dtype)# 缩放比例row_scale = height / new_heightcol_scale = width / new_width# 对新图像的每个像素进行插值for i in range(new_height):for j in range(new_width):# 在原图像中找到对应的坐标src_row = i * row_scalesrc_col = j * col_scalerow_floor = int(np.floor(src_row))col_floor = int(np.floor(src_col))# 初始化加权和weighted_sum = np.zeros(image.shape[2]) if len(image.shape) == 3 else 0.0weight_total = 0.0# 遍历高斯核for m in range(kernel_size):for n in range(kernel_size):row_offset = row_floor + m - kernel_size // 2col_offset = col_floor + n - kernel_size // 2# 如果超出图像边界,跳过该点if row_offset < 0 or row_offset >= height or col_offset < 0 or col_offset >= width:continue# 加权平均weight = kernel[m, n]if len(image.shape) == 3:weighted_sum += weight * image[row_offset, col_offset, :]else:weighted_sum += weight * image[row_offset, col_offset]weight_total += weight# 归一化结果if len(image.shape) == 3:new_image[i, j, :] = weighted_sum / weight_totalelse:new_image[i, j] = weighted_sum / weight_totalnew_image = Image.fromarray(np.clip(new_image, 0, 255).astype(np.uint8))return new_image

🍂内置函数

from scipy.ndimage import gaussian_filter
image1 = gaussian_filter(image, sigma=0.5)

💥完整代码

import numpy as np
import math
import matplotlib.pyplot as plt
from scipy.ndimage import gaussian_filter
from PIL import Image# -----------------------------------------------------#
#                   高斯插值
# ------------------------------------------------------#
def gaussian_weight(distance, sigma):"""计算距离对应的高斯权重"""return math.exp(-(distance ** 2) / (2 * sigma ** 2))def gaussian_kernel(size, sigma):"""生成高斯核"""kernel = np.zeros((size, size))center = size // 2for i in range(size):for j in range(size):distance = np.sqrt((i - center) ** 2 + (j - center) ** 2)kernel[i, j] = gaussian_weight(distance, sigma)return kernel / np.sum(kernel)  # 归一化核def gaussian_interpolation(image, scale_factor, kernel_size=3, sigma=0.5):"""使用高斯插值对图像进行缩放"""image = np.array(image)image = image.astype(np.float32)height, width = image.shape[:2]kernel = gaussian_kernel(kernel_size, sigma)new_height, new_width = int(height * scale_factor), int(width * scale_factor)if len(image.shape) == 3:  # 彩色图像new_image = np.zeros((new_height, new_width, image.shape[2]), dtype=image.dtype)else:  # 灰度图像new_image = np.zeros((new_height, new_width), dtype=image.dtype)# 缩放比例row_scale = height / new_heightcol_scale = width / new_width# 对新图像的每个像素进行插值for i in range(new_height):for j in range(new_width):# 在原图像中找到对应的坐标src_row = i * row_scalesrc_col = j * col_scalerow_floor = int(np.floor(src_row))col_floor = int(np.floor(src_col))# 初始化加权和weighted_sum = np.zeros(image.shape[2]) if len(image.shape) == 3 else 0.0weight_total = 0.0# 遍历高斯核for m in range(kernel_size):for n in range(kernel_size):row_offset = row_floor + m - kernel_size // 2col_offset = col_floor + n - kernel_size // 2# 如果超出图像边界,跳过该点if row_offset < 0 or row_offset >= height or col_offset < 0 or col_offset >= width:continue# 加权平均weight = kernel[m, n]if len(image.shape) == 3:weighted_sum += weight * image[row_offset, col_offset, :]else:weighted_sum += weight * image[row_offset, col_offset]weight_total += weight# 归一化结果if len(image.shape) == 3:new_image[i, j, :] = weighted_sum / weight_totalelse:new_image[i, j] = weighted_sum / weight_totalnew_image = Image.fromarray(np.clip(new_image, 0, 255).astype(np.uint8))return new_imageif __name__ == '__main__':path = r'test/test.jpg'scale_factor = 3image = Image.open(path).convert('RGB')height, width = image.sizeimage_gaussian = gaussian_interpolation(image, scale_factor, kernel_size=3, sigma=0.5)image1 = gaussian_filter(image, sigma=0.5)plt.subplot(1, 3, 1)plt.imshow(image)plt.title("Original")plt.subplot(1, 3, 2)plt.imshow(image_gaussian)plt.title("Gaussian")plt.subplot(1, 3, 3)plt.imshow(image1)plt.title("Scipy")plt.show()

🍓效果展示

深度学习的SR技术

        总体而言,基于插值的方法相对简单,在超分辨率重建中的性能往往不如基于学习的方法。因此下面我们就简单介绍3种在OpenCV基于深度学习的超分辨率模型实践方法吧。

1️⃣安装opencv和contrib模块

contrib模块是SR接口代码所在的位置,我们需要使用的接口或模块称为dnn_superresdnn代表深度神经网络superres代表超分辨率

pip install opencv-python
pip install opencv-contrib-python

2️⃣模型下载

当前在OpenCV主要支持4中不同的SR模型,它们都可以按2、3和4的比例放大图像,但是在准确性、大小和速度上各部相同。

EDSR:目前表现最好的模型。然而,它也是参数量最大的模型,因此具有最大的文件大小和最慢的推理。

下载链接:https://github.com/Saafke/EDSR_Tensorflow/tree/master/models

ESPCN:相对较小的模型,具有快速和良好的推理能力,可以进行实时视频放大(取决于图像大小)。

下载链接:https://github.com/fannymonori/TF-ESPCN/tree/master/export

FSRCNN:快速准确推理的小模型,还可以进行实时视频放大。

下载链接:https://github.com/Saafke/FSRCNN_Tensorflow/tree/master/models

LapSRN:中等大小的模型,可以提升8倍分辨率。

下载链接:https://github.com/fannymonori/TF-LapSRN/tree/master/export

3️⃣实践

import cv2
from cv2 import dnn_superressr = dnn_superres.DnnSuperResImpl_create()
# -------------------输入图像路径--------------------------#
path = r'test/test.jpg'
# ----------------------模型路径---------------------------#
model = r'models/EDSR/EDSR_x3.pb'image = cv2.imread(path)sr.readModel(model)
sr.setModel('edsr', 3)
result = sr.upsample(image)cv2.imshow('image', result)
cv2.waitKey()
cv2.destroyAllWindows()

4️⃣注意事项

  • 在使用.jpg图像时出现错误,请尝试切换到.png格式。

  • 确保setModel()中的参数与readModel()中使用的模型匹配。

  • 想使用GPU进行推理(默认是CPU),可以在读入模型后将后端设置为CUDA

  • path = "EDSR_x3.pb"
    sr.readModel(path)# Set CUDA backend and target to enable GPU inference
    sr.setPreferableBackend(cv2.dnn.DNN_BACKEND_CUDA)
    sr.setPreferableTarget(cv2.dnn.DNN_TARGET_CUDA)

5️⃣效果展示

我只尝试了3种SR模型,没有使用LapSRN模型,但就目前来看,EDSR在细节方面确实是表现得最好的一个,但缺点就是推理时间太长了,长达3秒钟。

画质增强专业软件

🍁除了使用代码进行画质增强,我们还可以使用相关的专业软件进行处理噢🍁

Upscayl的GitHub地址:https://github.com/upscayl/upscayl

        Upscayl项目是一个免费开源的 AI 图像升频器,支持 Linux、macOS 和 Windows 系统。它使用先进的人工智能算法来放大和增强低分辨率图像,且不损失质量。在该项目页面中,可以找到相关的代码、文档、发布版本等信息。如果大家对图像增强、人工智能图像处理等方面感兴趣,可以关注这个项目。

🍓效果展示

效果可以说是非常好了,完全碾压目前我所了解的所有图像增强算法,当然了,人家毕竟是专业的嘛。值得一说的是,这个软件不仅支持单张图像,还支持文件夹处理噢。

图像增强效果对比

        说了这么多的画质增强算法,我觉得需要有一张不同方法的效果对比图才足够让大家清楚各种算法的优缺点嘛,OK,大家继续往下看吧!

🍇推理时间对比

        可以看到,只考虑插值算法实现图像画质增强的方法中,双线性插值、双三次插值和Lanczos插值效果都还是不错的。当然深度学习的效果也是明显是优于传统插值算法的,而这当中EDSR模型性能最佳,但推理时间巨慢,不建议处理大批量数据。 

        当然,如果只考虑处理后的图像质量,那么使用Upscayl软件进行画质增强无疑是上上之选,不仅细节恢复的非常好,而且图像的噪声也处理得非常到位,对于科研工作者来说无疑是最佳的选择!🍉

🍹总结

        总的来说,超分辨率的主要作用在于恢复图像的细节部分,这对于因各种因素而导致画质模糊或像素低的图像具有重大意义。虽然超分辨率技术众多,但并非都适用于同一种数据,大家按需选择即可。当然,我的首选肯定是使用专业的软件进行图像恢复啦,毕竟人家是专业的呀!🐶

参考链接:超分辨率基准测试

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1535969.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

单机软件在Linux上的安装

mysql安装 5.7版本 mysql的程序在centos官方的库中是没有的&#xff0c;需要切换到淘宝的镜像&#xff0c;这个前面有教程或者配置mysql的源 yum -y install rpm rpm --import https://repo.mysql.Com/RPM-GPG-KEY-mysqL-2022 rpm -Uvh http://repo.mysql.com//mysql57-commun…

Linux基础---08软件的安装

安装方式优缺点编译安装自由定制&#xff0c;但较为繁琐rmp安装安装简单&#xff0c;但需要自己解决依赖&#xff0c;不支持定制yum安装自动解决rmp依赖&#xff0c;但不支持定制&#xff08;用的更多&#xff09; 下面就具体介绍三大安装方式&#xff1a; 一.编译安装 用Li…

IBM撤出中国区相关研发工作 裁员规模超千人

经济观察网 记者 钱玉娟 8月26日上午10点半&#xff0c;IBM中国举行了一场只有3分钟的全员会。IBM全球企业系统开发部副总裁Jack Hergenrother在会上宣布&#xff0c;IBM基础设施决定撤出IBM中国系统中心&#xff08;CSL&#xff09;与IBM中国开发中心&#xff08;CDL&#xff…

热门数据恢复软件大盘点

现在大家的数据都喜欢存放在一些电子设备里保存吧。这样既方便存放&#xff0c;也方便我们查找。但是这些设备可能因为病毒、误删除等原因造成数据的丢失。这篇文章我将介绍几款类似易我数据恢复软件的数据恢复工具&#xff0c;减少为数据丢失给我们造成损失。 1.FOXIT数据恢复…

3. Python计算水仙花数

Python计算水仙花数 一、什么是水仙花数&#xff1f; 百度答案 二、怎样使用Python计算水仙花数&#xff1f; 这里需要for循环&#xff0c;if判断&#xff0c;需要range()函数&#xff0c;需要知道怎么求个位数&#xff0c;十位数&#xff0c;百位数… 1. For循环 语句结…

通信工程学习:什么是SNI业务节点接口

SNI&#xff1a;业务节点接口 SNI业务节点接口&#xff0c;全称Service Node Interface&#xff0c;是接入网&#xff08;AN&#xff09;和一个业务节点&#xff08;SN&#xff09;之间的接口&#xff0c;位于接入网的业务侧。这一接口在通信网络中扮演着重要的角色&#xff0c…

智慧农业数据集(一)

目录 葡萄叶片病虫害害数据集 茄子果实病虫害数据集 81类水果数据集 小麦叶片病虫害数据集 番茄叶片病害数据集 草莓叶片病虫害数据集 水稻叶片病虫害数据集 菠萝成熟度数据集 10类水果数据集 棉花叶片病虫害数据集 棉花成熟度数据集 柑橘叶片病虫害数据集 苹果新…

离谱碾压!奇安信中标:高出第二名近70分!

2024年08月09日&#xff0c;广东省政务服务和数据管理局&#xff0c;近日发布了网络安全第三方服务&#xff08;2024年&#xff09;项目之关基检查及重要政务应用安全检查服务招标公告&#xff01; 预算金额&#xff1a;2,896,200.00元&#xff0c;其中安全检查服务包&#xf…

网络原理2-网络层与数据链路层

目录 网络层数据链路层 网络层 网络层做的工作&#xff1a; 1、地址管理–>IP地址 2、路由选择–>数据包传输的路径规划 网络层主要的协议就是IP协议 IP协议的报头结构&#xff1a; 4位版本&#xff1a; 有两个取值&#xff0c;4表示IPv4&#xff0c;6表示IPv6&am…

【DVWA】——File Upload(文件上传)

&#x1f4d6; 前言&#xff1a;文件上传漏洞是由于对上传文件未作过滤或过滤机制不严&#xff08;文件后缀或类型&#xff09;&#xff0c;导致恶意用户可以上传脚本文件&#xff0c;通过上传文件可达到控制网站权限的目的。 目录 &#x1f552; 1. Low&#x1f552; 2. Mediu…

嵌入式单片机程序运行基本机理

1. 程序各种要素说明 大家好,今天用一个最简单的程序跟大家讲清楚程序的构成。 1.1. 概述 硬件首先要知道硬件的组成。 在前面章节我们说过,芯片包含Flash和RAM。 他们虽然不是相同的东西,但是都属于同一个地址空间,32位芯片的地址空间大小是4G。 比如ST32,FLASH通常从…

[云服务器10]使用alist搭建云盘系统

hello大家好啊&#xff0c;今天为大家带来的是使用alist搭建一个网盘系统&#xff01; 首先我们得明确&#xff0c;人家阿里云盘&#xff0c;百度云盘都是存了PB级的数据&#xff0c;然后我大概算了一下&#xff0c;成本约为 2 554 880 2\space554\space880 2 554 880RMB每个月…

3.1 通信协议

通信协议 通信的目的&#xff1a;将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统 通信协议&#xff1a;制定通信的规则&#xff0c;通信双方按照协议规则进行数据收发 全双工&#xff1a;发送与接收互不影响 (如串口通信有两根数据线 tx,rx) 半双工 &#xff1…

155K Star,Python 入门到进阶最佳学习资源

Hi&#xff0c;骚年&#xff0c;我是大 G&#xff0c;公众号「GitHub 指北」会推荐 GitHub 上有趣有用的项目&#xff0c;一分钟 get 一个优秀的开源项目&#xff0c;挖掘开源的价值&#xff0c;欢迎关注。 导语 如果你正在寻找一个全面、系统、深入的 Python 学习项目&#…

CSP-CCF★★★201903-2二十四点★★★

目录 一、问题描述 二、解答 方法一&#xff1a;穷举法&#xff08;只列举了一部分&#xff09; 方法二&#xff1a;中缀表达式直接求值&#xff0c;两个栈&#xff0c;一个存放数值&#xff0c;一个存放符号 方法三&#xff1a;将中缀表达式转换为后缀来计算注意&#xff…

【Git】初识Git

本篇文章的环境是在 Ubuntu/Linux 环境下编写的 文章目录 版本控制器Git 基本操作安装 Git创建 Git 本地仓库配置 Git认识工作区、暂存区、版本库添加文件修改文件版本回退撤销修改删除文件 版本控制器 在日常工作和学习中&#xff0c;老板/老师要求我们修改文档&#xff0c;…

C++11新特性学习

C11 1. C11新特性 自动类型推导&#xff08;auto&#xff09;智能指针&#xff08;提供更安全和更高效的内存管理&#xff09;移动语义和右值引用 (move语义 &&&#xff0c;使得对象移动而非拷贝&#xff0c;在处理大量数据时提高程序性能)Lambda 表达式&#xff08;…

胤娲科技:解锁AI奥秘——产品经理的智能进化之旅

当AI不再是遥不可及的科幻 想象一下&#xff0c;你走进一家未来感十足的咖啡厅&#xff0c;无需言语&#xff0c;智能咖啡机就能根据你的偏好调制出一杯完美的拿铁&#xff1b; 打开手机&#xff0c;AI助手不仅提醒你今天有雨&#xff0c;还贴心推荐了最适合雨中漫步的音乐列表…

如何正确使用MMPI量表进行测试?

1、需要初中以上学历&#xff0c;能对测试题准确的理解。 2、应在安静、无干扰的环境中进行&#xff0c;确保自己能够集中注意力完成测试。 3、尽量不要选择“无法回答”这个选项&#xff0c;当然如果确实有无法回答的&#xff0c;也可以选&#xff0c;但是总数不要超过22个。…

java中,怎么使用反射?

在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是指在运行时能够获取类、方法、字段、构造函数等信息&#xff0c;并且能够在运行时动态调用类的方法、创建对象或访问字段。Java中的反射机制主要通过java.lang.reflect包中的类来实现。反射可以为开发者提供强大的…