【图像增强】使用 Albumentations Python 库(02)

news2024/9/23 16:02:21

一、说明

        在本博客的第 1 部分中,我介绍了使用 Albumentations Python 库进行图像增广的基础知识。本部分介绍高级详细信息。

二、使用 Albumentations 进行语义分割任务

我们将使用来自 TGS 盐鉴定挑战赛的图像和数据。TGS Salt Identification Challenge | Kaggle

import random
import cv2
from matplotlib import pyplot as plt
import albumentations as A



def visualize(image, mask, original_image=None, original_mask=None):
    fontsize = 18

    if original_image is None and original_mask is None:
        f, ax = plt.subplots(2, 1, figsize=(8, 8))

        ax[0].imshow(image)
        ax[1].imshow(mask)
    else:
        f, ax = plt.subplots(2, 2, figsize=(8, 8))

        ax[0, 0].imshow(original_image)
        ax[0, 0].set_title('Original image', fontsize=fontsize)

        ax[1, 0].imshow(original_mask)
        ax[1, 0].set_title('Original mask', fontsize=fontsize)

        ax[0, 1].imshow(image)
        ax[0, 1].set_title('Transformed image', fontsize=fontsize)

        ax[1, 1].imshow(mask)
        ax[1, 1].set_title('Transformed mask', fontsize=fontsize)



image = cv2.imread('0fea4b5049_image.png')
mask = cv2.imread('0fea4b5049.png', cv2.IMREAD_GRAYSCALE)
print(image.shape, mask.shape)
original_height, original_width = image.shape[:2]
visualize(image, mask)

使用 Albumentations 的语义分割

三、填充

        UNet 类型架构要求输入图像大小能被 2^N 整除,其中 N 是最大池化层数。在原版 UNet N=5 中,我们需要将输入图像填充到最接近的可被 2⁵ = 32 整除的数字,即 128。

        可以使用 PadIfNeeded 变换执行此操作。它会在所有四个侧面填充图像和蒙版。可以指定填充类型 (零、常量、反射)。默认填充是反射填充。

aug = A.PadIfNeeded(min_height=128, min_width=128, p=1)

augmented = aug(image=image, mask=mask)

image_padded = augmented['image']
mask_padded = augmented['mask']

print(image_padded.shape, mask_padded.shape)

visualize(image_padded, mask_padded, original_image=image, original_mask=mask)

四、CenterCrop 和 Crop

        要从填充版本获取原始图像和蒙版,我们可以使用 CenterCrop 或 Crop 转换

aug = A.CenterCrop(p=1, height=original_height, width=original_width)

augmented = aug(image=image_padded, mask=mask_padded)

image_center_cropped = augmented['image']
mask_center_cropped = augmented['mask']

print(image_center_cropped.shape, mask_center_cropped.shape)

assert (image - image_center_cropped).sum() == 0
assert (mask - mask_center_cropped).sum() == 0

visualize(image_padded, mask_padded, original_image=image_center_cropped,
          original_mask=mask_center_cropped)

五、非破坏性变换。

        对于没有明确 top 概念的影像(如本图),卫星和航空影像或医学影像通常最好添加不会添加或丢失信息的变换。

        有八种不同的方法可以表示平面上的同一方格。

        转换 HorizontalFlip、VerticalFlip、Transpose、RandomRotate90 的组合将能够将原始图像转换为所有八种状态。

#HorizontalFlip
aug = A.HorizontalFlip(p=1)
augmented = aug(image=image, mask=mask)
image_h_flipped = augmented['image']
mask_h_flipped = augmented['mask']
visualize(image_h_flipped, mask_h_flipped, original_image=image, original_mask=mask)

        水平翻转

#VerticalFlip
aug = A.VerticalFlip(p=1)
augmented = aug(image=image, mask=mask)
image_v_flipped = augmented['image']
mask_v_flipped = augmented['mask']
visualize(image_v_flipped, mask_v_flipped, original_image=image, original_mask=mask)

        垂直翻转

#RandomRotate90 (Randomly rotates by 0, 90, 180, 270 degrees)

aug = A.RandomRotate90(p=1)
augmented = aug(image=image, mask=mask)
image_rot90 = augmented['image']
mask_rot90 = augmented['mask']
visualize(image_rot90, mask_rot90, original_image=image, original_mask=mask)

        随机 90 度旋转

#Transpose (switch X and Y axis)

aug = A.Transpose(p=1)
augmented = aug(image=image, mask=mask)
image_transposed = augmented['image']
mask_transposed = augmented['mask']
visualize(image_transposed, mask_transposed, original_image=image, original_mask=mask)

移调

六、非刚性变换:ElasticTransform、GridDistortion、OpticalDistortion

        在医学成像问题中,非刚性转换有助于增强数据。目前尚不清楚他们是否会帮助解决这个问题,但让我们看看它们。我们将考虑 ElasticTransform、GridDistortion、OpticalDistortion。

        我们修复随机种子以达到可视化目的,因此增强将始终产生相同的结果。在实际的计算机视觉管道中,在对图像应用转换之前,不应修复随机种子,因为在这种情况下,管道将始终输出相同的图像。图像增广的目的是每次使用不同的转换

6.1 弹性变换ElasticTransform

#ElasticTransform 

aug = A.ElasticTransform(p=1, alpha=120, sigma=120 * 0.05, alpha_affine=None)
random.seed(7)
augmented = aug(image=image, mask=mask)
image_elastic = augmented['image']
mask_elastic = augmented['mask']
visualize(image_elastic, mask_elastic, original_image=image, original_mask=mask)

        Elastic Transform

6.2 GridDistortion 刚体变换

#GridDistortion
aug = A.GridDistortion(p=1)
random.seed(7)
augmented = aug(image=image, mask=mask)
image_grid = augmented['image']
mask_grid = augmented['mask']
visualize(image_grid, mask_grid, original_image=image, original_mask=mask)

        Grid Distortion

6.3 光学畸变OpticalDistortion

#OpticalDistortion

aug = A.OpticalDistortion(distort_limit=2, shift_limit=0.5, p=1)
random.seed(7)
augmented = aug(image=image, mask=mask)
image_optical = augmented['image']
mask_optical = augmented['mask']
visualize(image_optical, mask_optical, original_image=image, original_mask=mask)

Optical Distortion

6.4 随机裁剪RandomSizedCrop

#RandomSizedCrop
#One may combine RandomCrop and RandomScale but there is a transformation RandomSizedCrop that allows to combine them into one transformation.
aug = A.RandomSizedCrop(min_max_height=(50, 101),
                        height=original_height, width=original_width, p=1)
random.seed(7)
augmented = aug(image=image, mask=mask)
image_scaled = augmented['image']
mask_scaled = augmented['mask']
visualize(image_scaled, mask_scaled,
          original_image=image, original_mask=mask)

Randomized Size Crop

七、让我们尝试结合不同的转换

7.1 轻型非破坏性增强。

aug = A.Compose([
    A.VerticalFlip(p=0.5),
    A.RandomRotate90(p=0.5)]
)

random.seed(7)
augmented = aug(image=image, mask=mask)
image_light = augmented['image']
mask_light = augmented['mask']
visualize(image_light, mask_light, original_image=image, original_mask=mask)

垂直翻转和随机旋转 90 度相结合

7.2 让我们添加非刚性变换和 RandomSizedCrop Medium 增强

aug = A.Compose([
    A.OneOf([
        A.RandomSizedCrop(min_max_height=(50, 101), height=original_height,
                          width=original_width, p=0.5),
        A.PadIfNeeded(min_height=original_height, min_width=original_width, p=0.5)
    ],p=1),
    A.VerticalFlip(p=0.5),
    A.RandomRotate90(p=0.5),
    A.OneOf([
        A.ElasticTransform(p=0.5, alpha=120, sigma=120 * 0.05, alpha_affine=None),
        A.GridDistortion(p=0.5),
        A.OpticalDistortion(distort_limit=1, shift_limit=0.5, p=1),
    ], p=0.8)])

random.seed(11)
augmented = aug(image=image, mask=mask)

image_medium = augmented['image']
mask_medium = augmented['mask']

visualize(image_medium, mask_medium, original_image=image, original_mask=mask)


7.3 让我们添加非空间变换。

还可以添加许多非空间变换,如 CLAHE、RandomBrightness、RandomContrast、RandomGamma。它们将仅应用于图像而不是蒙版。

aug = A.Compose([
    A.OneOf([
        A.RandomSizedCrop(min_max_height=(50, 101), height=original_height,
                          width=original_width, p=0.5),
        A.PadIfNeeded(min_height=original_height, min_width=original_width, p=0.5)
    ], p=1),
    A.VerticalFlip(p=0.5),
    A.RandomRotate90(p=0.5),
    A.OneOf([
        A.ElasticTransform(alpha=120, sigma=120 * 0.05, alpha_affine=None, p=0.5),
        A.GridDistortion(p=0.5),
        A.OpticalDistortion(distort_limit=2, shift_limit=0.5, p=1)
        ], p=0.8),
    A.CLAHE(p=0.8),
    A.RandomBrightnessContrast(p=0.8),
    A.RandomGamma(p=0.8)])

random.seed(11)
augmented = aug(image=image, mask=mask)

image_heavy = augmented['image']
mask_heavy = augmented['mask']

visualize(image_heavy, mask_heavy, original_image=image, original_mask=mask)

7.4 使用专辑来增强关键点


在本笔记本中,我们将展示如何将专辑应用到关键点增强问题。请参阅变换及其支持的目标列表,以了解哪些空间级别增强支持关键点。您可以对具有关键点的图像使用任何像素级增强,因为像素级增强不会影响关键点。

注意:默认情况下,使用关键点的增强在转换后不会更改关键点的标签。如果关键点的标签是特定于侧面的,则可能会造成问题。例如,如果您有一个名为左臂的关键点并应用 Horizo​​ntalFlip 增强,您将获得具有相同左臂标签的关键点,但它现在看起来像右臂关键点。有关视觉示例,请参阅本文末尾的图片。

如果您使用此类类型的关键点,请考虑使用来自 albumentations-experimental 的 SymmetricKeypoints 增强,这些增强是为处理这种情况而精确创建的。

import random
import cv2
from matplotlib import pyplot as plt
import albumentations as A


KEYPOINT_COLOR = (0, 255, 0) # Green

def vis_keypoints(image, keypoints, color=KEYPOINT_COLOR, diameter=15):
    image = image.copy()

    for (x, y) in keypoints:
        cv2.circle(image, (int(x), int(y)), diameter, (0, 255, 0), -1)

    plt.figure(figsize=(8, 8))
    plt.axis('off')
    plt.imshow(image)


image = cv2.imread('keypoints_image.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

7.5 定义关键点

我们将使用 xy 格式作为关键点的坐标。每个关键点都用两个坐标定义,x是x轴上的位置,y是y轴上的位置。关键点坐标格式的详细说明请参考这篇文章——

Keypoints augmentation - Albumentations Documentation

keypoints = [
    (100, 100),
    (720, 410),
    (1100, 400),
    (1700, 30),
    (300, 650),
    (1570, 590),
    (560, 800),
    (1300, 750),
    (900, 1000),
    (910, 780),
    (670, 670),
    (830, 670),
    (1000, 670),
    (1150, 670),
    (820, 900),
    (1000, 900),
]
#Visualize the original image with keypoints
vis_keypoints(image, keypoints)

原始关键点(Original Key Points)

transform = A.Compose(
    [A.HorizontalFlip(p=1)],
    keypoint_params=A.KeypointParams(format='xy')
)
transformed = transform(image=image, keypoints=keypoints)
vis_keypoints(transformed['image'], transformed['keypoints'])

转换的图像和关键点

#A few more examples of augmentation pipelines

transform = A.Compose(
    [A.VerticalFlip(p=1)],
    keypoint_params=A.KeypointParams(format='xy')
)
transformed = transform(image=image, keypoints=keypoints)
vis_keypoints(transformed['image'], transformed['keypoints'])

random.seed(7)
transform = A.Compose(
    [A.RandomCrop(width=768, height=768, p=1)],
    keypoint_params=A.KeypointParams(format='xy')
)
transformed = transform(image=image, keypoints=keypoints)
vis_keypoints(transformed['image'], transformed['keypoints'])

random.seed(7)
transform = A.Compose(
    [A.Rotate(p=0.5)],
    keypoint_params=A.KeypointParams(format='xy')
)
transformed = transform(image=image, keypoints=keypoints)
vis_keypoints(transformed['image'], transformed['keypoints'])

transform = A.Compose(
    [A.CenterCrop(height=512, width=512, p=1)],
    keypoint_params=A.KeypointParams(format='xy')
)
transformed = transform(image=image, keypoints=keypoints)
vis_keypoints(transformed['image'], transformed['keypoints'])

random.seed(7)
transform = A.Compose(
    [A.ShiftScaleRotate(p=0.5)],
    keypoint_params=A.KeypointParams(format='xy')
)
transformed = transform(image=image, keypoints=keypoints)
vis_keypoints(transformed['image'], transformed['keypoints'])

#An example of complex augmentation pipeline

random.seed(7)
transform = A.Compose([
        A.RandomSizedCrop(min_max_height=(256, 1025), height=512, width=512, p=0.5),
        A.HorizontalFlip(p=0.5),
        A.OneOf([
            A.HueSaturationValue(p=0.5),
            A.RGBShift(p=0.7)
        ], p=1),
        A.RandomBrightnessContrast(p=0.5)
    ],
    keypoint_params=A.KeypointParams(format='xy'),
)
transformed = transform(image=image, keypoints=keypoints)
vis_keypoints(transformed['image'], transformed['keypoints'])

八、Albumentations 中的 MixUp 变换

在此转换中,我们创建原始图像和参考图像的加权平均值。变换还支持global_labels和蒙版

import random
import cv2
from matplotlib import pyplot as plt
from pathlib import Path
import numpy as np
import cv2
import albumentations as A


def visualize(image):
    plt.figure(figsize=(10, 5))
    plt.axis('off')
    plt.imshow(image)

def load_rgb(image_path):
    image = cv2.imread(image_path)
    return cv2.cvtColor(image, cv2.COLOR_BGR2RGB)


img_path = "woman.jpeg"
img = load_rgb(img_path)

class_id = 0
visualize(img)

8.1 Mixup 变换

要使用 transform,我们需要定义可以是任何 sequence 或 generator 的参考数据。

我们还需要定义read_fn,将项目从 reference_data 转换为带有键 image 和可选global_label mask 的字典。

reference_data = [{
    "image_path": "cat1.jpeg",
    "class_id": 1},
                  {"image_path": "tiger.jpeg",
    "class_id": 2}]

     

def int_to_onehot(value, num_classes):
    """Convert an array of integers to one-hot representation.

    Args:
        values (np.ndarray): Array of integers to be converted.
        num_classes (int): Total number of classes, determines the length of one-hot vectors.

    Returns:
        np.ndarray: One-hot encoded representation of `values`.
    """
    # Initialize the one-hot encoded array of shape (num_classes,)
    one_hot = np.zeros(num_classes, dtype=int)

    # Set the appropriate index to one
    one_hot[value] = 1

    return one_hot

NUM_CLASSES = 5

target_height = 2500
target_width = 1800

# We can process data as we want, including application of augmentations transform.

reference_aug = A.Compose([A.RandomCrop(width=target_width, 
                                        height=target_height, p=1)], p=1)

def read_fn(item):
    image = load_rgb(item["image_path"])

    transformed_image = reference_aug(image=image)["image"]

    global_label = int_to_onehot(item["class_id"], NUM_CLASSES)
    return {
        "image": transformed_image,
        "global_label": global_label
    }


visualize(read_fn(reference_data[0])["image"])

visualize(read_fn(reference_data[1])["image"])

transform = A.Compose([A.RandomCrop(width=target_width, height=target_height, p=1),
                                       A.MixUp(reference_data=reference_data,
                                              read_fn=read_fn, p=1),
                                      A.HorizontalFlip(p=1)], p=1)

original_global_label = int_to_onehot(class_id, NUM_CLASSES)

transformed = transform(image=img, global_label=original_global_label)
print("Global label = ", transformed["global_label"])
print("Mixing coefficient = ", transformed["mix_coef"])

visualize(transformed["image"])


Output:
Global label =  [0.76996663 0.23003337 0.         0.         0.        ]
Mixing coefficient =  0.7699666256848163

The output image is a mix of above 3 images. See the image below -

transformed = transform(image=img, global_label=original_global_label)
print("Global label = ", transformed["global_label"])
print("Mixing coefficient = ", transformed["mix_coef"])

visualize(transformed["image"])


Output:
Global label =  [0.34351105 0.         0.65648895 0.         0.        ]
Mixing coefficient =  0.34351104934091414

#What if you need to know image that was used for mixing?
mix_data = transformed["mix_data"]
mixing_image = mix_data["image"]
global_label_of_mixing_image = mix_data["global_label"]

8.2 将具有相同参数的相同增强应用于多个图像、蒙版、定界框或关键点

有时,您希望将同一组增强应用于相同类型的多个 input 对象。例如,您可能有一组来自视频的帧,并且您希望以相同的方式增强它们。或者,您可能为同一图像提供了多个蒙版,并且您希望对所有蒙版应用相同的增强。

在 Albumentations 中,您可以使用 Compose 的 additional_targets 参数声明其他目标及其类型。

对于其他目标的名称,您可以使用在 Python 中也是有效参数名称的任何字符串值。稍后,您将使用这些名称将其他目标传递给转换管道。因此,您不能使用以数字开头的字符串,例如 '0image',因为它不是有效的 Python 参数名称。

类型可以是 image、mask、bboxes 或 keypoints。

还有另一种方法可以将相同的增强应用于多个输入,例如图像、蒙版等。

ReplayCompose 是一种工具,可以记录应用于一组输入(例如,图像和关联的掩码)的增强参数,然后使用记录的值以相同的方式增强另一组输入。

支持相同类型的多个输入的 Compose 示例定义可能如下:

import random

import cv2
from matplotlib import pyplot as plt

import albumentations as A

def visualize(image):
    plt.figure(figsize=(10, 10))
    plt.axis('off')
    plt.imshow(image)


image = cv2.imread('multi_target_1.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image0 = cv2.imread('multi_target_2.jpg')
image0 = cv2.cvtColor(image0, cv2.COLOR_BGR2RGB)
image1 = cv2.imread('multi_target_3.jpg')
image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2RGB)

visualize(image)

visualize(image0)

visualize(image1)

8.3 定义增强管道

        管道需要三个图像作为输入,名为 image、image0 和 image1。然后,管道将以相同的方式增强这三个映像。因此,它将应用具有相同参数的同一组转换。

transform = A.Compose(
    [A.VerticalFlip(p=1)],
    additional_targets={'image0': 'image', 'image1': 'image'}
)

# original object and additional targets of the same type should have the same shape
image = image[:503, :723]
image1 = image[:503, :723]

transformed = transform(image=image, image0=image0, image1=image1)
visualize(transformed['image'])    

visualize(transformed['image0'])

visualize(transformed['image1'])

#An example of more complex pipeline

transform = A.Compose(
    [
        A.HorizontalFlip(p=0.5),
        A.ShiftScaleRotate(p=0.5),
        A.RandomBrightnessContrast(p=0.2),
        A.RGBShift(p=0.2),
    ],
    additional_targets={'image0': 'image', 'image1': 'image'}
)

random.seed(42)
transformed = transform(image=image, image0=image0, image1=image1)

visualize(transformed['image'])

visualize(transformed['image0'])

8.4 使用 Albumentations 向图像添加文本

        转换的代码基于 Dana Aubakirova https://github.com/danaaubakirova/doc-augmentation 中的代码。猫咪的图片属于 Sarah Bieszczad Mooze(小的)和 Meeko(大的)。

        此转换的输入是 Albumentations 格式的边界框。即 bbox = [x_min / 宽度, y_min / 高度, x_max / 宽度, y_max, 高度]

为此,我们需要 pillow 库。

from __future__ import annotations
import albumentations as A
import cv2
from matplotlib import pyplot as plt


def visualize(image):
    plt.figure(figsize=(10, 5))
    plt.axis('off')
    plt.imshow(image)

bgr_image = cv2.imread("cats.jpg")
image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)
font_path = "LiberationSerif-Regular.ttf"
visualize(image)

#Write text
transform = A.Compose([A.TextImage(font_path=font_path, p=1, font_color="yellow")])
metadata = {
    "bbox": [0.15, 0.9, 0.9, 0.98],
    "text": "Mooze and Meeko",
}

transformed = transform(image=image, textimage_metadata=metadata)
visualize(transformed["image"])

添加了文本的图像

#Inpaint background
#We black out parts of the image where insert text and inpaint them. Could be useful when replacing old text with a new one.

transform = A.Compose([A.TextImage(font_path=font_path, p=1, font_color=(255, 0, 0), clear_bg=True)])
metadata = {
    "bbox": [0.1, 0.3, 0.9, 0.38],
    "text": "Dangerous Tigers",
}

transformed = transform(image=image, textimage_metadata=metadata)
visualize(transformed["image"])

#Write several lines

transform = A.Compose([A.TextImage(font_path=font_path, p=1, font_color="black", clear_bg=True)])

metadata = [{
    "bbox": [0.02, 0.1, 0.95, 0.17],
    "text": "Big dreams in small packages...",
},
            {
    "bbox": [0.02, 0.85, 0.95, 0.91],
    "text": "...and even bigger in bigger ones."}
           ]

transformed = transform(image=image, textimage_metadata=metadata)
visualize(transformed["image"])

#Augment text
#We can insert text as is, or augment it on the fly.

#Swap words

transform = A.Compose([A.TextImage(font_path=font_path, p=1, font_color="white", augmentations=["swap"])])

metadata = [{
    "bbox": [0.02, 0.1, 0.95, 0.16],
    "text": "Big dreams in small packages...",
},
            {
    "bbox": [0.02, 0.85, 0.95, 0.91],
    "text": "...and even bigger in bigger ones."}
           ]

transformed = transform(image=image, textimage_metadata=metadata)

visualize(transformed["image"])

图像中的增强文本

#Random Deletion

transform = A.Compose([A.TextImage(font_path=font_path, p=1, font_color="red", augmentations=["deletion"])])

metadata = [{
    "bbox": [0.02, 0.1, 0.95, 0.16],
    "text": "Growing up with a giant...",
},
            {
    "bbox": [0.02, 0.85, 0.95, 0.91],
    "text": "...is always an adventure.."}
           ]

transformed = transform(image=image, textimage_metadata=metadata)

visualize(transformed["image"])

让我们使用 NLTK 库插入随机停用词。您应该已安装 NLTK 库。

!pip install nltk

#Insert random stopwords
import nltk

nltk.download('stopwords')
from nltk.corpus import stopwords
stops = stopwords.words('english')

transform = A.Compose([A.TextImage(font_path=font_path, p=1, font_color="white",
                                   augmentations=["insertion"], stopwords=stops)])

metadata = {
    "bbox": [0.15, 0.9, 0.9, 0.95],
    "text": "Mooze and Meeko",
}

transformed = transform(image=image, textimage_metadata=metadata)

visualize(transformed["image"])

随机插入的停用词

#Returning augmented text
#If you need text that was added to the image after "swap", "insertion" or "deletion" you may get it with:


transform = A.Compose([A.TextImage(font_path=font_path, p=1, 
                                   font_color="white", augmentations=["insertion", "swap"],
                                   stopwords=stops)])

metadata = [{
    "bbox": [0.02, 0.1, 0.95, 0.16],
    "text": "Big dreams in small packages...",
},
            {
    "bbox": [0.02, 0.85, 0.95, 0.91],
    "text": "...and even bigger in bigger ones."}
           ]

transformed = transform(image=image, textimage_metadata=metadata)

transformed["overlay_data"]
     
[{'bbox_coords': (19, 1088, 912, 1164),
  'text': 'for ...and even bigger in as bigger didn ones.',
  'original_text': '...and even bigger in bigger ones.',
  'bbox_index': 1,
  'font_color': 'white'},
 {'bbox_coords': (19, 128, 912, 204),
  'text': 'dreams in Big small packages...',
  'original_text': 'Big dreams in small packages...',
  'bbox_index': 0,
  'font_color': 'white'}]


Output:
[{'bbox_coords': (19, 1088, 912, 1164),
  'text': 'for ...and even bigger in as bigger didn ones.',
  'original_text': '...and even bigger in bigger ones.',
  'bbox_index': 1,
  'font_color': 'white'},
 {'bbox_coords': (19, 128, 912, 204),
  'text': 'dreams in Big small packages...',
  'original_text': 'Big dreams in small packages...',
  'bbox_index': 0,
  'font_color': 'white'}]

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

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

相关文章

基于carsim的线控转向仿真(2)--齿条力观测

观测器更详细的介绍文章可以关注博主以下两篇文章 从小车倒立摆系统看系统建模控制LQRLQE仿真_lqr平衡小车仿真模型-CSDN博客 好玩的直流电机调速实验、PID、极点配置、LQR、观测器;不讲大道理_观测器极点配置-CSDN博客 三个实例迅速掌握经典卡尔曼滤波用法_卡尔…

VIM的简单用法

vim三种模式的切换 Set nu:显示行号 Set nonu:不显示行号 Set mousea显示鼠标光标 Set cursorline:显示行线 为什么这些设定默认不能永久存在: 进程结束后,所占的内存空间会被系统回收,资源被释放,这些资源…

国内号码验证注册谷歌邮箱【亲测有效】

前言: 谷歌邮箱可以无需注册直接登录很多软件,但是直接很多人直接注册都会表示国内号码注册不了,所以需求还是有的,这里我尝试一下,顺便记录一下​。 ​环境前提:魔法 ​正文: 打开魔法,开启…

react笔记(React18)

以下笔记可能毫无章法,仅供个人学习记录使用。 关于状态提升: 状态提升适用于兄弟组件之间传递数据,共享状态,其实就是把两个兄弟组件要共同使用的数据存放到共同的父组件中,称状态提升。 关于context跨层级组件通信…

5步掌握“花开富贵”花园管理系统开发——基于Python Django+Vue

🍊作者:计算机毕设匠心工作室 🍊简介:毕业后就一直专业从事计算机软件程序开发,至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长:按照需求定制化开发项目…

Java超市收银系统(十、爬虫)

引言 爬虫功能实现,要求爬取页面数据至少100条,这里以豆瓣音乐为示例编写代码豆瓣音乐标签: 民谣 (douban.com)。 功能实现 除了爬虫功能增加,代码其他内容原理和之前博客发布是一致的,只不过这里为了区分,我们重新创…

IDM是海外加速器吗 IDM在国内好用吗

IDM是一款出色的下载加速器,它可以将下载任务分割成多个部分,利用多线程技术加速下载速度,支持断点续传功能,能够从上次下载中断的地方继续下载,提高了下载效率和稳定性,所以深受年轻人的欢迎。 一、IDM是…

集合及数据结构第十节(下)————常用接口介绍、堆的应用和java对象的比较

系列文章目录 集合及数据结构第十节(下)————常用接口介绍和堆的应用 常用接口介绍和堆的应用 PriorityQueue的特性.PriorityQueue常用接口介绍top-k问题堆排序PriorityQueue中插入对象元素的比较.对象的比较.集合框架中PriorityQueue的比较方式 文…

《系统架构设计师教程(第2版)》第15章-面向服务架构设计理论与实践-04-SOA设计

文章目录 1. SOA设计的标准要求1.1 文档标准1.2 通信协议标准1.3 应用程序统一登记与集成1.4 服务质量 (QoS)1.4.1 可靠性1.4.2 安全性1.4.3 策略1.4.4 控制1.4.5 管理 2. SOA的设计原则 1. SOA设计的标准要求 OASIS Organization for the Advancement of Structured Informati…

RISCV汇编编程讲解

第一章 引言 为什么要讲riscv? riscv的特点: -诞生于顶尖学术机构:诞生于加州大学伯克利分校的体系结构研究院。吸引了大批的顶尖企业参与(e.g. 谷歌、华为、高通、阿里巴巴为rsicv的发展提供了大量的资金支持和贡献了技术和人才…

【计算机网络】名词解释--网络专有名词详解(更新)

在网络通信中,有许多专业术语和概念,它们共同构成了网络通信的基础。以下是一些常见的网络术语及其定义和相互之间的关系: 一、网络基础 1.1 电路交换:电路交换是一种在数据传输前建立专用通信路径的通信方式。在通信开始前&…

RAG 技术原理

目录 RAG 技术原理背景和概念实现步骤1. ChatGPT/GLM 等大语言模型的调用2. 读取知识库数据3. 文本索引与答案检索4. 文本嵌入与向量检索5. 文本多路召回与重排序6. 文本问答Promopt优化 原创链接 RAG 技术原理 背景和概念 在自然语言处理领域,大型语言模型&#x…

使用 C 语言实现字符走迷宫 DFS算法应用

使用 C 语言实现字符走迷宫 DFS算法应用 迷宫问题是一个经典的编程问题,通常用于算法训练。我们将通过使用 C 语言来实现一个字符迷宫的求解,其中玩家可以控制字符在迷宫中移动,直到找到出口。 1. 问题描述 我们将设计一个二维迷宫&#xf…

Unity--AnimationCurve动画曲线设置

参考文章:https://blog.csdn.net/qq_20179331/article/details/131309128 打开Clip文件点击Curves选项,选中想要编辑的动作关键帧,右键选择Auto 这样动画就变成线性的了

爆改YOLOv8 |利用 iAFF迭代注意力改进C2f,高效涨点

1,本文介绍 iAFF的核心思想是通过细致的注意力机制优化特征融合,从而提升卷积神经网络的性能。它不仅解决了因尺度和语义不一致导致的特征融合问题,还引入了多尺度通道注意力模块,提供了一个统一且通用的特征融合方案。此外&…

二分查找算法:朴素二分+左右边界二分力扣实战应用

目录: 1、二分查找算法简介 2、算法原理及时间复杂度分析 2.1 朴素二分算法 3.2 查找左右边界的二分算法 3.2.1 查找左边界 3.2.2 查找右边界 3.3 时间复杂度分析 3、二分查找算法模版 3.1 朴素二分模版 3.2 查找左右边界的二分模版 4、算法应用【leetco…

企业收款码,自动统计职员绩效-微信支付商家版

一、企业收款码 在快节奏的商业世界中,效率与精准是企业成功的关键。微信支付商家版企业收款码,为你开启全新的绩效统计时代。 告别繁琐的传统统计方式,无需再耗费大量时间人工整理数据。企业收款码自动统计职员绩效,每一笔交易都…

Cortex-A7的GIC(通用中断控制器):中断处理状态机

0 资料 ARM Generic Interrupt Controller Architecture version 2.0 Architecture Specification1 中断处理状态机 1.1 中断处理状态说明及状态机转换图 说明: Inactive:未激活,中断无效。中断非挂起或非激活。 Pending:挂起&a…

iZotope Ozone 11 Advanced:专业音频制作与母带处理的巅峰之作

iZotope Ozone 11 Advanced是一款专为音频工程师、制作人和音乐人设计的顶级音频后期制作软件,无论是Mac还是Windows平台,都能为用户提供无与伦比的音频处理体验。该软件集成了最先进的人工智能技术和一系列精密的音频处理工具,让音频作品的最…

还在烦恼Cosplay论坛开发?探索PHP+Vue的完美解决方案!

🎓 作者:计算机毕设小月哥 | 软件开发专家 🖥️ 简介:8年计算机软件程序开发经验。精通Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等技术栈。 🛠️ 专业服务 🛠️ 需求定制化开发源码提…