问题描述
最近我正在编写一个脚本来操作图像中的像素值。这个想法是将落入给定范围的像素设置为特定值。没有使用从像素到像素循环的命令“for”,而是使用了图像表达式,例如:
Img = (Img>=thresh_Low && Img<=thresh_Up ? 0 : Img)
问题来了:如果我想用相邻像素的平均值替换像素值,而不仅仅是上面情况下的固定值(例如 0),像素循环似乎无法避免了。有没有人知道这里仍然可以使用图像表达方法的任何解决方法?
提前致谢。
解决方法
计算图像表达式比任何逐像素操作都要高效得多。即使您因此计算了一些不需要的平均值,脚本的执行速度也会快得多。因此:
您应该计算平均图像(对于所有像素,而不仅仅是蒙版像素),然后在蒙版分配中使用它。
以下示例说明了这一点。只有最后两行是您问题的直接答案。条件用于复制原始值或平均值:
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,2,size-1,1,size-1) /= 3
average.slice2(1,size-2,size-1) /= 4
average.slice2(0,1) /= 4
average.slice2(1,1) /= 5
...
- 创建第二张自动“计数”的图像并将其用于规范化。为此,只需创建一个与源大小相同的
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 },{ 0,{ 1,1 },0 }
}
image av = img.Convolution(kernel)
av.ShowImage()