HiDDeN論文解讀與代碼實現

論文: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=λI??LI?(Ico?,Ien?)+λM??LM?(M,M)+λA??LA?

  • 代碼對應
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=L1?i=1L?1(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?)=10?log10?(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;所以就有了這篇文章不同位數…