我需要在 UIImage 上应用黑白滤镜。我有一个视图,其中有一张用户拍摄的照片,但我对转换图像的颜色没有任何想法。
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = NSLocalizedString(@"#Paint!", nil);
imageView.image = image;
}
我怎样才能做到这一点?
我需要在 UIImage 上应用黑白滤镜。我有一个视图,其中有一张用户拍摄的照片,但我对转换图像的颜色没有任何想法。
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = NSLocalizedString(@"#Paint!", nil);
imageView.image = image;
}
我怎样才能做到这一点?
目标 C
- (UIImage *)convertImageToGrayScale:(UIImage *)image {
// Create image rectangle with current image width/height
CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
// Grayscale color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
// Create bitmap content with current image size and grayscale colorspace
CGContextRef context = CGBitmapContextCreate(nil, image.size.width, image.size.height, 8, 0, colorSpace, kCGImageAlphaNone);
// Draw image into current context, with specified rectangle
// using previously defined context (with grayscale colorspace)
CGContextDrawImage(context, imageRect, [image CGImage]);
// Create bitmap image info from pixel data in current context
CGImageRef imageRef = CGBitmapContextCreateImage(context);
// Create a new UIImage object
UIImage *newImage = [UIImage imageWithCGImage:imageRef];
// Release colorspace, context and bitmap information
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
CFRelease(imageRef);
// Return the new grayscale image
return newImage;
}
迅速
func convertToGrayScale(image: UIImage) -> UIImage {
// Create image rectangle with current image width/height
let imageRect:CGRect = CGRect(x:0, y:0, width:image.size.width, height: image.size.height)
// Grayscale color space
let colorSpace = CGColorSpaceCreateDeviceGray()
let width = image.size.width
let height = image.size.height
// Create bitmap content with current image size and grayscale colorspace
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
// Draw image into current context, with specified rectangle
// using previously defined context (with grayscale colorspace)
let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
context?.draw(image.cgImage!, in: imageRect)
let imageRef = context!.makeImage()
// Create a new UIImage object
let newImage = UIImage(cgImage: imageRef!)
return newImage
}
从ciimage标签来看,也许 OP 正在(正确地)认为 Core Image 会提供一种快速简便的方法来做到这一点?
这就是,在 ObjC 中:
- (UIImage *)grayscaleImage:(UIImage *)image {
CIImage *ciImage = [[CIImage alloc] initWithImage:image];
CIImage *grayscale = [ciImage imageByApplyingFilter:@"CIColorControls"
withInputParameters: @{kCIInputSaturationKey : @0.0}];
return [UIImage imageWithCIImage:grayscale];
}
和斯威夫特:
func grayscaleImage(image: UIImage) -> UIImage {
let ciImage = CIImage(image: image)
let grayscale = ciImage.imageByApplyingFilter("CIColorControls",
withInputParameters: [ kCIInputSaturationKey: 0.0 ])
return UIImage(CIImage: grayscale)
}
CIColorControls只是几个可以将图像转换为灰度的内置核心图像过滤器之一。CIPhotoEffectMono、CIPhotoEffectNoir和CIPhotoEffectTonal是不同的色调映射预设(每个都没有参数),您可以使用CIColorMap等滤镜进行自己的色调映射。
与涉及创建和绘制自己的替代方案不同CGBitmapContext
,这些替代方案无需额外工作即可保留原始图像的大小/比例和 alpha。
当 PiratM 的解决方案有效时,您会丢失 alpha 通道。要保留 Alpha 通道,您需要做一些额外的步骤。
+(UIImage *)convertImageToGrayScale:(UIImage *)image {
// Create image rectangle with current image width/height
CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
// Grayscale color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
// Create bitmap content with current image size and grayscale colorspace
CGContextRef context = CGBitmapContextCreate(nil, image.size.width, image.size.height, 8, 0, colorSpace, kCGImageAlphaNone);
// Draw image into current context, with specified rectangle
// using previously defined context (with grayscale colorspace)
CGContextDrawImage(context, imageRect, [image CGImage]);
// Create bitmap image info from pixel data in current context
CGImageRef imageRef = CGBitmapContextCreateImage(context);
// Release colorspace, context and bitmap information
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
context = CGBitmapContextCreate(nil,image.size.width, image.size.height, 8, 0, nil, kCGImageAlphaOnly );
CGContextDrawImage(context, imageRect, [image CGImage]);
CGImageRef mask = CGBitmapContextCreateImage(context);
// Create a new UIImage object
UIImage *newImage = [UIImage imageWithCGImage:CGImageCreateWithMask(imageRef, mask)];
CGImageRelease(imageRef);
CGImageRelease(mask);
// Return the new grayscale image
return newImage;
}
考虑到 Alpha 通道,@rickster 的版本看起来不错。但是没有 .AspectFit 或 Fill contentMode 的 UIImageView 无法显示它。因此,必须使用 CGImage 创建 UIImage。此版本实现为 Swift UIImage 扩展保持当前比例并提供一些可选的输入参数:
import CoreImage
extension UIImage
{
/// Applies grayscale with CIColorControls by settings saturation to 0.0.
/// - Parameter brightness: Default is 0.0.
/// - Parameter contrast: Default is 1.0.
/// - Returns: The grayscale image of self if available.
func grayscaleImage(brightness: Double = 0.0, contrast: Double = 1.0) -> UIImage?
{
if let ciImage = CoreImage.CIImage(image: self, options: nil)
{
let paramsColor: [String : AnyObject] = [ kCIInputBrightnessKey: NSNumber(double: brightness),
kCIInputContrastKey: NSNumber(double: contrast),
kCIInputSaturationKey: NSNumber(double: 0.0) ]
let grayscale = ciImage.imageByApplyingFilter("CIColorControls", withInputParameters: paramsColor)
let processedCGImage = CIContext().createCGImage(grayscale, fromRect: grayscale.extent)
return UIImage(CGImage: processedCGImage, scale: self.scale, orientation: self.imageOrientation)
}
return nil
}
}
更长但更快的方法是 @ChrisStillwells 答案的修改版本。考虑到 Swift 中的 alpha 通道和当前比例,实现为 UIImage 扩展:
extension UIImage
{
/// Create a grayscale image with alpha channel. Is 5 times faster than grayscaleImage().
/// - Returns: The grayscale image of self if available.
func convertToGrayScale() -> UIImage?
{
// Create image rectangle with current image width/height * scale
let pixelSize = CGSize(width: self.size.width * self.scale, height: self.size.height * self.scale)
let imageRect = CGRect(origin: CGPointZero, size: pixelSize)
// Grayscale color space
if let colorSpace: CGColorSpaceRef = CGColorSpaceCreateDeviceGray()
{
// Create bitmap content with current image size and grayscale colorspace
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.None.rawValue)
if let context: CGContextRef = CGBitmapContextCreate(nil, Int(pixelSize.width), Int(pixelSize.height), 8, 0, colorSpace, bitmapInfo.rawValue)
{
// Draw image into current context, with specified rectangle
// using previously defined context (with grayscale colorspace)
CGContextDrawImage(context, imageRect, self.CGImage)
// Create bitmap image info from pixel data in current context
if let imageRef: CGImageRef = CGBitmapContextCreateImage(context)
{
let bitmapInfoAlphaOnly = CGBitmapInfo(rawValue: CGImageAlphaInfo.Only.rawValue)
if let contextAlpha = CGBitmapContextCreate(nil, Int(pixelSize.width), Int(pixelSize.height), 8, 0, nil, bitmapInfoAlphaOnly.rawValue)
{
CGContextDrawImage(contextAlpha, imageRect, self.CGImage)
if let mask: CGImageRef = CGBitmapContextCreateImage(contextAlpha)
{
// Create a new UIImage object
if let newCGImage = CGImageCreateWithMask(imageRef, mask)
{
// Return the new grayscale image
return UIImage(CGImage: newCGImage, scale: self.scale, orientation: self.imageOrientation)
}
}
}
}
}
}
// A required variable was unexpected nil
return nil
}
}
在 Swift 5中,使用 CoreImage 做图像过滤,
谢谢@rickster
extension UIImage{
var grayscaled: UIImage?{
let ciImage = CIImage(image: self)
let grayscale = ciImage?.applyingFilter("CIColorControls",
parameters: [ kCIInputSaturationKey: 0.0 ])
if let gray = grayscale{
return UIImage(ciImage: gray)
}
else{
return nil
}
}
}
将@FBente 更新为Swift 5,
使用CoreImage做图像过滤,
extension UIImage
{
/// Create a grayscale image with alpha channel. Is 5 times faster than grayscaleImage().
/// - Returns: The grayscale image of self if available.
var grayScaled: UIImage?
{
// Create image rectangle with current image width/height * scale
let pixelSize = CGSize(width: self.size.width * self.scale, height: self.size.height * self.scale)
let imageRect = CGRect(origin: CGPoint.zero, size: pixelSize)
// Grayscale color space
let colorSpace: CGColorSpace = CGColorSpaceCreateDeviceGray()
// Create bitmap content with current image size and grayscale colorspace
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
if let context: CGContext = CGContext(data: nil, width: Int(pixelSize.width), height: Int(pixelSize.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
{
// Draw image into current context, with specified rectangle
// using previously defined context (with grayscale colorspace)
guard let cg = self.cgImage else{
return nil
}
context.draw(cg, in: imageRect)
// Create bitmap image info from pixel data in current context
if let imageRef: CGImage = context.makeImage(){
let bitmapInfoAlphaOnly = CGBitmapInfo(rawValue: CGImageAlphaInfo.alphaOnly.rawValue)
guard let context = CGContext(data: nil, width: Int(pixelSize.width), height: Int(pixelSize.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfoAlphaOnly.rawValue) else{
return nil
}
context.draw(cg, in: imageRect)
if let mask: CGImage = context.makeImage() {
// Create a new UIImage object
if let newCGImage = imageRef.masking(mask){
// Return the new grayscale image
return UIImage(cgImage: newCGImage, scale: self.scale, orientation: self.imageOrientation)
}
}
}
}
// A required variable was unexpected nil
return nil
}
}
斯威夫特 3.0 版本:
extension UIImage {
func convertedToGrayImage() -> UIImage? {
let width = self.size.width
let height = self.size.height
let rect = CGRect(x: 0.0, y: 0.0, width: width, height: height)
let colorSpace = CGColorSpaceCreateDeviceGray()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
guard let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else {
return nil
}
guard let cgImage = cgImage else { return nil }
context.draw(cgImage, in: rect)
guard let imageRef = context.makeImage() else { return nil }
let newImage = UIImage(cgImage: imageRef.copy()!)
return newImage
}
}
斯威夫特 4 解决方案
extension UIImage {
var withGrayscale: UIImage {
guard let ciImage = CIImage(image: self, options: nil) else { return self }
let paramsColor: [String: AnyObject] = [kCIInputBrightnessKey: NSNumber(value: 0.0), kCIInputContrastKey: NSNumber(value: 1.0), kCIInputSaturationKey: NSNumber(value: 0.0)]
let grayscale = ciImage.applyingFilter("CIColorControls", parameters: paramsColor)
guard let processedCGImage = CIContext().createCGImage(grayscale, from: grayscale.extent) else { return self }
return UIImage(cgImage: processedCGImage, scale: scale, orientation: imageOrientation)
}
}
import GPUImage
extension UIImage {
func blackWhite() -> UIImage? {
guard let image: GPUImagePicture = GPUImagePicture(image: self) else {
print("unable to create GPUImagePicture")
return nil
}
let filter = GPUImageAverageLuminanceThresholdFilter()
image.addTarget(filter)
filter.useNextFrameForImageCapture()
image.processImage()
guard let processedImage: UIImage = filter.imageFromCurrentFramebuffer(with: UIImageOrientation.up) else {
print("unable to obtain UIImage from filter")
return nil
}
return processedImage
}
}
Here is the swift 1.2 version
/// convert background image to gray scale
///
/// param: flag if true, image will be rendered in grays scale
func convertBackgroundColorToGrayScale(flag: Bool) {
if flag == true {
let imageRect = self.myImage.frame
let colorSpace = CGColorSpaceCreateDeviceGray()
let width = imageRect.width
let height = imageRect.height
let bitmapInfo = CGBitmapInfo(CGImageAlphaInfo.None.rawValue)
var context = CGBitmapContextCreate(nil, Int(width), Int(height), 8, 0, colorSpace, bitmapInfo)
let image = self.musicBackgroundColor.image!.CGImage
CGContextDrawImage(context, imageRect, image)
let imageRef = CGBitmapContextCreateImage(context)
let newImage = UIImage(CGImage: CGImageCreateCopy(imageRef))
self.myImage.image = newImage
} else {
// do something else
}
}
此代码(目标 c)工作:
CIImage * ciimage = ...;
CIFilter * filter = [CIFilter filterWithName:@"CIColorControls" withInputParameters:@{kCIInputSaturationKey : @0.0,kCIInputContrastKey : @10.0,kCIInputImageKey : ciimage}];
CIImage * grayscale = [filtre outputImage];
这kCIInputContrastKey : @10.0
是为了获得几乎黑白的图像。
(基于FBente的回答)。
斯威夫特 5.4.2:
func grayscaled(brightness: Double = 0.0, contrast: Double = 1.0) -> UIImage? {
guard let ciImage = CoreImage.CIImage(image: self, options: nil) else { return nil }
let paramsColor: [String : AnyObject] = [ kCIInputBrightnessKey: NSNumber(value: brightness),
kCIInputContrastKey: NSNumber(value: contrast),
kCIInputSaturationKey: NSNumber(value: 0.0) ]
let grayscale = ciImage.applyingFilter("CIColorControls", parameters: paramsColor)
guard let processedCGImage = CIContext().createCGImage(grayscale, from: grayscale.extent) else { return nil }
return UIImage(cgImage: processedCGImage, scale: self.scale, orientation: self.imageOrientation)
}
在 Swift 3.0 中
func convertImageToGrayScale(image: UIImage) -> UIImage {
// Create image rectangle with current image width/height
let imageRect = CGRect(x: 0, y: 0,width: image.size.width, height : image.size.height)
// Grayscale color space
let colorSpace = CGColorSpaceCreateDeviceGray()
// Create bitmap content with current image size and grayscale colorspace
let context = CGContext(data: nil, width: Int(image.size.width), height: Int(image.size.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.none.rawValue)
// Draw image into current context, with specified rectangle
// using previously defined context (with grayscale colorspace)
context?.draw(image.cgImage!, in: imageRect)
// Create bitmap image info from pixel data in current context
let imageRef = context!.makeImage()
// Create a new UIImage object
let newImage = UIImage(cgImage: imageRef!)
// Release colorspace, context and bitmap information
//MARK: ToolBar Button Methods
// Return the new grayscale image
return newImage
}