如何通过图像表达来操纵像素值?

How to manipulate pixel values via image expression?

最近我正在编写一个脚本来处理图像中的像素值。这个想法是将落入给定范围的像素设置为特定值。没有使用从一个像素到另一个像素循环的命令“for”,而是使用了一个图像表达式,例如:

Img = (Img>=thresh_Low && Img<=thresh_Up ? 0 : Img)

问题来了:如果我想用相邻像素的平均值替换像素值,而不是在上述情况下只是一个固定值,例如 0,像素循环似乎无法避免了。有谁知道图像表达的方法在这里仍然可以使用的任何解决方法吗?

提前致谢。

计算图像表达式比任何逐像素运算都高效得多。即使您因此计算了一些不需要的平均值,脚本的执行速度也会快得多。因此:

You should compute an average-image (for all pixels, not just the masked ones) and then use it in the masked assignment.

以下示例说明了这一点。只有最后两行是您问题的直接答案。条件用于复制原始值或平均值:

number aver_NN = 3      // Next neighbor averaging. 1 = 3x3, 2 = 5x5 etc.)
number maskRad = 0.3    // just a radius to show masking

image img := GetFrontImage()
if ( 2 != img.ImageGetNumDimensions() ) Throw( "Only 2D images are supported." )

// Create average image (ignoring border region for simplicity)
image av := img * 0
for( number dx=-aver_NN; dx<=aver_NN; dx++ )
    for( number dy=-aver_NN; dy<=aver_NN; dy++ )
        av += img.offset(dx,dy)

av /= (2*aver_NN + 1) ** 2

// Apply masked replacement
image replaced = iradius < iwidth*maskrad ? av : img
replaced.ShowImage()

根据下面的评论进一步解释

平均是通过使用 offset 命令将整个图像移动一个像素来完成的。此命令将用 0 值替换边框像素。 因此,将所有移位的图像相加并除以图像的数量可以得出每个像素的相邻像素的平均值,但边界像素的归一化是不正确的。以下脚本使用显式图像而不是 for 循环来显示这一点:


number size = 25
image test := realimage("Source",4,size,size)
test = 1 + random()
test.ShowImage()

image offset_N = test.offset(  0, -1 )
image offset_S = test.offset(  0,  1 )
image offset_W = test.offset( -1,  0 )
image offset_E = test.offset(  1,  0 )
offset_N.ShowImage()
offset_N.SetName("N")
offset_S.ShowImage()
offset_S.SetName("S")
offset_W.ShowImage()
offset_W.SetName("W")
offset_E.ShowImage()
offset_E.SetName("E")

image average = test + offset_N + offset_S + offset_W + offset_E
average /= 5
average.SetName("Average")
average.ShowImage()

EGUPerformActionWithAllShownImages("Arrange")

要解决边界问题,可以使用两种策略进行标准化。

  • 明确归一化图像和的子部分,知道有多少图像求和:
...

image average = test + offset_N + offset_S + offset_W + offset_E
average.SetName("Average")
// Divide corners by 3
// Divide edges by 4
// Divide rest by 5
average.slice2(0,0,0 ,0,2,size-1, 1,2,size-1) /= 3
average.slice2(1,0,0 ,0,size-2,1, 1,2,size-1) /= 4
average.slice2(0,1,0 ,0,2,size-1, 1,size-2,1) /= 4
average.slice2(1,1,0 ,0,size-2,1, 1,size-2,1) /= 5

...
  • 自动创建第二个图像 'counts' 并将其用于规范化。为此,简单地创建一个与源大小相同的 1 值图像并执行相同的求和步骤!这使得上面的脚本变为:
number aver_NN = 2      // Next neighbor averaging. 1 = 3x3, 2 = 5x5 etc.)
number maskRad = 1    // just a radius to show masking

image img := GetFrontImage()
if ( 2 != img.ImageGetNumDimensions() ) Throw( "Only 2D images are supported." )

// Create average image 
image av = img * 0
image weight = av 
image proxy = av + 1

for( number dx=-aver_NN; dx<=aver_NN; dx++ )
{
    for( number dy=-aver_NN; dy<=aver_NN; dy++ )
    {
        av += img.offset(dx,dy)
        weight += proxy.offset(dx,dy)
    }
}

weight.SetName("Sum weight")
weight.showImage()

av /= weight

// Apply masked replacement
image replaced = iradius < iwidth*maskrad ? av : img
replaced.ShowImage()
  • 还可以依靠内置的 Convolution() 命令创建平均图像,该命令可以立即正确处理边界情况。在这里,我们只需将平均图像创建为:
// Create average image 
// Define an averaging kernel
image kernel := [5,5] : {
 { 0, 0, 1, 0, 0 },
 { 0, 1, 1, 1, 0 },
 { 1, 1, 1, 1, 1 },
 { 0, 1, 1, 1, 0 },
 { 0, 0, 1, 0, 0 } 
}

image av = img.Convolution(kernel)
av.ShowImage()