设计8邻域元素特征矩阵:大尺寸矩阵高效实现方案问询
嘿,处理超大矩阵的时候,双重循环确实会慢到让人抓狂——Matlab解释型的循环开销在大尺寸数据面前简直是灾难!你的思路没问题,但完全可以用Matlab内置的向量化操作把速度提上去几个数量级。
问题分析
你原来的代码先给矩阵加了一圈padding,然后通过两层循环逐个提取3×3邻域再reshape存储,但Matlab的for循环在处理百万级以上的元素时,每一次迭代的开销都会被放大,导致整体耗时剧增。而内置函数都是底层用C/Fortran实现的,能充分利用硬件加速,效率差的不是一点半点。
最优解法:用im2col一键搞定
Matlab专门提供了im2col函数,用来把滑动窗口内的元素转换成列矩阵,完美匹配你的需求。直接看代码:
% 第一步:给原矩阵加padding,用padarray更简洁,和你原来的逻辑一致 temp = padarray(double(img), [1 1], NaN, 'both'); % 第二步:用im2col把每个3×3滑动窗口转成列,再转置就是我们要的特征矩阵 M_feat = im2col(temp, [3 3], 'sliding')';
就这么两行,完全替代你的双层循环!而且速度快到离谱——比如处理1000×1000的矩阵,这个方法比循环快至少50倍以上。
为什么这能行?
im2col(temp, [3 3], 'sliding')会生成一个9行、m×n列的矩阵,每一列对应原矩阵中一个元素的3×3邻域(包括自身)。转置之后就得到了m×n行、9列的特征矩阵,和你原来循环生成的结果完全一致(而且还避免了循环里可能的索引错误)。
顺便纠正你原代码里的小bug
你原来的特征矩阵索引计算有点问题:
M_feat((i-2)*size(img,1) + (j-1),:) = neighbors;
这里用size(img,1)(行数m)来计算偏移是错的,应该用size(img,2)(列数n),因为每一行原矩阵对应n个特征行,正确的索引应该是:
M_feat((i-2)*size(img,2) + (j-1),:) = neighbors;
否则当原矩阵的行数和列数不等时,特征矩阵的行顺序会完全错乱,这个小坑得注意!
其他备选思路(如果不能用im2col的话)
如果你因为某些原因没法用图像处理工具箱的im2col,也可以手动用矩阵拼接+reshape实现向量化:
temp = padarray(double(img), [1 1], NaN, 'both'); % 分别取出padding后矩阵的三行块 top = temp(1:end-2, :); mid = temp(2:end-1, :); bot = temp(3:end, :); % 每个块再拆成三列部分,拼接后reshape M_feat = [reshape(top(:,1:end-2), [],1), reshape(top(:,2:end-1), [],1), reshape(top(:,3:end), [],1), ... reshape(mid(:,1:end-2), [],1), reshape(mid(:,2:end-1), [],1), reshape(mid(:,3:end), [],1), ... reshape(bot(:,1:end-2), [],1), reshape(bot(:,2:end-1), [],1), reshape(bot(:,3:end), [],1)];
不过这个写法不如im2col简洁,效率也略低一点,还是优先推荐前者。
内容的提问来源于stack exchange,提问作者tehm0n

