代码之家  ›  专栏  ›  技术社区  ›  user9507127

自定义图像过滤器

  •  0
  • user9507127  · 技术社区  · 7 年前

    1. 简介:

    因此,我想为uiimages开发一种特殊的过滤方法——我的想法是将一张图片中的所有颜色都改为黑色,除了某种颜色,这样可以保持它们的外观。

    图像总是很好的,所以请看这张图像以获得我想要实现的目标:

    img

    2. 说明:

    我想应用一种能够在图像中找到特定颜色的过滤器(算法)。该算法必须能够将所有与参考颜色不匹配的颜色替换为例如“黑色”。

    我开发了一个简单的代码,可以替换任何图像中的特定颜色(具有阈值的颜色范围)。 但这个解决方案似乎不是一个快速的&真是一种高效的方式!


    func colorFilter(image: UIImage, findcolor: String, threshold: Int) -> UIImage {
        let img: CGImage = image.cgImage!
        let context = CGContext(data: nil, width: img.width, height: img.height, bitsPerComponent: 8, bytesPerRow: 4 * img.width, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue)!
        context.draw(img, in: CGRect(x: 0, y: 0, width: img.width, height: img.height))
        let binaryData = context.data!.assumingMemoryBound(to: UInt8.self),
            referenceColor = HEXtoHSL(findcolor) // [h, s, l] integer array
        for i in 0..<img.height {
            for j in 0..<img.width {
                let pixel = 4 * (i * img.width + j)
                let pixelColor = RGBtoHSL([Int(binaryData[pixel]), Int(binaryData[pixel+1]), Int(binaryData[pixel+2])])  // [h, s, l] integer array
                let distance = calculateHSLDistance(pixelColor, referenceColor) // value between 0 and 100
                if (distance > threshold) {
                    let setValue: UInt8 = 255
                    binaryData[pixel] = setValue; binaryData[pixel+1] = setValue; binaryData[pixel+2] = setValue; binaryData[pixel+3] = 255
                }
            }
        }
        let outputImg = context.makeImage()!
        return UIImage(cgImage: outputImg, scale: image.scale, orientation: image.imageOrientation)
    }
    


    3. 代码信息 上面的代码运行得很好,但绝对无效。由于所有的计算(尤其是颜色转换等),此代码花费了很长(太长)的时间,因此请查看此屏幕截图:

    img


    1. 我的问题 我确信有一种更简单的方法可以过滤特定的颜色(使用给定的阈值 #c6456f is similar to #C6476f, ... )而不是循环每个像素来比较它的颜色。

      • 所以我想的是一种类似于过滤器(CIFilter方法)的方法,作为顶部代码的替代方法。
    2. 一些注释

      • 因此,我不要求您发布任何包含使用建议的回复 openCV图书馆 . 我想专门与Swift一起开发这个“算法”。

      • 随着时间的推移,截屏图像的分辨率为500*800px

    3. 仅此而已

    你真的读到这么远了吗然而,祝贺你- 如果您能帮助我加快代码速度,我们将不胜感激 哦! (也许有更好的方法来获得像素颜色,而不是在每个像素上循环) 提前感谢一百万 :)

    2 回复  |  直到 7 年前
        1
  •  2
  •   maxim1000    7 年前

    首先要做的事情-profile(测量函数不同部分的时间消耗)。它经常表明时间花在了一些意想不到的地方,并且总是建议您将优化工作的方向。这并不意味着你必须专注于最耗时的事情,但它会告诉你时间花在哪里。不幸的是,我不熟悉Swift,因此无法推荐任何特定工具。

    关于遍历所有像素-取决于图像结构和您对输入数据的假设。我发现有两种情况可以避免这种情况:

    1. 当您的图像上构建了一些优化的数据结构时(例如,其区域中的一些统计数据)。当使用不同参数的相同(或类似)算法处理同一图像时,这通常是有意义的。如果每个图像只处理一次,可能对您没有帮助。

    2. 当您知道绿色像素始终存在于一个组中时,就不可能存在孤立的单个像素。在这种情况下,您可以跳过一个或多个像素,当您找到一个绿色像素时,分析其邻域。

        2
  •  0
  •   Spektre    7 年前

    我不在您的平台上编写代码,但。。。

    嗯,我假设你们的蒙版区域(有特定颜色)是连续的,并且足够大。。。这意味着你有足够大的区域(而不仅仅是几个像素厚的东西)的像素组。根据此假设,可以为颜色创建密度贴图。我的意思是,如果你的特定颜色的最小细节大小是10像素,那么你可以在每个轴上每8个像素检查一次,将初始扫描速度提高约64倍。然后仅对包含颜色的区域使用完全扫描。以下是您必须做的:

    1. 确定属性

      您需要为每个轴设置步长(可以跳过多少像素而不丢失彩色区域)。我们把这个叫做dx,dy。

    2. 创建密度贴图

      只要创建2D数组,如果区域的中心像素设置为特定颜色,该数组将保存信息。所以如果你的形象 xs,ys 分辨率比地图的分辨率高:

      int mx=xs/dx;
      int my=ys/dy;
      int map[mx][my],x,y,xx,yy;
      
      for (yy=0,y=dy>>1;y<ys;y+=dy,yy++)
       for (xx=0,x=dx>>1;x<xs;x+=dx,xx++)
        map[xx][yy]=compare(pixel(x,y) , specific_color)<threshold;
      
    3. 放大地图集区域

      现在,您应该放大中的设置区域 map[][] 到相邻单元格,因为 #2 可能会错过颜色区域的边缘。

    4. 处理所有设置的区域

      for (yy=0;yy<my;yy++)
       for (xx=0;xx<mx;xx++)
        if (map[xx][yy])
         for (y=yy*dy,y<(yy+1)*dy;y++)
          for (x=xx*dx,x<(xx+1)*dx;x++)
           if (compare(pixel(x,y) , specific_color)>=threshold) pixel(x,y)=0x00000000;
      

    如果你想加快速度甚至超过你需要检测集 地图[][] 位于边缘的单元格(至少有一个零邻居)您可以按如下方式对单元格进行distinquire:

    0 - no specific color is present
    1 - inside of color area
    2 - edge of color area
    

    只需在 O(mx*my) . 之后,只需检查边缘区域的颜色,以便:

    for (yy=0;yy<my;yy++)
     for (xx=0;xx<mx;xx++)
      if (map[xx][yy]==2)
       {
       for (y=yy*dy,y<(yy+1)*dy;y++)
        for (x=xx*dx,x<(xx+1)*dx;x++)
         if (compare(pixel(x,y) , specific_color)>=threshold) pixel(x,y)=0x00000000;
       } else if (map[xx][yy]==0)
       {
       for (y=yy*dy,y<(yy+1)*dy;y++)
        for (x=xx*dx,x<(xx+1)*dx;x++)
         pixel(x,y)=0x00000000;
       }
    

    这应该更快。如果您的图像分辨率 xs,ys 不是区域大小的倍数 mx,my 您应该通过零填充或特殊循环来处理图像的外边缘,以处理图像缺失的部分。。。

    顺便问一下,阅读和设置整个图像需要多长时间?

    for (y=0;y<ys;y++)
     for (x=0;x<xs;x++)
      pixel(x,y)=pixel(x,y)^0x00FFFFFF;
    

    如果仅此一项就太慢,则意味着您的像素访问太慢,您应该为此使用不同的api。这是Windows上常见的错误 GDI公司 人们通常使用的平台 Pixels[][] 比爬行的蜗牛还慢。还有其他方式,如位锁定/blitting、扫描线等,因此在这种情况下,您需要在您的平台上快速查找一些内容。如果你连这个东西都不能加速,那么你就不能做其他任何事情。。。顺便问一下,这个运行的硬件是什么?