【问题标题】:invert white to black uiimage将白色反转为黑色 uiimage
【发布时间】:2015-04-07 21:20:22
【问题描述】:

我有以下 UIImage:

使用 Objective-C,我希望能够将白色反转为黑色,反之亦然。

任何帮助将不胜感激。

【问题讨论】:

  • 你的意思是黑色背景和白色签名?
  • @Shashi3456643,是的,这就是我需要的

标签: ios uiimage


【解决方案1】:

Swift3

extension UIImage {
    func invertedImage() -> UIImage? {
        guard let cgImage = self.cgImage else { return nil }
        let ciImage = CoreImage.CIImage(cgImage: cgImage)
        guard let filter = CIFilter(name: "CIColorInvert") else { return nil }
        filter.setDefaults()
        filter.setValue(ciImage, forKey: kCIInputImageKey)
        let context = CIContext(options: nil)
        guard let outputImage = filter.outputImage else { return nil }
        guard let outputImageCopy = context.createCGImage(outputImage, from: outputImage.extent) else { return nil }
        return UIImage(cgImage: outputImageCopy, scale: self.scale, orientation: .up) 
    }
}

【讨论】:

  • 不知何故,这会影响图像(显示)大小
  • @Peter 我很好奇在哪里。自从我上次使用核心图像以来已经有一段时间了,所以我无法发现它。你有解决办法吗?
  • 这个问题已经让我发疯了一天 - 不知何故,ARC 将要反转的 UIImage 和方法返回的 UIImage 保存在内存中。您的代码似乎一切都正确,这是最令人沮丧的部分。将 CIImage 直接馈送到 UIImage 的替代方法(而不是像你那样通过 CGImage)也不起作用......我很想用 Objective C 编写这个特定的部分,并自己进行清理!
  • 好的,所以我发现了内存泄漏 - 函数返回的 UI 图像将 dataProvider 保存在内存中。我的代码将图像数据保存在图形内存中,从而保持系统内存使用率低。
  • @Peter 你解决了吗?您是否使用 Instruments 来查找内存泄漏?有时我有保留周期,我什至没有意识到。很抱歉给您带来了麻烦。
【解决方案2】:

对于 Xamarin C#(特定于 iOS;不是跨平台解决方案):

public static UIImage InvertImageColors( UIImage original )
{
    return ApplyFilter( original, new CIColorInvert() );
}

public static UIImage ApplyFilter( UIImage original, CIFilter filter )
{
    UIImage result;
    try {
        CIImage coreImage = original.CGImage;
        filter.SetValueForKey( coreImage, CIFilterInputKey.Image );
        CIImage output = filter.OutputImage;
        CIContext context = CIContext.FromOptions( null );
        CGImage cgImage = context.CreateCGImage( output, output.Extent );
        result = UIImage.FromImage( cgImage );

    } catch (Exception ex) {
        // .. Log error here ..
        result = original;
    }

    return result;
}

注意#1:改编自其他人的回答;我不知道原件在哪里。
注意 #2:故意每行一小步,这样您就可以看到中间类型,并轻松适应其他情况。

另见Xamarin docs - CIFilter class

【讨论】:

    【解决方案3】:

    斯威夫特

    使用CIContext 代替-UIImage:CIImage(参见https://stackoverflow.com/a/28386697/218152),并以@wtznc's response 为基础,这是一个独立的IBDesignable

    @IBDesignable
    class InvertImage: UIImageView {
    
        @IBInspectable var originalImage:UIImage? = nil
    
        @IBInspectable var invert:Bool = false {
            didSet {
                var inverted = false
                if let originalImage = self.originalImage {
                    if(invert) {
                        let image = CIImage(CGImage: originalImage.CGImage!)
                        if let filter = CIFilter(name: "CIColorInvert") {
                            filter.setDefaults()
                            filter.setValue(image, forKey: kCIInputImageKey)
                            
                            let context = CIContext(options: nil)
                            let imageRef = context.createCGImage(filter.outputImage!, fromRect: image.extent)
                            self.image = UIImage(CGImage: imageRef)
                            inverted = true
                        }
                    }
                }
                
                if(!inverted) {
                    self.image = self.originalImage
                }
            }
        }
    }
    

    要使用它,请设置 Original Image 而不是 Image,因为 Image 将动态关联:

    【讨论】:

      【解决方案4】:
      - (UIImage *)negativeImage
      {
          // get width and height as integers, since we'll be using them as
          // array subscripts, etc, and this'll save a whole lot of casting
          CGSize size = self.size;
          int width = size.width;
          int height = size.height;
      
          // Create a suitable RGB+alpha bitmap context in BGRA colour space
          CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
          unsigned char *memoryPool = (unsigned char *)calloc(width*height*4, 1);
          CGContextRef context = CGBitmapContextCreate(memoryPool, width, height, 8, width * 4, colourSpace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast);
          CGColorSpaceRelease(colourSpace);
      
          // draw the current image to the newly created context
          CGContextDrawImage(context, CGRectMake(0, 0, width, height), [self CGImage]);
      
          // run through every pixel, a scan line at a time...
          for(int y = 0; y < height; y++)
          {
              // get a pointer to the start of this scan line
              unsigned char *linePointer = &memoryPool[y * width * 4];
      
              // step through the pixels one by one...
              for(int x = 0; x < width; x++)
              {
                  // get RGB values. We're dealing with premultiplied alpha
                  // here, so we need to divide by the alpha channel (if it
                  // isn't zero, of course) to get uninflected RGB. We
                  // multiply by 255 to keep precision while still using
                  // integers
                  int r, g, b;
                  if(linePointer[3])
                  {
                      r = linePointer[0] * 255 / linePointer[3];
                      g = linePointer[1] * 255 / linePointer[3];
                      b = linePointer[2] * 255 / linePointer[3];
                  }
                  else
                      r = g = b = 0;
      
                  // perform the colour inversion
                  r = 255 - r;
                  g = 255 - g;
                  b = 255 - b;
      
                  if ( (r+g+b) / (3*255) == 0 )
                  {
      
                      linePointer[0] = linePointer[1] = linePointer[2] = 0;
                      linePointer[3] = 0;
                  }
                  else
                  {
                      // multiply by alpha again, divide by 255 to undo the
                      // scaling before, store the new values and advance
                      // the pointer we're reading pixel data from
                      linePointer[0] = r * linePointer[3] / 255;
                      linePointer[1] = g * linePointer[3] / 255;
                      linePointer[2] = b * linePointer[3] / 255;
      
                  }
                  linePointer += 4;
              }
          }
      
          // get a CG image from the context, wrap that into a
          // UIImage
          CGImageRef cgImage = CGBitmapContextCreateImage(context);
          UIImage *returnImage = [UIImage imageWithCGImage:cgImage];
      
          // clean up
          CGImageRelease(cgImage);
          CGContextRelease(context);
          free(memoryPool);
      
          // and return
          return returnImage;
      }
      

      我在 UIImage 扩展类中添加了上述方法。

      【讨论】:

      • 在 [self CGImage] 处构建错误:“IndexViewController”没有可见的@interface 声明选择器“CGImage”
      • 你是对的。我忘了提到该方法在 UIImage 类的扩展中......
      • 这怎么能用swift写?
      【解决方案5】:

      首先,您必须将 Core Image 框架添加到您的项目中。

      项目设置 -> 目标“项目名称” -> 构建阶段 -> 将二进制文件链接到库 -> 添加项目 -> CoreImage.framework

      其次,将 Core Image header 导入到您的实现文件中。

      #import <CoreImage/CoreImage.h>
      

      初始化一个UIImage对象来存储原始文件。

      UIImage *inputImage = [UIImage imageNamed:@"imageNamed"];
      

      创建一个CIFilter 来定义您希望如何修改原始UIImage 对象。

      CIFilter* filter = [CIFilter filterWithName:@"CIColorInvert"];
      [filter setDefaults];
      [filter setValue:inputImage.CIImage forKey:@"inputImage"];
      

      创建另一个UIImage 对象以保留修改后的图像。

      UIImage *outputImage = [[UIImage alloc] initWithCIImage:filter.outputImage];
      

      瞧!希望它会有所帮助。

      【讨论】:

      • 你能贴一张你正在测试的图片吗?
      猜你喜欢
      • 2020-01-28
      • 1970-01-01
      • 2020-11-11
      • 2013-09-17
      • 2018-03-09
      • 1970-01-01
      • 2019-12-13
      • 2015-06-30
      相关资源
      最近更新 更多