从NSImage获取像素和颜色

10

我已经创建了一个NSImage对象,理想情况下想确定它包含多少个像素颜色。这是否可能?

我创建了一个NSImage对象,希望能够确定它包含的每种像素颜色的数量。这是否可能?
6个回答

12

此代码将NSImage渲染到CGBitmapContext中:

- (void)updateImageData {

    if (!_image)
        return;

    // Dimensions - source image determines context size

    NSSize imageSize = _image.size;
    NSRect imageRect = NSMakeRect(0, 0, imageSize.width, imageSize.height);

    // Create a context to hold the image data

    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);

    CGContextRef ctx = CGBitmapContextCreate(NULL,
                                             imageSize.width,
                                             imageSize.height,
                                             8,
                                             0,
                                             colorSpace,
                                             kCGImageAlphaPremultipliedLast);

    // Wrap graphics context

    NSGraphicsContext* gctx = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:NO];

    // Make our bitmap context current and render the NSImage into it

    [NSGraphicsContext setCurrentContext:gctx];
    [_image drawInRect:imageRect];

    // Calculate the histogram

    [self computeHistogramFromBitmap:ctx];

    // Clean up

    [NSGraphicsContext setCurrentContext:nil];
    CGContextRelease(ctx);
    CGColorSpaceRelease(colorSpace);
}

给定一个位图上下文,我们可以直接访问原始图像数据,并计算每个颜色通道的直方图:

- (void)computeHistogramFromBitmap:(CGContextRef)bitmap {

    // NB: Assumes RGBA 8bpp

    size_t width = CGBitmapContextGetWidth(bitmap);
    size_t height = CGBitmapContextGetHeight(bitmap);

    uint32_t* pixel = (uint32_t*)CGBitmapContextGetData(bitmap);

    for (unsigned y = 0; y < height; y++)
    {
        for (unsigned x = 0; x < width; x++)
        {
            uint32_t rgba = *pixel;

            // Extract colour components
            uint8_t red   = (rgba & 0x000000ff) >> 0;
            uint8_t green = (rgba & 0x0000ff00) >> 8;
            uint8_t blue  = (rgba & 0x00ff0000) >> 16;

            // Accumulate each colour
            _histogram[kRedChannel][red]++;
            _histogram[kGreenChannel][green]++;
            _histogram[kBlueChannel][blue]++;

            // Next pixel!
            pixel++;
        }
    }
}

@end

我已经发布了一个完整的项目,一个Cocoa示例应用程序,其中包括上述内容。


9
从您的NSImage获取一个NSBitmapImageRep。然后您可以访问像素。
NSImage* img = ...;
NSBitmapImageRep* raw_img = [NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]];
NSColor* color = [raw_img colorAtX:0 y:0];

4
这是一种非常昂贵的方法,因为像Peter Hosey所说,colorAtX:y:将涉及为每个像素创建一个NSColor实例。更高效的方法是获取原始数据缓冲区,并使用指针遍历以计算直方图。 - gavinb
3
嗨,gavinb,你有关于这个问题的指导吗?(获取原始数据缓冲区并使用指针遍历)谢谢! - RickON
@clearlight 好的,我没有意识到会让任何人感到悬念。我写了一个示例应用程序,正如上面所描述的那样,并在这个问题的答案中添加了代码。我也在github上发布了它。祝你使用愉快! - gavinb
@clearlight 请看下面我的新回答 https://dev59.com/K3I-5IYBdhLWcg3wJE4K#43232455 - 这基本上实现了 @Peter Hosey 在他的回答中所描述的。使用 NSColor colorAtX:y 绝对不是正确的做法。 - gavinb

9
我建议创建你自己的 位图上下文, 将其包装在一个图形上下文中,并将其设置为当前上下文,告诉图像绘制自身,然后直接访问位图上下文背后的像素数据。
这将需要更多代码,但将节省通过TIFF表示和创建成千上万个NSColor对象的时间。如果你正在处理任何可观大小的图像,这些开销将迅速累加。

对于新的懒读者:这个解决方案是@gavinb的答案实现的。 - jjabba
您提供的文档链接已经失效,这不再是一个可行的答案。 - Ky -

1
使用NSBitmapImageRepcolorAtX不总是导致精确正确的颜色。
我用这段简单的代码找到了正确的颜色:
[yourImage lockFocus]; // yourImage is just your NSImage variable
NSColor *pixelColor = NSReadPixel(NSMakePoint(1, 1)); // Or another point
[yourImage unlockFocus];

1
这可能是一种更简便的方法,可以减少内存管理方面的复杂性。

https://github.com/koher/EasyImagy

代码示例 https://github.com/koher/EasyImagyCameraSample

import EasyImagy

let image = Image<RGBA<UInt8>>(nsImage: "test.png") // N.B. init with nsImage 

print(image[x, y])
image[x, y] = RGBA(red: 255, green: 0, blue: 0, alpha: 127)
image[x, y] = RGBA(0xFF00007F) // red: 255, green: 0, blue: 0, alpha: 127

// Iterates over all pixels
for pixel in image {
    // ...
}



//// Gets a pixel by subscripts Gets a pixel by  
let pixel = image[x, y]
// Sets a pixel by subscripts
image[x, y] = RGBA(0xFF0000FF)
image[x, y].alpha = 127
// Safe get for a pixel
if let pixel = image.pixelAt(x: x, y: y) {
    print(pixel.red)
    print(pixel.green)
    print(pixel.blue)
    print(pixel.alpha)

    print(pixel.gray) // (red + green + blue) / 3
    print(pixel) // formatted like "#FF0000FF"
} else {
    // `pixel` is safe: `nil` is returned when out of bounds
    print("Out of bounds")
}

1
在核心图像文档中查找“直方图”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接