1

いくつかの NSImage オブジェクトを 90 度回転させ、ある色のピクセルの色を別の色に変更し、RGB565 データ表現を NSData オブジェクトとして取得する必要がある状況に陥っています。

Accelerate フレームワークで vImageConvert_ARGB8888toRGB565 関数を見つけたので、これで RGB565 出力を実行できるはずです。

ここ StackOverflow で見つけたいくつかの UIImage ローテーションがありますが、CGContextRef ではなく NSGraphicsContext を使用する必要があるように見えるので、それらを NSImage に変換するのに問題がありますか?

理想的には、これらを NSImage カテゴリに入れたいので、呼び出すだけです。

NSImage *rotated = [inputImage rotateByDegrees:90];
NSImage *colored = [rotated changeColorFrom:[NSColor redColor] toColor:[NSColor blackColor]];
NSData *rgb565 = [colored rgb565Data];

画像操作は初めてなので、どこから始めればよいかわかりません。

私が得ることができる助けに感謝します。

編集 (2013/04/22)

このコードをつなぎ合わせて RGB565 データを生成することができました。逆さまに生成され、小さなアーティファクトがいくつかあります。1 つ目は異なる座標系が使用されているためであり、2 つ目はおそらく PNG から BMP に移行したためです。BMP を使用して開始し、透過的でない PNG を使用して、さらにテストを行います。

- (NSData *)RGB565Data
{
    CGContextRef cgctx = CreateARGBBitmapContext(self.CGImage);
    if (cgctx == NULL)
        return nil;

    size_t w = CGImageGetWidth(self.CGImage);
    size_t h = CGImageGetHeight(self.CGImage);
    CGRect rect = {{0,0},{w,h}};
    CGContextDrawImage(cgctx, rect, self.CGImage);

    void *data = CGBitmapContextGetData (cgctx);
    CGContextRelease(cgctx);

    if (!data)
        return nil;

    vImage_Buffer src;
    src.data = data;
    src.width = w;
    src.height = h;
    src.rowBytes = (w * 4);

    void* destData = malloc((w * 2) * h);

    vImage_Buffer dst;
    dst.data = destData;
    dst.width = w;
    dst.height = h;
    dst.rowBytes = (w * 2);

    vImageConvert_ARGB8888toRGB565(&src, &dst, 0);

    size_t dataSize = 2 * w * h; // RGB565 = 2 5-bit components and 1 6-bit (16 bits/2 bytes)
    NSData *RGB565Data = [NSData dataWithBytes:dst.data length:dataSize];

    free(destData);

    return RGB565Data;
}

- (CGImageRef)CGImage
{
    return [self CGImageForProposedRect:NULL context:[NSGraphicsContext currentContext] hints:nil];
}

CGContextRef CreateARGBBitmapContext (CGImageRef inImage)
{
    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    void *          bitmapData;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;


    size_t pixelsWide = CGImageGetWidth(inImage);
    size_t pixelsHigh = CGImageGetHeight(inImage);
    bitmapBytesPerRow   = (int)(pixelsWide * 4);
    bitmapByteCount     = (int)(bitmapBytesPerRow * pixelsHigh);

    colorSpace = CGColorSpaceCreateDeviceRGB();
    if (colorSpace == NULL)
        return nil;

    bitmapData = malloc( bitmapByteCount );
    if (bitmapData == NULL)
    {
        CGColorSpaceRelease( colorSpace );
        return nil;
    }
    context = CGBitmapContextCreate (bitmapData,
                                 pixelsWide,
                                 pixelsHigh,
                                 8,
                                 bitmapBytesPerRow,
                                 colorSpace,
                                 kCGImageAlphaPremultipliedFirst);
    if (context == NULL)
    {
        free (bitmapData);
        fprintf (stderr, "Context not created!");
    }
    CGColorSpaceRelease( colorSpace );

    return context;
}
4

3 に答える 3

1

ほとんどの場合、Core Image を使用する必要があります。

CIAffineTransform フィルターで実行できる回転。これは NSAffineTransform オブジェクトを取ります。以前にそのクラスを使用したことがあるかもしれません。(NSImage 自体を使用してローテーションを行うこともできますが、Core Image を使用する方が簡単で、おそらく次のステップでそれを使用する必要があります。)

「ある色のピクセルの色を別の色に変更する」という意味がわかりません。それは多くの異なることのいずれかを意味する可能性があります。ただし、そのためのフィルターがある可能性があります。

また、565 データが特に必要な理由もわかりませんが、それが本当に必要であると仮定すると、その機能が関与することは間違いありません。CIContextの最下位レベルのレンダリング メソッドを使用してコンポーネントあたり 8 ビットの ARGB 出力を取得し、その vImage 関数を使用して 565 RGB に変換します。

于 2013-04-22T08:10:15.133 に答える
1

NSBitmapImageRep を使用して、必要なものを取得できました(少しハックしてアクセスします)。誰かがこれを行うより良い方法を知っている場合は、共有してください。

- (NSBitmapImageRep)bitmap メソッドは私のハックです。NSImage は NSBitmapImageRep のみを持ち始めますが、ローテーション メソッドの後、カラー コードを壊す NSBitmapImageRep よりも優先される CIImageRep が追加されます (NSImage は色を付けない CIImageRep をレンダリングするため)。

BitmapImage.m (NSImage のサブクラス)

CGContextRef CreateARGBBitmapContext (CGImageRef inImage)
{
    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    void *          bitmapData;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;


    size_t pixelsWide = CGImageGetWidth(inImage);
    size_t pixelsHigh = CGImageGetHeight(inImage);
    bitmapBytesPerRow   = (int)(pixelsWide * 4);
    bitmapByteCount     = (int)(bitmapBytesPerRow * pixelsHigh);

    colorSpace = CGColorSpaceCreateDeviceRGB();
    if (colorSpace == NULL)
        return nil;

    bitmapData = malloc( bitmapByteCount );
    if (bitmapData == NULL)
    {
        CGColorSpaceRelease( colorSpace );
        return nil;
    }
    context = CGBitmapContextCreate (bitmapData,
                                     pixelsWide,
                                     pixelsHigh,
                                     8,
                                     bitmapBytesPerRow,
                                     colorSpace,
                                     kCGImageAlphaPremultipliedFirst);
    if (context == NULL)
    {
        free (bitmapData);
        fprintf (stderr, "Context not created!");
    }
    CGColorSpaceRelease( colorSpace );

    return context;
}

- (NSData *)RGB565Data
{
    CGContextRef cgctx = CreateARGBBitmapContext(self.CGImage);
    if (cgctx == NULL)
        return nil;

    size_t w = CGImageGetWidth(self.CGImage);
    size_t h = CGImageGetHeight(self.CGImage);
    CGRect rect = {{0,0},{w,h}};
    CGContextDrawImage(cgctx, rect, self.CGImage);

    void *data = CGBitmapContextGetData (cgctx);
    CGContextRelease(cgctx);

    if (!data)
        return nil;

    vImage_Buffer src;
    src.data = data;
    src.width = w;
    src.height = h;
    src.rowBytes = (w * 4);

    void* destData = malloc((w * 2) * h);

    vImage_Buffer dst;
    dst.data = destData;
    dst.width = w;
    dst.height = h;
    dst.rowBytes = (w * 2);

    vImageConvert_ARGB8888toRGB565(&src, &dst, 0);

    size_t dataSize = 2 * w * h; // RGB565 = 2 5-bit components and 1 6-bit (16 bits/2 bytes)
    NSData *RGB565Data = [NSData dataWithBytes:dst.data length:dataSize];

    free(destData);

    return RGB565Data;
}

- (NSBitmapImageRep*)bitmap
{
    NSBitmapImageRep *bitmap = nil;

    NSMutableArray *repsToRemove = [NSMutableArray array];

    // Iterate through the representations that back the NSImage
    for (NSImageRep *rep in self.representations)
    {
        // If the representation is a bitmap
        if ([rep isKindOfClass:[NSBitmapImageRep class]])
        {
            bitmap = [(NSBitmapImageRep*)rep retain];
            break;
        }
        else
        {
            [repsToRemove addObject:rep];
        }
    }

    // If no bitmap representation was found, we create one (this shouldn't occur)
    if (bitmap == nil)
    {
        bitmap = [[[NSBitmapImageRep alloc] initWithCGImage:self.CGImage] retain];
        [self addRepresentation:bitmap];
    }

    for (NSImageRep *rep2 in repsToRemove)
    {
        [self removeRepresentation:rep2];
    }

    return [bitmap autorelease];
}

- (NSColor*)colorAtX:(NSInteger)x y:(NSInteger)y
{
    return [self.bitmap colorAtX:x y:y];
}

- (void)setColor:(NSColor*)color atX:(NSInteger)x y:(NSInteger)y
{
    [self.bitmap setColor:color atX:x y:y];
}

NSImage+Extra.m (NSImage カテゴリ)

- (CGImageRef)CGImage
{
    return [self CGImageForProposedRect:NULL context:[NSGraphicsContext currentContext] hints:nil];
}

使用法

- (IBAction)load:(id)sender
{
    NSOpenPanel* openDlg = [NSOpenPanel openPanel];

    [openDlg setCanChooseFiles:YES];

    [openDlg setCanChooseDirectories:YES];

    if ( [openDlg runModalForDirectory:nil file:nil] == NSOKButton )
    {
        NSArray* files = [openDlg filenames];

        for( int i = 0; i < [files count]; i++ )
        {
            NSString* fileName = [files objectAtIndex:i];
            BitmapImage *image = [[BitmapImage alloc] initWithContentsOfFile:fileName];

            imageView.image = image;
        }
    }
}

- (IBAction)colorize:(id)sender
{
    float width = imageView.image.size.width;
    float height = imageView.image.size.height;

    BitmapImage *img = (BitmapImage*)imageView.image;

    NSColor *newColor = [img colorAtX:1 y:1];

    for (int x = 0; x <= width; x++)
    {
        for (int y = 0; y <= height; y++)
        {
            if ([img colorAtX:x y:y] == newColor)
            {
                [img setColor:[NSColor redColor] atX:x y:y];
            }
        }
    }
    [imageView setNeedsDisplay:YES];
}

- (IBAction)rotate:(id)sender
{
    BitmapImage *img = (BitmapImage*)imageView.image;
    BitmapImage *newImg = [img rotate90DegreesClockwise:NO];
    imageView.image = newImg;
}

編集 (2013/04/24)

次のコードを変更しました。

- (RGBColor)colorAtX:(NSInteger)x y:(NSInteger)y
{
    NSUInteger components[4];
    [self.bitmap getPixel:components atX:x y:y];
    //NSLog(@"R: %ld, G:%ld, B:%ld", components[0], components[1], components[2]);

    RGBColor color = {components[0], components[1], components[2]};

    return color;
}

- (BOOL)color:(RGBColor)a isEqualToColor:(RGBColor)b
{
    return ((a.red == b.red) && (a.green == b.green) && (a.blue == b.blue));
}

- (void)setColor:(RGBColor)color atX:(NSUInteger)x y:(NSUInteger)y
{
    NSUInteger components[4] = {(NSUInteger)color.red, (NSUInteger)color.green, (NSUInteger)color.blue, 255};

    //NSLog(@"R: %ld, G: %ld, B: %ld", components[0], components[1], components[2]);

    [self.bitmap setPixel:components atX:x y:y];
}

- (IBAction)colorize:(id)sender
{
    float width = imageView.image.size.width;
    float height = imageView.image.size.height;

    BitmapImage *img = (BitmapImage*)imageView.image;

    RGBColor oldColor = [img colorAtX:0 y:0];
    RGBColor newColor;// = {255, 0, 0};
    newColor.red = 255;
    newColor.green = 0;
    newColor.blue = 0;

    for (int x = 0; x <= width; x++)
    {
        for (int y = 0; y <= height; y++)
        {
            if ([img color:[img colorAtX:x y:y] isEqualToColor:oldColor])
            {
                [img setColor:newColor atX:x y:y];
            }
        }
    }
    [imageView setNeedsDisplay:YES];
}

しかし今では、ピクセルを最初に赤に変更し、2 回目に colorize メソッドが呼び出されたときに青に変更します。

編集 2 (2013 年 4 月 24 日)

次のコードはそれを修正します。これは、回転コードが NSBitmapImageRep にアルファ チャネルを追加していたためです。

- (RGBColor)colorAtX:(NSInteger)x y:(NSInteger)y
{
    if (self.bitmap.hasAlpha)
    {
        NSUInteger components[4];
        [self.bitmap getPixel:components atX:x y:y];
        RGBColor color = {components[1], components[2], components[3]};
        return color;
    }
    else
    {
        NSUInteger components[3];
        [self.bitmap getPixel:components atX:x y:y];
        RGBColor color = {components[0], components[1], components[2]};
        return color;
    }
}

- (void)setColor:(RGBColor)color atX:(NSUInteger)x y:(NSUInteger)y
{
    if (self.bitmap.hasAlpha)
    {
        NSUInteger components[4] = {255, (NSUInteger)color.red, (NSUInteger)color.green, (NSUInteger)color.blue};
        [self.bitmap setPixel:components atX:x y:y];
    }
    else
    {
        NSUInteger components[3] = {color.red, color.green, color.blue};
        [self.bitmap setPixel:components atX:x y:y];
    }
}
于 2013-04-23T13:49:58.893 に答える