Как сделать один цвет прозрачным на UIImage?

В моем приложении для iPhone есть экземпляр UIImage. Я хочу получить производный UIImage, который является результатом первого UIImage, в котором один из его цветов (например, пурпурный) сделан прозрачным. Как я могу это сделать?


person devguy    schedule 11.03.2009    source источник


Ответы (10)


-(void)changeColor
{
    UIImage *temp23=[UIImage imageNamed:@"leaf.png"];
    CGImageRef ref1=[self createMask:temp23];
    const float colorMasking[6] = {1.0, 2.0, 1.0, 1.0, 1.0, 1.0};
    CGImageRef New=CGImageCreateWithMaskingColors(ref1, colorMasking);
    UIImage *resultedimage=[UIImage imageWithCGImage:New];
}

-(CGImageRef)createMask:(UIImage*)temp
{
    CGImageRef ref=temp.CGImage;
    int mWidth=CGImageGetWidth(ref);
    int mHeight=CGImageGetHeight(ref);
    int count=mWidth*mHeight*4;
    void *bufferdata=malloc(count);

    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

    CGContextRef cgctx = CGBitmapContextCreate (bufferdata,mWidth,mHeight, 8,mWidth*4, colorSpaceRef, kCGImageAlphaPremultipliedFirst); 

    CGRect rect = {0,0,mWidth,mHeight};
    CGContextDrawImage(cgctx, rect, ref); 
    bufferdata = CGBitmapContextGetData (cgctx);

    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bufferdata, mWidth*mHeight*4, NULL);
    CGImageRef savedimageref = CGImageCreate(mWidth,mHeight, 8, 32, mWidth*4, colorSpaceRef, bitmapInfo,provider , NULL, NO, renderingIntent);
    CFRelease(colorSpaceRef);
    return savedimageref;
}   

Приведенный выше код протестирован, и я изменил зеленый цвет на красный с помощью маски

person Community    schedule 14.09.2009
comment
ваш код отлично работает. но как мне изменить красный цвет на зеленый или любой другой цвет - person Rahul Vyas; 24.09.2009
comment
Спасибо за хорошую работу. Но я не мог получить истинный результат, пока не сделал это изменение: kCGImageAlphaPremultipliedFirst - ›kCGImageAlphaPremultipliedLast После этого функция дала мне истинный результат. :) Спасибо. - person Vitaly; 04.07.2010
comment
как я могу изменить зеленый цвет на черный цвет? - person isarathg; 09.08.2011
comment
Похоже, здесь утечки памяти. Вам необходимо освободить контекст растрового изображения: CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(nil, size.width, size.height, 8, 0, colorSpace, kCGBitmapAlphaInfoMask); /* (Other code) */ CGContextRelease(context); CGColorSpaceRelease(colorSpace); - person Jonny; 03.04.2012
comment
@nasif, можешь объяснить, что такое colorMasking [6]? и colorMasking? я хочу изменить черный цвет в моем изображении на другой цвет, например красный - person tamtoum1987; 15.07.2016

В ПОРЯДКЕ. Попробовав, я не знаю, сколько версий этих решений, у меня есть собственная настроенная версия. Я обнаружил, что решение от @yubenyi работает достаточно хорошо, но если вы хотите получить результат от его changeWhiteColorTransparent () и передать ее обратно, она не работает.

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

После долгих проб и ошибок я добился того, что заменил цвет самостоятельно. Я сопротивлялся этому, потому что это казалось слишком тяжелой работой, когда есть API для этого, но они не всегда ведут себя так, как вы хотите. В частности, выходные данные CGImageCreateWithMaskingColors () нельзя использовать в качестве входных данных для другого вызова той же функции. Я не мог понять почему, но я думаю, что это как-то связано с альфа-каналом.

В любом случае мое решение:

- (UIImage*) replaceColor:(UIColor*)color inImage:(UIImage*)image withTolerance:(float)tolerance {
    CGImageRef imageRef = [image CGImage];

    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    NSUInteger bitmapByteCount = bytesPerRow * height;

    unsigned char *rawData = (unsigned char*) calloc(bitmapByteCount, sizeof(unsigned char));

    CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);

    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);

    CGColorRef cgColor = [color CGColor];
    const CGFloat *components = CGColorGetComponents(cgColor);
    float r = components[0];
    float g = components[1];
    float b = components[2];
    //float a = components[3]; // not needed

    r = r * 255.0;
    g = g * 255.0;
    b = b * 255.0;

    const float redRange[2] = {
        MAX(r - (tolerance / 2.0), 0.0),
        MIN(r + (tolerance / 2.0), 255.0)
    };

    const float greenRange[2] = {
        MAX(g - (tolerance / 2.0), 0.0),
        MIN(g + (tolerance / 2.0), 255.0)
    };

    const float blueRange[2] = {
        MAX(b - (tolerance / 2.0), 0.0),
        MIN(b + (tolerance / 2.0), 255.0)
    };

    int byteIndex = 0;

    while (byteIndex < bitmapByteCount) {
        unsigned char red   = rawData[byteIndex];
        unsigned char green = rawData[byteIndex + 1];
        unsigned char blue  = rawData[byteIndex + 2];

        if (((red >= redRange[0]) && (red <= redRange[1])) &&
            ((green >= greenRange[0]) && (green <= greenRange[1])) &&
            ((blue >= blueRange[0]) && (blue <= blueRange[1]))) {
            // make the pixel transparent
            //
            rawData[byteIndex] = 0;
            rawData[byteIndex + 1] = 0;
            rawData[byteIndex + 2] = 0;
            rawData[byteIndex + 3] = 0;
        }

        byteIndex += 4;
    }

    CGImageRef imgref = CGBitmapContextCreateImage(context);
    UIImage *result = [UIImage imageWithCGImage:imgref];

    CGImageRelease(imgref);
    CGContextRelease(context);
    free(rawData);

    return result;
}
person PKCLsoft    schedule 11.05.2012
comment
Для меня это мой любимый ответ, потому что толерантность может быть. Замечание: допуск составляет от 0 до 255, поэтому ниже 1 он не действует. Плавная версия теперь была бы идеальной! - person Diwann; 21.06.2012
comment
Отличный метод! Кстати. когда вы указываете [UIColor whiteColor], это не сработает из-за того, что это особый вид подкласса UIColor. Когда вы делаете [UIColor colorWithRed: 1.0f green: 1.0f blue: 1.0f alpha: 1.0f], вместо этого он работает. Может сэкономить следующему человеку время на отладку. - person Toad; 12.02.2013
comment
@Toad отличное место! Я уверен, что это было довольно неприятно. Когда я писал это, цветной объект создавался с помощью ползунков, поэтому я не заметил этой проблемы. - person PKCLsoft; 13.02.2013
comment
Полезный ответ (за который я уже проголосовал), но разве не должно быть CGImageRelease для CGBitmapContextCreateImage? например: CGImageRef imgref = CGBitmapContextCreateImage(context); UIImage *result = [UIImage imageWithCGImage:imgref]; CGImageRelease(imgref); - person ; 09.10.2014
comment
Одна проблема, которую я обнаружил с этим кодом, заключается в том, что если я предоставлю, например, [UIColor whiteColor], он фактически станет цветом шкалы серого с компонентами White / Alpha вместо RGBA, поэтому, когда вы извлекаете компоненты, вы получаете R (1), G (1) и B (0). Эта проблема описана здесь stackoverflow.com/questions / 4700168 / - person manecosta; 12.11.2014
comment
Верно, наверное, я не видел его комментария. Во всяком случае, я знаю, что если я использую инициализатор RGB, он работает нормально. Но в идеале код мог бы работать с любым типом цвета. Эта проблема описана в той ссылке, которую я предоставил, хотя решение, на мой взгляд, далеко не удовлетворительное. - person manecosta; 13.11.2014
comment
Отличный ответ, спасибо! Это действительно помогло мне в моей текущей борьбе :) Я также внес в него некоторые изменения, если вы хотите сделать белый цвет прозрачным на черно-белом изображении, изменив альфа на значение того, насколько белый пиксель. Это может пригодиться, когда вы, например, используете черно-белое изображение для маскировки слоя. Я разместил свою функцию ниже. - person romeouald; 27.06.2016

Это настройка кода yubenyi, которая будет работать с несколькими проходами. Он удаляет альфа-канал перед обработкой путем преобразования изображения в несжатый JPEG. Также добавлены некоторые комментарии о том, как работает выбор цветового диапазона.

-(UIImage *)changeWhiteColorTransparent: (UIImage *)image
{
    //convert to uncompressed jpg to remove any alpha channels
    //this is a necessary first step when processing images that already have transparency
    image = [UIImage imageWithData:UIImageJPEGRepresentation(image, 1.0)];
    CGImageRef rawImageRef=image.CGImage;
    //RGB color range to mask (make transparent)  R-Low, R-High, G-Low, G-High, B-Low, B-High
    const double colorMasking[6] = {222, 255, 222, 255, 222, 255};

    UIGraphicsBeginImageContext(image.size);
    CGImageRef maskedImageRef=CGImageCreateWithMaskingColors(rawImageRef, colorMasking);

    //iPhone translation
    CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0.0, image.size.height);
    CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0); 

    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, image.size.width, image.size.height), maskedImageRef);
    UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
    CGImageRelease(maskedImageRef);
    UIGraphicsEndImageContext();    
    return result;
}
person bgolson    schedule 01.09.2012
comment
Это должен был быть образ. Я внес правку. Спасибо за исправление! - person bgolson; 11.03.2013
comment
у меня сработало! Но мне пришлось поменять const float colorMasking[6] на const double colorMasking[6] - person moobi; 30.01.2015
comment
Спасибо @moobi .. спас мне жизнь! - person Kai Burghardt; 28.12.2015
comment
вам следует обновить const double colorMasking[6] до const CGFloat colorMasking[6] в iOS9 - person Jordi Puigdellívol; 02.06.2016
comment
Могу ли я использовать эту функцию, чтобы изменить черный цвет изображения на другой цвет? - person tamtoum1987; 15.07.2016

После использования ваших функций я нашел более простой способ сделать прозрачный фон для UIImage :)

Например, у вас есть изображение PNG с черным фоном, и вы хотите сделать этот фон прозрачным на экране.

Вы можете попробовать это:

UIImage * image = [UIImage imageNamed:@"image.png"];

const CGFloat colorMasking[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
image = [UIImage imageWithCGImage: CGImageCreateWithMaskingColors(image.CGImage, colorMasking)];

In Swift 5:

let colorMasking: [CGFloat] = [0, 0, 0, 0, 0, 0]
guard let cgImage = cgImage?.copy(maskingColorComponents: colorMasking) else { return }
image = UIImage(cgImage: cgImage)

У вас получилось изображение с прозрачным фоном.

Это все :)

person Vitaly    schedule 04.07.2010
comment
Разве это не то же самое, что небольшая часть принятого ответа, и я согласен с @AshishMathur, что это не полное решение. - person PKCLsoft; 13.02.2013

эта функция может работать!

-(UIImage *)changeWhiteColorTransparent: (UIImage *)image
{
    CGImageRef rawImageRef=image.CGImage;

    const float colorMasking[6] = {222, 255, 222, 255, 222, 255};

    UIGraphicsBeginImageContext(image.size);
    CGImageRef maskedImageRef=CGImageCreateWithMaskingColors(rawImageRef, colorMasking);
    {
        //if in iphone
        CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0.0, image.size.height);
        CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0); 
    }

    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, image.size.width, image.size.height), maskedImageRef);
    UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
    CGImageRelease(maskedImageRef);
    UIGraphicsEndImageContext();    
    return result;
}
person yubenyi    schedule 06.08.2010
comment
Блестяще! Именно то, о чем просили. Сработало из коробки! Спасибо. - person PKCLsoft; 03.05.2012
comment
Это делает все мое изображение прозрачным. Я что-то упускаю? - person Buyin Brian; 23.08.2012

Swift 4: (для белой маскировки)

extension UIImage { 
func imageByMakingWhiteBackgroundTransparent() -> UIImage? {

    let image = UIImage(data: UIImageJPEGRepresentation(self, 1.0)!)!
    let rawImageRef: CGImage = image.cgImage!

    let colorMasking: [CGFloat] = [222, 255, 222, 255, 222, 255]
    UIGraphicsBeginImageContext(image.size);

    let maskedImageRef = rawImageRef.copy(maskingColorComponents: colorMasking)
    UIGraphicsGetCurrentContext()?.translateBy(x: 0.0,y: image.size.height)
    UIGraphicsGetCurrentContext()?.scaleBy(x: 1.0, y: -1.0)
    UIGraphicsGetCurrentContext()?.draw(maskedImageRef!, in: CGRect.init(x: 0, y: 0, width: image.size.width, height: image.size.height))
    let result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return result

    }
}
person fegoulart    schedule 05.12.2017
comment
Обновлено для Swift 5: let image = UIImage(data: self.jpegData(compressionQuality: 1.0)!)! - person Daniel Barclay; 21.06.2020
comment
Привет, если я хочу другой цвет вместо белого, каким должен быть ColorMasking? Спасибо - person Daniel Arantes Loverde; 30.07.2020

Основываясь на превосходном ответе @ PKCLsoft, я внес правку, в которой он делает только белый цвет прозрачным, но он делает альфа-канал в зависимости от того, насколько «белый» пиксель. Это может пригодиться, когда вы пытаетесь создать маску из черно-белого изображения, чтобы получить гладкие края на текстах или фигурах.

- (UIImage *)makeWhiteColorTransparentInImage:(UIImage *)image {
   CGImageRef imageRef = [image CGImage];

   NSUInteger width = CGImageGetWidth(imageRef);
   NSUInteger height = CGImageGetHeight(imageRef);
   CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

   NSUInteger bytesPerPixel = 4;
   NSUInteger bytesPerRow = bytesPerPixel * width;
   NSUInteger bitsPerComponent = 8;
   NSUInteger bitmapByteCount = bytesPerRow * height;

   unsigned char *rawData = (unsigned char*) calloc(bitmapByteCount, sizeof(unsigned char));

   CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                             bitsPerComponent, bytesPerRow, colorSpace,
                                             kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
   CGColorSpaceRelease(colorSpace);

   CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);

   int byteIndex = 0;

   while (byteIndex < bitmapByteCount) {
      unsigned char red   = rawData[byteIndex];
      unsigned char green = rawData[byteIndex + 1];
      unsigned char blue  = rawData[byteIndex + 2];
      unsigned char alpha  = rawData[byteIndex + 3];

      if (alpha == 0.0) {
          rawData[byteIndex + 3] = 0.0;
      } else {
          rawData[byteIndex + 3] = 255.0 - (red + green + blue) / 3.0;
      }

      byteIndex += 4;
    }

    CGImageRef imgref = CGBitmapContextCreateImage(context);
    UIImage *result = [UIImage imageWithCGImage:imgref];

    CGImageRelease(imgref);
    CGContextRelease(context);
    free(rawData);

    return result;
}

Надеюсь, когда-нибудь это кому-то поможет :)

person romeouald    schedule 27.06.2016

быстрая версия для белого цвета фона до прозрачного цвета

extension UIImage {
    func imageByMakingWhiteBackgroundTransparent() -> UIImage? {

        let image = UIImage(data: self.jpegData(compressionQuality: 1.0)!)!
        let rawImageRef: CGImage = image.cgImage!

        let colorMasking: [CGFloat] = [222, 255, 222, 255, 222, 255]
        UIGraphicsBeginImageContext(image.size);

        let maskedImageRef = rawImageRef.copy(maskingColorComponents: colorMasking)
        UIGraphicsGetCurrentContext()?.translateBy(x: 0.0,y: image.size.height)
        UIGraphicsGetCurrentContext()?.scaleBy(x: 1.0, y: -1.0)
        UIGraphicsGetCurrentContext()?.draw(maskedImageRef!, in: CGRect.init(x: 0, y: 0, width: image.size.width, height: image.size.height))
        let result = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return result

    }

}
person Jagveer Singh    schedule 22.01.2020

Сам я никогда ничего подобного не делал, но похоже, CGImageCreateWithMaskingColors может здесь пригодиться. Однако использование Core Graphics выходит за рамки того, что я могу объяснить в ответе на этом сайте; Изучите свою документацию и найдите учебные пособия в Интернете.

person Becca Royal-Gordon    schedule 11.03.2009

Расширение UIImage в swift 5 с возможностью изменения любого цвета на прозрачный. На основе ответа @fegoulart.

extension UIImage {
func remove(color: UIColor) -> UIImage? {
    
    let cgColor = color.cgColor
    let components = cgColor.components
    var r = components?[0] ?? 0.0
    var g = components?[1] ?? 0.0
    var b = components?[2] ?? 0.0
    
    r = r * 255.0
    g = g * 255.0
    b = b * 255.0
    
    let colorMasking: [CGFloat] = [r, r, g, g, b, b]
    
    let image = UIImage(data: self.jpegData(compressionQuality: 1.0)!)!
    let rawImageRef: CGImage = image.cgImage!
    
    UIGraphicsBeginImageContext(image.size);
    
    let maskedImageRef = rawImageRef.copy(maskingColorComponents: colorMasking)
    UIGraphicsGetCurrentContext()?.translateBy(x: 0.0,y: image.size.height)
    UIGraphicsGetCurrentContext()?.scaleBy(x: 1.0, y: -1.0)
    UIGraphicsGetCurrentContext()?.draw(maskedImageRef!, in: CGRect.init(x: 0, y: 0, width: image.size.width, height: image.size.height))
    let result = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    return result
}
}
person Johannes Olsson    schedule 17.08.2020