超分辨率技术之插值算法

news2024/9/25 3:24:02

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

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

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

前言

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

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

目录

最近邻插值

双线性插值

双三次插值

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_img

if __name__ == '__main__':
    path = r'test/test.jpg'
    scale_factor = 3
    image = Image.open(path).convert('RGB')
    height, width = image.size
    image0 = 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_height
    col_scale = width / new_width

    for i in range(new_height):
        for j in range(new_width):
            # 找到原图像中的对应坐标
            src_row = i * row_scale
            src_col = j * col_scale
            # 计算上下和左右的整数像素位置
            row_top = min(int(np.floor(src_row)), height - 2)
            row_bottom = row_top + 1
            col_left = min(int(np.floor(src_col)), width - 2)
            col_right = col_left + 1
            # 计算插值的权重
            row_weight = src_row - row_top
            col_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_weight
                    bottom = bottom_left + (bottom_right - bottom_left) * col_weight
                    new_image[i, j, c] = top + (bottom - top) * row_weight
            else:  # 灰度图像
                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_weight
                bottom = bottom_left + (bottom_right - bottom_left) * col_weight
                new_image[i, j] = top + (bottom - top) * row_weight
                
    new_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_height
    col_scale = width / new_width
    for i in range(new_height):
        for j in range(new_width):
            # 找到原图像中的对应坐标
            src_row = i * row_scale
            src_col = j * col_scale
            # 计算上下和左右的整数像素位置
            row_top = min(int(np.floor(src_row)), height - 2)
            row_bottom = row_top + 1
            col_left = min(int(np.floor(src_col)), width - 2)
            col_right = col_left + 1
            # 计算插值的权重
            row_weight = src_row - row_top
            col_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_weight
                    bottom = bottom_left + (bottom_right - bottom_left) * col_weight
                    new_image[i, j, c] = top + (bottom - top) * row_weight
            else:  # 灰度图像
                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_weight
                bottom = bottom_left + (bottom_right - bottom_left) * col_weight
                new_image[i, j] = top + (bottom - top) * row_weight
                
    new_image = Image.fromarray(np.clip(new_image, 0, 255).astype(np.uint8))
    return new_image

if __name__ == '__main__':
    path = r'test/test.jpg'
    scale_factor = 3
    image = Image.open(path).convert('RGB')
    height, width = image.size
    
    image_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.5
    t = abs(t)
    if t <= 1:
        return (a + 2) * (t ** 3) - (a + 3) * (t ** 2) + 1
    elif t <= 2:
        return a * (t ** 3) - 5 * a * (t ** 2) + 8 * a * t - 4 * a
    return 0

def 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_factor
            orig_y = j / scale_factor
            # 取整得到插值中心的整数部分和小数部分
            x_int = int(orig_x)
            y_int = int(orig_y)
            dx = orig_x - x_int
            dy = 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] * weight

    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.BICUBIC)

💥完整代码

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt

# -----------------------------------------------------#
#                       双三次插值
# ------------------------------------------------------#
def cubic_weight(t):
    """计算三次插值权重的函数"""
    a = -0.5
    t = abs(t)
    if t <= 1:
        return (a + 2) * (t ** 3) - (a + 3) * (t ** 2) + 1
    elif t <= 2:
        return a * (t ** 3) - 5 * a * (t ** 2) + 8 * a * t - 4 * a
    return 0

def 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_factor
            orig_y = j / scale_factor
            # 取整得到插值中心的整数部分和小数部分
            x_int = int(orig_x)
            y_int = int(orig_y)
            dx = orig_x - x_int
            dy = 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] * weight

    new_img = Image.fromarray(np.clip(new_img_np, 0, 255).astype(np.uint8))
    return new_img

if __name__ == '__main__':
    path = r'test/test.jpg'
    scale_factor = 3
    image = Image.open(path).convert('RGB')
    height, width = image.size
    image0 = 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 1
    elif -a < x < a:
        return np.sinc(x) * np.sinc(x / a)
    else:
        return 0

def 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_factor
            orig_y = j / scale_factor
            # 取整得到整数部分和小数部分
            x_int = int(orig_x)
            y_int = int(orig_y)
            dx = orig_x - x_int
            dy = orig_y - y_int
            # Lanczos 插值核心部分,考虑 a×a 个邻近像素
            total_weight = 0
            for 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_y
                    new_img_np[i, j] += img_np[xm, yn] * weight
                    total_weight += weight
            # 防止权重过小,做归一化
            if total_weight > 0:
                new_img_np[i, j] = new_img_np[i, j] / total_weight

    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.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 1
    elif -a < x < a:
        return np.sinc(x) * np.sinc(x / a)
    else:
        return 0

def 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_factor
            orig_y = j / scale_factor
            # 取整得到整数部分和小数部分
            x_int = int(orig_x)
            y_int = int(orig_y)
            dx = orig_x - x_int
            dy = orig_y - y_int
            # Lanczos 插值核心部分,考虑 a×a 个邻近像素
            total_weight = 0
            for 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_y

                    new_img_np[i, j] += img_np[xm, yn] * weight
                    total_weight += weight
            # 防止权重过小,做归一化
            if total_weight > 0:
                new_img_np[i, j] = new_img_np[i, j] / total_weight

    new_img = Image.fromarray(np.clip(new_img_np, 0, 255).astype(np.uint8))
    return new_img


if __name__ == '__main__':
    path = r'test/test.jpg'
    scale_factor = 3
    image = Image.open(path).convert('RGB')
    height, width = image.size
    image0 = 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)  # 每段的长度
    # 构造三对角矩阵 A
    A = np.zeros((n + 1, n + 1))
    A[0, 0] = 1
    A[n, n] = 1
    for 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]
    # 构造右侧的向量 B
    B = 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])
    # 求解二阶导数向量 M
    M = np.linalg.solve(A, B)
    # 计算三次样条的系数 a, b, c, d
    a = y[:-1]
    b = (y[1:] - y[:-1]) / h - h * (2 * M[:-1] + M[1:]) / 3
    c = M[:-1]
    d = (M[1:] - M[:-1]) / (3 * h)

    return a, b, c, d

def 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) - 1
    y_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 ** 3
                break

    return y_new

def 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 RectBivariateSpline

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_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)  # 每段的长度
    # 构造三对角矩阵 A
    A = np.zeros((n + 1, n + 1))
    A[0, 0] = 1
    A[n, n] = 1
    for 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]
    # 构造右侧的向量 B
    B = 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])
    # 求解二阶导数向量 M
    M = np.linalg.solve(A, B)
    # 计算三次样条的系数 a, b, c, d
    a = y[:-1]
    b = (y[1:] - y[:-1]) / h - h * (2 * M[:-1] + M[1:]) / 3
    c = M[:-1]
    d = (M[1:] - M[:-1]) / (3 * h)
    return a, b, c, d

def 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) - 1
    y_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 ** 3
                break

    return y_new

def 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_image


if __name__ == '__main__':
    path = r'test/test.jpg'
    scale_factor = 3
    image = Image.open(path).convert('RGB')
    height, width = image.size
    image_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 // 2
    for 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_height
    col_scale = width / new_width
    # 对新图像的每个像素进行插值
    for i in range(new_height):
        for j in range(new_width):
            # 在原图像中找到对应的坐标
            src_row = i * row_scale
            src_col = j * col_scale
            row_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.0
            weight_total = 0.0
            # 遍历高斯核
            for m in range(kernel_size):
                for n in range(kernel_size):
                    row_offset = row_floor + m - kernel_size // 2
                    col_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_total
            else:
                new_image[i, j] = weighted_sum / weight_total
    
    new_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 // 2
    for 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_height
    col_scale = width / new_width
    # 对新图像的每个像素进行插值
    for i in range(new_height):
        for j in range(new_width):
            # 在原图像中找到对应的坐标
            src_row = i * row_scale
            src_col = j * col_scale
            row_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.0
            weight_total = 0.0
            # 遍历高斯核
            for m in range(kernel_size):
                for n in range(kernel_size):
                    row_offset = row_floor + m - kernel_size // 2
                    col_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_total
            else:
                new_image[i, j] = weighted_sum / weight_total
    
    new_image = Image.fromarray(np.clip(new_image, 0, 255).astype(np.uint8))
    return new_image

if __name__ == '__main__':
    path = r'test/test.jpg'
    scale_factor = 3
    image = Image.open(path).convert('RGB')
    height, width = image.size
    image_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_superres


sr = 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.coloradmin.cn/o/2138269.html

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

相关文章

天融信把桌面explorer.exe删了,导致开机之后无windows桌面,只能看到鼠标解决方法

win10开机进入桌面&#xff0c;发现桌面无了&#xff0c;但是可以ctrlaltdelete调出任务管理器 用管理员权限打开cmd&#xff0c;输入&#xff1a; sfc /scanfilec:\windowslexplorer.exe 在运行C:\windows\Explorer.exe&#xff1b;可以进入桌面&#xff0c;但是隔离几秒钟…

链式二叉树的基本操作(C语言版)

目录 1.二叉树的定义 2.创建二叉树 3.递归遍历二叉树 1&#xff09;前序遍历 2&#xff09;中序遍历 3&#xff09;后序遍历 4.层序遍历 5.计算节点个数 6.计算叶子节点个数 7.计算第K层节点个数 8.计算树的最大深度 9.查找值为x的节点 10.二叉树的销毁 从二叉树…

物体识别之微特征识别任务综述

“深度人工智能”是成都深度智谷科技旗下的人工智能教育机构订阅号&#xff0c;主要分享人工智能的基础知识、技术发展、学习经验等。此外&#xff0c;订阅号还为大家提供了人工智能的培训学习服务和人工智能证书的报考服务&#xff0c;欢迎大家前来咨询&#xff0c;实现自己的…

一模--解题--71-80

文章目录 9.资源管理71、 [单选] 一个项目连续错过交付日期&#xff0c;项目团队评估完该情况后&#xff0c;项目经理意识到团队绩效差的原因在于团队成员对于自己的职责缺乏清晰认识。项目经理首先应该关注哪一项&#xff1f; 13.干系人管理72、 [单选] 项目团队刚刚完成一个新…

element-plus的面包屑组件el-breadcrumb

面包屑组件主要用来显示当页面路径&#xff0c;以及快速返回之前的页面。 涉及2个组件 el-breadcrumb 和el-breadcrumb-item, el-breadcrumb的spearator指定item的分隔符 el-breadcrumb-item的to和replace属性和vue-router的一致&#xff0c;需要结合vue_router一起使用 用法…

WSL安装Redis

前言 本来一直是在虚拟机的Ubuntu开发 但是 搞着搞着内存不足 导致我某些数据损坏了 然后目前迁移到Wsl开发 运行WSL的相较于虚拟机你不需要很多的性能开销&#xff01; 我只是代码开发和git交互&#xff0c;如果是搞逆向还是虚拟机。 记录一下redis 安装卸载 免得以后又忘了…

【中等】机试-滑动窗口(双指针)-例:无重复字符的最长子串

※高频、重点 字节&#xff08;飞书&#xff09;、百度等大厂测开高频面试题&#xff1a;最长不重复子串 . - 力扣&#xff08;LeetCode&#xff09;字节飞书面经里的高频题&#xff0c;没做出来&#xff0c;需要好好复习。 重点考察-滑动窗口这个概念&#xff0c;自学记录一…

攻击者如何在日常网络资源中隐藏恶意软件

近二十年来&#xff0c;安全 Web 网关 (SWG) 一直在监控网络流量&#xff0c;以检测恶意软件、阻止恶意网站并保护企业免受基于 Web 的威胁。 然而&#xff0c;攻击者已经找到了许多绕过这些防御措施的方法&#xff0c;SquareX的安全研究人员对此进行了记录。 最危险的策略之…

【Linux】调试和Git及进度条实现

这里是阿川的博客&#xff0c;祝您变得更强 ✨ 个人主页&#xff1a;在线OJ的阿川 &#x1f496;文章专栏&#xff1a;Linux入门到进阶 &#x1f30f;代码仓库&#xff1a; 写在开头 现在您看到的是我的结论或想法&#xff0c;但在这背后凝结了大量的思考、经验和讨论 目录 1.…

LinkedHashMap 如何实现排序

目录 一、LinkedHashMap二、排序实现三、代码片段分析 一、LinkedHashMap LinkedHashMap 是 Java 中的一个集合类&#xff0c;它是 HashMap 的一个子类&#xff0c;继承了 HashMap 的所有特性&#xff0c;并且在此基础上增加了一个双向链表来维护元素的插入顺序或者访问顺序。L…

java的内存分配和回收机制

Java 与 C之间有一堵由内存动态分配和垃圾收集技术所围成的高墙&#xff0c;墙外面的人想进去&#xff0c;墙里面的人却想出来。 概述 垃圾收集&#xff08;GC&#xff09;需要完成的三件事情&#xff1a; 哪些内存需要回收&#xff1f;什么时候回收&#xff1f;如何回收&am…

CloudXR 套件扩展 XR 工作流

NVIDIA为开发者提供了一个先进的平台&#xff0c;开发者可以在该平台上使用全新NVIDIA CloudXR 套件来创建可扩展、品牌化的定制扩展现实&#xff08;XR&#xff09;产品。 NVIDIA CloudXR 套件基于全新架构而打造&#xff0c;是扩展XR生态的重要工具。它为开发者、专业人士和…

高级 API 性能:着色器

着色器通过使您能够控制渲染过程的各个方面&#xff0c;在图形编程中发挥着关键作用。它们在 GPU 上运行&#xff0c;负责操作顶点、像素和其他数据。 常规着色器计算着色器像素渲染顶点着色器几何体、域和外壳着色器 常规着色器 这些提示适用于所有类型的着色器。 推荐 避…

[产品管理-10]:NPDP新产品开发 - 8 - 波士顿矩阵(当下与未来)在产品市场战略方面的应用

目录 一、波士顿矩阵 理论基础 产品类型划分 分析步骤 重要性 注意事项 二、波士顿矩阵的应用实例 示例背景 数据收集与准备 绘制波士顿矩阵 产品线分类 制定战略对策 一、波士顿矩阵&#xff1a;现在 VS 未来 波士顿矩阵理论&#xff0c;又称市场增长率-相对市场份…

读构建可扩展分布式系统:方法与实践04应用服务

1. 应用服务 1.1. 任何系统的核心都在于实现应用需求的特定业务逻辑 1.2. 服务是可扩展软件系统的核心 1.2.1. 它们将契约定义为一个API&#xff0c;向客户端声明它们的能力 1.3. 应用服务器高度依赖于编程语言&#xff0c;但通常都会提供多线程编程模型&#xff0c;允许服…

Ubuntu系统使用Docker部署Jupyter Notebook并实现笔记云同步

文章目录 前言1. 选择与拉取镜像2. 创建容器3. 访问Jupyter工作台4. 远程访问Jupyter工作台4.1 内网穿透工具安装4.2 创建远程连接公网地址4.3 使用固定二级子域名地址远程访问 前言 本文主要介绍如何在Ubuntu系统中使用Docker本地部署Jupyter Notebook&#xff0c;并结合cpol…

Netty(零散记录)

Netty&#xff1a; 1、Netty三种IO 2、Netty和Reactor的 1、Netty对Reactor的支持 Netty的线程模型时基于Reactor模型实现的&#xff0c;Netty对Reactor三种模式都有非常好的支持&#xff0c;并做了一定的改善&#xff0c;一般情况下&#xff0c;在服务端会采用主从架构模型…

Leetcode面试经典150题-739.每日温度

应读者私信要求&#xff0c;本题协商题目的具体内容 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0…

计算机毕业设计 二手闲置交易系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

【目标检测数据集】厨房常见的水果蔬菜调味料数据集4910张39类VOC+YOLO格式

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4910 标注数量(xml文件个数)&#xff1a;4910 标注数量(txt文件个数)&#xff1a;4910 标注…