CGImage float форматът изисква размяна на байтове?

Извършвам обработка на изображения, която изисква данни за изображения с плаваща запетая в сива скала. Методът -imagePlanarFData: по-долу в момента е как извличам тези данни от вход CIImage:

- (NSData *)byteswapPlanarFData:(NSData *)data
                    swapInPlace:(BOOL)swapInPlace;
{
    NSData * outputData = swapInPlace ? data : [data mutableCopy];
    const int32_t * image = [outputData bytes];
    size_t length = [outputData length] / sizeof(*image);
    for (int i = 0;
         i < length;
         i++) {
        int32_t * val = (int32_t *)&image[i];
        *val = OSSwapBigToHostInt32(*val);
    }
    return outputData;
}

- (NSData *)imagePlanarFData:(CIImage *)processedImage;
{
    NSSize size = [processedImage extent].size;
    if (size.width == 0) {
        return nil;
    }
    dispatch_once(&_onceToken, ^ {
        _colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
        _bytesPerRow = size.width * sizeof(float);
        _cgx = CGBitmapContextCreate(NULL, size.width, size.height, 32, _bytesPerRow, _colorSpace, kCGImageAlphaNone | kCGBitmapFloatComponents);
        // Work-around for CIImage drawing EXC_BAD_ACCESS when running with Guard Malloc;
        // see <http://stackoverflow.com/questions/11689233/ciimage-drawing-exc-bad-access>
        NSDictionary * options = nil;
        if (getenv("MallocStackLogging") || getenv("MallocStackLoggingNoCompact")) {
            NSLog(@"Forcing CIImageContext to use software rendering; see %@",
                  @"<http://stackoverflow.com/questions/11689233/ciimage-drawing-exc-bad-access>");
            options = @{ kCIContextUseSoftwareRenderer: @YES };
        }
        _cix = [CIContext contextWithCGContext:_cgx
                                       options:options];
        _rect = CGRectMake(0, 0, size.width, size.height);
    });
    float * data = CGBitmapContextGetData(_cgx);
    CGContextClearRect(_cgx, _rect);
    [_cix drawImage:processedImage
             inRect:_rect
           fromRect:_rect];
    NSData * pixelData = [NSData dataWithBytesNoCopy:data
                                              length:_bytesPerRow * size.height
                                        freeWhenDone:NO];
    // For whatever bizarre reason, CoreGraphics uses big-endian floats (!)
    return [self byteswapPlanarFData:pixelData swapInPlace:NO];
}

Както бе споменато в коментара, бях доста изненадан да видя, че данните за плаващи пиксели излязоха от CGBitmapContext в голям ред. (Определих това само чрез проба и грешка.) Така беше въведен допълнителен методът -byteswapPlanarFData:swapInPlace: и всичко изглеждаше наред със света... За известно време.

Но сега бих искал да направя тези обработени данни обратно в CGImage.

Преди моят код взе float * data буфера, извлечен по-горе, и го използваше директно, за да изобрази нов CGImage и след това да го увие в NSImage, като този:

CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, bytesPerRow * size.height, NULL);
CGImageRef renderedImage = CGImageCreate(size.width, size.height, 32, 32, bytesPerRow, colorSpace, kCGImageAlphaNone | kCGBitmapFloatComponents, provider, NULL, false, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
NSImage * image = [[NSImage alloc] initWithCGImage:renderedImage
                                              size:size];
CGImageRelease(renderedImage);

Така че сега правя това:

pixelData = [mumble imagePlanarFData:processedImage];
// Swap data back to how it was before...
pixelData = [mumble byteswapPlanarFData:pixelData
                            swapInPlace:YES];
float * data = (float *)[pixelData bytes];
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, bytesPerRow * size.height, NULL);
CGImageRef renderedImage = CGImageCreate(size.width, size.height, 32, 32, bytesPerRow, colorSpace, kCGImageAlphaNone | kCGBitmapFloatComponents, provider, NULL, false, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
NSImage * image = [[NSImage alloc] initWithCGImage:renderedImage
                                              size:size];
CGImageRelease(renderedImage);

Горното не работи. Вместо това получавам повредено изображение и поток от оплаквания от CoreGraphics:

Mar 21 05:56:46 aoide.local LabCam[34235] <Error>: CMMConvLut::ConvertFloat 1 input (inf)
Mar 21 05:56:46 aoide.local LabCam[34235] <Error>: ApplySequenceToBitmap failed (-171)
Mar 21 05:56:46 aoide.local LabCam[34235] <Error>: ColorSyncTransformConvert - failed width = 160 height = 199 dstDepth = 7 dstLayout = 0 dstBytesPerRow = 1920 srcDepth = 7 srcLayout = 0 srcBytesPerRow = 640

Къде сгреших?


person Kaelin Colclasure    schedule 21.03.2013    source източник


Отговори (1)


Ааа, открих проблема... Оказа се, че има останала рутина от преди да добавя извикването за размяна на байтове към -imagePlanarFData:, която също разменяше байтовете на място...

Бих все пак искал да чуя някакво обяснение защо CoreGraphics очаква тези стойности в ред на байтовете в голям ред на нашата платформа в малък ред.

person Kaelin Colclasure    schedule 21.03.2013
comment
От lists.apple.com/archives/Quartz-dev/ /2011/Dec/msg00001.html: Като малко историческа странност, Quartz задава по подразбиране несъществуващ endian флаг като big-endian, а не host endian. - person aledalgrande; 25.09.2014