Получение черно-белого UIImage (не в градациях серого) - PullRequest
5 голосов
/ 09 декабря 2010

Мне нужно получить чистый черно-белый 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:

В ответ на комментарии, Вот методы bitmapFromImage и 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;
}

Ответы [ 6 ]

8 голосов
/ 10 декабря 2010

Если вы ищете пороговое значение изображения - все, что ярче определенного значения, становится белым, все темнее становится черным, и вы выбираете значение - тогда библиотека, подобная Изображение GPU будет работать для вас.

6 голосов
/ 27 августа 2013

Этот код может помочь:

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];
    }
}
2 голосов
/ 10 декабря 2010

Хотя это может быть излишним для ваших целей, я делаю это только для живого видео с камеры iPhone в моем примере приложения здесь .Это приложение принимает цвет и чувствительность и может сделать все пиксели белыми, которые находятся в пределах этого порога, и прозрачными, если нет.Для этого я использую программируемые шейдеры OpenGL ES 2.0, чтобы получить отзывчивость в реальном времени.Все это описано в этом посте здесь .

Опять же, это, вероятно, излишне для того, что вы хотите.В случае простого UIImage, который вы хотите преобразовать в черно-белое, вы, вероятно, можете прочитать необработанные пиксели, выполнить итерацию по ним и применить такой же порог, как и я, для вывода окончательного изображения.Это не будет так быстро, как шейдерный подход, но будет гораздо проще кодировать.

1 голос
/ 09 июня 2017

С помощью 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)
    }
}
1 голос
/ 03 июня 2013

Код работал правильно для меня, просто нужно немного подправить ... вот несколько изменений, которые я сделал, чтобы работать правильно, присвоив значение нулевому индексу 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;
            }
        }

Надеюсь, это сработает.

0 голосов
/ 28 января 2017

Вот быстрое решение 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
}
...