4

グレースケールではなくモノクロの白黒のように、1ビットのモノクロで画像のプレビューを表示しようとしています。これは、画像がファックスで送信された場合にどのように見えるかを示しているはずです。OS X では、1 ピクセルあたり 1 ビットという低いフォーマットは使用できません。8 ビットのグレースケールのみです。Core Graphics または別のフレームワーク (理想的にはディザリング) を使用してこの効果を実現する方法はありますか?

CIColorMonochrome というフィルターがあることは知っていますが、これは画像をグレースケールに変換するだけです。

4

1 に答える 1

3

1 ビットの深さの NSImageRep (および CG の世界でも) の作成は、AFAIK ではサポートされていないため、手動で行う必要があります。このタスクには CIImage を使用すると便利な場合があります。ここでは、古典的な (昔ながらの方法と呼ぶこともできます) 方法を説明します。これを行う方法を示すコードを次に示します。まず、グレーの画像が NSImageRep から作成されるため、ソース画像がどのような形式 (PDF ファイルの場合もある) であっても、明確に定義された単純な形式が得られます。結果のグレー イメージは、モノクロ イメージのソースです。グレー画像を作成するコードは次のとおりです (size / resolutionソース画像の を考慮せずに、ピクセルのみをカウントします!):

- (NSBitmapImageRep *) grayRepresentationOf:(NSImageRep *)aRep
{
    NSBitmapImageRep *newRep =
     [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
                                        pixelsWide:[aRep pixelsWide]
                                        pixelsHigh:[aRep pixelsHigh]
                                     bitsPerSample:8
                                   samplesPerPixel:1
                                          hasAlpha:NO   //must be NO !
                                          isPlanar:NO
                                    colorSpaceName:NSCalibratedWhiteColorSpace
                                       bytesPerRow:0
                                      bitsPerPixel:0 ];
    // this new imagerep has (as default) a resolution of 72 dpi

    [NSGraphicsContext saveGraphicsState];
    NSGraphicsContext *context = [NSGraphicsContext graphicsContextWithBitmapImageRep:newRep];
    if( context==nil ){
        NSLog( @"***  %s context is nil", __FUNCTION__ );
        return nil;
    }

    [NSGraphicsContext setCurrentContext:context];
    [aRep drawInRect:NSMakeRect( 0, 0, [newRep pixelsWide], [newRep pixelsHigh] )];

    [NSGraphicsContext restoreGraphicsState];

    return [newRep autorelease];
}

次のメソッドでは、指定された NSImageRep (そのサブクラスの 1 つ) から NXBitmapImageRep (ピクセルあたりのビット数 = 1、ピクセルあたりのサンプル数 = 1) を作成し、指定されたメソッドを使用します。

- (NSBitmapImageRep *) binaryRepresentationOf:(NSImageRep *)aRep
{
    NSBitmapImageRep *grayRep = [aRep grayRepresentation];
    if( grayRep==nil ) return nil;

    NSInteger numberOfRows = [grayRep pixelsHigh];
    NSInteger numberOfCols = [grayRep pixelsWide];

    NSBitmapImageRep *newRep =
      [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
                                        pixelsWide:numberOfCols
                                        pixelsHigh:numberOfRows
                                     bitsPerSample:1
                                   samplesPerPixel:1
                                          hasAlpha:NO
                                          isPlanar:NO
                                    colorSpaceName:NSCalibratedWhiteColorSpace
                                      bitmapFormat:0 
                                       bytesPerRow:0
                                      bitsPerPixel:0 ];

    unsigned char *bitmapDataSource = [grayRep bitmapData];
    unsigned char *bitmapDataDest = [newRep bitmapData];

    // here is the place to use dithering or error diffusion (code below)

    // iterate over all pixels
    NSInteger grayBPR = [grayRep bytesPerRow];
    NSInteger binBPR = [newRep bytesPerRow];
    NSInteger pWide = [newRep pixelsWide];

    for( NSInteger row=0; row<numberOfRows; row++ ){
      unsigned char *rowDataSource = bitmapDataSource + row*grayBPR;
      unsigned char *rowDataDest = bitmapDataDest + row*binBPR;

      NSInteger destCol = 0;
      unsigned char bw = 0;
      for( NSInteger col = 0; col<pWide; ){
        unsigned char gray = rowDataSource[col];
        if( gray>127 ) {bw |= (1<<(7-col%8)); };
        col++;
        if( (col%8 == 0) || (col==pWide) ){
            rowDataDest[destCol] = bw;
            bw = 0;
            destCol++;
        }
    }
}

// save as PNG for testing and return
    [[newRep representationUsingType:NSPNGFileType properties:nil] writeToFile:@"/tmp/bin_1.png" atomically:YES];
    return [newRep autorelease];
}

誤差拡散のために、グレー画像のビットマップを直接変更する次のコードを使用しました。これは、グレー イメージ自体が使用されなくなったため許可されています。

// change bitmapDataSource : use Error-Diffusion
for( NSInteger row=0; row<numberOfRows-1; row++ ){
    unsigned char *currentRowData = bitmapDataSource + row*grayBPR;
    unsigned char *nextRowData = currentRowData + grayBPR;
    for( NSInteger col = 1; col<numberOfCols; col++ ){
        NSInteger origValue = currentRowData[col];
        NSInteger newValue = (origValue>127) ? 255 : 0;
        NSInteger error = -(newValue - origValue);
        currentRowData[col] = newValue;

        currentRowData[col+1] = clamp(currentRowData[col+1] + (7*error/16));
        nextRowData[col-1] = clamp( nextRowData[col-1] + (3*error/16) );
        nextRowData[col] = clamp( nextRowData[col] + (5*error/16) );
        nextRowData[col+1] = clamp( nextRowData[col+1] + (error/16) );
    }
}

クランプはメソッドの前に定義されたマクロです

#define clamp(z) ( (z>255)?255 : ((z<0)?0:z) ) 

これにより、unsigned char バイトが有効な値 (0<=z<=255) を持つようになります。

于 2012-11-15T21:27:44.383 に答える