获取黑白 UIImage(非灰度)

发布于 2024-10-07 08:03:12 字数 2438 浏览 3 评论 0原文

我需要从另一个 UIImage (不是灰度)获取纯黑白 UIImage。有人可以帮助我吗?

感谢您的阅读。

已编辑:

这是建议的解决方案。感谢大家。我几乎知道这不是更好的方法,但效果很好。

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

编辑1:

为了回应评论,这里有方法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;
}

I need to get a pure black and white UIImage from another UIImage (not grayscale). 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:

In response to comments, Here are methods bitmapFromImage and imageWithBits.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

甜心 2024-10-14 08:03:12

如果您要寻找的是图像阈值 - 一切都比某个值更明亮,将所有值变成白色,一切都变暗,然后选择该值 - 然后像

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.

苍风燃霜 2024-10-14 08:03:12

这段代码可能有帮助:

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

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];
    }
}
非要怀念 2024-10-14 08:03:12

在 Swift 3 中,我能够通过使用 CIFilters 来实现此效果,首先应用 CIPhotoEffectNoir (使其灰度化),然后应用带有 CIColorControl 滤镜kCIInputContrastKey 输入参数设置为较高值(即 50)。设置 kCIInputBrightnessKey 参数还将调整黑白对比度的强度,负值表示图像较暗,正值表示图像较亮。例如:

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)
    }
}

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 (i.e. 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. 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)
    }
}
昔梦 2024-10-14 08:03:12

虽然这对于您的目的来说可能有些过分,但我只是在示例应用程序中对来自 iPhone 摄像头的实时视频执行此操作 这里。该应用程序采用颜色和灵敏度,并且可以将在该阈值内的所有像素变为白色,如果不在阈值内则变为透明。为此,我使用 OpenGL ES 2.0 可编程着色器以获得实时响应能力。整个事情在这篇文章这里进行了描述

再说一次,这对于你想要的东西来说可能有点过分了。对于要转换为黑白的简单 UIImage,您可能可以读取原始像素,迭代它们,并应用与输出最终图像相同的阈值。这不会像着色器方法那么快,但编码会简单得多。

While it may be overkill for your purposes, I do just that for live video from the iPhone camera in my sample application here. 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. 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. This won't be as fast as the shader approach, but it will be much simpler to code.

清引 2024-10-14 08:03:12

该代码对我来说工作正常,只需要一些调整...这里有一些更改,我通过为 dataBitmap[] 数组 的第 0 个索引赋值来使其正常工作...

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

希望它能工作。

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...

     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.

愿与i 2024-10-14 08:03:12

这是一个 swift 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's a swift 3 solution:

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
}
雪若未夕 2024-10-14 08:03:12

这是 Swift 5 的方法,在彩色图标 UIImage 上进行了测试。它将把有颜色的图标变成相同大小的黑白版本:

原始: 在此处输入图像描述

转换后:在此处输入图像描述

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

在 UIButton 上调用它的示例:

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

在此示例中,你可以做更简单的事情:

button.tintColor = .black

但这只是为了展示如果由于某种原因你没有使用按钮,如何取消某些东西的着色。

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:

Original: enter image description here

Transformed: enter image description here

 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:

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文