2009年4月12日日曜日

QuartzComposer / 全方位カメラからの変換

追記(2009/4/12): ステンレス製ボール+WebCamでやってみたw.



tumblrで見かけた「ストリートビュー車を作ってみた」からちょっと興味がわいたのでやってみた.

早速グーグル先生に相談.参考資料を用意.
参考: 全方位カメラのためのパノラマ画像展開(ありがとうございます)

参考には補正がいくつかのっていたがとりあえず難しい事は置いておいて勢いだけでやってみた.
  • 画像の中心が円の中心.
  • 周方向をx, 半径方向をyにマッピングするだけ

    QCPlugInのプロパティについて
    @interface ExtensionForOmniDirectionalCameraPlugIn : QCPlugIn
    {
    }
    @property(assign) id<QCPlugInInputImageSource> inputSourceImage;
    @property(assign) id<QCPlugInOutputImageProvider> outputResultImage;
    @end

    QCPlugInのexecute:atTime: withArguments:
    - (BOOL) execute:(id)context atTime:(NSTimeInterval)time withArguments:(NSDictionary*)arguments
    {
    id image;
    self.outputResultImage = nil;

    if(image= self.inputSourceImage) {
    if(![image lockBufferRepresentationWithPixelFormat:QCPlugInPixelFormatARGB8
    colorSpace:[image imageColorSpace]
    forBounds:[image imageBounds]]) {
    return NO;
    }
    NSBitmapImageRep *p= [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
    pixelsWide:[image bufferPixelsWide]
    pixelsHigh:[image bufferPixelsHigh]
    bitsPerSample:8
    samplesPerPixel:4
    hasAlpha:YES
    isPlanar:NO
    colorSpaceName:NSCalibratedRGBColorSpace
    bitmapFormat:NSAlphaFirstBitmapFormat
    bytesPerRow:[image bufferBytesPerRow]
    bitsPerPixel:0];
    [p autorelease];
    memcpy([p bitmapData], [image bufferBaseAddress], [image bufferBytesPerRow]*[image bufferPixelsHigh]);

    NSUInteger T= 360*2;
    NSUInteger R= [image bufferPixelsHigh] / 2;
    NSBitmapImageRep *q= [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
    pixelsWide:T
    pixelsHigh:R
    bitsPerSample:8
    samplesPerPixel:4
    hasAlpha:YES
    isPlanar:NO
    colorSpaceName:NSCalibratedRGBColorSpace
    bitmapFormat:NSAlphaFirstBitmapFormat
    bytesPerRow:4*T
    bitsPerPixel:0];
    [q autorelease];
    {
    NSUInteger CX= [image bufferPixelsWide] / 2;
    NSUInteger CY= [image bufferPixelsHigh] / 2;
    NSUInteger i, j;
    double ratio= T/360.0;
    for(i= 0;i<T;i++) {
    for(j=0; j<R; j++) {
    double th= (i/ratio) * M_PI / 180.0 - M_PI;
    NSUInteger x= (NSUInteger)(j * cos(th) + CX);
    NSUInteger y= (NSUInteger)(j * sin(th) + CY);
    NSColor *c= [p colorAtX:x y:y];
    [q setColor:c atX:i y:R-j-1];
    }
    }
    }

    OutputImageProvider *provider = [[OutputImageProvider alloc] initWithBuffer:(void *)[q bitmapData]
    withBytesPerRow:4*T
    pixelFormat:QCPlugInPixelFormatARGB8
    forBounds:NSMakeRect(0, 0, T, R)
    colorSpaceRef:[image imageColorSpace]];
    if(provider == nil)
    return NO;
    self.outputResultImage = provider;
    [provider release];
    }
    [image unlockBufferRepresentation];
    return YES;
    }


    上で出てきたOutputImageProvider関連
    @interface OutputImageProvider : NSObject <QCPlugInOutputImageProvider> 
    {
    void *_baseAddress;
    NSUInteger _rowBytes;
    NSString *_format;
    NSRect _bounds;
    CGColorSpaceRef _cgColorSpaceRef;
    }
    - (id)initWithBuffer:(void*)baseAddress
    withBytesPerRow:(NSUInteger)rowBytes
    pixelFormat:(NSString*)format
    forBounds:(NSRect)bounds
    colorSpaceRef:(CGColorSpaceRef)cgColorSpaceRef;
    @end
    @implementation OutputImageProvider
    - (id)initWithBuffer:(void*)baseAddress
    withBytesPerRow:(NSUInteger)rowBytes
    pixelFormat:(NSString*)format
    forBounds:(NSRect)bounds
    colorSpaceRef:(CGColorSpaceRef)cgColorSpaceRef
    {
    if(self = [super init]) {
    _baseAddress= baseAddress;
    _rowBytes= rowBytes;
    _format= [format retain];
    _bounds= bounds;
    _cgColorSpaceRef= cgColorSpaceRef;
    }
    return self;
    }
    - (void) dealloc
    {
    [_format release];
    [super dealloc];
    }
    - (NSRect) imageBounds
    {
    return _bounds;
    }
    - (CGColorSpaceRef) imageColorSpace
    {
    return _cgColorSpaceRef;
    }
    - (NSArray*) supportedBufferPixelFormats
    {
    return [NSArray arrayWithObjects: _format,
    nil];
    }
    - (BOOL) renderToBuffer:(void*)baseAddress
    withBytesPerRow:(NSUInteger)rowBytes
    pixelFormat:(NSString*)format
    forBounds:(NSRect)bounds
    {
    memcpy(baseAddress, _baseAddress, _rowBytes*_bounds.size.height);
    return YES;
    }
    @end




    実際の映像なんだけど,参考にある画像をCropするとか,グーグル先生で適切なキーワードで調べてみるといくつか掲載されているページがでてくるのでそちらから拝借して試した.(権利関係があるだろうから掲載しないけど)これだけでも人間が見る分には(w)こまらないことがわかったりした.

    自分で鏡等が用意できればそれで撮影して変換前後の掲載できるんだけどなw.
    元ネタのようにステンレス製半球ミラーを買えば良いんだろうけどw.
  • 0 件のコメント: