OSCR

LogoXpertNet: a novel lightweight logo classification using deep learning.

Code ↔ Paper

11 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 11 matches
  1. [1] § Methodology › Proposed LogoXpertNet for logo classification › Modified MobileNetv3 for feature extraction ↔ LogoXpertNet Model.py, lines 30 to 60 · score 0.80 · depthwise separable convolutions, pointwise convolution, Bottleneck Block, MobileNetV3, channel · Report a mismatch
  2. [2] § Methodology › Proposed LogoXpertNet for logo classification ↔ LogoXpertNet Model.py, lines 265 to 344 · score 0.73 · refined feature map, frequency domain, channel attention, FA CBAM, DCT, augmentation · Report a mismatch
  3. [3] § Methodology › Proposed LogoXpertNet for logo classification › Proposed hierarchical squeeze-excitation spatial attention block (HSE-SAB) ↔ LogoXpertNet Model.py, lines 199 to 260 · score 0.72 · concatenated pooled features, Hierarchical channel, modulates, ReLU, feature map, sigmoid · Report a mismatch
  4. [4] § Methodology › Proposed LogoXpertNet for logo classification › Proposed hierarchical squeeze-excitation spatial attention block (HSE-SAB) ↔ LogoXpertNet Model.py, lines 199 to 260 · score 0.71 · Hierarchical Squeeze Excitation, hierarchical channel, Spatial Attention Block, HSE SAB, dynamically, dimensions · Report a mismatch
  5. [5] § Methodology › Proposed LogoXpertNet for logo classification › Proposed feature aware convolution block attention module ↔ LogoXpertNet Model.py, lines 265 to 344 · score 0.70 · ReLU, max pooling, Channel attention, FA CBAM, feature map, sigmoid · Report a mismatch
  6. [6] § Methodology › Proposed LogoXpertNet for logo classification ↔ LogoXpertNet Model.py, lines 349 to 482 · score 0.66 · HSE SAB module, CLFF module, FA CBAM, LogoXpertNet, deep, layer · Report a mismatch
  7. [7] § Methodology › Proposed LogoXpertNet for logo classification › Modified MobileNetv3 for feature extraction ↔ LogoXpertNet Model.py, lines 30 to 60 · score 0.65 · depthwise separable convolutions, Bottleneck Block, MobileNetV3 · Report a mismatch
  8. [8] § Methodology › Proposed LogoXpertNet for logo classification › Proposed feature aware convolution block attention module ↔ LogoXpertNet Model.py, lines 10 to 20 · score 0.61 · Discrete Cosine Transform, FFT, DCT · Report a mismatch
  9. [9] § Methodology › Proposed LogoXpertNet for logo classification › Cross-layer feature fusion (CLFF) module ↔ LogoXpertNet Model.py, lines 137 to 194 · score 0.57 · Cross Layer Feature, deep layers, recalibration, feature map, CLFF, Fusion · Report a mismatch
  10. [10] § Methodology › Proposed LogoXpertNet for logo classification › Cross-layer feature fusion (CLFF) module ↔ LogoXpertNet Model.py, lines 137 to 194 · score 0.56 · recalibrated feature map, cross layer feature, fusion, CLFF, module · Report a mismatch
  11. [11] § Methodology › Proposed LogoXpertNet for logo classification ↔ LogoXpertNet Model.py, lines 10 to 20 · score 0.51 · Discrete Cosine Transform, DCT · Report a mismatch

Paper

Loaded from Europe PMC by your browser; OSCR does not store it: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 574 lines · 21 KB · no license · 11 matches

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. import numpy as np
  5. from torchvision.models import mobilenet_v3_small, MobileNet_V3_Small_Weights
  6. from typing import Optional, Tuple
  7. # ==================== Helper Functions ====================
  8. def dct2d(x: torch.Tensor) -> torch.Tensor:
  9. """
  10. Discrete Cosine Transform (DCT) implementation using FFT
  11. Approximation as mentioned in the paper
  12. """
  13. N, C, H, W = x.shape
  14. # Using real part of FFT as approximation of DCT
  15. x_padded = F.pad(x, (0, 0, 0, 0)) # No padding for now
  16. fft_result = torch.fft.fft2(x_padded, dim=(-2, -1))
  17. dct_result = torch.real(fft_result)
  18. return dct_result
  19. def local_avg_pool(x: torch.Tensor, kernel_size: int = 3, stride: int = 1) -> torch.Tensor:
  20. """
  21. Local Average Pooling for HSE-SAB
  22. """
  23. return F.avg_pool2d(x, kernel_size=kernel_size, stride=stride, padding=kernel_size//2)
  24. # ==================== Modified MobileNetV3 Backbone ====================
  25. class BottleneckBlock(nn.Module):
  26. """
  27. Bottleneck Block from MobileNetV3 with depthwise separable convolutions
  28. """
  29. def __init__(self, in_channels: int, out_channels: int, stride: int = 1, expansion_factor: int = 6):
  30. super().__init__()
  31. hidden_channels = in_channels * expansion_factor
  32. self.conv = nn.Sequential(
  33. # Pointwise convolution
  34. nn.Conv2d(in_channels, hidden_channels, 1, 1, 0, bias=False),
  35. nn.BatchNorm2d(hidden_channels),
  36. nn.ReLU6(inplace=True),
  37. # Depthwise convolution
  38. nn.Conv2d(hidden_channels, hidden_channels, 3, stride, 1,
  39. groups=hidden_channels, bias=False),
  40. nn.BatchNorm2d(hidden_channels),
  41. nn.ReLU6(inplace=True),
  42. # Pointwise convolution (linear)
  43. nn.Conv2d(hidden_channels, out_channels, 1, 1, 0, bias=False),
  44. nn.BatchNorm2d(out_channels)
  45. )
  46. self.use_residual = (stride == 1 and in_channels == out_channels)
  47. def forward(self, x: torch.Tensor) -> torch.Tensor:
  48. if self.use_residual:
  49. return x + self.conv(x)
  50. return self.conv(x)
  51. class ModifiedMobileNetV3(nn.Module):
  52. """
  53. Modified MobileNetV3 backbone with Bottleneck and CLFF blocks integrated
  54. """
  55. def __init__(self, num_classes: int = 1000, pretrained: bool = False):
  56. super().__init__()
  57. # Load pretrained MobileNetV3 Small
  58. weights = MobileNet_V3_Small_Weights.DEFAULT if pretrained else None
  59. backbone = mobilenet_v3_small(weights=weights)
  60. # Extract features
  61. self.features = backbone.features
  62. # Remove the last classification layer
  63. self.features = nn.Sequential(*list(self.features.children()))
  64. # Modify with Bottleneck blocks at specific layers
  65. self._modify_with_bottleneck()
  66. # Adaptive pooling
  67. self.avgpool = nn.AdaptiveAvgPool2d(1)
  68. # Store feature dimensions for CLFF
  69. self.feature_dims = {
  70. 'early': 24, # After layer 2 (adjust based on actual architecture)
  71. 'deep': 576 # Final feature dimension
  72. }
  73. def _modify_with_bottleneck(self):
  74. """
  75. Replace some convolution blocks with Bottleneck blocks
  76. """
  77. # Convert sequential to list for modification
  78. layers = list(self.features.children())
  79. # Replace certain layers with Bottleneck blocks
  80. # In practice, you'd replace specific conv blocks based on paper details
  81. # This is a simplified implementation
  82. for i in range(len(layers)):
  83. if isinstance(layers[i], nn.Conv2d) and layers[i].kernel_size == (3, 3):
  84. in_channels = layers[i].in_channels
  85. out_channels = layers[i].out_channels
  86. stride = layers[i].stride[0]
  87. # Create bottleneck block
  88. bottleneck = BottleneckBlock(in_channels, out_channels, stride)
  89. layers[i] = bottleneck
  90. self.features = nn.Sequential(*layers)
  91. def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
  92. """
  93. Returns both early and deep layer features for CLFF
  94. """
  95. # Extract early features (after 2nd block)
  96. early_feat = None
  97. for i, layer in enumerate(self.features):
  98. x = layer(x)
  99. if i == 2: # After second layer
  100. early_feat = x
  101. # Deep features (final output)
  102. deep_feat = x
  103. # Global pooling
  104. deep_feat = self.avgpool(deep_feat)
  105. deep_feat = torch.flatten(deep_feat, 1)
  106. return early_feat, deep_feat
  107. # ==================== CLFF Module ====================
  108. class CLFF(nn.Module):
  109. """
  110. Cross-Layer Feature Fusion Module
  111. """
  112. def __init__(self, early_channels: int, deep_channels: int,
  113. fusion_method: str = 'concat'):
  114. super().__init__()
  115. self.fusion_method = fusion_method
  116. # Squeeze-Excitation layers
  117. total_channels = early_channels + deep_channels if fusion_method == 'concat' else early_channels
  118. self.squeeze = nn.AdaptiveAvgPool2d(1)
  119. # Excitation with two FC layers
  120. reduction_ratio = 16
  121. reduced_channels = max(total_channels // reduction_ratio, 4)
  122. self.excitation = nn.Sequential(
  123. nn.Linear(total_channels, reduced_channels, bias=False),
  124. nn.ReLU(inplace=True),
  125. nn.Linear(reduced_channels, total_channels, bias=False),
  126. nn.Sigmoid()
  127. )
  128. def forward(self, F1: torch.Tensor, F2: torch.Tensor) -> torch.Tensor:
  129. """
  130. Args:
  131. F1: Early layer feature map [B, C1, H, W]
  132. F2: Deep layer feature map [B, C2, H, W]
  133. Returns:
  134. Recalibrated feature map
  135. """
  136. # Step 1: Feature Fusion
  137. if self.fusion_method == 'concat':
  138. F_fusion = torch.cat([F1, F2], dim=1) # [B, C1+C2, H, W]
  139. elif self.fusion_method == 'add':
  140. # Ensure same dimensions for addition
  141. if F1.shape != F2.shape:
  142. F2_resized = F.interpolate(F2, size=F1.shape[-2:], mode='bilinear', align_corners=False)
  143. F_fusion = F1 + F2_resized
  144. else:
  145. F_fusion = F1 + F2
  146. else:
  147. raise ValueError(f"Unknown fusion method: {self.fusion_method}")
  148. # Step 2: Squeeze Operation (Global Average Pooling)
  149. z = self.squeeze(F_fusion) # [B, C, 1, 1]
  150. z = z.flatten(1) # [B, C]
  151. # Step 3: Excitation Operation
  152. a = self.excitation(z) # [B, C]
  153. a = a.view(a.shape[0], a.shape[1], 1, 1) # [B, C, 1, 1]
  154. # Step 4: Recalibration
  155. F_out = F_fusion * a.expand_as(F_fusion)
  156. return F_out
  157. # ==================== HSE-SAB Module ====================
  158. class HSE_SAB(nn.Module):
  159. """
  160. Hierarchical Squeeze-Excitation Spatial Attention Block
  161. """
  162. def __init__(self, in_channels: int, reduction_ratio: int = 16,
  163. local_pool_size: int = 3):
  164. super().__init__()
  165. self.in_channels = in_channels
  166. # Multi-scale pooling
  167. self.global_avg_pool = nn.AdaptiveAvgPool2d(1)
  168. self.global_max_pool = nn.AdaptiveMaxPool2d(1)
  169. self.local_avg_pool = lambda x: local_avg_pool(x, kernel_size=local_pool_size)
  170. # Hierarchical channel-wise excitation
  171. reduced_channels = max(in_channels // reduction_ratio, 4)
  172. # Since we concatenate pooled features, input to FC will have more channels
  173. # We'll handle dynamic dimensions in forward pass
  174. self.fc1 = nn.Linear(in_channels * 3, reduced_channels) # 3 for avg, max, local
  175. self.fc2 = nn.Linear(reduced_channels, in_channels)
  176. # Spatial attention
  177. self.spatial_conv = nn.Conv2d(in_channels, 1, kernel_size=1)
  178. def forward(self, x: torch.Tensor) -> torch.Tensor:
  179. """
  180. Args:
  181. x: Input feature map [B, C, H, W]
  182. Returns:
  183. Refined feature map [B, C, H, W]
  184. """
  185. B, C, H, W = x.shape
  186. # Step 1: Multi-Scale Pooling
  187. F_avg = self.global_avg_pool(x) # [B, C, 1, 1]
  188. F_max = self.global_max_pool(x) # [B, C, 1, 1]
  189. F_local = self.local_avg_pool(x) # [B, C, H, W]
  190. # Resize F_local for concatenation
  191. F_local_pooled = F.adaptive_avg_pool2d(F_local, (1, 1))
  192. # Concatenate pooled features
  193. F_pooled = torch.cat([F_avg, F_max, F_local_pooled], dim=1) # [B, 3*C, 1, 1]
  194. F_pooled = F_pooled.view(B, -1) # [B, 3*C]
  195. # Step 2: Hierarchical Channel-Wise Excitation
  196. F_excite = F.relu(self.fc1(F_pooled))
  197. F_excite = self.fc2(F_excite) # [B, C]
  198. channel_weights = torch.sigmoid(F_excite).view(B, C, 1, 1) # [B, C, 1, 1]
  199. # Channel modulation
  200. F_c = x * channel_weights.expand_as(x) # [B, C, H, W]
  201. # Step 3: Spatial Attention
  202. spatial_weights = self.spatial_conv(x) # [B, 1, H, W]
  203. spatial_weights = torch.sigmoid(spatial_weights) # [B, 1, H, W]
  204. # Step 4: Apply spatial attention
  205. F_out = F_c * spatial_weights.expand_as(F_c)
  206. return F_out
  207. # ==================== FA-CBAM Module ====================
  208. class FA_CBAM(nn.Module):
  209. """
  210. Feature-Aware Convolution Block Attention Module
  211. Combines spatial, channel, and frequency domain attention
  212. """
  213. def __init__(self, in_channels: int, reduction_ratio: int = 16):
  214. super().__init__()
  215. self.in_channels = in_channels
  216. # Channel Attention
  217. reduced_channels = max(in_channels // reduction_ratio, 4)
  218. self.channel_attention = nn.Sequential(
  219. nn.AdaptiveAvgPool2d(1),
  220. nn.Flatten(),
  221. nn.Linear(in_channels, reduced_channels, bias=False),
  222. nn.ReLU(inplace=True),
  223. nn.Linear(reduced_channels, in_channels, bias=False),
  224. nn.Sigmoid()
  225. )
  226. # Spatial Attention
  227. self.spatial_attention = nn.Sequential(
  228. nn.Conv2d(2, 1, kernel_size=7, padding=3, bias=False),
  229. nn.Sigmoid()
  230. )
  231. # Frequency domain processing
  232. self.freq_conv = nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1)
  233. # Fusion convolution
  234. self.fusion_conv = nn.Conv2d(2, 1, kernel_size=3, padding=1)
  235. def forward(self, x: torch.Tensor) -> torch.Tensor:
  236. """
  237. Args:
  238. x: Input feature map [B, C, H, W]
  239. Returns:
  240. Refined feature map [B, C, H, W]
  241. """
  242. B, C, H, W = x.shape
  243. # Step 1: Channel Attention
  244. channel_weights = self.channel_attention(x).view(B, C, 1, 1)
  245. F_c = x * channel_weights.expand_as(x)
  246. # Step 2: Spatial Attention
  247. # Average and max pooling along channel axis
  248. avg_pool = torch.mean(F_c, dim=1, keepdim=True) # [B, 1, H, W]
  249. max_pool, _ = torch.max(F_c, dim=1, keepdim=True) # [B, 1, H, W]
  250. spatial_input = torch.cat([avg_pool, max_pool], dim=1) # [B, 2, H, W]
  251. spatial_weights = self.spatial_attention(spatial_input) # [B, 1, H, W]
  252. F_s = F_c * spatial_weights
  253. # Step 3: Frequency Domain Augmentation
  254. F_freq = dct2d(x) # Apply DCT
  255. F_freq = self.freq_conv(F_freq) # Convolution on frequency features
  256. # Step 4: Fusion via Convolution
  257. # Resize spatial weights to match frequency features if needed
  258. if spatial_weights.shape[-2:] != F_freq.shape[-2:]:
  259. spatial_weights = F.interpolate(spatial_weights, size=F_freq.shape[-2:],
  260. mode='bilinear', align_corners=False)
  261. fusion_input = torch.cat([spatial_weights, F_freq], dim=1) # [B, C+1, H, W]
  262. # We need to adjust for channel mismatch - use pointwise conv
  263. if fusion_input.shape[1] != 2: # If not 2 channels, reduce
  264. fusion_input = fusion_input[:, :2, :, :] # Simple slice for demo
  265. M_sf = torch.sigmoid(self.fusion_conv(fusion_input)) # [B, 1, H, W]
  266. # Step 5: Final Output
  267. if M_sf.shape[-2:] != F_c.shape[-2:]:
  268. M_sf = F.interpolate(M_sf, size=F_c.shape[-2:], mode='bilinear', align_corners=False)
  269. F_out = F_c * M_sf.expand_as(F_c)
  270. return F_out
  271. # ==================== Main LogoXpertNet Model ====================
  272. class LogoXpertNet(nn.Module):
  273. """
  274. Complete LogoXpertNet model for logo classification
  275. """
  276. def __init__(self, num_classes: int = 32, pretrained_backbone: bool = True):
  277. super().__init__()
  278. # 1. Modified MobileNetV3 backbone
  279. self.backbone = ModifiedMobileNetV3(num_classes=num_classes,
  280. pretrained=pretrained_backbone)
  281. # Get feature dimensions from backbone
  282. early_channels = 24 # Based on MobileNetV3-small architecture
  283. deep_channels = 576 # Final feature dimension
  284. # 2. CLFF Module
  285. self.clff = CLFF(early_channels=early_channels,
  286. deep_channels=deep_channels,
  287. fusion_method='concat')
  288. # 3. HSE-SAB Module
  289. self.hse_sab = HSE_SAB(in_channels=early_channels + deep_channels) # After concatenation
  290. # 4. FA-CBAM Module
  291. self.fa_cbam = FA_CBAM(in_channels=early_channels + deep_channels)
  292. # 5. Classification head
  293. # After attention modules, we need to reduce spatial dimensions
  294. self.global_pool = nn.AdaptiveAvgPool2d(1)
  295. # Calculate input features to classifier
  296. classifier_input = early_channels + deep_channels
  297. self.classifier = nn.Sequential(
  298. nn.Dropout(0.2),
  299. nn.Linear(classifier_input, 1024),
  300. nn.ReLU(inplace=True),
  301. nn.Dropout(0.2),
  302. nn.Linear(1024, num_classes)
  303. )
  304. # Initialize weights
  305. self._initialize_weights()
  306. def _initialize_weights(self):
  307. """Initialize weights for custom modules"""
  308. for m in self.modules():
  309. if isinstance(m, nn.Conv2d):
  310. nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
  311. if m.bias is not None:
  312. nn.init.constant_(m.bias, 0)
  313. elif isinstance(m, nn.BatchNorm2d):
  314. nn.init.constant_(m.weight, 1)
  315. nn.init.constant_(m.bias, 0)
  316. elif isinstance(m, nn.Linear):
  317. nn.init.normal_(m.weight, 0, 0.01)
  318. if m.bias is not None:
  319. nn.init.constant_(m.bias, 0)
  320. def forward(self, x: torch.Tensor) -> torch.Tensor:
  321. """
  322. Forward pass through LogoXpertNet
  323. Args:
  324. x: Input images [B, 3, 224, 224]
  325. Returns:
  326. Class logits [B, num_classes]
  327. """
  328. # 1. Extract features from backbone
  329. early_feat, deep_feat_pooled = self.backbone(x)
  330. # 2. Prepare deep features for CLFF (need spatial dimensions)
  331. # In a full implementation, you'd store intermediate spatial features
  332. # For now, we'll create a placeholder
  333. B, C_early, H, W = early_feat.shape
  334. # Create a placeholder for deep spatial features
  335. # In practice, you'd extract this from an intermediate layer
  336. deep_feat_spatial = F.interpolate(
  337. deep_feat_pooled.view(B, -1, 1, 1),
  338. size=(H, W),
  339. mode='bilinear',
  340. align_corners=False
  341. )
  342. # 3. Apply CLFF
  343. clff_output = self.clff(early_feat, deep_feat_spatial)
  344. # 4. Apply HSE-SAB
  345. hse_sab_output = self.hse_sab(clff_output)
  346. # 5. Apply FA-CBAM
  347. fa_cbam_output = self.fa_cbam(hse_sab_output)
  348. # 6. Global pooling and classification
  349. pooled = self.global_pool(fa_cbam_output)
  350. flattened = torch.flatten(pooled, 1)
  351. # 7. Final classification
  352. logits = self.classifier(flattened)
  353. return logits
  354. def get_attention_maps(self, x: torch.Tensor):
  355. """
  356. Extract attention maps for visualization
  357. Returns intermediate attention outputs
  358. """
  359. attention_maps = {}
  360. # Extract features
  361. early_feat, deep_feat_pooled = self.backbone(x)
  362. B, C_early, H, W = early_feat.shape
  363. # Create placeholder for deep spatial features
  364. deep_feat_spatial = F.interpolate(
  365. deep_feat_pooled.view(B, -1, 1, 1),
  366. size=(H, W),
  367. mode='bilinear',
  368. align_corners=False
  369. )
  370. # Get CLFF output
  371. clff_output = self.clff(early_feat, deep_feat_spatial)
  372. attention_maps['clff'] = clff_output
  373. # Get HSE-SAB output
  374. hse_sab_output = self.hse_sab(clff_output)
  375. attention_maps['hse_sab'] = hse_sab_output
  376. # Get FA-CBAM output
  377. fa_cbam_output = self.fa_cbam(hse_sab_output)
  378. attention_maps['fa_cbam'] = fa_cbam_output
  379. return attention_maps
  380. # ==================== Utility Functions ====================
  381. def create_logoxpertnet(model_name: str = 'small', num_classes: int = 32,
  382. pretrained: bool = True) -> LogoXpertNet:
  383. """
  384. Factory function to create LogoXpertNet models
  385. """
  386. if model_name == 'small':
  387. return LogoXpertNet(num_classes=num_classes, pretrained_backbone=pretrained)
  388. else:
  389. raise ValueError(f"Unknown model name: {model_name}")
  390. def count_parameters(model: nn.Module) -> int:
  391. """Count total trainable parameters"""
  392. return sum(p.numel() for p in model.parameters() if p.requires_grad)
  393. def test_model():
  394. """Test the model with dummy data"""
  395. print("Testing LogoXpertNet implementation...")
  396. # Create model
  397. model = create_logoxpertnet(num_classes=32, pretrained=False)
  398. # Count parameters
  399. total_params = count_parameters(model)
  400. print(f"Total parameters: {total_params:,}")
  401. # Test forward pass
  402. batch_size = 4
  403. dummy_input = torch.randn(batch_size, 3, 224, 224)
  404. # Test inference
  405. model.eval()
  406. with torch.no_grad():
  407. output = model(dummy_input)
  408. print(f"Input shape: {dummy_input.shape}")
  409. print(f"Output shape: {output.shape}")
  410. print(f"Output range: [{output.min():.3f}, {output.max():.3f}]")
  411. # Test attention maps
  412. attention_maps = model.get_attention_maps(dummy_input)
  413. print(f"\nAttention maps keys: {list(attention_maps.keys())}")
  414. for key, value in attention_maps.items():
  415. print(f"{key} shape: {value.shape}")
  416. print("\nModel test completed successfully!")
  417. return model
  418. # ==================== Example Usage ====================
  419. if __name__ == "__main__":
  420. # Test the model
  421. model = test_model()
  422. # Example of how to use the model
  423. print("\n" + "="*50)
  424. print("Example Usage:")
  425. print("="*50)
  426. print("""
  427. # Create model for FlickrLogos-32 (32 classes)
  428. model = create_logoxpertnet(num_classes=32, pretrained=True)
  429. # Move to GPU if available
  430. device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  431. model = model.to(device)
  432. # Prepare input
  433. batch = torch.randn(8, 3, 224, 224).to(device)
  434. # Forward pass
  435. logits = model(batch)
  436. # Get predictions
  437. predictions = torch.argmax(logits, dim=1)
  438. # Get attention maps for visualization
  439. attention_maps = model.get_attention_maps(batch)
  440. """)
  441. # Model summary
  442. print(f"\nModel Summary:")
  443. print(f"- Backbone: Modified MobileNetV3-small")
  444. print(f"- CLFF: Cross-Layer Feature Fusion")
  445. print(f"- HSE-SAB: Hierarchical Squeeze-Excitation Spatial Attention")
  446. print(f"- FA-CBAM: Feature-Aware Convolution Block Attention Module")
  447. print(f"- Output: {model.classifier[-1].out_features} classes")

LogoXpertNet Model.py at commit 01d679a, no license · at the source

Overview

Authors: Muhammad Tahir Mumtaz1, Mohd Khalid Awang1, Muhammad Usman Saeed2, Shafiq Hussain3, Dhouha Ben Noureddine4, Yassine Daadaa4
  1. Faculty of Informatics and Computing, Universiti Sultan Zainal Abidin (UniSZA),Terengganu, Malaysia
  2. School of Computer Science and Technology, Harbin Institute of Technology,Shenzhen, 518055 China
  3. Department of Computer Science, University of Sahiwal,Sahiwal, Punjab Pakistan
  4. College of Computer and Information Sciences, Imam Mohammad Ibn Saud Islamic University (IMSIU),13318 Riyadh, Saudi Arabia
Journal: Scientific reports, volume 16, issue 1, article 10956
Dates: received 21 October 2025; accepted 20 March 2026; published online 26 March 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1038/s41598-026-45682-z · PMID 41888352 · PMCID PMC13039303 · OpenAlex W7140936350
Open access: gold, a free copy (OpenAlex)
Status: code verified (how the code is found and verified)
Categories: methods / tools (subfield)
Methods: Spectral & time-frequency, Statistics, Machine learning
Keywords: Logo classification, Lightweight, Deep learning, Feature fusion, Computational biology and bioinformatics, Engineering, Mathematics and computing
Topic: Digital Media Forensic Detection (Computer Vision and Pattern Recognition, Computer Science), according to OpenAlex
Funding: This work was supported and funded by the Deanship of Scientific Research at Imam Mohammad Ibn Saud Islamic University (IMSIU) (grant number IMSIU-DDRSP2504)
Citations: not cited yet (Europe PMC); 38 references in the paper

Abstract

Logo classification is crucial in various applications, including brand monitoring, copyright protection, and digital forensics. Traditional computer vision techniques face significant limitations, particularly in handling scale variations, occlusions, and background clutter. While deep learning models, particularly convolutional neural networks (CNNs), offer superior solutions, they often come with high computational costs, posing challenges for real-time deployment. This paper introduces LogoXpertNet, a lightweight deep learning architecture specifically designed for efficient logo classification. The key innovations of LogoXpertNet include: (1) a modified MobileNetV3 backbone enhanced with bottleneck and squeeze-and-excitation (SE) blocks for efficient feature extraction; (2) a novel cross-layer feature fusion (CLFF) module that improves feature integration across network depths; (3) a newly proposed hierarchical squeeze-excitation spatial attention block (HSE-SAB) that dynamically attends to both spatial and channel-wise features; and (4) a feature-aware convolution block attention module (FA-CBAM) that uniquely fuses spatial and frequency-domain information for refined logo attention. Extensive experiments on the evaluated benchmark datasets, including FlickrLogos-32, BelgaLogos, and WebLogo-2M, show that LogoXpertNet achieves strong classification performance while maintaining low computational overhead. Because the reported accuracies approach saturation on these benchmarks, the results should be interpreted in the context of dataset characteristics, split construction, and metric definition rather than as evidence of universal performance across all real-world logo-recognition scenarios. LogoXpertNet provides an efficient and practical framework for benchmark logo classification and offers a promising basis for future validation under more challenging real-world conditions.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 11 matches between paragraphs and lines of code.

Usman548/LogoXpertNet

License: none: the authors keep all their rights
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: 01d679a39be1ce0298b6e084707093c093b1e667, 14 January 2026
Languages: Python (1)
Size: 1 file, 1 script
Software Heritage: not archived
Found in: “Code availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (1 file), PyTorch (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
1 file

Code availability

The implementation code of the LogoXpertNet is available at https://github.com/Usman548/LogoXpertNet.

Reproduced under the paper's license (CC BY), from the paper cited above.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 1 script, each with its path and the digest of its content;
  • 11 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

Datasets cited

Data availability

This research employed the publicly accessible datasets, which can be downloaded from the following links: Flickr32-Logos: https://www.uni-augsburg.de/en/fakultaet/fai/informatik/prof/mmc/research/datensatze/flickrlogos/ BelgaLogos: https://www-sop.inria.fr/members/Alexis.Joly/BelgaLogos/BelgaLogos.html Web2M: https://github.com/pytorch/vision/tree/main/torchvision/datasets

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 30 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 6 authors, 7 keywords, 1 funder, 19 references.

Cite

This paper

Mumtaz, M. T., Awang, M. K., Saeed, M. U., Hussain, S., Ben Noureddine, D., & Daadaa, Y. (2026). LogoXpertNet: a novel lightweight logo classification using deep learning. Scientific reports, 16(1), 10956. https://doi.org/10.1038/s41598-026-45682-z

BibTeX

@article{mumtaz2026logoxpertnet,
author = {Mumtaz, Muhammad Tahir and Awang, Mohd Khalid and Saeed, Muhammad Usman and Hussain, Shafiq and Ben Noureddine, Dhouha and Daadaa, Yassine},
title = {{LogoXpertNet: a novel lightweight logo classification using deep learning}},
journal = {Scientific reports},
year = {2026},
month = mar,
volume = {16},
number = {1},
pages = {10956},
publisher = {Nature Publishing Group},
issn = {2045-2322},
doi = {10.1038/s41598-026-45682-z},
url = {https://doi.org/10.1038/s41598-026-45682-z},
pmid = {41888352},
pmcid = {PMC13039303}
}

RIS

TY - JOUR
AU - Mumtaz, Muhammad Tahir
AU - Awang, Mohd Khalid
AU - Saeed, Muhammad Usman
AU - Hussain, Shafiq
AU - Ben Noureddine, Dhouha
AU - Daadaa, Yassine
TI - LogoXpertNet: a novel lightweight logo classification using deep learning
T2 - Scientific reports
J2 - Sci Rep
PY - 2026
DA - 2026/03/26
VL - 16
IS - 1
SP - 10956
SN - 2045-2322
PB - Nature Publishing Group
DO - 10.1038/s41598-026-45682-z
UR - https://doi.org/10.1038/s41598-026-45682-z
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41598-026-45682-z",
"type": "article-journal",
"title": "LogoXpertNet: a novel lightweight logo classification using deep learning",
"container-title": "Scientific reports",
"author": [
{
"family": "Mumtaz",
"given": "Muhammad Tahir"
},
{
"family": "Awang",
"given": "Mohd Khalid"
},
{
"family": "Saeed",
"given": "Muhammad Usman"
},
{
"family": "Hussain",
"given": "Shafiq"
},
{
"family": "Ben Noureddine",
"given": "Dhouha"
},
{
"family": "Daadaa",
"given": "Yassine"
}
],
"container-title-short": "Sci Rep",
"volume": "16",
"issue": "1",
"page": "10956",
"DOI": "10.1038/s41598-026-45682-z",
"PMID": "41888352",
"PMCID": "PMC13039303",
"ISSN": "2045-2322",
"publisher": "Nature Publishing Group",
"URL": "https://doi.org/10.1038/s41598-026-45682-z",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
26
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

No other paper with a page shares enough with this one yet: tools, categories, datasets, references or authors.

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Add the badge to the code's README

OSCR: the badge of this paper

If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.

Markdown
<!-- oscr-badge:start -->
[![OSCR](https://openscicode.org/badge/doi/10.1038/s41598-026-45682-z.svg)](https://openscicode.org/badge/doi/10.1038/s41598-026-45682-z)
<!-- oscr-badge:end -->
reStructuredText
.. oscr-badge:start

.. image:: https://openscicode.org/badge/doi/10.1038/s41598-026-45682-z.svg
   :target: https://openscicode.org/badge/doi/10.1038/s41598-026-45682-z
   :alt: OSCR

.. oscr-badge:end
HTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1038/s41598-026-45682-z"><img src="https://openscicode.org/badge/doi/10.1038/s41598-026-45682-z.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->

On GitHub, open the README, choose Edit (the pencil), paste the snippet under the title, then "Commit changes".

If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.

Report a mismatch between the code and the paper · Report a code error (signed in)

Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.

Report a reproduction failure (signed in)

Versions of its code: the releases of its code tied to a version of this paper (the preprint, the accepted manuscript, the version of record…), each with the tracing map versioned with it.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.