论文:HiDDeN: Hiding Data With Deep Networks
作者:Jiren Zhu, Russell Kaplan, Justin Johnson, Li Fei-Fei


一、研究背景

在图像信息隐藏领域,通常有两类典型的应用场景:

  • 隐写 (Steganography)

    • 目标:实现秘密通信。
    • 要求:消息可以从图像中被接收方解码出来,但攻击者很难区分哪些图像包含信息。
    • 关键点:隐蔽性,难以被检测。
  • 数字水印 (Digital Watermarking)

    • 目标:主要用于版权保护和身份认证。
    • 要求:即便图像经过压缩、裁剪、模糊等破坏,仍能正确恢复水印信息。
    • 关键点:鲁棒性,保证信息可恢复。

传统方法多依赖人工设计特征,如:

  • 修改像素的 最低有效位 (LSB)
  • 频域的低频部分嵌入信息。

这些方法在特定场景下有效,但适应性较差。 HiDDeN 则提出了全新的思路:利用 端到端可训练的卷积神经网络 替代传统手工特征,实现更强的 灵活性鲁棒性


二、核心思想

HiDDeN 将数据隐藏任务设计为一个 可微分的端到端管道,通过深度学习来自动学习嵌入策略:

1. 编码器 (Encoder)

  • 使用多层卷积提取封面图的特征。
  • 将消息向量扩展成与图像相同空间维度的“消息体积”,与特征拼接。
  • 最终生成含密图,保证与封面图在视觉上接近。

2. 噪声层 (Noise Layer)

在训练过程中引入失真,模拟现实场景:

  • Dropout / Cropout:随机替换像素或区域。
  • Crop:保留图像的一部分,裁剪其余部分。
  • Gaussian Blur:模拟图像模糊。
  • JPEG Mask / JPEG Drop:近似真实 JPEG 压缩的可微方法,保证训练过程中梯度可传播。

3. 解码器 (Decoder)

  • 多层卷积提取失真图像特征。
  • 使用全局平均池化获取消息相关信息。
  • 最终通过线性层输出消息位。
  • 解码器支持输入大小变化,因此对裁剪等操作具有适应性。

4. 对抗鉴别器 (Adversary)

  • 结构类似解码器。
  • 输出一个二分类概率:判断图像是否含密。
  • 通过对抗训练提升含密图的隐蔽性,降低被检测概率。

这种设计使模型能够在容量(信息嵌入量)隐蔽性(难以检测)鲁棒性(抗失真能力) =三方面取得平衡。


三、代码实现

实现了一个简化版 HiDDeN框架在CIFAR-10上的水印嵌入实验,用于在CIFAR-10图像中嵌入和恢复二进制水印消息,并验证其在多种噪声条件下的鲁棒性,以下文件为main.py文件完整程序代码。

import argparse, math, os
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import datasets, transforms, utils# ------------------------------
# Utils
# ------------------------------
def psnr(img1, img2, eps=1e-8):# img in [0,1]mse = F.mse_loss(img1, img2, reduction='mean').item()if mse < eps: return 99.0return 10.0 * math.log10(1.0 / mse)def make_gaussian_kernel(ks=5, sigma=1.0, device="cpu"):ax = torch.arange(ks, dtype=torch.float32) - (ks - 1) / 2.0xx, yy = torch.meshgrid(ax, ax, indexing="ij")kernel = torch.exp(-(xx**2 + yy**2) / (2 * sigma**2))kernel = kernel / kernel.sum()return kernel.to(device)# ------------------------------
# Noise layers (Identity / PixelDropout / GaussianBlur / Combined)
# ------------------------------
class NoiseLayer(nn.Module):def __init__(self, kind="identity", p=0.3, gs_ks=5, gs_sigma=1.0):super().__init__()self.kind = kindself.p = pself.gs_ks = gs_ksself.gs_sigma = gs_sigmaself.register_buffer("gs_kernel", torch.empty(0))  # init later per devicedef forward(self, ico, ien):if self.kind == "identity":return ienelif self.kind == "dropout":# pixel-wise mix: with prob p use cover pixel, else use encoded pixelif not self.training:# at test-time keep the same behavior for robustness evalpassmask = (torch.rand_like(ien[:, :1, :, :]) < self.p).float()  # [B,1,H,W]return mask * ico + (1.0 - mask) * ienelif self.kind == "gaussian":# depthwise gaussian blurif self.gs_kernel.numel() == 0 or self.gs_kernel.device != ien.device:k = make_gaussian_kernel(self.gs_ks, self.gs_sigma, ien.device)self.gs_kernel = k[None, None, :, :]  # [1,1,ks,ks]padding = self.gs_ks // 2C = ien.size(1)weight = self.gs_kernel.expand(C, 1, self.gs_ks, self.gs_ks)return F.conv2d(ien, weight, bias=None, stride=1, padding=padding, groups=C)elif self.kind == "combined":# randomly pick one per batchchoice = torch.randint(0, 3, (1,), device=ien.device).item()if choice == 0:return self.forward(ico, ien)  # combined -> fallthrough (avoid recursion)elif choice == 1:# simulate dropout with p uniformly sampled around target pp = float(torch.empty(1).uniform_(self.p*0.8, min(0.95, self.p*1.2)))mask = (torch.rand_like(ien[:, :1, :, :]) < p).float()return mask * ico + (1.0 - mask) * ienelse:# gaussian with slight sigma jittersigma = float(torch.empty(1).uniform_(max(0.5, self.gs_sigma*0.6),self.gs_sigma*1.5))ks = self.gs_ksk = make_gaussian_kernel(ks, sigma, ien.device)weight = k[None, None, :, :].expand(ien.size(1), 1, ks, ks)return F.conv2d(ien, weight, stride=1, padding=ks//2, groups=ien.size(1))else:return ien# ------------------------------
# Encoder / Decoder
# ------------------------------
class ConvBNReLU(nn.Module):def __init__(self, c_in, c_out, k=3, s=1, p=1):super().__init__()self.block = nn.Sequential(nn.Conv2d(c_in, c_out, k, s, p, bias=False),nn.BatchNorm2d(c_out),nn.ReLU(inplace=True),)def forward(self, x): return self.block(x)class Encoder(nn.Module):"""Input: cover image Ico [B,3,H,W], message bits M [B,L] in {0,1}Output: encoded image Ien [B,3,H,W] (clamped to [0,1])"""def __init__(self, L, img_ch=3, base=64):super().__init__()self.L = Lself.stem = nn.Sequential(ConvBNReLU(img_ch, base),ConvBNReLU(base, base),ConvBNReLU(base, base),ConvBNReLU(base, base),)# after concat([feat, msg_volume, image]), reduce to base then to 3self.fuse = ConvBNReLU(base + L + img_ch, base)self.to_img = nn.Conv2d(base, img_ch, kernel_size=1, stride=1, padding=0)def forward(self, ico, m_bits):B, C, H, W = ico.size()feat = self.stem(ico)# replicate bits spatially: [B,L] -> [B,L,H,W]m = m_bits.view(B, self.L, 1, 1).float().expand(B, self.L, H, W)x = torch.cat([feat, m, ico], dim=1)x = self.fuse(x)delta = self.to_img(x)ien = torch.clamp(delta, 0.0, 1.0)  # directly predict encoded image (simple & stable)return ienclass Decoder(nn.Module):"""Input: possibly noised image Ino [B,3,H,W]Output: logits over bits [B,L]"""def __init__(self, L, img_ch=3, base=64):super().__init__()self.L = Lself.body = nn.Sequential(ConvBNReLU(img_ch, base),ConvBNReLU(base, base),ConvBNReLU(base, base),ConvBNReLU(base, base),ConvBNReLU(base, base),ConvBNReLU(base, base),)self.head = nn.Sequential(ConvBNReLU(base, L),   # produce L feature maps)self.fc = nn.Linear(L, L)def forward(self, ino):x = self.body(ino)x = self.head(x)                    # [B,L,H,W]x = F.adaptive_avg_pool2d(x, 1)     # [B,L,1,1]x = x.view(x.size(0), self.L)       # [B,L]logits = self.fc(x)                 # [B,L]return logits# ------------------------------
# Training / Evaluation
# ------------------------------
def train(args):device = torch.device("cuda" if torch.cuda.is_available() and not args.cpu else "cpu")# Data: CIFAR-10 [0,1]tfm = transforms.ToTensor()train_set = datasets.CIFAR10(root="./data", train=True, download=True, transform=tfm)test_set  = datasets.CIFAR10(root="./data", train=False, download=True, transform=tfm)train_loader = DataLoader(train_set, batch_size=args.batch_size, shuffle=True, num_workers=2, pin_memory=True)test_loader  = DataLoader(test_set,  batch_size=args.batch_size, shuffle=False, num_workers=2, pin_memory=True)encoder = Encoder(L=args.L).to(device)decoder = Decoder(L=args.L).to(device)if args.noise == "identity":noise = NoiseLayer("identity")elif args.noise == "dropout":noise = NoiseLayer("dropout", p=args.drop_p)elif args.noise == "gaussian":noise = NoiseLayer("gaussian", gs_ks=args.gs_ks, gs_sigma=args.gs_sigma)else:noise = NoiseLayer("combined", p=args.drop_p, gs_ks=args.gs_ks, gs_sigma=args.gs_sigma)noise = noise.to(device)opt = torch.optim.Adam(list(encoder.parameters())+list(decoder.parameters()), lr=args.lr)bce = nn.BCEWithLogitsLoss()mse = nn.MSELoss()os.makedirs(args.out_dir, exist_ok=True)for epoch in range(1, args.epochs+1):encoder.train(); decoder.train(); noise.train()run_msg_loss = run_img_loss = run_acc = 0.0for imgs, _ in train_loader:imgs = imgs.to(device)B = imgs.size(0)# random bits per imagem_bits = torch.randint(0, 2, (B, args.L), device=device)# encodeien = encoder(imgs, m_bits)# noiseino = noise(imgs, ien)# decodelogits = decoder(ino)# lossesmsg_loss = bce(logits, m_bits.float())img_loss = mse(ien, imgs)loss = msg_loss + args.lambda_img * img_lossopt.zero_grad()loss.backward()opt.step()with torch.no_grad():pred = (torch.sigmoid(logits) > 0.5).long()acc = (pred == m_bits).float().mean().item()run_msg_loss += msg_loss.item() * Brun_img_loss += img_loss.item() * Brun_acc      += acc * Bn = len(train_loader.dataset)print(f"[Epoch {epoch}] msg_loss={run_msg_loss/n:.4f}  img_loss={run_img_loss/n:.5f}  bit_acc={run_acc/n:.4f}")# quick eval on test set + save a visualizationif epoch % args.eval_every == 0:test_bit_acc, test_psnr = evaluate(encoder, decoder, noise, test_loader, device)print(f"  -> Test bit_acc={test_bit_acc:.4f}  PSNR(cover,encoded)={test_psnr:.2f} dB")# dump a small griddump_examples(encoder, decoder, noise, test_loader, device, args.out_dir, epoch)torch.save({"encoder": encoder.state_dict(),"decoder": decoder.state_dict()}, os.path.join(args.out_dir, "ckpt.pt"))print("Training done. Checkpoints & samples saved to:", args.out_dir)@torch.no_grad()
def evaluate(encoder, decoder, noise, loader, device):encoder.eval(); decoder.eval(); noise.eval()acc_sum, psnr_sum, cnt = 0.0, 0.0, 0for imgs, _ in loader:imgs = imgs.to(device)B = imgs.size(0)m_bits = torch.randint(0, 2, (B, decoder.L), device=device)ien = encoder(imgs, m_bits)ino = noise(imgs, ien)logits = decoder(ino)pred = (torch.sigmoid(logits) > 0.5).long()acc = (pred == m_bits).float().mean().item()acc_sum += acc * Bpsnr_sum += psnr(imgs, ien) * Bcnt += Breturn acc_sum/cnt, psnr_sum/cnt@torch.no_grad()
def dump_examples(encoder, decoder, noise, loader, device, out_dir, epoch):encoder.eval(); decoder.eval(); noise.eval()imgs, _ = next(iter(loader))imgs = imgs.to(device)[:8]B = imgs.size(0)m_bits = torch.randint(0, 2, (B, decoder.L), device=device)ien = encoder(imgs, m_bits)ino = noise(imgs, ien)# save gridsutils.save_image(imgs,  os.path.join(out_dir, f"epoch{epoch:03d}_cover.png"),  nrow=4)utils.save_image(ien,   os.path.join(out_dir, f"epoch{epoch:03d}_encoded.png"), nrow=4)utils.save_image(ino,   os.path.join(out_dir, f"epoch{epoch:03d}_noised.png"),  nrow=4)# print first sample bits for sanitylogits = decoder(ino)pred = (torch.sigmoid(logits) > 0.5).long()print("[viz] sample#0 GT bits:", m_bits[0].tolist())print("[viz] sample#0 PR bits:", pred[0].tolist())def main():parser = argparse.ArgumentParser()parser.add_argument("--epochs", type=int, default=5)parser.add_argument("--batch-size", type=int, default=128)parser.add_argument("--L", type=int, default=30, help="number of bits to embed")parser.add_argument("--noise", type=str, default="combined",choices=["identity","dropout","gaussian","combined"])parser.add_argument("--drop-p", type=float, default=0.3)parser.add_argument("--gs-ks", type=int, default=5)parser.add_argument("--gs-sigma", type=float, default=1.0)parser.add_argument("--lambda-img", type=float, default=0.7, help="weight for image MSE")parser.add_argument("--lr", type=float, default=1e-3)parser.add_argument("--eval-every", type=int, default=1)parser.add_argument("--out-dir", type=str, default="runs_hidden_cifar10")parser.add_argument("--cpu", action="store_true")args = parser.parse_args()train(args)if __name__ == "__main__":main()

目录结构为:

Hidden
├── data
│   └── cifar-10-python.tar.gz
└── main.py

运行命令为:

# 仅保真,不加噪声(容量/保密取向)
python main.py --epochs 5 --noise identity# 针对像素级 Dropout 的鲁棒水印训练
python main.py --epochs 5 --noise dropout --drop-p 0.3# 混合扰动(Dropout/高斯 模糊 随机采样),更通用
python main.py --epochs 5 --noise combined

四、关键代码与论文公式对应

解析论文的核心公式,并将其与 main.py 代码实现逐一对应。


1. Encoder 对应公式

论文公式 (1):
Ien=Eθ(Ico,M) I_{en} = E_\theta(I_{co}, M) Ien=Eθ(Ico,M)

  • 含义:Encoder EθE_\thetaEθ 接收原始图像 IcoI_{co}Ico 与比特消息 MMM,输出带水印的图像 IenI_{en}Ien.

  • 代码对应

ien = encoder(imgs, m_bits)   # (公式1)

2. 噪声层对应公式

论文公式 (2):
Ino=N(Ico,Ien) I_{no} = N(I_{co}, I_{en}) Ino=N(Ico,Ien)

  • 含义:在 Encoder 和 Decoder 之间插入噪声层 NNN,输出失真后的图像 InoI_{no}Ino.

  • 代码对应

ino = noise(imgs, ien)   # (公式2)

3. Decoder 对应公式

论文公式 (3):
M′=Dϕ(Ino) M' = D_\phi(I_{no}) M=Dϕ(Ino)

  • 含义:Decoder DϕD_\phiDϕ 接收 InoI_{no}Ino,输出预测消息 M′M'M.

  • 代码对应

logits = decoder(ino)   # (公式3)

4. 损失函数对应公式

论文总损失公式(4):
L=λI⋅LI(Ico,Ien)+λM⋅LM(M,M′)+λA⋅LA \mathcal{L} = \lambda_I \cdot \mathcal{L}_I(I_{co}, I_{en}) + \lambda_M \cdot \mathcal{L}_M(M, M') + \lambda_A \cdot \mathcal{L}_A L=λILI(Ico,Ien)+λMLM(M,M)+λALA

  • 代码对应
msg_loss = bce(logits, m_bits.float())   # L_M
img_loss = mse(ien, imgs)                # L_I
loss = msg_loss + args.lambda_img * img_loss  # 总损失 (λ_A=0)

5. 评价指标对应公式

  • Bit Accuracy
    Acc=1L∑i=1L1(Mi=Mi′) Acc = \frac{1}{L} \sum_{i=1}^L 1(M_i = M'_i) Acc=L1i=1L1(Mi=Mi)
    代码:
acc = (pred == m_bits).float().mean().item()
  • PSNR
    PSNR(Ico,Ien)=10⋅log⁡10(1/MSE(Ico,Ien)) \text{PSNR}(I_{co}, I_{en}) = 10 \cdot \log_{10}(1/\text{MSE}(I_{co}, I_{en})) PSNR(Ico,Ien)=10log10(1/MSE(Ico,Ien))

代码:

def psnr(img1, img2, eps=1e-8):mse = F.mse_loss(img1, img2).item()return 10.0 * math.log10(1.0 / mse)

  • 公式 (1) Ien=Eθ(Ico,M)I_{en} = E_\theta(I_{co}, M)Ien=Eθ(Ico,M)encoder.forward
  • 公式 (2) Ino=N(Ico,Ien)I_{no} = N(I_{co}, I_{en})Ino=N(Ico,Ien)noise.forward
  • 公式 (3) M′=Dϕ(Ino)M' = D_\phi(I_{no})M=Dϕ(Ino)decoder.forward
  • 公式 (4) 总损失 → msg_loss + λ * img_loss
  • 指标 Bit Accuracy & PSNR → acc, psnr()

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实验室服务器配置|实验室多人共享GPU|通过Docker实现Linux系统多用户隔离与安全防控

利用实验室服务器跑实验的时候&#xff0c;通常就是两种方案&#xff0c;一个是向日葵远程桌面进行操作&#xff0c;一个是通过ssh进行连接&#xff0c;用ssh的话&#xff0c;一般服务器都在内网&#xff08;例如校园网&#xff09;&#xff0c;是无法在公网&#xff08;不在校…

2019考研数学(二)真题

一、选择题 (1) (2) (3) (4) 遗漏点&#xff1a;由通解知特解&#xff0c;特解代入微分方程 (5) ★记住这个题&#xff0c;用的泰勒展开(6) (7) 遗忘点&#xff1a; ★伴随矩阵的秩与原矩阵秩的关系&#xff1a; (8) 错误点&#xff1a;粗心 二、填空题 (9) 易混淆点&#xff…

10 分钟上手 ECharts:从“能跑”到“生产级”的完整踩坑之旅

10 分钟上手 ECharts&#xff1a;从“能跑”到“生产级”的完整踩坑笔记 如果你也曾 复制了官方 Demo 却不知道怎么拆、窗口一拉伸图表就变形、切换标签页后内存暴涨——这篇博客就是为你写的。 我会用 6 个递进版本 的源码&#xff0c;带你把一张 最简柱状图 逐步进化成 可销毁…

二级缓存在实际项目中的应用

二级缓存在项目中的应用 目录 1. 二级缓存简介2. 应用场景3. 重难点分析4. 结合SpringBoot使用5. 最佳实践与案例6. 总结 1. 二级缓存简介 1.1 什么是二级缓存 二级缓存&#xff08;Second-Level Cache&#xff09; 是Hibernate框架中的一个重要特性&#xff0c;它提供了应…

深入浅出CRC校验:从数学原理到单周期硬件实现 (2)CRC数学多项式基础

数学的优雅&#xff1a;剖开CRC的多项式除法核心看似复杂的CRC校验&#xff0c;其核心建立在优雅的数学基础之上。本文将为您揭开CRC算法的数学面纱&#xff0c;让您真正理解多项式除法的精妙之处。模2运算&#xff1a;CRC世界的特殊算术 CRC计算建立在一种特殊的代数系统上——…

软考初级有没有必要考?

对正在学习相关专业的学生或者是行业新人&#xff0c;这篇文章从软考初级的含义、适合哪些人考、考试难度等方面解答&#xff0c;帮助你判断要不要报考。一、软考初级是什么&#xff1f; 软考初级是软考体系里面的基础级别&#xff0c;主要面向在校大学生或是IT行业新人&#x…

11 Prompt 工程进阶:Few-shot 与 Chain-of-Thought

11 Prompt 工程进阶&#xff1a;Few-shot 与 Chain-of-Thought 前10节总结 & 后10节展望 在前 10 节&#xff0c;我们已经完成了 AI 产品经理的入门阶段&#xff1a; 1–3&#xff1a;理解了大模型的基本概念、Token、Prompt 基础&#xff1b;4–5&#xff1a;体验了本地部…

ARM1.(ARM体系结构)

1.基本概念嵌入式:以应用为心&#xff0c;以计算机技术为础&#xff0c;软便件可被的专用计算机系统。计算机系统的软件基本组成: 系统软件、应用软件。计算机系统的硬件基本组成&#xff1a;运算器、控制器、存诸器、输入设备、输出设备日常生活中遇到的专业术语&#xff1a…

Django全栈班v1.01 Python简介与特点 20250910

从零开始的Python编程之旅 “人生苦短&#xff0c;我用Python。”这不仅仅是Python程序员的口头禅&#xff0c;更是对Python强大能力的最好诠释&#xff01;&#xff01;&#xff01; 为什么全世界有超过1500万开发者选择Python&#xff1f; 为什么Python连续多年蝉联最受欢…

【WebApi】什么情况开启如何开启缓存

在 ASP.NET Core WebAPI 中开启缓存是优化性能、减少服务器负载和提升用户体验的非常重要的手段。但并非所有情况都适合开启缓存。 下面我将从 “什么情况下开启” 和 “如何开启” 两个方面为你详细解释。 一、什么情况下应该开启缓存? 总的来说,缓存适用于 “变化不频繁但…

Go语言类型断言全解析

类型断言的基本概念类型断言(Type Assertion)是Go语言中用于检查接口值底层具体类型的机制。它本质上是一种运行时类型检查的操作&#xff0c;允许程序在运行时判断接口变量是否持有特定的类型值&#xff0c;并提取该类型的值。这是Go语言类型系统中的一个重要特性&#xff0c;…

大模型在题目生成中的安全研究:攻击方法与防御机制

大模型在题目生成中的安全研究&#xff1a;攻击方法与防御机制 文章目录大模型在题目生成中的安全研究&#xff1a;攻击方法与防御机制一、引言二、大模型在题目生成中的安全漏洞与攻击方法2.1 大模型在题目生成中的安全漏洞分析2.1.1 训练数据相关漏洞2.1.2 模型架构与特性相关…

跟做springboot尚品甄选项目(二)

登录功能的书写 后端接口的书写 &#xff08;1&#xff09;创建配置文件 粘贴这两个文件&#xff08;E:\project\AllProJect\Shangpin Selection\项目材料素材\资料\资料\03-配置文件&#xff09; 在spzx-manager服务的src/resources目录下创建application.yml、application-…

前后端接口调试提效:Postman + Mock Server 的工作流

前后端接口调试提效&#xff1a;Postman Mock Server 的工作流 &#x1f31f; Hello&#xff0c;我是摘星&#xff01; &#x1f308; 在彩虹般绚烂的技术栈中&#xff0c;我是那个永不停歇的色彩收集者。 &#x1f98b; 每一个优化都是我培育的花朵&#xff0c;每一个特性都是…

大带宽香港云服务器在数据传输速度上有何优势?

为方便站长快速部署网站、优化用户访问体验&#xff0c;当下众多实力强劲的香港数据中心&#xff0c;均推出了大带宽云服务器产品。不过&#xff0c;市面上不少数据中心虽宣称提供 “专属大带宽”&#xff0c;但其线路配置中&#xff0c;国际线路占比高、绕行链路多&#xff0c…

HT862 智能音频功率放大器:为便携音频设备打造高效稳定的音质解决方案

在蓝牙音箱、智能手机、便携式游戏机等设备的设计中&#xff0c;音频功率放大器是决定音质表现、续航能力与使用稳定性的关键部件。一款优质的音频功放&#xff0c;不仅需要输出足够的功率以满足清晰响亮的听觉需求&#xff0c;还需在能效、温控、适配性上达到平衡&#xff0c;…

HarmonyOS-ArkUI Web控件基础铺垫7-HTTP SSL认证图解 及 Charles抓包原理 及您为什么配置对了也抓不到数据

HarmonyOS-ArkUI Web控件基础铺垫6--TCP协议- 流量控制算法与拥塞控制算法 HarmonyOS-ArkUI Web控件基础铺垫5--TCP协议- 动画展示超时重传&#xff0c;滑动窗口&#xff0c;快速重传 HarmonyOS-ArkUI Web控件基础铺垫4--TCP协议- 断联-四次挥手解析 HarmonyOS-ArkUI Web控件…

【qt】通过TCP传输json,json里包含图像

主要是使用协议头 发送方connect(m_pDetectWorker, &DetectionWorker::sig_detectImg, this, [](const QJsonObject &json){// 转换为JSON数据QJsonDocument doc(json);QByteArray jsonData doc.toJson(QJsonDocument::Compact);// 构建增强协议头struct EnhancedHead…

四,基础开发工具(下)

4.5自动构建make/Makefile4.5.1基本使用1示例2进一步解释3实践4最佳实践4.6练习&#xff1a;进度条4.6.1倒计时4.6.2进度条version14.6.2进度条version24.7版本控制器Git4.7.1git操作1操作一次&#xff0c;以后不愁2经典"三件套"3常用4版本回退4.7.2小结4.5自动构建m…

C++基本数据类型的范围

文章目录不同位数的系统下各个类型所占字节数如何存储的我发现我能搜到的相关文章都只讲了这些数据类型的范围是这样的&#xff0c;不说实际的存储情况&#xff0c;当你了解了类型实际是如何存储的&#xff0c;再去记忆这些范围就简单了&#xff0c;所以就有了这篇文章不同位数…