Cocoa 从NSImage获取像素和颜色

Cocoa 从NSImage获取像素和颜色,cocoa,nsimage,Cocoa,Nsimage,我已经创建了一个NSImage对象,理想情况下,我想确定它包含多少种颜色的像素。这可能吗?从您的NSImage获取一个。然后,您可以访问像素 NSImage* img = ...; NSBitmapImageRep* raw_img = [NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]]; NSColor* color = [raw_img colorAtX:0 y:0]; 我建议您自己创建,将其包装在一个文件夹中,并将其设

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

从您的
NSImage
获取一个。然后,您可以访问像素

NSImage* img = ...;
NSBitmapImageRep* raw_img = [NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]];
NSColor* color = [raw_img colorAtX:0 y:0];

我建议您自己创建,将其包装在一个文件夹中,并将其设置为当前上下文,然后直接访问位图上下文后面的像素数据


这将是更多的代码,但将节省您通过TIFF表示和创建数千或数百万NSColor对象的旅程。如果您正在处理任何可感知大小的图像,这些费用将迅速增加。

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

此代码将
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
-(void)ComputeHistorogramFromBitmap:(CGContextRef)位图{
//注:假设RGBA 8bpp
size\u t width=CGBitmapContextGetWidth(位图);
size\u t height=CGBitmapContextGetHeight(位图);
uint32_t*像素=(uint32_t*)CGBitmapContextGetData(位图);
对于(无符号y=0;y>0;
uint8_t绿色=(rgba&0x0000ff00)>>8;
uint8_t blue=(rgba&0x00ff0000)>>16;
//积累每种颜色
_直方图[kRedChannel][red]+;
_直方图[绿色通道][绿色]+;
_直方图[蓝色通道][蓝色]++;
//下一个像素!
像素++;
}
}
}
@结束
我已经发布了一个完整的项目,一个Cocoa示例应用程序,其中包括上述内容


colorAtX
NSBitmapImageRep
一起使用,并不总能得到准确的颜色

我通过以下简单代码获得了正确的颜色:

[yourImage lockFocus]; // yourImage is just your NSImage variable
NSColor *pixelColor = NSReadPixel(NSMakePoint(1, 1)); // Or another point
[yourImage unlockFocus];

对于某些人来说,这可能是一种更为精简的方法,可以降低内存管理的复杂性

代码示例

轻松导入
让image=image(nsImage:test.png)//N.B.使用nsImage初始化
打印(图像[x,y])
图像[x,y]=RGBA(红色:255,绿色:0,蓝色:0,阿尔法:127)
图像[x,y]=RGBA(0xFF00007F)//红色:255,绿色:0,蓝色:0,阿尔法:127
//迭代所有像素
用于图像中的像素{
// ...
}
////按下标获取像素按下标获取像素
设像素=图像[x,y]
//按下标设置像素
图像[x,y]=RGBA(0xFF0000FF)
图像[x,y].alpha=127
//安全获取一个像素
如果let pixel=image.pixelAt(x:x,y:y){
打印(像素红色)
打印(像素绿色)
打印(像素。蓝色)
打印(像素alpha)
打印(像素.灰色)/(红色+绿色+蓝色)/3
打印(像素)//格式为“#FF0000FF”
}否则{
//'pixel'是安全的:超出边界时返回'nil'
打印(“超出范围”)
}

这是一种非常昂贵的方法,因为正如Peter Hosey所指出的那样,
colorAtX:y:
将涉及为每个像素创建一个
NSColor
实例。获取原始数据缓冲区并使用指针遍历以计算直方图要高效得多。您好,gavinb,您对此有什么指导(获取原始数据缓冲区并使用指针遍历)吗?谢谢大家!@我没意识到我让任何人都处于悬念中。我编写了一个示例应用程序,它可以实现上述功能,并在回答这个问题时添加了代码。我也在github上发布了它。享受吧@clearlight在下面看到我的新答案-这基本上实现了@Peter Hosey在他的答案中描述的内容。使用
NSColor colorAtX:y
绝对不是这样做的。对于新的懒惰读者:这个解决方案是在@gavinb中实现的。您链接的文档是404。这不再是一个可行的答案。
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")
}