【问题标题】:NSImage from a 1D pixel array?NSImage 来自一维像素阵列?
【发布时间】:2011-03-28 12:09:11
【问题描述】:

我的程序中有一个大型一维动态数组,它表示磁盘上的 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 - 在此过程中丢失数据。石英二维码:

    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)];

有什么建议吗?

【问题讨论】:

    标签: cocoa image nsview nsdata nsimage


    【解决方案1】:

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

    确切的细节取决于像素数据的格式,但这里有一个灰度图像处理示例,其中源数据(samples 数组)在 [0,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 futzing 已经足够了。)

    【讨论】:

    • 非常感谢。我的数组只是一个双精度文件,文件可以是 16 位或 32 位。我假设,如果它是一个 16 位文件,我应该将整个数组除以 65536 以转换数组,使其仅从 0 扩展到 1?
    • @Saad 这当然是一种选择。请注意,从 16 位变为 8 位时,您将失去相当多的精度——这对您的目的可能无关紧要。您可能可以在初始化代表时指定 16 位样本,但我没有尝试过,也不知道它是否会对显示的结果产生任何明显的差异。
    • 类是否支持 16 位?我也将 bitsPerSample 设置为 16,将 bitsPerPixel 设置为 16。但是,这段代码应该只制作一个具有交替黑白列的图像(它适用于您的代码,但如果我将其更改为 16 位则不行)只会制作一个左侧为白色而右侧为黑色的图像。 if(j%2==0){pix[i * rowBytes + j] = (unsigned char)(0);}else{pix[i * rowBytes + j] = (unsigned char)(255);}
    • @Saad 看起来它确实支持 16。请参阅上面的更新以了解您所看到的内容。
    • 非常感谢您的回复。当位图为 8 位/像素时,我一切正常。但是,如果我将其设置为 16,并使数组变短,则图像根本不会出现。我只是得到一个黑色的图像。 (我正在使用从位图制作 NSImage 并将 setImage 发送到 ImageView)。如果我将每个像素显式设置为 0xFFFF,则生成的图像不是白色,而是灰色。将其保存为 TIFF 并在 Photoshop 中打开它可以确认其为 16 位,但灰度值约为 148 左右(PS 仅以 8 位表示形式报告颜色)。有关代码,请参阅我原始帖子中的编辑。
    【解决方案2】:

    这是您的代码的解决方案。这已扩展为支持所有 3 个通道。每个通道为 16 位。请注意,我将 imageArray[i] 分配给每个通道。这只是因为我目前还没有编写可以读取颜色 FITS 文件的代码,所以为了测试我只是将图像分配给每个通道。结果当然是屏幕上的灰度图像。但是,如果需要,可以很容易地对其进行修改,例如将 Red 分配给 Red 等等。

    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];
    

    【讨论】:

      【解决方案3】:

      根据您的数据调整来自 Converting RGB data into a bitmap in Objective-C++ Cocoa 的答案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-08
        • 1970-01-01
        • 2023-04-08
        • 1970-01-01
        • 2021-10-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多