Objective-C. Программное преобразование цветной картинки в черно-белую (grayscale) с сохранением альфа-канала

Недавно мне нужно было решить эту задачу для одного из проектов под iPhone. Естественное, на помощь я позвал Google, который показал мне ссылки сразу на несколько вариантов решения. Однако не все они справились с поставленной задачей должным образом. Все реализации, испробованные мною в начале, «забывали» про альфа-канал. Они преобразовывали его в черный цвет, что было для меня неприемлемо. Альфа-канал должен был сохраняться. Поэтому я стал просматривать варианты более внимательно. Нужную мне реализацию я нашел акурат под ранее не подошедшей. На ней я и остановился. А ниже привожу её исходный код на Objective-C. Для себя, чтобы не искать потом снова, а может и еще кому-нибудь пригодится.

typedef enum {
ALPHA = 0,
BLUE = 1,
GREEN = 2,
RED = 3
} PIXELS;
//конвертирует картинку в "черно-белую" с сохранением альфа-канала
+ (UIImage *)convertToGrayscale:(UIImage*)img {
CGSize size = [img size];
int width = size.width;
int height = size.height;

// пиксели будут рисоваться в этот массив
uint32_t *pixels = (uint32_t *) malloc(width * height * sizeof(uint32_t));

// очищаем пиксели с сохранением прозрачности
memset(pixels, 0, width * height * sizeof(uint32_t));

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

// создаем контекст с RGBA-пикселями
CGContextRef context = CGBitmapContextCreate(pixels, width, height, 8, width * sizeof(uint32_t), colorSpace,
kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);

// отображаем картинку на наш контекст
CGContextDrawImage(context, CGRectMake(0, 0, width, height), [img CGImage]);

for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
uint8_t *rgbaPixel = (uint8_t *) &pixels[y * width + x];

// преобразуем в черно-белый формат, используя следующий метод: http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
uint32_t gray = 0.3 * rgbaPixel[RED] + 0.59 * rgbaPixel[GREEN] + 0.11 * rgbaPixel[BLUE];

// устанавливаем пиксели в серый
rgbaPixel[RED] = gray;
rgbaPixel[GREEN] = gray;
rgbaPixel[BLUE] = gray;
}
}

// создаем CGImageRef из нашего контекста с модифицированными пикселями
CGImageRef image = CGBitmapContextCreateImage(context);

// завершаем работу с context, color space, и pixels
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
free(pixels);

// создаем новый UIImage для возвращения
UIImage *resultUIImage = [UIImage imageWithCGImage:image];

// завершаем работу с image
CGImageRelease(image);

return resultUIImage;
}

В результативности приведенного Objective-C кода я убедился сам. Результат отличный — рекомендую :)

Полезная статья? Их будет больше, если вы поддержите меня!

https://money.yandex.ru/embed/small.xml?account=410011399843532&quickpay=small&yamoney-payment-type=on&button-text=06&button-size=l&button-color=orange&targets=%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D1%80%D0%B6%D0%B8%D0%B2%D0%B0%D1%8E+%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82+lexpenz.com&default-sum=50&successURL=