【问题标题】:How to get the color of a pixel of a NSImage?如何获取 NSImage 像素的颜色?
【发布时间】:2020-10-21 17:16:24
【问题描述】:

我正在尝试从我的 NSImage(png 文件)中获取特定点的 CGColor。这个函数在 NSViews 上调用,它可以拖到我的 NSImageView 上。然后这个函数应该将一个变量(defaultColor)设置为颜色,它恰好位于 NSImageView 上的 NSView 的位置。为了进行测试,我将每个 NSView 着色为之前存储在变量中的颜色(因此,对于 NSView 位于 NSImageView 上的颜色)。 您如何在屏幕截图中看到例如我在 NSImageView 中显示了包含四种不同颜色的 300x300 图像。颜色将被正确检测,但颜色似乎是水平交换的。当 NSView 在底部时测量顶部的颜色,当 NSView 在顶部时测量底部的颜色。

字节顺序错了吗?我怎么能换这个?我已经读过How do I get the color of a pixel in a UIImage with Swift?Why do I get the wrong color of a pixel with following code?。那是我使用的代码的地方,我做了一些改动:

func setDefaulColor(image: NSImage)
{
    let posX = self.frame.origin.x + (self.frame.width / 2)
    let posY = self.frame.origin.y + (self.frame.height / 2)
    
    var r: CGFloat = 0
    var g: CGFloat = 0
    var b: CGFloat = 0
    var a: CGFloat = 1

    if posX <= image.size.width && posY <= image.size.height
    {
        var imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
        let imageRef = image.cgImage(forProposedRect: &imageRect, context: nil, hints: nil)
        
        var pixelData = imageRef!.dataProvider!.data
        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
        
        let pixelInfo: Int = Int(posY) * imageRef!.bytesPerRow + Int(posX) * 4
             
        r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
        g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
        b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
        a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)
        
    
    }
    
    self.defaultColor = CGColor(red: r, green: g, blue: b, alpha: a)
    setNeedsDisplay(NSRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height))
}

以下是一些截图:

NSViews on the top

NSViews on the bottom

NSImageView 显示的 PNG 文件应该是 RGBA 格式。你怎么能看到我认为从像素数据中提取的颜色:

    r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
    g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
    b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
    a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)

但似乎像素数据的加载顺序错误?

您知道如何更改数据顺序或为什么会发生这种情况吗?

【问题讨论】:

  • 也许这与 macOS 上的坐标系有关?该代码是为 iOS 应用程序编写的,在 iOS 上,0,0 点位于左上角,在 macOS 上,0,0 点似乎位于左下角。我如何在 macOS 上管理这个?

标签: ios swift macos image colors


【解决方案1】:

图像的坐标系和视图的坐标系不同。它们之间需要转换。

很难说

let posX = self.frame.origin.x + (self.frame.width / 2)
let posY = self.frame.origin.y + (self.frame.height / 2)

与您的图片相关,因为您没有指定任何其他信息。

如果你有一个图像视图,并且你想在某个位置提取像素(x, y),那么你需要考虑缩放和内容模式。

图像本身通常放置在字节缓冲区中,这样左上角的像素在前,然后是右上的像素。 NSView 的坐标系不是从左下角开始的。

首先,获取相对位置是最有意义的。这是一个坐标在 [0, 1] 内的点。在您看来,它应该是:

func getRelativePositionInView(_ view: NSView, absolutePosition: (x: CGFloat, y: CGFloat)) -> (x: CGFloat, y: CGFloat) {
    return ((absolutePosition.x - view.frame.origin.x)/view.frame.width, (absolutePosition.y - view.frame.origin.y)/view.frame.height)
}

现在应该将此点转换为需要进行垂直翻转并应用缩放的图像坐标系。

如果内容模式只是“缩放”(显示整个图像),那么解决方案很简单:

func pointOnImage(_ image: NSImage, relativePositionInView: (x: CGFloat, y: CGFloat)) -> (x: CGFloat, y: CGFloat)? {
    let convertedCoordinates: (x: CGFloat, y: CGFloat) = (
        relativePositionInView.x * image.size.width,
        (1.0-relativePositionInView.y) * image.size.height
    )
    guard convertedCoordinates.x >= 0.0 else { return nil }
    guard convertedCoordinates.y >= 0.0 else { return nil }
    guard convertedCoordinates.x < image.size.width else { return nil }
    guard convertedCoordinates.y < image.size.height else { return nil }

    return convertedCoordinates
}

其他一些更常见的模式是 scale-aspect-fill 和 scale-aspect-fit。这些在转换点时需要额外的计算,但这似乎不是您的问题的一部分(目前)。

所以这两种方法很可能会解决您的问题。但您也可以只应用一个非常简短的修复:

let posY = whateverViewTheImageIsOn.frame.height - (self.frame.origin.y + (self.frame.height / 2))

我个人认为这很混乱,但你自己判断吧。

还有一些其他考虑因素可能对您的情况有效,也可能无效。显示图像时,像素的颜色可能与缓冲区中的颜色不同。这主要是由于缩放。例如,纯黑白图像可能会在某些像素上显示灰色区域。如果这是您在寻找颜色时想要应用的东西,那么从NSView 创建图像更有意义。这种方法还可以为您解决很多数学问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 1970-01-01
    • 2013-07-11
    相关资源
    最近更新 更多