来自1D像素阵列的NSImage?

时间:2011-03-28 12:09:11

标签: cocoa image nsview nsdata nsimage

我的程序中有一个大的1D动态数组,表示磁盘上的FITS图像,即它保存图像的所有像素值。数组的类型是 double 。目前,我只关心单色图像。

由于Cocoa不直接支持FITS格式,我使用CFITSIO库读取图像。这有效 - 我可以按照自己的意愿操作数组,并使用库将结果保存到磁盘。

但是,我现在想要显示图像。我认为这是NSImage或NSView可以做的事情。但类引用似乎没有列出一个可以采用C数组并最终返回NSImage对象的方法。我找到的最接近的是 -initWithData:(NSData *)。但我不能100%确定这是否是我需要的。

我在这里咆哮错误的树吗?任何可以处理这个的类或方法的指针都是

编辑:

这是更新后的代码。请注意,我将每个像素设置为0xFFFF。这只会导致灰色图像。这只是一个测试。加载实际的FITS文件时,我将0xFFFF替换为imageArray[i * width + j]。这在8位中完美地工作(当然,我将每个像素值除以256以将其表示为8位)。

 NSBitmapImageRep *greyRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
                                                                     pixelsWide:width
                                                                     pixelsHigh:height
                                                                  bitsPerSample:16
                                                                samplesPerPixel:1
                                                                       hasAlpha:NO
                                                                       isPlanar:NO
                                              colorSpaceName:NSCalibratedWhiteColorSpace
                                                                    bytesPerRow:0
                                                                   bitsPerPixel:16];

 NSInteger rowBytes = [greyRep bytesPerRow];
 unsigned short*pix = (unsigned short*)[greyRep bitmapData];
 NSLog(@"Row Bytes: %d",rowBytes);

if(temp.bitPix == 16) // 16 bit image
{
    for(i=0;i<height;i++)
    {
        for(j=0;j<width;j++)
        {
            pix[i * rowBytes + j] = 0xFFFF;
        }
    }
}

我也尝试过直接使用Quartz2D。这确实产生了正确的图像,即使是16位也是如此。但奇怪的是,数据数组将0xFF视为白色,而不是0xFFFF。所以我仍然需要将所有内容除以0xFF - 在此过程中丢失数据。 Quartz2D代码:

    short* grey = (short*)malloc(width*height*sizeof(short));
    for(int i=0;i<width*height; i++)
    {
        grey[i] = imageArray[i];
    }

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
    CGContextRef bitmapContext = CGBitmapContextCreate(grey, width, height, 16, width*2, colorSpace, kCGImageAlphaNone);
    CFRelease(colorSpace);

    CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext);

    NSImage *greyImage = [[NSImage alloc] initWithCGImage:cgImage size:NSMakeSize(width, height)];

有什么建议吗?

3 个答案:

答案 0 :(得分:3)

initWithData仅适用于系统已经知道的图像类型。对于未知类型 - 和原始像素数据 - 您需要自己构建图像表示。您可以通过Core Graphics执行此操作,如Kirby链接到的答案中所建议的那样。或者,您可以通过创建和添加NSImage来使用NSBitmapImageRep

确切的细节将取决于像素数据的格式,但这里是灰度图像的过程示例,其中源数据(samples数组)在范围[0表示为double, 1]:

/* generate a greyscale image representation */
NSBitmapImageRep *greyRep =
    [[NSBitmapImageRep alloc]
        initWithBitmapDataPlanes: nil  // allocate the pixel buffer for us
                      pixelsWide: xDim 
                      pixelsHigh: yDim
                   bitsPerSample: 8
                 samplesPerPixel: 1  
                        hasAlpha: NO
                        isPlanar: NO 
                  colorSpaceName: NSCalibratedWhiteColorSpace // 0 = black, 1 = white in this color space
                     bytesPerRow: 0     // passing 0 means "you figure it out"
                    bitsPerPixel: 8];   // this must agree with bitsPerSample and samplesPerPixel

NSInteger rowBytes = [greyRep bytesPerRow];

unsigned char* pix = [greyRep bitmapData];
for ( i = 0; i < yDim; ++i )
{
    for ( j = 0; j < xDim; ++j )
    {
        pix[i * rowBytes + j] = (unsigned char)(255 * (samples[i * xDim + j]));
    }
}

NSImage* greyscale = [[NSImage alloc] initWithSize:NSMakeSize(xDim,yDim)];
[greyscale addRepresentation:greyRep];
[greyRep release];

编辑(回应评论)

我不确定是否支持16位样本,但您似乎已经确认它们是。

您所看到的仍然是将像素视为unsigned char,即8位。因此,您只设置每一行的一半,并且您将每个像素(每次一个字节)设置为两个字节值0xFF00 - 不是非常真实的白色,而是非常接近。

表示图像的另一半未被触摸,但初始化为0,因此保持黑色。

你需要先工作16位,首先从代表中转换你得到的值:

unsigned short * pix = (unsigned short*) [greyRep bitmapData];

然后为像素分配16位值:

if ( j % 2 )
{
    pix[i * rowBytes + j] = 0xFFFF;
}
else
{
    pix[i * rowBytes + j] = 0;
}

请注意,rowBytes以字节为单位,因此我们需要坚持unsigned char pix并在分配时进行投射,这有点丑陋:

if ( j % 2 )
{
    *((unsigned short*) (pix + i * rowBytes + j * 2)) = 0xFFFF;
}
else
{
    *((unsigned short*) (pix + i * rowBytes + j * 2)) = 0;
}

(我已经改变了子句的顺序,因为== 0似乎是多余的。实际上对于这样的事情来说,使用?:语法会更加简洁,但这个C语言足够多了。)< / p>

答案 1 :(得分:1)

以下是您的代码的解决方案。这扩展到支持所有3个频道。每个通道为16位。请注意,我将imageArray [i]分配给每个通道。这只是因为我目前还没有编写可以读取彩色FITS文件的代码,所以为了测试我只是将图像分配给每个通道。当然,结果是屏幕上的灰度图像。但如果有人想要,可以很容易地修改它,以便将红色分配给红色,依此类推。

NSBitmapImageRep *colorRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
                                                                        pixelsWide:width
                                                                        pixelsHigh:height
                                                                     bitsPerSample:16
                                                                   samplesPerPixel:3
                                                                          hasAlpha:NO
                                                                          isPlanar:NO
                                                                    colorSpaceName:NSCalibratedRGBColorSpace
                                                                       bytesPerRow:(3*2*width)
                                                                      bitsPerPixel:48];

    rowBytes = [greyRep bytesPerRow];
    NSLog(@"Row Bytes: %d",rowBytes);
    pix = [colorRep bitmapData];

    for(i=0;i<height*width;++i)
    {
        *((unsigned short*)(pix + 6*i)) = imageArray[i];
        *((unsigned short*)(pix + 6*i + 2)) = imageArray[i];
        *((unsigned short*)(pix + 6*i + 4)) = imageArray[i];
    }


    NSImage *theImage = [[NSImage alloc] initWithSize:NSMakeSize(width, height)];
    [greyScale addRepresentation:colorRep];
    [myimageView setImage:theImage];

答案 2 :(得分:0)

Converting RGB data into a bitmap in Objective-C++ Cocoa的答案与您的数据相匹配。