Преобразование NSData в CGImage и затем обратно в NSData делает файл слишком большим

Я построил камеру, используя AVFoundation,

Однажды мой AVCaptureStillImageOutput завершил captureStillImageAsynchronouslyFromConnection:completionHandler: метод, я создаю объект NSData, как это:

         NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];

Когда у меня есть NSData объект, я хотел бы повернуть изображение без преобразования в UIImage, Я узнал, что я могу преобразовать в CGImage сделать это.

После того, как у меня есть imageData, я начинаю процесс преобразования в CGImage, но я обнаружил, что CGImageRef заканчивается в тринадцать раз больше, чем NSData объект.

Вот код, который я использую для преобразования в CGImage от NSData:

CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)(imageData));
CGImageRef imageRef = CGImageCreateWithJPEGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);

Если я попытаюсь NSLog размер изображения достигает 30 мегабайт, когда NSData было 1,5-2 мегабайта изображения!

size_t imageSize = CGImageGetBytesPerRow(imageRef) * CGImageGetHeight(imageRef);

    NSLog(@"cgimage size = %zu",imageSize);

Я подумал, что, возможно, когда вы переходите от NSData к CGImage, изображение распаковывается, а затем, может быть, если я преобразую обратно в NSData, оно может вернуться к правильному размеру файла.

imageData = (NSData *) CFBridgingRelease(CGDataProviderCopyData(CGImageGetDataProvider(imageRef)));

Выше NSData имеет то же самое length как CGImageRef объект.

Если я пытаюсь сохранить изображение, это изображение размером 30 Мб, которое нельзя открыть.

Я совершенно новичок в использовании CGImage, поэтому я не уверен, что я преобразовываю из NSData в CGImage и обратно неправильно, или мне нужно вызвать какой-то метод для распаковки снова.

Заранее спасибо,

Будет

2 ответа

Решение

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

Хотя теоретически возможно преобразовать CGImageRef вернуться к NSData как вы описали, сами данные являются недействительными и не являются реальными JPEG или PNG, поскольку вы обнаружили, что они не читаются. Так что я не думаю, что NSData.length верно. Вы должны прыгнуть через несколько шагов, чтобы воссоздать NSData представление CGImageRef:

// incoming image data
NSData *image;

// create the image ref
CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef) image);
CGImageRef imageRef = CGImageCreateWithJPEGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);

// image metadata properties (EXIF, GPS, TIFF, etc)
NSDictionary *properties;

// create the new output data
CFMutableDataRef newImageData = CFDataCreateMutable(NULL, 0);
// my code assumes JPEG type since the input is from the iOS device camera
CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef) @"image/jpg", kUTTypeImage);
// create the destination
CGImageDestinationRef destination = CGImageDestinationCreateWithData(newImageData, type, 1, NULL);
// add the image to the destination
CGImageDestinationAddImage(destination, imageRef, (__bridge CFDictionaryRef) properties);
// finalize the write
CGImageDestinationFinalize(destination);

// memory cleanup
CGDataProviderRelease(imgDataProvider);
CGImageRelease(imageRef);
CFRelease(type);
CFRelease(destination);

NSData *newImage = (__bridge_transfer NSData *)newImageData;

С этими шагами newImage.length должен быть таким же, как image.length, Я не проверял, так как я фактически делаю обрезку между входом и выходом, но в зависимости от кадрирования размер примерно соответствует ожидаемому (выход составляет примерно половину пикселей ввода, и, следовательно, длина вывода составляет примерно половину размера длины ввода).

если кто-то ищет быструю версию «Covert CGImage to Data», вот она:

      extension CGImage {
    var jpegData: Data? {
        guard let mutableData = CFDataCreateMutable(nil, 0),
              let destination = CGImageDestinationCreateWithData(mutableData, kUTTypeJPEG, 1, nil)
        else {
            return nil
        }
        CGImageDestinationAddImage(destination, self, nil)
        guard CGImageDestinationFinalize(destination) else { return nil }
        return mutableData as Data
    }

}

Другие вопросы по тегам