改进yolo11-RepNCSPELAN等200+全套创新点大全:厨房食材检测图像分割系统源码&数据集全套
1.图片效果展示
项目来源 人工智能促进会 2024.10.30
注意:由于项目一直在更新迭代,上面“1.图片效果展示”和“2.视频效果展示”展示的系统图片或者视频可能为老版本,新版本在老版本的基础上升级如下:(实际效果以升级的新版本为准)
(1)适配了YOLOV11的“目标检测”模型和“实例分割”模型,通过加载相应的权重(.pt)文件即可自适应加载模型。
(2)支持“图片识别”、“视频识别”、“摄像头实时识别”三种识别模式。
(3)支持“图片识别”、“视频识别”、“摄像头实时识别”三种识别结果保存导出,解决手动导出(容易卡顿出现爆内存)存在的问题,识别完自动保存结果并导出到tempDir中。
(4)支持Web前端系统中的标题、背景图等自定义修改。
另外本项目提供训练的数据集和训练教程,暂不提供权重文件(best.pt),需要您按照教程进行训练后实现图片演示和Web前端界面演示的效果。
2.视频效果展示
2.1 视频效果展示
3.背景
研究背景与意义
随着智能家居和自动化厨房的迅速发展,厨房食材的智能识别与管理成为了一个重要的研究方向。传统的厨房管理方式往往依赖于人工识别和记录,不仅效率低下,而且容易出现错误。基于深度学习的图像识别技术,尤其是YOLO(You Only Look Once)系列模型,因其高效的实时检测能力而备受关注。YOLOv11作为该系列的最新版本,具备更强的特征提取能力和更快的处理速度,为厨房食材的自动识别提供了新的可能性。
本研究旨在基于改进的YOLOv11模型,构建一个厨房食材检测图像分割系统。该系统将利用包含4500张图像的多样化数据集,涵盖了包括苹果、米饭、黑胡椒、花椰菜等在内的四类食材。这些食材不仅在家庭烹饪中常见,也在健康饮食和营养管理中扮演着重要角色。通过对这些食材的精准识别与分割,系统能够实现对厨房库存的实时监控,帮助用户更好地管理食材,减少浪费,并提升烹饪效率。
此外,随着人们对健康饮食的关注,食材的种类和品质日益受到重视。通过引入深度学习技术,研究将推动厨房智能化的发展,提升用户的烹饪体验。该系统的实现不仅有助于家庭用户的日常生活,还可为餐饮行业提供数据支持,优化食材采购和库存管理,具有广泛的应用前景和市场价值。因此,本研究不仅具有重要的学术意义,也具备良好的实际应用潜力,为未来的智能厨房发展奠定基础。
4.数据集信息展示
4.1 本项目数据集详细数据(类别数&类别名)
nc: 32
names: [‘Apple’, ‘Basmatirice’, ‘Blackpepper’, ‘Broccoli’, ‘Brownsugar’, ‘Butter’, ‘Buttermilk’, ‘Buttonmushroom’, ‘Cashewnut’, ‘Chickenstock’, ‘Cilantro’, ‘Cinnamon’, ‘Egg’, ‘Flour’, ‘Garlic’, ‘Greenpepper’, ‘Lemon’, ‘Mayonnaise’, ‘Medjooldates’, ‘Mustard’, ‘Onion’, ‘Peas’, ‘Potato’, ‘Redbeans’, ‘Redpepper’, ‘Salt’, ‘Springonion’, ‘Tomato’, ‘Vegetableoil’, ‘Whitesugar’, ‘milk’, ‘yeast’]
该项目为【图像分割】数据集,请在【训练教程和Web端加载模型教程(第三步)】这一步的时候按照【图像分割】部分的教程来训练
4.2 本项目数据集信息介绍
本项目数据集信息介绍
本项目所使用的数据集名为“Ingredients”,其主要目的是为了训练和改进YOLOv11的厨房食材检测图像分割系统。该数据集包含32个类别,涵盖了多种常见的厨房食材,旨在为图像分割任务提供丰富的样本和多样化的特征。这些类别包括了从新鲜水果到调味品、从干货到乳制品的多种食材,具体类别有:苹果、巴斯马蒂米、黑胡椒、西兰花、红糖、黄油、酪乳、按钮蘑菇、腰果、鸡肉高汤、香菜、肉桂、鸡蛋、面粉、大蒜、青椒、柠檬、蛋黄酱、梅德乔尔枣、芥末、洋葱、豌豆、土豆、红豆、红椒、盐、春洋葱、西红柿、植物油、白糖、牛奶和酵母等。
数据集的多样性使其能够有效地捕捉不同食材在各种烹饪场景中的外观特征,这对于提高YOLOv11在厨房环境中的检测精度至关重要。每个类别的食材都经过精心挑选,确保涵盖了家庭烹饪中常见的食材类型,从而增强模型的实用性和适应性。此外,数据集中的图像经过精细标注,确保每个食材的边界清晰,便于进行高质量的图像分割训练。
通过使用“Ingredients”数据集,研究团队期望能够提升YOLOv11在复杂厨房环境中的检测能力,使其能够准确识别和分割出各种食材。这不仅有助于自动化厨房管理,还为智能烹饪助手的开发奠定了基础。整体而言,该数据集的构建为图像分割技术在食品识别领域的应用提供了坚实的支持,推动了相关研究的深入发展。
5.全套项目环境部署视频教程(零基础手把手教学)
5.1 所需软件PyCharm和Anaconda安装教程(第一步)
5.2 安装Python虚拟环境创建和依赖库安装视频教程(第二步)
6.改进YOLOv11训练教程和Web_UI前端加载模型教程(零基础手把手教学)
6.1 改进YOLOv11训练教程和Web_UI前端加载模型教程(第三步)
按照上面的训练视频教程链接加载项目提供的数据集,运行train.py即可开始训练
Epoch gpu_mem box obj cls labels img_size
1/200 20.8G 0.01576 0.01955 0.007536 22 1280: 100%|██████████| 849/849 [14:42<00:00, 1.04s/it]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:14<00:00, 2.87it/s]
all 3395 17314 0.994 0.957 0.0957 0.0843
Epoch gpu_mem box obj cls labels img_size
2/200 20.8G 0.01578 0.01923 0.007006 22 1280: 100%|██████████| 849/849 [14:44<00:00, 1.04s/it]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|██████████| 213/213 [01:12<00:00, 2.95it/s]
all 3395 17314 0.996 0.956 0.0957 0.0845
Epoch gpu_mem box obj cls labels img_size
3/200 20.8G 0.01561 0.0191 0.006895 27 1280: 100%|██████████| 849/849 [10:56<00:00, 1.29it/s]
Class Images Labels P R mAP@.5 mAP@.5:.95: 100%|███████ | 187/213 [00:52<00:00, 4.04it/s]
all 3395 17314 0.996 0.957 0.0957 0.0845
项目数据集下载链接
7.原始YOLOv11算法讲解
YOLOv11改进方向
与YOLOv 10相比,YOLOv 11有了巨大的改进,包括但不限于:
- 增强的模型结构:模型具有改进的模型结构,以获取图像处理并形成预测
- GPU优化:这是现代ML模型的反映,GPU训练ML模型在速度和准确性上都更好。
- 速度:YOLOv 11模型现在经过增强和GPU优化以用于训练。通过优化,这些模型比它们的前版本快得多。在速度上达到了25%的延迟减少!
- 更少的参数:更少的参数允许更快的模型,但v11的准确性不受影响
- 更具适应性:更多支持的任务YOLOv 11支持多种类型的任务、多种类型的对象和多种类型的图像。
YOLOv11功能介绍
Glenn Jocher和他的团队制作了一个令人敬畏的YOLOv 11迭代,并且在图像人工智能的各个方面都提供了YOLO。YOLOv 11有多种型号,包括:
- 对象检测-在训练时检测图像中的对象
- 图像分割-超越对象检测,分割出图像中的对象
- 姿态检测-当用点和线训练时绘制一个人的姿势
- 定向检测(OBB):类似于对象检测,但包围盒可以旋转
- 图像分类-在训练时对图像进行分类
使用Ultralytics Library,这些模型还可以进行优化,以:
- 跟踪-可以跟踪对象的路径
- 易于导出-库可以以不同的格式和目的导出
- 多场景-您可以针对不同的对象和图像训练模型
此外,Ultralytics还推出了YOLOv 11的企业模型,该模型将于10月31日发布。这将与开源的YOLOv
11模型并行,但将拥有更大的专有Ultralytics数据集。YOLOv 11是“建立在过去的成功”的其他版本的之上。
YOLOv11模型介绍
YOLOv 11附带了边界框模型(无后缀),实例分割(-seg),姿态估计(-pose),定向边界框(-obb)和分类(-cls)。
这些也有不同的尺寸:纳米(n),小(s),中(m),大(l),超大(x)。
![img](https://img-
blog.****img.cn/img_convert/e2ac12a32b7ca425217ea8391736d9ca.png)
YOLOv11模型
YOLOv11与前版本对比
与YOLOv10和YOLOv8相比,YOLOv11在Ultralytics的任何帖子中都没有直接提到。所以我会收集所有的数据来比较它们。感谢Ultralytics:
检测:
![img](https://img-
blog.****img.cn/img_convert/e7bc7f5fbb8ca2db5460668b96237f2d.png)
YOLOv11检测统计
![img](https://img-
blog.****img.cn/img_convert/b6836b131f872e1fd122404157de1e86.png)
YOLOv10检测统计
其中,Nano的mAPval在v11上为39.5,v10上为38.5;Small为47.0 vs 46.3,Medium为51.5 vs
51.1,Large为53.4 vs 53.2,Extra Large为54.7vs
54.4。现在,这可能看起来像是一种增量增加,但小小数的增加可能会对ML模型产生很大影响。总体而言,YOLOv11以0.3
mAPval的优势追平或击败YOLOv10。
现在,我们必须看看速度。在延迟方面,Nano在v11上为1.55 , v10上为1.84,Small为2.46 v2.49,Medium为4.70
v4.74,Large为6.16 v7.28,Extra Large为11.31
v10.70。延迟越低越好。YOLOv11提供了一个非常低的延迟相比,除了特大做得相当差的前身。
总的来说,Nano模型是令人振奋的,速度更快,性能相当。Extra Large在性能上有很好的提升,但它的延迟非常糟糕。
分割:
![img](https://img-
blog.****img.cn/img_convert/c4280d74fb735c5efdeec096ddafe027.png)
YOLOV11 分割统计
![img](https://img-
blog.****img.cn/img_convert/8500756f76cb2277280639aab069a22a.png)
YOLOV9 分割统计
![img](https://img-
blog.****img.cn/img_convert/a1f20d5fcab6323c2aea543136374876.png)
YOLOV8 分割数据
总体而言,YOLOv 11上的分割模型在大型和超大型模型方面比上一代YOLOv 8和YOLOv 9做得更好。
YOLOv 9 Segmentation没有提供任何关于延迟的统计数据。比较YOLOv 11延迟和YOLOv 8延迟,发现YOLOv 11比YOLOv
8快得多。YOLOv 11将大量GPU集成到他们的模型中,因此期望他们的模型甚至比CPU测试的基准更快!
姿态估计:
![img](https://img-
blog.****img.cn/img_convert/b8e15e57098723453b6cdc9eda75118e.png)
YOLOV11姿态估计统计
![img](https://img-
blog.****img.cn/img_convert/515264f7dd14fed8e2ece60c1c94806b.png)
YOLOV8姿态估计统计
YOLOv 11的mAP 50 -95统计量也逐渐优于先前的YOLOv 8(除大型外)。然而,在速度方面,YOLOv
11姿势可以最大限度地减少延迟。其中一些延迟指标是版本的1/4!通过对这些模型进行GPU训练优化,我可以看到指标比显示的要好得多。
定向边界框:
![img](https://img-
blog.****img.cn/img_convert/fa4ce5fe7fdc44bb11a9b5d1842e32a6.png)
YOLOv11 OBB统计
![img](https://img-
blog.****img.cn/img_convert/68f498919bc731806f5b24c0a0f317f5.png)
YOLOv8 OBB统计
OBB统计数据在mAP
50上并不是很好,只有非常小的改进,在某种程度上小于检测中的微小改进。然而,从v8到v11的速度减半,这表明YOLOv11在速度上做了很多努力。
最后,分类:
![img](https://img-
blog.****img.cn/img_convert/99c9a2edbcb11ba9bc66e1dc7006ed75.png)
YOLOv 11 CLS统计
![img](https://img-
blog.****img.cn/img_convert/4aa0a6e2dba8fc65ed529c1487665eb7.png)
YOLOv8 CLS统计
从v8到v11,准确性也有了微小的提高。然而,速度大幅上升,CPU速度更快的型号。
8.200+种全套改进YOLOV11创新点原理讲解
8.1 200+种全套改进YOLOV11创新点原理讲解大全
由于篇幅限制,每个创新点的具体原理讲解就不全部展开,具体见下列网址中的改进模块对应项目的技术原理博客网址【Blog】(创新点均为模块化搭建,原理适配YOLOv5~YOLOv11等各种版本)
改进模块技术原理博客【Blog】网址链接
8.2 精选部分改进YOLOV11创新点原理讲解
这里节选部分改进创新点展开原理讲解(完整的改进原理见上图和改进模块技术原理博客链接【如果此小节的图加载失败可以通过****或者Github搜索该博客的标题访问原始博客,原始博客图片显示正常】
### CBAM空间注意力机制
近年来,随着深度学习研究方向的火热,注意力机制也被广泛地应用在图像识别、语音识别和自然语言处理等领域,注意力机制在深度学习任务中发挥着举足轻重的作用。注意力机制借鉴于人类的视觉系统,例如,人眼在看到一幅画面时,会倾向于关注画面中的重要信息,而忽略其他可见的信息。深度学习中的注意力机制和人类视觉的注意力机制相似,通过扫描全局数据,从大量数据中选择出需要重点关注的、对当前任务更为重要的信息,然后对这部分信息分配更多的注意力资源,从这些信息中获取更多所需要的细节信息,而抑制其他无用的信息。而在深度学习中,则具体表现为给感兴趣的区域更高的权重,经过网络的学习和调整,得到最优的权重分配,形成网络模型的注意力,使网络拥有更强的学习能力,加快网络的收敛速度。
注意力机制通常可分为软注意力机制和硬注意力机制[4-5]。软注意力机制在选择信息时,不是从输入的信息中只选择1个,而会用到所有输入信息,只是各个信息对应的权重分配不同,然后输入网络模型进行计算;硬注意力机制则是从输入的信息中随机选取一个或者选择概率最高的信息,但是这一步骤通常是不可微的,导致硬注意力机制更难训练。因此,软注意力机制应用更为广泛,按照原理可将软注意力机制划分为:通道注意力机制(channel attention)、空间注意力机制(spatial attention)和混合域注意力机制(mixed attention)。
通道注意力机制的本质建立各个特征通道之间的重要程度,对感兴趣的通道进行重点关注,弱化不感兴趣的通道的作用;空间注意力的本质则是建模了整个空间信息的重要程度,然后对空间内感兴趣的区域进行重点关注,弱化其余非感兴趣区域的作用;混合注意力同时运用了通道注意力和空间注意力,两部分先后进行或并行,形成对通道特征和空间特征同时关注的注意力模型。
卷积层注意力模块(Convolutional Block Attention Module,CBAM)是比较常用的混合注意力模块,其先后集中了通道注意力模块和空间注意力模块,网络中加入该模块能有效提高网络性能,减少网络模型的计算量,模块结构如图所示。输入特征图首先经过分支的通道注意力模块,然后和主干的原特征图融合,得到具有通道注意力的特征图,接着经过分支的空间注意力模块,在和主干的特征图融合后,得到同时具有通道特征注意力和空间特征注意力的特征图。CBAM模块不改变输入特征图的大小,因此该模块是一个“即插即用”的模块,可以插入网络的任何位置。
通道注意力模块的结构示意图如图所示,通道注意力模块分支并行地对输入的特征图进行最大池化操作和平均池化操作,然后利用多层感知机对结果进行变换,得到应用于两个通道的变换结果,最后经过sigmoid激活函数将变换结果融合,得到具有通道注意力的通道特征图。
空间注意力模块示意图如图所示,将通道注意力模块输出的特征图作为该模块的输入特征图,首先对输入特征图进行基于通道的最大池化操作和平均池化操作,将两部分得到的结果拼接起来,然后通过卷积得到降为Ⅰ通道的特征图,最后通过sigmoid激活函数生成具有空间注意力的特征图。
9.系统功能展示
图9.1.系统支持检测结果表格显示
图9.2.系统支持置信度和IOU阈值手动调节
图9.3.系统支持自定义加载权重文件best.pt(需要你通过步骤5中训练获得)
图9.4.系统支持摄像头实时识别
图9.5.系统支持图片识别
图9.6.系统支持视频识别
图9.7.系统支持识别结果文件自动保存
图9.8.系统支持Excel导出检测结果数据
10. YOLOv11核心改进源码讲解
10.1 wtconv2d.py
以下是对代码中最核心部分的分析和详细注释。代码的主要功能是实现一个基于小波变换的卷积层(WTConv2d
),并提供小波变换和逆小波变换的功能。
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Function
import pywt
# 创建小波滤波器
def create_wavelet_filter(wave, in_size, out_size, type=torch.float):
# 创建小波对象
w = pywt.Wavelet(wave)
# 获取小波的分解滤波器
dec_hi = torch.tensor(w.dec_hi[::-1], dtype=type) # 高频滤波器
dec_lo = torch.tensor(w.dec_lo[::-1], dtype=type) # 低频滤波器
# 生成分解滤波器的组合
dec_filters = torch.stack([
dec_lo.unsqueeze(0) * dec_lo.unsqueeze(1),
dec_lo.unsqueeze(0) * dec_hi.unsqueeze(1),
dec_hi.unsqueeze(0) * dec_lo.unsqueeze(1),
dec_hi.unsqueeze(0) * dec_hi.unsqueeze(1)
], dim=0)
# 扩展滤波器以适应输入通道数
dec_filters = dec_filters[:, None].repeat(in_size, 1, 1, 1)
# 获取小波的重构滤波器
rec_hi = torch.tensor(w.rec_hi[::-1], dtype=type).flip(dims=[0])
rec_lo = torch.tensor(w.rec_lo[::-1], dtype=type).flip(dims=[0])
# 生成重构滤波器的组合
rec_filters = torch.stack([
rec_lo.unsqueeze(0) * rec_lo.unsqueeze(1),
rec_lo.unsqueeze(0) * rec_hi.unsqueeze(1),
rec_hi.unsqueeze(0) * rec_lo.unsqueeze(1),
rec_hi.unsqueeze(0) * rec_hi.unsqueeze(1)
], dim=0)
# 扩展滤波器以适应输出通道数
rec_filters = rec_filters[:, None].repeat(out_size, 1, 1, 1)
return dec_filters, rec_filters
# 小波变换
def wavelet_transform(x, filters):
b, c, h, w = x.shape # 获取输入的形状
pad = (filters.shape[2] // 2 - 1, filters.shape[3] // 2 - 1) # 计算填充
# 进行卷积操作
x = F.conv2d(x, filters.to(x.dtype).to(x.device), stride=2, groups=c, padding=pad)
x = x.reshape(b, c, 4, h // 2, w // 2) # 重新调整形状
return x
# 逆小波变换
def inverse_wavelet_transform(x, filters):
b, c, _, h_half, w_half = x.shape # 获取输入的形状
pad = (filters.shape[2] // 2 - 1, filters.shape[3] // 2 - 1) # 计算填充
x = x.reshape(b, c * 4, h_half, w_half) # 重新调整形状
# 进行转置卷积操作
x = F.conv_transpose2d(x, filters.to(x.dtype).to(x.device), stride=2, groups=c, padding=pad)
return x
# 定义小波变换的函数
class WaveletTransform(Function):
@staticmethod
def forward(ctx, input, filters):
ctx.filters = filters # 保存滤波器
with torch.no_grad():
x = wavelet_transform(input, filters) # 执行小波变换
return x
@staticmethod
def backward(ctx, grad_output):
grad = inverse_wavelet_transform(grad_output, ctx.filters) # 计算梯度
return grad, None
# 定义逆小波变换的函数
class InverseWaveletTransform(Function):
@staticmethod
def forward(ctx, input, filters):
ctx.filters = filters # 保存滤波器
with torch.no_grad():
x = inverse_wavelet_transform(input, filters) # 执行逆小波变换
return x
@staticmethod
def backward(ctx, grad_output):
grad = wavelet_transform(grad_output, ctx.filters) # 计算梯度
return grad, None
# 定义基于小波变换的卷积层
class WTConv2d(nn.Module):
def __init__(self, in_channels, out_channels, kernel_size=5, stride=1, bias=True, wt_levels=1, wt_type='db1'):
super(WTConv2d, self).__init__()
assert in_channels == out_channels # 输入通道数必须等于输出通道数
self.in_channels = in_channels
self.wt_levels = wt_levels
self.stride = stride
# 创建小波滤波器
self.wt_filter, self.iwt_filter = create_wavelet_filter(wt_type, in_channels, in_channels, torch.float)
self.wt_filter = nn.Parameter(self.wt_filter, requires_grad=False) # 不需要训练的小波滤波器
self.iwt_filter = nn.Parameter(self.iwt_filter, requires_grad=False) # 不需要训练的逆小波滤波器
# 初始化小波变换和逆小波变换的函数
self.wt_function = wavelet_transform_init(self.wt_filter)
self.iwt_function = inverse_wavelet_transform_init(self.iwt_filter)
# 基础卷积层
self.base_conv = nn.Conv2d(in_channels, in_channels, kernel_size, padding='same', stride=1, groups=in_channels, bias=bias)
self.base_scale = _ScaleModule([1, in_channels, 1, 1]) # 缩放模块
# 小波卷积层
self.wavelet_convs = nn.ModuleList(
[nn.Conv2d(in_channels * 4, in_channels * 4, kernel_size, padding='same', stride=1, groups=in_channels * 4, bias=False) for _ in range(self.wt_levels)]
)
self.wavelet_scale = nn.ModuleList(
[_ScaleModule([1, in_channels * 4, 1, 1], init_scale=0.1) for _ in range(self.wt_levels)]
)
# 如果步幅大于1,定义步幅操作
if self.stride > 1:
self.stride_filter = nn.Parameter(torch.ones(in_channels, 1, 1, 1), requires_grad=False)
self.do_stride = lambda x_in: F.conv2d(x_in, self.stride_filter.to(x_in.dtype).to(x_in.device), bias=None, stride=self.stride, groups=in_channels)
else:
self.do_stride = None
def forward(self, x):
# 前向传播
x_ll_in_levels = [] # 存储低频信息
x_h_in_levels = [] # 存储高频信息
shapes_in_levels = [] # 存储形状信息
curr_x_ll = x # 当前低频输入
# 小波变换阶段
for i in range(self.wt_levels):
curr_shape = curr_x_ll.shape
shapes_in_levels.append(curr_shape) # 记录当前形状
if (curr_shape[2] % 2 > 0) or (curr_shape[3] % 2 > 0):
curr_pads = (0, curr_shape[3] % 2, 0, curr_shape[2] % 2) # 处理奇数尺寸
curr_x_ll = F.pad(curr_x_ll, curr_pads) # 填充
curr_x = self.wt_function(curr_x_ll) # 执行小波变换
curr_x_ll = curr_x[:, :, 0, :, :] # 取低频部分
shape_x = curr_x.shape
curr_x_tag = curr_x.reshape(shape_x[0], shape_x[1] * 4, shape_x[3], shape_x[4]) # 重新调整形状
curr_x_tag = self.wavelet_scale[i](self.wavelet_convs[i](curr_x_tag)) # 执行小波卷积
curr_x_tag = curr_x_tag.reshape(shape_x) # 还原形状
x_ll_in_levels.append(curr_x_tag[:, :, 0, :, :]) # 存储低频信息
x_h_in_levels.append(curr_x_tag[:, :, 1:4, :, :]) # 存储高频信息
next_x_ll = 0 # 初始化下一个低频输出
# 逆小波变换阶段
for i in range(self.wt_levels - 1, -1, -1):
curr_x_ll = x_ll_in_levels.pop() # 取出低频信息
curr_x_h = x_h_in_levels.pop() # 取出高频信息
curr_shape = shapes_in_levels.pop() # 取出形状信息
curr_x_ll = curr_x_ll + next_x_ll # 结合当前低频和上一个低频
curr_x = torch.cat([curr_x_ll.unsqueeze(2), curr_x_h], dim=2) # 合并低频和高频信息
next_x_ll = self.iwt_function(curr_x) # 执行逆小波变换
next_x_ll = next_x_ll[:, :, :curr_shape[2],