2013-07-16 2 views
2

У меня есть 3 файла, один с красным каналом, один с зеленым каналом, один с голубым каналом. Теперь я хочу объединить эти 3 изображения с одним, где каждое изображение является одним цветным каналом в готовом изображении.Создайте изображение RGB с каждого канала

Как я могу это сделать с какао? У меня есть решение, которое работает, но работает слишком медленно:

NSBitmapImageRep *rRep = [[rImage representations] objectAtIndex: 0]; 
NSBitmapImageRep *gRep = [[gImage representations] objectAtIndex: 0]; 
NSBitmapImageRep *bRep = [[bImage representations] objectAtIndex: 0]; 
NSBitmapImageRep *finalRep = [rRep copy]; 
for (NSUInteger i = 0; i < [rRep pixelsWide]; i++) { 
    for (NSUInteger j = 0; j < [rRep pixelsHigh]; j++) { 
     CGFloat r = [[rRep colorAtX:i y:j] redComponent]; 
     CGFloat g = [[gRep colorAtX:i y:j] greenComponent]; 
     CGFloat b = [[bRep colorAtX:i y:j] blueComponent]; 
     [finalRep setColor:[NSColor colorWithCalibratedRed:r green:g blue:b alpha:1.0] atX:i y:j]; 
    } 
} 
NSData *data = [finalRep representationUsingType:NSJPEGFileType properties:[NSDictionary dictionaryWithObject:[NSNumber numberWithDouble:0.7] forKey:NSImageCompressionFactor]]; 
[data writeToURL:[panel URL] atomically:YES]; 

ответ

1

Accelerate.framework обеспечивает функцию объединения трех плоских изображений в один пункт назначения: vImageConvert_Planar8toRGB888.
Я не пробовал ваш подход, но метод, основанный на vImage, довольно быстро.
Я смог объединить три (R, G, B) плоскости изображения 1680x1050 в ~ 0,1 с на моем Mac. Фактическое преобразование занимает ~ 1/3 того времени. Остальное - установка & file IO.

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{ 
    NSDate* start = [NSDate date]; 

    NSURL* redImageURL = [[NSBundle mainBundle] URLForImageResource:@"red"]; 
    NSURL* greenImageURL = [[NSBundle mainBundle] URLForImageResource:@"green"]; 
    NSURL* blueImageURL = [[NSBundle mainBundle] URLForImageResource:@"blue"]; 
    NSData* redImageData = [self newChannelDataFromImageAtURL:redImageURL]; 
    NSData* greenImageData = [self newChannelDataFromImageAtURL:greenImageURL]; 
    NSData* blueImageData = [self newChannelDataFromImageAtURL:blueImageURL]; 

    //We use our "Red" image to measure the dimensions. We assume that all images & the destination have the same size 
    CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)redImageURL, NULL); 
    NSDictionary* properties = (__bridge NSDictionary*)CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL); 
    CGFloat width = [properties[(id)kCGImagePropertyPixelWidth] doubleValue]; 
    CGFloat height = [properties[(id)kCGImagePropertyPixelHeight] doubleValue]; 

    self.image = [self newImageWithSize:CGSizeMake(width, height) fromRedChannel:redImageData greenChannel:greenImageData blueChannel:blueImageData]; 
    NSLog(@"Combining 3 (R, G, B) planes of size %@ took:%fs", NSStringFromSize(CGSizeMake(width, height)), [[NSDate date] timeIntervalSinceDate:start]); 
} 

- (NSImage*)newImageWithSize:(CGSize)size fromRedChannel:(NSData*)redImageData greenChannel:(NSData*)greenImageData blueChannel:(NSData*)blueImageData 
{ 
    vImage_Buffer redBuffer; 
    redBuffer.data = (void*)redImageData.bytes; 
    redBuffer.width = size.width; 
    redBuffer.height = size.height; 
    redBuffer.rowBytes = [redImageData length]/size.height; 

    vImage_Buffer greenBuffer; 
    greenBuffer.data = (void*)greenImageData.bytes; 
    greenBuffer.width = size.width; 
    greenBuffer.height = size.height; 
    greenBuffer.rowBytes = [greenImageData length]/size.height; 

    vImage_Buffer blueBuffer; 
    blueBuffer.data = (void*)blueImageData.bytes; 
    blueBuffer.width = size.width; 
    blueBuffer.height = size.height; 
    blueBuffer.rowBytes = [blueImageData length]/size.height; 

    size_t destinationImageBytesLength = size.width*size.height*3; 
    const void* destinationImageBytes = valloc(destinationImageBytesLength); 
    NSData* destinationImageData = [[NSData alloc] initWithBytes:destinationImageBytes length:destinationImageBytesLength]; 
    vImage_Buffer destinationBuffer; 
    destinationBuffer.data = (void*)destinationImageData.bytes; 
    destinationBuffer.width = size.width; 
    destinationBuffer.height = size.height; 
    destinationBuffer.rowBytes = [destinationImageData length]/size.height; 

    vImage_Error result = vImageConvert_Planar8toRGB888(&redBuffer, &greenBuffer, &blueBuffer, &destinationBuffer, 0); 
    NSImage* image = nil; 
    if(result == kvImageNoError) 
    { 
     //TODO: If you need color matching, use an appropriate colorspace here 
     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
     CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)(destinationImageData)); 
     CGImageRef finalImageRef = CGImageCreate(size.width, size.height, 8, 24, destinationBuffer.rowBytes, colorSpace, kCGBitmapByteOrder32Big|kCGImageAlphaNone, dataProvider, NULL, NO, kCGRenderingIntentDefault); 
     CGColorSpaceRelease(colorSpace); 
     CGDataProviderRelease(dataProvider); 
     image = [[NSImage alloc] initWithCGImage:finalImageRef size:NSMakeSize(size.width, size.height)]; 
     CGImageRelease(finalImageRef); 
    } 
    free((void*)destinationImageBytes); 
    return image; 
} 

- (NSData*)newChannelDataFromImageAtURL:(NSURL*)imageURL 
{ 
    CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)imageURL, NULL); 
    if(imageSource == NULL){return NULL;} 
    CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); 
    CFRelease(imageSource); 
    if(image == NULL){return NULL;} 
    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image); 
    CGFloat width = CGImageGetWidth(image); 
    CGFloat height = CGImageGetHeight(image); 
    size_t bytesPerRow = CGImageGetBytesPerRow(image); 
    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(image); 
    CGContextRef bitmapContext = CGBitmapContextCreate(NULL, width, height, 8, bytesPerRow, colorSpace, bitmapInfo); 
    NSData* data = NULL; 
    if(NULL != bitmapContext) 
    { 
     CGContextDrawImage(bitmapContext, CGRectMake(0.0, 0.0, width, height), image); 
     CGImageRef imageRef = CGBitmapContextCreateImage(bitmapContext); 
     if(NULL != imageRef) 
     { 
      data = (NSData*)CFBridgingRelease(CGDataProviderCopyData(CGImageGetDataProvider(imageRef))); 
     } 
     CGImageRelease(imageRef); 
     CGContextRelease(bitmapContext); 
    } 
    CGImageRelease(image); 
    return data; 
} 
+0

спасибо! именно то, что я искал :) еще один вопрос: некоторые из изображений имеют все 3 канала в нем, но мне нужен только один. Как я могу это сделать? – thomasguenzel

+0

Откуда берутся ваши изображения канала? Изображения, которые я использовал для тестирования вышеуказанного метода, уже были в сером цветовом пространстве. Есть некоторые SO-вопросы, посвященные этой теме, например. (не пробовал): http://stackoverflow.com/questions/3340564/convert-image-to-bw-problem-cgcontext-iphone-dev –

+0

vImage_Buffer redBuffer2; redBuffer2.width = size.width; redBuffer2.height = size.height; redBuffer2.rowBytes = size.width; vImageConvert_RGB888toPlanar8 (& redBuffer, & redBuffer2, nil, nil, kvImageNoFlags); Я добавил это ниже "redBuffer.rowBytes = [redImageData length] /size.height;" но это дает мне EXC_BAD_ACCESS, что я делаю неправильно? – thomasguenzel

0

Ваша программа создает много очень многих много много объектов цвета.

Хотя ваша программа может просто получить доступ к изображениям reps 'bitmapData, это потребует, чтобы ваша программа много знала о представлениях растровых изображений.

Прежде чем принимать этот подход, вы должны предпочесть позволить Кварцу делать тяжелые, оказывая каждое изображение к CGBitmapContext (например, с помощью CGContextDrawImage(gtx, rect, img.CGImage)), а затем извлечения/копирования визуализированного значения компонентов из оказанных результате к адресате RGB растрового изображения ,

Если ваши входы не являются многокомпонентными цветовыми моделями (например, в градациях серого), вы должны визуализировать исходную цветовую модель, чтобы сохранить кучу времени и памяти процессора.