在SQL中实现卷积

在SQL中实现卷积,sql,postgresql,image-processing,convolution,Sql,Postgresql,Image Processing,Convolution,我有一个表d,其中包含字段x,y,f,(PK是x,y),我想实现卷积,其中定义了一个新列c,作为给定任意内核的卷积(2D)。在过程语言中,这很容易定义(见下文)。我相信它可以使用联接在SQL中定义,但我在这样做时遇到了困难 在程序语言中,我会: def conv(x, y): c = 0 # x_ and y_ are pronounced "x prime" and "y prime", # and take on *all* x and y values in

我有一个表
d
,其中包含字段
x,y,f
,(PK是
x,y
),我想实现卷积,其中定义了一个新列
c
,作为给定任意内核的卷积(2D)。在过程语言中,这很容易定义(见下文)。我相信它可以使用联接在SQL中定义,但我在这样做时遇到了困难

在程序语言中,我会:

def conv(x, y):
     c = 0
     # x_ and y_ are pronounced "x prime" and "y prime", 
     # and take on *all* x and y values in the table; 
     # that is, we iterate through *all* rows
     for all x_, y_
         c += f(x_, y_) * kernel(x_ - x, y_ - y)
     return c
内核
可以是任意函数。在我的例子中,它是
1/k^(sqrt(x_dist^2,y_dist^2))
,内核(0,0)=1

出于性能原因,我们不需要查看每个
x\ux,y\ux
。我们可以在距离小于阈值的地方对其进行过滤

我认为这可以通过使用笛卡尔乘积联接、聚合SQL和以及WHERE子句来实现

在SQL中执行此操作的另一个挑战是空值。一个幼稚的实现会将它们视为零。我想做的是将内核作为加权平均值处理,而不考虑空值。也就是说,我将使用函数
wkernel
作为我的内核,并将上面的代码修改为:

def conv(x, y):
     c = 0
     w = 0
     for all x_, y_  
         c += f(x_, y_) * wkernel(x_ - x, y_ - y)
         w += wkernel(x_ - x, y_ - y)
     return c/w
这将使NULLs工作得很好

澄清一下:你不能有一个局部观测,其中x=NULL,y=3。但是,您可能会丢失观察值,例如,在x=2和y=3的位置没有记录。我把它称为NULL,因为整个记录都丢失了。我上面的程序代码可以处理这个问题

我相信以上可以在SQL中实现(假设
wkernel
已经作为一个函数实现),但我不知道如何实现。我用的是Postgres9.4


样本表:

 Table d
 x  | y  | f
 0  | 0  | 1.4
 1  | 0  | 2.3
 0  | 1  | 1.7
 1  | 1  | 1.2
输出(仅显示一行):


卷积是图像处理和信号处理中使用的一种标准算法,我相信它可以在SQL中完成,这对于我们现在使用的大型数据集非常有用。

我假设了一个函数wkernel,例如:

create or replace function wkernel(k numeric, xdist numeric, ydist numeric)
returns numeric language sql as $$
  select 1. / pow(k, sqrt(xdist*xdist + ydist*ydist))
$$;
以下查询提供了所需内容,但不限制关闭值:

select d1.x, d1.y, SUM(d2.f*wkernel(2, d2.x-d1.x, d2.y-d1.y)) AS c
from d d1 cross join d d2 
group by d1.x, d1.y;

 x | y |            c            
---+---+-------------------------
 0 | 0 | 3.850257072695778143380
 1 | 0 | 4.237864186319019036455
 0 | 1 | 3.862992722666908108145
 1 | 1 | 3.725299918145074500610
(4 rows)
有一些任意的限制:

select d1.x, d1.y, SUM(d2.f*wkernel(2, d2.x-d1.x, d2.y-d1.y)) AS c
from d d1 cross join d d2
where abs(d2.x-d1.x)+abs(d2.y-d1.y) < 1.1
group by d1.x, d1.y;

 x | y |            c            
---+---+-------------------------
 0 | 0 | 3.400000000000000000000
 1 | 0 | 3.600000000000000000000
 0 | 1 | 3.000000000000000000000
 1 | 1 | 3.200000000000000000000
(4 rows)
生成完整的值集(调用此关系已完成):

现在我们只需要使用前面给出的查询以及cfg和completed关系来计算值。请注意,我们不计算边界上的值的卷积:

SELECT d1.x, d1.y, SUM(d2.f*wkernel(k, d2.x-d1.x, d2.y-d1.y)) / SUM(wkernel(k, d2.x-d1.x, d2.y-d1.y)) AS c
FROM cfg cross join completed d1 cross join completed d2
WHERE d1.x BETWEEN minx AND maxx
  AND d1.y BETWEEN miny AND maxy
  AND abs(d2.x-d1.x)+abs(d2.y-d1.y) <= scope
GROUP BY d1.x, d1.y;

 x | y |            c            
---+---+-------------------------
 0 | 0 | 1.400000000000000000000
 0 | 1 | 1.700000000000000000000
 1 | 0 | 2.300000000000000000000
 1 | 1 | 1.200000000000000000000
(4 rows)
选择d1.x,d1.y,SUM(d2.f*wkernel(k,d2.x-d1.x,d2.y-d1.y))/SUM(wkernel(k,d2.x-d1.x,d2.y-d1.y))作为c
从cfg交叉连接完成d1交叉连接完成d2
其中d1.x在minx和maxx之间
d1.y在miny和maxy之间

和abs(d2.x-d1.x)+abs(d2.y-d1.y)我假设了一个函数wkernel,例如:

create or replace function wkernel(k numeric, xdist numeric, ydist numeric)
returns numeric language sql as $$
  select 1. / pow(k, sqrt(xdist*xdist + ydist*ydist))
$$;
以下查询提供了所需内容,但不限制关闭值:

select d1.x, d1.y, SUM(d2.f*wkernel(2, d2.x-d1.x, d2.y-d1.y)) AS c
from d d1 cross join d d2 
group by d1.x, d1.y;

 x | y |            c            
---+---+-------------------------
 0 | 0 | 3.850257072695778143380
 1 | 0 | 4.237864186319019036455
 0 | 1 | 3.862992722666908108145
 1 | 1 | 3.725299918145074500610
(4 rows)
有一些任意的限制:

select d1.x, d1.y, SUM(d2.f*wkernel(2, d2.x-d1.x, d2.y-d1.y)) AS c
from d d1 cross join d d2
where abs(d2.x-d1.x)+abs(d2.y-d1.y) < 1.1
group by d1.x, d1.y;

 x | y |            c            
---+---+-------------------------
 0 | 0 | 3.400000000000000000000
 1 | 0 | 3.600000000000000000000
 0 | 1 | 3.000000000000000000000
 1 | 1 | 3.200000000000000000000
(4 rows)
生成完整的值集(调用此关系已完成):

现在我们只需要使用前面给出的查询以及cfg和completed关系来计算值。请注意,我们不计算边界上的值的卷积:

SELECT d1.x, d1.y, SUM(d2.f*wkernel(k, d2.x-d1.x, d2.y-d1.y)) / SUM(wkernel(k, d2.x-d1.x, d2.y-d1.y)) AS c
FROM cfg cross join completed d1 cross join completed d2
WHERE d1.x BETWEEN minx AND maxx
  AND d1.y BETWEEN miny AND maxy
  AND abs(d2.x-d1.x)+abs(d2.y-d1.y) <= scope
GROUP BY d1.x, d1.y;

 x | y |            c            
---+---+-------------------------
 0 | 0 | 1.400000000000000000000
 0 | 1 | 1.700000000000000000000
 1 | 0 | 2.300000000000000000000
 1 | 1 | 1.200000000000000000000
(4 rows)
选择d1.x,d1.y,SUM(d2.f*wkernel(k,d2.x-d1.x,d2.y-d1.y))/SUM(wkernel(k,d2.x-d1.x,d2.y-d1.y))作为c
从cfg交叉连接完成d1交叉连接完成d2
其中d1.x在minx和maxx之间
d1.y在miny和maxy之间

和abs(d2.x-d1.x)+abs(d2.y-d1.y)如果你展示相关的表格会有所帮助。另外,将空值视为0的假设是错误的。@vkp示例表已添加。虽然空值通常不被视为零,但在本例中,使用我展示的算法,它们最终被视为零。(省略
c+=
与添加零相同。)如何评估
1.4(0,0)
?你的公式有点不对劲。什么是
X_
Y_
?所示表格的输出应该是什么样子?@GordonLinoff
f(0,0)=1.4;f(1,0)=2.3;f(X,Y)=其中X=X和Y=Y的行中f的值
。有意义吗?如果你展示相关的表格会有所帮助。另外,将空值视为0的假设是错误的。@vkp示例表已添加。虽然空值通常不被视为零,但在本例中,使用我展示的算法,它们最终被视为零。(省略
c+=
与添加零相同。)如何评估
1.4(0,0)
?你的公式有点不对劲。什么是
X_
Y_
?所示表格的输出应该是什么样子?@GordonLinoff
f(0,0)=1.4;f(1,0)=2.3;f(X,Y)=其中X=X和Y=Y的行中f的值
。有意义吗?太棒了。我澄清了这个问题,以解释我所说的空值是什么意思:完全缺失的观测值。这说明了吗?你能在回答中解释一下吗?我关心的是,在每次迭代之后,w都会被替换,而不是更新。。。(你计算一个w(n-1)的值,不需要任何代价,最后一次迭代就可以确定w的值)!修正了。好吧,那么整个空值问题与您的问题无关。处理我所看到的缺失信息的唯一方法是首先将整个信息生成为一个视图,然后处理它。然而,这将不是很有效。大约一小时后回来;-)@FabianPijcke我认为你不需要空检查。为(x,y)网格添加缺少的数据点需要一个(2D)日历表+左键连接此carthesian产品。不过,我不认为空单元格会增加总数。太棒了。我澄清了这个问题,以解释我所说的空值是什么意思:完全缺失的观测值。这说明了吗?你能在回答中解释一下吗?我关心的是,在每次迭代之后,w都会被替换,而不是更新。。。(你计算一个w(n-1)的值,不需要任何代价,最后一次迭代就可以确定w的值)!修正了。好吧,那么整个空值问题与您的问题无关。处理我所看到的丢失信息的唯一方法是首先
WITH cfg(minx, maxx, miny, maxy, scope, k) AS (
  SELECT MIN(x), MAX(x), MIN(y), MAX(y), 1, 2
  FROM d
), completed(x, y, f) AS (
  SELECT x.*, y.*, COALESCE(f, 0)
  FROM cfg
  CROSS JOIN generate_series(minx - scope, maxx + scope) x
  CROSS JOIN generate_series(miny - scope, maxy + scope) y
  LEFT JOIN d ON d.x = x.* AND d.y = y.*
)
SELECT d1.x, d1.y, SUM(d2.f*wkernel(k, d2.x-d1.x, d2.y-d1.y)) / SUM(wkernel(k, d2.x-d1.x, d2.y-d1.y)) AS c
FROM cfg cross join completed d1 cross join completed d2
WHERE d1.x BETWEEN minx AND maxx
  AND d1.y BETWEEN miny AND maxy
  AND abs(d2.x-d1.x)+abs(d2.y-d1.y) <= scope
GROUP BY d1.x, d1.y;