【问题标题】:How to implement a box or gaussian blur on iOS如何在 iOS 上实现框或高斯模糊
【发布时间】:2010-11-11 12:36:45
【问题描述】:

我希望能够以相对较快的速度拍摄图像并对其进行模糊处理(例如 0.1 秒)。图片尺寸几乎永远不会大于 256 x 256 像素。

我是否必须遍历每个像素并与相邻像素进行平均,还是有更高级别的方法可以做到这一点?

PS:我知道多个框模糊可以近似于高斯模糊。

【问题讨论】:

    标签: ios image-processing blur


    【解决方案1】:

    您可能想看看 Mario Klingemann 的 StakBlur 算法。它不是高斯分布,但非常接近。

    【讨论】:

    • 我可以只使用 CGImageRef 作为 StackBlur 的输入吗?不知道我从哪里得到像素数据。
    【解决方案2】:

    这里有两个解决穷人模糊的技巧:

    1. 拍摄图像,以部分不透明度绘制 5 或 6 次(或任意多次),每次在不同方向偏移几个像素。在更多方向上绘制更多次可以让您获得更好的模糊效果,但您显然会权衡处理时间。如果您想要半径相对较小的模糊,这很有效。

    2. 对于单色图像,您实际上可以使用内置阴影作为简单的模糊。

    【讨论】:

    • 如果“部分不透明度”是“一对像素”的高斯钟形曲线函数,则定义为高斯模糊(减去混叠问题)。
    • 您知道多幅绘图对于 4 向模糊的速度有多快吗?也就是4左4右。
    【解决方案3】:

    如果您总是或至少经常使用相同的模糊设置,您可能会通过在频域而不是空间域中进行过滤来提高速度。

    1. 预先计算过滤器图像 G(u,v),它是 2D 高斯
    2. 对输入图像 f(x,y)->F(u,v) 应用傅立叶变换
    3. 通过乘法过滤:H(u,v) = F(u,v) .* G(u,v)(像素乘法,而不是矩阵乘法)
    4. 通过傅里叶逆变换将过滤后的图像转换回空间域:H(u,v) -> h(x,y)

    这种方法的优点是与平均邻域相比,逐像素乘法应该非常快。因此,如果您处理大量图像,这可能会有所帮助。

    缺点是我不知道在 iPhone 上执行傅立叶变换的速度有多快,所以这很可能比其他实现慢得多。

    除此之外,我猜由于 iPhone 支持 OpenGL,您也许可以使用它的纹理功能/绘图来做到这一点。很抱歉,虽然我不是 OpenGL 专家,也不能真正给出任何实用的建议。

    【讨论】:

      【解决方案4】:

      任何通过 OpenGL 在像素级别上修改图像的算法都会有点慢;对 OpenGL 纹理进行逐像素操作,然后每帧更新它,遗憾的是性能不足。

      在实施复杂的模糊例程之前,请花一些时间编写测试装置并尝试像素操作。

      【讨论】:

        【解决方案5】:

        来自how-do-i-create-blurred-text-in-an-iphone-view:

        查看 Apple 的 GLImageProcessing iPhone 示例。除其他外,它会进行一些模糊处理。

        相关代码包括:

        static void blur(V2fT2f *quad, float t) // t = 1
        {
            GLint tex;
            V2fT2f tmpquad[4];
            float offw = t / Input.wide;
            float offh = t / Input.high;
            int i;
        
            glGetIntegerv(GL_TEXTURE_BINDING_2D, &tex);
        
            // Three pass small blur, using rotated pattern to sample 17 texels:
            //
            // .\/.. 
            // ./\\/ 
            // \/X/\   rotated samples filter across texel corners
            // /\\/. 
            // ../\. 
        
            // Pass one: center nearest sample
            glVertexPointer  (2, GL_FLOAT, sizeof(V2fT2f), &quad[0].x);
            glTexCoordPointer(2, GL_FLOAT, sizeof(V2fT2f), &quad[0].s);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            glColor4f(1.0/5, 1.0/5, 1.0/5, 1.0);
            validateTexEnv();
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        
            // Pass two: accumulate two rotated linear samples
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE);
            for (i = 0; i < 4; i++)
            {
                tmpquad[i].x = quad[i].s + 1.5 * offw;
                tmpquad[i].y = quad[i].t + 0.5 * offh;
                tmpquad[i].s = quad[i].s - 1.5 * offw;
                tmpquad[i].t = quad[i].t - 0.5 * offh;
            }
            glTexCoordPointer(2, GL_FLOAT, sizeof(V2fT2f), &tmpquad[0].x);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
            glActiveTexture(GL_TEXTURE1);
            glEnable(GL_TEXTURE_2D);
            glClientActiveTexture(GL_TEXTURE1);
            glTexCoordPointer(2, GL_FLOAT, sizeof(V2fT2f), &tmpquad[0].s);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            glBindTexture(GL_TEXTURE_2D, tex);
            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB,      GL_INTERPOLATE);
            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB,         GL_TEXTURE);
            glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB,         GL_PREVIOUS);
            glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB,         GL_PRIMARY_COLOR);
            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB,     GL_SRC_COLOR);
            glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA,    GL_REPLACE);
            glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA,       GL_PRIMARY_COLOR);
        
            glColor4f(0.5, 0.5, 0.5, 2.0/5);
            validateTexEnv();
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        
            // Pass three: accumulate two rotated linear samples
            for (i = 0; i < 4; i++)
            {
                tmpquad[i].x = quad[i].s - 0.5 * offw;
                tmpquad[i].y = quad[i].t + 1.5 * offh;
                tmpquad[i].s = quad[i].s + 0.5 * offw;
                tmpquad[i].t = quad[i].t - 1.5 * offh;
            }
            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        
            // Restore state
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
            glClientActiveTexture(GL_TEXTURE0);
            glBindTexture(GL_TEXTURE_2D, Half.texID);
            glDisable(GL_TEXTURE_2D);
            glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB,     GL_SRC_ALPHA);
            glActiveTexture(GL_TEXTURE0);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glDisable(GL_BLEND);
        }
        

        【讨论】:

          【解决方案6】:

          最基本的模糊(或者更多的柔化)是平均 2 个相邻像素并将平均值应用于两个像素。在整个图像中重复此操作,您会得到轻微的模糊(柔化)。

          【讨论】:

            【解决方案7】:

            我为 iOS3.2+ 应用程序找到了一个非常快速但非常糟糕的方法

              UIView *myView = [self view];
              CALayer *layer = [myView layer];
              [layer setRasterizationScale:0.25];
              [layer setShouldRasterize:YES];
            

            这会将视图光栅化为 4x4 像素块,然后使用双线性过滤将其放大……如果您只是想在模态视图下模糊背景视图,它的速度非常快而且看起来还不错。

            要撤消它,只需将光栅化比例设置回 1.0 或关闭光栅化。

            【讨论】:

            • 确实是一个很好的解决方案!我最近注意到了一些奇怪的事情——如果我做 1/2 的幂,模拟器上的模糊效果很好,但在设备上我会看到边距出现在一边——有人注意到了吗? 10 倍!
            • @Gabe : 你能帮我解决这个问题吗:“使用 OpenGL-ES 的绘画应用程序中的模糊效果(湿湿效果)”stackoverflow.com/questions/6980402/…
            • 我也在标签上使用过这个。很棒的资源!
            • 请注意,视网膜/非视网膜设备上的模糊效果会有所不同。您应该将 0.25 与 [[UIScreen mainScreen] scale] 相乘,以使其在所有像素密度上看起来都相同。
            • 这比我见过的任何自定义模糊实现都快!
            猜你喜欢
            • 2019-09-02
            • 1970-01-01
            • 2010-09-11
            • 2016-02-07
            • 2013-04-23
            • 2014-07-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多