NSImage + NSBitmapImageRep = Преобразование файла изображения RAW из одного формата в другой

Я пытаюсь написать прототип, чтобы доказать, что преобразование RAW из одного формата в другой возможно. Мне нужно преобразовать необработанный файл Nikon, имеющий формат .NEF, в формат Canon .CR2. С помощью различных сообщений я создаю BitmapImageRep исходного изображения в формате TIFF и использую его для записи выходного файла с расширением .CR2.

Он действительно работает, но единственная проблема для меня в том, что размер входного файла составляет 21,5 МБ, а на выходе получается 144,4 МБ. При использовании NSTIFFCompressionPackBits дает мне 142,1 МБ.

Я хочу понять, что происходит, я пробовал различные доступные перечисления сжатия, но безуспешно.

Пожалуйста, помогите мне понять это. Это исходный код:

@interface NSImage(RawConversion)
- (void) saveAsCR2WithName:(NSString*) fileName;
@end

@implementation NSImage(RawConversion)
- (void) saveAsCR2WithName:(NSString*) fileName
{
    // Cache the reduced image
    NSData *imageData = [self TIFFRepresentation];
    NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
//    http://www.cocoabuilder.com/archive/cocoa/151789-nsbitmapimagerep-compressed-tiff-large-files.html
    NSDictionary *imageProps = [NSDictionary dictionaryWithObjectsAndKeys:  [NSNumber numberWithInt:NSTIFFCompressionJPEG],NSImageCompressionMethod,
                                                                            [NSNumber numberWithFloat: 1.0], NSImageCompressionFactor,
                                                                            nil];
    imageData = [imageRep representationUsingType:NSTIFFFileType properties:imageProps];
    [imageData writeToFile:fileName atomically:NO];
}
@end

Как я могу получить выходной файл в формате CR2, но почти равный размеру входного файла с небольшими изменениями, необходимыми для файла CR2?

Редактировать 1: внесены изменения, основанные на предложении Питера использовать метод CGImageDestinationAddImageFromSource, но я все равно получаю тот же результат. Размер исходного файла NEF 21,5 МБ, но размер файла назначения после преобразования 144,4 МБ.

Пожалуйста, просмотрите код:

-(void)saveAsCR2WithCGImageMethodUsingName:(NSString*)inDestinationfileName withSourceFile:(NSString*)inSourceFileName
{
    CGImageSourceRef sourceFile = MyCreateCGImageSourceRefFromFile(inSourceFileName);
    CGImageDestinationRef destinationFile = createCGImageDestinationRefFromFile(inDestinationfileName);
    CGImageDestinationAddImageFromSource(destinationFile, sourceFile, 0, NULL);
//https://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/ImageIOGuide/ikpg_dest/ikpg_dest.html
    CGImageDestinationFinalize(destinationFile);
}

CGImageSourceRef MyCreateCGImageSourceRefFromFile (NSString* path)
{
    // Get the URL for the pathname passed to the function.
    NSURL *url = [NSURL fileURLWithPath:path];
    CGImageSourceRef  myImageSource;
    CFDictionaryRef   myOptions = NULL;
    CFStringRef       myKeys[2];
    CFTypeRef         myValues[2];

    // Set up options if you want them. The options here are for
    // caching the image in a decoded form and for using floating-point
    // values if the image format supports them.
    myKeys[0] = kCGImageSourceShouldCache;
    myValues[0] = (CFTypeRef)kCFBooleanTrue;
    myKeys[1] = kCGImageSourceShouldAllowFloat;
    myValues[1] = (CFTypeRef)kCFBooleanTrue;

    // Create the dictionary
    myOptions = CFDictionaryCreate(NULL, (const void **) myKeys,
                                   (const void **) myValues, 2,
                                   &kCFTypeDictionaryKeyCallBacks,
                                   & kCFTypeDictionaryValueCallBacks);

    // Create an image source from the URL.
    myImageSource = CGImageSourceCreateWithURL((CFURLRef)url, myOptions);
    CFRelease(myOptions);

    // Make sure the image source exists before continuing
    if (myImageSource == NULL){
        fprintf(stderr, "Image source is NULL.");
        return  NULL;
    }
    return myImageSource;
}

CGImageDestinationRef createCGImageDestinationRefFromFile (NSString *path)
{
    NSURL *url = [NSURL fileURLWithPath:path];
    CGImageDestinationRef myImageDestination;

//https://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/ImageIOGuide/ikpg_dest/ikpg_dest.html
    float compression = 1.0; // Lossless compression if available.
    int orientation = 4; // Origin is at bottom, left.
    CFStringRef myKeys[3];
    CFTypeRef   myValues[3];
    CFDictionaryRef myOptions = NULL;
    myKeys[0] = kCGImagePropertyOrientation;
    myValues[0] = CFNumberCreate(NULL, kCFNumberIntType, &orientation);
    myKeys[1] = kCGImagePropertyHasAlpha;
    myValues[1] = kCFBooleanTrue;
    myKeys[2] = kCGImageDestinationLossyCompressionQuality;
    myValues[2] = CFNumberCreate(NULL, kCFNumberFloatType, &compression);
    myOptions = CFDictionaryCreate( NULL, (const void **)myKeys, (const void **)myValues, 3,
                                   &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

//https://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/ImageIOGuide/imageio_basics/ikpg_basics.html#//apple_ref/doc/uid/TP40005462-CH216-SW3
    CFStringRef destFileType = CFSTR("public.tiff");
//    CFStringRef destFileType = kUTTypeJPEG;
    CFArrayRef types = CGImageDestinationCopyTypeIdentifiers(); CFShow(types);

    myImageDestination = CGImageDestinationCreateWithURL((CFURLRef)url, destFileType, 1, myOptions);
    return myImageDestination;
}

Редактировать 2: Использовал второй подход, рассказанный @Peter. Это дает интересный результат. Эффект такой же, как при переименовании файла в программе поиска, например, «example_image.NEF» в «example_image.CR2». Удивительно, что происходит как при программном преобразовании, так и в поисковике: исходный файл размером 21,5 МБ окажется размером 59 КБ. Это без какого-либо сжатия, установленного в коде. Пожалуйста, посмотрите код и предложите:

-(void)convertNEFWithTiffIntermediate:(NSString*)inNEFFile toCR2:(NSString*)inCR2File
{
    NSData *fileData = [[NSData alloc] initWithContentsOfFile:inNEFFile];
    if (fileData)
    {
        NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:fileData];
//        [imageRep setCompression:NSTIFFCompressionNone
//                          factor:1.0];
        NSDictionary *imageProps = nil;
        NSData *destinationImageData = [imageRep representationUsingType:NSTIFFFileType properties:imageProps];
        [destinationImageData writeToFile:inCR2File atomically:NO];
    }
}

person Raj Pawan Gumdal    schedule 17.03.2013    source источник
comment
Почему вы пометили это как [какао-прикосновение], если вы используете NSImage и NSBitmapImageRep?   -  person Peter Hosey    schedule 17.03.2013
comment
Извините, спасибо за исправление. Я разработчик iOS и привык отмечать это таким образом. Сейчас попробую оба ваших метода.   -  person Raj Pawan Gumdal    schedule 17.03.2013
comment
Переименование файла в Finder меняет его размер на 59 КБ? Это не может быть правдой.   -  person Peter Hosey    schedule 17.03.2013
comment
Удивительно то, что происходит.   -  person Raj Pawan Gumdal    schedule 18.03.2013
comment
NSImage может читать файлы почти всех типов, включая NEF и CR2, но NSBitmapImageRep может предоставить нам типы файлов только одного из следующих: NSTIFFFileType, NSBMPFileType, NSGIFFileType, NSJPEGFileType, NSPNGFileType, NSJPEG2000FileType. Означает ли это, что нет возможности записать файл CR2 из BitmapImageRep?   -  person Raj Pawan Gumdal    schedule 18.03.2013


Ответы (3)


Первое, что я бы попробовал, вообще не связано с NSImage или NSBitmapImageRep. Вместо этого я бы создал CGImageSource для исходного файла и CGImageDestination для файла назначения и использовал CGImageDestinationAddImageFromSource для передачи всех изображений из A в B.

person Peter Hosey    schedule 17.03.2013
comment
Я следовал этому подходу, но пока не добился успеха :( См. Мой отредактированный вопрос, в котором есть недавний код. Пробуем ваш следующий подход прямо сейчас. - person Raj Pawan Gumdal; 17.03.2013
comment
@Raj: Вы использовали тот же формат вывода, поэтому, конечно, получили тот же результат. Если вам нужны данные CR2, запрашивайте их, а не TIFF. - person Peter Hosey; 18.03.2013
comment
Но как получить данные CR2 из следующих доступных? NSTIFFFileType, NSBMPFileType, NSGIFFileType, NSJPEGFileType, NSPNGFileType, NSJPEG2000FileType - person Raj Pawan Gumdal; 18.03.2013
comment
@Raj: Обратите внимание, какой ответ мы комментируем. Эти типы предназначены для NSBitmapImageRep, а не для CGImageDestination. - person Peter Hosey; 18.03.2013
comment
Ах да, вы снова правы, но для CGImageDestinationRef также доступно лишь несколько опций, CR2 среди них отсутствует: kUTTypeImage, kUTTypeJPEG, kUTTypeJPEG2000, kUTTypeTIFF, kUTTypePICT, kUTTypeTIFF, kUTTypePICT, kUTTypeTyPNG, kUTTypeImpePNG, kUTTypePNGO - person Raj Pawan Gumdal; 18.03.2013
comment
@Raj: Есть некоторые различия в фактическом списке, возвращаемом CGImageDestinationCopyTypeIdentifiers (по состоянию на 10.7.5), но да, вы правы - CR2 там нет. Я полагаю, вы могли бы узнать, что такое ИМП, и попробовать его, но если бы его даже нет в списке ИМП, объявленных системой, я бы не стал задерживать дыхание. - person Peter Hosey; 18.03.2013
comment
Я проверил определенные системой UTI, используя этот код: CFArrayRef types = CGImageDestinationCopyTypeIdentifiers (); CFShow (типы); Для CR2 нет ИМП. Означает ли это, что производители камер предоставляют код только для чтения файлов RAW, но не для обратной записи в них? И означает ли это, что это невозможно сделать? Интересный! - person Raj Pawan Gumdal; 18.03.2013
comment
@Raj: Есть намного больше UTI, но это те, которые поддерживает CGImageDestination. Я понятия не имею, что поставщики камер сделали или не предоставили. Однако кажется, что вы не можете сделать это ни с помощью CGImageDestination, ни с помощью NSBitmapImageRep; предполагая, что вы не можете сделать это с помощью Core Image (который, как я знаю, может читать необработанные изображения, но это также решенная проблема в двух других API), вам придется получить чью-то библиотеку для выполнения это или напишите свой собственный код, чтобы сделать это. - person Peter Hosey; 18.03.2013
comment
Хорошо, теперь я понимаю, спасибо за ваше руководство. У меня создалось впечатление, что, поскольку NSImage читает это, должен быть и обратный механизм. Было бы неплохо поработать, но фотографы будут использовать его с жестокими намерениями! На самом деле я начал с этой попытки доказать, что фотограф каким-то образом преобразовал наше изображение NEF RAW в CR2 и теперь объявляет его своим. В любом случае это не имеет отношения к StackOverflow! - person Raj Pawan Gumdal; 18.03.2013

В этом коде вы дважды конвертируете в TIFF:

  1. Я предполагаю, что вы создаете NSImage из исходного файла.
  2. Вы запрашиваете у NSImage его TIFFRepresentation (преобразование TIFF # 1).
  3. Вы создаете NSBitmapImageRep из первых данных TIFF.
  4. Вы просите NSBitmapImageRep сгенерировать второе представление TIFF (преобразование TIFF # 2).

Рассмотрите возможность создания NSBitmapImageRep непосредственно из исходных данных и вообще не используйте NSImage. Затем вы сразу перейдете к шагу 4, чтобы сгенерировать выходные данные.

(Но я бы все равно сначала попробовал CGImageDestinationAddImageFromSource.)

person Peter Hosey    schedule 17.03.2013
comment
Как вы сказали, пожалуйста, посмотрите Edit 2 в моем вопросе, что-то, что мне не хватает? Есть ли другой способ добиться того же? - person Raj Pawan Gumdal; 17.03.2013

Файлы необработанных изображений имеют собственное (проприетарное) представление. Например, они могут использовать 14-битный компонент и шаблоны мозаики, которые не поддерживаются вашим кодом. Я думаю, вам следует использовать API нижнего уровня и действительно перепроектировать формат RAW, в который вы пытаетесь сохранить.

Я бы начал с DNG, что относительно просто, поскольку Adobe предоставляет SDK для его написания.

person Yoav    schedule 29.04.2014