【问题标题】:How does CIMedianFilter works? (Algorithm)CIMedianFilter 是如何工作的? (算法)
【发布时间】:2013-12-19 12:25:28
【问题描述】:

CIMedianFilter 是如何工作的?我的意思是它的算法,我想要它来消除噪音,我试着用这个代码来做:

                // -------------------------- W O R K I N G   O N   R E D -----------------
                // red pixels
                NSNumber *red1 = [NSNumber numberWithInt:rgbaPixel1[3]];
                NSNumber *red2 = [NSNumber numberWithInt:rgbaPixel2[3]];
                NSNumber *red3 = [NSNumber numberWithInt:rgbaPixel3[3]];
                NSNumber *red4 = [NSNumber numberWithInt:rgbaPixel4[3]];
                NSNumber *red5 = [NSNumber numberWithInt:rgbaPixel5[3]];

                // red array
                NSMutableArray *redArray = [NSMutableArray arrayWithObjects:red1, red2, red3, red4, red5, nil];
                // sorting
                NSSortDescriptor *lowToHigh = [NSSortDescriptor sortDescriptorWithKey:@"self" ascending:YES];
                [redArray sortUsingDescriptors:[NSArray arrayWithObject:lowToHigh]];
                // getting median
                int redMedian = [[redArray objectAtIndex:2] intValue];
                // setting the pixels red value to the median
                rgbaPixel1[3] = redMedian;
                 /////////////////////////////////testing if sorting and median is true
                 //            NSLog(@"Sir, here's a test (%@, %@, %@, %@, %@) and the median is %i", [redArray objectAtIndex:0],
                 //                                                                                    [redArray objectAtIndex:1],
                 //                                                                                    [redArray objectAtIndex:2],
                 //                  [redArray objectAtIndex:3], [redArray objectAtIndex:4], shit);
                // ---------------------------- E N D   O F   R E D ------------------------

                // ----------------------------- W O R K I N G   O N   G R E E N ---------------
                // getting green pixels first
                NSNumber *green1 = [NSNumber numberWithInteger:rgbaPixel1[2]];
                NSNumber *green2 = [NSNumber numberWithInteger:rgbaPixel2[2]];
                NSNumber *green3 = [NSNumber numberWithInteger:rgbaPixel3[2]];
                NSNumber *green4 = [NSNumber numberWithInteger:rgbaPixel4[2]];
                NSNumber *green5 = [NSNumber numberWithInteger:rgbaPixel5[2]];

                // creating array of greens
                NSMutableArray *greenArray = [NSMutableArray arrayWithObjects:green1, green2, green3, green4, green5, nil];
                // sorting the array
                [greenArray sortUsingDescriptors:[NSArray arrayWithObject:lowToHigh]];
                // getting the median
                int greenMedian = [[greenArray objectAtIndex:2] intValue];

                // setting the pixels green value to median value
                rgbaPixel1[2] = greenMedian;
                // ---------------------------- E N D   O F   G R E E N ------------------------

                // -------------------------- W O R K I N G   O N  B L U E ---------------------
                // getting blue pixel
                NSNumber *blue1 = [NSNumber numberWithInteger:rgbaPixel1[1]];
                NSNumber *blue2 = [NSNumber numberWithInteger:rgbaPixel2[1]];
                NSNumber *blue3 = [NSNumber numberWithInteger:rgbaPixel3[1]];
                NSNumber *blue4 = [NSNumber numberWithInteger:rgbaPixel4[1]];
                NSNumber *blue5 = [NSNumber numberWithInteger:rgbaPixel5[1]];

                // creating array for blue values
                NSMutableArray *blueArray = [NSMutableArray arrayWithObjects:blue1, blue2, blue3, blue4, blue5, nil];
                // sorting the array of blues
                [blueArray sortUsingDescriptors:[NSArray arrayWithObject:lowToHigh]];
                // getting the median
                int blueMedian = [[blueArray objectAtIndex:2] intValue];


                // setting pixel blue value to the median we just got :)
                rgbaPixel1[1] = blueMedian;

                // --------------------------------- E N D   O F   B L U E ----------------------

但它没有那么大的效果!或者也许我以错误的方式获取 RGB 值,我真的需要一些帮助。

【问题讨论】:

    标签: ios iphone uiimage core-image cgimage


    【解决方案1】:

    我不能代表 CIMedianFilter,因为我不知道 Core Image 在那里做什么的细节,但我已经为我的 GPUImage 框架编写了一个中值滤波器,我可以在那里描述这个过程。

    首先,我应该说,在迭代图像的像素时,您在上面使用 NSNumber 和 NSMutableArray 中的大量对象所做的事情在性能方面会很糟糕。此外,所有这些自动释放对象的内存管理将是棘手的。为此,您至少需要使用标量类型和 C 数组,以及用于排序的内联函数。更好的是,您可以将其迁移到 GPU。

    我在 GPUImage 中基于 GPU 的实现基于 Morgan McGuire 和 Kyle Whitson 在 ShaderX6 中的 "A Fast, Small-Radius GPU Median Filter" 章节。本文描述了一些可用于加速片段着色器中 GPU 端中值过滤的优化。我在片段着色器中的 3x3 中值滤波器实现如下所示:

     precision highp float;
    
     varying vec2 textureCoordinate;
     varying vec2 leftTextureCoordinate;
     varying vec2 rightTextureCoordinate;
    
     varying vec2 topTextureCoordinate;
     varying vec2 topLeftTextureCoordinate;
     varying vec2 topRightTextureCoordinate;
    
     varying vec2 bottomTextureCoordinate;
     varying vec2 bottomLeftTextureCoordinate;
     varying vec2 bottomRightTextureCoordinate;
    
     uniform sampler2D inputImageTexture;
    
    #define s2(a, b)                temp = a; a = min(a, b); b = max(temp, b);
    #define mn3(a, b, c)            s2(a, b); s2(a, c);
    #define mx3(a, b, c)            s2(b, c); s2(a, c);
    
    #define mnmx3(a, b, c)          mx3(a, b, c); s2(a, b);                                   // 3 exchanges
    #define mnmx4(a, b, c, d)       s2(a, b); s2(c, d); s2(a, c); s2(b, d);                   // 4 exchanges
    #define mnmx5(a, b, c, d, e)    s2(a, b); s2(c, d); mn3(a, c, e); mx3(b, d, e);           // 6 exchanges
    #define mnmx6(a, b, c, d, e, f) s2(a, d); s2(b, e); s2(c, f); mn3(a, b, c); mx3(d, e, f); // 7 exchanges
    
     void main()
     {
         vec3 v[6];
    
         v[0] = texture2D(inputImageTexture, bottomLeftTextureCoordinate).rgb;
         v[1] = texture2D(inputImageTexture, topRightTextureCoordinate).rgb;
         v[2] = texture2D(inputImageTexture, topLeftTextureCoordinate).rgb;
         v[3] = texture2D(inputImageTexture, bottomRightTextureCoordinate).rgb;
         v[4] = texture2D(inputImageTexture, leftTextureCoordinate).rgb;
         v[5] = texture2D(inputImageTexture, rightTextureCoordinate).rgb;
    //     v[6] = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
    //     v[7] = texture2D(inputImageTexture, topTextureCoordinate).rgb;
         vec3 temp;
    
         mnmx6(v[0], v[1], v[2], v[3], v[4], v[5]);
    
         v[5] = texture2D(inputImageTexture, bottomTextureCoordinate).rgb;
    
         mnmx5(v[1], v[2], v[3], v[4], v[5]);
    
         v[5] = texture2D(inputImageTexture, topTextureCoordinate).rgb;
    
         mnmx4(v[2], v[3], v[4], v[5]);
    
         v[5] = texture2D(inputImageTexture, textureCoordinate).rgb;
    
         mnmx3(v[3], v[4], v[5]);
    
         gl_FragColor = vec4(v[4], 1.0);
    }
    

    这对于在 iOS 设备上运行实时视频来说已经足够快了,但是 3x3 半径足够小,以至于您看不到最终图像的显着变化。它提供了少量的空间去噪,但您可能需要扩展到 5x5 的区域才能看到更显着的去噪效果。这也将开始使图像略微模糊,因此需要进行一些权衡。对于视频,您也许可以将其与强度较弱的低通滤波器结合起来,以进行一些时间去噪。

    我将把它留作练习,让您将上述论文改编为大于 3x3 的案例。

    【讨论】:

      【解决方案2】:

      上面代码的 OpenGL ES 3.0 替代方案如下:

      kernel vec4 medianUnsharpKernel(sampler u) {
      vec4 pixel = unpremultiply(sample(u, samplerCoord(u)));
      vec2 xy = destCoord();
      int radius = 3;
      int bounds = (radius - 1) / 2;
      vec4 sum  = vec4(0.0);
      for (int i = (0 - bounds); i <= bounds; i++)
      {
          for (int j = (0 - bounds); j <= bounds; j++ )
          {
              sum += unpremultiply(sample(u, samplerTransform(u, vec2(xy + vec2(i, j)))));
          }
      }
      vec4 mean = vec4(sum / vec4(pow(float(radius), 2.0)));
      float mean_avg = float(mean);
      float comp_avg = 0.0;
      vec4 comp  = vec4(0.0);
      vec4 median  = mean;
      for (int i = (0 - bounds); i <= bounds; i++)
      {
          for (int j = (0 - bounds); j <= bounds; j++ )
          {
              comp = unpremultiply(sample(u, samplerTransform(u, vec2(xy + vec2(i, j)))));
              comp_avg = float(comp);
              median = (comp_avg < mean_avg) ? max(median, comp) : median;
          }
      }
      
      return premultiply(vec4(vec3(abs(pixel.rgb - median.rgb)), 1.0)); 
      }
      

      如您所见,这与旧设备更兼容,仅使用 OpenGL 的一个子集;而且,我相信它可能运行得更快(它更短)。通过将第一次传递的像素值存储到一个数组中,然后从数组中调用它们进行第二次传递,我可能可以将速度提高一倍。

      它也更容易理解,因为它基本上由两个步骤组成: 1. 计算 3x3 邻域中源像素周围像素值的平均值; 2. 找出同一邻域中小于均值的所有像素的最大像素值。 3. [可选] 从源像素值中减去中间像素值进行边缘检测。

      如果您使用中值进行边缘检测,有几种方法可以修改上述代码以获得更好的结果,即混合中值过滤和截断媒体过滤(替代和更好的“模式”过滤) .如果您有兴趣,请询问。

      【讨论】:

      • 我不明白你的算法从平均值计算中位数。对于数组(0,100,100),均值是67,最大值
      • 我复制了 Brad Larson 的过滤器,准确无误。如果数学没有加起来,不要看我。有一百万个网站告诉你如何从平均值计算中位数(?);如有必要,请根据您自己的需要参考其中任何一个。
      猜你喜欢
      • 2013-12-04
      • 2012-09-01
      • 1970-01-01
      • 2022-07-02
      • 2011-09-16
      • 1970-01-01
      • 1970-01-01
      • 2020-05-05
      • 2012-09-14
      相关资源
      最近更新 更多