Single-Image Crowd Counting via Multi-Column Convolutional Neural Network

news2024/11/25 9:53:33

================================================================

Single-Image Crowd Counting via Multi-Column Convolutional Neural Network

    • 论文背景
    • 人群密度方法过去的发展历史
      • 早期方法
      • 基于轨迹聚类的方法
      • 基于特征回归的方法
      • 基于图像的方法
    • Multi-column CNN用于人群计数
      • 基于密度图的人群计数
      • 通过几何自适应核生成密度图
      • 密度图估计的多列CNN

论文背景

在2015年的上海大规模踩踏事件中,35人丧生。自那以后,世界各地发生了许多类似的人潮踩踏事件,导致更多的伤亡。因此,精确估算图像或视频中的人群数量对于计算机视觉技术在人群控制和公共安全方面的应用变得愈发重要。在公共集会、体育赛事等场景中,参与人数或密度是未来活动规划和空间设计的基本信息。良好的人群计数方法也可以扩展到其他领域,如从显微图像中计数细胞或细菌、在野生动物保护区估算动物群体的数量,或者估算交通枢纽或交通拥堵中的车辆数量等。

人群密度方法过去的发展历史

早期方法

以前的方法主要采用检测风格的框架,通过在视频序列的两个连续帧上扫描检测器,基于增强外观和运动特征,估算行人的数量。一些方法使用类似的基于检测的框架进行行人计数。然而,这些方法的局限性在于,它们通常假定人群由可以通过某些给定检测器检测到的单独实体组成,这在拥挤的环境或非常密集的人群中显着影响检测器性能和最终的估算准确性。

具体步骤如下
当我们想要数一群人的时候,可以通过观察视频中相邻两帧的变化来实现。方法是这样的:

  • 看变化: 首先,我们会拿到视频中的两个连续画面,然后比较它们之间有哪些地方发生了变化,特别是有没有人在动。
  • 找动的人: 如果有地方发生了变化,我们会认为那些是移动的个体,也就是人。这就是通过观察画面变化来找到人群的方法。
  • 确认特征: 在找到的移动的地方,我们可能会再看一些其他特征,比如这些“移动的东西”是否看起来像人,或者它们的动作是不是像人的动作。
  • 数人: 最后,通过统计在两个画面之间发现的“移动的东西”,我们就能大致估算视频中整个人群的数量。

基于轨迹聚类的方法

这种方法的核心思想是通过对视觉特征的轨迹进行聚类来计数人群。简单来说,就是通过观察视频中物体在一段时间内的运动轨迹,将相似的轨迹归为一类,从而估算人群的数量。但需要注意的是,这种方法适用于视频,而不适用于单个静止图像。

具体步骤如下

  • 轨迹提取: 先从视频中提取物体的运动轨迹,可以使用一些跟踪器(比如KLT跟踪器)来追踪物体在不同帧之间的位置变化。
  • 轨迹聚类: 将这些轨迹按照它们的相似度进行聚类,相似的轨迹被归为同一类。这里的相似度可能包括轨迹的形状、长度、速度等方面的特征。
  • 估算人数: 每个聚类代表了一组相似运动的物体,我们可以认为这是同一群人。通过统计不同聚类中的轨迹数,就能估算出人群的数量。

基于特征回归的方法

这种方法通过对图像的前景进行分割,提取各种特征,然后使用回归函数来估算人群的数量

具体步骤如下

  • 分割前景: 在图像中,人群通常是前景,而其他部分是背景。首先,需要对图像进行前景分割,将人群从背景中分离出来。这可以使用图像分割算法来实现。
  • 提取特征: 从前景图中提取各种特征,这些特征可以包括人群区域的面积、边缘数量、纹理特征等。这些特征将用作回归模型的输入。
  • 回归函数: 使用回归函数来建模特征与人群数量之间的关系。回归函数的选择可以是简单的线性或分段线性函数,也可以采用更复杂的模型,如岭回归、高斯过程回归或神经网络。回归函数的目标是找到一个映射,使得输入特征可以准确地预测人群的数量。

具体的计算公式如下(以线性回归为例)

给定特征向量 x x x(包括面积、边缘数量、纹理等),人群数量的估计 y ^ \hat y y^为:
y ^ = w ⋅ x + b \hat y =w⋅x+b y^=wx+b

其中, w w w是回归系数向量, b b b是偏置。通过训练回归模型,得到最优的 w w w b b b参数,使得预测值 y ^ \hat y y^接近实际的人群数量。

这种方法的优势在于简单且易于理解,而更复杂的模型可以处理更高度非线性的关系,提高估算的准确性。

基于图像的方法

一些研究专注于从静态图像中估算人群数量。不同于前面介绍的视频帧间运动或轨迹聚类方法,这些方法主要依赖于单个静态图像。以下是其中一些方法的原理:

  • 多信息源计数: 一种方法是利用单个图像中的多个信息源来计算密集群中的个体数量。这些信息源可以包括SIFT(尺度不变特征变换)、傅立叶分析、小波分解、GLCM(灰度共生矩阵)特征以及对头部的低置信度检测。通过综合利用这些信息,可以更全面地估算人群数量。
  • 特征融合和支持向量机(SVM): 另一种方法是利用预训练的卷积神经网络(CNN)提取图像特征,并将这些特征用于训练支持向量机(SVM)。具体而言,研究者可以使用多个信息源,如SIFT、傅立叶分析、小波分解、GLCM特征以及低置信度头部检测,将它们融合为一个综合的特征向量。然后,这个特征向量可以用于训练SVM,从而建立图像特征与人群数量之间的关系。

具体而言,特征融合可以表示为:

特征向量=[SIFT特征,傅立叶分析特征,小波分解特征,GLCM特征,低置信度头部检测特征,…]

然后,将这个特征向量用于训练SVM模型,该模型可以根据输入的图像特征向量来预测人群的数量。

这些方法的优势在于能够从静态图像中获取人群数量的信息,而不需要考虑视频帧之间的运动或轨迹信息。然而,对于不同的场景和图像,需要综合利用多个信息源,以提高估算的准确性。

Multi-column CNN用于人群计数

基于密度图的人群计数

在通过卷积神经网络(CNNs)估算给定图像中人数的过程中,有两种自然的配置。

  • 一种是输入为图像,输出为估算的头部计数的网络。
  • 另一种是输出人群的密度图(每平方米多少人),然后通过积分得出头部计数。

本文支持第二种选择,因为密度图保留更多信息。相对于总人群数量,密度图给出了图像中人群的空间分布信息,这在许多应用中都很有用。例如,如果某个区域的密度远高于其他区域,可能表明该区域发生了异常情况。

提出了使用CNN学习密度图的方法,其学习的滤波器更适应于不同大小头部,从而更适用于透视效应变化显著的任意输入。

通过几何自适应核生成密度图

由于CNN需要从输入图像训练以估算密度图,训练数据中给定的密度质量在很大程度上决定了方法的性能。

具体步骤

首先,对于图像中的每个头,将用一个 δ δ δ函数来表示。这个 δ δ δ函数在图像上的位置由 x i x_i xi表示,其中 i i i是头部的编号。如果图像中有 N N N个头,那么将这些 δ δ δ函数的和表示为 H ( x ) H(x) H(x),其中 H ( x ) H(x) H(x)是一个包含 N N N δ δ δ函数的函数,即:

H ( x ) = ∑ i = 1 N δ ( x − x i ) H(x) = \sum_{i=1}^Nδ(x - x_i) H(x)=i=1Nδ(xxi)

为了将其转换为连续的密度函数,可以将该函数与高斯核 G σ G_σ Gσ卷积,使得密度为:
F ( x ) = H ( x ) ∗ G σ ( x ) F(x) = H(x) * G_σ(x) F(x)=H(x)Gσ(x)

然而,这样的密度函数假定 x i x_i xi是图像平面上的独立样本,而实际上,每个 x i x_i xi是3D场景中地面上的人群密度样本,由于透视失真,与不同 x i x_i xi对应的像素对应于场景中不同尺寸的区域。

为了准确估算人群密度 F F F,需要考虑由地平面和图像平面之间的单应性引起的扭曲。然而,在任务和数据集中,通常并不知道场景的几何形状。尽管如此,如果假设每个头周围的人群在图像上是均匀分布的,那么头与其最近的 k k k个邻居(在图像上)之间的平均距离给出了透视效应引起的几何扭曲的合理估计。

因此,应该基于图像中每个人的头尺寸自适应地确定传播参数 σ σ σ。对于给定图像中的每个头 x i x_i xi,将其到其 k k k个最近邻居的距离表示为 [ d 1 i , d 2 i , . . . , d m i ] [d^i_1, d^i_2, ..., d^i_m] [d1i,d2i,...,dmi]。平均距离是 d ˉ i = 1 m ∑ j = 1 m d j i \bar d^i = \frac{1}{m}\sum_{j=1}^m d^i_j dˉi=m1j=1mdji

因此,与 x i x_i xi关联的像素大致对应于场景中地面上半径与 d ˉ i \bar d_i dˉi成比例的区域。因此,为了估算 x i x_i xi周围的人群密度,需要将 δ ( x − x i ) δ(x - x_i) δ(xxi)与方差 σ i σ_i σi成比例的高斯核卷积。更具体地说,密度 F F F应为

F ( x ) = ∑ i = 1 N δ ( x − x i ) ∗ G σ ( x ) F(x) =\sum_{i=1}^Nδ(x-x_i) * G_σ(x) F(x)=i=1Nδ(xxi)Gσ(x)

其中 σ i = β ∗ d ˉ i σ_i = β * \bar d_i σi=βdˉi β β β是参数。换句话说,我们使用自适应于每个数据点周围局部几何的密度核,称为几何自适应核。在实验中,我们经验性地发现 β = 0.3 β = 0.3 β=0.3给出了最佳结果。

密度图估计的多列CNN

由于透视失真,图像通常包含不同大小的头,因此具有相同大小感受野的滤波器不太可能捕捉到不同尺度的人群密度特征。因此,更自然的方法是使用具有不同局部感受野大小的滤波器来学习原始像素到密度图的映射。受多列深度神经网络(MDNNs)成功的启发,提出使用多列卷积神经网络(MCNN)来学习目标密度图。在我们的MCNN中,对于每一列,我们使用不同大小的滤波器来建模与不同尺度头相对应的密度图。例如,具有较大感受野的滤波器对应于建模与较大头相对应的密度图。模型架构图如下:

在这里插入图片描述

MCNN包含三个平行的CNN,其滤波器具有不同大小的局部感受野。为简化起见,我们对所有列使用相同的网络结构(即,conv–pooling–conv–pooling),除了滤波器的大小和数量。对于每个2×2区域,应用最大池化,并且由于ReLU对于CNN的性能良好,采用修正线性单元(ReLU)作为激活函数。

为了减少计算复杂性(要优化的参数数量),对具有较大滤波器的CNN使用较少的滤波器。堆叠所有CNN的输出特征图,并将它们映射到密度图。为了将特征图映射到密度图,采用滤波器大小为1×1。然后,使用欧几里得距离来衡量估算的密度图与地面实况之间的差异。损失函数定义如下:

在这里插入图片描述
其中, Θ Θ Θ是MCNN中可学习参数的集合,N是训练图像的数量, X i X_i Xi是输入图像, F i F_i Fi是图像 X i X_i Xi的地面实况密度图。 F ( X i ; Θ ) F(X_i; \Theta) F(Xi;Θ)表示由带有参数 Θ Θ Θ的MCNN生成的估算密度图, L L L是估算密度图与地面实况密度图之间的损失。

注意事项:

  • 由于使用两层最大池化,每个图像的空间分辨率减少了1/4。因此,在训练阶段,我们在生成密度图之前将每个训练样本下采样1/4。
  • 传统的CNN通常将输入图像规范化为相同的大小。这里我们更喜欢输入图像保持其原始大小,因为将图像调整为相同的大小会引入难以估计的密度图中的额外失真。
  • 除了CNN中的滤波器具有不同的大小之外,MCNN与传统MDNN之间的另一个区别是使用具有可学习权重的所有CNN的输出(即,1×1滤波器)进行组合。相比之下,在提出的MDNNs中,输出简单地平均。

骨干结构在计算机视觉模型中无论是图像分类目标检测或者人群计数等任务中起到了提取图像特征的作用。因此它骨干模型的内部输出图像其实就是一个类似的密度图,MCNN再结合多尺度就会把整个图片里所有人头都检测

我这里有一个实现骨干模型内部可视化的脚本可以参考,模型就用的最常见的骨干模型VGG,VGG模型代码在后面。

import torch
import torch.nn as nn
from torch.autograd import Variable
from torchvision.transforms import transforms
import numpy as np
import cv2
from functools import partial
import matplotlib
import os
import json
matplotlib.use('agg')
import matplotlib.pyplot as plt
import requests

from models import Vgg16Conv
from models import Vgg16Deconv

def load_and_preprocess_image(img_path):
    """加载和预处理图像"""

    # 从img_path读取图像
    img = cv2.imread(img_path)
    img = cv2.resize(img, (224, 224))

    # PyTorch必须通过以下均值和标准差对图片进行标准化
    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])
    
    img = transform(img)
    img.unsqueeze_(0)
    return img


def hook_function(module, input, output, key):
    """用于存储特征图的钩子函数"""

    if isinstance(module, nn.MaxPool2d):
       model.feature_maps[key] = output[0]
       model.pool_locs[key] = output[1]
    else:
       model.feature_maps[key] = output


def register_hooks(model):
    """为骨干模型注册特征图存储钩子"""

    def hook(module, key):
        layer.register_forward_hook(partial(hook_function, key=key))

    for idx, layer in enumerate(model._modules.get('features')):    
        # _modules返回一个有序字典
        hook(layer, key=idx)


def visualize_layer(layer, vgg16_conv, vgg16_deconv):
    """可视化骨干模型的某一层的反卷积结果"""

    num_feat = vgg16_conv.feature_maps[layer].shape[1]
    
    # 将其他特征图激活设置为零
    new_feat_map = vgg16_conv.feature_maps[layer].clone()

    # 选择最大激活的特征图
    act_lst = []
    for i in range(0, num_feat):
        choose_map = new_feat_map[0, i, :, :]
        activation = torch.max(choose_map)
        act_lst.append(activation.item())

    act_lst = np.array(act_lst)
    mark = np.argmax(act_lst)

    choose_map = new_feat_map[0, mark, :, :]
    max_activation = torch.max(choose_map)
    
    # 对其他特征图进行零值处理
    if mark == 0:
        new_feat_map[:, 1:, :, :] = 0
    else:
        new_feat_map[:, :mark, :, :] = 0
        if mark != vgg16_conv.feature_maps[layer].shape[1] - 1:
            new_feat_map[:, mark + 1:, :, :] = 0
    
    choose_map = torch.where(choose_map==max_activation,
            choose_map,
            torch.zeros(choose_map.shape)
            )

    # 对其他激活进行零值处理
    new_feat_map[0, mark, :, :] = choose_map
    
    # 输出反卷积结果
    deconv_output = vgg16_deconv(new_feat_map, layer, mark, vgg16_conv.pool_locs)

    new_img = deconv_output.data.numpy()[0].transpose(1, 2, 0)  # (H, W, C)
    # 归一化
    new_img = (new_img - new_img.min()) / (new_img.max() - new_img.min()) * 255
    new_img = new_img.astype(np.uint8)
    return new_img, int(max_activation)


if __name__ == '__main__':
    
    img_path = './data/cat.jpg'

    # 前向处理
    img = load_and_preprocess_image(img_path)
    vgg16_conv = Vgg16Conv()
    vgg16_conv.eval()
    register_hooks(vgg16_conv)
    conv_output = vgg16_conv(img)
    pool_locs = vgg16_conv.pool_locs


    # 反向处理
    vgg16_deconv = Vgg16Deconv()
    vgg16_deconv.eval()
    plt.figure(num=None, figsize=(16, 12), dpi=80)
    plt.subplot(2, 4, 1)
    plt.title('Original Picture')
    img = cv2.imread(img_path)
    img = cv2.resize(img, (224, 224))
    plt.imshow(img)    
    for idx, layer in enumerate([14, 17, 19, 21, 24, 26, 28]):
    # for idx, layer in enumerate(vgg16_conv.conv_layer_indices):        
        plt.subplot(2, 4, idx+2)
        img, activation = visualize_layer(layer, vgg16_conv, vgg16_deconv)
        plt.title(f'{layer} Layer, Max Activation: {activation}')
        plt.imshow(img)

    plt.savefig('result.jpg')
    print('Result picture has been saved at ./result.jpg')

model.vgg16_conv.py

import torch
import torch.nn as nn
import torchvision.models as models
import torchvision

from collections import OrderedDict

class Vgg16Conv(nn.Module):
    """
    vgg16 convolution network architecture
    """

    def __init__(self, num_cls=1000):
        """
        Input
            number of class, default is 1k.
        """
        super(Vgg16Conv, self).__init__()
    
        self.features = nn.Sequential(
            # conv1
            nn.Conv2d(3, 64, 3, padding=1),
            nn.ReLU(),
            nn.Conv2d(64, 64, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2, return_indices=True),
            
            # conv2
            nn.Conv2d(64, 128, 3, padding=1),
            nn.ReLU(),
            nn.Conv2d(128, 128, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2, return_indices=True),

            # conv3
            nn.Conv2d(128, 256, 3, padding=1),
            nn.ReLU(),
            nn.Conv2d(256, 256, 3, padding=1),
            nn.ReLU(),
            nn.Conv2d(256, 256, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2, return_indices=True),

            # conv4
            nn.Conv2d(256, 512, 3, padding=1),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3, padding=1),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2, return_indices=True),

            # conv5
            nn.Conv2d(512, 512, 3, padding=1),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3, padding=1),
            nn.ReLU(),
            nn.Conv2d(512, 512, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, stride=2, return_indices=True)
        )

        self.classifier = nn.Sequential(
            nn.Linear(512 * 7 * 7, 4096),
            nn.ReLU(),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(),
            nn.Dropout(),
            nn.Linear(4096, num_cls),
            nn.Softmax(dim=1)
        )

        # index of conv
        self.conv_layer_indices = [0, 2, 5, 7, 10, 12, 14, 17, 19, 21, 24, 26, 28]
        # feature maps
        self.feature_maps = OrderedDict()
        # switch
        self.pool_locs = OrderedDict()
        # initial weight
        self.init_weights()

    def init_weights(self):
        """
        initial weights from preptrained model by vgg16
        """
        vgg16_pretrained = models.vgg16(pretrained=True)
        # fine-tune Conv2d
        for idx, layer in enumerate(vgg16_pretrained.features):
            if isinstance(layer, nn.Conv2d):
                self.features[idx].weight.data = layer.weight.data
                self.features[idx].bias.data = layer.bias.data
        # fine-tune Linear
        for idx, layer in enumerate(vgg16_pretrained.classifier):
            if isinstance(layer, nn.Linear):
                self.classifier[idx].weight.data = layer.weight.data
                self.classifier[idx].bias.data = layer.bias.data
    
    def check(self):
        model = models.vgg16(pretrained=True)
        return model

    def forward(self, x):
        for idx, layer in enumerate(self.features):
            if isinstance(layer, nn.MaxPool2d):
                x, location = layer(x)
                # self.pool_locs[idx] = location
            else:
                x = layer(x)
        
        # reshape to (1, 512 * 7 * 7)
        x = x.view(x.size()[0], -1)
        output = self.classifier(x)
        return output

if __name__ == '__main__':
    model = models.vgg16(pretrained=True)
    print(model)

model.vgg16_deconv.py

import torch
import torch.nn as nn
import torchvision.models as models

import sys

class Vgg16Deconv(nn.Module):
    """
    vgg16 transpose convolution network architecture
    """
    def __init__(self):
        super(Vgg16Deconv, self).__init__()

        self.features = nn.Sequential(
            # deconv1
            nn.MaxUnpool2d(2, stride=2),
            nn.ReLU(),
            nn.ConvTranspose2d(512, 512, 3, padding=1),
            nn.ReLU(),
            nn.ConvTranspose2d(512, 512, 3, padding=1),
            nn.ReLU(),
            nn.ConvTranspose2d(512, 512, 3, padding=1),

            # deconv2
            nn.MaxUnpool2d(2, stride=2),
            nn.ReLU(),
            nn.ConvTranspose2d(512, 512, 3, padding=1),
            nn.ReLU(),
            nn.ConvTranspose2d(512, 512, 3, padding=1),
            nn.ReLU(),
            nn.ConvTranspose2d(512, 256, 3, padding=1),
            
            # deconv3
            nn.MaxUnpool2d(2, stride=2),
            nn.ReLU(),
            nn.ConvTranspose2d(256, 256, 3, padding=1),
            nn.ReLU(),
            nn.ConvTranspose2d(256, 256, 3, padding=1),
            nn.ReLU(),
            nn.ConvTranspose2d(256, 128, 3, padding=1),
            
            # deconv4
            nn.MaxUnpool2d(2, stride=2),
            nn.ReLU(),
            nn.ConvTranspose2d(128, 128, 3, padding=1),
            nn.ReLU(),
            nn.ConvTranspose2d(128, 64, 3, padding=1),
            
            # deconv5
            nn.MaxUnpool2d(2, stride=2),
            nn.ReLU(),
            nn.ConvTranspose2d(64, 64, 3, padding=1),
            nn.ReLU(),
            nn.ConvTranspose2d(64, 3, 3, padding=1)    
        )

        self.conv2deconv_indices = {
                0:30, 2:28, 5:25, 7:23,
                10:20, 12:18, 14:16, 17:13,
                19:11, 21:9, 24:6, 26:4, 28:2
                }

        self.unpool2pool_indices = {
                26:4, 21:9, 14:16, 7:23, 0:30
                }

        self.init_weight()

    def init_weight(self):
        vgg16_pretrained = models.vgg16(pretrained=True)
        for idx, layer in enumerate(vgg16_pretrained.features):
            if isinstance(layer, nn.Conv2d):
                self.features[self.conv2deconv_indices[idx]].weight.data = layer.weight.data
                #self.features[self.conv2deconv_indices[idx]].bias.data\
                # = layer.bias.data
        
    def forward(self, x, layer, activation_idx, pool_locs):
        if layer in self.conv2deconv_indices:
            start_idx = self.conv2deconv_indices[layer]
        else:
            raise ValueError('layer is not a conv feature map')

        for idx in range(start_idx, len(self.features)):
            if isinstance(self.features[idx], nn.MaxUnpool2d):
                x = self.features[idx]\
                (x, pool_locs[self.unpool2pool_indices[idx]])
            else:
                x = self.features[idx](x)
        return x

在这里插入图片描述
在这里插入图片描述
从上面图片来看,每层都有一些特征保留出来,而这种特征看起来像是一些opencv特征算法提取的感觉。这种特征结合高斯密度函数,就可以转为密度图啦

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

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

相关文章

回溯法解决01背包问题

输入(共n1行):物品数量、背包体积 下面n行依次输入物品价值和体积 需要注意的点: ①输入的顺序 ②存储价值和体积的数组下标从1开始 ③每一轮符合条件时,及时更新VALUE(价值总和) 从前面做的回溯法可以总结出一些…

bootstrap5开发房地产代理公司Hamilton前端页面

一、需求分析 房地产代理网站是指专门为房地产行业提供服务的在线平台。这些网站的主要功能是连接房地产中介机构、房产开发商和潜在的买家或租户,以促成买卖或租赁房产的交易。以下是一些常见的房地产代理网站的功能: 房源发布:房地产代理网…

009、引用

1. 引用与借用 下面的示例重新定义了一个新的 calculate_length 函数。与之前不同的是,新的函数签名使用了 String 的引用作为参数而没有直接转移值的所有权: fn main() { let s1 String::from("hello"); let len calculate_length(&s1…

病情聊天机器人,利用Neo4j图数据库和Elasticsearch全文搜索引擎相结合

项目设计目的: 本项目旨在开发一个病情聊天机器人,利用Neo4j图数据库和Elasticsearch全文搜索引擎相结合,实现对病情相关数据的存储、查询和自动回答。通过与用户的交互,机器人可以根据用户提供的症状描述,给出初步的可…

【C++杂货铺】C++11新特性——可变参数模板

文章目录 一、可变模板参数相关概念的引入二、获取参数包中参数的个数三、递归函数方式展开参数包四、逗号表达式展开参数包五、可变模板参数的实际应用——emplace相关接口5.1 回顾一下 push_back 的三种用法5.2 emplace_back 使用方法介绍5.3 听说 emplace_back 可以提高效率…

SCT2601,可替代LMR16006X/MP2459/MP2456;4.5V-60V Vin,0.6A,高效降压DCDC转换器

•宽输入范围:4.5V-60V •高达0.6A的连续输出电流 •0.765V2.5%反馈参考电压 •集成500mΩ高压侧MOSFET •低静态电流为80uA •轻负载下的脉冲跳过模式(PSM) •最小接通时间80ns •内置6ms软启动时间 •开关频率为700KHz •可编程输入电压欠压…

探秘AI数字人克隆系统OEM源码:实现24小时无人值守直播间的奥秘

随着人工智能技术的不断发展,AI数字人克隆系统OEM源码正在引起广泛的关注。其中,实现24小时无人值守直播间成为了许多企业和机构的追求。本文将深入探讨如何利用AI数字人克隆系统OEM源码实现24小时无人值守直播间,并揭示其背后的奥秘。 一、…

Acrel-2000MG工商业储能站能量管理系统-光伏储能一体化解决方案

安科瑞 崔丽洁 Acrel-2000MG储能能量管理系统是安科瑞专门针对工商业储能电站研制的本地化能量管理系统,可实现了储能电站的数据采集、数据处理、数据存储、数据查询与分析、可视化监控、报警管理、统计报表、策略管理、历史曲线等功能。其中策略管理,支…

【活动回顾】ABeam 德硕 | 企业座谈及宣讲会--石河子大学专场

ABeam 石河子大学校招之旅 沙漠绿洲,戈壁明珠 近日,ABeam大中华区董事长兼总经理中野洋辅先生带领西安招聘团队来到新疆维吾尔自治区石河子市——石河子大学,与信息科学与技术学院领导就校企合作事宜进行了深入的座谈交流,并在随…

【Unity入门】UGUI之Slider(滑动条)

目录 一、什么是Slider?二、Slider属性与功能 一、什么是Slider? Slider控件允许用户可以通过鼠标来在预先确定的范围调节数值 我们可以在Hierarchy视图右键 -> UI ->Slider来创建滑动条 通过上图可以发现Unity内置的Slider主要有3部分&#x…

【web】vue 播放后端(flask)发送的 mp3 文件

文章目录 演示后端(flask)前端(vue3)重要说明 演示 后端(flask) 后端返回的是 mp3 文件的 url,是可以直接在浏览器上打开后播放的处理跨域请求pip install flask-cors后端代码from flask impor…

一场高规格品鉴会,窥探剑南春冲击高端的“野心”

执笔 | 洪大大 编辑 | 扬 灵 12月27日,以“锦绣巴蜀品味东方”为主题的剑南春东方红致敬中国时代品鉴会在有云鹿洄天府1911中国川菜体验中心举办,数十位媒体代表和嘉宾汇聚一堂,共同品味剑南春东方红的品质魅力,感知高端白酒品…

什么是缓存、为什么要用缓存、缓存分类、缓存测试、缓存更新、缓存设计考虑点、缓存测试点

一、缓存 缓存是一种将数据存储在高速缓存中的技术,它可以提高应用程序的性能和响应速度。 二、 为什么要用缓存 1. 高性能(主要目的) 查询耗时,但变化少,又有很多读请求情况下,可以将查询结果放到缓存中。减少对数据库的压力&…

光伏、储能一体化监控及运维解决方案 安科瑞 许敏

前言:今年以来,在政策利好推动下光伏、风力发电、电化学储能及抽水蓄能等新能源行业发展迅速,装机容量均大幅度增长,新能源发电已经成为新型电力系统重要的组成部分,同时这也导致新型电力系统比传统的电力系统更为复杂…

clang-format

Clang-Format Clang-Format Style Options — Clang 18.0.0git documentation VSCode 1.1 安装扩展 C 1.2 设置 1.3 使用 .clang-fornat 放置在项目(代码)文件夹下使用 .clang-fornat 为文件名 --- # https://clang.llvm.org/docs/ClangFormatStyle…

document360的替代品:原来它也这么好用

在当今信息爆炸的时代,企业和组织需要一个高效的知识管理系统来整理、存储和共享知识。document360是一款备受欢迎的知识库管理系统,但是它并非唯一的选择。本文将介绍document360的替代品,感兴趣就往下看吧。 首先,让我们来了解…

Python分析了京东7万+条评论,仅用3分钟,结果发现……

之前学习Python爬虫采集,为了练手用Scrapy写了一个爬虫,整整采集了京东平台vivo旗舰店7万多条评论。一直也没觉得这些评论数据有啥用,就留在MongoDB中吃灰。最近学了jieba和wordcloud之后,突发奇想着分析下这7万多条评论数据&…

【网络安全】上网行为代理服务器启用Alerts

文章目录 启用AlertsAlert Limits per 24 hoursEmail AlertsSystem Alerts Suspicious Activity AlertsPermitted Suspicious Activity AlertsBlocked Suspicious Activity Alerts Protocol&Category Usage Alerts告警邮件范例推荐阅读 这里的Web Proxy主要代指proxy serve…

HarmonyOS应用开发-搭建开发环境

本文介绍如何搭建 HarmonyOS 应用的开发环境,介绍下载安装 DevEco Studio 开发工具和 SDK 的详细流程。华为鸿蒙 DevEco Studio 是面向全场景的一站式集成开发环境,面向全场景多设备,提供一站式的分布式应用开发平台,支持分布式多…

声纹识别资源汇总(不断更新)

目录 一、任务说明二、指标三、声纹识别研究现状四、数据集开源(1)VoxCeleb:(2)WSJ and LibriSpeech Corpus(3)VOiCES Dataset(4)English Multi-speaker Corpus for Voic…