【问题标题】:How to manipulate pixel values via image expression?如何通过图像表达来操作像素值?
【发布时间】:2021-01-08 19:34:12
【问题描述】:

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

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

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

提前致谢。

【问题讨论】:

    标签: image expression pixel dm-script manipulate


    【解决方案1】:

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

    您应该计算一个平均图像(针对所有像素,而不仅仅是蒙版的像素),然后在蒙版分配中使用它。

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

    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()
    

    下面由于cmets的进一步解释

    平均是通过使用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
    
    ...
    
    • 创建第二张自动“计数”的图像并将其用于标准化。为此,只需创建一个与源大小相同的1-valued 图像并执行相同的求和步骤!这使得上面的脚本变成:
    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()
    

    【讨论】:

    • 非常感谢您提供这些有用的信息。你的意思是“for”循环仍然是准备掩码所必需的,即平均图像?顺便说一句,我在如何计算附近像素的平均值作为掩码方面非常挣扎,尤其是在边界处,因为 for 循环会报告错误消息。进一步的例子将不胜感激。
    • @DMAdventurer 是的,但这是平均移位的 for 循环,而不是像素。要计算平均图像,您需要对在每个方向上偏移一或两个像素的图像求和(因此 9 或 25 次迭代)。因此,该图像在每个像素中都具有来自原始像素及其相邻像素的值。上述脚本中的 for 循环不会在边界区域产生错误,因为 'offset' 命令适用于图像并且只是对缺失的像素进行零填充。我将在上面的编辑中添加另一个示例。
    • 另外...计算像素平均图像似乎只使用简单的数学,但它是有效的。我的大脑太复杂了。同时,卷积方法对我来说非常酷。欠你一杯啤酒。
    • 一个附属问题。我正在尝试添加一个附加功能来将这种图像处理应用于局部区域而不是整个图像,例如,初步分配的 ROI。事实上,我首先想到的是再次使用 for 循环,但给定 x 和 y 坐标。是否可以将这个想法融入其中,但仍然使用图像表达方式?
    • @DMAdventurer 如果该区域是一个常规的矩形子数组,您只需使用 slice2() 命令,然后像使用输入图像一样使用它。
    猜你喜欢
    • 1970-01-01
    • 2018-10-11
    • 2014-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-15
    • 2010-12-08
    相关资源
    最近更新 更多