在没有 SVD 的情况下分解可分离整数 2D 滤波器系数的快速/有效方法

信息处理 过滤器 可分离性 1天 二维
2022-01-11 00:03:47

我希望能够快速确定给定的整数系数 2D 内核是否可分离为具有整数系数的两个 1D 内核。例如

 2   3   2
 4   6   4
 2   3   2

可分为

 2   3   2

 1
 2
 1

使用整数算术对可分离性的实际测试似乎相当简单,但分解成具有整数系数的一维滤波器被证明是一个更困难的问题。困难似乎在于行或列之间的比率可能是非整数(有理分数),例如,在上面的示例中,比率为 2、1/2、3/2 和 2/3。

我真的不想使用像 SVD 这样的重型方法,因为 (a) 对于我的需要而言,它的计算成本相对较高,并且 (b) 它仍然不一定有助于确定整数系数。

有任何想法吗 ?


更多信息

系数可以是正数、负数或零,并且可能存在其中一个或两个一维向量之和为零的病理情况,例如

-1   2  -1
 0   0   0
 1  -2   1

可分为

 1  -2   1

-1
 0
 1
4个回答

我已经接受了@Phonon的答案并对其进行了一些修改,以便它仅在顶行和左列上使用 GCD 方法,而不是在行/列总和上。这似乎更好地处理病理病例。如果顶行或左列全为零,它仍然会失败,但可以在应用此方法之前检查这些情况。

function [X, Y, valid] = separate(M)    % separate 2D kernel M into X and Y vectors 
  X = M(1, :);                          % init X = top row of M
  Y = M(:, 1);                          % init Y = left column of M
  nx = numel(X);                        % nx = no of columns in M
  ny = numel(Y);                        % ny = no of rows in M
  gx = X(1);                            % gx = GCD of top row
  for i = 2:nx
    gx = gcd(gx, X(i));
  end
  gy = Y(1);                            % gy = GCD of left column
  for i = 2:ny
    gy = gcd(gy, Y(i));
  end
  X = X / gx;                           % scale X by GCD of X
  Y = Y / gy;                           % scale Y by GCD of Y
  scale = M(1, 1) / (X(1) * Y(1));      % calculate scale factor
  X = X * scale;                        % apply scale factor to X
  valid = all(all((M == Y * X)));       % result valid if we get back our original M
end

非常感谢@Phonon@Jason R为此提供了最初的想法。

知道了!贴MATLAB代码,今晚或者明天发解释

% Two original arrays
N = 3;
range = 800;
a = round( range*(rand(N,1)-0.5) )
b = round( range*(rand(1,N)-0.5) )

% Create a matrix;
M = a*b;
N = size(M,1);

% Sanity check
disp([num2str(rank(M)) ' <- this should be 1!']);

% Sum across rows and columns
Sa = M * ones(N,1);
Sb = ones(1,N) * M;

% Get rid of zeros
SSa = Sa( Sa~=0 );
SSb = Sb( Sb~=0 );

if isempty(SSa) | isempty(SSb)
    break;
end

% Sizes of array without zeros
Na = numel(SSa);
Nb = numel(SSb);

% Find Greatest Common Divisor of Sa and Sb.
Ga = SSa(1);
Gb = SSb(1);

for l=2:Na
    Ga = gcd(Ga,SSa(l));
end

for l=2:Nb
    Gb = gcd(Gb,SSb(l));
end

%Divide by the greatest common divisor
Sa = Sa / Ga;
Sb = Sb / Gb;

%Scale one of the vectors
MM = Sa * Sb;
Sa = Sa * (MM(1) / M(1));

disp('Two arrays found:')
Sa
Sb
disp('Sa * Sb = ');
Sa*Sb
disp('Original = ');
M

也许我正在轻视这个问题,但似乎你可以:

  • 打破N-经过-M矩阵A成行ai,i=0,1,,N1.
  • 对于每行索引j>0

    • 按元素划分aj经过a0产生行中每个元素的比率j与第 0 行的对应项rj. 这需要使用浮点数或其他一些小数运算来完成。
    • 检查中的元素rj来确定它们是否是常数。您需要在此比较中允许一些容差以允许浮点舍入。
    • 如果值在rj是恒定的,然后行aj是行的标量倍数a0. 存储行的比率j0在一个列表中x.
    • 如果比率向量不是常数,则行aj是行的标量倍数a0,因此您将无法以这种方式分解矩阵。停止检查。
  • 如果在上述测试中所有行都被认为是第 0 行的常数倍数,则取行比率标量列表x并按最小比率对其进行归一化:

xk,norm=xkmini=0N1xi

  • 这样做之后,比率的标准化列表将包含具有最小范数的行的值 1。您想看看是否可以以某种方式缩放此列表以生成包含所有整数系数的列表。蛮力方法可以只进行线性搜索,即计算: 对于每个值,在计算比例的比例列表后,您需要检查每个比例以查看它们是否为整数值(同样,有一定的公差)。设置为您愿意在行间比率中寻找的最大分母。xnorm
    xscaled=Kxnorm,K=1,2,,M
    KM

不是最优雅的方法,并且可能有更好的方法,但它应该可以工作,实现起来非常简单,并且对于中等大小的矩阵应该相对较快。

另一种方法是找到一个可分离的近似 到你的内核,看看它有多接近。两种方法,即最小化 : 1) 蛮力优化 ; 这需要时间〜它们的长度的总和,而不是乘积 2) 对于固定,最优只是一个投影,所以 依次xyzA|Axyz|
x y z
yzxx y z x y z ...

(来自math.stackexchange 上的近似卷积作为可分离卷积 的总和。)