无监督图像拼接实战:手把手教你用Python复现TIP 2021论文核心算法

# 无监督图像拼接实战:从论文到可运行代码的深度工程指南 如果你对计算机视觉中的图像拼接感兴趣,并且已经厌倦了仅仅阅读论文公式而无法动手实践的困境,那么这篇文章正是为你准备的。我们将深入TIP 2021年那篇著名的《Unsupervised Deep Image Stitching: Reconstructing Stitched Features to Images》论文,但视角完全不同——我们不打算复述论文的理论,而是聚焦于一个更实际的问题:**如何将这篇论文的核心思想,转化为一行行清晰、可调试、能在你自己数据集上跑起来的Python代码**。 这篇文章面向的是已经具备一定PyTorch和深度学习基础,希望深入某个具体领域并实现完整项目闭环的开发者。你会发现,从论文图示到实际张量操作,从损失函数公式到训练循环中的梯度回传,中间存在着大量论文未曾提及的“工程沟壑”。我们将一起搭建网络、处理真实世界UDIS-D数据集、设计训练策略,并解决那些你大概率会遇到的、让程序崩溃或结果失真的典型问题。我们的目标不是复现一个“玩具”,而是构建一个理解深刻、可扩展的工程实践框架。 ## 1. 核心思想拆解:超越论文图示的工程化理解 在动手写代码之前,我们必须对论文的“无监督”和“两阶段”架构建立一种更贴近实现的直觉。论文将流程分为**无监督粗对齐**和**无监督图像重建**两大阶段,这听起来清晰,但在代码层面,这意味着我们需要管理两个相对独立但又共享部分权重的子网络,以及一套复杂的、非标准的数据流。 ### 1.1 “无监督”在代码里意味着什么? 在监督学习中,我们的损失函数直接比较网络输出和“标准答案”(Ground Truth)。而在这篇论文的语境下,“无监督”主要体现在两个方面,这在代码设计上至关重要: 1. **对齐阶段的无监督**:我们没有“正确的”单应性矩阵作为标签。损失函数 `L‘_PW`(公式2)的核心思想是**比较经变换后的图像A与图像B在有效区域内的差异**。这里的“有效区域”由一个掩码`E`来定义,它消融了图像A变换后产生的无效像素(空洞)所对应的图像B区域。在代码中,这转化为一个关键的张量乘法操作和掩码生成逻辑。 > 注意:理解这个掩码`E`的生成是第一个难点。它并非固定不变,而是随着网络预测的单应性矩阵`H`动态变化的。你需要根据`H`对图像A的四个角点进行变换,计算出变换后图像A的边界,进而得到其在图像B坐标系下的有效区域。 2. **重建阶段的无监督**:我们同样没有拼接好的完美图像作为监督信号。损失函数由**内容损失**和**缝隙损失**组成。内容损失确保拼接后的图像在内容上与输入图像一致;缝隙损失则专门作用于重叠区域,迫使网络平滑地融合边界,消除接缝。这些损失的计算都依赖于动态生成的**内容掩码**和**缝隙掩码**。 **一个工程上的关键洞察**:论文中的“无监督”并非完全不需要任何标注。UDIS-D数据集提供了图像对和粗略的重叠区域标注,这些信息被用来生成上述的内容掩码和缝隙掩码。因此,在数据加载部分,我们需要精心设计这些掩码的预处理流程。 ### 1.2 两阶段数据流与梯度传播 网络结构图看起来很优美,但在PyTorch中,我们需要明确每一段张量的来源、形状和去向。下图展示了在代码中必须实现的数据流核心路径: ``` 源图像(I_B) + 目标图像(I_A) | v [无监督单应性网络] ---> 预测单应性矩阵 H (3x3) | v [拼接域变换层] ---> 计算最小包围矩形,得到扭曲后的图像对 (I_AW, I_BW) | v |----------------> [低分辨率重建分支] (输入下采样至256x256) | | | v | 低分辨率拼接图 S_LR | | | v (上采样) | 上采样后的 S_LR | | +-------------------------+ (通道拼接) | v [高分辨率细化分支] (输入为 concat(S_LR_up, I_AW, I_BW)) | v 最终高分辨率输出 S_HR ``` 这个流程揭示了几个编码关键点: * **共享权重**:单应性网络是独立的,而重建阶段的两个分支(低分辨率和高分辨率)是否共享部分编码器权重?论文未明确,但工程上,让低分辨率分支的编码器部分作为高分辨率分支的特征提取前端是常见优化。 * **梯度隔离与联合训练**:我们可以选择先训练对齐阶段,冻结其权重后再训练重建阶段;也可以设计一个总损失函数(公式13),进行端到端的联合训练。后者更复杂,但效果可能更好,需要小心设置各损失项的权重 `ω_LR, ω_HR, ω_CS`。 ## 2. 工程实现:用PyTorch搭建网络骨架 理论清晰后,我们开始构建代码的支柱。我们将采用模块化设计,每个类对应论文中的一个核心组件。 ### 2.1 无监督单应性网络模块 论文引用了一个现有的多尺度深度单应性模型。为了简化,我们先实现一个基础的、基于CNN回归的单应性估计器。在实践中,你可以替换为更复杂的如`HomographyNet`或`DLT`为基础的模型。 ```python import torch import torch.nn as nn import torch.nn.functional as F class BasicHomographyNet(nn.Module): """ 一个基础的单应性矩阵预测网络。 输入:拼接后的图像对 (batch, 6, H, W) [I_A, I_B 在通道维拼接] 输出:单应性矩阵参数 (batch, 8) [忽略最后一个元素,假设为1] """ def __init__(self): super().__init__() self.feature_extractor = nn.Sequential( nn.Conv2d(6, 64, kernel_size=3, padding=1), nn.ReLU(), nn.Conv2d(64, 64, kernel_size=3, padding=1), nn.ReLU(), nn.MaxPool2d(2), # ... 更多层,最终特征图缩小到足够小 ) # 假设经过提取后特征图大小为 (batch, 512, 4, 4) self.regressor = nn.Sequential( nn.Flatten(), nn.Linear(512*4*4, 1024), nn.ReLU(), nn.Dropout(0.5), nn.Linear(1024, 8) # 输出8个参数 ) def forward(self, x): features = self.feature_extractor(x) params = self.regressor(features) # 将8参数转换为3x3矩阵(最后一项为1) batch_size = params.shape[0] H = torch.eye(3, device=params.device).unsqueeze(0).repeat(batch_size, 1, 1) H[:, 0, 0] = params[:, 0] + 1.0 # 增加恒等变换的偏置 H[:, 0, 1] = params[:, 1] H[:, 0, 2] = params[:, 2] H[:, 1, 0] = params[:, 3] H[:, 1, 1] = params[:, 4] + 1.0 H[:, 1, 2] = params[:, 5] H[:, 2, 0] = params[:, 6] H[:, 2, 1] = params[:, 7] # H[:, 2, 2] = 1.0 # 已初始化为1 return H ``` ### 2.2 拼接域变换层:从矩阵到像素 这是算法从“数学”走向“图像”的关键一步。我们需要实现一个可微分的层,它接收单应性矩阵`H`和原始图像`I_A`, `I_B`,输出经过变换和对齐到**最小包围矩形**后的图像对`I_AW`, `I_BW`,以及这个新矩形的尺寸。 ```python def stiching_domain_transform(H, img_A, img_B): """ 实现论文中的拼接域变换。 Args: H: 单应性矩阵 (batch, 3, 3) img_A: 目标图像 (batch, 3, H, W) img_B: 源图像 (batch, 3, H, W) Returns: warped_A, warped_B: 变换对齐后的图像对 (batch, 3, H_new, W_new) new_size: (H_new, W_new) """ batch_size, _, H_orig, W_orig = img_A.shape device = H.device # 1. 计算原始图像四个角点坐标 corners = torch.tensor([[0, 0, 1], [W_orig-1, 0, 1], [W_orig-1, H_orig-1, 1], [0, H_orig-1, 1]], dtype=torch.float32, device=device) corners = corners.T.unsqueeze(0).repeat(batch_size, 1, 1) # (batch, 3, 4) # 2. 用H变换目标图像(img_A)的角点 warped_corners = torch.bmm(H, corners) # (batch, 3, 4) warped_corners = warped_corners / warped_corners[:, 2:3, :] # 齐次坐标归一化 xy_warped = warped_corners[:, :2, :] # (batch, 2, 4) # 3. 计算最小包围矩形 x_min = xy_warped[:, 0, :].min(dim=1)[0].floor().int() # (batch,) x_max = xy_warped[:, 0, :].max(dim=1)[0].ceil().int() y_min = xy_warped[:, 1, :].min(dim=1)[0].floor().int() y_max = xy_warped[:, 1, :].max(dim=1)[0].ceil().int() # 4. 计算新画布尺寸 (为简化,这里取batch内最大尺寸,实际可按需padding) W_new = (x_max - x_min).max().item() H_new = (y_max - y_min).max().item() # 更鲁棒的做法是定义一个固定输出尺寸,然后计算适应它的变换矩阵 # 5. 构建目标画布上的网格并反变换回原图采样 (使用torch.nn.functional.grid_sample) # 这里省略详细的网格生成和采样代码,它涉及构建新尺寸的网格,并用H的逆进行反变换。 # 核心是调用 F.grid_sample(img, grid, align_corners=False) # 伪代码返回 # warped_A = F.grid_sample(img_A, grid_A, ...) # warped_B = F.grid_sample(img_B, grid_B, ...) return warped_A, warped_B, (H_new, W_new) ``` > 提示:`grid_sample`是实现空间变换层的核心函数。你需要为`warped_A`和`warped_B`分别计算采样网格。对于`warped_B`,其变换矩阵是`H`(因为`I_B`需要通过`H`与`I_A`对齐);对于`warped_A`,可以视为用单位矩阵变换到新画布。 ### 2.3 重建网络:编码器-解码器与残差细化 重建部分分为低分辨率变形分支和高分辨率细化分支。我们将其实现为一个整体网络。 ```python class ReconstructionNet(nn.Module): def __init__(self): super().__init__() # 低分辨率分支 (Encoder-Decoder with skip connections) self.lr_encoder = nn.ModuleList([...]) # 论文中描述的卷积+池化层 self.lr_decoder = nn.ModuleList([...]) # 反卷积层 # 高分辨率分支 (Residual Blocks) self.hr_initial_conv = nn.Conv2d(3*2 + 3, 64, 3, padding=1) # 输入: [I_AW, I_BW, S_LR_up] self.hr_resblocks = nn.Sequential(*[ResidualBlock(64) for _ in range(8)]) self.hr_final_conv = nn.Conv2d(64, 3, 3, padding=1) def forward(self, warped_A, warped_B): # 低分辨率路径 lr_input = F.interpolate(torch.cat([warped_A, warped_B], dim=1), size=(256, 256)) lr_feat = lr_input skip_features = [] for enc_layer in self.lr_encoder: lr_feat = enc_layer(lr_feat) if isinstance(enc_layer, nn.MaxPool2d): skip_features.append(lr_feat) for i, dec_layer in enumerate(self.lr_decoder): if i > 0: # 跳过连接 lr_feat = torch.cat([lr_feat, skip_features[-i]], dim=1) lr_feat = dec_layer(lr_feat) S_LR = torch.sigmoid(lr_feat) # 输出在[0,1] # 高分辨率路径 S_LR_up = F.interpolate(S_LR, size=warped_A.shape[2:]) hr_input = torch.cat([warped_A, warped_B, S_LR_up], dim=1) hr_feat = self.hr_initial_conv(hr_input) hr_feat = self.hr_resblocks(hr_feat) # 防止信息消失:融合浅层特征 (论文中第一层和倒数第二层) S_HR = torch.sigmoid(self.hr_final_conv(hr_feat)) return S_LR, S_HR ``` ## 3. 损失函数工程:细节决定成败 损失函数是驱动无监督学习的引擎。实现它们时,必须注意数值稳定性和计算效率。 ### 3.1 对齐损失 `L‘_PW` 我们需要实现公式(2)。关键在于正确生成掩码`E`,它指示了`I_A`经`H`变换后,在`I_B`坐标系下的有效区域。 ```python def alignment_loss(H, img_A, img_B): """ 计算无监督单应性损失 L‘_PW """ # 1. 对img_A应用H变换,得到I_A_warped I_A_warped = warp_image(img_A, H) # 使用grid_sample实现 # 2. 生成有效区域掩码E (I_A_warped中非空洞的区域) # 简单方法:检查I_A_warped的像素是否非零(但可能有边缘效应) # 更准确的方法:对img_A的全1掩码进行同样的变换 ones_mask = torch.ones_like(img_A[:, :1, ...]) # (batch, 1, H, W) E = warp_image(ones_mask, H, mode='nearest') # (batch, 1, H, W) E = (E > 0.5).float() # 二值化 # 3. 计算损失 loss = torch.abs(E * I_A_warped - E * img_B).sum() / (E.sum() + 1e-6) return loss ``` ### 3.2 重建损失:内容损失与缝隙损失 内容损失`L_content`和缝隙损失`L_seam`需要动态生成内容掩码`M^AC/M^BC`和缝隙掩码`M^S`。这些掩码依赖于对齐后的图像`I_AW, I_BW`。 | 损失类型 | 计算方式 | 代码实现关键点 | | :--- | :--- | :--- | | **内容损失** | `L1(I_AW * M^AC, S * M^AC)` + `I_BW`同理 | 掩码`M^AC`是`I_AW`的有效区域(非黑边),可通过阈值化得到。 | | **缝隙损失** | `L1(S * M^S, I_AW * M^S)` + `I_BW`同理 | 掩码`M^S`是重叠区域的“接缝”区域,论文通过膨胀内容掩码的差异得到。 | | **感知损失** | `LP = ‖φ(S) - φ(I_ref)‖` | 使用预训练VGG19,提取`conv5_3`(低分)或`conv3_3`(高分)特征计算L2损失。 | 一个常见的误区是直接使用论文中的公式而不考虑批处理(Batch)和归一化。在代码中,所有损失项应在批内取平均,并合理加权。 ```python class ReconstructionLoss(nn.Module): def __init__(self, vgg_layer='conv5_3', lambda_c=1.0, lambda_s=0.5): super().__init__() self.vgg = VGG19Features(vgg_layer) # 自定义的VGG特征提取器 self.lambda_c = lambda_c self.lambda_s = lambda_s self.l1_loss = nn.L1Loss() def forward(self, S, I_AW, I_BW, M_AC, M_BC, M_S): # 内容损失 loss_content = self.l1_loss(S * M_AC, I_AW * M_AC) + \ self.l1_loss(S * M_BC, I_BW * M_BC) # 缝隙损失 loss_seam = self.l1_loss(S * M_S, I_AW * M_S) + \ self.l1_loss(S * M_S, I_BW * M_S) # 感知损失 (以I_AW为参考,实际论文可能更复杂) phi_S = self.vgg(S) phi_ref = self.vgg(I_AW) loss_perceptual = F.mse_loss(phi_S, phi_ref) total_loss = self.lambda_c * loss_content + self.lambda_s * loss_seam + loss_perceptual return total_loss, {'content': loss_content, 'seam': loss_seam, 'perceptual': loss_perceptual} ``` ## 4. 实战:处理UDIS-D数据集与训练策略 UDIS-D是一个大型真实场景数据集。直接从官网下载后,你会发现它包含多种场景和挑战,如大视差、光照变化等。 ### 4.1 数据加载与预处理管道 我们需要一个`Dataset`类来读取图像对、生成必要的掩码(或从标注中加载),并进行数据增强。 ```python from torch.utils.data import Dataset, DataLoader from PIL import Image import os import json class UDISDataset(Dataset): def __init__(self, root_dir, phase='train', transform=None): self.root = root_dir self.phase = phase self.transform = transform # 假设数据组织为:root/phase/pair_list.txt 记录了图像对和重叠区域标注 with open(os.path.join(root, phase, 'pair_list.txt'), 'r') as f: self.pairs = f.readlines() def __getitem__(self, idx): line = self.pairs[idx].strip().split() imgA_path = os.path.join(self.root, self.phase, line[0]) imgB_path = os.path.join(self.root, self.phase, line[1]) # 加载图像 img_A = Image.open(imgA_path).convert('RGB') img_B = Image.open(imgB_path).convert('RGB') # 加载或计算掩码 (这里简化,假设有预生成掩码) mask_path = os.path.join(self.root, self.phase, 'masks', line[2]) overlap_mask = Image.open(mask_path) # 重叠区域标注 if self.transform: # 对img_A, img_B, overlap_mask应用相同的空间变换(如随机裁剪、缩放) img_A, img_B, overlap_mask = self.transform(img_A, img_B, overlap_mask) # 将重叠掩码转化为内容掩码和缝隙掩码 M_AC = (img_A != 0).all(dim=0, keepdim=True).float() # 简单假设非黑即有效 M_BC = (img_B != 0).all(dim=0, keepdim=True).float() # 缝隙掩码:对重叠区域进行形态学操作(膨胀后相减) kernel = torch.ones(1,1,5,5) M_overlap = overlap_mask.unsqueeze(0).float() M_overlap_dilated = F.conv2d(M_overlap, kernel, padding=2) > 0 M_S = (M_overlap_dilated.float() - M_overlap).clamp(min=0) return img_A, img_B, M_AC, M_BC, M_S def __len__(self): return len(self.pairs) ``` ### 4.2 分阶段训练与调试技巧 直接端到端训练这样一个复杂网络极易失败。我建议采用分阶段、渐进式的训练策略: 1. **第一阶段:仅训练单应性网络** * **目标**:让网络学会预测一个能将图像B大致对齐到图像A的单应性矩阵。 * **方法**:冻结重建网络,只使用`alignment_loss`。使用较小的学习率(如1e-4)。 * **调试**:可视化`I_AW`和`I_BW`。理想情况下,两者的重叠区域应该基本对齐。如果图像扭曲成无意义的形状,检查`alignment_loss`计算是否正确,特别是掩码`E`。 2. **第二阶段:冻结单应性网络,训练低分辨率重建分支** * **目标**:在低分辨率下学会生成初步的拼接图。 * **方法**:使用`ReconstructionLoss`(仅低分辨率部分),输入是下采样后的`I_AW`, `I_BW`。 * **调试**:观察`S_LR`。它应该是一个完整的、低分辨率的拼接图,接缝处可能模糊但内容连贯。如果输出全黑或全灰,检查解码器是否梯度消失,尝试使用更激进的初始化或加入批归一化层。 3. **第三阶段:联合微调高分辨率分支** * **目标**:提升细节清晰度,消除接缝伪影。 * **方法**:解冻所有网络(或保持单应性网络微调),使用完整的损失函数(公式13)。此时学习率应进一步降低(如5e-5)。 * **调试**:比较`S_HR`和`S_LR`上采样后的结果。`S_HR`应在纹理细节上更丰富,接缝更不明显。如果出现网格状伪影,可能是`grid_sample`时`align_corners`参数设置问题。 **一个我踩过的坑**:感知损失`L_P`的权重需要仔细调整。权重太大,会导致输出图像过度平滑,失去纹理;权重太小,则对消除接缝帮助有限。一个实用的技巧是,在训练初期给感知损失一个较小的权重,随着训练进行再慢慢增加。 ### 4.3 评估与可视化:不仅仅是看Loss曲线 对于图像生成任务,Loss下降不代表视觉质量提升。必须建立一套可视化流水线。 * **定期保存验证集结果**:在每个Epoch结束时,用固定的几组验证图像进行推理,并保存拼接结果`S_HR`。制作一个GIF来观察整个训练过程中图像的演变过程,非常直观。 * **检查中间特征**:如论文中所做,可视化低分辨率分支编码器-解码器各层的特征图。这能帮你理解网络到底“关注”哪里,有助于诊断问题。 * **定量指标(谨慎使用)**:虽然是无监督,但仍可借用一些图像质量评估指标,如**SSIM(结构相似性)** 在重叠区域的计算,或者**接缝处的梯度幅值**来辅助判断。但记住,这些指标与主观视觉感受可能不完全一致。 将这篇论文的算法工程化,是一个充满挑战也极具成就感的过程。它迫使你深入理解每一个数学符号在张量运算中的对应,并思考在GPU上高效实现的方法。当你看到自己编写的代码成功地将两张存在视差和亮度差异的照片天衣无缝地拼接在一起时,那种满足感远胜于单纯阅读论文。希望这份指南能为你扫清一些障碍,剩下的,就交给你的代码和耐心了。记住,在深度学习的工程实践中,**一次成功的运行,往往建立在数十次失败的调试之上**。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

Python内容推荐

matlab加密代码-PIE:论文“用于同时照明和反射率估计的图像增强概率方法”的Python代码(2015年TIP)

matlab加密代码-PIE:论文“用于同时照明和反射率估计的图像增强概率方法”的Python代码(2015年TIP)

本文介绍了一种使用Python和PIE库处理图像序列的方法,支持命令行参数设置输入输出路径及测试样本。核心功能包括图像梯度计算、PSF到OTF转换、去模糊和增强处理,并通过傅里叶变换和优化技术提升图像

基于五种主流 AIGC 检测算法原理的中文学术文本与图像反制工具 | Claude Code Skill.zip

基于五种主流 AIGC 检测算法原理的中文学术文本与图像反制工具 | Claude Code Skill.zip

测试集覆盖CNKI近五年核心期刊论文样本12786篇、arXiv中文预印本4321篇、国家自然科学基金项目书3892份及高校学位论文库15673份,图像测试集包含IEEE TIP、CVPR Workshop

soapy_AO_

soapy_AO_

**波前重建算法**: 自适应光学系统的核心是能够从传感器数据中重建波前畸变的算法。`soapy_AO` 可能包含了多种重建算法,比如最小二乘法、快速傅里叶变换等。4.

计算机与网络英语词汇

计算机与网络英语词汇

时钟定时(Clock Timing)依赖于时钟信号来同步操作,是大多数数字电路设计的核心。

整理的GTK+笔记

整理的GTK+笔记

GTK+支持多种编程语言,包括但不限于C、C++、TOM、PERL、Python、GUILE和ADA。

120m2双程卧式U型管换热器.rar

120m2双程卧式U型管换热器.rar

120m2双程卧式U型管换热器.rar

60m3每小时袋式过滤器图纸.rar

60m3每小时袋式过滤器图纸.rar

60m3每小时袋式过滤器图纸.rar

1000L碱罐流程图.rar

1000L碱罐流程图.rar

1000L碱罐流程图.rar

易语言源码康复中心医药管理系统

易语言源码康复中心医药管理系统

易语言源码康复中心医药管理系统

易语言源码局域网自动更新易语言源码例程

易语言源码局域网自动更新易语言源码例程

易语言源码局域网自动更新易语言源码例程

UAV—USV跨域协同搜索算法仿真.zip

UAV—USV跨域协同搜索算法仿真.zip

1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

在MATLAB中实现的全局搜索路径规划,包含A和人工势场。.zip

在MATLAB中实现的全局搜索路径规划,包含A和人工势场。.zip

1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

微信支付V3 Java jsApi 扫码支付示例

微信支付V3 Java jsApi 扫码支付示例

打开链接下载源码: https://pan.quark.cn/s/a4b39357ea24 本人近期参与了微信支付的实现工作,先前上传的关于单个微信jsapi的java版本demo已经被重新整理,涵盖了扫码支付和jsapi支付两种模式的demo,代码内容详尽,对于诸多易错环节均添加了注释说明,同时提供了报错时后台与前台的代码demo,前台部分包含了二维码生成的demo,此外还新增了java版本处理微信支付回调通知的代码demo,该整合版本汇集了近期项目中所遇到的所有相关难题及其解决方案,若存在疑问,欢迎联系我。

QYR-18900-2026-2032全球与中国X射线管组件市场现状及未来发展趋势 Sample-macq.docx

QYR-18900-2026-2032全球与中国X射线管组件市场现状及未来发展趋势 Sample-macq.docx

QYR-18900-2026-2032全球与中国X射线管组件市场现状及未来发展趋势 Sample-macq.docx

HTML5 CSS3进度条实例源码

HTML5 CSS3进度条实例源码

JAVA, Node, Python 部分代码只实现了签名算法,需要开发者传入 jsapiticket 和 url ,其中 jsapiticket 需要通过 http://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi&accesstoken=ACCESSTOKEN 接口获取,url 为调用页面的完整 url 。 PHP 部分代码包括了获取 accesstoken 和 jsapiticket 的操作,只需传入 appid 和 appsecret 即可,但要注意如果已有其他业务需要使用 accesstoken 的话,应修改获取 accesstoken 部分代码从全局缓存中获取,防止重复获取 access_token ,超过调用频率。 源码链接: https://pan.quark.cn/s/a4b39357ea24 注意事项: jsapiticket 的有效期为 7200 秒,开发者必须全局缓存 jsapiticket ,防止超过调用频率。

易语言源码两步计算应用题

易语言源码两步计算应用题

易语言源码两步计算应用题

250ZJ65 渣浆泵.rar

250ZJ65 渣浆泵.rar

250ZJ65 渣浆泵.rar

Fix-binding-feedback

Fix-binding-feedback

Fix_binding_feedback

金蝶K3基础资料导入导出说明

金蝶K3基础资料导入导出说明

代码转载自:https://pan.quark.cn/s/b3223228ebb9 GKk3 rules

CS1237模块24位 stc8h 51单片机测试程序

CS1237模块24位 stc8h 51单片机测试程序

CS1237模块24位 stc8h 51单片机的测试程序,0.96寸oled屏显示,串口数据发送,可采集正负电压,资料包含原理图,源代码,已经验证过

最新推荐最新推荐

recommend-type

处理minio文件分析链接的python

处理minio文件分析链接的python
recommend-type

minio 文件服务器

minio 文件服务器环境搭建/以及示例代码,方便搭建文件服务器,代码包含传统的本地保存、minio保存、s3保存等示例代码。
recommend-type

minio-py:用于 Python 的 MinIO 客户端 SDK

适用于 Amazon S3 兼容云存储的 MinIO Python SDK MinIO Python SDK 是简单存储服务(又名 S3)客户端,用于对任何与 Amazon S3 兼容的对象存储服务执行存储桶和对象操作。 有关 API 和示例的完整列表,请查看 最低要求 Python 3.6 或更高版本。 使用pip下载 pip3 install minio 下载源 git clone https://github.com/minio/minio-py cd minio-py python setup.py install 快速入门示例 - 文件上传器 此示例程序连接到与 S3 兼容的对象存储服务器,在该服务器上创建一个存储桶,然后将文件上传到该存储桶。 您需要以下项目才能连接到 S3 兼容的对象存储服务器: 参数 描述 端点 S3 服务的 URL。 访问密钥 S3 服务中帐户的
recommend-type

二、python+前端 实现MinIO分片上传

二、python+前端 实现MinIO分片上传
recommend-type

Python连接MinIO[项目代码]

本文详细介绍了如何使用Python连接MinIO服务器,实现高效的对象存储管理。MinIO是一个高性能的分布式对象存储服务器,兼容Amazon S3云存储服务API。文章首先概述了对象存储在云计算和大数据领域的优势,然后详细指导了环境准备步骤,包括安装MinIO、Python MinIO客户端库以及获取访问信息。接着,提供了一个完整的Python脚本示例,展示了如何连接到MinIO服务器、创建存储桶、上传和下载文件以及列出存储桶中的对象。此外,文章还强调了安全性、错误处理、访问控制和性能优化等注意事项。最后,总结了MinIO的灵活性和可扩展性,使其成为构建云原生应用的理想选择。
recommend-type

学生成绩管理系统C++课程设计与实践

资源摘要信息:"学生成绩信息管理系统-C++(1).doc" 1. 系统需求分析与设计 在进行学生成绩信息管理系统开发前,首先需要进行系统需求分析,这是确定系统开发目标与范围的过程。需求分析应包括数据需求和功能需求两个方面。 - 数据需求分析: - 学生成绩信息:需要收集学生的姓名、学号、课程成绩等数据。 - 数据类型和长度:明确每个数据项的数据类型(如字符串、整型等)和长度,例如学号可能是字符串类型且长度为一定值。 - 描述:详细描述每个数据项的意义,以确保系统能够准确处理。 - 功能需求分析: - 列出功能列表:用户界面应提供清晰的操作指引,列出所有可用功能。 - 查询学生成绩:系统应能通过学号或姓名查询学生的成绩信息。 - 增加学生成绩信息:允许用户添加未保存的学生成绩信息。 - 删除学生成绩信息:能够通过学号或姓名删除已经保存的成绩信息。 - 修改学生成绩信息:通过学号或姓名修改已有的成绩记录。 - 退出程序:提供安全退出程序的选项,并确保所有修改都已保存。 2. 系统设计 系统设计阶段主要完成内存数据结构设计、数据文件设计、代码设计、输入输出设计、用户界面设计和处理过程设计。 - 内存数据结构设计: - 使用链表结构组织内存中的数据,便于动态增删查改操作。 - 数据文件设计: - 选择文本文件存储数据,便于查看和编辑。 - 代码设计: - 根据功能需求,编写相应的函数和模块。 - 输入输出设计: - 设计简洁明了的输入输出提示信息和操作流程。 - 用户界面设计: - 用户界面应为字符界面,方便在命令行环境下使用。 - 处理过程设计: - 设计数据处理流程,确保每个操作都有明确的处理逻辑。 3. 系统实现与测试 实现阶段需要根据设计阶段的成果编写程序代码,并进行系统测试。 - 程序编写: - 完成系统设计中所有功能的程序代码编写。 - 系统测试: - 设计测试用例,通过测试用例上机测试系统。 - 记录测试方法和测试结果,确保系统稳定可靠。 4. 设计报告撰写 最后,根据系统开发的各个阶段,撰写详细的设计报告。 - 系统描述:包括问题说明、数据需求和功能需求。 - 系统设计:详细记录内存数据结构设计、数据文件设计、代码设计、输入/输出设计、用户界面设计、处理过程设计。 - 系统测试:包括测试用例描述、测试方法和测试结果。 - 设计特点、不足、收获和体会:反思整个开发过程,总结经验和教训。 时间安排: - 第19周(7月12日至7月16日)完成项目。 - 7月9日8:00到计算机学院实验中心(三楼)提交程序和课程设计报告。 指导教师和系主任(或责任教师)需要在文档上签名确认。 系统需求分析: - 使用表格记录系统需求分析的结果,包括数据项、数据类型、数据长度和描述。 - 分析数据项如学生成绩信息、状态器、链表节点等,确定其属性和行为。 以上就是文档中提到的学生成绩信息管理系统开发的关键知识点。开发此类系统需要熟练掌握C++编程基础,了解面向对象的程序设计思想,以及熟悉文件操作和链表等数据结构的应用。此外,良好的软件开发流程意识、测试意识和文档撰写能力也是必不可少的。
recommend-type

别再手动拖拽了!用Lumerical脚本批量创建FDTD仿真结构(附完整代码)

# 告别低效建模:Lumerical脚本自动化实战指南 在光子学仿真领域,时间就是科研生命线。当同行还在GUI界面里反复点击菜单时,你已经用脚本批量生成了20组参数化结构——这不是未来场景,而是每位FDTD用户都应该掌握的基础生产力革命。本文将彻底改变你与Lumerical的交互方式,从手动拖拽的农耕时代,跃迁到自动化建模的工业文明。 ## 1. 为何脚本建模是必然选择 2019年Nature Photonics的一项研究显示,科研工作者在仿真工具上平均浪费37%的时间在重复性操作上。对于需要参数扫描的纳米光学结构设计,这个数字可能更高。手动创建10个不同尺寸的纳米柱阵列意味着: -
recommend-type

Java邮件解析任务中,如何安全高效地提取HTML邮件内容并避免硬编码、资源泄漏和类型转换异常?

<think>我们被要求优化一段Java代码,该代码用于处理邮件(特别是来自特定发件人的构建通知邮件)。代码的主要问题包括: 1. 重复获取邮件内容:在检查MIME类型后,多次调用`msg[i].getContent()`,这可能导致性能问题或流关闭异常。 2. 类型转换问题:直接将邮件内容转换为`Multipart`而不进行类型检查,可能引发`ClassCastException`。 3. 代码结构问题:逻辑嵌套过深,可读性差,且存在重复代码(如插入邮件详情的操作在两个地方都有)。 4. 硬编码和魔法值:例如在解析HTML表格时使用了硬编码的索引(如list3.get(10)),这容易因邮件
recommend-type

RH公司应收账款管理优化策略研究

资源摘要信息:"本文针对RH公司的应收账款管理问题进行了深入研究,并提出了改进策略。文章首先分析了应收账款在企业管理中的重要性,指出其对于提高企业竞争力、扩大销售和充分利用生产能力的作用。然后,以RH公司为例,探讨了公司应收账款管理的现状,并识别出合同管理、客户信用调查等方面的不足。在此基础上,文章提出了一系列改善措施,包括完善信用政策、改进业务流程、加强信用调查和提高账款回收力度。特别强调了建立专门的应收账款回收部门和流程的重要性,并建议在实际应用过程中进行持续优化。同时,文章也意识到企业面临复杂多变的内外部环境,因此提出的策略需要根据具体情况调整和优化。 针对财务管理领域的专业学生和从业者,本文提供了一个关于应收账款管理问题的案例研究,具有实际指导意义。文章还探讨了信用管理和征信体系在应收账款管理中的作用,强调了它们对于提升企业信用风险控制和市场竞争能力的重要性。通过对比国内外企业在应收账款管理上的差异,文章总结了适合中国企业实际环境的应收账款管理方法和策略。" 根据提供的文件内容,以下是详细的知识点: 1. 应收账款管理的重要性:应收账款作为企业的一项重要资产,其有效管理关系到企业的现金流、财务健康以及市场竞争力。不良的应收账款管理会导致资金链断裂、坏账损失增加等问题,严重影响企业的正常运营和长远发展。 2. 应收账款的信用风险:在信用交易日益频繁的商业环境中,企业必须对客户信用进行评估,以便采取合理的信用政策,降低信用风险。 3. 合同管理的薄弱环节:合同是应收账款管理的法律基础,严格的合同管理能够保障企业权益,减少因合同问题导致的应收账款风险。 4. 客户信用调查:了解客户的信用状况对于预测和控制应收账款风险至关重要。企业需要建立有效的客户信用调查机制,识别和筛选信用良好的客户。 5. 应收账款回收策略:企业应建立有效的账款回收机制,包括定期的账款跟进、逾期账款的催收等。同时,建立专门的应收账款回收部门可以提升回收效率。 6. 应收账款管理流程优化:通过改进企业内部管理流程,如简化审批流程、提高工作效率等措施,能够提升应收账款的管理效率。 7. 应收账款管理策略的调整和优化:由于企业的内外部环境复杂多变,因此制定的管理策略需要根据实际情况进行动态调整和持续优化。 8. 信用管理和征信体系的作用:建立和完善企业内部信用管理体系和征信体系,有助于企业更好地控制信用风险,并在市场竞争中占据有利地位。 9. 对比国内外应收账款管理实践:通过研究国内外企业在应收账款管理上的不同做法和经验,可以借鉴先进的管理理念和方法,提升国内企业的应收账款管理水平。 综上所述,本文深入探讨了应收账款管理的多个方面,为RH公司乃至其他同类型企业提供了应收账款管理的改进方向和策略,对于财务管理专业的教育和实践都具有重要的参考价值。
recommend-type

新手别慌!用BingPi-M2开发板带你5分钟搞懂Tina Linux SDK目录结构

# 新手别慌!用BingPi-M2开发板带你5分钟搞懂Tina Linux SDK目录结构 第一次拿到BingPi-M2开发板时,面对Tina Linux SDK里密密麻麻的文件夹,我完全不知道从哪下手。就像走进一个陌生的大仓库,每个货架上都堆满了工具和零件,却找不到操作手册。这种困惑持续了整整两天,直到我意识到——理解目录结构比死记硬背每个文件更重要。 ## 1. 为什么SDK目录结构如此重要 想象你正在组装一台复杂的模型飞机。如果所有零件都混在一个箱子里,你需要花大量时间寻找每个螺丝和面板。但如果有分门别类的隔层,标注着"机身部件"、"电子设备"、"紧固件",组装效率会成倍提升。Ti