NSImage из массива 1D пикселей? - PullRequest
2 голосов
/ 28 марта 2011

В моей программе есть большой 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 ]

3 голосов
/ 28 марта 2011

initWithData работает только для типов изображений, о которых система уже знает. Для неизвестных типов - и необработанных данных пикселей - вам нужно создать представление изображения самостоятельно. Вы можете сделать это через Core Graphics, как предлагается в ответе, на который Кирби ссылается. Кроме того, вы можете использовать NSImage, создав и добавив NSBitmapImageRep.

Точные детали будут зависеть от формата ваших данных пикселей, но вот пример процесса для изображения в оттенках серого, где исходные данные (массив 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-фьюзинга достаточно.)

1 голос
/ 30 марта 2011

Вот решение с вашим кодом. Это было расширено для поддержки всех 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];
0 голосов
/ 28 марта 2011
...