QRSuperResolutionNet:一種結(jié)構(gòu)感知與識別增強(qiáng)的二維碼圖像超分辨率網(wǎng)絡(luò)(附代碼解析)
QRSuperResolutionNet:一種結(jié)構(gòu)感知與識別增強(qiáng)的二維碼圖像超分辨率網(wǎng)絡(luò)(附代碼解析)
趁著 web開發(fā)課程 期末考試前夕,寫一篇博客。{{{(>_<)}}}
將我最近所做的工作整理一下,同時(shí)該工作已經(jīng)寫成論文,已被ei檢索會議錄取~~~
最近,我一直在回味過去這兩三個(gè)月的項(xiàng)目經(jīng)歷,感慨萬千。
這個(gè)項(xiàng)目我從開始做到現(xiàn)在得有兩三個(gè)月,立項(xiàng)前我也一直在看超分方向以及擴(kuò)散模型方向的論文,看看自己適合什么樣的。后來偶然間,感覺qrcode這個(gè)方向的論文比較少,就嘗試入手。
可從項(xiàng)目開始到結(jié)束,我整個(gè)人一直處于忐忑不安的狀態(tài)。心里就像懸著一塊大石頭,生怕自己花了大把時(shí)間,最后卻一無所獲。畢竟,這件事的成功與否,對我來說充滿了不確定性。當(dāng)我真正開始訓(xùn)練自己的模型時(shí),才發(fā)現(xiàn)事情遠(yuǎn)沒有我想象的那么簡單。很多時(shí)候,我絞盡腦汁加上一些自認(rèn)為很巧妙的模塊,結(jié)果模型的收斂效果、準(zhǔn)確率(ACC)等指標(biāo)卻差得一塌糊涂,甚至還不如原生(native)模型。那一刻,我只能苦笑,心里滿是無奈。
那段時(shí)間,我?guī)缀醣粔旱么贿^氣來。期末考試的壓力如影隨形,課程任務(wù)也堆積如山,而我還要擠出時(shí)間來訓(xùn)練模型。甚至在幾門專業(yè)課考試前的幾個(gè)小時(shí),我還在忙著修改代碼、調(diào)整論文格式。那種忙碌和焦慮,讓我?guī)缀跻罎⒘恕?/p>
好在,最后的結(jié)果還算不錯(cuò),我的論文被一個(gè)EI檢索會議錄取了。雖然我知道EI檢索會議的含金量可能并不算高,但對我來說,這卻是我十幾年學(xué)習(xí)生涯中第一篇真正屬于自己的文章,是我獨(dú)自摸索、獨(dú)自奮斗的成果。沒有依賴任何人,我憑借自己的努力走到了這一步,這份成就感已經(jīng)讓我心滿意足。我也相信這個(gè)不是終點(diǎn),是我的學(xué)習(xí)生涯的新的起點(diǎn)。
在代碼完成后,為了找到一個(gè)時(shí)間合適、能趕上進(jìn)度的會議,我又花了很長時(shí)間搜尋。然后,按照會議的要求修改論文格式,最讓我頭疼的就是用Word修改公式了。那一刻,我簡直想抓狂,但又不得不咬著牙堅(jiān)持下去。(っ °Д °;)っ
時(shí)光飛逝,轉(zhuǎn)眼間我已經(jīng)大三了,站在人生的又一個(gè)十字路口,回首過往三年的大學(xué)生活,心里五味雜陳。這三年,就像一場驚心動魄的冒險(xiǎn),我在里面盡情嘗試、探索,但也免不了碰得頭破血流。我收獲了成長,積累了經(jīng)驗(yàn),也吸取了教訓(xùn),可同時(shí),也有不少后悔的地方,很多時(shí)候稀里糊涂地做著無用功,甚至到現(xiàn)在還不清楚自己真正想要什么。但即便如此,我也不忍心太苛責(zé)自己。畢竟,我一個(gè)人從山東魯西南地區(qū)的鄉(xiāng)鎮(zhèn)小學(xué),一路跌跌撞撞走到城里的初中,再到高中,在那所每年1600多人里,僅有400多人能本科上線,兩三百人能有本科可讀的高中里,一路拼殺,好不容易才走到了今天。這一路走來,每一步都寫滿了艱辛,可我也已經(jīng)走了很遠(yuǎn)了。
寫了很多,又刪了很多,千言萬語,很多話到了嘴邊又咽了回去。我只希望自己能在接下來這一年多的本科生涯里,繼續(xù)做好自己的本職工作,繼續(xù)努力。
廢話不多說,下面就是正式的講解階段。
希望各位大佬多多提提意見,共同進(jìn)步!
也希望可以給項(xiàng)目點(diǎn)個(gè)star,謝謝各位。~~~
項(xiàng)目地址:https://github.com/zhongzhengli13/QRSuperResolutionNet-for-qrcode
1. 背景介紹
二維碼圖像在實(shí)際應(yīng)用中常常由于壓縮、模糊、低分辨率采集等原因?qū)е沦|(zhì)量下降,直接影響解碼準(zhǔn)確率。傳統(tǒng)圖像超分方法主要關(guān)注視覺質(zhì)量,但對二維碼這類結(jié)構(gòu)化強(qiáng)、容錯(cuò)模式敏感的圖像來說,僅提升 PSNR 和 SSIM 并不足夠。因此,我在ESRGAN的基礎(chǔ)上進(jìn)行改進(jìn),設(shè)計(jì)了一種融合了 殘差密集模塊(RRDB)、通道注意力機(jī)制(SEBlock) 與 Transformer 編碼器 的二維碼圖像超分辨率模型 —— QRSuperResolutionNet(簡稱:QRSRNet),旨在同時(shí)提升圖像質(zhì)量與識別魯棒性。
2. 網(wǎng)絡(luò)設(shè)計(jì)思路
QRSuperResolutionNet 的目標(biāo)是將輸入的 64×64 灰度二維碼圖像重建為清晰度和結(jié)構(gòu)都更優(yōu)的 256×256 圖像,同時(shí)最大限度提升下游識別工具(如 pyzbar)的解碼成功率。
核心結(jié)構(gòu)設(shè)計(jì)包括:
- 主干特征提取網(wǎng)絡(luò):堆疊多個(gè)帶有通道注意力的 RRDB 模塊;
- Transformer 編碼器:用于建模二維碼圖案中的長程結(jié)構(gòu)依賴;
- 雙分支上采樣:
- 主分支采用 PixelShuffle 逐步上采樣;
- 輔助分支采用 Bicubic 插值形成跳躍連接,增強(qiáng)穩(wěn)定性;
- 多級損失函數(shù)融合:訓(xùn)練過程中結(jié)合 L1 損失、感知損失和識別損失,提高圖像質(zhì)量與可識別性。
3. 模塊結(jié)構(gòu) && 代碼詳解
以下為模型中各個(gè)關(guān)鍵模塊的 PyTorch 實(shí)現(xiàn)與功能解析。
3.1 SEBlock:通道注意力機(jī)制
SEBlock 引入了通道維度的顯著性建模,強(qiáng)化關(guān)鍵通道響應(yīng),減弱冗余信息。
class SEBlock(nn.Module):
def __init__(self, channels, reduction=16):
super(SEBlock, self).__init__()
self.pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channels, channels // reduction),
nn.ReLU(inplace=True),
nn.Linear(channels // reduction, channels),
nn.Sigmoid()
)
def forward(self, x):
b, c, _, _ = x.size()
y = self.pool(x).view(b, c)
y = self.fc(y).view(b, c, 1, 1)
return x * y
3.2 ResidualDenseBlock:殘差密集塊
源自 ESRGAN,結(jié)合了殘差連接與密集連接,提升了特征流動與重用能力。
class ResidualDenseBlock(nn.Module):
def __init__(self, channels=64, growth_channels=32):
super().__init__()
self.conv1 = nn.Conv2d(channels, growth_channels, 3, 1, 1)
self.conv2 = nn.Conv2d(channels + growth_channels,
growth_channels, 3, 1, 1)
self.conv3 = nn.Conv2d(
channels + 2 * growth_channels, growth_channels, 3, 1, 1)
self.conv4 = nn.Conv2d(
channels + 3 * growth_channels, growth_channels, 3, 1, 1)
self.conv5 = nn.Conv2d(
channels + 4 * growth_channels, channels, 3, 1, 1)
self.lrelu = nn.LeakyReLU(0.2, inplace=True)
def forward(self, x):
x1 = self.lrelu(self.conv1(x))
x2 = self.lrelu(self.conv2(torch.cat([x, x1], 1)))
x3 = self.lrelu(self.conv3(torch.cat([x, x1, x2], 1)))
x4 = self.lrelu(self.conv4(torch.cat([x, x1, x2, x3], 1)))
x5 = self.conv5(torch.cat([x, x1, x2, x3, x4], 1))
return x + 0.2 * x5
3.3 RRDB:殘差-in-殘差模塊
RRDB 模塊由三個(gè) ResidualDenseBlock 堆疊構(gòu)成,并在末尾加入 SEBlock,用于加強(qiáng)局部建模能力。
class RRDB(nn.Module):
def __init__(self, channels):
super().__init__()
self.rdb1 = ResidualDenseBlock(channels)
self.rdb2 = ResidualDenseBlock(channels)
self.rdb3 = ResidualDenseBlock(channels)
self.se = SEBlock(channels)
def forward(self, x):
out = self.rdb1(x)
out = self.rdb2(out)
out = self.rdb3(out)
out = x + 0.2 * out
out = self.se(out)
return out
3.4 TransformerBlock:結(jié)構(gòu)感知模塊
利用多頭注意力機(jī)制對整個(gè)圖像的結(jié)構(gòu)進(jìn)行建模,引入非局部信息,增強(qiáng)上下文一致性。
class TransformerBlock(nn.Module):
def __init__(self, dim, num_heads=4, mlp_ratio=2.0, dropout=0.1):
super().__init__()
self.norm1 = nn.LayerNorm(dim)
self.attn = nn.MultiheadAttention(
dim, num_heads, dropout=dropout, batch_first=True)
self.norm2 = nn.LayerNorm(dim)
self.mlp = nn.Sequential(
nn.Linear(dim, int(dim * mlp_ratio)),
nn.ReLU(inplace=True),
nn.Linear(int(dim * mlp_ratio), dim)
)
def forward(self, x):
b, c, h, w = x.shape
x_flat = x.view(b, c, -1).permute(0, 2, 1) # B x N x C
x_norm = self.norm1(x_flat)
attn_out, _ = self.attn(x_norm, x_norm, x_norm)
x = x_flat + attn_out
x = x + self.mlp(self.norm2(x))
x = x.permute(0, 2, 1).view(b, c, h, w)
return x
3.5 主網(wǎng)絡(luò) QRSuperResolutionNet
主結(jié)構(gòu)由以下幾部分組成:
entry:輸入卷積body:多個(gè) RRDB 構(gòu)成的特征提取主干transformer:結(jié)構(gòu)感知編碼器upsample:PixelShuffle 上采樣兩次,實(shí)現(xiàn) 4× 分辨率提升skip_up:Bicubic 上采樣跳躍連接,提升圖像穩(wěn)定性exit:輸出卷積,生成超分圖像
class QRSuperResolutionNet(nn.Module):
def __init__(self, in_channels=1, out_channels=1, base_channels=64, num_blocks=5):
super().__init__()
self.entry = nn.Conv2d(in_channels, base_channels, 3, 1, 1)
# 主體 RRDB 模塊
self.body = nn.Sequential(*[RRDB(base_channels)
for _ in range(num_blocks)])
# Transformer 編碼模塊
self.transformer = TransformerBlock(dim=base_channels)
# 上采樣跳躍分支
self.skip_up = nn.Sequential(
nn.Upsample(scale_factor=4, mode='bicubic', align_corners=False),
nn.Conv2d(in_channels, out_channels, 3, 1, 1)
)
# PixelShuffle 上采樣
self.upsample = nn.Sequential(
nn.Conv2d(base_channels, base_channels * 4, 3, 1, 1),
nn.PixelShuffle(2),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(base_channels, base_channels * 4, 3, 1, 1),
nn.PixelShuffle(2),
nn.LeakyReLU(0.2, inplace=True)
)
self.exit = nn.Conv2d(base_channels, out_channels, 3, 1, 1)
def forward(self, x):
feat = self.entry(x)
feat = self.body(feat)
feat = self.transformer(feat) # 加入 transformer 結(jié)構(gòu)
feat = self.upsample(feat)
out = self.exit(feat)
# 融合 Bicubic 分支輸出
skip = self.skip_up(x)
out = out + skip
return torch.clamp(out, 0.0, 1.0)
4. 模型測試代碼
快速測試模型輸出尺寸是否符合預(yù)期:
if __name__ == "__main__":
model = QRSuperResolutionNet()
dummy_input = torch.randn(1, 1, 64, 64)
output = model(dummy_input)
print("輸出尺寸:", output.shape) # 預(yù)期:(1, 1, 256, 256)
5.整體model代碼展示
import torch
import torch.nn as nn
import torch.nn.functional as F
class SEBlock(nn.Module):
def __init__(self, channels, reduction=16):
super(SEBlock, self).__init__()
self.pool = nn.AdaptiveAvgPool2d(1)
self.fc = nn.Sequential(
nn.Linear(channels, channels // reduction),
nn.ReLU(inplace=True),
nn.Linear(channels // reduction, channels),
nn.Sigmoid()
)
def forward(self, x):
b, c, _, _ = x.size()
y = self.pool(x).view(b, c)
y = self.fc(y).view(b, c, 1, 1)
return x * y
class ResidualDenseBlock(nn.Module):
def __init__(self, channels=64, growth_channels=32):
super().__init__()
self.conv1 = nn.Conv2d(channels, growth_channels, 3, 1, 1)
self.conv2 = nn.Conv2d(channels + growth_channels,
growth_channels, 3, 1, 1)
self.conv3 = nn.Conv2d(
channels + 2 * growth_channels, growth_channels, 3, 1, 1)
self.conv4 = nn.Conv2d(
channels + 3 * growth_channels, growth_channels, 3, 1, 1)
self.conv5 = nn.Conv2d(
channels + 4 * growth_channels, channels, 3, 1, 1)
self.lrelu = nn.LeakyReLU(0.2, inplace=True)
def forward(self, x):
x1 = self.lrelu(self.conv1(x))
x2 = self.lrelu(self.conv2(torch.cat([x, x1], 1)))
x3 = self.lrelu(self.conv3(torch.cat([x, x1, x2], 1)))
x4 = self.lrelu(self.conv4(torch.cat([x, x1, x2, x3], 1)))
x5 = self.conv5(torch.cat([x, x1, x2, x3, x4], 1))
return x + 0.2 * x5
class RRDB(nn.Module):
def __init__(self, channels):
super().__init__()
self.rdb1 = ResidualDenseBlock(channels)
self.rdb2 = ResidualDenseBlock(channels)
self.rdb3 = ResidualDenseBlock(channels)
self.se = SEBlock(channels)
def forward(self, x):
out = self.rdb1(x)
out = self.rdb2(out)
out = self.rdb3(out)
out = x + 0.2 * out
out = self.se(out)
return out
class TransformerBlock(nn.Module):
def __init__(self, dim, num_heads=4, mlp_ratio=2.0, dropout=0.1):
super().__init__()
self.norm1 = nn.LayerNorm(dim)
self.attn = nn.MultiheadAttention(
dim, num_heads, dropout=dropout, batch_first=True)
self.norm2 = nn.LayerNorm(dim)
self.mlp = nn.Sequential(
nn.Linear(dim, int(dim * mlp_ratio)),
nn.ReLU(inplace=True),
nn.Linear(int(dim * mlp_ratio), dim)
)
def forward(self, x):
b, c, h, w = x.shape
x_flat = x.view(b, c, -1).permute(0, 2, 1) # B x N x C
x_norm = self.norm1(x_flat)
attn_out, _ = self.attn(x_norm, x_norm, x_norm)
x = x_flat + attn_out
x = x + self.mlp(self.norm2(x))
x = x.permute(0, 2, 1).view(b, c, h, w)
return x
class QRSuperResolutionNet(nn.Module):
def __init__(self, in_channels=1, out_channels=1, base_channels=64, num_blocks=5):
super().__init__()
self.entry = nn.Conv2d(in_channels, base_channels, 3, 1, 1)
# 主體 RRDB 模塊
self.body = nn.Sequential(*[RRDB(base_channels)
for _ in range(num_blocks)])
# Transformer 編碼模塊
self.transformer = TransformerBlock(dim=base_channels)
# 上采樣跳躍分支
self.skip_up = nn.Sequential(
nn.Upsample(scale_factor=4, mode='bicubic', align_corners=False),
nn.Conv2d(in_channels, out_channels, 3, 1, 1)
)
# PixelShuffle 上采樣
self.upsample = nn.Sequential(
nn.Conv2d(base_channels, base_channels * 4, 3, 1, 1),
nn.PixelShuffle(2),
nn.LeakyReLU(0.2, inplace=True),
nn.Conv2d(base_channels, base_channels * 4, 3, 1, 1),
nn.PixelShuffle(2),
nn.LeakyReLU(0.2, inplace=True)
)
self.exit = nn.Conv2d(base_channels, out_channels, 3, 1, 1)
def forward(self, x):
feat = self.entry(x)
feat = self.body(feat)
feat = self.transformer(feat) # 加入 transformer 結(jié)構(gòu)
feat = self.upsample(feat)
out = self.exit(feat)
# 融合 Bicubic 分支輸出
skip = self.skip_up(x)
out = out + skip
return torch.clamp(out, 0.0, 1.0)
# 測試模型尺寸
if __name__ == "__main__":
model = QRSuperResolutionNet()
dummy_input = torch.randn(1, 1, 64, 64)
output = model(dummy_input)
print("輸出尺寸:", output.shape) # 預(yù)期:(1, 1, 256, 256)
6. 實(shí)驗(yàn)結(jié)果與性能對比
在自建的低質(zhì)量二維碼數(shù)據(jù)集上進(jìn)行對比測試:


本方法在圖像質(zhì)量與可識別性上均優(yōu)于對比方法,尤其在嚴(yán)重退化條件下表現(xiàn)穩(wěn)定。
7. 項(xiàng)目開源地址與后續(xù)計(jì)劃
項(xiàng)目完整代碼開源于 GitHub,歡迎 clone、交流與反饋:
GitHub 地址:
https://github.com/zhongzhengli13/QRSuperResolutionNet-for-qrcode
郵箱:878954714@qq.com
未來改進(jìn)方向包括:
- 融合模型壓縮與知識蒸餾以部署到移動端設(shè)備
8. 總結(jié)
QRSuperResolutionNet 是一個(gè)融合結(jié)構(gòu)建模、注意力機(jī)制與識別增強(qiáng)的超分辨率模型,專為低質(zhì)量二維碼圖像設(shè)計(jì),兼顧視覺質(zhì)量與下游可識別性,在實(shí)驗(yàn)中表現(xiàn)優(yōu)異。模型輕量高效,便于落地部署,適合用于智慧零售、物流追溯等領(lǐng)域的二維碼圖像修復(fù)與增強(qiáng)任務(wù)。
如有任何問題,歡迎在博客評論區(qū)或 GitHub 提出,我會積極回復(fù)與維護(hù)。

浙公網(wǎng)安備 33010602011771號