简体   繁体   English

获取黑白 UIImage(非灰度)

[英]Getting a Black and White UIImage (Not Grayscale)

I need to get a pure black and white UIImage from another UIImage (not grayscale).我需要从另一个 UIImage(不是灰度)获得纯黑白 UIImage。 Anyone can help me?任何人都可以帮助我吗?

Thanks for reading.谢谢阅读。

EDITED:编辑:

Here is the proposed solution.这是建议的解决方案。 Thanks to all.谢谢大家。 Almost I know that is not the better way to do it, it works fine.几乎我知道这不是更好的方法,它工作正常。

// Gets an pure black and white image from an original image.
- (UIImage *)pureBlackAndWhiteImage:(UIImage *)image {

    unsigned char *dataBitmap = [self bitmapFromImage:image];

    for (int i = 0; i < image.size.width * image.size.height * 4; i += 4) {

        if ((dataBitmap[i + 1] + dataBitmap[i + 2] + dataBitmap[i + 3]) < (255 * 3 / 2)) {
            dataBitmap[i + 1] = 0;
            dataBitmap[i + 2] = 0;
            dataBitmap[i + 3] = 0;
        } else {
            dataBitmap[i + 1] = 255;
            dataBitmap[i + 2] = 255;
            dataBitmap[i + 3] = 255;
        }
    }

    image = [self imageWithBits:dataBitmap withSize:image.size];

    return image;
}

EDITED 1:编辑 1:

In response to comments, Here are methods bitmapFromImage and imageWithBits .为了回应评论,这里有方法bitmapFromImageimageWithBits

// Retrieves the bits from the context once the image has been drawn.
- (unsigned char *)bitmapFromImage:(UIImage *)image {

    // Creates a bitmap from the given image.
    CGContextRef contex = CreateARGBBitmapContext(image.size);
    if (contex == NULL) {
        return NULL;
    }

    CGRect rect = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height);
    CGContextDrawImage(contex, rect, image.CGImage);
    unsigned char *data = CGBitmapContextGetData(contex);
    CGContextRelease(contex);

    return data;
}

// Fills an image with bits.
- (UIImage *)imageWithBits:(unsigned char *)bits withSize:(CGSize)size {

    // Creates a color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    if (colorSpace == NULL) {

        fprintf(stderr, "Error allocating color space\n");
        free(bits);
        return nil;
    }

    CGContextRef context = CGBitmapContextCreate (bits, size.width, size.height, 8, size.width * 4, colorSpace, kCGImageAlphaPremultipliedFirst);
    if (context == NULL) {

        fprintf (stderr, "Error. Context not created\n");
        free (bits);
        CGColorSpaceRelease(colorSpace );
        return nil;
    }

    CGColorSpaceRelease(colorSpace );
    CGImageRef ref = CGBitmapContextCreateImage(context);
    free(CGBitmapContextGetData(context));
    CGContextRelease(context);

    UIImage *img = [UIImage imageWithCGImage:ref];
    CFRelease(ref);
    return img;
}

如果您正在寻找的是对图像进行阈值处理 - 比某个值更明亮的一切变为白色,一切变暗变暗,然后选择值 - 那么像GPU图像这样的库将适合您。

This code may help: 此代码可能有所帮助:

for (int i = 0; i < image.size.width * image.size.height * 4; i += 4) {
        if (dataBitmap[i + 0] >= dataBitmap[i + 1] && dataBitmap[i + 0] >= dataBitmap[i + 2]){
        dataBitmap[i + 1] = dataBitmap[i + 0];
        dataBitmap[i + 2] = dataBitmap[i + 0];
    }
    else if (dataBitmap[i + 1] >= dataBitmap[i + 0] && dataBitmap[i + 1] >= dataBitmap[i + 2]) {
        dataBitmap[i + 0] = dataBitmap[i + 1];
        dataBitmap[i + 2] = dataBitmap[i + 1];
    }
    else  {
        dataBitmap[i + 0] = dataBitmap[i + 2];
        dataBitmap[i + 1] = dataBitmap[i + 2];
    }
}

While it may be overkill for your purposes, I do just that for live video from the iPhone camera in my sample application here . 虽然对于您的目的而言可能有些过分,但我在示例应用程序中使用iPhone相机的实时视频。 That application takes a color and a sensitivity, and can turn all pixels white that are within that threshold and transparent if not. 该应用程序采用颜色和灵敏度,并且可以将所有像素变为白色,即使不是,也可以是透明的。 I use OpenGL ES 2.0 programmable shaders for this in order to get realtime responsiveness. 我为此使用OpenGL ES 2.0可编程着色器以获得实时响应。 The whole thing is described in this post here . 整个事情是在这个职位描述这里

Again, this is probably overkill for what you want. 再说一遍,这可能是你想要的东西。 In the case of a simple UIImage that you want to convert to black and white, you can probably read in the raw pixels, iterate through them, and apply the same sort of thresholding I did to output the final image. 在想要转换为黑白的简单UIImage的情况下,您可以读取原始像素,遍历它们,并应用我所做的相同类型的阈值来输出最终图像。 This won't be as fast as the shader approach, but it will be much simpler to code. 这不会像着色器方法那么快,但编码会更简单。

The code worked right for me,just need some tweak ...here are few changes I made to work it properly by assigning value to dataBitmap[] array 's zeroth index... 代码对我来说很合适,只需要一些调整...这里有一些我通过赋值给dataBitmap[] array的第零索引来正确地工作的改变...

     for (int i = 0; i < image.size.width * image.size.height * 4; i += 4) {
            //here an index for zeroth element is assigned
            if ((dataBitmap[i + 0]+dataBitmap[i + 1] + dataBitmap[i + 2] + dataBitmap[i + 3]) < (255 * 4 / 2)) {   
         //  multiply four,instead of three              
                dataBitmap[i + 0] = 0;
                dataBitmap[i + 1] = 0;
                dataBitmap[i + 2] = 0;
                dataBitmap[i + 3] = 0;
            } else {
                dataBitmap[i + 0] = 255;
                dataBitmap[i + 1] = 255;
                dataBitmap[i + 2] = 255;
                dataBitmap[i + 3] = 255;
            }
        }

Hope it will work. 希望它会奏效。

With Swift 3, I was able to accomplish this effect by using CIFilters, first by applying CIPhotoEffectNoir (to make it grayscale) and then applying the CIColorControl filter with the kCIInputContrastKey input parameter set to a high value (ie 50). 使用Swift 3,我能够通过使用CIFilter来实现此效果,首先应用CIPhotoEffectNoir (使其灰度),然后应用CIColorControl过滤器,将kCIInputContrastKey输入参数设置为高值(即50)。 Setting the kCIInputBrightnessKey parameter will also adjust how intense the black-and-white contrast appears, negative for a darker image, and positive for a brighter image. 设置kCIInputBrightnessKey参数还将调整黑白对比度的强度,对于较暗的图像为负,对于较亮的图像为正。 For example: 例如:

extension UIImage {
    func toBlackAndWhite() -> UIImage? {
        guard let ciImage = CIImage(image: self) else {
            return nil
        }
        guard let grayImage = CIFilter(name: "CIPhotoEffectNoir", withInputParameters: [kCIInputImageKey: ciImage])?.outputImage else {
            return nil
        }
        let bAndWParams: [String: Any] = [kCIInputImageKey: grayImage,
                                          kCIInputContrastKey: 50.0,
                                          kCIInputBrightnessKey: 10.0]
        guard let bAndWImage = CIFilter(name: "CIColorControls", withInputParameters: bAndWParams)?.outputImage else {
            return nil
        }
        guard let cgImage = CIContext(options: nil).createCGImage(bAndWImage, from: bAndWImage.extent) else {
            return nil
        }
        return UIImage(cgImage: cgImage)
    }
}

Here's a swift 3 solution: 这是一个快速的3解决方案:

class func pureBlackAndWhiteImage(_ inputImage: UIImage) -> UIImage? {

    guard let inputCGImage = inputImage.cgImage, let context = getImageContext(for: inputCGImage), let data = context.data else { return nil }

    let white = RGBA32(red: 255, green: 255, blue: 255, alpha: 255)
    let black = RGBA32(red: 0, green: 0, blue: 0, alpha: 255)

    let width = Int(inputCGImage.width)
    let height = Int(inputCGImage.height)
    let pixelBuffer = data.bindMemory(to: RGBA32.self, capacity: width * height)

    for x in 0 ..< height {
        for y in 0 ..< width {
            let offset = x * width + y
            if pixelBuffer[offset].red > 0 || pixelBuffer[offset].green > 0 || pixelBuffer[offset].blue > 0 {
                pixelBuffer[offset] = black
            } else {
                pixelBuffer[offset] = white
            }
        }
    }

    let outputCGImage = context.makeImage()
    let outputImage = UIImage(cgImage: outputCGImage!, scale: inputImage.scale, orientation: inputImage.imageOrientation)

    return outputImage
}

class func getImageContext(for inputCGImage: CGImage) ->CGContext? {

    let colorSpace       = CGColorSpaceCreateDeviceRGB()
    let width            = inputCGImage.width
    let height           = inputCGImage.height
    let bytesPerPixel    = 4
    let bitsPerComponent = 8
    let bytesPerRow      = bytesPerPixel * width
    let bitmapInfo       = RGBA32.bitmapInfo

    guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
        print("unable to create context")
        return nil
    }

    context.setBlendMode(.copy)
    context.draw(inputCGImage, in: CGRect(x: 0, y: 0, width: CGFloat(width), height: CGFloat(height)))

    return context
}

struct RGBA32: Equatable {
    var color: UInt32

    var red: UInt8 {
        return UInt8((color >> 24) & 255)
    }

    var green: UInt8 {
        return UInt8((color >> 16) & 255)
    }

    var blue: UInt8 {
        return UInt8((color >> 8) & 255)
    }

    var alpha: UInt8 {
        return UInt8((color >> 0) & 255)
    }

    init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
        color = (UInt32(red) << 24) | (UInt32(green) << 16) | (UInt32(blue) << 8) | (UInt32(alpha) << 0)
    }

    static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
}

func ==(lhs: RGBA32, rhs: RGBA32) -> Bool {
    return lhs.color == rhs.color
}

Here is a Swift 5 way of doing this, was tested on a colorized icon UIImage. It will take the icon which had color into a black and white version of the same size:这是一个 Swift 5 方法,在彩色图标 UIImage 上进行了测试。它将把有颜色的图标变成相同大小的黑白版本:

Original:原版的: 在此处输入图像描述

Transformed:转化: 在此处输入图像描述

 extension UIImage {

    func blackAndWhite() -> UIImage? {
        let renderer = UIGraphicsImageRenderer(size: size)
        let result = renderer.image { _ in
            let imageRect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
            draw(in: imageRect, blendMode: .luminosity, alpha: 1.0)
        }
        return result
     }
 }

example of calling it on a UIButton:在 UIButton 上调用它的示例:

button.setTitle("Filters", for: .normal)
button.setImage(icon.blackAndWhite(), for: .normal)

In this example, you can just do the simpler:在这个例子中,你可以做更简单的:

button.tintColor = .black

but this was just to show how to un-colorize something if for some reason you were not using a button.但这只是为了展示如果由于某种原因您没有使用按钮,如何取消着色。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM