How to Convert an Uiimage to Grayscale in Swift Using Cifilter

Convert UIImage to grayscale keeping image quality

Try below code:

Note: code Updated and error been fixed...

  • Code tested in Swift 3.
  • originalImage is the image that you trying to convert.

Answer 1:

     var context = CIContext(options: nil)

Update: CIContext is the Core Image component that handles rendering and All of the processing of a core image is done in a CIContext. This is somewhat similar to a Core Graphics or OpenGL context.For more info available in Apple Doc.

     func Noir() {

let currentFilter = CIFilter(name: "CIPhotoEffectNoir")
currentFilter!.setValue(CIImage(image: originalImage.image!), forKey: kCIInputImageKey)
let output = currentFilter!.outputImage
let cgimg = context.createCGImage(output!,from: output!.extent)
let processedImage = UIImage(cgImage: cgimg!)
originalImage.image = processedImage
}

Also you need to Considered following filter that can produce similar effect

  • CIPhotoEffectMono
  • CIPhotoEffectTonal

Output from Answer 1:

Sample Image

Output from Answer 2:

Sample Image

Improved answer :

Answer 2: Auto adjusting input image before applying coreImage filter

var context = CIContext(options: nil)

func Noir() {

//Auto Adjustment to Input Image
var inputImage = CIImage(image: originalImage.image!)
let options:[String : AnyObject] = [CIDetectorImageOrientation:1 as AnyObject]
let filters = inputImage!.autoAdjustmentFilters(options: options)

for filter: CIFilter in filters {
filter.setValue(inputImage, forKey: kCIInputImageKey)
inputImage = filter.outputImage
}
let cgImage = context.createCGImage(inputImage!, from: inputImage!.extent)
self.originalImage.image = UIImage(cgImage: cgImage!)

//Apply noir Filter
let currentFilter = CIFilter(name: "CIPhotoEffectTonal")
currentFilter!.setValue(CIImage(image: UIImage(cgImage: cgImage!)), forKey: kCIInputImageKey)

let output = currentFilter!.outputImage
let cgimg = context.createCGImage(output!, from: output!.extent)
let processedImage = UIImage(cgImage: cgimg!)
originalImage.image = processedImage

}

Note: If you want to see the better result.You should be testing your code on real device not in the simulator...

How can I convert an UIImage to grayscale in Swift using CIFilter?

You can use CIColorControls and set Contrast Key kCIInputContrastKey to increase the black/white contrast as follow:

Xcode 9 • Swift 4

extension String {
static let colorControls = "CIColorControls"
}

extension UIImage {
var coreImage: CIImage? { return CIImage(image: self) }
}

extension CIImage {
var uiImage: UIImage? { return UIImage(ciImage: self) }
func applying(contrast value: NSNumber) -> CIImage? {
return applyingFilter(.colorControls, parameters: [kCIInputContrastKey: value])
}
func renderedImage() -> UIImage? {
guard let image = uiImage else { return nil }
return UIGraphicsImageRenderer(size: image.size,
format: image.imageRendererFormat).image { _ in
image.draw(in: CGRect(origin: .zero, size: image.size))
}
}
}

let url = URL(string: "https://i.stack.imgur.com/Xs4RX.jpg")!
do {
if let coreImage = UIImage(data: try Data(contentsOf: url))?.coreImage,
let increasedContrast = coreImage.applying(contrast: 1.5) {
imageView.image = increasedContrast.uiImage
// if you need to convert your image to data (JPEG/PNG) you would need to render the ciimage using renderedImage method on CIImage
}
} catch {
print(error)
}

To convert from colors to grayscale you can set the Saturation Key kCIInputSaturationKey to zero:

extension CIImage {
func applying(saturation value: NSNumber) -> CIImage? {
return applyingFilter(.colorControls, parameters: [kCIInputSaturationKey: value])
}
var grayscale: CIImage? { return applying(saturation: 0) }
}

let url = URL(string: "https://i.stack.imgur.com/Xs4RX.jpg")!
do {
if let coreImage = UIImage(data: try Data(contentsOf: url))?.coreImage,
let grayscale = coreImage.grayscale {
// use grayscale image here
imageView.image = grayscale.uiImage
}
} catch {
print(error)
}

Convert UIImage colored to grayscale using CGColorSpaceCreateDeviceGray()

func convertToGrayScale(image: UIImage) -> UIImage {
let imageRect:CGRect = CGRect(x:0, y:0, width:image.size.width, height: image.size.height)
let colorSpace = CGColorSpaceCreateDeviceGray()
let width = image.size.width
let height = image.size.height

let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)
//have to draw before create image

context?.draw(image.cgImage!, in: imageRect)
let imageRef = context!.makeImage()
let newImage = UIImage(cgImage: imageRef!)

return newImage
}

Convert image to grayscale

What exactly takes place when you use this function? Is the function returning an invalid image, or is the display not showing it correctly?

This is the method I use to convert to greyscale.

- (UIImage *) convertToGreyscale:(UIImage *)i {

int kRed = 1;
int kGreen = 2;
int kBlue = 4;

int colors = kGreen | kBlue | kRed;
int m_width = i.size.width;
int m_height = i.size.height;

uint32_t *rgbImage = (uint32_t *) malloc(m_width * m_height * sizeof(uint32_t));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rgbImage, m_width, m_height, 8, m_width * 4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextSetShouldAntialias(context, NO);
CGContextDrawImage(context, CGRectMake(0, 0, m_width, m_height), [i CGImage]);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);

// now convert to grayscale
uint8_t *m_imageData = (uint8_t *) malloc(m_width * m_height);
for(int y = 0; y < m_height; y++) {
for(int x = 0; x < m_width; x++) {
uint32_t rgbPixel=rgbImage[y*m_width+x];
uint32_t sum=0,count=0;
if (colors & kRed) {sum += (rgbPixel>>24)&255; count++;}
if (colors & kGreen) {sum += (rgbPixel>>16)&255; count++;}
if (colors & kBlue) {sum += (rgbPixel>>8)&255; count++;}
m_imageData[y*m_width+x]=sum/count;
}
}
free(rgbImage);

// convert from a gray scale image back into a UIImage
uint8_t *result = (uint8_t *) calloc(m_width * m_height *sizeof(uint32_t), 1);

// process the image back to rgb
for(int i = 0; i < m_height * m_width; i++) {
result[i*4]=0;
int val=m_imageData[i];
result[i*4+1]=val;
result[i*4+2]=val;
result[i*4+3]=val;
}

// create a UIImage
colorSpace = CGColorSpaceCreateDeviceRGB();
context = CGBitmapContextCreate(result, m_width, m_height, 8, m_width * sizeof(uint32_t), colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGImageRef image = CGBitmapContextCreateImage(context);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
UIImage *resultUIImage = [UIImage imageWithCGImage:image];
CGImageRelease(image);

free(m_imageData);

// make sure the data will be released by giving it to an autoreleased NSData
[NSData dataWithBytesNoCopy:result length:m_width * m_height];

return resultUIImage;
}

Apply Black and White Filter to UIImage

Objective 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;
}

Swift

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
}

Control black and white coloring using CIImage filter in swift

You had 2 mistakes in your kernel code. Here's the correct code:

let kernel = CIColorKernel( source:
"kernel vec4 replaceGrayWithBlackOrWhite(__sample s) {" +
"if (s.r > 0.25 && s.g > 0.25 && s.b > 0.25) {" +
" return vec4(0.0,0.0,0.0,1.0);" +
"} else {" +
" return vec4(1.0,1.0,1.0,1.0);" +
"}" +
"}"
)
  • The change from sampler to __ sample is because a CIColorKernel, which is optimized for working with one pixel at a time, just passes into the kernel that pixel. Thus, there's also no need for calling sample(samplerCoord()). CIWarpKernel, and CIKernel use sampler, as you send into the kernel a RIO (region of interest) and those kernels can access surrounding pixels... think blur effects.

  • The second change was to the if statement. if (s.rgb > 0.7) is comparing a vec3 (or three floats) to a single float. I had to play around with values once I corrected these and think that (a) using AND (&&) instead or OR (||) along with (b) lowering the threshold to 0.25 makes for a closer black/white image. Play around yourself with this to see what you want.

I've created a small Swift 5 project (using a hard-coded image) that uses this kernel.

There's no comments in it and it contains various extensions (and a subclass of GLKView) because I gleaned things from a production project. Besides focusing on your kernel code issue, one word of warning about the code - it contains several force-unwraps that should be removed for "production-ready" code.

Gradually change color of UIImage from RGB to Grayscale

I tried to implement a CoreImage filter on the fly but the performance was horrible. So I ended up having one image with colors and one image with grayscales. Both image views are stacked and their alphas is changed according to the scroll position. The result is a nice and smooth transition from color to grayscale when you scroll:

class ViewController: UIViewController, UIScrollViewDelegate {

@IBOutlet weak var scrollView: UIScrollView!
let cgImage = UIImage(named: "image.jpg")!.CGImage!
let colorImage = UIImageView()
let grayscaleImage = UIImageView()

override func viewDidLoad() {
super.viewDidLoad()

let image = UIImage(named: "image.jpg")!
colorImage.image = image
scrollView.addSubview(colorImage)

let beginImage = CIImage(CGImage: cgImage)
let filter = CIFilter(name: "CIColorControls")!
filter.setValue(beginImage, forKey: kCIInputImageKey)
filter.setValue(0, forKey: kCIInputSaturationKey)
grayscaleImage.image = UIImage(CIImage: filter.outputImage!)
scrollView.addSubview(grayscaleImage)

scrollView.contentSize = image.size
colorImage.frame = CGRectMake(0, 0, image.size.width, image.size.height)
grayscaleImage.frame = CGRectMake(0, 0, image.size.width, image.size.height)
}

func scrollViewDidScroll(scrollView: UIScrollView) {
let percentage = scrollView.contentOffset.x / (scrollView.contentSize.width - scrollView.bounds.size.width)
colorImage.alpha = percentage
grayscaleImage.alpha = 1 - percentage
}
}

Getting a Black and White UIImage (Not Grayscale)

If what you're looking for is to threshold the image -- everything brighter than a certain value turns white, everything darker turns black, and you pick the value -- then a library like GPU Image will work for you.



Related Topics



Leave a reply



Submit